learn Python. Create Games

User Tools

Site Tools


Step 013 - Catch the Thief Game

How to Play

screenshot from This code example should teach nearly nothing new but instead put all the wisdom from the previous steps together to create a little game. In this catch-the-thief-game up to 2 players control sprites1) (the Pygame snake and the babytux bird) and are tasked to help the police (blue circle with white “P”) to catch a thief (red triangle with transparent “T”). To make the game interesting (and difficult), the police moves toward the middle position between the both players (this position is marked with a black cross). The thief moves randomly.

All sprites are controlled indirectly, like ships in water (or spaceships in space): The player point the mouse or press the cursor - keys <key>←</key> <key>→</key> <key>↑</key> <key>↓</key> in the direction the sprite should move. The sprite will eventually move into this direction but will continue to do so even if it gets a new order. Bouncing off the screen edge will change the direction. If no orders are given, all sprites except the thief will eventually come to an halt because of friction. The player can use an emergency break and force his sprite to halt by pressing <key>ENTER</key> or pressing the left mouse button. Instead of the cursor keys, the player can also use <key>w</key>, <key>a</key>, <key>s</key>, <key>s</key>, <key>CTRL</key>.

If no second player is present, one skilled player can try to control both sprites with mouse & keyboard.

Code Discussion

In the code, those pygame surfaces are called sprites. Please note that pygame has it's own, very sophisticated pygame.Sprite class (see the next steps) but those pygame sprites do not appear in this code example.

This code example define many functions with def. Most important are the last two lines, basically the only lines outside a function:

if __name__ == "__main__":

Those lines control if the game is started directly or imported from another program (like a program that manages a highscore list). If the programm is started directly, the internal variable __name__ has the value "__main__". In this case, the game start itself by calling the play_the_game() function.

Because the loaded background image is larger than the pygame display screen resolution, the background image is resized using this command:

    background = pygame.transform.scale(background, (screen.get_width(), screen.get_height()))

After defining some useful functions for later use the game creates several pygame surfaces: screen and background and several sprites: bird, snake, police, cross, thief where each sprite get the variables x, y, dx, dy with the spritename as prefix. X and Y control the position while dx and dy control the movement speed.

Inside the mainloop, all sprites are cleaned, updated (player commands are computed here) and finally blittedn on the screen again. The mainloop switch into a special GameOver - mode after the playtime runs out. While in the GameOver mode, no sprites are displayed and only the score is rendered for the players information.

The cleanblit function calls and a lot of headache could all be ignored and instead a simple line of

            #screen.blit(background, (0,0))  # not GameOver

would do the job. However, framerate would drop when using large screen resolutions.

Before blitting (drawing) the sprites, a check is made if the police sprite is near enough at the thief sprite to credit the players with a score point. There are some code lines to see if the pygame sound mixer is currently busy and if he is silent the spring.wav sound is played. Also because at each catching of the thief the screen is filled with a random colour, the code line checks if this frame (mainloop cycle) is the first of “normal” activity (no thief is catched) right after an “catch” frame. If yes, the old original background is blitted on the screen.

            catch_in_last_frame = catch_in_this_frame # save old catch info
            catch_in_this_frame = False
            if (distx < police.get_width() /2) and (disty < police.get_height()/2):
                catch_in_this_frame = True
                points += seconds
                if not pygame.mixer.get_busy():
           # only play this sound if mixer is silent at the moment
                # no catch this time
                if catch_in_last_frame:
                    screen.blit(background, (0,0)) # restore backgrounnd


<note tip>ideas:

  • head over to , find a map from your city, and use it ! (right-click on the map, select “save image as…”)
  • Improve one-player modus by adding <key>w</key> <key>a</key> <key>s</key> <key>d</key> as control keys
  • enable direct control for mouse or keyboard (like manipulating snakex, snakey instead of snakedx, snakedy)
  • create additional sprites
  • play a music file
  • use proportional force (like a rubber band): the further away the police surface is from the cross, the faster it travels toward the cross
  • split the code into a config file (with all the constants and variables) and into a game file
  • dynamic score points: calculate the current distance between police and thief. Give points all the time, based on this distance (0 distance - max points)
  • draw a line between police and thief. The line should become thicker if the distance police-thief becomes shorter



<note>trivia: This games is inspired by the Austrian tv cult series Kottan. As the content and visuals of this series are -sadly- neither public domain nor creative-commons licensed i use free graphics. But on your own computer, you can replace the sprite graphics with some characters of your favorite TV show.</note>


Source Code on Github

To run this example you need:

file in folder download pygame Download the whole Archive with all files from Github:
an openstreetmap from Vienna
pygame snake from

from wikimedia commons
from Battle of Wesnoth
from Neverball

View/Edit/Download the file directly in Github:

click reload in your browser if you see no code here:

Comment this Page


pygame surfaces, not pygame sprites
/var/www/horst/ · Last modified: 2014/01/09 11:07 (external edit)