Skip navigation

What I love about the Internet is the community that happens at times.  I posted my game code up for all to see and review and I received a lot of good feedback.  One guy, Bob, event went out of his way to help me understand classes and wrote up a basic structure for my style of game.

from collections import OrderedDict # for storing menus.

# adjust for Python 2 or 3
import sys
if sys.version[0] >= ‘3’:
getUserInput = input
else:
getUserInput = raw_input

def main():
place = ‘road’ # starting place
while place:
place = places[place].enter()
if not place:
ans = getUserInput(‘enter yes if you want to play another round.’)
if ans == ‘yes’:
place = ‘road’

class Choice:
def __init__(self, prompt, next, description=None):
self.prompt = prompt
self.key = prompt.partition(‘ ‘)[0].lower() # get 1st word of prompt
self.description = description
self.next = next

class Place:
defaultChoice = Choice(”, ‘cycle’, “I don’t understand that!”)
def __init__(self, name, description):
self.name = name
self.description = description
self.choices = OrderedDict()
self.menu = ”

def addChoice(self, choice):
key = choice.key
self.choices[key] = choice
self.menu += choice.prompt + ‘\n’

def enter(self):
print(self.description)
while True:
userChoice = getUserInput(self.menu[:-1]).l

ower()
choice = self.choices.get(userChoice, self.defaultChoice)
if choice.description:
print(choice.description)
if choice.next != ‘cycle’:
return choice.next# create the kingdom
places = {}place = Place(‘road’, ‘You are standing on a road. Nearby is a small house’)
place.addChoice(Choice(‘Enter the house’, ‘house’))
place.addChoice(Choice(‘South’, ‘road2’))
places[place.name] = placeplace = Place(‘road2’, ‘You are standing on a road, surrounded by howling wolves.’)
place.addChoice(Choice(‘Run for your life’, ”, ‘outrun a wolf? ha!’))
places[place.name] = placeplace = Place(‘house’, ‘You are in a small house. There are keys here. A stairway ascends.’)
place.addChoice(Choice(‘Climb the stairs’, ”, ‘your foot breaks a weak riser and you fall to your death’))
place.addChoice(Choice(‘Exit’, ‘road’, ‘you leave the house’))
places[place.name] = place

main() # start the game

I tried running it and get an immediate error.
ImportError: cannot import name OrderedDict

Well, let me do some research into this and see if it’s just a Python version issue because in the code there is something about checking version and using different routines.

Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
[GCC 4.4.5] on linux2

Ah ha, the Docs on OrderedDict say new for version 2.7 whereas I seem to have version 2.6.  How odd though, I thought I was running 2.7 all this time.  Off to Google to find a link/how-to on this.

I found this link, which does include an automated script to do the update for you (this one compiles from source) but experience has shown that such things never turn out exact and I really can’t be bothered to spend hours and hours trying to figure out what went wrong.

Then I found this link, which suggests using a mix of Debian versions, which I know causes bad things to happen.  Besides, Debian Wheezy is now stable and as soon as I get an external HD, I’ll be backing everything up and upgrading my OS, which includes v2.7 of Python.  So, time to wait then I can try this code out.

I will say this though.  From what I read in the above code, I’m not convinced that it will be any easier to program.  It just doesn’t look/feel intuitive enough when trying to debug an error message that might come up.  That and it seems to scream ‘complication’ when the following is to be adhered to.

      The Zen of Python

    Beautiful is better than ugly.
    Explicit is better than implicit.
    Simple is better than complex.
    Complex is better than complicated.
    Flat is better than nested.
    Sparse is better than dense.
    Readability counts.
    Special cases aren't special enough to break the rules.
    Although practicality beats purity.
    Errors should never pass silently.
    Unless explicitly silenced.
    In the face of ambiguity, refuse the temptation to guess.
    There should be one-- and preferably only one --obvious way to do it.
    Although that way may not be obvious at first unless you're Dutch.
    Now is better than never.
    Although never is often better than *right* now.
    If the implementation is hard to explain, it's a bad idea.
    If the implementation is easy to explain, it may be a good idea.
    Namespaces are one honking great idea -- let's do more of those!

Side Note: Why the hell is WordPress suggesting “Pink(singer)” for this post?!?! Also, they suggest ‘transportation’ for just about every, single post I have written for any of my blogs here after the publish phase.  What gives WordPress??  You desperate for some Transportation posts??

Advertisements

4 Comments

  1. A class is a package holding together some state (i.e. data) together with the operations that you’d want to perform on it.

    You know a few very well by now:

    A list is some state (the items in the list and their order) together with some operations that you can perform on them (my_list[1], my_list[1:5], my_list.append(), my_list.find(), etc’).

    When your code models lots of kinds of objects (e.g. you have code for what a place is and how it behaves, code for what a choice is, code for what an item is, code for what an inventory is, code for what a monster is, etc’) you’ll want to have everything in nice packages like these so you don’t get confused between, say, how to describe a place to the player and how to describe a monster to the player (and it also saves some typing).

    These are almost equivalent:

    ## no classes

    def Monster(name, hp, strength):
    return {“name”: name, “hp”: hp, “hp_left”: hp, “strength”: strength}

    def describe_monster(monster):
    return “A wild %s. %d/%d HP.” % (monster[“name”], monster[“hp_left”], monster[“hp”])

    def sustain_damage(monster, damage):
    if damage > monster[“hp_left”]:
    monster[“hp_left”] = 0
    return True
    else:
    monster[“hp_left”] -= damage
    return False

    def deal_damage(monster, armor_class):
    return max(0, monster[“strength”] – armor_class)

    def Place(name, description):
    return {“name”: name, “description”: description}

    def describe_place(place):
    return “The %s.\n\n%s” % (place[“name”], place[“description”])

    m = Monster(“goblin”, 10, 4)
    print describe_monster(monster)

    sustain_damage(m, 5)
    deal_damage(m, 3)
    print describe_monster(monster)

    p = Place(“Townsville”, “A small town in nowhere”)
    print describe_place(p)

    ## classes

    class Monster(object):
    def __init__(self, name, hp, strength):
    self.name = name
    self.hp = hp
    self.hp_left = hp_left
    self.strength = strength

    def describe(self):
    return “A wild %s. %d/%d HP.” % (self.name, self.hp_left, self.hp)

    def sustain_damage(monster, damage):
    if damage > self.hp_left:
    self.hp_left = 0
    return True
    else:
    self.hp_left -= damage
    return False

    def deal_damage(self, armor_class):
    return max(0, self.strength – armor_class)

    class Place(object):
    def __init__(self, name, description):
    self.name = name
    self.description = description

    def describe(self):
    return “The %s.\n\n%s” % (self.name, self.description)

    m = Monster(“goblin”, 10, 4)
    print m.describe()

    m.sustain_damage(5)
    m.deal_damage(3)
    print m.describe()

    p = Place(“Townsville”, “A small town in nowhere”)
    print p.describe()

    ## end

    Notice how we don’t need separate names for describe_monster() and describe_place().

    Notice how method (function) calls are better related visually to the dictionary holding the object state.

    Notice how the dictionaries holding state get a more “first class” treatment, with nice syntax.

    Notice how you cannot describe_place(m) by mistake.

    As far as you should care, classes are a convention on how to describe things we talk about in our code, a convention agreed by everybody and given some nice helpful syntax.

    Later you will learn that there are some nifty things you are “given for free” when you describe things as classes.

    • BTW, this is a blog about python. Please, Please, arrange for a nice way to post code with indentation.

      In the meantime:

      You might want to open it twice, to compare side-by-side.

      • Yeah, I know that indentation is not quite working for posting code. Have to think on how best to deal with it here. Thanks.

    • Thanks for the explanation. First time anyone, anywhere that I’ve seen, has bothered to try to explain the difference.

      When I have a moment, I’ll go over this in detail to see how much this makes sense to me.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: