text adventure question

Steven D'Aprano steve at REMOVE.THIS.cybersource.com.au
Sat Dec 2 22:12:14 EST 2006


On Sat, 02 Dec 2006 10:22:28 -0700, Ara Kooser wrote:

>    I am working on a text adventure game for python to get back into
> python programming. My version 0.1 used only functions so I could get
> familiar with how those work. I want to move beyond that. I am not
> sure what would be a good Python way of handling this.  I was
> wondering if classes would help? What things should be included in the
> main program?

Think about nouns and verbs. Verbs, "doing words", are functions and
methods. Nouns, "things", are classes and objects. You should model nouns
in your game with classes and objects, and verbs (actions) with functions
and methods.

Here is a simple example: a room. A room is a thing. So we might define a
room like this:


class Room:
    def __init__(self, name):
        self.name = name

Now we create a new room, and fill it:

bedroom = Room("King's Bedroom")
bedroom.description = "You are in a fancy bedroom."
self.contents.append("crown")

Now we create a function for looking around:

def look(here):
    "Look around the place you are in"
    print here.description

And when you are in the King's bedroom, you (the programmer) would call
the look function like this:

look(bedroom)


But look above: I said that the King's bedroom contains a "crown", using
only a built-in object (str). Maybe that's all you need. Or perhaps you
need something more fancy:

class Object:
# note: not "object", lower case, as that is a built-in type
    def __init__(self, name, value, description):
        self.name = name
        self.value = value
        self.description = description


crown = Object("King's crown", 15000, "a gold crown with many jewels")
scepter = Object("King's scepter", 10000, "a silver scepter")
vorpel_sword = Object("vorpel sword", 200, "a strange looking sword")
bedpan = Object("bedpan", 3, "a smelly metal bowl")


Now you can write a function to look at any object:

def look_at(something):
    print something.description


and it will work for both objects and rooms.

Let's put some more things in the King's bedroom:

bedroom.contents.extend([crown, bedpan, vorpel_sword, scepter])

Rooms also need doors:

bedroom.north = "wall"
bedroom.south = "wall"
bedroom.west = "wall"
bedroom.east = Room("corridor") # a corridor is just a long, empty room

Perhaps that's not the best way to do it, because now the King's bedroom
has an exit into the corridor, but the corridor has no exit into the
bedroom! Perhaps you need to think about a better way to map the world.
But for now, we can make a really simple fix for that:

corridor = bedroom.east
corridor.west = bedroom
corridor.north = "wall"
corridor.south = "wall"
corridor.east = Room('kitchen')

Hmmm... we're doing a lot of unnecessary typing here. Better to create
rooms with four walls already! Go back and change the definition of a room:

class Room:
    def __init__(self, name):
        self.name = name
        self.north = "wall"
        self.south = "wall"
        self.east = "wall"
        self.west = "wall"


and now you only need to change the directions that AREN'T walls.


Now you have defined the rooms and objects in your text adventure. You
should do something similar for monsters, and any other thing in the game.
Then create functions for verbs:

def fight(what):
    if type(what) == Room:
        print "Mindlessly attacking the room doesn't " \
        "accomplish anything except bruising your fists."
    elif type(what) == Object:
        print "My, you have a bad temper. You pummel the object. " \
        "It doesn't notice."
    elif type(what) == Monster:
        fight_monster()  # you have to write this function
    elif type(what) == Royalty:
        print "You try to punch the King. His bodyguards leap " \
        "out from the shadows and kick the bejeebus out of you, " \ 
        "then drag you into the deepest dungeon. You have a long, "\
        long LONG time to consider what you did wrong as the rats " \
        "nibble at your toes."
        end_game()
     else:
        print "You don't know how to fight", what


def say(words, what):
    """Say <words> to <what>."""
    if "poot" in words:
        print "Do you kiss your mommy with that potty-mouth?"

    if type(what) == Room:
        print "The room doesn't answer."
    elif type(what) == Monster:
        print "The monster doesn't understand English."
    # and you can write the rest


One suggested improvement: if you find yourself writing lots of code like
the above, testing "if type(what) == Room" etc., there is a better way of
doing it. Move that block of code into a method of Room:

class Room:
    # def __init__ stays the same as before
    def hears(words):
        """The player says <words> to the Room, and the Room responds."""
        print "The room doesn't answer."

and then your function becomes much simpler:

def say(words, what):
    """Say <words> to <what>."""
    if "poot" in words:
        print "Do you kiss your mommy with that potty-mouth?"
    else:
        # the player talks, so <what> hears and responds.
        what.hears(words)


There is more that you can do too. You need a variable to store the
player's current position:

start = Room("the village")
position = start

And you need a function that understands written commands. Here is one
suggestion: define a class for commands:

commandlist = {}
class Command:
    def __init__(self, name):
        self.name = name
        commandlist[name] = self

then define commands themselves:

Command("go")
Command("eat")
Command("sleep")
Command("fight")

(There is a lot more to it that that, I leave the rest to you.)


-- 
Steven.




More information about the Python-list mailing list