ThePythonGameBook

learn Python. Create Games

User Tools

Site Tools


en:resources:people:jens_horst:part1step002

step 002 - adventure game

The code examples below work best if you write them with an program editor like idle. Slowly, the examples will grow into a playable game.

Some examples, so called snippets are only parts of a bigger code example and may not run stand-alone.

By default, all code examples are designed for python3.x. If an separate version for python2.x exist, it is displayed below the python3.x example.

<note tip> For a deeper introduction into python, read those sources:

</note>

Line numbers in code examples are only displayed to make referring more simple. Line numbers are never part of python code and should not be copied into an python editor. If a code example has line numbers, copy and paste the code example without selecting the line numbers, or click on the download link if available. On smaller code samples / snippets, you can hide (fold/unfold) the line numbers with an mouseclick (see picture). Larger code samples/snippets will have both an version with line numbers and below an version without line numbers.

input / output

print

The most basic Python command is the print function, ideal to print something on the screen like a situation description:

download snippet001.py

print("You are Sir Robin, the young knight. Your way is blocked by an angry dragon.")

multi-line output

to include a multi-lined string, encapsulate the string in triple quotation marks (single or double):

download snippet002.py

print ("""What do you do now ?
1...wait
2...fight
3...run away""")

alternatively, you could also write several strings in one print command and python will glue them together:

download: snippet003.py

print("my first line " 
"is made up from "
"several parts")

python will give this output:

my first line is made up from several parts

input

but how to get the players response ? For that, we need the input command or raw_input and a concept called variable.

download: snippet004.py

myanswer = input("press the corresponding number and ENTER:")

Variable

myanswer is a so-called variable. Think of it as an placeholder or conatiner that stores the players choice for later use. Variables can be named like you want, as long as the name follow some rules:

  • case-sensitive but shall begin with an lower case letter.
  • no reserved python keyword
  • do not begin with a number
  • do not contain special characters like the comma, the semicolon, dash, space etc. Underscore is allowed, but has sometimes a special meaning.

In this snippet, I give variables whose name I forgot made up the prefix “my”.

input accept any keyboard input as string. To make the program remember the player's input, I assign the variable myanswer to the string typed in by the player.

variable output

Let's confirm the player of his choice:

download: snippet005.py

print("Your choosed the answer number {0}".format(myanswer))

What does that mean? The 0 inside the curled brackets is the first placeholder for something to put inside the string. In Python, counting generally begins with 0, not with 1. The

.format(myanswer)

after the string means that python will replace the first placeholder with the first argument of the format command (myanswer). Alternatively, if you really want you could also write this like in visual_basic

download: snippet006.py

print("You choose the answer number", mymyanswer)

conditional structures

if elif else

Now let the player know what happens, depending on the choice he made. For that, we need an if / elif / else construct:

download: snippet007.py

if myanswer == "1":
    print ("nothing happens, the dragon also wait")
elif myanswer == "2":
    print ("you fight the dragon. The dragon run away !")
    # next situation...
elif myanswer == "3":
    print ("You run away, but the dragon is faster than you. The dragon eat you. Game Over")
else:
    print ("wrong key pressed")

line by line discussion

if myanswer == "1":

an equal testing is coded by two equal signs( == ). One single equal sign is used for assignment. Remember the myanswer variable above. the value of the variable myanswer is a string (in quotes), even if it contain only numbers.

    print ("nothing happens, the dragon also wait")

the identation (the spaces at the beginning) is required in python after each line ending with an colon.

elif myanswer == "2":

there can be no, one or many elif: statements after an if: statement

    print ("you fight the dragon. The dragon run away !")

| the code block below an elif: or else: statement can have several lines.

    # next situation...

all text following an hash mark ( # ) is considered a comment. Comments are meant to be understood by humans, not by the computer.

else:

there can be no or one else: statement after an if: (or after the last elif:) statemnet <note tip>find out more about this topic:

</note> putting the code snippets 001 until 005 together leads to an very early version of the dragon game:

dragon001.py

download code example 008: dragon001.py

# filename: dragon001.py
print("You are Sir Robin, the young knight. Your way is blocked by an angry dragon.")
print ("""What do you do now ?
1...wait
2...fight
3...run away""")
myanswer = input("press the corresponding number and ENTER:")
print("Your choosed the answer number {0}".format(myanswer))
if myanswer == "1":
    print ("nothing happens, the dragon also wait")
elif myanswer == "2":
    print ("you fight with the dragon.")
    print ("you are now faced with the next situation....")
    # continue the code here later
elif myanswer == "3":
    print ("You run away, but the dragon is faster than you. The dragon eat you. Game Over")
else:
    print ("wrong key pressed")

line by line discussion or why dragon001.py sucks

# filename: python001.py

A comment. Not really needed for the computer, but good for us humans.

print("You are Sir Robin...")

The situation description. Graphic would be better

print ("""What do you do now ?
1...wait
2...fight
3...run away""")

One single print command spanning several (physical) lines makes the code hard to read

myanswer = input("press the .. number...")

The player is asked once for an decision and the corresponding number (a string !) is stored in the variable myanswer. It would be nice to have an “emergency exit” key like <key>q</key> to quit the game at will. Later.

print("Your choosed the answer...."

The player is told what option he selected. This is good for forgetful players or games played with latency (like over the internet). Maybe not so necessary in this game.

if myanswer == "1":

The first if block processes the answer only once. Note that the variable myanswer is a string and must thus be contained in quotes.

    print ("nothing happens...")

Because the previous line ends with a colon <key>:</key> this line must be idented. Bad: The game always end, even if the player choose answer 1

    # continue the code here later

| This is a comment to remind us humans to implement more code here later. If you write only comments after an if:, elif: or else: branch, you must at least write an single pass command. pass does nothing, but allows python to work correctly.

else:
    print ("wrong key pressed")

Missing (yet): It would be nice if after the “wrong key pressed” message the player is asked again to press a key.

loops

It would be nice to have the program ask the player for a choice until he type in a correct number. As the game will have many situation descriptions and choices later, it would be good to include an emergency exit option if the player need to end the game quickly or is bored.

This can be done with a loop. Python knows the while-loop and the for-loop. The for-loop is used to repeat commands a defined number of time. Because i don't know how stupid or persistent the player is with typing in wrong numbers, i need the while-loop.

download: snippet009.py

# --- repeat until the playe type in a correct number (or q) ----
while myanswer not in ["q", "1", "2", "3"]:
    myanswer = input("press q and ENTER to quit or the corresponding number and ENTER:")
# --- end of while loop ----

The identation is necessary after each line ending with an colon. The idented line(s) will be repeated until the value of myanswer is equal to an element in the list. The list is defined by square brackets. <note tip> find out more about this topic:

</note>

The while loop runs as long as the condition (the test) equals True or until the loop is leaved by an break command. Because the program can not test an variable that does not exit, the variable is defined and filled with an “stupid” value beforehand.

Note that by catching all bad answers with the while-loop, the else construct becomes unnecessary.

dragon002.py

download code example 010: dragon002.py

# filename: dragon002.py
print("You are Sir Robin, the young knight. Your way is blocked by an angry dragon.")
print("""What do you do now ?
1...wait
2...fight
3...run away""")
myanswer="something stupid" # assing a value to the variable to make testing it possible
 
while myanswer not in ["q", "1", "2", "3"]: 
    myanswer = input("press q and ENTER to quit or the corresponding number and ENTER:")
 
if myanswer == "1":
    print("nothing happens, the dragon also wait")
    #continue the code here later
elif myanswer == "2":
    print("you fight the dragon. The dragon run away !")
    print("you are now faced with the next situation....")
    # continue the code here later
elif myanswer == "3":
    print("You run away, but the dragon is faster than you.\n"
          "The dragon eat you. Game Over")

test it out, you can now type in “stupid” answers as long as you like, the game will ask you for a correct answer until you give one or press “q”.


line by line discussion or why dragon002.py still suck

# filename: python002.py

A comment. Still not needed for the computer, but good for us humans.

print("You are Sir Robin, ...")

| The situation description. Still lacking graphic !

myanswer="something stupid" # assing...

The variable myanswer is assigned a “stupid” value. This is necessary because the myanswer will be tested soon. Note that the rest of the code line after the <key>#</key> sign is a comment!

 

An empty (!) line. Empty lines sometimes make the code more pretty to look at.

while myanswer not in ["q", "1", "2", "3"]:

The while loop. The condition of this while loop is at the beginning not met, “stupid” is no element of the list [“q”, “1”, “2”, “3”] and thus the condition equals True and python continues with the next line.

    myanswer = input("press q or ...")

Because the previous line ends with an colon, this line is idented. The player has now an “emergency exit” option by pressing <key>q</key> This line will be repeated until the condition in the previous line becomes False, meaning the player is making a reasonable choice by pressing one of the keys listed in the previous line line.

The \n inside the print command is called an escape sequence. This special escape sequence will force python to print a new line. Note that print command spans several (physical) lines. If a print command has several strings, all of them will be glued together by python automatically. This multi-line syntax was coded to make the code more pretty to the human eye. The <key>q</key> key has no elif branch, not even an else branch. The while loop make sure that only keys from the accepted list were typed in by the player. If the player pressed <key>q</key>, no if elif branch is processed and python continues at the next line. Because there is no next line, python exit and the game ends.

The game still does not make much sense to play:

  • if you choose answer 1 (wait) the game ends instead of repeating the the situation
  • if you press <key>q</key> to end the game there is no message like you quit the game
  • if you choose answer 2 there is the game ends instead of leading to a new situation or a more detailed fight.

Let's say that to win the game, the player fight the dragon until the dragon is wounded at the belly and at the tail and at the head. Translated into computer programming language, that means that while the dragon is not wounded in all three spots, the player has to keep fighting. To control if this condition is met, let us assign three variables (belly, tail, head) to the value False and repeat the while loop until all 3 variables become True. If the player want to leave the fight (because he is bored or can not figure out how to win), let him break out of the while loop.

loops: break , else

While loops can have an optional else part after the while statement. The code in this else statement will be executed if the while loop is leaved “correctly” (the condition of the while loop becomes False). A break command ends the current loop immediately, even if the condition of the while loop remain True. If an else part exist after the while loop, it will not be executed if the while loop is leaved by an break command.

<note tip> find out more about this topic:

</note>

dragon003.py

This code example let you fight a dragon: download code example 011: dragon003.py

# filename: dragon003.py
# assign 3 variables to the value False
belly = False # mybelly or belly_wound or dragonbelly etc.
head = False
tail = False
print("You fight the dragon!")
situation2="""Where do you want to attack the dragon?
1...attack the dragon's head
2...attack the dragon's belly
3...attack the dragon's tail
q...quit the game"""
# repeat until all those 3 variables are True
while not (belly and head and tail):
    print(situation2)
    myanswer = input("what do you want to do?")
    if myanswer == "1":
       print("You hit the dragon on the head with your sword!")
       head = True
    elif myanswer == "2":
       print("You inflict an ugly wound on the dragon's belly")
       belly = True
    elif myanswer =="3":
       print("You hack a deep wound in the dragon's tail")
       tail = True
    elif myanswer =="q":
       break # leave the game
else:
    print("The dragon bleeds from head, tail and belly and runs away.\n You are victorious! Congratulation, you have won the game")
print("bye-bye")

line by line discussion or why dragon003.py is not so bad at all:

# filename: dragon003.py

A comment. You know, only for us humans.

belly = False # mybelly....
head = False
tail = False

Alternatively, this three code lines could be condensed into one single code line. While saving space, it makes the code harder to read:snippet013.py

head, belly, tail = False, False, False
situation2="""Where do you...""")

situation2 is a variable holding the text for the print statement later. This makes the code better readable and offers the possibility for text-manipulation later.

while not (belly and head and tail):

The condition for the while loop is written very condensed. If a boolean variable1) is tested, python allows to test the variable directly. Alternatively, instead of:snippet014.py

if belly == True:

you can write:snippet015.py

if belly:

The long form of the while condition in this line would be:snippet016.py

while not (belly==True and tail == True and head == True):

situatoin2 is a text variable, but not a string. Thus, situation2 does not need to be contained in quotes. The value of situation2 is a string, and was assigned with the help of quotes in a previous line.

    if myanswer == "1":

Nothing new: the players response is assigned to the text variable myanswer. Each possible value of myanswer is processed now. Note that the value of myanswer is a string and thus need to be contained in quotes. The testing for equal is coded in python with two equal signs. On of the most popular programming errors with python is to code an equal testing with only one equal sign.

       print("You hit the dragon ...")

Only some gory text description at the moment. Later we will add graphics here, i promise.

       head = True

That's new: we assign the value True to the boolean variable head. Meaning, the dragon is now sufficient hit at the head. Note that only one equal sign is used now. Also Note that True and False both begin with an upper case letter.

    elif myanswer =="q":

dragon002.py had a far superiour input-handling than this example. Only input that made sense was accepted in dragon002.py, whereas this example accepts every input from the player, no matter how stupid.

       break # leave the game

A break command ! This will break out of the current (while) loop and force python to continue at the non-indented line below.

else:

This else block will only be computed by python if the while loop is leaved correctly (condition becoming False), but not when the while loop is leaved by an break command.

    print("The dragon bleeds...\n You...")

Note the escape sequence \n to force python to make a new line.

print("bye-bye")

This last line will always be displayed, no matter how the while loop was leaved.

If you compare dragon002.py with dragon003.py, you should see that both programs have something in common. Something more than the name dragon alone. Detecting things that two different programs (should) have in common is an important skill for any good python programmer. (See → Pattern_recognition).

functions

def and return

Basically, dragon003.py could not only be viewed as an -up to now missing- part of python002.py; both programs also share the task of accepting the players input with the input command. Instead of writing the nearly identical piece of code twice, it makes sense to out-source this code-snippet into a function and call this function (with variable parameters if needed) from different parts of the python program. A function will always return a value. If the return statement is missing the function will return the constant None.

<note tip>find out more about this topic:

</note>

sinppet015.py

Snippet015.py is just a demonstration step for the coming dragon004.py

download snippet017: snippet017.py

#filename: snippet017.py
def getanswer(acceptable=[]):
    """asking the user for an answer. adds "q" to the list of acceptable answers.
    returns the accepted answer or ask again."""
    acceptable.append("q")
    localanswer = "something stupid"  # assign a unacceptable value to localanswer
    while localanswer not in acceptable: 
        localanswer = input("press q and ENTER to quit or the corresponding number and ENTER:")
    return localanswer
 
# --- the program start here ---
print("You are Sir Robin, the young knight. Your way is blocked by an angry dragon.")
print("""What do you do now ?
1...wait
2...fight
3...run away""")
myanswer = getanswer(["1", "2", "3"]) # call the function getanswer, store the return value in myanswer

|


line by line code sinppet discussion

#filename: snippet017.py

A comment. Note that snippets are not meant to run stand-alone and will later be merged into an meaningful code example.

def getanswer(acceptable=[]):

Each function must start with the command def, followed by the function name and a pair of round brackets <key>(</key><key>)</key> Inside the round brackets can be one or more optional parameters. In this example, acceptable is a parameter. If the function call forget to submit an parameter, the function default the parameter to the value right of the equal sign. In this case, the default value for acceptable is an empty list. Remember, lists have the square brackets.

    """asking the user..."""

A function can have an docstring. If the docstring is more than one line long, he need to be contained in triple quotes. The docstring is very helpful for automatic generated helpfiles and help functions of the program editor. Basically, the docstring tells the human reader what the function is supposed to do.

    acceptable.append("q")

Heavy stuff ! Instead of forcing the function call to always insert a <key>q</key> in the argument list of acceptable answers, i instead simply append an <key>q</key> to the acceptable list. This is not necessary, but it is always a good idea to make the function as clever and comfortable as possible instead of reuqiring lots of intellectual effort at each function call.

    localanswer = "something stupid"

All variables have an scope where they are valid. Variables first assigned inside a function “live” only inside this function and are not known outside the function. If you want to make a variable to be known outside the function as well, you need the global command. Because localanswer will soon be used in a test it is necessary to assign an (not acceptable) value to the variable.

    while localanswer not in acceptable:

A while loop (inside the function) with a condition that is True at first: “someting stupid” is most likely not an acceptable answer (an element of the list acceptable), meaning the while condition remains True, and python continues with the next indented line.

        localanswer = input("press...")

Where the player is asked to give input:

    return localanswer

Until his answer is acceptable. This answer will be returned to the function call.

# --- the...

Comment to make it clear that we are not inside a function now. we are in the main program now.

myanswer = getanswer(["1", "2", "3"]) # call..

A lot happens in this single line. Note the comment after the <key>#</key> sign: The variable myanswer is assigned a value. The value is generated from the function getanswer. As soon as getanswer has received an answer from the player, this answer is returned and instantly assigned to the variable myanswer. This is called a function call. Note that the variable localanswer is not visible outside the function scope. The function getanswer is called with an parameter, a list of acceptable answers. Note that q is not in this list, because getanswer adds q to the list independently. This is only a snippet and lots of code is missing here.

putting it all togehter

To merge dragon003.py and dragon002.py into an complete playable dragon004.py it is only necessary to combine the commands learned.

Let's try it out:

dragon004.py

A not very elegant but playable version of the dragon adventure game with two nested while loops: download code example 018: dragon004.py

# filename: dragon004.py
def getanswer(acceptable=["1","2","3"]):
    """asking the user for an answer. adds "q" to the list of acceptable answers.
    returns the accepted answer or ask again."""
    acceptable.append("q")
    localanswer = "something stupid"  # assign a unacceptable value to localanswer
    while localanswer not in acceptable: 
        localanswer = input("press q and ENTER to quit or the corresponding number and ENTER:")
    return localanswer
# --- the program start here ---
# -- assign values to variables
situation1=("You are Sir Robin, the brave knight. Your way is blocked by an angry dragon.")
options1=("""What do you do now ?
1...wait
2...fight
3...run away""")
situation2=("You fight the dragon!")
options2="""Where do you want to attack the dragon?
1...attack the dragon's head
2...attack the dragon's belly
3...attack the dragon's tail
4...retreat away from the dragon
q...quit the game"""
belly = False # the dragon is very healthy yet
head = False
tail = False
gameOver = False 
# --- the main loop ---
print(situation1)
while not gameOver:
    print(options1)
    myanswer = getanswer()
    if myanswer == "q":
        gameOver = True 
    elif myanswer == "1":
        print("nothing happens, the dragon also wait")
        # continue #
    elif myanswer == "2":
        print("You bravely decide to fight the dragon.")
        # ----- dragon fight loop ----
        print(situation2)
        while not (belly and head and tail):
            print(options2)
            myanswer = getanswer(["1","2","3","4"])
            if myanswer == "1":
               print("You hit the dragon on the head with your sword!")
               head = True
            elif myanswer == "2":
               print("You inflict an ugly wound on the dragon's belly")
               belly = True
            elif myanswer =="3":
               print("You hack a deep wound in the dragon's tail")
               tail = True
            elif myanswer =="4":
               print("You carefully retreat some steps away from the dragon")
               print(situation1) #return to the main loop
               break # leave the dragon loop 
            elif myanswer =="q":
               gameOver = True                  
               break # leave the dragon loop and leave main loop
        else:
            # dragon loop was leaved correctly
            print("The dragon bleeds from head, tail and belly and runs away.\n"
                  "You are victorious! Congratulation, you have won the game")
            gameOver = True #leave the dragon loop correctly 
    elif myanswer == "3":
        print("You run away, but the dragon is faster than you.\n"
              "The dragon eat you. Game Over")
        gameOver = True
print("Bye-Bye, thanks for playing")

line by line discussion or why dragon004.py is ugly

# filename: dragon004.py

A comment, as always more meaningful for us humans than for the computer.

def getanswer(acceptable=["1","2","3"]):

The function getanswer is defined here. The default value of the parameter acceptable is set to the most common value, a list containing the numbers (as strings!) from 1 to 3.

# --- the program start here --

The program start here after all funcions (we had only one) are defined

options1=("""What do you ..."""

Multi-lined text variables don't become prettier when defined later in indented code so i define them right here. Note the split between situation description and options for the player.

gameOver = False 

The boolean variable gameOver controls the main loop of the game. The game runs until gameOver becomes True.

print(situation1)

Note that the situation is described before the main loop repeat itself. This helps to make the output less confusing if a player decide to wait several turns.

while not gameOver:

Alternatively, this line could be written: snippet019.py

while not gameOver == True:
    myanswer = getanswer()

Thanks to clever defaulting (in the defination of the function getanswer it is not necessary to pass any parameter to the function at all.

    if myanswer == "q":
        gameOver = True 

The “emergency exit” from the game will leave the main loop and thus the game.

    elif myanswer == "1":
        print("nothing happens...")
        # continue

A single continue statement would return to the beginning of the current loop immediately. This is not necessary here (thus the continue line is out-commented) but it would work.

        while not (belly and head and tail):

While still in the main game loop, python now start a second nested loop, the dragon fighting loop:

            myanswer = getanswer(["1","2","3","4"])

The default parameter of getanswer is [“1”,“2”,“3”]. Because a 4th option (retreat) is possible during the fight, the whole acceptable list must be handed as parameter at the function call.

               print(situation1)

Because the player leave the dragon fight loop and return to the main loop, the description1 is printed again.

               gameOver = True

By setting gameOver to True before breaking out of the dragon loop, the main loop will also be leaved.

        else:
            # dragon loop was leaved correctly
            print("The dragon bleeds...")

This else part will only be computed if the dragon fight loop was leaved correctly (by beating the dragon)

    elif myanswer == "3":
        print("You run away...")

What is that ? it is part of the main loop. For coding beauty, it would make sense to code this branch before the dragon fight branch.

print("Bye-Bye, thanks for playing")

This last line is not part of any loop and will always be printed, no matter how the player leave the game (winning, loosing or quitting)

Congratulation, you now have a playable game. While it is possible to expand the game (coding more decicions, options and nested while loops), the code of the game will soon become very confusing to read (for us humans, not for the computer), with lots of very indented lines and several (nested) while loops. This approach is called procedural programming and can be good described by (a painted) flow chart.

The program (game) logic is like a flow or river computed directly into the code.

However, there exist another program paradigma, the data -driven programming (leading to object orientation). By rebuilding the code of the example above in the next page, i will show you how to put much of the game logic into the game data (such as the text variables situation1, options2 etc.) itself and only maintaining a very small main-loop.


1)
any variable that is either True or False
/var/www/horst/thepythongamebook.com/data/pages/en/resources/people/jens_horst/part1step002.txt · Last modified: 2014/01/09 11:07 (external edit)