Rob Cook


Home | Donate

Rock, paper, scissors, code! - part three


2020-04-07

Making improvements

The top item on our backlog is to fix the bug caused by an invalid player choice. Let's remind ourselves of the code we use to get the choice.


# Get player choice.
input_str = input ("1 rock, 2 paper, 3 scissors? ")
player_choice = int(input_str)

We get the input from the keyboard with the first line, and turn it into an integer with the second. The important thing to note is the input() function simply reads whatever is typed on the keyboard until the return key is pressed. Our message prompts for a number from 1 to 3, but the player is free to type in anything.

So what kinds of input could we end up with in our input_str variable?

There are two types of bad input we want to avoid here: a number outside the range we want, and something that isn't a number at all. Ideally our program should keep asking the player for their choice until we get something we can use. Let's write out an algorithm that describes this process.

An algorithm for getting the player's choice

  1. Prompt for player input from the keyboard and assign it to input_str.
  2. If input_str is a number, convert it and assign to player_choice as before.
  3. If input_str is not a number, assign zero to player_choice.
  4. Repeat these steps while player_choice is not 1, 2, or 3.

It might not be obvious why we need step three. If something other than a number is added, we know this is invalid input. Setting the player_choice variable to zero makes our check in step four simpler. We only have to check if the number is outside the range we want. (Don't worry, this will make more sense when you see the code.)

To implement this in code we need to know two new things in Python.

The first can be done with a function called isnumeric(). The second requires us to learn about looping, which is the term programmers use for running some code multiple times.

Asking variables questions

Some variables allow us to call functions on them. Functions called in this way are known as methods. Let's see an example.


my_str = "3"
my_str.isnumeric() # This is True.
my_other_str = "h"
my_other_str.isnumeric() # This is False.

For now it is enough to know that methods exist on some types of variables, and you call them by putting a dot . after the variable name, followed by the method name and it's arguments. Using the isnumeric() method our code for getting the player's choice now looks as follows.


# Get player choice.
player_choice = 0
input_str = input ("1 rock, 2 paper, 3 scissors? ")
if input_str.isnumeric():
    player_choice = int(input_str)

Task

Find the following lines of code in your program, and replace them with the new code given above for getting the player's choice.


# Get player choice.
input_str = input ("1 rock, 2 paper, 3 scissors? ")
player_choice = int(input_str)

Repeating code with while

We are almost finished with our improved way of getting the player's choice. The last piece of the puzzle is for our program to keep asking the player until their choice is a valid one.

Python offers multiple ways to repeat blocks of code. What we would like to say here is "while the player's choice is outside the range 1 to 3, ask them for their input again". The key word here is while, and Python has a while loop that does just what we need.

Using a while loop our code to get the player's choice now looks as follows.


# Get player choice.
player_choice = 0
while player_choice < 1 or player_choice > 3:
    input_str = input ("1 rock, 2 paper, 3 scissors? ")
    if input_str.isnumeric():
        player_choice = int(input_str)

Note the new line starting with the word while. This introduces the loop. Immediately afterwards we give two conditions, joined with an or. This code will keep running while the player_choice variable is less than 1 or greater than 3. In other words outside our range of 1 to 3.

Task

Change your program again, this time adding in the line with the while loop on it. Make sure you add it in the correct place, immediately after we set player_choice = 0. Pay attention to the indentation of lines. Note how the lines under the while are indented with a tab, and the line under the if statement is indented with two tabs. This spacing is important to Python.

Writing our own function

Programmers are lazy (in the best possible way) and like to save time and effort where they can. Writing our own functions lets us use the same code in many places in a program, without having to write it all again.

Defining a function is done with def. Here is an example that defines a function called say().


def say():
    print("Inside our own function!")
    print("Time to leave.")

Note that Python does not run the code inside the function until we call it. The def merely defines the function.

We can call this function in the same way we do print() and other built-in ones. Here is part of a program that uses the say() function.


print("Outside the function.")
say()
print("Outside again.")

The above program would output:


Outside the function.
Inside our own function!
Time to leave.
Outside again.

We can also define functions that expect arguments. These act as input to the function, and are assigned to variables only it can use. To write a function like this we need to add variable names to the function definition. We can then use these variables inside the function.


def say(message):
    print("The message is:")
    print(message)

When defining functions in this way, we say that the list of variables given in the definition are the function's parameters. A function defined with parameters must be given arguments for them when called. Let's see that in action in a complete program.


#!/bin/python3

def say(message):
    print("The message is:")
    print(message)

say("hi there!")
say("goodbye...")

If you were to run this program you would see the following output.


The message is:
hi there!
The message is:
goodbye...

Finally we can also return values from a function. This is done with the return statement. Here is a program that defines a function to add two numbers and return the result to the caller.


#!/bin/python3

def add(a, b):
    total = a + b
    return total

print("10 + 20 =", end=" ")
result = add(10,20)
print(result)
print("5 + 6 =", end=" ")
result = add(5,6)
print(result)

When run we see the output.


10 + 20 = 30
5 + 6 = 11

Writing the choice to word function

Now we know how to write a function that takes input in the form of arguments and returns a result, we can implement our choice_to_word() function. Recall that this function will greatly reduce the code we need in our program to print out the player and computer choices to screen.

Our function will take a single parameter called choice, which is a number from 1 to 3. It will return the word relating to that number in our game as a string (one of Rock, Paper, or Scissors).


# Map the numbers 1,2,3 to the words Rock, Paper, Scissors. 
def choice_to_word(choice):
    if choice == 1:
        word = "Rock"
    elif choice == 2:
        word = "Paper"
    else:
        word = "Scissors"
    return word

Task

Add the definition for the choice_to_word() to your program. Functions must be defined before they are used. Therefore it is good practice to place function definitions at the top of your code. Add the function definition after the comments you wrote outlining our algorithm, and before the line starting import random.

Now we can replace all of this code...


# Show player choice.
if player_choice == 1:
    print("rock", end=" ")
elif player_choice == 2:
    print("paper", end=" ")
else:
    print("scissors", end=" ")
# Show computer choice.
if comp_choice == 1:
    print("vs rock")
elif comp_choice == 2:
    print("vs paper")
else:
    print("vs scissors")

...with these lines.


# Show player and computer choice.
player_word = choice_to_word(player_choice)
comp_word = choice_to_word(comp_choice)
print(player_word, "vs", comp_word)

Task

Delete the old code for printing out the player and computer choices, and replace it with the lines that use our new function. Test your program to see that it still prints out the correct choices for a game. Remember to follow the debugging steps if you have errors.

Play again?

The final task remaining on our backlog is to make the game playable many times. It would be nice if, after a game, we ask the player whether they want to play again. This will require two changes to our program: a prompt to get player input, and another while loop to make the game code keep running.

We have already seen how a while loop can be used to repeat a block of code. If we wrap all our code for playing the game in such a loop, we can play multiple times.

Task

After the line import random in your program, add the following two lines of code. Note the colon : at the end of the second line.


playing = True
while playing:

Task

Indent the start of every line that comes after the while playing: line with an extra tab. Your code should look as follows.


#!/bin/python3

# 1. Make the computer pick a random choice between rock, paper, or scissors.
# 2. Remember the choice in a variable.
# 3. Ask the player to choose either rock, paper, or scissors.
# 4. Remember the choice in a variable.
# 5. Print both choices on screen - for example `rock vs scissors`.
# 6. Compare the two variables to see who has won, or if the game is a draw.
# 7. Print out a message to say if the player won, drew, or lost the game.

# Map the numbers 1,2,3 to the words Rock, Paper, Scissors. 
def choice_to_word(choice):
    if choice == 1:
        word = "Rock"
    elif choice == 2:
        word = "Paper"
    else:
        word = "Scissors"
    return word

import random
playing = True
while playing:
    # Get computer choice.
    comp_choice = random.randint(1,3)
    # ... rest of lines are also indented ...

After we have printed out the message telling the player the outcome of the game, we can use the input() function to ask if they want to play again. Inputting anything other than a y for yes will cause our game loop to stop. We do this by setting the playing variable to False.


# Ask player for another game.
again = input("Play again? ")
if again != "y":
    playing = False

The next time the while loop evaluates it's condition it will be false. Python will run whatever code comes immediately after the block associated with the loop. Let's add a goodbye message to the very end of our program.


# Say goodbye.
print("Thank you for playing!")

Task

Add the code for the play again prompt and goodbye message to your program. Remember that the play again prompt is part of the while loop block, so must be indented. The goodbye message is outside the loop, so doesn't require indentation.

Complete code listing


#!/bin/python3

# 1. Make the computer pick a random choice between rock, paper, or scissors.
# 2. Remember the choice in a variable.
# 3. Ask the player to choose either rock, paper, or scissors.
# 4. Remember the choice in a variable.
# 5. Print both choices on screen - for example `rock vs scissors`.
# 6. Compare the two variables to see who has won, or if the game is a draw.
# 7. Print out a message to say if the player won, drew, or lost the game.

# Map the numbers 1,2,3 to the words Rock, Paper, Scissors. 
def choice_to_word(choice):
    if choice == 1:
        word = "Rock"
    elif choice == 2:
        word = "Paper"
    else:
        word = "Scissors"
    return word

import random
playing = True
while playing:
  # Get computer choice.
  comp_choice = random.randint(1,3)
  # Get player choice.
  player_choice = 0
  while player_choice < 1 or player_choice > 3:
      input_str = input ("1 rock, 2 paper, 3 scissors? ")
      if input_str.isnumeric():
          player_choice = int(input_str)
  # Show player and computer choice.
  player_word = choice_to_word(player_choice)
  comp_word = choice_to_word(comp_choice)
  print(player_word, "vs", comp_word)
  # Determine who won.
  if player_choice == comp_choice:
      print("A draw.")
  elif player_choice == 1 and comp_choice == 3:
      print("Rock blunts scissors. You win!")
  elif player_choice == 2 and comp_choice == 1:
      print("Paper wraps rock. You win!")
  elif player_choice == 3 and comp_choice == 2:
      print("Scissors cut paper. You win!")
  else:
      print("You lose :(")
  # Ask player for another game.
  again = input("Play again? ")
  if again != "y":
      playing = False
# Say goodbye.
print("Thank you for playing!")

That's it! We have tackled all the items on our backlog for improving our game. Our game now handles invalid input from the player, and lets us play for as long as we want to. Take some time to study the final code listing and make sure you are happy with how it all works. Well done.

end