Search code examples
pythonpython-3.xmacosadventure

Quitting to Game Over when player dies


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

Solution

  • 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!")