LP(2)THW: Exercise 45 - write a game

I am having a very bad time with this exercise. So far, all I can manage is more or less an imitation of Exercise 43. Mine is a generic castle adventure, complete with items, trap doors, hidden passageways (to be triggered by a cheesy hidden book in the library, naturally), etc.

What I’d like to do is, use an Engine() class to keep track of/update some things that I can then display at every user interaction. For example, current room, inventory, and (eventually) player health. Engine() runs over and over until the user either wins, or dies. A lot like Ex. 43.

The problem is: I have no idea how to pass values in and out of functions/classes and/or call them properly, apparently.

So far here’s my code:

# Welcome to Generic Castle Adventure!
# details to be implemented later
from trapdoor import TrapDoor
# we will use the below for error-handling
from sys import exit
# and generating random numbers, for combat/puzzles etc.
from random import randint
from time import sleep


health = 100
inventory = 'Empty'
condition = 'headache'
start_room = 'broom_closet'

class Room(object):

    def enter(self):
        print "TBD, come back later"
        exit(1)

class BroomCloset(Room):

    def room(self):
        print "You wake up in a puddle of your own sick."
        print "Your head is pounding.\n"
        sleep(1)
        print "The last thing you remember is, mistakenly stumbling"
        print "in here at the feast last night, thinking it was the garderobe.\n"
        sleep(1)
        print "You're not sure what else happened, but you're"
        print "alone now.  It smells like urine AND vomit in here, yuck.\n"
        sleep(1)
        print "You are in a broom closet just off the"
        print "Great Hall of a generic European style castle.\n"
        sleep(2)
        print "There are 2 things here: %s and %s\n" % ('dusty shelf', 'old broom')
        sleep(1)
        print "The door back into the Great Hall is cracked open."
        print "You can see that the broom closet is in a small alcove, under"
        print "the stairs to the upper floor of the castle.\n"
        sleep(1)
        print "Choose an action:"
        print "[open door, take broom, die]"
        action = raw_input("> ")

        if action == 'open door':
            nextroom = GreatHall()
            nextroom.room()
            return 'great_hall'
            # currentroom = '
        elif action == 'take broom':
            nextroom = GreatHall()
            inventory = 'old broom'
            print "You take the also-dusty, well-worn broom."
            # return inventory
            return 'great_hall'
        elif action == 'die':
            nextroom = Death()
            nextroom.room()
            return 'death'

        else:
            print "What are you doing dude?"
            return 'broom_closet'

class UpperHallway(Room):

    #trapdoor_result = TrapDoor().trap_opens()

    def creaking_floorboards(self):
        print "As you walk down the drafty hallway,"
        print "you hear the floorboards creak."

class ThroneRoom(Room):

    def room(self):
        print "The ornate throne room lies before you.\n"
        sleep(1)
        print "This space for rent"

        return 'throne_room'



    pass

class Dungeon(Room):

    pass

class HiddenRoom(Room):

    pass

class LibraryRoom(Room):

    pass

class GreatHall(Room):

    def room(self):
        # print "You are in the Great Hall of the castle.\n"
        # sleep(2)
        print "Description of Great Hall:"
        print "The main space is filled with several"
        print "long feasting tables, littered with debris from last night's"
        print "festivities.\n"
        sleep(1)
        print "You find it odd that no one has cleaned up the mess.  In fact,"
        print "you hear no signs of normal life - the castle seems empty so far.\n"
        sleep(1)
        print "You see 2 possible ways to go:"
        print "There is a stairway leading to the private quarters."
        print "There is a grand archway leading to the throne room.\n"
        sleep(1)
        print "Which way will you go?"
        print "[stairway, throne room, stay here]"
        action = raw_input("> ")
        sleep(2)
        if action == 'stairway':
            return 'stairway'
        elif action == 'throne room':
            return 'throne_room'
        elif action == 'stay here':
            return 'great_hall'
        else:
            print "Unexpected condition encountered, exiting"
            print "Class GreatHall(), function room()"
            exit(1)

    pass

class Bedchamber(Room):

    pass

# class Garderobe(Room)
# To be implemented later - optional

class Stairway(Room):

    def room(self):
        print "You follow a winding stairway to the upper hallway."
        return 'stairway_landing'

    pass

class StairwayLanding(Room):

    pass

class Death(Room):

    def room(self):
        print "You died."
        exit(0)


class Map(object):

    rooms = {
                'broom_closet': BroomCloset(),
                'upper_hallway': UpperHallway(),
                'throne_room': ThroneRoom(),
                'dungeon': Dungeon(),
                'hidden_room': HiddenRoom(),
                'library_room': LibraryRoom(),
                'great_hall': GreatHall(),
                'bedchamber': Bedchamber(),
#                'garderobe': Garderobe(),
                'stairway': Stairway(),
                'stairway_landing': StairwayLanding(),
                'death': Death()
    }

    # this will contain list of all possible rooms
    def __init__(self, start_room):

        self.start_room = start_room

    def next_room(self, room_name):

        value = Map.rooms.get(room_name)

        return value

    def opening_room(self):


        return self.next_room(self.start_room)

class Engine(object):

    def __init__(self, room_map):

        self.room_map = room_map

    def play(self):

        #global inventory


        current_room = self.room_map.opening_room()

        # last_room = self.room_map.next_room('finished')

        last_room = self.room_map.next_room('death')


        while current_room != last_room:

            next_room_name = current_room.room()

            current_room = self.room_map.next_room(next_room_name)


            print "You are in the %s\n" % str(current_room_name)
            print "You have these items: %s\n" % inventory
        if current_room == last_room:
            print "Last room condition reached, this is only for debug."
        else:
            print "Unexpected condition, class Engine(), function play()."
            print "Exiting."
            exit(1)

        current_room.room()

the_map = Map('broom_closet')

# print(the_map.__dict__)
# above line only for debugging

the_game = Engine(the_map)

the_game.play()

It doesn’t work, of course. For instance, here’s what happens if I try to take the broom while still in the broom closet:

> take broom
You take the also-dusty, well-worn broom.
You are in the <__main__.GreatHall object at 0x000000000295EF88>

You have these items: Empty

Description of Great Hall:
The main space is filled with several
long feasting tables, littered with debris from last night's
festivities.

You find it odd that no one has cleaned up the mess.  In fact,
you hear no signs of normal life - the castle seems empty so far.

You see 2 possible ways to go:
There is a stairway leading to the private quarters.
There is a grand archway leading to the throne room.

Which way will you go?
[stairway, throne room, stay here]
>                                         

See, inventory is still “empty.” And I don’t understand why that weird code prints (<main.GreatHall etc>) rather than simply saying “You are in the Great Hall.”

I have no idea how I would update the inventory, or exactly where the value of “inventory” is being stored so I could change it.

Help!

You are so close. Actually, I’d say you’re done with this except for a small fix to this code.

You basically don’t know what’s being returned by one of your functions, so in play() function, do this:

print(">>> play", current_room, last_room)

while current_room != last_room:
    print "## while top", current_room, last_room
    next_room_name = current_room.room()
    print "## called current_room, next_room_name=", next_room_name, current_room, next_room
    current_room = self.room_map.next_room(next_room_name)
    print "## after next_room", current_room, next_room
    if current_room == last_room:
       print "!!! last room reached", current_room, next_room
   else:
       print "Unexpected condition, class Engine(), function play().", current_room, next_room
       print "Exiting."
       exit(1)

   print "### calling current_room room", current_room, next_room
   current_room.room()

See how I stripped out all the noise, and then I print every variable in play and print that I’m about to call this, or exited that, etc.

Next, if you add this to your rooms:

    def __str__(self):
        return self.name

Then assuming you have a room.name this will make it so when you do "%s" % room it will put the room name there instead of debug garbage.

Finally, your indentation is totally messed up. I had to reindent that whole part of the play function. Go check that your text editor is using spaces only, no tabs characters, and definitely no mixed tabs and spaces.

1 Like

I worked on it a lot more after that first post and cleared some things up, but not others. Fixed a couple dumb typos that were causing the program to crash.

There was a glaring typo in my original implementation of Engine(), BTW. The “else” clause of play() needs to do a “pass”, or else the game will always exit the first time you take any action because the current_room != last_room is not True. Whoops.

I am using Atom as my Python 2 editor, as suggested at the beginning of the book. So I’m not sure what’s going on with the indents.

I’m cleaning up a few things and will post my code. It’s been heavily revised and might actually be fun to play, if you don’t spoil things by reading through it first. :slight_smile:

Thanks!

Oh, btw I’m not having much luck with your version of the while-loop within play() inside Engine().

Specifically, your code calls to print next_room before the value is set, so I get an error message complaining about that. I assume that was a typo (or else I missed something bigger, equally likely).

I get having Engine() display the name of the current room, but what about passing values to update inventory? I assume I need to have inventory be a global variable but not sure how to do that, or update it using Engine() if that’s the way.

Well, I can’t run that code without your entire project, so the code I posted is just a suggestion and if you copy-paste it then it will most likely not work. You’ll have to use that as a guide to how to change your code to print out things the way I do to sort out how you’re getting variables you shouldn’t. Most likely one of your rooms is returning a variable it shouldn’t so this will print it out.

Now, inventory is a real pain because you need it everywhere. You have three choices:

  1. Make it a class variable on something like Engine, then access it from everywhere with Engine.inventory.
  2. Make it a global variable at the top of the script that named INVENTORY, make it a dict, then you can add things into it.
  3. Give up and move on. Inventory is literally one of the worst things to try to make at this point because it’s supposed to be a simple game, and you can easily thwart yourself by trying to do too much at this point.

Try each one and then move on. I’m not kidding. You’ve totally won this one at the point.

1 Like

OK, all done and thanks again! Here’s my completed code in case anyone wants to play with it. Actually it might be a good example for some of you to use in the “audit someone else’s code” study drill:

# Welcome to Generic Castle Adventure!
# details to be implemented later
from trapdoor import TrapDoor
# we will use the below for error-handling
from sys import exit
# and generating random numbers, for combat/puzzles etc.
from random import randint
from time import sleep


health = 100
global inventory
#inventory = {1:'Nothing'}
condition = 'headache'
start_room = 'broom_closet'

class Room(object):

    def room(self):
        print "TBD, come back later"
        exit(1)

class BroomCloset(Room):

    def __str__(self):
        self.name = 'broom closet'
        return self.name

    def room(self):
        print "You wake up in a puddle of your own sick."
        print "Your head is pounding.\n"
        sleep(1)
        print "The last thing you remember is, mistakenly stumbling"
        print "in here at the feast last night, thinking it was the garderobe.\n"
        sleep(1)
        print "You're not sure what else happened, but you're"
        print "alone now.  It smells like urine AND vomit in here, yuck.\n"
        sleep(1)
        print "You are in a broom closet just off the"
        print "Great Hall of a generic European style castle.\n"
        sleep(2)
        print "There are 2 things here: %s and %s\n" % ('dusty shelf', 'old broom')
        sleep(1)
        print "The door back into the Great Hall is cracked open."
        print "You can see that the broom closet is in a small alcove, under"
        print "the stairs to the upper floor of the castle.\n"
        sleep(1)
        print "Choose an action:"
        print "[open door, take broom, stay here, die]"
        action = raw_input("> ")

        if action == 'open door':
            nextroom = GreatHall()
            #nextroom.room()
            return 'great_hall'
            # currentroom = '
        elif action == 'take broom':
            nextroom = GreatHall()
            the_game.inventory = 'old broom'
            print "You take the also-dusty, well-worn broom."
            # return inventory
            return 'great_hall'
            return inventory

        elif action == 'stay here':
            nextroom = 'broom_closet'
            return 'broom_closet'

        elif action == 'die':
            nextroom = Death()
            nextroom.room()
            return 'death'

        else:
            print "What are you doing dude?"
            return 'broom_closet'

class UpperHallway(Room):

    def __str__(self):
        self.name = 'upper hallway'
        return self.name

    def room(self):
        print "As you walk down the drafty hallway,"
        print "you hear the floorboards creak."
        return 'upper_hallway'
        currentroom = 'upper_hallway'

class ThroneRoom(Room):

    def __str__(self):
        self.name = 'throne room'
        return self.name

    def room(self):
        print "The ornate throne room lies before you.\n"
        sleep(1)
        print "Under construction.  At this time, your only option is to"
        print "return to the Great Hall, or stay here."
        print "[go back, stay here]"
        action = raw_input("> ")

        if action == 'go back':
            return 'great_hall'
        elif action == 'stay here':
            return 'throne_room'
        else:
            print "Unexpected condition encountered."
            print "Class ThroneRoom(), function room()"
            print "Exiting."
            exit(1)


        return 'throne_room'



    pass

class Dungeon(Room):

    def __str__(self):
        self.name = 'dungeon'
        return self.name

    def room(self):
        print "You are in the Dungeon."
        print "It is dark in here and smells terrible."
        print "There is an old, rusty metal door with a small window high up."
        print "You notice a stone about halfway down the back wall, which is"
        print "a slightly lighter shade than the rest, and sticks out a bit."
        print "What will you do?"
        print "[try door, push stone]"
        action = raw_input("> ")

        if action == 'try door':
            print "The door is firmly locked.  You can't budge it."
            #nextroom = 'dungeon'
            return 'dungeon'
        elif action == 'push stone':
            print "A hidden door slowly swings open, almost noiselessly."
            print "You go through once the opening is large enough."
            print "Before you have time to react, the formerly hidden door"
            print "closes behind you.\n"
            #nextroom = 'hidden_room'
            return 'hidden_room'
        else:
            print "Unexpected condition encountered, exiting"
            print "Class Dungeon(), function room()"
            exit(1)


class HiddenRoom(Room):

    def __str__(self):
        self.name = 'hidden room'
        return self.name

    def room(self):

        print "You are in a small chamber."
        sleep(1)
        print "Three walls are stone, with no apparent opening\n."
        print "In the remaining direction, you see a short passageway, then"
        print "what looks like wood paneling, with light escaping from a small"
        print "aperture.  By advancing and peering through the hole, you see"
        print "that there is a richly furnished room on the other side."
        print "Dominating the walls you can see are numerous shelves, filled"
        print "with books.\n"
        print "What will you do?"
        print "[push on wall, stay here]"
        action = raw_input("> ")

        if action == 'push on wall':
            return 'library_room'
        elif action == 'stay here':
            return 'hidden_room'
        else:
            print "You can't do that.  You remain where you are."
            return 'hidden_room'


class LibraryRoom(Room):

    def __str__(self):
        self.name = 'library'
        return self.name

    def room(self):
        print "You are in the Library."
        print "Richly appointed, its walls are lined with shelves full of books."
        print "There is a closed door on the opposite wall."
        print "You suspect it leads back into the Great Hall."
        print "You notice one book that appears to stand out from the rest."
        print "You couldn't swear to it, but the cover seems unusually worn."
        print "Peering at the spine, you make out the title:"
        print "How to Escape from Anywhere.\n"
        print "What will you do?"
        print "[open door, take book]"

        action = raw_input("> ")

        if action == 'open_door':
            print "You return to the Great Hall."
            return 'great_hall'
        elif action == 'take book':
            print "You find that the tome levers out, but does not budge otherwise."
            print "The entire shelf section swings out of the way, revealing a small"
            print "formerly-hidden chamber.  You catch a whiff of something rotten"
            print "as you step through the opening, and the ersatz bookshelf"
            print "swings closed behind you."
            return 'hidden_room'
        else:
            print "Unexpected condition encountered, exiting"
            print "Class HiddenRoom(), function room()"
            exit(1)


class GreatHall(Room):

    def __init__(self):
        pass

    def __str__(self):
        self.name = 'Great Hall'
        return self.name

    def room(self):
        # print "You are in the Great Hall of the castle.\n"
        # sleep(2)
        print "Description of Great Hall:\n"
        print "The main space is filled with several"
        print "long feasting tables, littered with debris from last night's"
        print "festivities.\n"
        sleep(1)
        print "You find it odd that no one has cleaned up the mess.  In fact,"
        print "you hear no signs of normal life - the castle seems empty so far.\n"
        sleep(1)
        print "You see 2 possible ways to go:"
        print "There is a stairway leading to the private quarters."
        print "There is a grand archway leading to the throne room.\n"
        print "There is an ordinary-looking door labeled 'Library'.\n"
        sleep(1)
        print "Which way will you go?"
        print "[stairway, throne room, library, stay here]"
        action = raw_input("> ")
        sleep(2)
        if action == 'stairway':
            return 'stairway'

        elif action == 'throne room':
            return 'throne_room'

        elif action == 'library':
            return 'library_room'

        elif action == 'stay here':
            return 'great_hall'

        else:
            print "Unexpected condition encountered, exiting"
            print "Class GreatHall(), function room()"
            exit(1)

    pass

class Bedchamber(Room):

    def __str__(self):
        self.name = 'bedchamber'
        return self.name

    def room(self):
        print "You have entered the castle's private bedchamber."
        print "There is a large bed with an ornate, 4-post canopy."
        print "It is neatly made and not occupied...suspicious, given"
        print "that you recall the feast lasting well into the wee hours.\n"
        print "On the nightstand, you see a fabulously jewel-encrusted bracelet."
        print "What will you do?"
        print "[go back, stay here, take bracelet]"
        action = raw_input("> ")
        sleep(2)
        if action == 'go back':
            #currentroom = 'stairway_landing'
            return 'stairway_landing'
        elif action == 'stay here':
            #currentroom = 'bedchamber'
            return 'bedchamber'
        elif action == 'take bracelet':
            #currentroom = 'bedchamber'
            return 'trapdoor'
        else:
            print "Unexpected condition.  Class BedChamber(), function room()."
            print "Exiting."
            exit(1)
# class Garderobe(Room)
# To be implemented later - optional

class Stairway(Room):

    def __str__(self):
        self.name = 'stairway'
        return self.name

    def room(self):
        print "You follow a spiraling stairway to the upper hallway."
        return 'stairway_landing'

class StairwayLanding(Room):

    def __str__(self):
        self.name = 'stairway landing'
        return self.name

    def room(self):
        print "You are at the top of the stairs.  A long hallway stretches before you."
        print "At the far end, you see a heavy, but plain wooden door.  That must be"
        print "the master bedchamber."
        print "Where will you go?"
        print "[stairway, bedchamber]"
        action = raw_input("> ")
        sleep(2)
        if action == 'stairway':
            return 'stairway'
        elif action == 'bedchamber':
            return 'bedchamber'
        else:
            print "Unexpected condition.  Class StairwayLanding(), function room()."
            print "Exiting."
            exit(1)

class Death(Room):

    def __str__(self):
        self.name = 'death'
        return self.name

    def room(self):
        print "You died."
        exit(0)


class Map(object):

    rooms = {
                'broom_closet': BroomCloset(),
                'upper_hallway': UpperHallway(),
                'throne_room': ThroneRoom(),
                'dungeon': Dungeon(),
                'hidden_room': HiddenRoom(),
                'library_room': LibraryRoom(),
                'great_hall': GreatHall(),
                'bedchamber': Bedchamber(),
#                'garderobe': Garderobe(),
                'stairway': Stairway(),
                'stairway_landing': StairwayLanding(),
                'death': Death(),
                'trapdoor': TrapDoor()
    }

    # this will contain list of all possible rooms
    def __init__(self, start_room):

        self.start_room = start_room

    def next_room(self, room_name):

        value = Map.rooms.get(room_name)

        return value

    def opening_room(self):


        return self.next_room(self.start_room)

class Engine(object):

    inventory = 'Empty'

    def __init__(self, room_map):

        self.room_map = room_map

    def play(self):

        #inventory = 'Empty'

        current_room = self.room_map.opening_room()

        # last_room = self.room_map.next_room('finished')

        last_room = self.room_map.next_room('death')

        while current_room != last_room:
            next_room_name = current_room.room()
            current_room = self.room_map.next_room(next_room_name)
            print "You are in the %s.\n" % str(current_room)
            print "You have these items: %s\n" % the_game.inventory
            if current_room == last_room:
                print "Last room condition reached, this is only for debug."
            else:
                pass
            #    print "Unexpected condition, class Engine(), function play()."
            #    print "Exiting."
            #    exit(1)
        current_room.room()

the_map = Map('broom_closet')

# print(the_map.__dict__)
# above line only for debugging

the_game = Engine(the_map)

the_game.play()

You will also need the code for trapdoor.py (or write your own):

from random import randint

class TrapDoor(object):
    def __init__(self):
        pass
        #print "class Trapdoor() loaded."

    def room(self):

        openchance = randint(1,2)
        # for debugging purposes, we'll print the value of openchance
        # print "openchance = %i" % openchance

        if openchance == 1:
            # openchance == 1 mean trapdoor opens beneath your feet
            print "A trapdoor opens beneath you suddenly."
            print "You fall down a dark, foul-smelling passage."
            print "Fortunately, it is gently-sloped and short enough that you"
            print "are not likely to be killed when you land."
            return 'dungeon'

        elif openchance == 2:
            # openchance == 2 means trapdoor stays closed
            print "You find that the bejeweled bracelet is firmly fixed to the"
            print "top of the nightstand.  However, it does move enough to pull"
            print "on a fine, nearly invisible wire that disappears inside the"
            print "bulk of the nightstand.  You hear a click, but it appears the trap"
            print "has malfunctioned somehow."
            return 'bedchamber'

        else:
            # shouldn't be possible, but included for completeness:
            print "Unexpected condition encountered; exiting."
            exit(1)
1 Like
A free service run by Zed A. Shaw for learncodethehardway.org.