patternpythonMinor
Use if/else/elif conditionals to write a basic Rock Paper Scissors game
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
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
Note that I've done it the other way round to Joel: I think
Now we have a very natural way to describe the cases when the players don't pick the same thing:
-
Where once there were nine
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.
-
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:
This is the right way to do it, but the
Explicit is better than implicit.
So let’s write a full question here, so that it's clear what the choices mean:
-
To validate the input, you add an
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:
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
As an additional point, you might want to do some sort of “fuzzy” matching on the input. The program needs to get the string
-
Finally, consider putting this code inside a special section
Any code in this section only gets used if the file is run directly (e.g., if somebody types
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
-
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.