Exercise 41: A Room With A View Of A Bear With A Broadsword

Did you figure out the secret of the function in the dict from the last exercise? Can you explain it to yourself? Let me explain it and you can compare your explanation with mine. Here are the lines of code we are talking about:

cities['_find'] = find_city
city_found = cities['_find'](cities, state)

Remember that functions can be variables too. The def find_city just makes another variable name in your current module that you can use anywhere. In this code first we are putting the function find_city into the dict cities as '_find'. This is the same as all the others where we set states to some cities, but in this case it's actually the function we put in there.

Alright, so once we know that find_city is in the dict at _find, that means we can do work with it. The 2nd line of code (used later in the previous exercise) can be broken down like this:

  1. Python sees city_found = and knows we want to make a new variable.
  2. It then reads cities and finds that variable, it's a dict.
  3. Then there's ['_find'] which will index into the cities dict and pull out whatever is at _find.
  4. What is at ['_find'] is our function find_city so Python then knows it's got a function, and when it hits ( it does the function call.
  5. The parameters cities, state are passed to this function find_city, and it runs because it's called.
  6. find_city then tries to look up states inside cities, and returns what it finds or a message saying it didn't find anything.
  7. Python takes what find_city returned, and finally that is what is assigned to city_found all the way at the beginning.

I'm going to teach you a trick. Sometimes these things read better in English if you read the code backwards, so let's try that. Here's how I would do it for that same line (remember backwards):

  1. state and city are...
  2. passed as parameters to...
  3. a function at...
  4. '_find' inside...
  5. the dict cities...
  6. and finally assigned to city_found.

Here's another way to read it, this time "inside-out".

  1. Find the center item of the expression, in this case ['_find'].
  2. Go counter-clock-wise and you have a dict cities, so this finds the element _find in cities.
  3. That gives us a function. Keep going counter-clock-wise and you get to the parameters.
  4. The parameters are passed to the function, and that returns a result. Go counter-clock-wise again.
  5. Finally, we are at the city_found = assignment, and we have our end result.

After decades of programming I do not even think about these three ways to read code. I just glance at it and know what it means, and I can even glance at a whole screen of code, and all the bugs and errors jump out at me. That took an incredibly long time and quite a bit more study than is sane. To get that way, I learned these three ways of reading most any programming language:

  1. Front to back.
  2. Back to front.
  3. Counter-clock-wise.

Try them out when you have a difficult statement to figure out.

Let's now type in your next exercise, and then go over after that. This one is gonna be fun.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
from sys import exit
from random import randint

def death():
    quips = ["You died.  You kinda suck at this.",
             "Your mom would be proud. If she were smarter.",
             "Such a luser.",
             "I have a small puppy that's better at this."]

    print quips[randint(0, len(quips)-1)]
    exit(1)


def princess_lives_here():
    print "You see a beautiful Princess with a shiny crown."
    print "She offers you some cake."

    eat_it = raw_input("> ")

    if eat_it == "eat it":
        print "You explode like a pinata full of frogs."
        print "The Princess cackles and eats the frogs. Yum!"
        return 'death'

    elif eat_it == "do not eat it":
        print "She throws the cake at you and it cuts off your head."
        print "The last thing you see is her munching on your torso. Yum!"
        return 'death'

    elif eat_it == "make her eat it":
        print "The Princess screams as you cram the cake in her mouth."
        print "Then she smiles and cries and thanks you for saving her."
        print "She points to a tiny door and says, 'The Koi needs cake too.'"
        print "She gives you the very last bit of cake and shoves you in."
        return 'gold_koi_pond'

    else:
        print "The princess looks at you confused and just points at the cake."
        return 'princess_lives_here'

def gold_koi_pond():
    print "There is a garden with a koi pond in the center."
    print "You walk close and see a massive fin poke out."
    print "You peek in and a creepy looking huge Koi stares at you."
    print "It opens its mouth waiting for food."
    
    feed_it = raw_input("> ")

    if feed_it == "feed it":
        print "The Koi jumps up, and rather than eating the cake, eats your arm."
        print "You fall in and the Koi shrugs than eats you."
        print "You are then pooped out sometime later."
        return 'death'

    elif feed_it == "do not feed it":
        print "The Koi grimaces, then thrashes around for a second."
        print "It rushes to the other end of the pond, braces against the wall..."
        print "then it *lunges* out of the water, up in the air and over your"
        print "entire body, cake and all."
        print "You are then pooped out a week later."
        return 'death'

    elif feed_it == "throw it in":
        print "The Koi wiggles, then leaps into the air to eat the cake."
        print "You can see it's happy, it then grunts, thrashes..."
        print "and finally rolls over and poops a magic diamond into the air"
        print "at your feet."

        return 'bear_with_sword'

    else:
        print "The Koi gets annoyed and wiggles a bit."
        return 'gold_koi_pond'


def bear_with_sword():
    print "Puzzled, you are about to pick up the fish poop diamond when"
    print "a bear bearing a load bearing sword walks in."
    print '"Hey! That\' my diamond! Where\'d you get that!?"'
    print "It holds its paw out and looks at you."
    
    give_it = raw_input("> ")

    if give_it == "give it":
        print "The bear swipes at your hand to grab the diamond and"
        print "rips your hand off in the process.  It then looks at"
        print 'your bloody stump and says, "Oh crap, sorry about that."'
        print "It tries to put your hand back on, but you collapse."
        print "The last thing you see is the bear shrug and eat you."
        return 'death'

    elif give_it == "say no":
        print "The bear looks shocked.  Nobody ever told a bear"
        print "with a broadsword 'no'.  It asks, "
        print '"Is it because it\'s not a katana?  I could go get one!"'
        print "It then runs off and now you notice a big iron gate."
        print '"Where the hell did that come from?" You say.'

        return 'big_iron_gate'
    else:
        print "The bear look puzzled as to why you'd do that."
        return "bear_with_sword"

def big_iron_gate():
    print "You walk up to the big iron gate and see there's a handle."

    open_it = raw_input("> ")

    if open_it == 'open it':
        print "You open it and you are free!"
        print "There are mountains.  And berries! And..."
        print "Oh, but then the bear comes with his katana and stabs you."
        print '"Who\'s laughing now!?  Love this katana."'

        return 'death'

    else:
        print "That doesn't seem sensible.  I mean, the door's right there."
        return 'big_iron_gate'


ROOMS = {
    'death': death,
    'princess_lives_here': princess_lives_here,
    'gold_koi_pond': gold_koi_pond,
    'big_iron_gate': big_iron_gate,
    'bear_with_sword': bear_with_sword
}


def runner(map, start):
    next = start

    while True:
        room = map[next]
        print "\n--------"
        next = room()

runner(ROOMS, 'princess_lives_here')

It's a lot of code, but go through it, make sure it works, play it.

What You Should See

Here's me playing the game. Bears are cool.

$ python ex41.py 

--------
You see a beautiful Princess with a shiny crown.
She offers you some cake.
> make her eat it
The Princess screams as you cram the cake in her mouth.
Then she smiles and cries and thanks you for saving her.
She points to a tiny door and says, 'The Koi needs cake too.'
She gives you the very last bit of cake and shoves you in.

--------
There is a garden with a koi pond in the center.
You walk close and see a massive fin poke out.
You peek in and a creepy looking huge Koi stares at you.
It opens its mouth waiting for food.
> throw it in
The Koi wiggles, then leaps into the air to eat the cake.
You can see it's happy, it then grunts, thrashes...
and finally rolls over and poops a magic diamond into the air
at your feet.

--------
Puzzled, you are about to pick up the fish poop diamond when
a bear bearing a load bearing sword walks in.
"Hey! That' my diamond! Where'd you get that!?"
It holds its paw out and looks at you.
> say no
The bear looks shocked.  Nobody ever told a bear
with a broadsword 'no'.  It asks, 
"Is it because it's not a katana?  I could go get one!"
It then runs off and now you notice a big iron gate.
"Where the hell did that come from?" You say.

--------
You walk up to the big iron gate and see there's a handle.
> open it
You open it and you are free!
There are mountains.  And berries! And...
Oh, but then the bear comes with his katana and stabs you.
"Who's laughing now!?  Love this katana."

--------
I have a small puppy that's better at this.
$

Extra Credit

  1. Explain how returning the next room works.
  2. Create more rooms, making the game bigger.
  3. Instead of having each function print itself, learn about "doc comments". See if you can write the room description as doc comments, and change the runner to print them.
  4. Once you have doc comments as the room description, do you need to have the function prompt even? Have the runner prompt the user, and pass that in to each function. You functions should just be if-statements printing the result and returning the next room.
  5. This is actually a small version of something called a "finite state machine". Go read about them. They might not make sense but try anyway.