Lab 4-1: Rock Paper Scissors¶
Lab Requirements and Specifications¶
Rock Paper Scissors - the ultimate, time-tested witness to the greatest and most difficult question in the world - “Who should go first?”.
In this lab, you’ll be creating a program to play rock paper scissors with the user.
At the beginning of the game, the program should ask for the number of rounds to be played. Each round will consist of the player and computer selecting their choice between rock, paper, and scissors. A round is a round, even if the players tie. The player should be able to choose their move by picking from a menu. The menu should have some shorthand way of picking an item, either by choosing 1/2/3 or a/b/c, instead of typing in the entire word “rock” or “paper” or “scissors”. Alternatively, you can use the method shown in the sample output (using [r|p|s]
to show options).
- As a reminder, these are the interactions between rock, paper, and scissors:
- Rock beats Scissors
- Paper beats Rock (don’t ask me how)
- Scissors beats Paper
The game will keep track of the number of rounds each player (or computer) has won, as well as the number of ties. A sample scoreboard display might look like:
Player Wins: 1
Computer Wins: 2
Ties: 3
It does not have to match the above example to the letter - you may present it in whichever way you wish. Just make sure it’s easy to read and interpret.
If all the rounds are played or if a player wins the majority (more than half) of the rounds, the game should end. When the game ends, the program should report how many rounds each player/computer has won, the number of ties, as well as who the overall winner is.
“Weeks of programming can save you hours of planning.” – unknown
- As our labs get more and more complex, you’ll find that planning and writing pseudocode gets more and more beneficial. In this lab, much of the planning is done for you - we have given you incomplete functions (clues) to write, and you will use these functions to build the game.
- Now that we can create and call functions, the majority of your program should be functions. Break the game down into simple steps or instructions - each of those will have its own function.
- Take advantage of
return
statements. Your code should not have any global variables. Data should be passed to functions through arguments, and from functions through thereturn
statement. - Each function should have a single purpose. If you find that you have written a function that is doing multiple things, it means you should break it down further into more functions.
- The one exception to the above should be your main game function, which will contain the game loop, as well as any persistent variables.
- Which variables will you have to keep track of during all the rounds, and after all the rounds are completed? Make a note of which ones should be initialized outside of your loop.
- Be sure to provide the player with the appropriate prompts, letting them know their options, and appropriate round information. See below as an example.
- Don’t forget to
import random
to userandom.randint()
!
You should name your file FILN_rps.py
, where FILN is your first initial and last name, all lowercase, no space.
A code template is provided, which outlines the functions you should be writing as well as what those functions should be doing. It is your job to write the main game loop to piece together the game using these functions. Copy and paste the following into your file, and start from there.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | import random
#function name: get_p1_move
# arguments: none
# purpose: present player with options, use input() to get player move
# returns: the player's move as either 'r', 'p', or 's'
def get_p1_move():
#code here
#function name: get_comp_move():
# arguments: none
# purpose: randomly generates the computer's move,
# either 'r' 'p' or 's'
# returns: the computer's randomly generated move
def get_comp_move():
#code here
#function name: get_rounds
# arguments: none
# purpose: allows the user to choose a number of rounds from 1 to 9.
# returns: the user-chosen number of rounds
def get_rounds():
#code here
#function name: get_round_winner
# arguments: player move, computer move
# purpose: based on the player and computer's move, determine
# the winner or if it's a tie
# returns: returns a string based on the following:
# "player" if player won
# "comp" if computer won
# "tie" if it's a tie
def get_round_winner(p1move, cmove):
#code here
#function name: get_full_move
# arguments: a single letter move 'r','p', or 's'
# purpose: returns the "full" word of a given move
# returns: returns a string based on the following:
# "Rock" if given "r"
# "Paper" if given "p"
# "Scissors" if given "s"
def get_full_move(shortmove):
#code here
#function name: print_score
# arguments: player score, computer score, number of ties
# purpose: prints the scoreboard
# returns: none
def print_score(pscore, cscore, ties):
#code here
#function name: rps
# arguments: none
# purpose: the main game loop. This should be the longest, using
# all the other functions to create RPS
# returns: none
def rps():
#code here
#function name: tests
# arguments: none
# purpose: a place for you to write your tests. replace 'rps' below
# with 'tests' to run this function instead of the game loop
# returns: none
def test():
#code here
rps()
|
Notice how all the function names are verbs (get, display). Remember that functions should do things, and therefore have names that reflect what they do.
When getting user input, you may assume that the user will type in something valid. In other words, you don’t have to make sure that the user was following directions. However, your directions, especially for input, should be very clear to the user.
Testing Your Program¶
Since we have learned functions, testing our functions has become much easier. We can now write tests for individual functions.
For example, for the get_round_result function shown above, we can write the following tests for the function to make sure everything works as expected:
1 2 3 4 | #reminder: player move is first arg, computer move is second arg
print(get_round_result("rock","rock")) #should print "tie"
print(get_round_result("rock","scissors")) #should print "player"
print(get_round_result("paper","scissors")) #should print "computer"
|
Except you would probably want to write more test cases to make sure that every case works.
Below is a sample runthrough of the entire program:
Welcome to Rock Paper Scissors!
Enter the number of rounds to play, from 1 to 9: 5
-----------------
Round 1!
Enter your move from [r|p|s]: r
You picked: Rock
Computer picked: Paper
Computer won!
Player Wins: 0
Computer Wins: 1
Ties: 0
-----------------
Round 2!
Enter your move from [r|p|s]: p
You picked: Paper
Computer picked: Rock
Player won!
Player Wins: 1
Computer Wins: 1
Ties: 0
-----------------
Round 3!
Enter your move from [r|p|s]: s
You picked: Scissors
Computer picked: Paper
Player won!
Player Wins: 2
Computer Wins: 1
Ties: 0
-----------------
Round 4!
Enter your move from [r|p|s]: r
You picked: Rock
Computer picked: Scissors
Player won!
Player Wins: 3
Computer Wins: 1
Ties: 0
The player is victorious! Player:3 to Comp:1!
Note that the game ended after the 4th round because the player won 3 out of 5 total rounds, ensuring his victory.
The following space is provided in case you want to test code out or write it in the browser:
Taking it Further¶
1: Right now the game is only PvC, or Player vs Computer. You can easily adjust your code to make it PvP, or Player vs Player. The biggest problem with PvP games like this is that the other player can see what you type in, and will know how to counter you. However, there is a solution around this. Python’s input()
function “echoes” what we type in - meaning we see it on the screen too. This is a good thing, because it allows us to make sure what we typed in is what we want. However, if we don’t want our input to be visible, then we will need to need to use the getpass
library.
We can do this by using import getpass
, similar to how we would get the random
library. We can then use the function getpass.getpass()
, which works almost identically to the input()
function, except nothing is echoed - you cannot see what you are typing in - which is perfect for our purposes! Copy and paste the code below and run it to test it out:
import getpass
inp = getpass.getpass("Enter your name: ")
print("Your name is {}".format(inp))
print("Note how you can't see it next to the prompt!")
Note that the getpass
library is not included in the packages on this site. This will not work on any site that implement Skulpt, such as this one, or Trinket. It’s best done on your desktop, or in a virtualmachine IDE such as Koding or C9.io
2: The next thing you can do is create RPSLS - Rock Paper Scissors Lizard Spock. It’s similar to RPS, except with more elements.
- Here are the relationships between moves in RPSLS:
- Rock crushes Scissors and crushes Lizard
- Paper covers Rock and disproves Spock
- Scissors cuts Paper and decapitates Lizard
- Lizard eats Paper and poisons Spock
- Spock vaporizes Rock and crushes Scissors