Rock, Paper, Scissors is one of the best first projects you can build in Python. It is small enough to finish in one sitting, but it touches every skill that actually matters at the beginner level: getting input from the user, making decisions with conditionals, using the standard library, and keeping a program running with a loop.
By the end of this tutorial you will have a working terminal game that plays multiple rounds, tracks a score, and lets the player quit whenever they want. Every line of the final code is explained so you understand not just what it does but why it is written that way.
What the Program Will Do
Before writing any code, it helps to know what you are building. The finished program will do the following when run:
- Ask the player to type
rock,paper,scissors, orquit. - Have the computer pick a move at random.
- Compare the two moves, print who won the round, and update the score.
- Repeat until the player types
quit, then print the final score.
That is four distinct behaviors, and each one maps to a specific Python concept. Asking for input maps to the input() function. Picking randomly maps to the random module. Comparing moves maps to if/elif/else. Repeating maps to a while loop. This is why game projects are so effective for learning: the game requirements drive you toward real-world patterns.
This tutorial assumes you have Python 3 installed and can run a .py file from the terminal or an IDE like VS Code. You do not need any prior experience with the random module or game logic.
The Game Logic at a Glance
Rock beats scissors. Scissors beats paper. Paper beats rock. If both players choose the same thing, the round is a tie. That is all the domain logic this game requires, and you will represent it entirely with conditional statements.
| Player Choice | Computer Choice | Result |
|---|---|---|
| rock | scissors | Player wins |
| rock | paper | Computer wins |
| rock | rock | Tie |
| paper | rock | Player wins |
| paper | scissors | Computer wins |
| scissors | paper | Player wins |
| scissors | rock | Computer wins |
Build the correct Python function signature for a winner-checking function that takes two arguments:
def, followed by the function name, then parentheses containing any parameters, and ends with a colon. class and return are not used in a function signature — class defines a class, and return belongs inside the function body.
The random Module and random.choice()
The random module is part of Python's standard library, which means it ships with Python and you never need to install it. To use it, you add one line at the top of your script:
import random
The function you will use is random.choice(). It takes a sequence — any list, tuple, or string — and returns one element chosen at random. Here is the only call you need for the computer's move:
choices = ["rock", "paper", "scissors"]
computer_choice = random.choice(choices)
Every time that line runs, computer_choice will be one of the three strings. random.choice() does not remove the chosen item from the list, so all three options remain available on the next call. That is the only randomness this program needs.
You can test random.choice() in the Python REPL before writing your full program. Open a terminal, type python3, then run import random followed by random.choice(["rock", "paper", "scissors"]) a few times to see the output change.
Why a List and Not Three Separate Variables?
Storing the choices in a list has two advantages. First, random.choice() needs a sequence — it cannot pick from three separate variables. Second, the list makes input validation effortless. You can check whether what the player typed is valid with a single expression: player_choice in choices. If the player types something unexpected, that expression evaluates to False and you handle the error before any other logic runs.
- What it returns
- A single randomly selected element from a non-empty sequence.
- Use in this project
- Pick one of "rock", "paper", or "scissors" for the computer's move each round.
- What it returns
- A random integer N such that a <= N <= b. Both endpoints are included.
- Use in this project
- Not needed here, but you could use it to pick an index (0, 1, or 2) and then look up the corresponding choice in the list.
- What it returns
- A new list of k unique elements chosen from the population without replacement.
- Use in this project
- Not appropriate here since you only need one selection at a time, but useful if you ever build a card game that deals multiple unique cards from a deck.
Getting Player Input and Validating It
The input() function pauses the program and waits for the user to type a line and press Enter. It always returns a string, regardless of what the user types. For this game, you want to normalize the input so that typing Rock or ROCK works the same as typing rock:
player_choice = input("Enter rock, paper, scissors, or quit: ").strip().lower()
Two string methods are chained here. .strip() removes any leading or trailing whitespace the player may have accidentally typed. .lower() converts the entire string to lowercase. Chaining them means both transformations happen in one line before the value is stored in player_choice.
After normalizing, check whether the player wants to quit, and then check whether the input is valid:
if player_choice == "quit":
break
if player_choice not in choices:
print("Invalid choice. Please type rock, paper, or scissors.")
continue
The break statement exits the while loop immediately. The continue statement skips the rest of the current loop iteration and jumps back to the top, which means the invalid input never reaches the winner-determination logic.
The code below tries to read and normalize user input, then check if it is valid. One line contains a bug. Find it.
.upper() to .lower() on line 2. The choices list contains lowercase strings ("rock", "paper", "scissors"), so converting the input to uppercase means the not in choices check on line 3 will always be True and every valid input will be treated as invalid.
Putting It All Together: The Complete Game
Here is the full program. Read through it once before moving on to the step-by-step build section below.
import random
choices = ["rock", "paper", "scissors"]
player_score = 0
computer_score = 0
def determine_winner(player, computer):
if player == computer:
return "tie"
wins_against = {"rock": "scissors", "paper": "rock", "scissors": "paper"}
if wins_against[player] == computer:
return "player"
return "computer"
print("Rock, Paper, Scissors — type 'quit' to exit.\n")
while True:
player_choice = input("Your move (rock/paper/scissors): ").strip().lower()
if player_choice == "quit":
break
if player_choice not in choices:
print("Invalid choice. Please type rock, paper, or scissors.\n")
continue
computer_choice = random.choice(choices)
print(f"Computer chose: {computer_choice}")
result = determine_winner(player_choice, computer_choice)
if result == "tie":
print("It's a tie!")
elif result == "player":
player_score += 1
print("You win this round!")
else:
computer_score += 1
print("Computer wins this round.")
print(f"Score — You: {player_score} Computer: {computer_score}\n")
print(f"\nFinal score — You: {player_score} Computer: {computer_score}")
print("Thanks for playing.")
"Simple things should be simple, complex things should be possible." — Alan Kay
How to Build Rock, Paper, Scissors in Python
Follow these five steps to construct the game from scratch. Each step introduces one concept and adds it to the file you are building.
-
Import the random module
Create a new file called
rps.py. On the very first line, writeimport random. This line tells Python to load the random module so thatrandom.choice()is available later in the script. Imports always go at the top of a Python file. -
Define the valid choices and score variables
After the import, declare
choices = ["rock", "paper", "scissors"]. Then addplayer_score = 0andcomputer_score = 0. These three names are used throughout the rest of the program, so they need to exist before the loop starts. -
Write the game loop with input validation
Add
while True:to start an infinite loop. Inside it, callinput()and chain.strip().lower()to normalize what the player types. Check for"quit"first, then check whether the input is inchoices. Usebreakto exit on quit andcontinueto skip invalid input. -
Have the computer choose and determine the winner
After input validation passes, call
random.choice(choices)and store the result. Then calldetermine_winner(player_choice, computer_choice). The function uses a dictionary to map each choice to what it beats, which is cleaner than nine separateifbranches. Increment the right score variable based on what the function returns. -
Display results and print the final score
After updating the score, print the round result and the running score inside the loop so the player sees it after each round. After the loop ends (when the player types quit), print the final score with a closing message. The code after a
whileloop runs once the loop exits, which is exactly what you need here.
A while True loop without a break or an exit condition runs forever. Always make sure at least one code path inside the loop calls break. For this game, the only exit path is the player typing quit.
Understanding the wins_against Dictionary
The wins_against dictionary inside determine_winner encodes all three win conditions in one data structure:
wins_against = {"rock": "scissors", "paper": "rock", "scissors": "paper"}
Reading it: rock beats scissors, paper beats rock, scissors beats paper. The function checks whether wins_against[player] equals the computer's choice. If it does, the player wins. If neither the tie nor the win condition is true, the computer wins. This is a useful pattern to remember: when you have a fixed set of relationships between values, a dictionary is often cleaner than a long chain of elif statements.
Python Learning Summary Points
import randomloads the random module, which providesrandom.choice()for selecting a random element from a list.- The
input()function always returns a string. Chaining.strip().lower()normalizes the input so case and whitespace do not cause unexpected failures. - A
while Trueloop runs indefinitely until abreakstatement is reached, which is the standard pattern for terminal-based game loops in Python. - The
continuestatement skips the rest of the current loop iteration. Using it after detecting invalid input prevents bad data from reaching the logic that depends on valid data. - A dictionary can replace a long chain of
elifstatements when you are mapping one value to another. Thewins_againstpattern here is worth remembering for any project involving fixed rule sets.
From here, there are a number of natural extensions you can try: add a round limit so the game ends after five rounds, track win streaks, allow the player to choose how many rounds to play, or add a "best of N" mode. Each extension introduces a new concept without requiring you to rewrite what you have already built.
Frequently Asked Questions
Building Rock, Paper, Scissors in Python teaches the random module, the input() function, if/elif/else conditional logic, a while loop for game replay, and basic function design. These are foundational control flow concepts every Python beginner needs.
Python uses random.choice() from the built-in random module. You pass it a list such as ["rock", "paper", "scissors"] and it returns one element at random each time it is called.
Use a while True loop and check the player's input after each round. When the player types quit, call break to exit the loop. This pattern is called a game loop and it is standard for terminal-based Python games.
input() is a built-in Python function that pauses the program and waits for the user to type something and press Enter. It always returns a string, so you may need to convert the result if you expect a number.
Use a conditional check before processing the input. For this game, check whether the player's choice is in the valid options list using the in operator. If it is not, print an error message and use continue to restart the loop iteration without processing the invalid round.
Yes, but functions make the code easier to read and reuse. Wrapping the win/lose/tie logic inside a determine_winner() function means you write that logic once instead of repeating it. Starting with inline code and then refactoring into functions is a good learning exercise.
random.choice() returns a single element selected at random from a non-empty sequence such as a list or a tuple. It does not remove the element from the sequence, so the same element can be selected again on the next call.
Declare integer variables such as player_score and computer_score before the game loop, then increment them with += 1 each time the respective player wins a round. Print the scores at the end of each round or when the player quits.