HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Making user menus in a text-based game

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
menususertextgamebasedmaking

Problem

I'm new to Python and programming in general, but I'm making it work. I have this little game I've been working on as a learning exercise and I'm a bit concerned that I'm using far too many if/else statements for the user menus. Is there perhaps a better way?

```
# Bizarro house text adventure game
from sys import exit
import time
#error message for an incorrect choice
def error():
print """
Invalid number
Time rewinds to the beginning of your journey.
"""
start()
#Beginning of the game
def start():
print """
You're standing at the entrance to an old spooky mansion.
It's just after midnight.
What would you like to do?
1. Go inside
2. leave
"""

choice = raw_input("> ")

if choice == "1":
entrance()
elif choice == "2":
dead("This could have been an exciting adventure if you weren't too scared,")
else:
print "Wrong answer try again."
start()
def dead(why):
print why, "better luck next time."
exit(0)
# Group of functions for the downstairs area
def entrance():
print """
There is a room on the left,
a room on the right,
and a kitchen in the back.
Also a set of stairs
Which way?
1. Left
2. Right
3. Kitchen
4. Upstairs
"""

choice = raw_input("> ")
if choice == "1":
left_room()
elif choice == "2":
right_room()
elif choice == "3":
kitchen()
elif choice == "4":
stairwell()
else:
error()

def left_room():
print """
The room is dark.
You hear a voice that says:
'I see you, and so does Jesus'
You use your cell phone for light.
You shine the light around the room.
You find a lightswitch.
You also shine the light on a parrot cage.
The parrot repeats his warning.
'I see you and so does Jesus'
What do you do?
1. Turn on light
2. Leave room
"""

choice = raw_input("> ")
if choice == "1":
d

Solution

Python allows for object oriented programming. Using this would simplify your program immensely. So let's walk through remaking this.

Let's think about what your game consists of. You have locations, options the player can choose from, and actions those options should trigger. In addition, you want to keep track of some state about the player (alive, any items, etc.).

First, we want to define a game state. The state should keep track of our current location, if we are alive, and a map of locations. A few extra functions are added to make it easier later.

class State:
    def __init__(self, starting_loc):
        self.alive = True
        self.location = starting_loc
        self.locations = {}

    def addloc(self, location):
        self.locations[location.name] = location

    def gotoloc(self, locname):
        self.location = self.locations[locname]


Now we want to define a location. Locations have their description and a list of things you can do there. Simple enough, but let's also give them a name, since we want to map their name to them in the State class. We also define a method for getting user input and executing the action if it was good.

class Location:
    def __init__(self, name, desc, options=None):
        self.name = name
        self.desc = desc
        self.options = options

    def start(self):
        print self.desc

    def print_opts(self):
        if(self.options != None):
            for i in range(len(self.options)):
                print "  {0}. {1}".format(i, self.options[i].text)

    def get_choice(self, state):
        choice = raw_input("> ")
        print "You chose \"{0}\"".format(choice)
        try:
            index = int(choice)
            self.options[index].action.execute(state)
            return True
        except Exception as e:
            print(e)
            print("Please choose a valid option")
            return False


Now, what should we put in options? You can see above that I want to be able to get an action from them, and I want to print some text from them, so we define the class like so

class Option:
    def __init__(self, text, action):
        self.text = text
        self.action = action


Now what about actions themselves? They need to define an execute method that takes a state object. So lets define an action for moving and killing the player.

class GoToLocation:
    def __init__(self, location):
        self.loc = location

    def execute(self, state):
        state.gotoloc(self.loc)
        state.location.start()

class KillPlayer:
    def __init__(self, message):
        self.message = message

    def execute(self, state):
        state.alive = False
        print(self.message)


Now let's create a few locations:

start_loc = Location("start",
                     "You're standing at the entrance to a spooky mansion",
                     [Option("Go Inside", GoToLocation("entrance")),
                      Option("Leave", KillPlayer("Scardy Cat"))])

entrance = Location("entrance",
                    """You are standing in the entrance hall.
There is a room on the left, a room on the right, and a kitchen in the back.    
There is also a set of stairs in front of you.""",
                    [Option("Left", KillPlayer("The building collapses")),
                     Option("Right", KillPlayer("The building collapses")),
                     Option("Kitchen", KillPlayer("The building collapses")),
                     Option("Upstairs", KillPlayer("The building collapses")),
                     Option("Outside", GoToLocation("start"))])


And now we can put it all together:

if(__name__=="__main__"):
    s = State(start_loc)
    s.addloc(start_loc)
    s.addloc(entrance)
    s.location.start()
    while(s.alive):
        s.location.print_opts()
        s.location.get_choice(s)


If you want different actions, you can now create a simple class that defines the execute method. So, let's say you wanted a Message action that doesn't do anything but print it's message.

class Message:
    def __init__(self, msg):
        self.msg = msg
    def execute(self, state):
        print self.msg


This class won't change the state of the game but will print the message given.

A class to change what an option does is also possible

class OptionMutator:
    def __init__(self, location, index, newoption):
        self.locname = location
        self.index = index
        self.newoption = newoption
    def execute(self, state):
        loc = state.locations[self.locname]
        if(self.index = len(loc.options)):
            loc.options.append(self.newoption)
        else:
            loc.options[self.index] = self.newoption


How about an action that does multiple actions? Easy!

```
class MultiAction:
def __init__(self, actions=None):
self.actions = actions
def execute(self, state):
if(self.actions == None): return
for action in self.actions:

Code Snippets

class State:
    def __init__(self, starting_loc):
        self.alive = True
        self.location = starting_loc
        self.locations = {}

    def addloc(self, location):
        self.locations[location.name] = location

    def gotoloc(self, locname):
        self.location = self.locations[locname]
class Location:
    def __init__(self, name, desc, options=None):
        self.name = name
        self.desc = desc
        self.options = options

    def start(self):
        print self.desc

    def print_opts(self):
        if(self.options != None):
            for i in range(len(self.options)):
                print "  {0}. {1}".format(i, self.options[i].text)

    def get_choice(self, state):
        choice = raw_input("> ")
        print "You chose \"{0}\"".format(choice)
        try:
            index = int(choice)
            self.options[index].action.execute(state)
            return True
        except Exception as e:
            print(e)
            print("Please choose a valid option")
            return False
class Option:
    def __init__(self, text, action):
        self.text = text
        self.action = action
class GoToLocation:
    def __init__(self, location):
        self.loc = location

    def execute(self, state):
        state.gotoloc(self.loc)
        state.location.start()

class KillPlayer:
    def __init__(self, message):
        self.message = message

    def execute(self, state):
        state.alive = False
        print(self.message)
start_loc = Location("start",
                     "You're standing at the entrance to a spooky mansion",
                     [Option("Go Inside", GoToLocation("entrance")),
                      Option("Leave", KillPlayer("Scardy Cat"))])

entrance = Location("entrance",
                    """You are standing in the entrance hall.
There is a room on the left, a room on the right, and a kitchen in the back.    
There is also a set of stairs in front of you.""",
                    [Option("Left", KillPlayer("The building collapses")),
                     Option("Right", KillPlayer("The building collapses")),
                     Option("Kitchen", KillPlayer("The building collapses")),
                     Option("Upstairs", KillPlayer("The building collapses")),
                     Option("Outside", GoToLocation("start"))])

Context

StackExchange Code Review Q#65305, answer score: 8

Revisions (0)

No revisions yet.