My First Raspberry Pi Game " Part 12 " Scoring, done!
January 16, 2013 [Games, My First Raspberry Pi Game, Python, Raspberry Pi, Tech, Videos]Parts: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12.
Writing your first ever computer program on the Raspberry Pi.
Today, we finish!
Our game is almost done. All we need to do now is let you play several times, and give you a score at the end.
First, because we're going to use it lots of times, we need to make the ready_screen function set its background colour properly. Open redgreen.py in LeafPad, and add a single line to the function ready_screen, making it look like this:
def ready_screen():
screen.fill( pygame.Color( "black" ) )
white = pygame.Color( "white" )
write_text( screen, "Ready?", white, True )
pygame.display.flip()
Previously, ready_screen was always the first thing we did, so we got away with not drawing a background colour because it starts off plain black. Now, we need to do it.
Next, let's do the really interesting part. We want to play the game several times, and whenever we want to do something several times, we need a loop. This time we'll use a for loop, letting us go through a list of things. Scroll to the very bottom, and change the code to look like this:
# We start from here start() for i in range( 10 ): ready_screen() wait() shape() end()
The new lines are green above, and lines that haven't changed except being indented by putting four spaces at the beginning are blue.
A for loop lets you run through a list of things, running the same code each time. A for loop always looks like for NAME in LIST where NAME is the name of a new variable, and LIST is a list of things. What we've done here is make a list of 10 numbers by calling the range function and giving it an argument of 10, and told Python to put the particular item of the list that we're working on now into a variable called i.
So, the ready_screen, wait and shape functions will each get called 10 times. Each time they are called, i will be a different number. We're not using i yet, so all that matters for the moment is that the code runs 10 times. Try it out by opening LXTerminal and typing ./redgreen.py, and you'll see that you can play the game 10 times, and then it will finish.
Playing 10 times is all very well, but it's not a lot of fun if I can't see how well I've done at the end. Let's keep track of our score.
We'll award the player 1 point for every time they get it right, and no points if they get it wrong. The places where we know which of these has happened are in red_shape and green_shape. Let's change them to pass back a score (either 1 or 0) depending on what you did:
def green_shape(): ...the rest of green_shape is still here... pressed = shape_wait() if pressed: green_success() return 1 else: green_failure() return 0
def red_shape(): ...the rest of green_shape is still here... pressed = shape_wait() if pressed: red_failure() return 0 else: red_success() return 1
I've abbreviated it above, but we're not changing anything in these functions except at the very bottom, where we're adding two return lines to each function.
Whenever the player succeeds, we return a score of 1 point, and whenever they fail we return 0 points.
We're not doing anything with this score yet. We call the green_shape and red_shape functions from inside shape, so first let's make sure shape passes back the answer to where we need it:
def shape(): GREEN = 0 RED = 1 shape = random.choice( [GREEN, RED] ) if shape == GREEN: return green_shape() else: return red_shape()
shape doesn't need to do anything special here - just take the answer coming from green_shape or red_shape and use the return statement to pass it back to us.
Now shape is giving us back an answer, we can use it in the main code right at the bottom:
start() correct = 0 for i in range( 10 ): ready_screen() wait() correct += shape() end( correct )
We've made a variable called correct that keeps hold of how many correct answers we've been given (i.e. the score). It starts off as zero, and every time we call shape we add on the answer that comes back. shape will either return 0 or 1, so correct will increase by either 0 or 1 each time.
The last thing we've done here is pass the answer (the player's final score) into the end function so we can display it. To use this answer, we need to change end a bit:
def end( correct ): print "You got %d correct answers" % correct screen.fill( pygame.Color( "black" ) ) white = pygame.Color( "white" ) write_text( screen, "Thanks for playing!", white, True ) msg = "Score: %d Press a key to exit" % correct write_text( screen, msg, white, False ) pygame.display.flip() pygame.event.clear() timed_wait( 0, press_events )
We changed the def line to allow us to pass in the score, giving it the same name we used below, correct. Then we added a line that prints out the answer into the terminal, just for good measure, and we modified the write_text line, splitting it into 2 parts - creating a variable called msg containing our message, and then using it on the next line.
Twice above we've used a nice feature of Python that makes building our own messages quite simple. If you write a string like "Score: %d Press a key to exit" you can substitute a number into it using the % "operator" as we've done (an operator is something like + or / that combines 2 things). Where the %d appears in the string, it gets replaced by the number inside the variable you supply (correct in our case). You can also substitute in other strings (using %s) and lots of other things if you want to. This allows us to put the score into a string and then print it on the screen.
If you try your game now you will see it counts how many right answers you got and tells you at the end. Wouldn't it be better, though, if it told you how you were doing all the way through?
Scroll up to the ready_screen function and modify it to take two arguments and use them to keep us informed:
def ready_screen( go_number, correct ): screen.fill( pygame.Color( "black" ) ) white = pygame.Color( "white" ) write_text( screen, "Ready?", white, True ) go_number_str = "Turn: %d Score: %d" % ( ( go_number + 1 ), correct ) write_text( screen, go_number_str, pygame.Color( "white" ), False ) pygame.display.flip()
The arguments we take are called go_number and correct. correct will be the current score, as we've seen before, and go_number is the counter telling us how far we've got.
We use a slightly different form of the % operator here to substitute two values into a string instead of one. To do this, we put a list of values on the right instead of just one: ( ( go_number + 1 ), correct ). We need brackets around the outside so that Python knows it is a list and doesn't just take the first value on its own. When we use a list like this, the values will be substituted in order, one for each %d (or %s or similar) that is in the string. You must always have the same number of %ds in the string as values in the list.
You may be wondering why we have to add one to go_number. We'll see in a moment.
To be able to provide the two new arguments to ready_screen we need to change the code right at the bottom to look like this:
start()
correct = 0
for i in range( 10 ):
ready_screen( i, correct )
wait()
correct += shape()
end( correct )
Remember when we made the for loop I mentioned that i would be a different number each time we ran the code inside the loop? We pass that number in to ready_screen where it will be used as the go_number. We also pass in the current score, correct.
The reason why we needed to add 1 to go_number inside ready_screen is that when you have a loop like for i in range( 10 ), the variable i actually gets the values 0, 1, 2, ... with the last value being 9, instead of ranging from 1 to 10 as you might expect. The reasoning behind this is kind of lost in the mists of time, and kind of makes perfect sense, depending how you look at it. Anyway, believe me when I tell you that once you've got used to it you're going to find it warm and comforting, but for now you may find it a bit weird.
And, on that typically strange note, we have finished! Try out your program, and you should find it tells you what go you're on, and what your score is all the way through.
Something else you might like to do now is make your game run in full-screen mode (like many games). You can do that by changing the start function like this:
def start():
global screen
pygame.init()
screen = pygame.display.set_mode( screen_size, pygame.FULLSCREEN )
If you have any problems, compare your version with mine here: redgreen.py
I've made a slightly extended version of the game that measures your reaction speed and gives you a score based on how quickly you press. In future I may even add more features. If you'd like to follow the project, you can find it here: redgreen on github.
I'll be doing more series in the future, some for beginners like this one, and some more advanced topics. If you'd like to find out what I'm doing, subscribe to the blog RSS feed, follow me on Twitter or go to my YouTube page and subscribe.