So, I'm working on a python text-based game that I can share with my friends. I've got most of the game working, but I'm struggling with the game over portion for when a user selects certain commands. I'm not using pygame for this because I can't find a 64-bit version. Below is what I'm looking at. What do I put in the gameOver function to actually quit the game, or if the player wants, try again?
import time
import random
import sys
def searchAreas():
print("1. Area1")
print("2. Area2")
print("3. Area3")
print("4. Just give up")
def badResponse():
print("Sorry, I don't understand " + search_area)
def gameOver():
print("You decide to give up because life is too hard.")
print("Would you like to try again?")
player_choice = input("> ")
if player_choice == "Yes":
mainGame()
elif player_choice == "No":
print("# what goes here to quit the game?")
else:
badResponse()
def mainGame():
search_area_options = ["1","2","3"]
search_area = ""
while search_area not in search_area_options:
print("Where do you want to start looking?")
searchAreas()
search_area = str(input("> "))
if search_area == "1":
print("Text here")
elif search_area == "2":
print("Text here")
elif search_area == "3":
print("text here")
elif search_area == "4":
gameOver()
else:
badResponse()
mainGame()
When typing anything but the four options, or when going into the gameOver function, I'm seeing this error:
Traceback (most recent call last):
File "./test.py", line 45, in <module>
mainGame()
File "./test.py", line 43, in mainGame
badResponse()
File "./test.py", line 14, in badResponse
print("Sorry, I don't understand " + search_area)
NameError: name 'search_area' is not defined
When designing games, more often than tradidional "backend" Python coding, we find the need for this pattern: from an intern function to "jump" to an outer function.
So in games, it will be common that from a function called from your mainloop you will want to go out of the mainloop and go to a place where the code setup the next game stage, or one that shows a game-over screen, and offers to start a new game.
Python has the "sys.exit" call that stops the program altogether, so, while you might call it from the code checking for the end-of-game condidions, that would quit your came program entirely, and not give the user the option to start a new match. (and if your game is on a graphic UI rather than a console "print and input" game, the already bad experience becomes catastrophic, as the game itself would just suddenly close with no traces).
So, while this could be managed with a "state variable" that could be set by these functions, and managed by the mainloop (in your case, the while
statement in the mainGame
function), that design is tedious and error-prone - it'd be something like:
def mainGame(...):
...
game_over = False
while not game_over:
if search_area not in search_area_options:
game_over = True
...
if search_area == "4":
game_over = True
So, note that with this design, if something changes the "game_over" flag to True,
no matter where, on the next iteration, the "while" condition will fail, and
the program will naturally end the execution of your mainGame
function - and
if there is no outside function handling a "play again?" screen, the program ends.
It is alright, and maybe the correct thing to do for a simple game like this.
But in more complex designs, your options in the main-loop my become more complicated - you can call functions that could implement mini-games on their own, or the checkings thenselves might not be trivial - and, above all, there might be more than a "game over" condition to exit this main function, for example, a "winning" condition that would lead the game for the next stage.
In these cases, instead of book-keeping the game state in variables, you might want to make use of Python's Exception mechanism. Exceptions are a construct in the language that occurs naturally on a program error, which enables the program to either stop, or continue to run in a function "above" the place where the exception took place - if the programmer just includes the correct "try-except" clauses to catch the exception.
So, a complex game can take place, that can handle arbitrarily comples games, and still, through creating good-named exceptions and placing try-except clauses appropriately, it is easy to always know of where the execution will lead to - the skeleton for a more complex game using this strategy could be something along:
# start
class BaseGameException(BaseException): pass
class ProgramExit(BaseGameException): pass
class GameOver(BaseGameException): pass
class MiniGameOver(BaseGameException): pass
class NextStage(BaseGameException): pass
def minigame():
while True:
# code for game-within-game mini game
...
if some_condition_for_winning_main_game_stage:
raise NextStage
...
def main_game(stage):
# get data, scenarios, and variables as appropriate for the current stage
while True:
...
if some_condition_for_minigame:
minigame()
...
if condition_for_death:
raise GameOver
...
def query_play_again():
# print and get messag reponse on whether to play again
...
if player_decided_to_quit:
# This takes execution to outsude the "main_game_menu" function;
raise ProgramExit
def main_game_menu():
"""Handle game start/play again/leatherboard screens"""
stage = 1
while True:
# print welcome message, and prompt for game start
try:
main_game(stage)
except NextStage:
# print congratulations message, and prepare for next stage
stage += 1
except GameOver:
# player died - print proper messages, leatherboard
query_play_again()
stage = 1
# if the program returns here, just let the "while" restart the game
if __name__ == "__main__":
try:
main_game_menu()
except ProgramExit:
print("Goodbye!")