My First Raspberry Pi Game – Part 12 – Scoring, done!

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.

My First Raspberry Pi Game – Part 11 – Being less rude

Parts: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12.

Writing your first ever computer program on the Raspberry Pi.

We’ve nearly finished our game. Next on our list is to fix that bug where you can’t exit some of the time, and make our code a bit tidier in the process.

The first thing I want to do is make the Esc key quit the game. This is fairly normal behaviour, and will help if we run in full screen mode, where there is no close button to click.

Open up redgreen.py in LeafPad as usual, and find the function shape_wait and the line if evt.type == pygame.QUIT:. Replace the whole line with this:

        if is_quit( evt ):

We’ve replaced the code asking whether the event was a quit event (i.e. the user closed the window) with a call to a function. Let’s write that function. Just above shape_wait type in this function:

def is_quit( evt ):
    return evt.type == pygame.QUIT

You’ve just done another bit of refactoring. Instead of writing the code directly in the if line, we’ve added a call to the function, and the function does exactly the same thing as we did before: it returns True if the event is a quit event, and False otherwise. If you try the program now (by saving in LeafPad, opening LXTerminal and typing ./redgreen.py) you should see it behaves exactly as it did before.

You may well ask why we did it. The answer is because now we can change the is_quit function to do something extra. Replace it with this:

def is_quit( evt ):
    return (
        evt.type == pygame.QUIT or
        (
            evt.type == pygame.KEYDOWN and
            evt.key == pygame.K_ESCAPE
        )
    )

This is a more complicated bit of logic, saying that we will return True if either of two things is true: EITHER the event is a quit event (as before), OR the event is a KEYDOWN, and the specific key that was pressed was the Escape key (“Esc”) on the keyboard. Notice that the brackets around the “and” part help us know which bits go together – we don’t want to quit for any keypress event, only one where the key is Escape.

If you try your program again, you should find you can press Escape to exit when you’re looking at a red or green shape.

The shape_wait function is quite a useful one, and the next thing we want to do is use it in a few more places. Before we can do that, we need to refactor it to make it a bit more flexible.

Make a new function called timed_wait further up, just before start, and cut the entire body of shape_wait and paste it into timed_wait. So it looks like this:

def timed_wait():
    event_types_that_cancel = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN
    ... all the rest of shape_wait here ...
    pygame.time.set_timer( finished_waiting_event_id, 0 )

Now change shape_wait to look like this:

def shape_wait():
    """
    Wait while we display a shape.  Return True if a key was pressed,
    or false otherwise.
    """
    return timed_wait()

As usual, we’ve just replaced some code with a call to a function that contains the exact same code, so hopefully our program will work exactly as before.

Now we’re going to make timed_wait a bit more general, while still preserving all the same behaviour. We do this by changing some of the variables we use in timed_wait into arguments we pass in. Change shape_wait to look like this:

def shape_wait():
    """
    Wait while we display a shape.  Return True if a key was pressed,
    or false otherwise.
    """
    press_events = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN 
    return timed_wait( 2000, press_events ) # 2 seconds

and modify timed_wait to accept those arguments (notice I also added a description of what it does):

def timed_wait( time_to_wait, event_types_that_cancel ):
    """
    Wait for time_to_wait, but cancel if a relevant event happens.
    Return True if cancelled, or False if we waited the full time.
    """

    finished_waiting_event_id = pygame.USEREVENT + 1
    pygame.time.set_timer( finished_waiting_event_id, time_to_wait )

    pygame.event.clear()

    pressed = False
    waiting = True
    while waiting:
        evt = pygame.event.wait()
        if is_quit( evt ):
            quit()
        elif evt.type in event_types_that_cancel:
            waiting = False
            pressed = True
        elif evt.type == finished_waiting_event_id:
            waiting = False

    pygame.time.set_timer( finished_waiting_event_id, 0 )

    return pressed

Again, after these changes our program should work exactly as before – we’re passing values in as arguments that are exactly what we used to make as variables. But now, timed_wait is a lot more flexible, and we’ll use that flexibility very soon.

But first, we need to make a change to cover the unexpected. Inside timed_wait we’ve made a timer using pygame.time.set_timer and at the end we’ve cancelled it by calling pygame.time.set_timer again. However, if something goes wrong in between where we create the timer, and where we cancel it, it’s possible that something called an “exception” will be “thrown”. When an exception is thrown, the program stops running normally, line by line, and jumps out to somewhere else.

I’m not going to explain any more about exceptions here, but I am going to show you how to make absolutely sure that something will happen, even if an exception is thrown. The way to do that is to use a try ... finally block. We want to make sure our timer is always cancelled, so as soon as we’ve made it, we start a try block, and at the end we say finally. Anything inside that finally block will be run, even if an exception was thrown in the code inside the try block. The changes look like this:

def timed_wait( time_to_wait, event_types_that_cancel ):
    """
    Wait for time_to_wait, but cancel if a relevant event happens.
    Return True if cancelled, or False if we waited the full time.
    """

    finished_waiting_event_id = pygame.USEREVENT + 1
    pygame.time.set_timer( finished_waiting_event_id, time_to_wait )

    try:
        pygame.event.clear()

        pressed = False
        waiting = True
        while waiting:
            evt = pygame.event.wait()
            if is_quit( evt ):
                quit()
            elif evt.type in event_types_that_cancel:
                waiting = False
                pressed = True
            elif evt.type == finished_waiting_event_id:
                waiting = False
    finally:
        pygame.time.set_timer( finished_waiting_event_id, 0 )

    return pressed

The lines in green are new, and the ones in blue are just indented by four more spaces to make them part of the try and finally blocks. Now, we know that even if something goes wrong while we’re waiting, we will always cancel the timer we set up. Yet more good manners!

Now, after all that work, we finally have a timed_wait function that is flexible enough to be used everywhere we want to wait for something. Let’s start with the wait function. Change it to look like this:

def wait():
    time_to_wait = random.randint( 1500, 3000 ) # Between 1.5 and 3 seconds
    timed_wait( time_to_wait, () )

By using our clever timed_wait function instead of the built-in pygame.time.wait we gain some extra politeness: we can now quit the program on the “Ready?” screen by closing the window or pressing the Escape key. Try it!

Notice that we passed in () as the second argument to timed_wait. This argument is called event_types_that_cancel and is normally a list of types of event that will stop us waiting. () is Python’s way of saying an empty list, so we’re saying we don’t want to stop for any normal events (such as key presses) – only for quit events, or when the time is up.

Before we change lots more code to use timed_wait, we are going to make a new variable that we can use in lots of places in the code. Quite a few times, we want to wait for either a key press or a mouse click. We want this when we’re showing a red or green square, and when we’re at the end saying goodbye, and ideally we also want it when we’re telling the user how they did, so they can skip it if they’re impatient. So, right near the top, add a new line just below where we create screen_size:

screen_width = 640
screen_height = 480
screen_size = screen_width, screen_height
press_events = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN

This variable press_events will be our list of normal event types that we consider to be a “press” – essentially, the player doing something. Now that we’ve defined this at the top, we can take out the variable with the same name from shape_wait – it will use the global one instead:

def shape_wait():
    """
    Wait while we display a shape.  Return True if a key was pressed,
    or false otherwise.
    """
    return timed_wait( 2000, press_events ) # 2 seconds

We can also re-use press_events in the end function, and at the same time call our new timed_wait function:

def end():
    screen.fill( pygame.Color( "black" ) )
    white = pygame.Color( "white" )
    write_text( screen, "Thanks for playing!", white, True )
    write_text( screen, "Press a key to exit", white, False )
    pygame.display.flip()
    pygame.event.clear()
    timed_wait( 0, press_events )

Notice that this time we pass zero as the time to wait – this just means we will never time out on this screen – the zero gets passed in and used in the pygame.time.set_timer call, but passing in zero for the time there means “cancel this event”, and is harmless if the event doesn’t actually exist, so no timer will be set up – we will only stop waiting when the player presses something, which is what we want here.

Now we can make our success and failure functions more polite. Change them all to look like this:

def green_success():
    tick()
    green = pygame.Color( "green" )
    white = pygame.Color( "white" )
    write_text( screen, "Well done!", green, True )
    write_text( screen, "You pressed on green!", white, False )
    pygame.display.flip()
    timed_wait( 2000, press_events ) # 2 seconds

def green_failure():
    cross()
    red   = pygame.Color( "red" )
    white = pygame.Color( "white" )
    write_text( screen, "Bad Luck!", red, True )
    write_text( screen, "Green means press something!", white, False )
    pygame.display.flip()
    timed_wait( 2000, press_events ) # 2 seconds

def red_success():
    tick()
    green = pygame.Color( "green" )
    white = pygame.Color( "white" )
    write_text( screen, "Well done!", green, True )
    write_text( screen, "You didn't press on red!", white, False )
    pygame.display.flip()
    timed_wait( 2000, press_events ) # 2 seconds

def red_failure():
    cross()
    red   = pygame.Color( "red" )
    white = pygame.Color( "white" )
    write_text( screen, "Bad Luck!", red, True )
    write_text( screen, "Red means don't press anything!", white, False )
    pygame.display.flip()
    timed_wait( 2000, press_events ) # 2 seconds

They all call timed_wait saying wait for 2 seconds, but skip if a key is pressed because the player is impatient to get on to the next round. This change means not only can you skip past these success and failure screens, but also you can quit while they are visible, and the last vestige of rudeness has been wiped out from our game.

Well done – just one job left, which is to allow several rounds, and count the player’s score as they play. We’ll do that next time.

In the meantime, you can fix a bug I made – I typed get_width instead of get_height, which made my circles too big. Change the line inside green_shape that looks like radius = screen.get_width() / 3 to look like this:

    radius = screen.get_height() / 3

There we are – much better

You can check your verson against mine here: redgreen.py

See you next time, when hopefully we’ll finish the game!

My First Raspberry Pi Game – Part 10 – Red square

Parts: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12.

Writing your first ever computer program on the Raspberry Pi.

We’re writing a really simple game – you have to press a key when you see green, and not press a key when you see red.

I’ve been promising for a while that there will be a red square as well as a green circle, and this time we’re going to make that dream a reality.

The code we’ve written so far has this overall structure:

start()

ready_screen()

wait()

shape()

end()

It gets started, tells you to get ready, waits a random time, shows you a shape and collects your keypresses (or not), and then it ends.

Previously, the shape function just showed a green shape every time, by calling a function called green_shape. Not any more – change it to look like this:

def shape():
    GREEN = 0
    RED   = 1
    shape = random.choice( [GREEN, RED] )

    if shape == GREEN:
        green_shape()
    else:
        red_shape()

The first 2 lines just make two variables for us called GREEN and RED. They can have any values, so long as they’re not the same, so I’ve chosen 0 and 1.

The reason we’ve made these variables is so that we can make a random choice of one or the other. To do this, we call the choice function from the random module (which we already have listed as imported at the top). choice takes in a list of things to choose from, and returns the one it chose randomly.

So the shape variable will contain the value of either GREEN or RED. We do an if to decide what to do based on which it is.

If we chose GREEN, we do what we used to do, and call green_shape but if we chose RED we will end up in the else part of the if, and call a new function we will call red_shape.

So, what will red_shape look like? Quite a lot like green_shape actually. Just above the shape function, add this:

def red_shape():
    red = pygame.Color( "red" )
    height = 2 * ( screen.get_height() / 3 )
    left = ( screen.get_width() / 2 ) - ( height / 2 )
    top = screen.get_height() / 6

    screen.fill( pygame.Color( "white" ) )
    pygame.draw.rect( screen, red, ( left, top, height, height ), 0 )

    write_text( screen, "Don't press!", pygame.Color( "black" ), False )

    pygame.display.flip()

    pressed = shape_wait()

    if pressed:
        red_failure()
    else:
        red_success()

Most of this function is taken up with drawing a red rectangle, which we do by working out what size it should be, then calling pygame.draw.rect with the right dimensions and colour. After that we write some text encouraging the player to leave their keyboard alone, and then we do the normal pygame.display.flip to show this on the screen.

Once we’ve drawn the shape, we do something very similar to what we did inside green_shape – we wait to see what happens, by calling the already-existing shape_wait function, and get the answer back from it saying whether or not the player pressed something.

This time, if they pressed something they got it wrong, so we call a new function called red_failure, and if they did nothing they did the right thing, so we call another new function called red_success.

These two functions are also quite simple. Type them in above green_shape:

def red_success():
    tick()
    green = pygame.Color( "green" )
    white = pygame.Color( "white" )
    write_text( screen, "Well done!", green, True )
    write_text( screen, "You didn't press on red!", white, False )
    pygame.display.flip()
    pygame.time.wait( 2000 ) # Can't quit or skip!

def red_failure():
    cross()
    red   = pygame.Color( "red" )
    white = pygame.Color( "white" )
    write_text( screen, "Bad Luck!", red, True )
    write_text( screen, "Red means don't press anything!", white, False )
    pygame.display.flip()
    pygame.time.wait( 2000 ) # Can't quit or skip!

These functions re-use lots of existing code – they draw a tick or a cross and then use write_text to tell the player what happened, and then they do the normal flip and wait for a bit.

Try your program – it should now show you a red square about half of the time, instead of a green circle every time, and it should give you feedback about whether you did the right or the wrong thing. Feel free to try it a few times, and make sure you’ve run through all the combinations. If you made a mistake somewhere you may not see it until you actually run the relevant bit of code.

Once you’re happy with that, let’s fix a little bug while we’re here. Somehow I missed a bit from the shape_wait function, so if you press a key on the ready screen, it will register as you pressing the key really quickly when the shape appears. Try running your program and hammering a key when it says “Ready?”. You’ll see it thinks you pressed immediately the shape appears (whether red or green). This is annoying, and could even allow cheating, but we can prevent it by adding a single line to shape_wait:

def shape_wait():
    """
    Wait while we display a shape.  Return True if a key was pressed,
    or false otherwise.
    """

    event_types_that_cancel = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN

    time_to_wait = 2000 # Display the shape for 2 seconds
    finished_waiting_event_id = pygame.USEREVENT + 1
    pygame.time.set_timer( finished_waiting_event_id, time_to_wait )

    pygame.event.clear()

    pressed = False
    waiting = True
    while waiting:
        evt = pygame.event.wait()
        if evt.type == pygame.QUIT:
            quit()
        elif evt.type in event_types_that_cancel:
            waiting = False
            pressed = True
        elif evt.type == finished_waiting_event_id:
            waiting = False

    pygame.time.set_timer( finished_waiting_event_id, 0 )

    return pressed

As we’ve seen before, pygame.event.clear tells PyGame to forget all the events that have happened recently, which prevents this problem.

If you want to check your version against mine, you can find it here: redgreen.py.

We’ve nearly built a fully-working game. We’ve got two main tasks ahead of us: fix the “can’t exit” bug, and allow multiple rounds with a score at the end. We’ll do them in that order, so next time it’s bug-fixing, and some more refactoring to help us do it.

My First Raspberry Pi Game – Part 06 – A better class of waiting

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 wait, but better, while we show the ready screen we made last time. In the process we cover two of the most important things you need to understand to write computer programs.

The program we’ve written so far can wait around for something to happen, but it can’t distinguish between the various possible “somethings” that might happen. Specifically, it finishes very abruptly if we even move our mouse over the window!

In this part we’ll teach our program to ignore the movements of the mouse, but keep looking out for events it is interested in. We’ll also make it able to exit quickly if someone closes its window.

First, we need to get hold of the sys object (actually it’s called a “module”) which allows us to do things with the system, like quit, which is what we’re going to do with it.

Open up LeadPad again, and immediately above the import pygame line, add this:

import sys

Now we’re going to write a new function that allows us to exit the game immediately, whenever we want to. Just above the def ready_screen() line, add this new function:

def quit():
    pygame.quit()
    sys.exit()

This function tells PyGame we are finishing (by calling pygame.quit), then exits: sys.exit() stops everything dead.

Finally, we make our waiting a bit posher, by editing the end function look like this:

def end():
    pygame.event.clear()
    event_types_that_cancel = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN
    waiting = True
    while waiting:
        evt = pygame.event.wait()
        if evt.type == pygame.QUIT:
            quit()
        elif evt.type in event_types_that_cancel:
            waiting = False

There’s quite a lot here, but we’ll go through it line by line.

First, we clear the events as before using pygame.event.clear to make sure anything that has already happened doesn’t interfere with us.

Next we make a variable called event_types_that_cancel that is a list of all the types of events that we care about. They are key presses (KEYDOWN), and mouse clicks (MOUSEBUTTONDOWN). We’ll use this variable later.

Now we make another variable, called waiting. We use this to decide when we’ve finished. We set it to True, which means we are waiting, i.e. we haven’t finished.

The next line while waiting is the start of a loop, which means a piece of code we run lots of times. A “while” loop keeps on running until something happens. In this case, the lines after while waiting: will run again and again until the waiting variable becomes False.

The rest of the lines are all “inside” the loop, meaning they run again and again until the loop finishes. Notice that they are indented by a further 4 spaces to show that they are inside. If we had more lines later that were not indented, they would run after the loop had finished.

The first line creates a variable called evt that holds on to what came back when we called the function pygame.event.wait(). This function waits until an event happens. When an event happens, it could be a mouse movement, or a key press, or one of several other types of events. The old version of this code just called pygame.event.wait(), but didn’t look at what type of event happened. That’s why it finished even when you moved the mouse over the window. This new version captures the event that happened in the evt variable, and then does different things depending on what type of event it was.

The line starting with if lets us decide what to do based on what is happening. The first decision we make is evt.type == pygame.QUIT, which means if the type of event we have found is a special QUIT event. This type of event happens if the user closes the window by clicking the “X” in the top right-hand corner. If this happened, we go on to the next line, which calls the quit() function we created earlier. If this didn’t happen, we skip that line, and go on to the next one that is only indented as much as the original if line.

The next line after we skip is the one starting with elif. The “condition” we are checking (decision we are making) is whether evt.type in event_types_that_cancel which means is the event we have seen one of the ones we are interested it – is its type one of the things in event_types_that_cancel. event_types_that_cancel is a list containing the event type for mouse clicks, and the one for key presses, so if something interesting happened we will go to the next line, which is waiting = False. This sets waiting to False, meaning we will stop the loop. If the event was a different type (e.g. a mouse movement), we will skip the waiting = False line as well.

If neither of the conditions was true (so the event was not the user closing the window, or pressing a key or clicking the mouse) then we do nothing in the if and elif bit, and because we’re in a loop, we go back to the beginning and wait() again for another event. This goes on forever until one of the events we’re interested in happens. When it does, we either quit() immediately, or stop looping because waiting becomes False (meaning the while line lets us escape). When we’ve stopped looping we carry on, leaving the end function, and get to the end of the program.

If you can understand this you have just grasped two of the most important parts of programming – looping and conditional execution.

I suggest a long cup of tea.

While you’re sipping, try out our new program by opening LXTerminal as before, and typing the usual incantation:

./redgreen.py

Notice this time it doesn’t quit when you move the mouse, but it does if you click, or press a key, or close the window.

If it doesn’t work compare your program with my version: redgreen.py.

Next time we’ll make an actual green shape appear, as promised a long time ago!

My First Raspberry Pi Game – Part 05 – Say something

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 will write some writing on that blank screen we made last time.

But first, a couple of tricks (we are doing magic after all). We’re going to make our program know it is a Python program, without needing to be told. To do this we need to do 2 things.

First, open up LeadPad as normal and add this line at the very (very) top of the file. Make sure there are no empty lines above it:

#!/usr/bin/env python

That’s a “hash” (or, for Americans, “pound”) symbol, followed by an exclamation mark. Note that all the slashes are forward slashes, not backward.

This tells our Raspberry Pi that this is a Python program. Now we need to tell our Pi that this program is allowed to run by itself, instead of its name being passed to the python program like we have been doing before.

To do this, open up LXTerminal as before, and type exactly this (and press Return):

chmod +x redgreen.py

If all of this worked correctly, you should be able to run our program in a new way. Instead of typing python redgreen.py like we were before, we can type this in to LXTerminal:

./redgreen.py

That’s a dot, followed by a forward slash, followed by the name of our program.

This means run the program in this directory (that is the “./” part) called “redgreen.py”. Your Pi will look at redgreen.py and find the line we added that starts with “#!” and know to use Python to run it.

Now let’s get on with writing something on the screen. Go back to LeafPad and change the line starting with screen_size = to be these 3 lines:

screen_width = 640
screen_height = 480
screen_size = screen_width, screen_height

This creates 3 variables – screen_width, screen_height and the one we had before screen_size, which is now made by putting the first 2 together. We’re going to use screen_height later.

Just below those 3 lines, type this:

screen = None
ready_text = None

This gets 2 variables ready for us, and makes them empty. We’re going to fill them in inside start.

Change the start function to look exactly like this:

def start():
    global screen, ready_text
    pygame.init()
    screen = pygame.display.set_mode( screen_size )
    font = pygame.font.Font( None, screen_height / 5 )
    ready_text = font.render( "Ready?", 1, pygame.Color( "white" ) )

The green lines above are the bits we’ve added. The line beginning global tells Python we want to work with those variables we got ready earlier inside this function, even though we created them outside it. (Without saying they were “global” we would be in danger of working with versions of them that only existed while we were inside the function, and disappeared as soon as we left.)

The font line makes a new font (a font is a typeface, or way of writing text). The first argument we passed was None because we don’t care at the moment which font we use (e.g. “Arial” or “Times New Roman”) – we are happy with the default. The second argument is for the size of the font we want, and we passed in screen_height / 5, which means the value of the screen_height variable we created near the top, divided by 5. The “/” character is how we write division in Python – it is supposed to look a bit like a fraction.

Finally, on the last line we create another variable called ready_text, which contains the “rendered” version of the word “Ready?”, using the font we created, in white. “Render” means we create a picture showing the writing we wanted. We’ll draw this picture onto the screen in a second.

Now that all our preparation is over, we can finally write a fuller version of the ready_screen function. Change it to look like this:

def ready_screen():
    textpos = ready_text.get_rect(
        centerx = screen.get_width() / 2,
        centery = screen.get_height() / 2
    )

    screen.blit( ready_text, textpos )
    pygame.display.flip()

The first 4 lines (textpos up to the closing bracket all on its own) are really all one “line of code” – they are like one sentence of our program, that happens to span multiple lines. In Python if we want to continue a sentence (we call it a “statement”) we just leave a bracket unclosed. Python knows we have finished when we have closed all the brackets.

This “line” from textpost = up to ) creates a variable called textpos, which contains the place on the screen where we want to put our writing. We look inside ready_text (which is our rendered writing that we created above) for a function called get_rect that calculates a rectangle for us that is the right place on the screen to put the writing. The arguments we pass to get_rect are screen.get_width() / 2 and screen.get_height() / 2, which are telling it that it should calculate the rectangle by putting its centre in the middle of the screen. The middle of the screen is half-way across its width, and half-way down its height, which is why we are dividing the width and the height of the screen by 2.

Something worth noticing here is that the arguments to get_rect have names – we wrote centerx = and centery =. In Python we are allowed to give the names of arguments, or sometimes we can miss them out if we are happy just to put them in the right order. The get_rect function can actually take lots of different arguments, so it needs to know which ones you mean, which is why we named them.

Finally the last 2 lines do the real work. The screen.blit line tells PyGame to write the ready_text picture (the rendered writing) onto the screen at the position stored inside textpos. pygame.display.flip() is what we do to tell PyGame we’ve finished messing about with the screen, and we’re ready for it to display what we’ve done.

[The word “blit” is an oddity from the olden days which I’m afraid you’ll just have to memorise, and “flip” comes from the fact that behind the scenes there are really two screens – the one we are displaying, and the one we are working on. flip() switches them over, displaying the one we were working on, and making the other one ready to be worked on.]

If you’ve got this far, well done! With any luck, we’re going to see the word “Ready?” on the screen in big letters.

Switch to LXTerminal again and type our new spell:

./redgreen.py

Don’t move the mouse or press anything: a window should appear, with the word “Ready?” written in big white letters. When you press a key or move the mouse over it, it should disappear.

If something goes wrong, check back over the instructions carefully, and compare your file with this one: redgreen.py.