5
\$\begingroup\$

I'm new to programming, became interested in learning a while ago and only got to it recently. I'm working on a very simple text-based adventure game using Python, and would like some tips on simplifying my code. Any other tips are also welcome. Please don't criticize the storyline though, since this is not the point of this post.

def prompt():
    x = raw_input("What should you do? ")
    return x

def cpuprompt1():
    x = raw_input("Enter a website or [exit].")
    return x

def cpuprompt2():
    x = raw_input("NAME/PASSWORD please. Type [exit] to exit.")
    return x

def trailerpk1():
    print "You're in the trailer park."
    print "You have your [door] close to you and an unknown [stoner] standing next to a car."

    command3 = prompt()

    if command3 == "stoner":
        print "[STONER]: Hey! Want to take a hit?"
        print "[YOU]: Would love to!"
        print "You take a hit. Some dank shit."
        print "You start seeing things... some weird things..."
        print "www.plan7286.org"

        trailerpk2()

        command5 = prompt()

        if command5 == "door":
            home1()

    elif command3 == "door":
        home()

def trailerpk2():
    print "What was that?!"
    print "You have your [door] close to you."

def trailerpk3():
    print "You're in the trailer park. Nothing too interesting here."
    print "Your [door] is right next to you. You should probably go home."

def trailerpk4():
    print "You're in the trailer park. I should probably take one more hit with that [stoner] over there."

    command2 = prompt()

    if command2 == "stoner":
        stoner1()

def trailerpk5():
    print "The stoner is gone. You should probably head to that [lake]!"

    command2 = prompt()

    if command2 == "lake":
        lake1()

def trailerpk6():
    print "You're at the trailer park. The stoner is gone."
    print "You can either go to the [lake] or [home]"

    command2 = prompt()

    if command2 == "lake":
        lake2()

    elif command2 == "home":
        home3()

def trailerpk7():
    print "You're at the trailer park. You can either go to the [lake] or [home]"

    command2 = prompt()

    if command2 == "lake":
        lake3()

    elif command2 == "home":
        home4()

def lake1():
    print "You're at the lake. Beautiful. That [cottage] really is there!"

    command2 = prompt()

    if command2 == "cottage":
        cottage1()

def lake2():
    print "You're at the lake. It's so beautiful! You can either go to the [trailer park] or [downtown]"

    command2 = prompt()

    if command2 == "trailer park":
        trailerpk6()

    elif command2 == "downtown":
        downtown1()

def lake3():
    print "You're at the lake. It's so beautiful! You can either go to the [trailer park], [downtown], or to the [cottage]"

    command2 = prompt()

    if command2 == "trailer park":
        trailerpk7()

    elif command2 == "downtown":
        downtown2()

    elif command2 == "cottage":
        cottage2()

def cottage1():
    print "This place looks creepy."
    print "You go inside. There is a [desk] and a [trash bin] nearby."

    command2 = prompt()

    if command2 == "desk":
       print "A regular desk. There is a ripped piece of paper that reads 'go7286'"
       print "Go check out that [trash bin]!"

       command4 = prompt()

       if command4 == "trash bin":
           print "There is a ripped piece of paper that reads 'indi'"
           print "Weird... Well, better [leave] this place now."

           command7 = prompt()

           if command7 == "leave":
               lake2()

    elif command2 == "trash bin":
        print "There is a ripped piece of paper that reads 'indi'"
        print "Go check out that [desk]!"

        command6 = prompt()

        if command6 == "desk":
            print "A regular desk. There is a ripped piece of paper that reads 'go7286'"
            print "Weird... Well, better [leave] this place now."

            command8 = prompt()

            if command8 == "leave":
                lake2()

def cottage2():
    print "You take a peek through the door..."
    print "That Todd Henley guy is here! He lives here!"
    lake3()

def downtown1():
    print "You're downtown. There is a [bar] and a [shop]. You can also go back to the [lake]"

    command2 = prompt()

    if command2 == "bar":
        bar1()

    elif command2 == "shop":
        shop1()

    elif command2 == "lake":
        lake2()

def downtown2():
    print "You're downtown. There is a [bar] and a [shop]. You can also go back to the [lake]"

    command2 = prompt()

    if command2 == "bar":
        bar2()

    elif command2 == "shop":
        shop2()

    elif command2 == "lake":
        lake3()

def bar1():
    print "You walk inside the bar. There is a lonely [man] sitting in the counter. You can also [leave]"

    command2 = prompt()

    if command2 == "man":
        print "[MAN]: Hey, I love your shirt! Indigo is definitely my favorite color."
        print "[YOU]: Oh, thanks... I'm Hautch, what's your name?"
        print "[MAN]: Todd Henley. Nice to meet you!"
        print "[YOU]: So, what do you work with?"
        print "[MAN]: I can't talk about that."
        print "[YOU]: OK... See ya!"
        downtown2()

    elif command2 == "leave":
        downtown1()

def bar2():
    print "You walk inside the bar. That Todd Henley dude isn't here anymore..."
    print "Guess he really didn't like my question."
    downtown2()

def shop1():
    print "OUT OF ORDER"
    downtown1()

def shop2():
    print "OUT OF ORDER"
    downtown2()

def stoner1():
    print "[STONER]: Hey dude! want another hit? Here ya go!"
    print "You take another hit."
    print "You start to see some things... there is a cottage."
    print "It doesn't look familiar, but it sits right next to a lake nearby."
    print "[STONER]: Good shit, huh? I have to go, it was nice meeting you!"

    trailerpk5()

def computer():
    print "Your computer. Many hours spent here."

    computercmd1 = cpuprompt1()

    if computercmd1 == "www.plan7286.org":
        print "Welcome to the official Plan 7286 website."

        computercmd2 = cpuprompt2()

        if computercmd2 == "Todd Henley/indigo7286":
            print "Welcome, Todd."
            print "36.444890, -116.688752"

        elif computercmd2 == "exit":
            home()

    elif computercmd1 == "exit":
        home()

def computer1():
    print "Your computer. Many hours spent here."

    computercmd1 = cpuprompt1()

    if computercmd1 == "www.plan7286.org":
        print "Welcome to the official Plan 7286 website."

        computercmd2 = cpuprompt2()

        if computercmd2 == "Todd Henley/indigo7286":
            print "Welcome, Todd."
            print "36.444890, -116.688752"

        elif computercmd2 == "exit":
            home2()

    elif computercmd1 == "exit":
        home1()

def computer2():
    print "Your computer. Many hours spent here."

    computercmd1 = cpuprompt1()

    if computercmd1 == "www.plan7286.org":
        print "Welcome to the official Plan 7286 website."

        computercmd2 = cpuprompt2()

        if computercmd2 == "Todd Henley/indigo7286":
            print "Welcome, Todd."
            print "36.444890, -116.688752"

        elif computercmd2 == "exit":
            home3()

    elif computercmd1 == "exit":
        home3()

def computer3():
    print "Your computer. Many hours spent here."

    computercmd1 = cpuprompt1()

    if computercmd1 == "www.plan7286.org":
        print "Welcome to the official Plan 7286 website."

        computercmd2 = cpuprompt2()

        if computercmd2 == "Todd Henley/indigo7286":
            print "Welcome, Todd."
            print "36.444890, -116.688752"
            print "Coordinates... I should probably [go] to this place."

            command2 = prompt()

            if command2 == "go":
                desert1()

        elif computercmd2 == "exit":
            home4()

    elif computercmd1 == "exit":
        home4()

def desert1():
    print "It was a long trip, but you made it here."
    print "There is a [tent] in the middle of the desert..."

    command2 = prompt()

    if command2 == "tent":
        tent1()

def tent1():
    print "A tiny tent... there is no one inside, but there is some high tech equipment."
    print "There is a [button]."

    command2 = prompt()

    if command2 == "button":
        button1()

def button1():
    print "You press the button."
    print "Our universe is back to normal!"
    print "Thanks for pressing that button, stranger. Our universe can now go back to normal life instead of living as a simulation."

def home():
    print "You're home."
    print "You have your [door] to the left, and your [computer] to your right."

    command6 = prompt()

    if command6 == "computer":
        computer()

    elif command6 == "door":
        trailerpk1()

def home1():
    print "You're home."
    print "You have your [door] to the left, and your [computer] to your right."

    command6 = prompt()

    if command6 == "computer":
        computer1()

    elif command6 == "door":
        trailerpk3()

        command6 = prompt()

        if command6 == "door":
            home1()

def home2():
    print "You're home."
    print "You have your [door] to the left."

    command2 = prompt()

    if command2 == "door":
        trailerpk4()

def home3():
    print "You're back home. You have your [door] to the left and your [computer] to the right."

    command2 = prompt()

    if command2 == "door":
        trailerpk6()

    elif command2 == "computer":
        computer2()

def home4():
    print "You're back home. You have your [door] to the left and your [computer] to the right."

    command2 = prompt()

    if command2 == "door":
        trailerpk7()

    elif command2 == "computer":
        computer3()

print "You're in your small trailer park home"
print "It's small, but you've grown to like it."
print "You have your [door] to the left, and your [computer] to your right."
command1 = prompt()

if command1 == "door":

    trailerpk1()

elif command1 == "computer":
    computer()
\$\endgroup\$
  • \$\begingroup\$ [criticism of story] and with that out of the way, welcome to Code Review! I don't know nearly enough about Python to give a good answer, but someone here does, probably. \$\endgroup\$ – Nic Hartley Nov 12 '15 at 1:25
10
\$\begingroup\$

Prompt

A few mistakes made while prompting the user for an action:

  1. You use an intermediate variable but don't make anything out of it before returning it;
  2. Your prompt, cpuprompt1, and cpuprompt2 are very similar: you could simplify the overall design by using a default argument in prompt;
  3. You never check for user input and assume it will always fit the proposed choices: what if the user inputs 'home' in trailerpk5? Or 'Door' at the very beginning? It’s the end of story without notice.

The first thing you can do to improve that is redefine prompt as such:

def prompt(message="What should you do? "):
    answer = raw_input(message)
    stripped = answer.strip()
    lowered = stripped.lower()
    return lowered

You can eliminate the need for intermediate variables by chaining operators:

def prompt(message="What should you do? "):
    return raw_input(message).strip().lower()

You can then call it using prompt() as you already do or using prompt("Enter a website or [exit]. ") or prompt("NAME/PASSWORD please. Type [exit] to exit. ") to replace cpuprompt1 and cpuprompt2.

Next you’ll want to check if the action makes sense. You can either add an else clause in the caller so that inputing 'stone' in trailerpk1 (for instance) will execute it again, or you can handle it in prompt by using variable-length arguments:

def prompt(*args, message="What should you do? "):
    answer = raw_input(message).strip().lower()
    if args:
        # Check that the user inputed something useful
        while answer not in args:
            print "Unknown action, try again."
            answer = raw_input(message).strip().lower()
    return answer

You can then use it like so, for instance:

def trailerpk7():
    print "You're at the trailer park. You can either go to the [lake] or [home]"
    action = prompt('lake', 'home')

    if command2 == "lake":
        lake3()
    else: # We know that nothing else than 'lake' or 'home' has been inputed
        home4()

or like:

def computer():
    print "Your computer. Many hours spent here."

    computercmd1 = cpuprompt1("www.plan7286.org", "exit", message="Enter a website or [exit]. ")

    if computercmd1 == "www.plan7286.org":
        print "Welcome to the official Plan 7286 website."

        computercmd2 = cpuprompt2("Todd Henley/indigo7286", "exit", message="NAME/PASSWORD please. Type [exit] to exit. ")

        if computercmd2 == "Todd Henley/indigo7286":
            print "Welcome, Todd."
            print "36.444890, -116.688752"
        else:
            home()
    else:
        home()

But, as you can see, there are quite some repetitions involved here. Let’s try something better.

On recursion and expanding your story

One of the main drawback of your code, beside user input, is that it’s hard to follow the path of the story. Worse, when you’ll want to expand some part of the story you might have to spend quite some time to figure out (again) the interactions between methods.

The last thing to note is that you’re using functions calls within functions calls within functions calls… and if your story grows bigger and bigger, you may end up with some users getting a RecursionError: maximum recursion depth exceeded.

One way to handle that (and remove the redundancy we saw above) is to use dictionaries. They allow you to map things to other things. In your case, you can map places to actions; and actions to other places. For instance:

MAP = {
    "HOME": {
        "door": "PARK1",
        "computer": "COMPUTER1",
    },
    "PARK1": {
        "door": "HOME",
        "stoner": "PARK2",
    },
    "COMPUTER1": {
        "www.plan7286.org": "COMPUTER2",
        "exit": "HOME",
    },
    "COMPUTER2": {
        "exit": "HOME",
    },
}

I simplified on purpose, but see how close to your functions this code look? The advantage being that you can better see how the different actions lead to different places.

An other advantage appears when checking for valid actions. Since you already have a structure that store them, there is no need for redundancy:

def go_elsewhere(starting_place, message="What should you do? "):
    valid_actions = MAP[starting_place] # grab the content out of the MAP dictionary
    # Note that `valid_actions` is also a dictionary
    while True:
        answer = raw_input(message).strip().lower()
        if answer in valid_actions:
            # if the action actualy exists in the second dictionary, we’re all good
            break
        print "Unknown action, try again."
    # Return the new place associated to the action
    return valid_actions[answer]

You can then have a unique story function that will just loop and print messages related to places:

def story():
    place = "HOME"
    while place != "PARK2": # stop here since we didn't implement after that
        place = go_elsewhere(place)

OK, we’ve got some navigation going on but the story is not explicit. Time for an other dictionnary to map bits of story to places:

STORY = {
    "HOME": {
        "story": """You're home.
You have your [door] to the left, and your [computer] to your right.""",
    },
    "PARK1": {
        "story": """You're in the trailer park.
You have your [door] close to you and an unknown [stoner] standing next to a car.""",
    },
    "COMPUTER1": {
        "story": "Your computer. Many hours spent here.",
        "prompt": "Enter a website or [exit]. "
    },
    "COMPUTER2": {
        "story": "Welcome to the official Plan 7286 website.",
        "prompt": "NAME/PASSWORD please. Type [exit] to exit. ",
    },
}

def description(place):
    bit_of_story = STORY[place]
    print bit_of_story["story"]
    if "prompt" in bit_of_story:
        return bit_of_story["prompt"]
    else:
        return "What should you do? "

def story():
    place = "HOME"
    while place != "PARK2":
        prompt_message = description(place)
        place = go_elsewhere(place, prompt_message)

Last words

  • You might have noted the """ used in some bits of story: they open and close multiline strings.

  • With story, description, and go_elsewhere, you’re pretty much done. All you need to do is to extend the MAP and STORY dictionaries to get your story going.

  • You can print your introduction in story and just call story() to get things started. Even better, you can wrap your call to story into an if __name__ == '__main__': clause. It doesn't add much for your use case but it is good practice anyway: it allows to not execute code directly when importing your file but keep the behaviour unchanged when run from the command-line.

  • You might also need to take into account that some places lead nowhere, like your shops. You can handle it by tweaking a little bit how you call the various functions and by checking the number of available actions one place has to offer. The number of actions can be obtained with len(valid_actions) in go_elsewhere but the most idiomatic way of doing it is to check the truth value of valid_actions directly:

    def go_elsewhere(starting_place, message):
        valid_actions = MAP[starting_place]
        if not valid_actions:
            return None
    
        while True:
            answer = raw_input(message).strip().lower()
            if answer in valid_actions:
                break
            print "Unknown action, try again."
        return valid_actions[answer]
    
    def story():
        place = "HOME"
        while place != "PARK2":
            prompt_message = description(place)
            new_place = go_elsewhere(place, prompt_message)
            if new_place is None:
                place = old_place
            else:
                old_place = place
                place = new_place
    

    For it to work, you will need to define an empty dictionary as actions for such places.

Putting it all together

Expand MAP and STORY together to match your actual code. Change END to whatever place is the end of the story after that. Make it a tuple and adapt the while place != END part if you want to support multiple ends.

MAP = {
    "HOME": {
        "door": "PARK1",
        "computer": "COMPUTER1",
    },
    "PARK1": {
        "door": "HOME",
        "stoner": "PARK2",
    },
    "COMPUTER1": {
        "www.plan7286.org": "COMPUTER2",
        "exit": "HOME",
    },
    "COMPUTER2": {
        "Todd Henley/indigo7286": "COMPUTER3",
        "exit": "HOME",
    },
    "COMPUTER3": {},
}

STORY = {
    "HOME": {
        "story": """You're home.
You have your [door] to the left, and your [computer] to your right.""",
    },
    "PARK1": {
        "story": """You're in the trailer park.
You have your [door] close to you and an unknown [stoner] standing next to a car.""",
    },
    "COMPUTER1": {
        "story": "Your computer. Many hours spent here.",
        "prompt": "Enter a website or [exit]. "
    },
    "COMPUTER2": {
        "story": "Welcome to the official Plan 7286 website.",
        "prompt": "NAME/PASSWORD please. Type [exit] to exit. ",
    },
    "COMPUTER3": {
        "story": """Welcome, Todd.
36.444890, -116.688752""",
    },
}

BEGIN, END = "HOME", "PARK2"
INTRODUCTION = """You're in your small trailer park home
It's small, but you've grown to like it."""
CONCLUSION = "End of Story!"

def description(place):
    bit_of_story = STORY[place]
    print bit_of_story["story"]
    if "prompt" in bit_of_story:
        return bit_of_story["prompt"]
    else:
        return "What should you do? "

def go_elsewhere(starting_place, message):
    valid_actions = MAP[starting_place]
    if not valid_actions:
        return None

    while True:
        answer = raw_input(message).strip().lower()
        if answer in valid_actions:
            break
        print "Unknown action, try again."
    return valid_actions[answer]

def story():
    place = BEGIN
    print INTRODUCTION

    while place != END:
        prompt_message = description(place)
        new_place = go_elsewhere(place, prompt_message)
        if new_place is None:
            place = old_place
        else:
            old_place = place
            place = new_place

    print CONCLUSION

if __name__ == '__main__':
    story()
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.