Ex36 tips please

Hello! So, as I was working on my little linear game (with the crappiest sci-fi plot ever), I realized my game is TOO SIMPLE. I am looking for some suggestions to make it more interesting like puzzles, or add something to the program that I have never used before. Half of it is print statements though… :sob: I will post my code here.

from sys import exit

def bot_room():
    print("You entered room #627, the old robot facility.")
    print("These robots had been made for construction and not battle.")
    print("But Hades has hacked them and they will kill anyone on sight.")
    print("You see an EMP charge on the far end of the room.")
    print("Do you battle the bots, try to outrun the bots and get to the HQ, or take a risk to obtain the EMP charge?")

    choice = input('> ')

    if 'battle' in choice:
        dead("The robots are much stronger than you and kill you...")
    elif 'outrun' in choice:
        dead("The robots were much faster and killed you...")
    elif 'EMP' in choice or 'emp' in choice:
        print("You obtained the EMP charge and set it off before the bots could kill you.")
        print("You now have the battle exo-skeleton.")
        print("You suddenly hear cries from down the hallway.")
        print("Do you investigate or go to the HQ?")

        decision = input('> ')

        if 'investigate' in decision:
            hostage_room('exo-skeleton')
        elif 'HQ' in decision:
            hq_room(False)
        else:
            print("I don't know what that means.")
    else:
        print("I don't know what that means.")


def turret_room():
    print("You entered room #572, the defense system management center.")
    print("The defense systems have been taken over and the turrets shoot on sight.")
    print("The turrets can fire .50 cal rounds but overheat in a while.")
    print("You have a simple hacking module, but you will have to take a risk and get to console running from cover to cover.")
    print("Do you hack the defense system, or try to escape?")

    choice = input('> ')

    if 'hack' in choice:
        print("You make your way hiding behind cover and moving when turrets overheat.")
        print("You were finally able to install the module and deactivated the turrets.")
        print("You have obtained energy gun.")
        print("You suddenly hear cries from down the hallway.")
        print("Do you investigate or go to the HQ?")

        decision = input('> ')

        if 'investigate' in decision:
            hostage_room('energy gun')
        elif 'HQ' in decision:
            hq_room(False)
        else:
            print("I don't know what that means.")
    elif 'escape' in choice:
        dead("The turrets shoot you down before you could run away...")
    else:
        print("I don't know what that means.")


def hostage_room(weapon):
    print("You investigate and find hostages.")
    print("Hades has captured the researchers working in THE FACILITY and used nano bots to take over the minds of the guards.")
    print("You are running out of time. You try saving the hostages or go to the HQ.")
    print("Do you save the hostages or leave them?")

    choice = input('> ')

    if 'save' in choice:
        print(f"You used your {weapon} and succesfully knocked out all guards.")
        print("You have secured the hostage researchers.")
        print("One of them tell you where Hades hid the quantum module.")
        print("You thank him and go to the module's location.\nYou have obtained the quantum module.")

        hq_room(True)

    elif 'leave' in choice:
        print("You let the hostages die...")
        print("You leave and were tricked by Hades. You are now in the HQ.")

        hq_room(False)

    else:
        print("I don't understand that")


def hq_room(quantum_module):
    print("You have reached the HQ.")

    if quantum_module == True:
        ending()
    elif quantum_module == False:
        # Work in progress


def dead(reason):
    print(f'{reason} Mission failed.')
    exit(0)


def start():
    print('<will enter some stuff here later>')
    room = input('> ')

    if 'robot' in room: 
        bot_room()
    elif 'defense' in room:
        turret_room()
    else:
        print("You can only go to these 2 locations")


def ending():
    print("You connected the module.")
    print("INSTALLING MODULE..... MODULE INSTALLED")
    print("Hades is trying to destroy the module. You must stop him.")
    print("1011010----101101000----1111000----100101100----10110100")
    print("5 circles appeared before you. The numbers and the circles have a connection, you must figure it out!")

    op1 = int(input('> '))
    op2 = int(input('> '))
    op3 = int(input('> '))
    op4 = int(input('> '))
    op5 = int(input('> '))

    solution = [op1, op2, op3, op4, op5]

    if solution == [90, 360, 120, 300, 180]:
        print("You are almost there, just a bit more.")
        print("Do you want a hint?")

        choice = input('Y/N')

        if choice == 'Y' or choice == 'y':
            print("Those are all angles. Picture a clock with 3 o'clock as 0 degrees.")
        else:
            print("Okay, but you must figure it out quick.")
    else:
        dead("You got it wrong.")

    op1 = int(input('> '))
    op2 = int(input('> '))
    op3 = int(input('> '))
    op4 = int(input('> '))
    op5 = int(input('> '))

    solution = [op1, op2, op3, op4, op5]

    if solution == [12, 3, 11, 5, 9]:
        print("You did it! Hades has been stopped. You saved the world. Great job. Mission accomplished.")
        print("You will always be remembered as a hero.")

As of now, it is still a work in progress and so the last half is full of bugs and pretty weird too. I am very sorry for that. And for it’s length too. And for it’s simplicity too. :pray: (I looked at other threads related to ex36 and my game is probably the most unoriginal… :frowning_face:)

Also, is there a way I can add an inventory system and checkpoints where the players loses? Mostly, I just want to add some stuff like puzzles, for example in turret_room() function. I did a little puzzle in ending() which is not complete yet but again, that puzzle is kinda simple. :expressionless:

This is nice! I like that you are already passing some inventory between the scenes.

You could expand that of course, though you’d have to think about how you want to do it. You could simply pass a list of items through the function chain. You could also store the inventory in some sort of engine class and make the scenes run as methods of that class.

Or you go crazy and devise classes for all sorts of items, weapons and armor with different stats, keys with different codes that open doors or don’t… Make an actor base class with the usual actor stats, health, stamina and whatnot, then store the inventory with your player instance… make a fight function that lets different actors win depending on their stats…

1 Like

Thanks a lot! At first I too thought of including stats but wasn’t too sure how to do it. I haven’t reached the exercise that covers classes in the book but searched the internet and found some helpful articles (didn’t wanna skip any exercise or blast through any of them :sweat_smile:). I understood how classes work though (the hard way). Will make some additions to the game and show it in another thread for a review. I don’t know how fun the game would be but it’d be fun to code it! Thanks again for the tips :blush:

I think hit points is the classic challenge on this. Create a variable at the top like this:

HP = 10

Then, at the top of each function do:

global HP

That will let you change it inside the function. After that, you can have people lose and gain hitpoints as they do things, and have them die when it goes too low.

It might also help to create functions that deal with HP:

def hit(damage):
    global HP
    HP = HP - damage
    if HP <= 0: die()

I leave it to you to figure out how to add HP like when someone eats something.

1 Like

I believe it should also be possible to update the HP value by returning a value from within the function, right? For example, you’d set HP beforehand:

HP = 10

and then later, something like:

def hit(damage,HP):
NewHP = HP - damage
if NewHP <= 0: die()
return NewHP

So you would call it later in some fashion similar to this:

updated_HP = hit(damage,HP)

when you’ve also defined the damage value somewhere.

I’m sure I’m missing something though, and I haven’t actually implemented this yet. But I was having some trouble understanding just how to pass data into and out of functions, so I spent some time playing with that today.

Global variables are an obvious solution, sure, but I feel it’s useful to have more than one way to do something like this. I can’t think of a specific example just now, but there might be situations where you don’t want to use a global variable, or at least I imagine so.

This is so cool to see the author interacting with us in the forums, btw. One reason I bought your book.

Also, OP, no worries. My game is even simpler.

I literally only have you starting in a central room, from which you can proceed north, south, east, or west.

I’ve worked out a degree of error-checking, with some rooms telling you if you try to go in an impossible direction.

I tried setting up a way to have a different message for the first time you enter a room vs. if you’ve been there before, or are still there, but it got out of hand so I scaled things back a bit.

Ugh. I tried to post my code, but it turned into an ugly mess. I’ll see if I can work out how to make it readable in this forum.

Yes, you can do it that way, but it’s going to be much more complicated as you’ll have to remember to pass it to every function, then return it, and then keep it updated on every call to every function.

Since this is a piece of data that’s used everywhere it’s perfectly fine to make a global. The convention of ALLCAPS on globals helps people know it’s a global.

Where you run into trouble with globals is when you’re using a ton of them and for things that aren’t actually use everywhere.

1 Like

@zedshaw Right, I couldn’t figure out a way to go around HP. I almost ditched including HP in favour of defense stat, but I guess I will most certainly do it. Also, I included strength, defense, agility, stamina, and intelligence stats. Is there any mathematical formula or a way to calculate who will win? :thinking: Not necessarily an in-built function, because I am divided on whether I should just do arbitrary damage numbers or have it depend on stats. By the way, since I included stats, I also made different ways to go through a single scene. Like if the players has high strength, they can battle, high intelligence: hack the enemy bots, etc. Equipping different items give stat bonuses (just arbitrary numbers nothing more). Like a player picked this item, add this number to this stat. I will include the code when it is a bit more refined.

@Arrarr I just have 2 rooms to go to in the beginning, then an optional room which is super important because if the player skips it, then they don’t get the main quest item. :sweat_smile:

1 Like

You are the game designer here… it’s your job to come up with one. :wink:

What you describe sounds great. Keep going!

1 Like

To spill the beans, python has a random module.

For example, you’d do something like:

import random
random.randint(a,b)

where a and b are your minimum and maximum desired values.

You could even get fancy and test the resulting values to enforce a balanced distribution. Then automatically re-roll if you have all max (or all min) stats.

I haven’t got that far in my game. I’m moving on to the other lessons and coming back to it periodically, just to tinker with it.

My only real goal for the Lesson 36 game at this point is to make it more efficient without obfuscating, while exploring various ways to do things.

For example, you’ll see (if I ever figure out how to post my code without it being formatted to unreadability, argh!) that I wrote a reusable function for the user input prompt. That way I’m not duplicating code in every room, rather the room’s function just calls the prompt function at the appropriate time. More importantly, if I change the way the function works, I only have to do it once, not 5 times!

It should be possible to move away from each room having its own function, rather having each room defined as some kind of data that can be loaded, saved, and rewritten on the fly (a matrix, perhaps). Then you’d perform operations on that data to determine what happens, and record outcomes. You could even have savegames, by writing the current state of the game world to a file. But I’ve only got as far as a thought experiment on that, so far.

Fence it with code tags:

[code]
Your code here...
[/code]
# A text-based adventure game!
# called Compass
from sys import exit
import time

# import modules needed to exit conditions, and also time for sleep delay
# sleep gives a nicer "feel" to the prompts, vs. instant printing

# greeting text and explanation of game
print "Welcome to the world of Compass."
print "This is a simple game to help me learn Python 2 programming."
print "You will be asked for input.  Your only valid choices are:\n"
print "N for move north"
print "S for move south."
print "E for move east."
print "W for move west.\n"
print "Capitalization matters."
# print "You can also spell out the directions if you like, e.g. 'north.'"
# print "Capitalization is unimportant, just don't use mixed-case lIkE tHiS."
# Above two lines to be implemented later
print "Any other input is likely to fail, or worse."
time.sleep(1)

valid_directions = ('N', 'S', 'E', 'W')
# This is one way to error-check input...but not implemented yet


# this next line is only for debugging
# print valid_directions

def central_room():

    currentroom = "central"
    # value currentroom will be used later to enable
    # print statements being reusable amongst rooms

    currentroom_direction = "None"

    # if already_here == False:

    print "You are in a large square room."
    print "You see passages to the %s, %s, %s, and %s." % valid_directions

    # else:
    #    pass

    # currentroom_direction gets used when determining whether player
    # is trying to perform an impossible move

    print "You are in the %s room." % currentroom

    time.sleep(1)

    print "\nChoose a direction to proceed:"
    # time.sleep(2)
    choice = raw_input("N,S,E,W > ")

    # our old friend raw_input, prompting user for direction of travel
    # prompt includes valid movement directions
    # validchoices = [N, S, E, W]

    print "Your choice was %s" % choice

    move_input(choice, currentroom_direction)

    # move_input() checks validity of movement choices
    # if player tries to move through a wall, e.g. east when already in east_room
    # then player gets message stating move is not possible

    time.sleep(1)
    # if choice in

    if choice == "N":
        # already_here = False
        # your first time into the North room
        north_room()

    elif choice == "S":
        # already_here = False
        # your first time into the South room
        south_room()

    elif choice == "E":
        # already_here = False
        # your first time into the East room
        east_room()

    elif choice == "W":
        # already_here = False
        # your first time into the West room
        west_room()

    else:
        print "That was an unexpected input."
        print "Nice try, but you're still stuck in the %s room." % currentroom

        central_room()

def north_room():

    currentroom = "north"
    currentroom_direction = "N"

    # if already_here == True:
    #    print "You are still in the north room."

    # elif already_here == False:
    # print "Welcome to the north room."
    time.sleep(1)
    print "You don't see anything of interest here."
    print "There is a passage to the %s." % opposite_direction(currentroom_direction)
    print "\nChoose a direction to proceed."

    choice = raw_input("N,S,E,W > ")

    print "Your choice was %s" % choice
    move_input(choice, currentroom_direction)

    time.sleep(1)
    # else:
    #   exit(1)

    if choice == "N":
        # print "You cannot go that way."
        north_room()

    elif choice == "S":
        central_room()

    elif choice == "E":
        # print "You cannot go that way."
        north_room()

    elif choice == "W":
        # print "You cannot go that way."
        north_room()

    else:
        print "That was an unexpected input."
        print "Nice try, but you're still stuck in the %s room.\n" % currentroom

        north_room()

def south_room():

    currentroom = "south"
    currentroom_direction = "S"
    furniture = "dusty bookshelf"

    # if already_here != True:
    print "You are in the %s room." % currentroom
    print "You see a %s against the %s wall." % (furniture, currentroom_direction)
    print "There is a passage to the %s." % opposite_direction(currentroom_direction)

    time.sleep(1)

    print "\nChoose a direction to proceed."
    choice = raw_input("N,S,E,W > ")

    print "Your choice was %s" % choice

    move_input(choice, currentroom_direction)

    if choice == "N":
        central_room()

    elif choice == "S":
        # print "\aYou cannot go that way.\n"
        south_room()

    elif choice == "E":
        # print "You cannot go that way."
        south_room()

    elif choice == "W":
        # print "You cannot go that way."
        south_room()

    else:
        print "That was an unexpected input."
        print "Nice try, but you're still stuck in the %s room.\n" % currentroom
        south_room()

def east_room():

    currentroom = "east"
    currentroom_direction = "E"
    furniture = "stone bench"

    # if already_here != True:
    print "You are in the %s room." % currentroom
    print "There is a %s against the %s wall." % (furniture, currentroom_direction)
    print "You see a passage to the %s." % opposite_direction(currentroom_direction)

    time.sleep(1)
    print "\nChoose a direction to proceed."
    choice = raw_input("N,S,E,W > ")
    print "Your choice was %s" % choice

    move_input(choice, currentroom_direction)

    if choice == "N":
        # print "You cannot go that way."
        east_room()

    elif choice == "S":
        # print "You cannot go that way."
        east_room()

    elif choice == "E":
        # print "You cannot go that way."
        east_room()

    elif choice == "W":
        central_room()

    else:
        print "That was an unexpected input."
        print "Nice try, but you're still stuck in the %s room.\n" % currentroom
        east_room()

def west_room():
    # fail_counter = 0
    currentroom = "west"
    currentroom_direction = "W"

    # if already_here != True:
    print "You are in the %s room." % currentroom
    print "You don't see anything of interest here."
    print "There is a passage to the %s." % opposite_direction(currentroom_direction)
    time.sleep(1)
    print "\nChoose a direction to proceed."
    choice = raw_input("N,S,E,W > ")
    print "Your choice was %s" % choice
    move_input(choice, currentroom_direction)

    if choice == "N":
        # print "You cannot go that way."
        west_room()

    elif choice == "S":
        # print "You cannot go that way."
        west_room()

    elif choice == "E":
        central_room()

    elif choice == "W":
        # print "You cannot go that way."
        west_room()

    else:
        print "That was an unexpected input."
        print "Nice try, but you're still stuck in the %s room.\n" % currentroom

        # fail_counter +=1
        # already_here = True

        west_room()




def move_input(choice, currentroom_direction):

    if choice == currentroom_direction:
        print "That is not a valid choice.\n"
        print "You remain in the %s room." % currentroom_direction

    else:
        pass

def opposite_direction(currentroom_direction):

    if currentroom_direction == 'N':
        return 'S'

    elif currentroom_direction == 'S':
        return 'N'

    elif currentroom_direction == 'E':
        return 'W'

    elif currentroom_direction == 'W':
        return 'E'

    else:
        return 'C'
#    elif direction == "N":
#
#    else:
#        reason = "you tried to do the impossible."
#        fail(reason)

# def travel(direction):
#    if direction == "N":
#        north_room()
#    elif direction == "S":
#        south_room()
#    elif direction == "E":
#        east_room()
#    else:
#        west_room()

#def choose_prompt():
#    print "\nChoose a direction to proceed."
#    choice = raw_input("N,S,E,W > ")
#    print "Your choice was %s" % choice
#    return choice


while True:

    # already_here = False
    fail_counter = 0

    central_room()

I used random() for some magic spells that had consequences. It made testing interesting…

But actually helped as I then learned about ‘mocks’. :partying_face:

Alright! I am back with some more code (finally!):

from sys import exit

HP = 100

class Character:
    def __init__(self, defense, strength, stamina, agility, intelligence):
        self.defns = defense
        self.str = strength
        self.stam = stamina
        self.agl = agility
        self.intl = intelligence


def damage(entity, player, health):    
    global HP
    enemy_hp = 100
    dmg_player = (entity.str * entity.stam) - (player.defns * 2)
    dmg_entity = (player.str * player.stam * 2) - (entity.defns * 0.5)

    while enemy_hp > 0:
        HP -= dmg_player
        enemy_hp -= dmg_entity 
        # Loop ends when enemy is dead.

    return HP


def character_creator():
    print("This is your only chance to customize your abilities. There is no way to change stats later, so take your decisions carefully. And a little tip, neither being a generalist nor being too niche will get you anywhere. Be wise...\n\n\n")

    print("Total stat points = 20")

    while True:
        stat1 = input('Defense: ')
        stat2 = input('Strength: ')
        stat3 = input('Stamina: ')
        stat4 = input('Agility: ')
        stat5 = input('Intelligence: ')
        
       # To ensure wrong data types don't cause problems later.
        try:
            defense = int(stat1)
            strength = int(stat2)
            stamina = int(stat3)
            agility = int(stat4)
            intelligence = int(stat5)

            total_stats = defense + strength + stamina + agility + intelligence

            if total_stats == 20:
                start(defense, strength, stamina, agility, intelligence)
            elif total_stats < 20:
                print("You have remaining stat points, you must use them all.")
            else:
                print("You used more stats points than available. You can't have more than 20.")

        except ValueError:
            print("That is an invalid input. Try again!")


def start(*stats):
    defense, strength, stamina, agility, intelligence = stats
    main = Character(defense, strength, stamina, agility, intelligence)

    print('Type "robots" for now, please!') # Cause I have to work on turret room...

    while True:
        room = input('> ')

        if 'robot' in room:
            bot_room(main)
        elif 'defense' in room:
            turret_room(main)
        else:
            print("You can only go to these 2 locations")


def bot_room(player):
    global HP

    print("You entered room #627, the old robot facility.")
    print("These robots had been made for construction and not battle.")
    print("But Hades has hacked them and they will kill anyone on sight.")
    print("You see an EMP charge on the far end of the room.")
    print(f"Do you battle the bots, try to hack the bots, or try to obtain the EMP charge?\nCurrent HP: {HP}")


    bots = Character(5, 4, 4, 4, 5)

    while True: #To make sure the program doesn't end due to wrong input.
        choice = input('> ')

        if 'battle' in choice and player.str > bots.str:
            print("You have destroyed the bots.")
            HP = damage(bots, player, HP)
            print(f"Remaining HP: {HP}\n")
            print("You can either take the battle exoskeleton or the shield weaver armor.")
            print("BATTLE EXOSKELETON:\nStrength +2\nStamina -1\n\nSHIELD WEAVER\nDefense +2\nAgility -1")

            armor = input("Which one do you take?\n>")
            exit(0)

I tested it and it all works. Just gotta complete the rest. I have to make a mini puzzle for when a player with high intelligence stat wants to hack the enemies and make changes to my code. I am trying to complete it by Monday. My code is a bit weird, I know. Hope it is readable though. So, how is it? Is it okay, crap, good? All opinions are welcome!

@florian I made a scheme for calculating damage! It works fine. I may increase the damage but I will have to test it. Was very frustrating though :sob:

@zedshaw I have thought of a way to recover HP. I will make a function out of it and then have different items (I think there would only be 2 items) restore different amounts. I will show it the next time I include my code or in another thread for a full program review if I am done with it. :blush:

1 Like

You can use player in your __init__ but that’s going to be SUPER BAD later on. You should always call it self. Always. Python people are very very upset about that.

Okay, I will change player to self in my script and edit it in the post above too. :+1:

Great, it’s taking shape!

I do have a few small tips, feel free to ignore them. :wink:


First: Please do save yourself loads of head scratching in future by adopting the habit of using clear and consistent names early on.

For example, in your damage function you have player and dmg_player, which is fine, but then there’s entity and dmg_entitiy and then enemy_hp? I’d use enemy right away, it’s much more expressive than entity. And I’d do the attributes consistently either at the beginning or at the end so I’d have enemy, enemy_dmg and enemy_hp.

Another example is the str attribute of your Character class. str is the built-in string type. There’s no name clash here, but there sure was one in my head when I read your code…: “What’s that now, is he requesting a string representation or what…?” :slight_smile: Why not use full words for those attributes? They won’t clash with the arguments of the init function, if that’s what you’re trying to avoid.

Now, don’t get me wrong, your code is absolutely fine. Only, using arbitrary names all over the place is a great way to make sure you’ll end up in a mess eventually when you work on more complex stuff. :woozy_face:


About global, just to make sure you’re aware of this: You don’t need to return HP from the damage function because you’re really changing the global HP variable from inside that function.


Little syntax trick: You get *stats in your start function. You can just pass that on to Character.__init__ with Character(*stats). But why not create your player inside character_creator and pass that to start?


Keep at it! I look forward to seeing the next iteration. And I hope this is somewhat helpful.

2 Likes

Thanks for the tips! I agree, the names in the damage() function were kinda weird.:thinking: I changed them all to enemy. Also, I changed self.str to self.strng. I could also type the full word but I will stick to this for now.

About passing the player stats. At first I tried passing them from character creator but for some reason, it didn’t work. I did a lot of tweaking and fixing and that is the thing that finally worked for me. Though, I guess it is definitely a bit weird. :sweat_smile: Again, thanks a lot for the tips! :blush: I am almost done with the different scenarios. After that, it is just copying, pasting, and tweaking them in different locations. Will show the full thing soon! :grin:

Oh, and a question! Is using too many while True okay? It indents the code a bit too much but otherwise the program goes to the last loop/function in the case of a wrong out.

1 Like