Skip to content

Instantly share code, notes, and snippets.

@macloo
Created January 30, 2014 01:00

Revisions

  1. macloo created this gist Jan 30, 2014.
    118 changes: 118 additions & 0 deletions ex43_v1.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,118 @@
    # Exercise 43: Basic Object-Oriented Analysis and Design
    # http://learnpythonthehardway.org/book/ex43.html

    # STAGE 1 - this works
    # only classes Map and Engine are really built
    # each Scene child-class can be run
    # start w/ skeleton from Zed, then build it out

    class Scene(object):

    def enter(self):
    print self.name # works
    print self.descrip # works
    # this applies to all classes under Scene, but Zed does it differently

    class Engine(object):

    def __init__(self, scene_map):
    self.scene_map = scene_map
    # gets the game's map from instance "mymap," at bottom of this file

    def play(self):
    current_scene = self.scene_map.opening_scene()
    # see the Map object: runs function named opening_scene()
    # this runs only once
    # this STARTS THE GAME

    # this (below) is supposed to be an infinite loop (but mine is not)
    print "\n--------"
    current_scene.enter()
    # use later:
    # current_scene = self.scene_map.next_scene(next_scene_name)

    class Death(Scene):

    # def enter(self):
    # pass

    name = "You have died!"
    descrip = ''' Your spirit leaves swiftly as your body collapses.'''

    class CentralCorridor(Scene):

    # def enter(self):
    # print "You entered Corridor."

    name = "Central Corridor"
    descrip = ''' A broad passage extends in front of and behind you.'''

    class LaserWeaponArmory(Scene):

    name = "Laser Weapon Armory"
    descrip = ''' Shelves and cases line the walls of this room.
    Weapons of every description fill the shelves and cases.
    There is a digital keypad set into the wall.'''

    class TheBridge(Scene):

    name = "The Bridge"
    descrip = ''' Clearly this is a central command station of the
    spaceship. A wide viewscreen shows the stars against a black
    curtain of empty space.'''

    class EscapePod(Scene):

    name = "Escape Pod"
    descrip = ''' Set into the wall are several closed and locked
    hatch doors. Each one leads to an escape pod.'''

    # Map tells us where we are and where we can go
    # it does not make us move - Engine does that
    class Map(object):

    scenes = {
    'death' : Death(),
    'corridor' : CentralCorridor(),
    'armory' : LaserWeaponArmory(),
    'bridge' : TheBridge(),
    'pod' : EscapePod()
    }
    # above is a dictionary that maps all our scene classes to strings
    # note, we never have to instantiate those classes (why?)

    def __init__(self, start_scene_key):
    self.start_scene_key = start_scene_key
    # above we make a local var named start_scene_key
    # this is a string, same as the arg we passed in ('corridor')
    # start_scene_key remains unchanged throughout the game

    def next_scene(self, scene_name):
    val = Map.scenes.get(scene_name)
    # above is how we get value out of the dictionary named scenes
    return val
    # Zed does not have this return
    # this function can be called repeatedly in the game,
    # unlike opening_scene, which is called only ONCE

    def opening_scene(self):
    return self.next_scene(self.start_scene_key)
    # this function exists only for starting, using the first
    # string we passed in ('corridor')
    # it combines the previous 2 functions and is called only once
    # (called in Engine)

    '''
    The three functions above are so circular, they confuse me. But the logic is this: 1) initialize the Map with a scene, passed in as a string, which gets the class name out of the list, "scenes." 2) That first location is used ONLY by the opening_scene() function, which in turn CALLS the middle function, next_scene(). 3) next_scene() is the function that will run again and again, always pulling the class name out of the list, "scenes."
    Question: Shouldn't the variable current_scene, which live in Engine, instead be here, in Map?
    '''

    print "\nType one of the following words: pod, corridor, bridge, armory, death."
    seed = raw_input("> ")
    # this is for testing only - get the "seed" for the map

    mymap = Map(seed) # instantiate a new Map object w/ one arg
    mygame = Engine(mymap) # instantiate a new Engine object w/ one arg
    mygame.play() # call function from that Engine instance

    176 changes: 176 additions & 0 deletions ex43_v3.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,176 @@
    # Exercise 43: Basic Object-Oriented Analysis and Design
    # http://learnpythonthehardway.org/book/ex43.html

    # STAGE 3 - this works - no errors
    # now I can go from scene to scene & always return to Engine when done
    # infinite loop now running in Engine

    from sys import exit # for quitting the game

    class Scene(object):

    def enter(self):
    print self.name # works
    print self.descrip # works
    # this applies to all classes under Scene, but Zed does it differently
    # seems to me this is more efficient - every scene prints its name & descrip

    class Engine(object):

    def __init__(self, scene_map):
    self.scene_map = scene_map
    # gets the game's map from instance "mymap," at bottom of this file

    def play(self):
    current_scene = self.scene_map.opening_scene()
    # see the Map object: this runs function named opening_scene()
    # this runs only once
    # this STARTS THE GAME

    while True: # infinite loop to run the game
    print "\n--------"
    current_scene.enter() # from Scene
    # note: will throw error if no new scene passed in by next line:
    next_scene_name = current_scene.action()
    # get the name of the next scene from the action() function that
    # runs in the current scene - what it returns
    print "Returned to Engine."
    # this prints after running the current scene
    # it's just a placeholder at this stage

    current_scene = self.scene_map.next_scene(next_scene_name)
    # here we use that val returned by current scene to go to
    # the next scene, running function in Map

    '''
    Note: as long as every action() in every scene returns a string
    that exists in the dictionary "scenes" in Map, the loop above
    will run w/o errors. So any sequence in action() that takes you
    to another room must end with: return 'sometext'
    '''

    class Death(Scene):

    name = "You have died!"
    descrip = ''' Your spirit leaves swiftly as your body collapses.\n'''

    def action(self):
    exit() # this is Death, so game over

    class CentralCorridor(Scene):

    name = "Central Corridor"
    descrip = ''' A broad passage extends in front of and behind you.
    The are doors to your left and right. The is a ladder going up.'''

    def action(self):
    while True:
    response = raw_input("> ")
    if "look" in response:
    print self.descrip
    elif "up" in response:
    return 'bridge'
    elif response != "":
    print "Huh? I didn't understand that."
    else:
    print "Something went wrong ..."
    return 'death'

    class LaserWeaponArmory(Scene):

    name = "Laser Weapon Armory"
    descrip = ''' Shelves and cases line the walls of this room.
    Weapons of every description fill the shelves and cases.
    There is a digital keypad set into the wall.'''

    def action(self):
    while True:
    response = raw_input("> ")
    if "look" in response:
    print self.descrip
    elif response != "":
    print "Huh? I didn't understand that."
    else:
    print "Something went wrong ..."
    return 'death'

    class TheBridge(Scene):

    name = "The Bridge"
    descrip = ''' Clearly this is a central command station of the
    spaceship. A wide viewscreen shows the stars against a black
    curtain of empty space. There is a ladder going down.'''

    def action(self):
    while True:
    response = raw_input("> ")
    if "look" in response:
    print self.descrip
    elif "down" in response:
    return 'corridor'
    elif response != "":
    print "Huh? I didn't understand that."
    else:
    print "Something went wrong ..."
    return 'death'

    class EscapePod(Scene):

    name = "Escape Pod"
    descrip = ''' Set into the wall are several closed and locked
    hatch doors. Each one leads to an escape pod.'''

    def action(self):
    while True:
    response = raw_input("> ")
    if "look" in response:
    print self.descrip
    elif response != "":
    print "Huh? I didn't understand that."
    else:
    print "Something went wrong ..."
    return 'death'

    # Map tells us where we are and where we can go
    # it does not make us move - Engine does that
    class Map(object):

    scenes = {
    'death' : Death(),
    'corridor' : CentralCorridor(),
    'armory' : LaserWeaponArmory(),
    'bridge' : TheBridge(),
    'pod' : EscapePod()
    }
    # above is a dictionary that maps all our scene classes to strings
    # note, we never have to instantiate those classes (why?)

    def __init__(self, start_scene_key):
    self.start_scene_key = start_scene_key
    # above we make a local var named start_scene_key
    # this is a string, same as the arg we passed in ('corridor')
    # start_scene_key remains unchanged throughout the game

    def next_scene(self, scene_name):
    val = Map.scenes.get(scene_name)
    # above is how we get value out of the dictionary named scenes
    return val
    # Zed does not have this return
    # this function can be called repeatedly in the game,
    # unlike opening_scene, which is called only ONCE

    def opening_scene(self):
    return self.next_scene(self.start_scene_key)
    # this function exists only for starting, using the first
    # string we passed in ('corridor')
    # it combines the previous 2 functions and is called only once
    # (called in Engine)

    print "\nType one of the following words: pod, corridor, bridge, armory, death."
    seed = raw_input("> ")
    # this is for testing only - get the "seed" for the map

    mymap = Map(seed) # instantiate a new Map object w/ one arg
    mygame = Engine(mymap) # instantiate a new Engine object w/ one arg
    mygame.play() # call function from that Engine instance

    409 changes: 409 additions & 0 deletions ex43_v7.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,409 @@
    # Exercise 43: Basic Object-Oriented Analysis and Design
    # http://learnpythonthehardway.org/book/ex43.html

    # STAGE 7
    # escape pod complete
    # added timer to announce impending doom like Star Trek auto-destruct

    '''
    Aliens have invaded a space ship and our hero has to go through a maze of rooms defeating them so he can escape into an escape pod to the planet below. The game will be more like a Zork or Adventure type game with text outputs ... The game will involve an engine that runs a map full of rooms or scenes. Each room will print its own description when the player enters it and then tell the engine what room to run next out of the map.
    '''

    from sys import exit # for quitting the game
    from random import randint
    import time # for the countdown

    # I had to make global vars for the bomb because it moves from room to room
    # at least I think I had to do it this way
    # and the timer also needs to be used in more than one room

    bomb_with_hero = False
    bomb_armed = False
    ticks = time.time()



    class Scene(object):

    def enter(self):
    print self.name # works
    print self.descrip # works
    # this applies to all classes under Scene, but Zed does it differently
    # seems to me this is more efficient - every scene prints its name & descrip


    class Engine(object):

    def __init__(self, scene_map):
    self.scene_map = scene_map
    # gets the game's map from instance "mymap," at bottom of this file

    def play(self):
    current_scene = self.scene_map.opening_scene()
    # see the Map object: this runs function named opening_scene()
    # this runs only once
    # this STARTS THE GAME

    while True: # infinite loop to run the game - repeats until exit()
    print "\n--------"
    current_scene.enter() # from Scene

    # note: will throw error if no new scene passed in by next line:
    next_scene_name = current_scene.action()
    # get the name of the next scene from the action() function that
    # runs in the current scene - what it returns

    current_scene = self.scene_map.next_scene(next_scene_name)
    # here we use that val returned by current scene to go to
    # the next scene, running function in Map


    class Bomb(object):

    def __init__(self):
    self.present = True # this allows us to remove the bomb
    # changed this to a global variable at top --
    # self.armed = False # not set to explode yet

    def takebomb(self):
    while True:
    response = raw_input("> ")
    if "case" in response or "open" in response:
    print "You open the case and look inside. Yep. It's a bomb!"
    print "You close the case. It has a convenient handle for carrying."
    elif "take" in response or "pick up" in response:
    print "You pick up the case by its handle. It is not too heavy."
    print "Thank goodness -- because you are not in the best of shape."
    self.present = False
    global bomb_with_hero
    bomb_with_hero = True # now bomb is being carried
    return
    elif "arm" in response or "set" in response:
    print "I don't think you want to do that yet."
    elif "bomb" in response:
    print "Do you want to do something with the bomb?"
    else:
    print "Huh? What?"

    def setbomb(self):
    print "You set the case down and open it. You see a big switch "
    print 'marked "On/Off." It is set to "Off." You flip the switch!'
    global bomb_armed
    bomb_armed = True
    global ticks
    ticks = time.time() # this changes ticks to the time when bomb is set
    print
    print 'The timer now reads "00:00:30."'
    print "I think you'd better hurry to the escape pod!"
    print 'The timer now reads "00:00:29."'
    global bomb_with_hero
    bomb_with_hero = False # now bomb is not being carried
    return


    class Countdown(object):

    def announce(self, basetime):
    nowticks = time.time()
    timeleft = 30 - int(nowticks - basetime)
    global bomb_armed
    if bomb_armed == True and timeleft > 0:
    print 'The ship\'s computer announces: "The explosive device will '
    print 'detonate in %d seconds."' % timeleft
    elif bomb_armed == True:
    print "The ship explodes into a quadrillion pieces! "
    print "Your mortal remains are flung to the far corners of the universe!"
    # return 'death'
    else:
    pass


    class Alien(object):

    def __init__(self):
    self.present = True
    self.stamina = 10

    def report(self, s):
    if s > 8:
    print "The alien is strong! It resists your pathetic attack!"
    elif s > 5:
    print "With a loud grunt, the alien stands firm."
    elif s > 3:
    print "Your attack seems to be having an effect! The alien stumbles!"
    elif s > 0:
    print "The alien is certain to fall soon! It staggers and reels!"
    else:
    print "That's it! The alien is finished!"

    def fight(self, stam, p): # stamina and present
    while p == True:
    response = raw_input("> ")
    # fight scene
    if "hit" in response or "attack" in response:
    less = randint(0, stam)
    stam -= less # subtract random int from stamina
    self.report(stam) # see above
    if stam <= 0:
    p = False
    return p, 'corridor'
    # you end up back in corridor even if fight is on bridge
    # because I am lazy
    else:
    pass
    elif "fight" in response:
    print "Fight how? You have no weapons, silly space traveler!"
    else:
    print "The alien zaps you with its powerful ray gun!"
    return p, 'death' # new, lowered stamina number


    class Death(Scene):

    name = "You have died!"
    descrip = ''' Your spirit leaves swiftly as your body collapses.\n'''

    def action(self):
    exit() # this is Death, so game over
    # the only other exit is in EscapePod()


    class CentralCorridor(Scene):
    '''
    A Gothon (alien) is already standing here. Must be defeated
    before continuing.
    '''
    def __init__(self):
    self.alien = Alien()
    # initialize the corridor scene with an alien present

    name = "Central Corridor"
    descrip = ''' A broad passage extends in front of and behind you.
    The are doors to your left and right. The is a ladder going up.'''

    def action(self):
    # -----
    # shortcut to pod scene - 3 lines
    # global bomb_armed
    # bomb_armed = True
    # self.alien.present = False
    # -----
    if self.alien.present:
    print " An alien is here."
    self.alien.present, location = self.alien.fight(self.alien.stamina, self.alien.present)
    # catch the returns from fight() in Alien -
    # pass in stamina and present, get out present and scene name
    return location
    else:

    while True:
    response = raw_input("> ")
    if "look" in response:
    print self.descrip
    elif "up" in response:
    return 'bridge'
    elif "right" in response:
    return 'armory'
    elif "left" in response:
    return 'pod'
    elif response != "":
    print "Huh? I didn't understand that."
    else:
    print "Something went wrong ..."
    return 'death'


    class LaserWeaponArmory(Scene): # keypad works!
    '''
    This is where the hero gets a neutron bomb to blow up the ship before
    getting to the escape pod. It has a keypad he has to guess the number for.
    '''

    def __init__(self):
    self.doorlocked = True # self.door.locked threw an error
    self.keycode = randint(1, 9) * 111 # 3 of the same number
    self.bomb = Bomb()
    # initialize the armory scene with door locked and bomb here

    name = "Laser Weapon Armory"
    descrip = ''' The door to this room is closed and locked.
    There is a digital keypad set into the wall.'''
    descrip2 = ''' Shelves and cases line the walls of this room.
    Weapons of every description fill the shelves and cases. '''

    def action(self):
    global bomb_with_hero # lets Python know we will use this
    if self.doorlocked == True:
    self.keypad()
    while True: # this works - keypad() returns to here
    response = raw_input("> ")
    if "look" in response:
    print self.descrip
    elif "bomb" in response and self.bomb.present:
    print "Searching the shelves, you discover a small red case."
    print 'On the case is a label: "Neutron Bomb."'
    self.bomb.takebomb()
    elif "bomb" in response and bomb_with_hero == True:
    print "You are carrying the bomb."
    elif "leave" in response or "exit" in response:
    return 'corridor'
    elif response != "":
    print "Huh? I didn't understand that."
    else:
    print "Something went wrong ..."
    return 'death'

    # this should probably not be infinite - probably should have range instead
    # it does not let you out till you get it right
    # added exit & leave as options
    def keypad(self):
    while self.doorlocked == True:
    print "The keypad has 9 buttons with numbers from 1 to 9."
    print "3 numbers must be entered to unlock the door."
    response = raw_input("> ")
    if "leave" in response or "exit" in response:
    return 'corridor'
    elif not response.isdigit() or (int(response) > 999 or int(response) < 100):
    print "That is not a suitable number. Try again."
    elif int(response) == self.keycode:
    self.doorlocked = False
    print "The door slides smoothly and quietly open."
    self.descrip = self.descrip2 # switches the description text
    print self.descrip
    elif int(response) > self.keycode:
    print "That number is too high."
    elif int(response) < self.keycode:
    print "That number is too low."
    else:
    "No good. Try again with 3 numbers."


    class TheBridge(Scene):
    '''
    Another battle scene with a Gothon before the hero can place the bomb here.
    '''
    def __init__(self):
    self.alien = Alien()
    # initialize the corridor scene with an alien present
    # I can't initialize a bomb because one is not here yet
    # exactly the same alien as we saw in corridor

    name = "The Bridge"
    descrip = ''' Clearly this is a central command station of the
    spaceship. A wide viewscreen shows the stars against a black
    curtain of empty space. There is a ladder going down.'''

    def action(self):
    if self.alien.present:
    print " Another alien is here!"
    self.alien.present, location = self.alien.fight(self.alien.stamina, self.alien.present)
    # catch the returns from fight() in Alien - same as fight in
    # corridor
    return location
    else:
    while True:
    response = raw_input("> ")
    if "look" in response:
    print self.descrip
    elif "down" in response:
    return 'corridor'
    elif "bomb" in response and bomb_with_hero == True:
    self.bomb = Bomb() # create a Bomb object here
    self.bomb.setbomb()
    elif "bomb" in response and self.bomb.present:
    print "That bomb is set to blow! Get out!!"
    # the order above is very important
    elif response != "":
    print "Huh? I didn't understand that."
    else:
    print "Something went wrong ..."
    return 'death'

    class EscapePod(Scene):
    '''
    Where the hero escapes, but only after guessing the right escape pod.
    '''
    name = "Escape Pod"
    descrip = ''' Set into the wall are several closed and locked
    hatch doors. Each one leads to an escape pod. The pods are
    numbered 1 through 5.'''

    def action(self):
    podnum = randint(1, 5)
    global ticks
    self.countdown = Countdown()
    while True:
    # bomb timer announcement here
    self.countdown.announce(ticks)
    response = raw_input("> ")
    if "look" in response:
    print self.descrip
    elif "open" in response:
    choice = int(raw_input("Which pod? "))
    if choice == podnum:
    self.pod_eject()
    elif choice > 5:
    print "There are only 5 pods, silly!"
    else:
    print "That hatch seems to be jammed."
    elif "leave" in response or "exit" in response:
    return 'corridor'
    elif response != "":
    print "Huh? I didn't understand that."
    else:
    print "Something went wrong ..."
    return 'death'

    def pod_eject(self):
    print "Ejected! You are safe!"
    global bomb_armed
    if bomb_armed == True:
    print "Your ship explodes in a quadrillion pieces, flinging "
    print "alien body parts to the far corners of the universe!"
    print "Safe in your cozy pod, you fly away to a nice planet.\n"
    exit() # only exits in the game besides Death()
    else:
    print "Um ... did you forget something? The aliens are firing "
    print "torpedoes at you from your own ship! Aaaiiieeee --"
    print "That is the end of you!\n"
    exit() # only exits in the game besides Death()

    # Map tells us where we are and where we can go
    # it does not make us move - Engine does that
    class Map(object):

    scenes = {
    'death' : Death(),
    'corridor' : CentralCorridor(),
    'armory' : LaserWeaponArmory(),
    'bridge' : TheBridge(),
    'pod' : EscapePod()
    }
    # above is a dictionary that maps all our scene classes to strings
    # note, we never have to instantiate those classes (why?)

    def __init__(self, start_scene_key):
    self.start_scene_key = start_scene_key
    # above we make a local var named start_scene_key
    # this is a string, same as the arg we passed in ('corridor')
    # start_scene_key remains unchanged throughout the game

    def next_scene(self, scene_name):
    val = Map.scenes.get(scene_name)
    # above is how we get value out of the dictionary named scenes
    return val
    # Zed does not have this return
    # this function can be called repeatedly in the game,
    # unlike opening_scene, which is called only ONCE

    def opening_scene(self):
    return self.next_scene(self.start_scene_key)
    # this function exists only for starting, using the first
    # string we passed in ('corridor')
    # it combines the previous 2 functions and is called only once
    # (called in Engine)

    mymap = Map('corridor') # instantiate a new Map object w/ one arg
    mygame = Engine(mymap) # instantiate a new Engine object w/ one arg
    mygame.play() # call function from that Engine instance