Tag: Classes

Rock, Paper, Scissors Game in Python: Part I

Rock, Paper, Scissors Game in Python: Part I

You probably have played the little hand game Rock, Paper, Scissors one in a while. Essentially two players bring up a fist, a flat hand ot two fingers to represent rock, paper or scissors. The winner is based on these three simple rules:

  • Rock breaks scissors
  • Scissors cuts paper
  • Paper covers rock

In the case of a tie, no one wins.

This little game is easy to create in Python using the keyboard to enter R, P or S, or Q to quit. The second player is the program itself which uses a random number generator to choose one of the three outcomes. This little game is sometimes given as an early assignment, but beginners usually miss some of the really clever features of Python that can make the game more elegant and succinct. We’ll illustrate tuples, classes and subclasses, and dictionaries in this simple article.

We can start by using a tuple to represent the three possible moves:

moves=("P", "R", "S")   #tuple of legal play characters

These are the only three characters that the player should be allowed to enter. So we can create a simple entry loop that repeats until one of those three characters is entered. We use the upper method to make sure the entry is uppercase. Note that the code “play in moves” returns True if the character entered is P, R or S and otherwise returns False.

legal = False
while not legal:
    play = input("enter play (P, R, S): ").upper()
    legal = play in moves 

 And, if you want to allow the player to enter “Q” to quit the game you could check for that Q and set a quit flag:

def playit(self):
    legal = False
    while not legal:
        play = input("enter play (P, R, S): ").upper()
        if play=="Q":
            quit = True
        legal = play in Player.moves or quit

 As you can see, this is now a function.  But why not make it part of a class?

You may recall that classes are a major part of Python. Classes are components that can contain data, and can exist in many instances with differing data in each.

A Player class

Our complete player class has the name of the player and a play variable containing the latest play. It also keeps the state of the quit flag and the legal flag.

class Player():
    moves=("P", "R", "S")   #tuple of legal play characters

    def __init__(self, name):
        self.name = name    #initialize name,quit, and count
        self.quit = False
        self.wincount = 0   #counter of wins

    def playit(self):
        legal = False
        while not legal:
            self.play = input("enter play (P, R, S): ").upper()
            if self.play=="Q":
                self.quit = True

            legal = self.play in Player.moves or self.quit
 # the number of wins is counted here
def countWin(self):
    self.wincount += 1
 

So, as you can see, the Player class has a playit method which sets the play variable to P, R or S. It also can be set to Q, and the quit flag indicates that this is not a real play. Note that the moves tuple is a class variable. This means it is part of all classes of this type and you can access it by Player.moves.

But, what about the other player? It is the computer program itself, and it uses the randint method to select 0, 1 or 2 and these indices indicate which of the three plays has been choses. So, we need another class like Player for the computer player.

As you may recall, you can derive new classes from old ones, so we create an AutoPlayer class derived from Player. It only contains the new playit method. All the other variables and methods are in the base Player class. So, our simple AutoPlayer class is just

class AutoPlayer(Player):
    def __init__(self, name):
        super().__init__(name) # pass the name into the parent class

    def playit(self):
        playval = randint(0, 2)     # select 0, 1 or 2
        self.play = Player.moves[playval] # get that play from the tuple
        # print out what it has selected
        print(self.name + " selects " + self.play)

So, now, we see how we can write a loop for you to play the game:

player1 = Player("You")
player2 = AutoPlayer("Auto")
while not player1.quit: #loop until a 'Q' is entered
    player1.playit()
    if not player1.quit:
        player2.playit()

Picking a Winner

But how do we know who won? This is really simple, since there are only 3 cases (plus the tie) to check for.  We can create a Dictionary of these cases and the resulting message you print out:

game= {"RS": "rock breaks scissors",
       "SP": "scissors cuts paper",
       "PR": "paper covers rock"
       }

We only need to check for ties and check for who wins, by combining the string for the two players.We do all this within a Winner class which then returns the outcome:  First we initialize the variables:

class Winner():
    game= {"RS": "rock breaks scissors",
           "SP": "scissors cuts paper",
           "PR": "paper covers rock"
           }

    def __init__(self, p1, p2):
        # copy variables into class
        # to make this simpler to read
        self.p1 = p1
        self.p2 = p2

Note that the game dictionary is also a class variable.

Then, the findWin method checks for ties and if there is no tie, it calls checkWin to check if p1 beat p2 or p2 beat p1:

# A winner matches on of the three
# game dictionary entries abouve
# we check p1+2 and p2+p1
def checkWin(self, p1, p2):
    # here are the two outcomes
    # for both player orders
    match1 = p1.play + p2.play
    match2 = p2.play + p1.play
    mesg = Winner.game.get(match1)
    if mesg != None:
        p1.countWin()
        outcome = p1.name + " win -- "+mesg
    else:
        mesg = Winner.game.get(match2)
        p2.countWin()
        outcome =  p2.name + " win -- " + mesg
    return outcome

def findWin(self):
    if self.p1.play == self.p2.play:
       return "Tie, no winner"
    else:
        return self.checkWin(self.p1,self.p2)

Note that Player class contains a countWin method that increments the winner count of that player won. Then, when you finally type ‘Q’ the program can print out the final score.

player1 = Player("You")
player2 = AutoPlayer("Auto")
while not player1.quit: #loop until a 'Q' is entered
    player1.playit()
    if not player1.quit:
        player2.playit()
        # compute the winner
        winner = Winner(player1, player2)
        print(winner.findWin()) # print winner

# print out the wins for each player
# when you type "q" for quit
print(player1.name, player1.wincount)
print(player2.name, player2.wincount)

The final output

Here is how the program works:

enter play (P, R, S): r
Auto selects R
Tie, no winner
enter play (P, R, S): r
Auto selects S
You win -- rock breaks scissors
enter play (P, R, S): q
You 2
Auto 2

Summary

We used the tuple to check for legal input (P,R or S) and then created a Player class which has a playit method and contains a play variable which contains the charact representing that play. Then we created a derived AutoPlayer class which has the same internal variables and methods, but where the playit method uses a random number function to generate 0, 1 or 2. These are then indexes into the tuple  to find the character representing that play.

Finally, we created a dictionary of the three possible plays and the message describing the winning cases and used the Winner class to find if p1+p2 won or id p2+p1 won. Thus, in this simple example we used the tuple, the dictionary, classes and a derived class to write a simple game program.

The complete code for this and the programs in the following articles is available at GitHub under jameswcooper/articles