Ex 36: Looking for ways to optimize this part of the code

This is just a skeleton of a larger program that does things in a maze of rooms. I identified that navigating the maze from room to room was the first order of business to knock off before deciding on what would happen in various rooms.

After a bunch of research and false starts and dead ends I came up with something that does the job. But what I’m interested in knowing is if there’s a better way I haven’t considered. For example, I’d like to get rid of that try_entry() function and stick that code in the navigation() function but the problem is I need a way to redo the input if what was input was invalid and breaking that code out into a separate function was all I could think of to accomplish that. I wouldn’t mind not relying on global variables as well but I don’t think I can make this work with return codes as long as I have that separate try_entry() function.

# room you start out in
room = "5"
maze = {}
room_list = []

# defines the maze as a dictionary
maze["1"] = ["two", "four"]
maze["2"] = ["three", "five", "one"]
maze["3"] = ["six", "two"]
maze["4"] = ["five", "seven", "one"]
maze["5"] = ["two", "six", "eight", "four"]
maze["6"] = ["nine", "five", "three"]
maze["7"] = ["eight", "four"]
maze["8"] = ["nine", "seven", "five"]
maze["9"] = ["eight", "six"]

# this is the navigation function used to navigate through the maze
def navigation():
    # room needs to be global because we will modify it
    global room
    global room_list

    # uses the current string in room as the dictiornary key to look for.
    # then assigns the list of values inside to room_list
    room_list = maze[room]

    # we need to know the number of doors stored in the list because
    # the number of doors varies depending on the room
    door_number = len(maze[room])

    print("There are", door_number, "doors here. They are labeled as follows:")
    for door in range(len(maze[room])):
        print(room_list[door - 1])

    print("\nWhich door do you want to try?")

    try_entry()

def try_entry():
    global room
    global room_list

    door_choice = str(input("> "))

    # if the door is one of the available choices from the room's dictionary entry
    if door_choice in room_list:

        # nest of if-statements designed to route you to the function for that
        # specific room
        if door_choice == "one":
            room_one()
        elif door_choice == "two":
            room_two()
        elif door_choice == "three":
            room_three()
        elif door_choice == "four":
            room_four()
        elif door_choice == "five":
            room_five()
        elif door_choice == "six":
            room_six()
        elif door_choice == "seven":
            room_seven()
        elif door_choice == "eight":
            room_eight()
        elif door_choice == "nine":
            room_nine()

    # otherwise you screwed up and entered an invalid room
    else:
        print("That isn't going to work. Try again")
        try_entry()

def room_one():
    global room
    room = "1"
    print("You are in room 1. Now what?")
    navigation()

def room_two():
    global room
    room = "2"
    print("You are in room 2. Now what?")
    navigation()

def room_three():
    global room
    room = "3"
    print("You are in room 3. Now what?")
    navigation()

def room_four():
    global room
    room = "4"
    print("You are in room 4. Now what?")
    navigation()

def room_five():
    global room
    room = "5"
    print("You are in room 5. Now what?")
    navigation()

def room_six():
    global room
    room = "6"
    print("You are in room 6. Now what?")
    navigation()

def room_seven():
    global room
    room = "7"
    print("You are in room 8. Now what?")
    navigation()

def room_eight():
    global room
    room = "8"
    print("You are in room 8. Now what?")
    navigation()

def room_nine():
    global room
    room = "9"
    print("You are in room 9. Now what?")
    navigation()

navigation()

Nice!

Use a while loop. Something like this will work:

choice = input('>')
while choice not in maze[room]:
    # try again
# go on

If there’s a way to do something recursively, you can usually do it with a loop as well.

1 Like

You can also put functions in dicts:

map = {
 "1": room_one,
 "2": room_two,
}

next_room = map["1"]
next_room()

# or make a "wrong_way" function

next_room = map.get("1", wrong_way)
next_room()

# that .get will either get the room, or return wrong_way
# you can then always call it, and have wrong_way() print out your wrong way message
2 Likes

That use of ‘functions in dicts and assigning as a value that is then called’, absolutely blew my mind when I first saw it.

I think when you then explained @zedshaw that the call name (next_room) and the call itself () we’re not one action, my programming world doubled in size!

Yeah…the While loop was the obvious first choice. However after Zed pooh-poohed While loops in previous exercises, I thought going with the While loop would be the lazy choice and I should push myself to look beyond the obvious and broaden my knowledge. But sometimes the most obvious things are the best option.

Uhm, remember, I don’t say Never ever ever use a while loop.

I say, if you’re going through a list of things, don’t use a while loop.

If you’re looping forever, use a while loop. It’s in the book and I even do that in my own game.

In your case, you’re just running a loop until the end of the game, so that’s what I do and you could do that too.

But also, it’s impossible for me to write out all the infinite possible rules, and the “rules” change all the time as programmers come up with weirder things they think make “good code”. You should take what I say more as, “This is generally what you should do, but if you have a reason to not, then just be able to justify it.” Lots of time you’ll come up with things I hadn’t anticipated and then do what works.

1 Like

I know that’s what you meant but I still generally view the While loop as the choice of last resort, not the first resort. Obviously there are scenarios where it is the choice of first resort.

1 Like

One thing that I have noticed, and I am too inexperienced to know if this applies to coding in general, is that if I have a complex nest of if-statements where you only want to allow a couple of actions but need to prevent a bunch of other actions to plug logic holes it seems to me like you need to put all your logic hole blockers at the top of the nest and the end should be reserved for the actions you actually want to succeed…

…otherwise what happens is if you put an if statement in near the top that you want to succeed it could wind up keeping alive a bunch of conditions you want blocked and you’ll have to code twice as hard to get yourself out of it. But if you put your blocked conditions tests at the top of the heap they get caught and killed immediately and allow for the valid conditions you want tested to proceed without complication.

Yes, I’m working with such a situation right now. Spent all day yesterday coding in a complex nested set of if-statements that have to test multiple conditions into my game that I thought had trapped all the logic holes only there was one that was getting through and I realized I’d have to tear apart the code and start over from the ground up because what I had was structured all wrong for what I wanted to do…even though at the time it looked good.

Keeping blocking conditions at the top is a valid strategy.

Also, try to avoid deeply nested conditional statements. If your logic requires them, consider pulling parts of them into separate functions.

1 Like

Well after over a week and a half on Ex 36 and over 1,000 lines of code later, I got something I can show off. Yes, I overthought the code…plugging every logic hole I could come up with (and I came up with lots…particularly the last four hours where I just tried to play the game through to the end). Plus I set up lots of condition branches meaning you can do things at various points in the game and have the outcome be different depending on the conditions.

It’s an adventure game…albeit a “tiny” adventure game. And like any adventure game there are a couple of spots which are insanely hard, even with the hints I dropped in the code. With enough trial and error one should be able to get through. Of course one could always cheat and look at the code to figure out the answers…but that would ruin the fun.

I built this thing so it’s easily expandable if one wants to expand it. I don’t. I didn’t realize just how much I was biting off doing this game this way…particularly when it came to plugging all the logic holes. I learned some things along the way…like I should have shoved all my control variables in a dictionary so that I didn’t have to worry about setting up global variables. In my defense, I wasn’t expecting this many control variables when I started writing the code.

I can play through the game in 5 minutes or so but a newbie is going to be spending time on it just figuring out how the floorplan is laid out…never mind solving the puzzles.

I would post the code to see if anyone can break the game and reveal a logic error. I would like to say I got them all but I’m too smart to say something that stupid.

As I said, I would post the code except it’s too long to fit in this message…lol! In fact I can’t split it in half and have it show up intact. Any ideas?

You can upload it to GitLab or GitLab and share that.

However, if it’s 1000 lines of nested logic, I wouldn’t expect anyone to read it :smiley:

1 Like

Nice. It’s always good to go wayyyyyy too far in studying things, 'cause then you know you can. I’d say toss onto gitlab or github like @gpkesley mentions, and then when you get to automated testing see if you can thrash it yourself.

1 Like
A free service run by Zed A. Shaw for learncodethehardway.org.