HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Use if/else/elif conditionals to write a basic Rock Paper Scissors game

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
paperscissorsconditionalselseelifwritegamerockusebasic

Problem

I have the following code for the Rock, Paper, Scissor game. The code works fine in Python 2.7. Are there other more concise, more readable, or more pythonic ways of solving this problem in Python?

player1 = raw_input ("?")
player2 = raw_input ("?")

if (player1 == 'rock' and player2 == 'scissors'):
    print "Player 1 wins."

elif (player1 == 'rock' and player2 == 'rock'):
    print "Tie"

elif (player1 == 'scissors' and player2 == 'paper'):
    print "Player 1 wins."

elif (player2 == 'scissors' and player2 == 'scissors'):
    print "Tie"

elif (player1 == 'paper' and player2 == 'paper'):
    print "Tie"

elif (player1 == 'paper' and player2 == 'scissors'):
    print "Player 2 wins."

elif (player1 == 'rock'and player2 == 'paper'):
    print "Player 2 wins."

elif (player1 == 'paper' and player2 == 'rock'):
    print "Player 2 wins."

elif (player1 == 'scissors' and player2 == 'rock'):
    print "Player 2 wins."
else:
    print "This is not a valid object selection."

Solution

Here are some suggestions:

-
We have three if statements that end in "Tie". We could take all three conditions at once ((player1 == 'scissors' and player2 == 'scissors') or ...), or we could notice that we want the two players to have picked the same thing. So immediately we can simplify these three statements:

if (player1 == player2):
    print "Tie"


That's a lot shorter and simpler, and it doesn't depend on what choices are available. This would work just as well in a game with five or fifty options.

-
There are still six if statements left, for the cases where the two players pick different things. As suggested by Joel in the comments, a dictionary explaining the hierarchy between the different choices might be useful.

beats = {
'scissors': 'rock',
'rock': 'paper',
'paper': 'scissors',
}


Note that I've done it the other way round to Joel: I think 'rock' = beats['scissors'] looks more natural, but I don’t think it makes too much of a difference.

Now we have a very natural way to describe the cases when the players don't pick the same thing:

if (player1 == player2):
print "Tie"
elif (player1 == beats[player2]):
print "Player 1 wins."
elif (player2 == beats[player1]):
print "Player 2 wins."


-
Where once there were nine if statements, now there are just three, and this can be made to work for any variant on the game. By swapping out beats, we could play Rock-Paper-Scissors-Lizard-Spock, Bear-Hunter-Ninja, Bacon-Lettuce-Tomato, or anything else we wanted.

This is a good idea for programming in general: we've separated the game logic from the specific choices. Keeping them separate makes our program easier to write and to debug.

-
I like to wrap self-contained blocks of code (like this game logic) into their own functions.

def play_game(player1, player2, beats):
"""Returns the result of the game with player1 vs player2."""
if (player1 == player2):
return "Tie"
elif (player1 == beats[player2]):
return "Player 1 wins."
elif (player2 == beats[player1]):
return "Player 2 wins."


-
Now the top and bottom matter. This gets the players’s moves. By separating this from the game logic, we can work on this without worrying about whether we've broken the game.

To get the player's move, you've used:

player1 = raw_input("?")
player2 = raw_input("?")


This is the right way to do it, but the "?" is unnecessarily ambiguous. You should be clear what you want. To quote from The Zen of Python:


Explicit is better than implicit.

So let’s write a full question here, so that it's clear what the choices mean:

player1 = raw_input("What is player 1's move? ")
player2 = raw_input("What is player 2's move? ")


-
To validate the input, you add an else block at the end of the code:

else:
print "This is not a valid object selection."


You'd be better off validating the input when you receive it. Right now, the players have no way of telling which of them entered the wrong thing.

Here's one way you might do that:

player1 = raw_input("What is player 1's move? ")

while player1 not in ['rock', 'paper', 'scissors']:
player1 = raw_input("That is not a valid object selection. Please try again. ")


Here we continue asking until we get something we like. But we can be more helpful. For example, you could tell the player what choices are available (you could even print this before the players make their initial choices).

While we're looking at this, we can use beats.keys() to get the list of possible choices, rather than typing them out every time. Here's an example:

print "The choices are " + str(beats.keys())

player1 = raw_input("What is player 1's move? ")
while player1 not in beats.keys():
player1 = raw_input("I didn't get that. Please choose from " + str(beats.keys()))


As an additional point, you might want to do some sort of “fuzzy” matching on the input. The program needs to get the string "rock" precisely, but it’s pretty clear what somebody who types "Rock" or "rock\n" means. String methods like lower() and strip() might be useful here.

-
Finally, consider putting this code inside a special section if __name__ == '__main__'.

Any code in this section only gets used if the file is run directly (e.g., if somebody types $ python rockpaper.py at the command line). On the other hand, if you use import rockpaper in another script, it gets ignored. Right now, anybody who import's this script starts playing rock-paper-scissors (which they may not want).

There's more information on this around the web or on Stack Overflow.

So this is the sort of thing we might be left with:

`def play_game(player1, player2, beats):
"""Returns the result of the game with player1 vs player2."""
if (player1 == player2):
return "Tie"
elif (player1 == beats[player2]):
return "Player 1 wins."
elif (player2 == beats[player1]):
return "Player 2 wins."

if

Code Snippets

if (player1 == player2):
    print "Tie"

Context

StackExchange Code Review Q#43024, answer score: 7

Revisions (0)

No revisions yet.