Ex43 without Map().scenes

After a while, I’ve mastered ex43 and started tinkering with it. I was wondering why we us a dictionary in Map() since it only translates from “english” to a class name. Why not just return the class name directly within each scene? Seems like less code, but I’m sure there’s a reason.

I rewrote my code to do this and it seems to work:

def play(self):
    current_scene = self.scene_map.opening_scene()

    while current_scene != 'end':
        current_scene = current_scene.enter()

    print('Game is over')

An example of a Scene():

class CentralCorridor(Scene):
    def enter(self):
        # print scene content and if player passes, then
        return LaserWeaponArmory()

other scenes removed for post…

class Finished(Scene):

    def enter(self):
        print('winner finished')
        return 'end'

class Map(object):

    def __init__(self):
        self.start_scene = CentralCorridor()
   def opening_scene(self):
       return self.start_scene


a_map = Map()
a_game = Engine(a_map)
a_game.play()

You absolutely can and it will work. But think about programme design and especially if this was a much bigger game, with teams of developers working on different aspects.

You might have a Developer working on the engine and one per room. They might not know full details or how the item they are calling can be interacted with. For example, imagine the LaserWeaponArmory developer needed to include an argument for ‘laser_type’ in the call to decide if you got the ‘BFG’ or not!

return LaserWeaponArmory(laser_type)

You may not know this and your call without the argument would fail with a missing argument exception.

It is a good design to add a level of abstration between and action to do and the object that is requesting/invoking those actions. It makes the design scalable.

A similar example is a menu. You could call the actions (assuming you wrote them) from a menu directly like:

def menu():
        print(""" MENU
                 1. Run
                 2. Hide
                 3. Exit """)

    choices = input("What do you want to do?")
    if choices == 1:
        return run()
    elif choices == 2:
        return hide()
    else:
        return exit()

But adding a layer of abstraction you can separate the user request / code to invoke the action, from the menu implementation, like in the crude/simplified example.

def menu():
    print("""    MENU
                 1. Run
                 2. Hide
                 3. Exit """)

    # a dict of choices to select
    choices = { "1": run, "2": hide, "3": exit }

def usr_action():
    choice = input("What do you want to do? ")
    # take the user input and get the action from the 'choices' dict
    action = choices.get(choice)
    # if in the dict - call that item - literally adding () to it 
    if action:
        action()
    else:
        print( "Not a valid choice.")

So if your menu got much bigger, your usr_action code remains unchanged, even if you had 50 menu items (and the worst game ever!)

I’m sure @zedshaw will provide a much more succinct explanation in about 20 words :slight_smile:

No that’s a great reply. But, there’s two viewpoints in this code that you can view it:

  1. As a “real” professional program.
  2. As something for a student to learn things from.

Perspective #1 would say that my code is bad design and that it’s better to just return the class, OR, you could say that it’s better designed because you can move the map around by just changing 1 dictionary. It just depends on what you’re going for in your “professional” design.

But, I’m not trying to get you to make something professional. The purpose of the exercise is actually #2, and the thing you are trying to grasp is the most evil of all programming designs:

INDIRECTION

Your idea is way better to be honest. If I don’t need to rewire the map, then just return an object and move on with my life.

The problem is, programmers LOVE indirection. That’s where I don’t just return the object. I return a string, that is referenced through a dictionary, that is in a class, that finds the class, that creates the object, and is then stored in another class, that then calls a function, to then return the next chain.

So, with that in mind, I’d say do your change and see how it compares. You’ll find that it is way easier to understand, but less flexible. Then keep in mind that most code out there is like what I wrote, since we know from experience that we’ll have to change things later.

Finally, when you write software you’re better off doing something that is simple and inflexible, then add indirection and flexibility later. Bad designs tend to try for ultimate flexibility in the beginning before they know if they need to have that indirection or not.

Thanks to both of your for the detailed response.

I kinda figured that, but wasn’t thinking that parameters other than the room name may need to be passed to the next room.