Categories
Begin Python

Making a Rock, Paper, Scissors game in Python

Before you code, figure out what you’re trying to do with the code
A brief note on code
Using comments in code
Expanding shorthand logic
Considering another approach
No one right way to approach problems
Let’s actually start writing some code
Don’t trust user input
Picking a choice at random
Comparing values with if/elif/else
Extra Credit
What if we want to keep playing and not have it start over again all the time?
Debugging (finding/fixing) Errors
Super extra credit
Still other approaches
Whoa! That was a lot!

Before you code, figure out what you’re trying to do with the code

Let’s create a game. In real life, it works like this: there are two players—each player simultaneously picks one of three options: rock, paper, or scissors. If the two players pick rock and paper, paper wins (presumably by enveloping the rock?). If the two players pick paper and scissors, scissors wins (by cutting the paper). If the two players pick rock and scissors, rock wins (by smashing the scissors). If the two players pick the same option, it’s a tie, and they have to play again.

What I’ve just described above is the absolute first thing you should do before you start a programming project. You don’t start by writing code—you start by understanding what you’re trying to achieve with the code. Generally speaking, when you code, you’ll either code for yourself (in which case, it can be helpful to you to articulate your actual goals and workflow) or you code for someone else (in which case, you don’t even know what to code unless that person explains to you what the process should be).

You can draw a visual or just write out the process in plain English, but understanding the flow and logic is far more important than actual programming code.

A brief note on code

Even though you can copy and paste code into whatever text editor you’re using, you will learn better if you retype instead of copy/paste, so I’d highly recommend you retype the code you see. Mistyping or misspelling things is part of the learning process, so don’t fret if you get an error message.

The nice thing is that python error messages will usually tell you exactly what line of code the error appears in.

Using comments in code

So one way that you can approach writing out your process is to start with comments (“code” that doesn’t do anything).

# Person one "plays" rock, paper, or scissors

# Person two "plays" rock, paper, or scissors

# We have to determine which person won or if there was a tie

# We have to say who won

To make things a bit simpler, we’re actually going to have only one player and then the opponent will just be the Python program itself, so the comments will look a bit more like this:

# The user (person) "plays" rock, paper, or scissors

# The computer (program) "plays" rock, paper, or scissors

# We have to determine whether the user won or if there was a tie

# We have to say whether the user won or lost

Expanding shorthand logic

We have to determine whether the user won or if there was a tie is a bit too vague. How do we determine that? Well, one way we can do it is a bit cumbersome, with a series of if/then statements:

# The user (person) "plays" rock, paper, or scissors

# The computer (program) "plays" rock, paper, or scissors

# We have to determine whether the user won or if there was a tie
   # If the user played rock and the computer played rock, then it's a tie
   # If the user played rock and the computer played paper, then the computer wins
   # If the user played rock and the computer played scissors, then the user wins
   # If the user played paper and the computer played rock, then the user wins
   # If the user played paper and the computer played paper, then it's a tie
   # If the user played paper and the computer played scissors, then the computer wins
   # If the user played scissors and the computer played rock, then the computer wins
   # If the user played scissors and the computer played paper, then the user wins
   # If the user played scissors and the computer played scissors, then it's a tie

# We have to say whether the user won or lost

But now we have a different problem here—a long list of if/then statements, which maybe could be simpler.

Considering another approach

What if we assigned a number to each option? Rock is 0, paper is 1, and scissors is 2.

If we do that, look at each of these scenarios:

   # If the user played rock and the computer played rock, then it's a tie
      # 0 - 0 = 0
   # If the user played rock and the computer played paper, then the computer wins
      # 0 - 1 = -1
   # If the user played rock and the computer played scissors, then the user wins
      # 0 - 2 = -2
   # If the user played paper and the computer played rock, then the user wins
      # 1 - 0 = 1
   # If the user played paper and the computer played paper, then it's a tie
      # 1 - 1 = 0
   # If the user played paper and the computer played scissors, then the computer wins
      # 1 - 2 = -1
   # If the user played scissors and the computer played rock, then the computer wins
      # 2 - 0 = 2
   # If the user played scissors and the computer played paper, then the user wins
      # 2 - 1 = 1
   # If the user played scissors and the computer played scissors, then it's a tie
      # 2 - 2 = 0

It should be fairly obvious that if the user and computer pick the same number, subtracting one from the other will leave you with 0.

But also notice that if the user wins, it’s either -2 or 1. If the computer wins, it’s either 2 or -1.

So that makes the logic a lot simpler.


   # Find the numeric equivalent of what the user plays
   # Find the numeric equivalent of what the computer plays
   # Find the difference between the two
   # If the difference is 0, it's a tie
   # If the difference is -2 or 1, the user wins
   # Any other scenario, the computer wins

Why does this work? Well, think about it. If you list out the three possibilities in the Rock, Paper, Scissors order, each possibility is one higher number than the previous one. Rock, 0; Paper, 1. Paper is one more than Rock, so Paper wins. Paper, 1; Scissors, 2. Scissors is one more than Paper, so Scissors wins. Rock isn’t 1 more than Scissors (unless you consider the first to be one higher than the last), but it is 2 less than Scissors (-2).

No one right way to approach problems

We’ve basically gone from 9 separate (and extremely repetitive) if/then statements down to 3 if/then statements. This is really the tough part of programming—not the actual code you write but your approach to problem-solving.

There are distinct advantages to the latter approach (fewer steps, less repetition) and also advantages to the former approach (less abstraction—you don’t have to arbitrarily assign numbers to rock, paper, and scissors). There is no one right way to approach things, but you should be thinking about all the pros and cons.

Another advantage the latter has over the former is flexibility. What if, all of a sudden, the rules changed and rock beat paper, which beat scissors, which beat rock? In the former approach, you’d have to rewrite six of the nine if/then statements. In the latter approach, all you’d have to do is reassign the numbers as rock 2, paper 1, scissors 0 instead of rock 0, paper 1, scissors 2… or pick different winning numbers.

Let’s actually start writing some code

The first thing we want to do is define what the available choices are, so in your text editor or online editor, type in (again, you can technically copy and paste, but typing will help you get used to the syntax of programming):

choices = { "Rock": 0, "Paper": 1, "Scissors": 2 }

All this does is just define what the available choices are and assign a number to the choice.

Now, let’s get the user to pick one of the choices:

# Define the available choices
choices = { "Rock": 0, "Paper": 1, "Scissors": 2 }

# Get the user's choice of the three
user_choice = raw_input("\nType Rock, Paper, or Scissors to play against the computer: ")

# Print what the user's choice, so she knows what she picked
print user_choice

If it worked, when you run your program, it should prompt you to type in Rock, Paper, or Scissors and then display whatever you typed in.

Don’t trust user input

That’s a problem right there—you can’t trust user input. You want the user to type in Rock, Paper, or Scissors, but the user could just as easily type in I hate this game (deliberately incorrect input), Scisors (accidental misspelling), or scissors (not a misspelling but not an exact match for Scissors either).

So, let’s validate the input.

# Define the available choices
choices = { "Rock": 0, "Paper": 1, "Scissors": 2 }

# Get the user's choice of the three
user_choice = raw_input("\nType Rock, Paper, or Scissors to play against the computer: ")

# Make sure the user input is valid
if user_choice in choices:
   print "You chose %s." % user_choice
else:
   print "Your choice of %s is not a valid choice." % user_choice

A few notes about syntax here, because we’re moving a little out of the problem-solving aspect of programming and more into the nuts and bolts of the actual code:

  • Note that the difference between the comments (human-readable notes to yourself) and the actual code the computer runs is just the inclusion (comment) or omission (actual code) of a pound sign # (number sign or hashtag, if that’s what you want to call it).
  • user_choice is a variable, which means you can change its value or you can keep using the same value it has. Think of it sort of like a shopping cart that can hold only one item. So you go to the store, put in a can of soda. Your one-item shopping cart will keep holding that can of soda, until you take the can of soda out and then replace it with a jar of peanut butter. When you get the raw_input from the user, you’re putting whatever the user typed in into the one-item “shopping cart” called user_choice, and every time you reference user_choice, you’ll be referencing whatever the user typed in.
  • choices is a dictionary, which is exactly what it sounds like. In a normal dictionary, you look up a word (rock), and it gives you a definition (a large mass of stone forming a hill, cliff, promontory, or the like). Or in a language-to-language dictionary, you look up a word in one language (rock), and it gives you the corresponding word in another language (roccia). In this case, though, instead of giving you a definition or a foreign language equivalent, the dictionary gives a random number we assign it. (Note for Python veterans: Yes, you can easily use a list instead of a dictionary for this; we’ll go over that later.)
  • Notice how there are indents after the if line and the else line? In some programming languages, there are semi-colons or brackets that separate out parts of the if this is true, do that code. In Python, though, everything is done with spacing. Even though they look like tabs, they’re actually spaces. In this case, I’m using sets of three spaces, but you can pick four spaces or even five, as long as you’re consistent—so one indent would be three spaces, two indents would be six spaces, three indents would be nine spaces, etc.
  • Notice the %s in what you’re printing? That’s just a placeholder to say “I’m going to substitute a string of characters [as opposed to numbers] in this part of the sentence, and I’ll tell you what to substitute in later.” After the lone %, the variable user_choice is what gets substituted in.

Even though we’re “validating” the input, the two problems I mentioned before still apply—a misspelling or lowercase version of the word will appear as invalid input. For now, for simplicity’s sake, let’s let the misspelling piece go. The lowercase issue is a lot easier to fix.

# Define the available choices
choices = { "Rock": 0, "Paper": 1, "Scissors": 2 }

# Get the user's choice of the three
user_choice = raw_input("\nType Rock, Paper, or Scissors to play against the computer: ")

# Make sure the user input is valid
if user_choice.title() in choices:
   print "You chose %s." % user_choice
else:
   print "Your choice of %s is not a valid choice." % user_choice

The addition of .title() to the user_choice variable changes (temporarily, just for that one line) the variable’s value to title case, so ROCK, rock, or even rOCK will all become (just for that one line) Rock.

Picking a choice at random

If we go back to our original comments, it looks as if we need to get the computer choice, now that we have the user choice:

# The user (person) "plays" rock, paper, or scissors

# The computer (program) "plays" rock, paper, or scissors

# We have to determine whether the user won or if there was a tie

# We have to say whether the user won or lost

We could certainly hard-code a computer choice, but we don’t want the computer choosing rock every time. We want the computer to make a random choice every time so it won’t be predictable.

import random

# Define the available choices
choices = { "Rock": 0, "Paper": 1, "Scissors": 2 }

# Get the user's choice of the three
user_choice = raw_input("\nType Rock, Paper, or Scissors to play against the computer: ")

# Make sure the user input is valid
if user_choice.title() in choices:
   print "You chose %s." % user_choice

   # Get the computer's choice by random
   computer_choice = random.choice(choices.keys()) 

   # Say what the computer chose
   print "The computer chose %s as its choice." % computer_choice
else:
   print "Your choice of %s is not a valid choice." % user_choice

A few more syntax notes here:

  • In order to allow for random choices, we have to import the random module.
  • We’re creating a new variable here called computer_choice, but instead of getting the value for it from the user, we’re generating the value randomly. Let’s look at the inside of the parentheses first. Remember our dictionary called choices? That has a lookup value and then a translation. In Python land, the lookup value is called a key and its translation is called the corresponding value. So choices.keys() just means get keys from the dictionary of choices. Then random.choice() just picks a random choice out of those keys. The equal sign assigns that to the variable computer_choice.

Comparing values with if/elif/else

At this point, we have user input and computer input, and we’ve checked that the user input is valid. To actually “play” the game, we don’t want to just say what the user played and what the computer played, but we want to evaluate who won, so we’re going to use our logic from earlier:

import random

# Define the available choices
choices = { "Rock": 0, "Paper": 1, "Scissors": 2 }

# Get the user's choice of the three
user_choice = raw_input("\nType Rock, Paper, or Scissors to play against the computer: ")

# Make sure the user input is valid
if user_choice.title() in choices:
   print "You chose %s." % user_choice

   # Get the computer's choice by random
   computer_choice = random.choice(choices.keys()) 

   # Say what the computer chose
   print "The computer chose %s as its choice." % computer_choice

   # Find the numeric equivalent of what the user plays
   # Find the numeric equivalent of what the computer plays
   # Find the difference between the two
   choice_difference=choices[user_choice.title()]-choices[computer_choice]

   # If the difference is 0, it's a tie
   if choice_difference == 0:
      print "It's a tie!"

   # If the difference is -2 or 1, the user wins
   elif choice_difference == -2 or choice_difference == 1:
      print "The user wins!"

   # Any other scenario, the computer wins
   else:
      print "The computer wins!"

else:
   print "Your choice of %s is not a valid choice." % user_choice

Some more notes on the code changes here:

  • Remember the indents with spaces? Up until this point, we’d used only one set of indents, but now we have if/then statements inside of other if/then statements, so the indents are doubled up sometimes.
  • Note that elif means else if, which means if the first thing isn’t true but this second thing is true, do something (say the user won). And then the else after that just means if neither of the previous conditions is true, do another thing (say the computer won).

Play the game a couple of times and just make sure it’s working.

Extra Credit

In our comments, we find the numeric equivalent of what the user plays (e.g., 0 for rock) and then we separately find the numeric equivalent of what the computer plays (e.g., 2 for scissors), and then we have a separate third step to find the difference between the two.

Instead of doing that, which you can do by assigning those as variables, we can just combine all three steps into one (which is what is above). Instead of “numeric choice is 0 and other numeric choice is 2 and subtract second numeric choice from first one,” it’s just doing it in one step, which is “find the difference between this numeric choice and that one.”

One approach isn’t necessarily better than the other. It depends on what you may want to do later. For example, if you anticipate possibly needing the numeric equivalents again, it would be silly to look it up again when you could just have a variable stand in place for it.

If you have time, try assigning choices[user_choice.title()] and choices[computer_choice] as variables and then using those variables to get choice_difference.

What if we want to keep playing and not have it start over again all the time?

This is where a while loop might come in handy. A loop is just repetition until a certain point. For example, let’s say you have a laundry hamper full of clean and folded clothes, and you have to put those clothes in dresser drawers. The instructions you would give yourself would be “Keep taking clothes out of the hamper and putting them in dresser drawers until the hamper is empty.”

Or imagine you’re on a sports team, and your coach tells you to keep running back and forth on the tennis courts until she tells you to stop. That’s also a loop (“While I’m waiting for my coach to tell me to stop, I’ll keep running back and forth”).

So in this case, we probably want to keep playing Rock, Paper, Scissors until the user tells us she wants to quit.

In the case of the laundry hamper, the “stop” is a natural one (when the hamper is empty). We won’t try this right now, but in the future, you’ll come to know this as a for loop (for each item in the hamper, do put-item-away-in-dresser).

In the case of the sports team, the “stop” is an arbitrary one of your coach telling you to stop when she feels like it. So she may say “stop” in five minutes or in ten minutes or in an hour. This approach is really better suited for a while loop (while the coach hasn’t said stop, keep running back and forth).

The same principle applies to our Rock, Paper, Scissors game. The user may want to play just once or play thirteen times or play a hundred times. We’ll just create a while loop and say essentially “while the user hasn’t said ‘quit,’ let’s have the user keep playing the game.”

So there are three major pieces to this that we need to break down:

  • We need to define a “quit” word for the user to use when she wants to quit the game.
  • We need to define which part of the code is the “game” that we’ll keep playing over and over again.
  • We need to create a while loop that keeps playing the game until the quit word appears.
import random

# Define the quit word
quit_word="quit"

# Define the available choices
choices = { "Rock": 0, "Paper": 1, "Scissors": 2 }
 
def game():
   # Give instructions
   print "\nThis game will keep playing until you type quit"

   # Get the user's choice of the three
   user_choice = raw_input("\nType Rock, Paper, or Scissors to play against the computer: ")
 
   # Make sure the user input is valid
   if user_choice.title() in choices:
      print "You chose %s." % user_choice
 
      # Get the computer's choice by random
      computer_choice = random.choice(choices.keys()) 
 
      # Say what the computer chose
      print "The computer chose %s as its choice." % computer_choice
 
      # Find the numeric equivalent of what the user plays
      # Find the numeric equivalent of what the computer plays
      # Find the difference between the two
      choice_difference=choices[user_choice.title()]-choices[computer_choice]
 
      # If the difference is 0, it's a tie
      if choice_difference == 0:
         print "It's a tie!"
 
      # If the difference is -2 or 1, the user wins
      elif choice_difference == -2 or choice_difference == 1:
         print "The user wins!"
 
      # Any other scenario, the computer wins
      else:
         print "The computer wins!"
 
   else:
      print "Your choice of %s is not a valid choice." % user_choice

while user_choice!=quit_word:
   game()

Some notes:

  • def game() is just defining (as what’s called a function) which part of the code is the game that you want to have the user keep playing over and over again. Note that you then have to indent all the code within (if you’re using TextWrangler, you can highlight all the code and then hit Cmd]).
  • The while loop at the bottom just says “while the user hasn’t typed in the quit word, keep playing the game.”

Debugging (finding/fixing) Errors

When you run this code, you should get the following error:

while user_choice!=quit_word:
NameError: name 'user_choice' is not defined

That’s because the user_choice in the while loop is a global variable (can be used anywhere in the code) and the user_choice in the game() function is a local variable (can be used only in the game() function). In order to have anything outside of the game() function recognize the user_choice variable, you have to make that a global variable:

def game():
   # Make user choice a global variable
   global user_choice

   # Get the user's choice of the three
   user_choice = raw_input("\nType Rock, Paper, or Scissors to play against the computer: ")

But then you’ll run into yet another error, which is

NameError: global name 'user_choice' is not defined

That’s because the while loop test condition (is the user_choice not equal to the quit_word?) comes before the user has even had a chance to put in any input. So we’ll have to give the user_choice a value before the user types in a value:

# Initialize user_choice variable
user_choice=''

while user_choice!=quit_word:
   game()

Now your game should work, but you’ll run into this issue: once you type in quit, instead of quitting, it will say your choice is an invalid choice (because it’s not Rock, Paper, or Scissors):

Type Rock, Paper, or Scissors to play against the computer: quit
Your choice of quit is not a valid choice.

Why is that? Well, let’s look at this while loop step by step:

  1. Initially, you define user_choice as blank (two apostrophes with nothing in between).
  2. The while loop says “Hey, is the user choice not the quit word? It’s not the quit word, so let’s play the game.”
  3. You play a legitimate word like Rock or Scissors.
  4. The while loop says “Hey, this is legitimate. Still not the quit word, so let’s keep playing.”
  5. Then you type quit to quit.
  6. You’re still in the game. Part of the game says “If it’s not a valid choice, say it’s not a valid choice,” so it will tell you that.
  7. Only after the game is finished, does the while loop see your quit word and then say “Okay. Let’s stop.”

So the game itself has to also recognize the quit word. You can do this by changing the last else in the game to be an else if:

      # Any other scenario, the computer wins
      else:
         print "The computer wins!"

   elif user_choice!=quit_word:
      print "Your choice of %s is not a valid choice." % user_choice

# Initialize user_choice variable
user_choice=''

Run it again after making that change and see the difference.

Super extra credit

So we accounted for upper and lower cases (rock, ROCK, rOCK) but not for misspellings or other variations. What if we want to account for those?

We could expand our dictionary to include a bunch of other possibilities, or we could create a separate dictionary that translates misspellings and abbreviations to the proper value. I prefer the latter approach, because it feels cleaner to me, even though there are more steps:

# Define the available choices
choices = { "Rock" : 0, "Paper": 1, "Scissors": 2}

# Make a dictionary of misspellings or alternatives
other_choices = { "Rocks" : "Rock", "Papper" : "Paper", "Scisors" : "Scissors", "Scissor": "Scissors", "Scisor": "Scissors", "R" : "Rock", "P": "Paper", "S": "Scissors" }

So what this accounts for is people misspelling scissors or just putting the first letter instead of the full word. Makes the game a little more flexible.

One more thing to do, which is the translation of the misspelling or abbreviation to the actual original dictionary key:

      # Double-check user input is one of the keys
      if user_choice.title() in other_choices:
         user_choice=other_choices[user_choice.title()]

      if user_choice.title() in choices:
         print "Your choice of %s is a valid choice." % user_choice

We’re basically assigning a new user_choice based on how the original user_choice went, and then using that new user_choice the way we did before with the original dictionary of choices.

Still other approaches

Super credit to Chelsea for helping think about this problem in still other ways (yes, there is always more than one “right” answer!). Instead of a dictionary, you can use a list:

choices = [ "Rock", "Paper", "Scissors" ]

select a list item at random instead of a key:

computer_choice = random.choice(choices)

and then use the list index as the number to compare:

choice_difference=choices.index(user_choice.title())-choices.index(computer_choice)

For beginners, even though it’s a bit counterintuitive, I would recommend the dictionary over the list, so they can easily see the matching up of the number to the Rock, Paper, or Scissors. Generally speaking, though, you want to use a list whenever possible instead of a dictionary.

Another cool idea Chelsea suggested was storing a dictionary of which play beats the other play:

choices = { "Rock": "Paper", "Paper": "Scissors", "Scissors": "Rock" }

It may seem a bit redundant, but it offers flexibility in case the rules ever change.

Whoa! That was a lot!

Yes, none of this “Hello, World” business you usually get, but I really have tried to take you step by step on how to build a program and use a lot of the standard building blocks (modules, functions, variables, dictionaries, while loops, conditions).

If you have dabbled in programming before, a lot of this should seem familiar to you, and this may have been a good refresher exercise for you.

If you have never programmed before, you may not have gotten all of this, but hopefully you got something. I’d highly encourage you to repeat the exercise from scratch and make sure you are retyping code and not just copying and pasting (it makes a difference).

2 replies on “Making a Rock, Paper, Scissors game in Python”

Leave a Reply

Your email address will not be published. Required fields are marked *