Your text game will need to figure out what to do each time your player types something. Here are some ways to do that:
1) Linear Game Flow
If you don’t use any control structures, the game will always do the same thing next, regardless of what the player types. This doesn’t meet my definition of “game,” so design something better than this.
(But you might want linear / deterministic game flow in some situations)
2) if / elif / else Branching
You can program your entire game using if / elif / else control structures. You can write hundreds of them and nest them a dozen levels deep, but it will be confusing and awful to work with. You will definitely need if/else logic in your game, but try to avoid nesting more than a level deep or so when possible. Combine if/else with other types of control structures and data structures, described below.
3) for loops
If something is going to repeat a set number of times in your game (three rounds, a set number of levels or enemies, etc), you can repeat all of your code using a simple for loop.
(see for loops with range)
4) while loops
When something needs to repeat indefinitely, or repeat an unknown number of times depending on player actions and game state, use while loops.
Set your condition so that the while loop ends when it’s supposed to.
Use the break command to stop the while loop mid-iteration.
5) Functions
Breaking your game into chunks using functions makes it easier to modify your code and add more features and game content. It also lets you think about a smaller portion of your code at a time as you improve and fix the code.
You can have functions for different types of events that happen in the game, or for different areas of the game.
Functions can call each other, and functions can be called within a loop.
If a player choice or game state will cause multiple things to happen, you can bundle those things into a function to make the high level flow of your code easier to understand and modify.
Here’s a basic example program layout that uses functions and a while loop:
import random def intro(): print('Welcome to this game!') def battle(): if random.random() < 0.50: print('Player wins a battle!') player['score'] += 50 else: print('Player has died!') print('GAME OVER') player['dead'] = True def status(): print(f'Your score is {player["score"]}.') player = {'score': 0, 'dead': False} intro() while player['score']<100 and not player['dead']: battle() status() if player['score'] >= 100: print('YOU WIN')
Notice that the main while loop at the end is quite short. You could make battle()
and status()
much more complicated and interesting, and the main loop could still be the same simple code.
Also notice that the code is structured in the following order. You should follow this pattern:
- Imports
- Function definitions
- Create variables / lists / dictionaries / objects
- Game code
The first three categories in this list are setup code, and then the last part is where the action starts. This is a modular setup that lets you focus on programming individual parts separately.
6) Main game while loop with if/elif/else and functions
If the game consists of rounds where the player does one action per round and gets one response, this can be a good pattern to follow.
The main loop is a while loop that ends when the player quits or wins or loses the game.
Inside the loop, you ask the player exactly once for their user input.
After the input, the code decides how to respond using if/elif/else branches. Then the main loop repeats.
Within each if/elif/else branch, call a function. This makes the main loop short and easy to understand, and you can think about the other options individually when it’s time to program those pieces of the game.
while score < 100: a = input('What is your next action?\n') if a=='quit': break elif a=='help': game_help(): elif a=='travel': travel() else: print('Command unrecognized.')
This ensures that exactly one result will happen every time the user enters a command, and the game will continue indefinitely until the user wins (score of 100+) or quits. In the example above, you would of course need to define score
, game_help()
and travel()
.
7) Functions that call each other
For a “Choose Your Own Adventure” style game, you can make a function for each event. Then send the player to a different function depending on their choice.
def city(): print('You are in a city.') a = input('Where to next?\n') if a == 'forest': return forest() else: return city() def forest(): print('You are in a forest.') a = input('Where to next?\n') if a == 'city': return city() else: return forest() city()
Each function starts by printing some text to describe what happens next, then it asks them to make a choice.
Collect user input, then use if/elif/else to decide which function to call next.
In some cases, the function can call itself to repeat the same area if needed.
Use a while loop to make sure a user picks one of the valid choices, or you can have the function call itself in the else branch for a similar effect (a function calling itself is recursive).
You can run into a “cyclomatic complexity” warning with this type of game, because eventually you have a lots of function calls within function calls within function calls.
You can try to get around this by doing something like return forest()
instead of just forest()
when you jump from on function to the next. Adding the return closes out the previous function.
8) Function Dictionary instead of if/elif/else
Here’s a way to dramatically simplify and shorten your code if you’re using if/elif/else with functions.
Store the names of functions in a list according to the player text commands that are used to activate those functions. This gets rid of most of the if/elif/else code and still accomplishes the same result.
actions = { 'move': move, 'shop': shopping, 'help': game_help } while True: a = input('Enter command:\n') if a in actions: actions[a]() else: print('Command unrecognized.')
In the actions
dictionary, move
,shopping
and game_help
are function names that would be defined previously (notice that no parentheses are included – only the names).
The command actions[a]()
looks up the value a
(a string) in the actions
dictionary, which returns the function stored there. The parentheses ()
at the end of the command call that function.
Compared to the previous example (“Functions that call each other”), this type of setup requires you to design a more complicated system up front, but then you don’t have to repeatedly write the same type of if/elif/else logic for every area. So it ends up being faster and easier to add more game areas and functions later on in the project.
Also, if you want to change the way all of the game areas work in the code, you would only have to make that change in one place.