How to Make a Game Play Again in Python
Game programming is a great style to acquire how to program. You use many tools that you lot'll meet in the real earth, plus yous get to play a game to test your results! An ideal game to beginning your Python game programming journey is stone paper scissors.
In this tutorial, you'll learn how to:
- Code your own stone paper scissors game
- Take in user input with
input()
- Play several games in a row using a
while
loop - Clean up your lawmaking with
Enum
and functions - Define more circuitous rules with a dictionary
What Is Rock Paper Scissors?
Yous may take played rock paper scissors before. Perhaps yous've used it to make up one's mind who pays for dinner or who gets first choice of players for a squad.
If yous're unfamiliar, rock paper scissors is a paw game for two or more players. Participants say "rock, paper, pair of scissors" and then simultaneously course their easily into the shape of a rock (a fist), a slice of paper (palm facing downward), or a pair of scissors (two fingers extended). The rules are straightforward:
- Rock smashes scissors.
- Paper covers stone.
- Pair of scissors cut newspaper.
Now that you lot take the rules downwards, you can starting time thinking nearly how they might translate to Python code.
Play a Single Game of Rock Paper Scissors in Python
Using the description and rules above, you can brand a game of rock paper scissors. Before you dive in, you're going to need to import the module you'll utilize to simulate the computer'south choices:
Awesome! Now you're able to use the dissimilar tools within random
to randomize the reckoner's actions in the game. Now what? Since your users will too demand to exist able to cull their actions, the first logical thing you need is a way to accept in user input.
Take User Input
Taking input from a user is pretty straightforward in Python. The goal here is to inquire the user what they would like to choose as an action and then assign that choice to a variable:
user_action = input ( "Enter a pick (rock, paper, scissors): " )
This will prompt the user to enter a selection and salve it to a variable for later on employ. At present that the user has selected an activity, the computer needs to make up one's mind what to exercise.
Make the Computer Cull
A competitive game of rock newspaper pair of scissors involves strategy. Rather than trying to develop a model for that, though, you can relieve yourself some time by having the computer select a random action. Random selections are a not bad way to take the reckoner cull a pseudorandom value.
Yous can use random.selection()
to have the computer randomly select between the actions:
possible_actions = [ "rock" , "newspaper" , "pair of scissors" ] computer_action = random . choice ( possible_actions )
This allows a random element to be selected from the listing. You can also print the choices that the user and the computer made:
print ( f " \due north You lot chose { user_action } , computer chose { computer_action } . \n " )
Printing the user and computer deportment can exist helpful to the user, and it tin also help y'all debug subsequently in instance something isn't quite right with the outcome.
Determine a Winner
At present that both players have made their choice, y'all just need a way to decide who wins. Using an if
… elif
… else
cake, you tin compare players' choices and determine a winner:
if user_action == computer_action : print ( f "Both players selected { user_action } . It's a tie!" ) elif user_action == "rock" : if computer_action == "pair of scissors" : impress ( "Rock smashes scissors! Y'all win!" ) else : print ( "Newspaper covers rock! You lose." ) elif user_action == "paper" : if computer_action == "rock" : print ( "Paper covers rock! You lot win!" ) else : print ( "Pair of scissors cuts paper! Yous lose." ) elif user_action == "scissors" : if computer_action == "paper" : print ( "Scissors cuts paper! Y'all win!" ) else : print ( "Stone smashes scissors! You lose." )
Past comparing the tie condition commencement, you go rid of quite a few cases. If you didn't do that, then you'd demand to check each possible activity for user_action
and compare it against each possible action for computer_action
. By checking the tie condition first, yous're able to know what the computer chose with only ii conditional checks of computer_action
.
And that's it! All combined, your lawmaking should now look similar this:
import random user_action = input ( "Enter a choice (rock, paper, scissors): " ) possible_actions = [ "rock" , "newspaper" , "scissors" ] computer_action = random . option ( possible_actions ) print ( f " \north You chose { user_action } , estimator chose { computer_action } . \n " ) if user_action == computer_action : impress ( f "Both players selected { user_action } . It's a tie!" ) elif user_action == "rock" : if computer_action == "pair of scissors" : print ( "Stone smashes pair of scissors! You win!" ) else : print ( "Newspaper covers rock! Y'all lose." ) elif user_action == "paper" : if computer_action == "rock" : print ( "Paper covers rock! You win!" ) else : print ( "Scissors cuts paper! You lot lose." ) elif user_action == "scissors" : if computer_action == "paper" : print ( "Pair of scissors cuts paper! You win!" ) else : impress ( "Stone smashes scissors! You lose." )
You've at present written code to take in user input, select a random activity for the computer, and decide the winner! But this only lets you play one game before the program finishes running.
Play Several Games in a Row
Although a single game of rock paper scissors is super fun, wouldn't it be better if yous could play several games in a row? Loops are a groovy way to create recurring events. In item, yous tin can apply a while
loop to play indefinitely:
import random while True : user_action = input ( "Enter a choice (rock, newspaper, scissors): " ) possible_actions = [ "rock" , "paper" , "scissors" ] computer_action = random . choice ( possible_actions ) impress ( f " \n You lot chose { user_action } , computer chose { computer_action } . \due north " ) if user_action == computer_action : impress ( f "Both players selected { user_action } . Information technology'due south a necktie!" ) elif user_action == "rock" : if computer_action == "scissors" : impress ( "Rock smashes scissors! You win!" ) else : print ( "Newspaper covers rock! You lose." ) elif user_action == "paper" : if computer_action == "rock" : print ( "Paper covers rock! You lot win!" ) else : print ( "Pair of scissors cuts paper! You lot lose." ) elif user_action == "pair of scissors" : if computer_action == "paper" : print ( "Pair of scissors cuts paper! You lot win!" ) else : print ( "Rock smashes scissors! Y'all lose." ) play_again = input ( "Play again? (y/n): " ) if play_again . lower () != "y" : pause
Notice the highlighted lines to a higher place. It'due south important to check if the user wants to play once more and to pause
if they don't. Without that check, the user would exist forced to play until they terminated the panel using Ctrl + C or a like method.
The check for playing again is a check against the cord "y"
. But checking for something specific similar this might get in harder for the user cease playing. What if the user types "yes"
or "no"
? String comparison is often tricky because you never know what the user might enter. They might do all lowercase, all upper-case letter, or even a mixture of the two.
Here are the results of a few dissimilar cord comparisons:
>>>
>>> play_again = "yes" >>> play_again == "due north" False >>> play_again != "y" True
Hmm. That's non what you want. The user might not be likewise happy if they enter "yes"
expecting to play again but are kicked from the game.
Describe an Action With enum.IntEnum
Because cord comparisons tin can cause bug similar y'all saw in a higher place, it'southward a good idea to avoid them whenever possible. One of the showtime things your plan asks, however, is for the user to input a string! What if the user inputs "Stone"
or "rOck"
by mistake? Capitalization matters, so they won't be equal:
>>>
>>> impress ( "rock" == "Rock" ) Imitation
Since capitalization matters, "r"
and "R"
aren't equal. One possible solution would be to utilise numbers instead. Assigning each activity a number could save you some trouble:
ROCK_ACTION = 0 PAPER_ACTION = i SCISSORS_ACTION = two
This allows you lot to reference different actions by their assigned number. Integers don't suffer the same comparison issues equally strings, so this could work. At present you tin have the user input a number and compare information technology directly confronting those values:
user_input = input ( "Enter a pick (rock[0], paper[one], scissors[ii]): " ) user_action = int ( user_input ) if user_action == ROCK_ACTION : # Handle ROCK_ACTION
Because input()
returns a cord, y'all need to convert the render value to an integer using int()
. And then y'all can compare the input to each of the actions above. This works well, but information technology might rely on you naming variables correctly in club to keep rails of them. A improve way is to use enum.IntEnum
and ascertain your own action form!
Using enum.IntEnum
allows you lot to create attributes and assign them values similar to those shown above. This helps make clean up your lawmaking past grouping deportment into their own namespaces and making the code more expressive:
from enum import IntEnum class Action ( IntEnum ): Rock = 0 Paper = one Pair of scissors = 2
This creates a custom Action
that you lot tin can apply to reference the different types of actions you lot support. It works by assigning each aspect within it to a value you specify.
Comparisons are all the same straightforward, and now they have a helpful class proper noun associated with them:
>>>
>>> Action . Rock == Action . Rock True
Because the fellow member values are the same, the comparison is equal. The class names also make information technology more obvious that yous want to compare two actions.
Y'all can even create an Action
from an int
:
>>>
>>> Activity . Stone == Action ( 0 ) True >>> Action ( 0 ) <Activeness.Rock: 0>
Activeness
looks at the value passed in and returns the appropriate Action
. This is helpful considering now you can take in the user input as an int
and create an Action
from information technology. No more than worrying nigh spelling!
The Flow(chart) of Your Program
Although rock paper pair of scissors might seem uncomplicated, it'south important to carefully consider the steps involved in playing it so that you can be sure your program covers all possible scenarios. For any project, even pocket-size ones, it's helpful to create a flowchart of the desired beliefs and implement code effectually it. You could attain a similar issue using a bulleted listing, but it'd be harder to capture things like loops and provisional logic.
Flowcharts don't have to be overly complicated or even use real code. Just describing the desired behavior ahead of time tin help you fix problems before they happen!
Hither's a flowchart that describes a unmarried game of stone newspaper scissors:
![Flow chart for a single game of rock paper scissors](https://files.realpython.com/media/rps-flowchart-single-game.214891bb82d6.png)
Each player selects an activeness and then a winner is adamant. This flowchart is accurate for a single game as y'all've coded it, only it'due south not necessarily accurate for existent-life games. In real life, the players select their actions simultaneously rather than ane at a time like the flowchart suggests.
In the coded version, however, this works because the player's choice is hidden from the figurer, and the estimator'due south option is hidden from the role player. The two players can brand their choices at different times without affecting the fairness of the game.
Flowcharts help you grab possible mistakes early on and also let you encounter if you want to add more functionality. For instance, here's a flowchart that describes how to play games repeatedly until the user decides to stop:
![Flow chart for playing repeated games of rock paper scissors until the user decides to stop.](https://files.realpython.com/media/rps-flowchart-repeated-games.f78a64553925.png)
Without writing code, you lot can come across that the outset flowchart doesn't have a manner to play again. This approach allows you to tackle issues like these before programming, which helps you create neater, more manageable code!
Split Your Lawmaking Into Functions
Now that you've outlined the catamenia of your programme using a flowchart, you tin try to organize your code so that it more closely resembles the steps you've identified. One natural style to do this is to create a role for each footstep in the flowchart. Functions are a great way to dissever larger chunks of lawmaking into smaller, more manageable pieces.
You don't necessarily need to create a function for the conditional check to play again, merely you lot tin if you lot'd like. You lot can start by importing random
if you haven't already and defining your Action
class:
import random from enum import IntEnum class Action ( IntEnum ): Rock = 0 Paper = 1 Pair of scissors = 2
Hopefully this all looks familiar so far! Now here'south the code for get_user_selection()
, which doesn't accept in any arguments and returns an Action
:
def get_user_selection (): user_input = input ( "Enter a choice (stone[0], newspaper[1], scissors[ii]): " ) selection = int ( user_input ) action = Action ( choice ) return action
Notice how you take in the user input as an int
and go dorsum an Action
. That long message for the user is a bit cumbersome, though. What would happen if you lot wanted to add more actions? You'd take to add together even more text to the prompt.
Instead, you lot can utilize a listing comprehension to generate a portion of the input:
def get_user_selection (): choices = [ f " { action . name } [ { action . value } ]" for action in Action ] choices_str = ", " . join ( choices ) selection = int ( input ( f "Enter a option ( { choices_str } ): " )) action = Action ( selection ) return activeness
Now you no longer need to worry nearly adding or removing actions in the future! Testing this out, you tin can see how the lawmaking prompts the user and returns an action associated with the user's input value:
>>>
>>> get_user_selection () Enter a choice (rock[0], paper[1], scissors[2]): 0 <Action.Stone: 0>
Now you need a function for getting the estimator'southward activity. Like get_user_selection()
, this function should accept no arguments and return an Activeness
. Considering the values for Activeness
range from 0
to 2
, you lot're going to desire to generate a random number within that range. random.randint()
tin help with that.
random.randint()
returns a random value between a specified minimum and maximum (inclusive). You tin employ len()
to assistance figure out what the upper jump should exist in your lawmaking:
def get_computer_selection (): selection = random . randint ( 0 , len ( Action ) - 1 ) action = Action ( selection ) return activity
Because the Action
values get-go counting from 0
, and len() starts counting from ane
, it'due south important to do len(Action) - 1
.
When you test this, in that location won't be a prompt. It volition simply return the action associated with the random number:
>>>
>>> get_computer_selection () <Action.Scissors: ii>
Looking good! Adjacent, you lot need a way to determine a winner. This function will take two arguments, the user's action and the computer'due south action. Information technology doesn't need to return anything since it'll but display the result to the console:
def determine_winner ( user_action , computer_action ): if user_action == computer_action : print ( f "Both players selected { user_action . name } . It's a tie!" ) elif user_action == Action . Rock : if computer_action == Action . Scissors : print ( "Rock smashes scissors! You win!" ) else : print ( "Paper covers rock! Yous lose." ) elif user_action == Activeness . Paper : if computer_action == Action . Rock : print ( "Paper covers stone! You win!" ) else : impress ( "Scissors cuts newspaper! You lose." ) elif user_action == Action . Scissors : if computer_action == Action . Paper : print ( "Scissors cuts paper! You win!" ) else : impress ( "Rock smashes scissors! Y'all lose." )
This is pretty similar to the first comparing you used to decide a winner. Now you can simply straight compare Activity
types without worrying most those pesky strings!
You tin even test this out by passing different options to determine_winner()
and seeing what gets printed:
>>>
>>> determine_winner ( Action . Rock , Activeness . Scissors ) Rock smashes scissors! Y'all win!
Since you're creating an activeness from a number, what would happen if your user tried to create an action from 3
? Retrieve, largest number you've defined so far is 2
:
>>>
>>> Action ( iii ) ValueError: 3 is not a valid Activeness
Whoops! Yous don't want that to happen. Where in the flowchart could you add some logic to ensure the user has entered a valid choice?
It makes sense to include the check immediately later the user has made their choice:
![A flow chart for playing rock paper scissors that includes a check for whether or not the user's input is valid.](https://files.realpython.com/media/rps-diagram-with-check.4397a039b248.png)
If the user enters an invalid value, then you repeat the step for getting the user's choice. The but real requirement for the user selection is that it's between 0
and ii
, inclusive. If the user's input is outside this range, then a ValueError
exception will exist raised. To avoid displaying the default error bulletin to the user, you tin handle the exception.
At present that yous've divers a few functions that reverberate the steps in your flowchart, your game logic is a lot more organized and compact. This is all your while
loop needs to contain now:
while True : try : user_action = get_user_selection () except ValueError as e : range_str = f "[0, { len ( Action ) - 1 } ]" impress ( f "Invalid selection. Enter a value in range { range_str } " ) continue computer_action = get_computer_selection () determine_winner ( user_action , computer_action ) play_again = input ( "Play over again? (y/n): " ) if play_again . lower () != "y" : interruption
Doesn't that look a lot cleaner? Detect how if the user fails to select a valid range, then y'all use continue
rather than break
. This makes the code continue to the next iteration of the loop rather than break out of information technology.
Rock Paper Pair of scissors … Lizard Spock
If you've seen The Large Bang Theory, then you may be familiar with rock paper scissors lizard Spock. If non, then here'due south a diagram depicting the game and the rules deciding the winner:
![A diagram illustrating the rules of Rock Paper Scissors Lizard Spock](https://files.realpython.com/media/rps-lizardspock.88cf9623a6f0.png)
You can use the same tools you lot learned well-nigh above to implement this game. For case, yous could add to Activity
and create values for lizard and Spock. Then you lot would just need to change get_user_selection()
and get_computer_selection()
to incorporate these options. Updating determine_winner()
, withal, would exist a lot more work.
Instead of adding a lot of if
… elif
… else
statements to your code, you can use a dictionary to assist show the relationships between actions. Dictionaries are a great manner to show a fundamental-value human relationship. In this case, the key can be an activity, like pair of scissors, and the value can be a list of actions that information technology beats.
And so what would this look similar for your determine_winner()
with only three options? Well, each Activeness
tin can beat only one other Action
, so the list would incorporate merely a single item. Here's what your code looked like before:
def determine_winner ( user_action , computer_action ): if user_action == computer_action : print ( f "Both players selected { user_action . name } . It's a tie!" ) elif user_action == Action . Rock : if computer_action == Action . Scissors : impress ( "Rock smashes scissors! You lot win!" ) else : impress ( "Paper covers rock! You lose." ) elif user_action == Action . Paper : if computer_action == Activity . Stone : impress ( "Paper covers rock! You win!" ) else : print ( "Scissors cuts paper! You lose." ) elif user_action == Action . Scissors : if computer_action == Action . Paper : print ( "Pair of scissors cuts newspaper! Yous win!" ) else : print ( "Rock smashes scissors! You lose." )
Now, instead of comparing to each Action
, you lot tin have a dictionary that describes the victory weather condition:
def determine_winner ( user_action , computer_action ): victories = { Activeness . Rock : [ Action . Scissors ], # Rock beats scissors Action . Paper : [ Activeness . Rock ], # Paper beats rock Activeness . Scissors : [ Action . Paper ] # Scissors beats paper } defeats = victories [ user_action ] if user_action == computer_action : print ( f "Both players selected { user_action . name } . It'south a tie!" ) elif computer_action in defeats : impress ( f " { user_action . name } beats { computer_action . name } ! You win!" ) else : print ( f " { computer_action . name } beats { user_action . name } ! Y'all lose." )
Y'all all the same exercise the same as before and check the tie status first. But instead of comparing each Action
, you instead compare the action that the user_input
beats against the computer_action
. Since the key-value pair is a listing, you can employ the membership operator in
to check if an element resides inside it.
Since you no longer use long if
… elif
… else
statements, information technology'due south relatively painless to add together checks for these new deportment. You tin start by adding cadger and Spock to Action
:
class Action ( IntEnum ): Stone = 0 Newspaper = 1 Scissors = 2 Lizard = 3 Spock = 4
Next, add all the victory relationships from the diagram. Brand certain to do this underneath Activeness
then that victories
will exist able to reference everything in Action
:
victories = { Action . Scissors : [ Action . Lizard , Action . Paper ], Action . Paper : [ Action . Spock , Action . Rock ], Action . Stone : [ Activity . Lizard , Action . Pair of scissors ], Activity . Cadger : [ Action . Spock , Activeness . Paper ], Activeness . Spock : [ Action . Pair of scissors , Activeness . Rock ] }
Notice how now each Action
has a list containing two elements that information technology beats. In the basic rock paper scissors implementation, there was simply i chemical element.
Because you intentionally wrote get_user_selection()
to suit new actions, yous don't have to modify annihilation almost that code. The same is true for get_computer_selection()
. Since the length of Action
has changed, the range of random numbers will too change.
Look at how much shorter and more manageable the code is at present! To see the total code for your complete program, expand the box below.
import random from enum import IntEnum grade Action ( IntEnum ): Stone = 0 Paper = 1 Scissors = 2 Lizard = 3 Spock = 4 victories = { Activity . Scissors : [ Activity . Cadger , Action . Paper ], Action . Paper : [ Action . Spock , Activity . Rock ], Activity . Rock : [ Action . Lizard , Action . Scissors ], Action . Lizard : [ Action . Spock , Action . Paper ], Action . Spock : [ Activity . Scissors , Action . Rock ] } def get_user_selection (): choices = [ f " { action . proper name } [ { activeness . value } ]" for action in Action ] choices_str = ", " . join ( choices ) selection = int ( input ( f "Enter a choice ( { choices_str } ): " )) action = Action ( option ) return action def get_computer_selection (): selection = random . randint ( 0 , len ( Action ) - ane ) action = Action ( selection ) return action def determine_winner ( user_action , computer_action ): defeats = victories [ user_action ] if user_action == computer_action : impress ( f "Both players selected { user_action . proper name } . It's a tie!" ) elif computer_action in defeats : impress ( f " { user_action . name } beats { computer_action . proper noun } ! You win!" ) else : print ( f " { computer_action . proper name } beats { user_action . name } ! You lose." ) while True : effort : user_action = get_user_selection () except ValueError as e : range_str = f "[0, { len ( Action ) - 1 } ]" print ( f "Invalid selection. Enter a value in range { range_str } " ) continue computer_action = get_computer_selection () determine_winner ( user_action , computer_action ) play_again = input ( "Play again? (y/n): " ) if play_again . lower () != "y" : break
That's it! You lot've implemented stone paper scissors lizard Spock in Python code. Double-check to make sure y'all didn't miss anything and give information technology a playthrough.
Conclusion
Congratulations! You just finished your starting time Python game! You now know how to create rock newspaper scissors from scratch, and you're able to aggrandize the number of possible actions in your game with minimal effort.
In this tutorial, you learned how to:
- Code your ain stone paper scissors game
- Take in user input with
input()
- Play several games in a row using a
while
loop - Make clean upwards your code with
Enum
and functions - Describe more complex rules with a dictionary
These tools will go along to assist you throughout your many programming adventures. If you have any questions, then feel complimentary to attain out in the comments section below.
Source: https://realpython.com/python-rock-paper-scissors/
Post a Comment for "How to Make a Game Play Again in Python"