Rock Paper Scissors Game in 60 lines (Python3)
To build our Rock Paper Scissors game we are going to use some elements of OOP, in fact, we are just going to create one class
that is going to do all of it. So, without further ado...
import
functionchoice
fromrandom
module- declare
class RPS
- create an instance of this
class
calledgame
from random import choice
class RPS:
def __init__(self):
pass
# some more functions will go here
game = RPS()
Inside the __init__
function, we are going to have 4 variables:
opt
is alist
of optionsusr_score
andcpu_score
are self-explanatoryrules
is adict
that is going to help us decide who won without the need for extensiveif / else
statements
The last line of __init__
is going to be a call to some function match
that also belongs to the class RPS
.
# from now on we concentrate on the RPS class itself
class RPS:
def __init__(self):
self.opt = ['rock', 'paper', 'scissors']
self.usr_score = 0
self.cpu_score = 0
self.rules = {
'rock' : 'scissors',
'paper' : 'rock',
'scissors' : 'paper'
}
self.match()
# some more functions will go here
def match(self):
pass
Now, before we start implementing the match
function, let's declare some useful 'helper' functions. I really like using functions in my code because they make debugging really easy, so do not be surprised when you see a function that is one line long...
# from now on we concentrate on the RPS class itself
class RPS:
def __init__(self):
self.opt = ['rock', 'paper', 'scissors']
self.usr_score = 0
self.cpu_score = 0
self.rules = {
'rock' : 'scissors',
'paper' : 'rock',
'scissors' : 'paper'
}
self.match()
def err(self, msg):
print(f"ERROR: {msg}")
def disp_scores(self):
print(f"SCORES:\n\tYOU: {self.usr_score}\n\tCPU: {self.cpu_score}\n")
def tie(self):
print("It's a tie")
self.usr_score += 1
self.cpu_score += 1
def usr_win(self):
print("You won!")
self.usr_score += 1
def cpu_win(self):
print("CPU won!")
self.cpu_score += 1
def ask(self):
pass
def match(self):
pass
These 'helper' functions are going to be called each time we run the match
function. You may have also noticed the ask
function almost at the very end of the class
definition. This function has to make sure that we get user input in the correct format before evaluating the results. This function, in case of invalid user input becomes recursive, so "Yaaay!". Let's see...
First of all, in the ask
function, we want to display the options. For user's convenience, we are going to enumerate them:
# now concentrating on the ask function
def ask(self):
for i in range( len(self.opt) ): print(f"{i + 1}. {self.opt[i]}")
# it's incomplete, we're gonna write some more here!
Now, we are going to ask user for input
:
# now concentrating on the ask function
def ask(self):
for i in range( len(self.opt) ): print(f"{i + 1}. {self.opt[i]}")
usr = input("\nSelect one option: ")
# it's incomplete, we're gonna write some more here!
We, as software developers, have to think ahead and always assume that user is an idiot who always makes mistakes (but we still love him). Therefore, next few lines are going to deal with exception
s that may arise if an invalid input is given:
# now concentrating on the ask function
def ask(self):
for i in range( len(self.opt) ): print(f"{i + 1}. {self.opt[i]}")
usr = input("\nSelect one option: ")
try: usr = int(usr) - 1
except ValueError:
self.err("INPUT INVALID! YOU MUST INPUT A NUMBER!\n")
return self.ask()
if -1 < usr < len(self.opt): return self.opt[usr]
else:
self.err( "INPUT INVALID! YOU MUST INPUT A NUMBER BETWEEN 1 AND {len(self.opt)}!\n" )
return self.ask()
Note that whenever we encounter an error, we let user know by displaying an error message and then we return self.ask()
. This is because we want to run function ask
again and again until user would be so kind as to provide us with a valid input value which we are then going to return. At this point, we have actually completed three fourth of our game! Congrats!
Now the last bit: the match
function. The match
function is a lazy dude that uses other functions' labour to fulfill its own purpose, but we aren't angry at it... after all, we've designed it this way :)
First two lines will select CPU's choice and ask
user to input their answer. Let's also print
those, so that the user knows what CPU's choice was. By the way, for those of you who don't know how random.choice()
function works, it is a pseudo-random function that return
s one value from a given list
(in this case, the opt
list).
# now concentrating on the match function
def match(self):
cpu = choice(self.opt)
usr = self.ask()
print(f"You chose {usr.upper()} and CPU chose {cpu.upper()}")
When we have those choices, we can evaluate and decide who is the winner. As I said earlier on, the dict
called rules
will help us with it. How does it work then? Well, if we take a look at the rules
...
self.rules = {
'rock' : 'scissors',
'paper' : 'rock',
'scissors' : 'paper'
}
...we can see that item on the left always beats items on the right and this is very useful in this case. For every pair of choices, there are only 3 possible outcomes:
- Both choices are the same and thus, it is a tie
- User wins
- CPU wins
Therefore, logically, the first thing to do is to check whether it is a tie (it's also the easiest one). If we use user's choice as a key and request its value from the rules
and this value is equal to CPU's choice then we know that user won since we've designed rules
in such way the any key 'beats' its value. If it is not a tie and user didn't win, then we must give point to the CPU. This is how it looks in code:
# focusing on the match function
def match(self):
cpu = choice(self.opt)
usr = self.ask()
print(f"You chose {usr.upper()} and CPU chose {cpu.upper()}")
if usr == cpu: self.tie()
else:
if self.rules[usr] == cpu: self.usr_win()
else: self.cpu_win()
The match
function is almost complete now: only 5 lines to go. We want to display scores, which is easily done using the disp_scores
function. And the last bit: we want to know whether the user wants to play one more time or quit the game, so we declare a bool
variable that is going to be the answer to this question and its value will, once more, depend on the user input
. If it is True
, we'll call our match
function again, otherwise we'll print("Bye-bye!")
.
# focusing on the match function
def match(self):
cpu = choice(self.opt)
usr = self.ask()
print(f"You chose {usr.upper()} and CPU chose {cpu.upper()}")
if usr == cpu: self.tie()
else:
if self.rules[usr] == cpu: self.usr_win()
else: self.cpu_win()
self.disp_scores()
endit = input("If you want to stop, enter 'q'. Otherwise, press ENTER. ").lower() == 'q'
print() # this is just for spacing
if not endit: self.match()
else: print("Bye-bye!")
That's it fellas. We are officially done now. Hope it was fun.
There you have the Link to the Repl containing my source code for this game.
I love your program!!! It's awesome and beautiful. I learned a lot from it!
I find out that there have a small bug in line 30.
self.err( "INPUT INVALID! YOU MUST INPUT A NUMBER BETWEEN 1 AND {len(self.opt)}!\n" )
This error keep popup TypeError: can only concatenate str (not "int") to str
need to use
str(len(self.title))
I am a very beginner in Python. Thank you for sharing your program!
I really love python and hope one day I'll be able to make a game like this.(I'm a complete beginner)
It is a really awesome game! Thanksfor sharing it!
Nice optimised piece of code there Timmy :-)
Nice! Thanks for sharing! :)
Thanks, complete beginner like some of these peeps down here, really helped, and I learned a few terms here and there from it. Thanks! :) ;)