Thursday, 16 May 2013

Python - Rock Paper Scissors inside a GUI

In a previous blog post I explained how to write a program to play Rock Paper and Scissors against a computer. This worked very well, and probably provided hours of entertainment ;-), but it did not look very pretty. Everything was displayed by text scrolling in a shell window.

It would be much nicer if we could put this game into a GUI (Graphical User Interface)

Well it turns out this is easier than you think! There are a few tools available in Python to allow you to program a GUI. In this blog I will show you how to use one particular tool called Tkinter to do this.

We can re-use a lot of code from our RockPaperScissors.py program, so lets start with that as our base code. If you need a copy of the old program you can download it from the link below.

Download RockPaperScissors.py

So how does Tkinter work?

Well in our previous RockPaperScissors.py program we interacted with the program from a shell window. We typed 'R' to select Rock, 'P' for Paper, and 'S' for Scissors. Pressing return set off a chain of events which caused the computer to randomly select one of Rock, Paper or Scissors, which then went on to compare the two and finally declare a winner.

Instead of typing into a text window, by using Tkinter we can assign widgets such as buttons or radio buttons to input our options, and to set the program running.

The program is doing the same thing, but we are communicating with it in a slightly different way.

At first sight, it looks complicated, but it's not.

The first thing we need to do is to import the tkinter libraries into our program. So at the very top of the program, above import random and import sys add the following lines.

from Tkinter import *
from ttk import *



The next thing we need to do is to define our GUI window. Right at the bottom of the RockPaperScissors code type the following.

root = Tk()
root.title ('Rock Paper Scissors')



The first line defines the root window, and the second line adds a title into the window. Rock Paper Scissors seems like a good enough title to me.

Now within our window we need to have a frame. A frame can split the window into different areas. We only need one frame. These next few lines defines how this frame fits within our window. Type the following below the lines you have just added.

mainframe = Frame(root, padding = '3 3 12 12')
mainframe.grid(column=0, row = 0, sticky=(N,W,E,S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0,weight=1)




The first row defines the padding, or space between our frame and window. We may modify this later to improve the look of our game. The next three lines help determine the layout style of the window.

In TKinter there are several methods you can use for layout. We are going to use the grid option in this game. The grid system gives more control than some of the others.

So what is the grid system? Well imagine splitting your window up into columns and rows. The top row would be row 0 and the left column would be column 0. If I wanted to put something such as a button in the top left I would place it in column = 0 and row = 0. If I wanted to put something to the right of this it would be column = 1 and row = 0. Below the original item would be row 1 and column 0. Very similar to a graph axis or map co-ordinates, with top left being row 0 and column 0.

If for example you didn't have anything in row 0, or column 0, then the program would ignore these, until something was put in there. So if I put my first item in row 1 and column 1 that would be in the top left.

Lets do just that now.

I am going to put a label in the top left of the window. A label is a Tkinter widget, that allows you to display text. Probably the simplest of widgets available.

Label(mainframe, text='Player').grid(column=1, row = 1, sticky = W)



Looks complicated right? Well no... remember with programming to break things down into little bits, and that's the same with looking at code.

Label - tells the program you want to add a label widget.
mainframe - you want the widget in the mainframe. We only have one frame in this program, but its feasible you could have others.
text - 'Player', This allows us to input what our label should say. Put your name in here if you want to.
grid - remember we are using the grid layout system. grid and then the items in the bracket allow us to define where we want the label in our grid.
column = 1 - We want to place our widget in column 1
row = 1 - We want to place our widget in row 1
sticky = W Says we want to align our text with the left side of the box. Tkinter uses points of a compass (N,S,E,W) to describe where we want to align our items.

There we go, see I told you it wasn't complicated!

Should we test this now? Well before we do lets comment out everything in our code from While True: to print " "

To do this highlight everything you want to comment out by dragging the mouse over it and then click on Format, and then Comment Out Region. You could highlight the area and press Alt and 3 to do the same thing. The lines commented out will be shown in red.



Why do we do this? Well as I said previously Tkinter calls things in a different way, by using buttons and other widgets. The GUI also has its own eternal loop running, so the program doesn't quit. Therefore we do not need to have a while loop to keep our program running.

lets add root.mainloop() right at the bottom of the program.

root.mainloop()

This is responsible for telling the program to run the GUI.

Press F5 and save the file as RockPaperScissorsGUI.py when asked.

Does your window look like this?



How cool is that? Ok its not massively inspiring, but it is the building blocks for greater things!!

So how should we structure the rest of the GUI?

My plan is underneath the player label we will have the option to chose either Rock, Paper or Scissors. There are a few ways to do this, but I like the option of Radiobuttons. A Radiobutton allows you to select one, and only one, of a few different options. As we only want the Player to select one of Rock, Paper or Scissors sounds like a good option.

So lets sort out the three Radiobuttons.

The radiobuttons use mainly the same options as we saw in the label with three exceptions.

Radiobutton(mainframe, text ='Rock', variable = player_choice, value = 'Rock').grid(column=1, row=2, sticky=W)
Radiobutton(mainframe, text ='Paper', variable = player_choice, value = 'Paper').grid(column=1, row=3, sticky=W)
Radiobutton(mainframe, text ='Scissors', variable = player_choice, value = 'Scissors').grid(column=1, row=4, sticky=W)



Lets look at the Radiobutton widget and see how this varies when compared to the Label widget.

Radiobutton - This has changed from Label to Radiobutton, which is obvious, as we are calling a different type of widget.
text - defines the text next to the Radiobutton so you know what each one refers to.
variable - This is new to Radiobutton. You use the variable to store some information to indicate which Radiobutton you have selected.
value - defines what you store in that variable. We are using the strings Rock, Paper and Scissors.
grid onwards remains the same as for Label and defines where the radiobuttons are positioned. In this case they are all in column 1, but below each other in Row 2, 3 and 4.

Before we run the program to have a look at it we need to create the variable we refer to which is called player_choice. This is the variable which will hold either Rock, Paper or Scissors, depending on the Radiobutton selected.

I try to keep all my variables together, and put them before the start of the layout code. Therefore above the line of code specifying the first label you created put the following line.

player_choice = StringVar()



This defines the variable player_choice, as a String Variable. Tkinter is able to refer back to this variable at any time.

Again press F5 to save and run your program. Does it look like this?



Try clicking on the Radio Buttons. Notice how they light up as you pick each one. The program is really coming together now.

We want to do the same thing for the computer. So try and create the code to show a label named as Computer and to reference a variable called computer_choice for the Radiobuttons. Ensure these labels and buttons are in column 3.

Did your code look like this?

Label(mainframe, text='Computer').grid(column=3, row = 1, sticky = W)
Radiobutton(mainframe, text ='Rock', variable = computer_choice, value = 'Rock').grid(column=3, row=2, sticky=W)
Radiobutton(mainframe, text ='Paper', variable = computer_choice, value = 'Paper').grid(column=3, row=3, sticky=W)
Radiobutton(mainframe, text ='Scissors', variable = computer_choice, value = 'Scissors').grid(column=3, row=4, sticky=W)

Did you also remember to create a new String Variable called computer_choice?

computer_choice = StringVar()


OK we have Radiobuttons and labels for both the computer and for the player. We now need a button to tell the program we have made our choice, and we want to play against the computer.

Lets put the button between the existing columns i.e. into column 2.

Button(mainframe, text="Play", command = play).grid(column = 2, row = 2, sticky = W)



The button widget is again like the other widgets. It is in the mainframe, it has text to label the button and it has some information in the grid section to describe the layout of where we want the button. Additionally it has command = play.

When we press the button we obviously want it to do something, and it is this command statement which allows us to tell the program what we want it to do. In this case we are saying command = play, which tells our program that when the button is pressed we want to run the function called play.

Now in our code, we do not have a play function. Before we started to work on the GUI our program used a while loop to continuously run the program. The play function we need to write is carrying out a lot of the work which was done in our while loop. It requires us to get the human choice, the computer choice and compare them, then declare a winner. Therefore we can modify the while loop to become our play function.

First uncomment out the While loop. To do this highlight the text you commented out earlier, and select Format and then Uncomment Region. Alt and 4 is a the shortcut keys to do this.

Now change
While True:

to

def play():



This creates a function called play using the code from the while loop, as we want this to be the basis of the play function which the button calls when pressed.

Now the first line in our new play function asks you to call the function makeYourChoice, and stores the result of this as a variable called human_choice. However the makeYourChoice function explained to the user what key they should press to make their choice of Rock, Paper or Scissors. We don't need any of that with our program, as we are using the Radio Buttons to do the same task. So we can delete the makeYourChoice function and all its contents.

Instead the humanChoice variable needs to be the choice of the RadioButtons we created earlier. Remember we stored the selected choice in a variable called player_choice? Therefore we should be able to just call this variable and get the information from inside it.

To get the info from inside player_choice we use the get command.

humanChoice = player_choice.get()




This line gets the information stored in player_choice, and stores it in a variable called humanChoice.

The next line calls computerRandom.

We we still want to the computer to pick a random choice, but we also want to store that random choice in the StringVar called computer_choice. This will change the computer Radiobuttons to match the choice the computer has made.

Lets make that change inside of the computerRandom function.

Inside the computerRandom function, after the line randomchoice = random.randomint(0,2) add the following line

computer_choice.set(options[randomChoice])



This line will set the value stored in computer_choice as either Rock, Paper or Scissors depending on the random choice created by the previous line.

That is the only modification we need to make to the computerRandom function so we can go back into the play function.

We see the next two lines are printing the choice of the human and the computer. We don't need these any more, as our choices are displayed graphically in the GUI, so these lines can be deleted.

The result line is the same. We still want to compare the humanChoice against the computerChoice.

Now we get into reporting the result. We don't just want to print to the shell window as we did previously, it would be much nicer if we could report back the result into the GUI.

Well we can by using a Label widget again.

Under where you created the Button widget add a new Label widget

Label(mainframe, textvariable = result_set).grid(column = 1, row = 5, sticky =W, columnspan = 2)



This is the same as the previous Label widgets you have created with two exceptions.

The first is instead of text you have textvariable = result_set. This is because you want the text to change depending on who won. So the label will be populated with whatever text is stored in the result_set variable.

The second change is we have added columnspan = 2. What this is saying is although we have defined our text to be in column = 1, if it doesn't fit, allow it to span into the next column.

OK before we can start to populate the variable result_set we have to create it first.

Head back to the area where all the variables are defined and add

result_set = StringVar() underneath the two already defined.



We can then finish off the play function by populating the results_set variable with the result.

This is done by the command result_set.set("text goes here")

We need to report back a Rock, Paper or Scissors.

By modifying the text which previously printed the result into the Python shell window we can update the label we have just created to report back the result.

To do that instead of printing we need to set the data in the result_set variable.

if result == "Draw":
        result_set.set("Its a draw")
    elif result == "Computer Wins":
        result_set.set("Unlucky you lost!")
    else:  result_set.set("Well done you won!")



Delete the print " " line.

Lets save and test your program by pressing F5.

Does this work as you expected?

One other thing I notice is that the Player doesn't have a choice to begin with. Which is fine, but what happens if the player doesn't choose anything? The program states that the Player is the winner. Hardly seems fair that the player can win without making a choice, so lets think how we can fix that.

There are two ways I can think of immediately.
  • We could ensure the player has a default option picked.
  • We could check to ensure there is an option picked and report back if there isn't.
To set the default option for the player we can set the player_choice variable to be Rock by default.

Under where you set your variables, we will assign Rock to player_choice.

player_choice.set("Rock")




Running the program shows that Rock is chosen by default.

Or if you would prefer modify the play() function to check that a player_choice has been set.

if player_choice.get() != '':

Then indent the remainder of the function adding the following at the end.

    else:
result_set.set("Please choose an option")



I will let you decide which one of those you would like to include in your program, if any!

Hope you enjoyed your first venture into TKinter. I would suggest playing around with it a little so you can see what changes.

For those of you who would like the source code you can down load it from the link below. I have left the code, which I have said delete, commented out so you can see the changes. The code has both options for overcoming the fact the player could win without selecting a choice, but they are both commented out. You can choose which one you want to implement.

RockPaperScissors-GUI.py

Tuesday, 7 May 2013

Basic Python - Rock Paper Scissors

When thinking about ideas for a Python tutorial I remembered the old game, Rock Paper Scissors. This is a fun game to write in Python and includes loads of concepts, so makes it an ideal beginner program to write.

Lets think first of all how we would structure our program.

  • Firstly a human will need to make a choice of Rock, Paper or Scissors.
  • Secondly the computer will need to make its choice, this will need to be random.
  • These two choices need to be compared.
  • Finally the winner needs to be declared.

I have written this in Python 2.7.

To load Python 2.7 click on the IDLE icon on the desktop.

This should open the following window




Then click on File then New Window to get to the stage where you can start to enter your program.



So lets look into how we can write this.

The first thing is you need to make a choice. Lets write this as a function, which we can call later. Remember the idea behind a function is not complicated. Its just a series of instructions you can call any time.

Lets call the function makeYourChoice()

def makeYourChoice():



We want to be able to ask for the user input so they can choose between Rock, Paper and Scissors. Perhaps also we might want to add an option to quit, as I guess our game will keep running until we want to leave it.

Lets tell the person playing the game what we would like them to input for each choice. I have suggested R to be typed for Rock, P for Paper and S for Scissors. Q sounds about right for quit. Note the indent of a tab (4 spaces) before each of these lines, as we are inside our function.

    print "Press R for Rock"
    print "Press P for Paper"
    print "Press S for Scissors"
    print "Press Q to Quit!"



Now we have asked the user what they would like to choose we need to get them to input their choice. The raw_input command is what you need for this. So underneath your last line enter.

    userChoice = raw_input("What do you want to choose?").lower()


So this line creates a variable userChoice. Into this variable it adds the input the user enters. At the same time it shows some text explaining what it is the program is expecting the user to enter. Finally, I have converted whatever is entered into lower case text with .lower(). This way, I know if the user enters upper or lower case, I will only be dealing with lower case.

So we have given the user a list of options, and asked them to make a choice. We can see we will have an r,p,s or q stored in our variable userChoice.

To make it a little easier later on, lets turn the r,p & s into the words "Rock", "Paper" and "Scissors"

For Rock we want to say if the user has chosen 'r' let the function return "Rock" to be used in the rest of the program.

    if userChoice == "r":
        return "Rock"


Remember putting r and Rock inside " " or ' ' indicates it is a string of letters we are dealing with.

So thats the option for Rock so this needs repeating for Paper and Scissors. Why not try programming those yourself? Is this what you typed?




The last option is q for quit. Well if the user wants to quit, we don't want to return anything to the program, we just want to quit. So lets add a line to do that.

    if userChoice == "q":
        sys.exit(0)



This relies on you having the sys libraries available, so you need to put the following line at the very top of your program.

import sys




Right we have covered the option for Rock, Paper, Scissors and Quit. That's everything, right? Or is it? What if the user was to type something other than the four options? What would our program do then? I think its worth putting something in there in case they do press something else. It is safe to say people don't always follow instructions!

Let us add in something which says if they don't do any of the above, then give them the instructions again. We can use an else statement. These go very well after if commands. It means do all the things in the if statement first, else for all other cases do this.

    else:
        makeYourChoice()



So what we are going to do is say if you have not chosen any of the if statements, run the else statement. The else option tells the computer to run the function we are writing again. We know this is the function which asks people to make their choice, and records it. So if they have that wrong, just ask them again!

Right so that is the first stage finished. There were a few interesting things in there. Firstly the print commands to ask the user to do something. Next we had the raw_input asking the user to enter something, and storing the input into a variable. Finally we had some if statements, and the else statement. These allow choices to be made.

So here is our function in full.



Should we test this now?

We know we are returning either Rock, Paper or Scissors, so lets just run our function with a print statememt to see what it says.

Below our function type the following

print makeYourChoice()



This line will print the output from the makeYourChoice() function.

Now run this program by pressing F5. This will save the program first.

Call the program RockPaperScissors.py when asked.



When asked try typing R. What happens? Run it again and test for P and S.  What about a small p?

Now try something to test our else statement. Type in g. What happened. This was different from R,P or S. What if you type in Rock. Was this expected? Remember our if statements only pick up a R,P or S, so our program sees Rock the same as it sees a g.

Finally run it again and see what happens if you try q for quit?

We have finished with the print makeYourChoice() line so it is no longer required. You can either delete the line or you can place a # in front of the line to make Python ignore it. I am going to delete it.

Thats stage one finished, so we can now move onto stage 2.

Stage 2 is the fact the computer needs to make a choice. What we need to do is get the computer to randomly choose either Rock, Paper or Scissors. Lets write this as a function.

The first thing we need to do is import the random libraries into Python. This allows us to use some pre-programmed commands based on making random decisions. To import random into Python at the very top of the program type

import random



Below the end of the makeYourChoice() function, lets start a new function called computerRandom() which we will use for the computer to make its choice.

def computerRandom():

The first thing we need to do is to give the computer the three options to pick from. Lets store these in a list called options.

    options = ["Rock","Paper","Scissors"]

The options are in a list. The first thing in a list is in position 0, the second in 1 and the third in 2. From this we know that Rock is in position 0, Paper in position 1, and Scissors in position 2. Therefore we need to randomly choose either a 0,1 or a 2.

Lets store the random choice in a variable called randomChoice.

    randomChoice = random.randint(0,2)

random.randint(0,2) calls upon the random libraries you have imported into your program. This command randomly chooses an integer between 0 and 2. An integer can only be a whole number so the overall option is either a 0, 1 or a 2. Which is what we required.

Finally lets return our option. We want to return Rock, Paper or Scissors and not a 0,1 or a 2.

    return options[randomChoice]

Your function should look like this:



Lets look at that last line in a little more detail.

return options[Randomchoice] takes the random choice, which is a 0,1 or a 2 and maps this into the options list which is either a Rock, Paper or Scissors.

options [0] would return Rock, options[1] would return Paper and options[2] would return Scissors.

The third stage is to carry out a comparison. Again lets write this as a function.

The function is to compare the choice from the player with the computer choice. So lets name the function and create it with two inputs.

def comparison (humanChoice, computerChoice)

If both players have picked the same option then it is a draw. Lets write this as an if statement.

    if humanChoice == computerChoice:
return "Draw"

This states that if both are the same (== denotes this) then the word "Draw" is returned.

The next result is if the player picks Rock and the computer picks paper. Paper beats rock, therefore the computer wins.

Again lets cover that in an if statement

    if humanChoice == "Rock" and computerChoice == "Paper":
        return "Computer Wins"

Lets do something similar for the other options where the computer would win.

  • Player picks Paper and computer picks Scissors
  • Player picks Scissors and computer picks Rock


    if humanChoice == "Paper" and computerChoice == "Scissors":
        return "Computer Wins"
    if humanChoice == "Scissors" and computerChoice == "Rock":
        return "Computer Wins"

Now what if the player were to win? Well we have covered a draw and the computer winning, so everything else has to be the player winning. Lets cover that with an else statement.

    else:
        return "Human Wins"

Your finished comparison function should now look like:



We have now written all our functions for each part of the program, so lets put all this together.

As we know we have an option to quit the game, lets make the game keep going until we decide to quit.

We can do this by putting the main body of the game in a while loop. By saying while True we will remain in the while loop until we decide to quit.

while True:



Lets get the players choice, and the computers choice by calling each of the respective functions.

    humanChoice = makeYourChoice()
    computerChoice =  computerRandom()



So the player and the computer know there is no cheating going on, and also as it makes the game look nicer, lets print out what both have chosen.

    print "You chose", humanChoice
    print "The computer chose", computerChoice



We also have written a function to compare the two choices, which will determine a winner. Lets call this function and store the result in a variable called result.

    result = comparison (humanChoice, computerChoice)



Now we can declare a winner! Lets us the if statements again to do this.

    if result == "Draw":
        print "Its a draw"
    elif result == "Computer Wins":
        print "Unlucky you lost!"
    else: print "Well done you won!"



You can see we have used an if and an else statement as previously. However we also have a elif statement. elif stands for 'else if'. There is a subtle difference between using an if statement and an elif statement. Using elif means you should only get one answer printed in this case. Although an if statement would have worked in this case I wanted to introduce the element of elif.

Finally lets add an extra line to print a space after each game.

print " "



Your whole program should now look like this





Following requests I have decided to make the source code available for this program. Although you have the source code I would suggest you type the program into your computer. This will get you used to dealing with debugging your code.