Rock-Paper-Scissors

This lesson will teach you how to make a rock-paper-scissors game using the Tkinter module and add an advanced AI.

How to Play



    Before you begin programming a game, it's important to understand how a game is played. Even if you think you already know how to play Rock-Paper-Scissors, as a programmer it is important to write down the rules so that you don't make logic errors when programming.

    Cannot Load Image

    Rock-Paper-Scissors is a 2-player game, where each player will pick (at the same time) one of three choices: Rock, Paper, or Scissors. Based on what the two players pick, a winner is determined.

    If both players pick the same choice, then the game is a Tie. Otherwise, a winner is determined by comparing the two choices.

    1. Rock wins against Scissors
    2. Scissors wins against Paper
    3. Paper wins against Rock

    We will be creating a single-player game where the human player makes their selection on a GUI using Tkinter, and a simple computer player artificial intelligence (AI) will make a choice for the second player.

    The AI that you will make will begin by making choices randomly. Later, you'll add some very basic machine learning to the AI, so that it learns from the choices the human player makes and tries to make slightly better choices based on how the human plays the game.

    Here's a brief video of the basic code in action



    Now that we understand the rules, create a new file called rockpaperscissors.py to save your code. We will start with the basic code to set up the window using Tkinter.

    import tkinter
    
    tk_window = tkinter.Tk()
    tk_window.geometry("400x300")
    tk_window.title("Rock-Paper-Scissors Game")
              

    When you play the module, you should see the main game's window set up with your title.

Game Logic Flow



    We're going to create the logic for our game in this order:

    1. Add how the AI will make the choice for player 2

    2. Add a function that determines who will win the game based on the choices

    3. Add functions that let the player make each of the choices and plays the game

    4. Add buttons to the screen that let the player make their choice


    Let's start with the AI. We'll import the random module, and have the computer make a random choice from the three options.

    import tkinter
    import random
    
    tk_window = tkinter.Tk()
    tk_window.geometry("400x300")
    tk_window.title("Rock-Paper-Scissors Game")
    
    def random_computer_choice():
        return random.choice(['rock','paper','scissors'])
          

    The three options for the AI are 'rock', 'paper', and 'scissors', so every time it makes a choice, it will make a random choice from that list.

    Next, we're going to add the function that will calculate the result of the game.

    def random_computer_choice():
        return random.choice(['rock','paper','scissors'])
    
    def determine_game_result(human_choice):
        comp_choice = random_computer_choice()
        print("Human Player Choice: " + human_choice + " and Computer Player Choice: " + comp_choice)
          

    Right now, all this function does is run the random_computer_choice function to pick a random choice for the computer, and then prints out the choices. You'll see that there is one parameter for the function, human_choice, but right now the only way to call that function is from the shell. We'll come back to this function later and add more logic to determine who wins and who loses the game, but the next thing we need to do is enable the player to pick the choice they want to make with UI buttons.

Let the Player Decide



    For the next part of our game, we need to let the player make their choice. To do that, we're going to make 1 function for each choice, and put 1 button on the screen that lets the player make the choice.

    First, this is the function that lets the player make the choice of 'rock'.


    def determine_game_result(human_choice):
        comp_choice = random_computer_choice()
        print("Human Player Choice: " + human_choice + " and Computer Player Choice: " + comp_choice)
    
    def rock():
        user_choice = 'rock'
        determine_game_result(user_choice)
                    

    This function sets the user_choice variable to 'rock', and calls the result() function that we created. Next, we need a button on the screen that lets the user call this function.

    def rock():
        user_choice = 'rock'
        determine_game_result(user_choice)
    
    button_rock = tkinter.Button(text = "Rock", command = rock)
    button_rock.grid(column = 0, row = 1)
                    

    Play the module, and you will be able to click the Rock button to select rock.



    The player is at a disadvantage as they can only pick rock right now, but the computer player can make all three choices!

    Add code for the other two choices (paper and scissors), just like we did for rock. You should create a function for each choice as well as a button for each choice.

    def rock():
        user_choice = 'rock'
        determine_game_result(user_choice)
    
    button_rock = tkinter.Button(text = "Rock", command = rock)
    button_rock.grid(column = 0, row = 1)
    
    def paper():
        user_choice = 'paper'
        determine_game_result(user_choice)
    
    button_paper = tkinter.Button(text = "Paper", command = paper)
    button_paper.grid(column = 0, row = 2)
    
    def scissors():
        user_choice = 'scissors'
        determine_game_result(user_choice)
    
    button_scissors = tkinter.Button(text = "Scissors", command = scissors)
    button_scissors.grid(column = 0, row = 3)
                    

    Play the module, and you will now be able to make all three choices, and see which choice the computer AI made in the console window.



Find the Winner



    Now that the human player and the computer player can both make choices, we can return to the result() function and calculate a winner from the choices.

    Let's review the potential outcomes of Rock-Paper-Scissors. If both players pick the same choice, then the game is a Tie. Otherwise, a winner is determined by comparing the two choices.

    1. Rock wins against Scissors
    2. Scissors wins against Paper
    3. Paper wins against Rock

    The result of "Tie" is easy to add, so we'll add that to the determine_game_result function first.

    def determine_game_result(human_choice):
        comp_choice=random_computer_choice()
        print("Human Player Choice: " + human_choice + " and Computer Player Choice: " + comp_choice)
        if human_choice == comp_choice:
            print("Tie!")
                  

    Next, we'll add elif statements for each of the win conditions for the player. We need to check both the player's choice and the computer's choice, so we will add the and keyword between those checks inside of the elif statement.

    def determine_game_result(human_choice):
        comp_choice = random_computer_choice()
        print("Human Player Choice: " + human_choice + " and Computer Player Choice: " + comp_choice)
        if human_choice == comp_choice:
            print("Tie!")
        elif human_choice == "rock" and comp_choice == "scissors":
            print("Human Player Wins!")
        elif human_choice == "scissors" and comp_choice == "paper":
            print("Human Player Wins!")
        elif human_choice == "paper" and comp_choice == "rock":
            print("Human Player Wins!")
                  

    There's one last thing to add...what if the player loses? If the result is NOT a Tie, and the player does NOT win, then the last remaining result is that the computer wins. This means that we don't need to add more elif statements, we can just add an else statement to handle all the other potential conditions.

    def determine_game_result(human_choice):
        comp_choice = random_computer_choice()
        print("Human Player Choice: " + human_choice + " and Computer Player Choice: " + comp_choice)
        if human_choice == comp_choice:
            print("Tie!")
        elif human_choice == "rock" and comp_choice == "scissors":
            print("Human Player Wins!")
        elif human_choice == "scissors" and comp_choice == "paper":
            print("Human Player Wins!")
        elif human_choice == "paper" and comp_choice == "rock":
            print("Human Player Wins!")
        else:
            print("Computer Player Wins!")
                

    Run your game again and when you click the buttons, you will see whether you won, lost or tied printed in the Shell window.



    All right, the player can tie, win, and lose. But it would be better if the player could see the result inside of the Tkinter window that we made.

    To show our result on the screen, we're going to do the following:

    1. Create a new function that will handle displaying the result of the game inside of a text area in our window.

    2. Save our game result into a variable showing both player's choices and who won the game.

    3. Create a text area on the screen to display the result.

    4. Add our game result to the text area that we created.


    To create a place to see the result of the game, we need a text area that's a little bigger than a simple label.

    We're going to use the Text object in tkinter to create a text area that can show multiple lines.

    The tkinter.Text() function requires two inputs, a height and a width. The height is how many rows of text can be shown, and the width is how many columns of individual characters can be shown.

    After we place the text area on the grid the same way we placed the buttons on the grid, then we insert the text using the insert function. The two arguments are '1.0', which indicates to put the text at the very beginning, and the string that you want to display.

    In the string we show to the player, we can use \n to add a newline for the text area.

    def determine_game_result(human_choice):
        comp_choice = random_computer_choice()
        print("Human Player Choice: " + human_choice + " and Computer Player Choice: " + comp_choice)
        if human_choice == comp_choice:
            display_winner = "Result: Tie!"
        elif human_choice == "rock" and comp_choice == "scissors":
            display_winner = "Result: Human Player Wins!"
        elif human_choice == "scissors" and comp_choice == "paper":
            display_winner = "Result: Human Player Wins!"
        elif human_choice == "paper" and comp_choice == "rock":
            display_winner = "Result: Human Player Wins!"
        else:
            display_winner = "Computer Player Wins!"
        display_game_result(human_choice, comp_choice, display_winner)
    
    def display_game_result(human_choice, comp_choice, display_winner):
        game_result = "Human Player Choice: " + human_choice + "\nComputer Player Choice: " + comp_choice +"\n" + display_winner
        text_area = tkinter.Text(height = 12, width = 40)
        text_area.grid(column = 0, row = 4)
        text_area.insert('1.0', game_result)
                




    As you can see, the text box doesn't display until we click the button.

    Let's make it display at the start of the game by adding some default text at the start.

    Write this code outside of the result function so it can display without the need to press a button.

    def determine_game_result(human_choice):
        comp_choice=random_computer_choice()
        print("Human Player Choice: " + human_choice + " and Computer Player Choice: " + comp_choice)
        if human_choice == comp_choice:
            display_winner = "Result: Tie!"
        elif human_choice == "rock" and comp_choice == "scissors":
            display_winner = "Result: Human Player Wins!"
        elif human_choice == "scissors" and comp_choice == "paper":
            display_winner = "Result: Human Player Wins!"
        elif human_choice == "paper" and comp_choice == "rock":
            display_winner = "Result: Human Player Wins!"
        else:
            display_winner = "Computer Player Wins!"
        display_game_result(human_choice, comp_choice, display_winner)
    
    def display_game_result(human_choice, comp_choice, display_winner):
        game_result = "Human Player Choice: " + human_choice + "\nComputer Player Choice: " + comp_choice +"\n" + display_winner
        text_area = tkinter.Text(height = 12, width = 40)
        text_area.grid(column = 0, row = 4)
        text_area.insert('1.0', game_result)
    
    text_area = tkinter.Text(height = 12, width = 40)
    text_area.grid(column = 0,row = 4)
    text_area.insert('1.0', "Ready to Play!")
                




    The player can now make a selection, and can know the result of one game. But what if we want to track the player's win streak over time?

Saving the Player's Score Using Global Variables



    We can track who has won the most matches over time by adding global variables to the game. Global variables are helpful for variables we want to update over the course of a game that can be accessed in any function in the python code (often called global scope). Global variables exist outside of functions.

    If you want to change a global variable, you need to specify

    import tkinter
    import random
    
    user_score = 0
    comp_score = 0
    
    tk_window = tkinter.Tk()
    tk_window.geometry("400x300")
    tk_window.title("Rock-Paper-Scissors Game")
                  

    Now that we have defined these variables that we will reuse, we will update the determine_game_result function to make use of these variables. We will do the following:

    1. Allow the determine_game_result function to access those variables.

    2. Add to the player's score if the player wins.

    3. Add to the computer's score if the player loses.

    4. Update the result text to include information about the player and computer's current scores.


    The global keyword is added to the variables at the beginning of the function so that the function can modify those variables inside of the function.

    def determine_game_result(human_choice):
        global user_score
        global comp_score
        comp_choice = random_computer_choice()
        print("Human Player Choice: " + human_choice + " and Computer Player Choice: " + comp_choice)
        if human_choice == comp_choice:
            display_winner = "Result: Tie!"
        elif human_choice == "rock" and comp_choice == "scissors":
            display_winner = "Result: Human Player Wins!"
            user_score += 1
        elif human_choice == "scissors" and comp_choice == "paper":
            display_winner = "Result: Human Player Wins!"
            user_score += 1
        elif human_choice == "paper" and comp_choice == "rock":
            display_winner = "Result: Human Player Wins!"
            user_score += 1
        else:
            display_winner = "Computer Player Wins!"
            comp_score += 1
        display_game_result(human_choice, comp_choice, display_winner)
    
    def display_game_result(human_choice, comp_choice, display_winner):
        game_result = "Human Player Choice: " + human_choice + "\nComputer Player Choice: " + comp_choice + "\n" + display_winner + "\nHuman Score: " + str(user_score) + "\nComputer Score: " + str(comp_score)
        text_area = tkinter.Text(height = 12, width = 40)
        text_area.grid(column = 0, row = 4)
        text_area.insert('1.0', game_result)
                  




    This is the final program code for the basic rock-paper-scissors game:

    import tkinter
    import random
    
    user_score = 0
    comp_score = 0
    
    tk_window = tkinter.Tk()
    tk_window.geometry("400x300")
    tk_window.title("Rock-Paper-Scissors Game")
    
    def random_computer_choice():
        return random.choice(['rock','paper','scissors'])
    
    def determine_game_result(human_choice):
        global user_score
        global comp_score
        comp_choice=random_computer_choice()
        print("Human Player Choice: " + human_choice + " and Computer Player Choice: " + comp_choice)
        if human_choice == comp_choice:
            display_winner = "Result: Tie!"
        elif human_choice == "rock" and comp_choice == "scissors":
            display_winner = "Result: Human Player Wins!"
            user_score += 1
        elif human_choice == "scissors" and comp_choice == "paper":
            display_winner = "Result: Human Player Wins!"
            user_score += 1
        elif human_choice == "paper" and comp_choice == "rock":
            display_winner = "Result: Human Player Wins!"
            user_score += 1
        else:
            display_winner = "Computer Player Wins!"
            comp_score += 1
        display_game_result(human_choice, comp_choice, display_winner)
    
    def display_game_result(human_choice, comp_choice, display_winner):
        game_result = "Human Player Choice: " + human_choice + "\nComputer Player Choice: " + comp_choice + "\n" + display_winner + "\nHuman Score: " + str(user_score) + "\nComputer Score: " + str(comp_score)
        text_area = tkinter.Text(height = 12, width = 40)
        text_area.grid(column = 0, row = 4)
        text_area.insert('1.0', game_result)
    
    text_area = tkinter.Text(height = 12, width = 40)
    text_area.grid(column = 0,row = 4)
    text_area.insert('1.0', "Ready to Play!")
    
    def rock():
        user_choice = 'rock'
        determine_game_result(user_choice)
    
    button_rock = tkinter.Button(text = "Rock", command = rock)
    button_rock.grid(column = 0,row = 1)
    
    def paper():
        user_choice = 'paper'
        determine_game_result(user_choice)
    
    button_paper = tkinter.Button(text = "Paper", command = paper)
    button_paper.grid(column = 0,row = 2)
    
    def scissors():
        user_choice = 'scissors'
        determine_game_result(user_choice)
    
    button_scissors = tkinter.Button(text = "Scissors", command = scissors)
    button_scissors.grid(column = 0, row = 3)
    				

    Test the game and try to get to 10 wins before the computer! Right now, because the computer will make choices randomly, no matter what you choose, over time your scores will be roughly the same....

    ...but what if we could make the AI smarter? Could we make the AI for a simple rock-paper-scissors game smart enough to consistently beat the human player? Let's find out!

Making the AI Smarter: Adding Memory to the AI



    Before you try to alter the AI for a game, it sometimes helps if you watch other people play the game and see what they do. Try to have someone play your game now, and make note of how they decide which choice they are going to make.

    One pattern you might have seen is if your player thinks that the AI is operating off of pure randomness, they might decide to pick the same option every single time. After all, if the AI is making choices based on pure randomness, why would they ever make the effort change their decision? We're going to take advantage of players that think like this by adding memory to our AI, so it does not work on pure randomness.

    Let's review our AI as it exists right now.

    def random_computer_choice():
        return random.choice(['rock','paper','scissors'])
                  

    Right now, the AI chooses from a basic list of 3 options, so we would say there is equal weight to each of the three potential choices. The AI is equally likely to pick each of the three options. However, if the player picks 'rock' more often than they pick 'paper' or 'scissors', then it makes sense for the AI to pick 'paper' more often than the other two choices. This AI will take advantage of players who are lazy in their decision-making!

    To implement this, we'll do the following:

    1. Create a list that contains the choices that the AI can choose from

    2. Set our program to pick from that list instead of our current list

    3. After the player makes a choice, we are going to add to that list the choice that would beat the player's choice! This will give greater weight to that choice in the random selection.


    Let's get started with the variable and the AI definition.

    user_score = 0
    comp_score = 0
    comp_choices = ['rock', 'paper', 'scissors']
    
    tk_window = tkinter.Tk()
    tk_window.geometry("400x300")
    tk_window.title("Rock-Paper-Scissors Game")
    
    def random_computer_choice():
        return random.choice(comp_choices)
                  

    We have now changed the random_computer_choice() function to use our global variable list. Next, we're going to change the determine_game_result function to append choices to our comp_choices list based on the choice the player makes.

    We make our decision on what value to append to the list based on this logic:

    1. If the player picks 'rock', we'll append 'paper' to the list, since paper beats rock

    2. If the player picks 'paper', we'll append 'scissors' to the list, since scissors beats paper

    3. If the player picks 'scissors', we'll append 'rock' to the list, since rock beats scissors


    The basic logical assumption of this AI is that if a player makes a choice, they are more likely to pick the same choice again than to change their choice for the next round.

    def determine_game_result(human_choice):
        global user_score
        global comp_score
        comp_choice=random_computer_choice()
        print("Human Player Choice: " + human_choice + " and Computer Player Choice: " + comp_choice)
        if human_choice == comp_choice:
            display_winner = "Result: Tie!"
        elif human_choice == "rock" and comp_choice == "scissors":
            display_winner = "Result: Human Player Wins!"
            user_score += 1
        elif human_choice == "scissors" and comp_choice == "paper":
            display_winner = "Result: Human Player Wins!"
            user_score += 1
        elif(human_choice=="paper" and comp_choice=="rock"):
            display_winner = "Result: Human Player Wins!"
            user_score += 1
        else:
            display_winner = "Computer Player Wins!"
            comp_score += 1
            
        if human_choice == 'rock':
            comp_choices.append('paper')
        elif human_choice == 'paper':
            comp_choices.append('scissors')
        else:
            comp_choices.append('rock')
        display_game_result(human_choice, comp_choice, display_winner)
    
    def display_game_result(human_choice, comp_choice, display_winner):
        game_result = "Human Player Choice: " + human_choice + "\nComputer Player Choice: " + comp_choice + "\n" + display_winner + "\nHuman Score: " + str(user_score) + "\nComputer Score: " + str(comp_score)
        text_area = tkinter.Text(height = 12, width = 40)
        text_area.grid(column = 0, row = 4)
        text_area.insert('1.0', game_result)
                  

    Try playing against the AI now. If you pick the same every option every time...the AI figures you out! Having the AI for your game react based on a combination of randomness and memory is a fun way to make the AI feel like a real player.

Additional Challenges



    Predictive AI Method

    Our advanced AI is pretty good, but is ineffective if the player knows how the AI thinks, since they will pick a different value every round. Try to make an AI that assumes the player will change their mind every round instead, and see how that AI performs. This AI would always pick an answer that will NOT beat the player's choice from last round.

    1. Remember which choice the player made last round

    2. Make your choice based on what would beat a choice the player did NOT make last round.


    Adaptive AI

    If the player understands the AI, they might try to take advantage of it. Can you create an AI that changes its strategy over time? It could start with the memory method, then change to a prediction method like the one above if it is losing.

    Improved Interface

    Tkinter allows lots of options to make the interface look more interesting. You can add these arguments to your Button() and Text() functions. The below code will create a button that has a blue background and white text.

    Note: the below code does not work for macs.

    button_paper = tkinter.Button(text = "Paper", command = paper, background = 'Blue', foreground = 'White')
    button_paper.grid(column = 0, row = 2)
                  

    Try to add more colors to your GUI to make it more appealing to the players that are going to play your game.