#LyX 1.2 created this file. For more info see http://www.lyx.org/ \lyxformat 220 \textclass article \language english \inputencoding auto \fontscheme default \graphics default \paperfontsize default \spacing single \papersize Default \paperpackage a4 \use_geometry 0 \use_amsmath 0 \use_natbib 0 \use_numerical_citations 0 \paperorientation portrait \secnumdepth 3 \tocdepth 3 \paragraph_separation indent \defskip medskip \quotes_language english \quotes_times 2 \papercolumns 1 \papersides 1 \paperpagestyle default \layout Title Developing Tic-Tac-Toe with pyGame \layout Author Mr. Yergler \newline Dr. Ceder \newline Canterbury School \layout Date 16 May 2002 \layout Abstract pyGame is a set of Python modules designed for writing games. pyGame serves as an abstraction layer for the SDL library, which means that games written with pyGame can be run on Windows, Linux, MacOS, and BeOS, just to name a few. This tutorial will guide you through writing a simple game in Python (tic-tac-t oe) using the pyGame library. By the completion of the tutorial, you should have a grasp of the basic concepts of Python and pyGame. \layout Section Program Initialization \layout Standard In developing tic-tac-toe, we're going to address the problem using logical steps, or blocks. The goal is to learn a single pyGame concept at each step. Our first step is to set up program and initialize the pyGame libraries. The following code sample demonstates this: \layout LyX-Code # tic-tac-toe \layout LyX-Code # pyGame Tutorial \layout LyX-Code # Mr. Yergler \layout LyX-Code # 13 May 2002 \layout LyX-Code \hfill \layout LyX-Code import pygame \layout LyX-Code from pygame.locals import * \layout LyX-Code \hfill \layout LyX-Code pygame.init() \layout LyX-Code ttt = pygame.display.set_mode((300,325)) \layout LyX-Code pygame.display.set_caption = ('Tic-Tac-Toe') \layout Standard The first four lines of code are comments, which Python ignores. Comments are used solely for informational purposes and to improve the \begin_inset Quotes eld \end_inset readability \begin_inset Quotes erd \end_inset of your code. In Python, any line beginning with a # is a comment. \layout Standard The next two lines (import..., from pygame...) tell Python to load the pyGame libraries. This is a good time to note that Python is case sensitive, so pygame is not the same as PYGAME. \layout Standard The final three lines call the pyGame initialization routines, create a window, and set it's title bar caption. In this case, we're creating a 300x325 pixel window for our program. \layout Standard Note that the empty parenthesis after pygame.init() are required. The double parenthesis around the window dimensions, ((300,325)), are also required. \layout Section The Main Loop \layout Standard In order to have a working Windows program we need to add some code to handle \begin_inset Quotes eld \end_inset events \begin_inset Quotes erd \end_inset , like mouse clicks, key presses, etc. So let's do that now. PyGame processes events in a queue. That is, if you click the mouse, then press a key, the mouse click is processed before the keystroke. \layout Standard To respond to events, we use a while loop. This basic loop is present below \layout LyX-Code # the initialization stuff goes here \layout LyX-Code \hfill \layout LyX-Code #set up our loop control variable \layout LyX-Code running = 1 \layout LyX-Code \hfill \layout LyX-Code while (running==1): \begin_deeper \layout LyX-Code for event in pygame.event.get(): \begin_deeper \layout LyX-Code if event.type is QUIT: \begin_deeper \layout LyX-Code running = 0 \end_deeper \end_deeper \layout LyX-Code \layout LyX-Code \end_deeper \layout Standard This loop basically says that until the user quits the game (generally by closing the window), continually check for events and update the display. In the example above, the only event we check for is quitting and the program doesn't do anything except stop, but now that the program skeleton works, we can add actual program code and behavior. \layout Section Drawing the board \layout Standard Our next step is to draw the tic-tac-toe board itself. To do this, we're going to write a function. A function is a self-contained \begin_inset Quotes eld \end_inset package \begin_inset Quotes erd \end_inset of commands we can use from our main program. In Python functions begin with the \series medium def \series default keyword. We're going to call our function initBoard. The initial function declaration is shown below: \layout LyX-Code def initBoard(ttt): \begin_deeper \layout LyX-Code # initialize the board and return it \layout LyX-Code # as a variable \layout LyX-Code # ------------------------------- \layout LyX-Code # ttt : a properly initialized display variable \end_deeper \layout LyX-Code \begin_deeper \layout LyX-Code # return the board \layout LyX-Code return background \end_deeper \layout Standard The first line of code defines the function. Python uses indentation to organize code into logical blocks. Therefore, the entire function must be indented an equal amount. In its current form, this function simply returns the value of the variable background (which is undefined). \layout Standard To actually initialize the board, we use the following code: \layout LyX-Code background = pygame.Surface (ttt.get_size()) \layout LyX-Code background = background.convert() \layout LyX-Code background.fill ((250,250,250)) \layout Standard This code does three things. In the first two lines we define background as a \begin_inset Quotes eld \end_inset surface \begin_inset Quotes erd \end_inset variable. A pyGame surface is anything we can draw or place graphics on. The final line fills the background with white. The tuple (250,250,250) is the RGB representation for white. At this point our function would return a white background. Our next step is to draw the lines on the background. We do that with the following code: \layout LyX-Code # draw the grid lines \layout LyX-Code # vertical lines... \layout LyX-Code pygame.draw.line (background, (0,0,0), (100,0), (100,300), 2) \layout LyX-Code pygame.draw.line (background, (0,0,0), (200,0), (200,300), 2) \layout LyX-Code \hfill \layout LyX-Code # horizontal lines... \layout LyX-Code pygame.draw.line (background, (0,0,0), (0,100), (300,100), 2) \layout LyX-Code pygame.draw.line (background, (0,0,0), (0,200), (300,200), 2) \layout Standard The pygame.draw.line function call shown above takes 5 parameters. The first, background, tells pyGame what surface to do the drawing on. The second, (0,0,0), is the RGB representation for the color (black in this case). The next two are the start and end points for the line. Finally, the width in pixels. The four lines of code above would draw two horizontal lines and two vertical lines. \layout Standard Our finished initBoard function is shown below: \layout LyX-Code def initBoard(ttt): \begin_deeper \layout LyX-Code # initialize the board and return it \layout LyX-Code # as a variable \layout LyX-Code # ------------------------------- \layout LyX-Code # ttt : a properly initialized \layout LyX-Code # display variable \end_deeper \layout LyX-Code \hfill \begin_deeper \layout LyX-Code background = pygame.Surface (ttt.get_size()) \layout LyX-Code background = background.convert() \layout LyX-Code background.fill ((250,250,250)) \layout LyX-Code \hfill \layout LyX-Code # draw the grid lines \layout LyX-Code # vertical lines... \layout LyX-Code pygame.draw.line (background, (0,0,0), (100,0), (100,300), 2) \layout LyX-Code pygame.draw.line (background, (0,0,0), (200,0), (200,300), 2) \layout LyX-Code \hfill \layout LyX-Code # horizontal lines... \layout LyX-Code pygame.draw.line (background, (0,0,0), (0,100), (300,100), 2) \layout LyX-Code pygame.draw.line (background, (0,0,0), (0,200), (300,200), 2) \end_deeper \layout LyX-Code \hfill \begin_deeper \layout LyX-Code # return the board \layout LyX-Code return background \end_deeper \layout Standard Note that the last line of the function returns the background variable. Returning the variable allows us to use it elsewhere in the program. Now that we have our initialization function complete, we need to add code to call it and get the background variable. We'll add the following line above the main loop in the program: \layout LyX-Code # create the game board \layout LyX-Code board = initBoard (ttt) \layout Section Displaying the Board \layout Standard At this point we have a function (initBoard) which draws the game board on a \begin_inset Quotes eld \end_inset surface \begin_inset Quotes erd \end_inset and returns it to the caller. However, if you ran this program, it wouldn't do anything for two reasons. First, we haven't actually called the function; we'll get to that. Second, pyGame does something called double-buffering with it's graphics. That is, when you make changes to a surface, they're not actually displayed until you explicitly put them on the screen. The reasoning behind this is that if you're doing complex animations, they'll appear smoother if the computer completes it's drawing operations before displaying the results. But that's not important right now. What is important is getting the results onto the screen. To accomplish this, we're going to write another function, showBoard. Our showBoard function takes two parameters: a variable representing the display, and a variable representing the surface we want drawn onto the screen. This function is very short and is presented below. \layout LyX-Code def showBoard (ttt, board): \begin_deeper \layout LyX-Code # (re)draw the game board (board) on the screen (ttt) \layout LyX-Code ttt.blit (board, (0,0)) \layout LyX-Code pygame.display.flip() \end_deeper \layout Standard Now that we have the code to display the board, we need to add it to the main event loop, so that the board will be (re)displayed after events are processed: \layout LyX-Code while (running==1): \layout LyX-Code for event in pygame.event.get(): \begin_deeper \begin_deeper \layout LyX-Code if event.type is QUIT: \begin_deeper \layout LyX-Code running = 0 \end_deeper \end_deeper \end_deeper \layout LyX-Code # update the display \layout LyX-Code showBoard (ttt, board) \layout Section Responding to Mouse Clicks \layout Standard Tic-Tac-Toe isn't much of a game if the users can't click to make their moves. To respond to a mouse click, we're going to use a function that \layout List \labelwidthstring 00.00.0000 a. determines where the user clicked \layout List \labelwidthstring 00.00.0000 b. determines if that space has been used \layout List \labelwidthstring 00.00.0000 c. draws the X or O \layout Standard To track what spaces have been used and whose turn it is we're going to declare two global variables. The first, XO, will track whose turn it currently is. The second, grid, will track which spaces have been used. We'll add their declarations, as shown below, before the pyGame initialization calls. \layout LyX-Code # X will go first... \layout LyX-Code XO = \begin_inset Quotes eld \end_inset X \begin_inset Quotes erd \end_inset \layout LyX-Code \hfill \layout LyX-Code # declare an empty grid \layout LyX-Code grid = [ [ None, None, None ], \layout LyX-Code [ None, None, None ], \layout LyX-Code [ None, None, None ] ] \layout Standard In declaring and initializing XO, we enclose the letter X in double quotes ( \begin_inset Quotes eld \end_inset X \begin_inset Quotes erd \end_inset ) to indicate that it's a character, not another variable. When declaring the variable grid, we're using the None identifier. None is a special value in Python, which simply represents an empty variable space. \layout Standard Next we need to add a handler to the main event loop. Modify the while loop as shown below: \layout LyX-Code while (running==1): \layout LyX-Code for event in pygame.event.get(): \begin_deeper \begin_deeper \layout LyX-Code if event.type is QUIT: \begin_deeper \layout LyX-Code running = 0 \end_deeper \end_deeper \layout LyX-Code elif event.type is MOUSEBUTTONDOWN: \begin_deeper \layout LyX-Code clickBoard(board) \end_deeper \layout LyX-Code \layout LyX-Code #update the display \layout LyX-Code showBoard (ttt, board) \end_deeper \layout Standard As you can see, we're responding to the MOUSEBUTTONDOWN event (we don't distinguish between which button was clicked), and calling a function call clickBoard. We pass clickBoard a single parameter, the pyGame surface we're drawing the board on. \layout Standard PyGame provides a method to get the coordinates a user clicked at by using the following code: \layout LyX-Code (mouseX, mouseY) = pygame.mouse.get_pos() \layout Standard This stores the X and Y coordinate of the mouse into two different variables: mouseX and mouseY. However, we want to deal with clicks in terms of which board space the user clicked in. We'll use a function to translate the mouse X and Y into the board's row and column. The boardPos function is shown below: \layout LyX-Code def boardPos (mouseX, mouseY): \begin_deeper \layout LyX-Code # determine the row the user clicked \layout LyX-Code if (mouseY < 100): \begin_deeper \layout LyX-Code row = 0 \end_deeper \layout LyX-Code elif (mouseY < 200): \begin_deeper \layout LyX-Code row = 1 \end_deeper \layout LyX-Code else: \begin_deeper \layout LyX-Code row = 2 \end_deeper \end_deeper \layout LyX-Code \begin_deeper \layout LyX-Code # determine the column the user clicked \layout LyX-Code if (mouseX < 100): \begin_deeper \layout LyX-Code row = 0 \end_deeper \layout LyX-Code elif (mouseX < 200): \begin_deeper \layout LyX-Code row = 1 \end_deeper \layout LyX-Code else: \begin_deeper \layout LyX-Code row = 2 \end_deeper \end_deeper \layout LyX-Code \begin_deeper \layout LyX-Code #return the row & column \layout LyX-Code return (row, col) \end_deeper \layout Standard Notice that in this case we're returning both the row and column in one step. Armed with the boardPos function and our knowledge of pyGame, the clickBoard function is shown below. \layout LyX-Code def clickBoard (board): \begin_deeper \layout LyX-Code # determine where the user clicked on the board and draw the X or O \end_deeper \layout LyX-Code \begin_deeper \layout LyX-Code # tell Python that we want access to the global variables grid & XO \layout LyX-Code global grid, XO \layout LyX-Code \layout LyX-Code (mouseX, mouseY) = pygame.mouse.get_pos() \layout LyX-Code (row, col) = boardPos (mouseX, mouseY) \layout LyX-Code \end_deeper \layout LyX-Code \begin_deeper \layout LyX-Code # make sure this space isn't used \layout LyX-Code if ((grid[row][col] == \begin_inset Quotes eld \end_inset X \begin_inset Quotes erd \end_inset ) or (grid[row][col] == \begin_inset Quotes eld \end_inset O \begin_inset Quotes erd \end_inset )): \begin_deeper \layout LyX-Code # this space is in use \layout LyX-Code return \end_deeper \end_deeper \layout LyX-Code \begin_deeper \layout LyX-Code # draw an X or O \layout LyX-Code drawMove (board, row, col, XO) \end_deeper \layout LyX-Code \begin_deeper \layout LyX-Code # toggle XO to the other player's move \layout LyX-Code if (XO == \begin_inset Quotes eld \end_inset X \begin_inset Quotes erd \end_inset ): \begin_deeper \layout LyX-Code XO = \begin_inset Quotes eld \end_inset O \begin_inset Quotes erd \end_inset \end_deeper \layout LyX-Code else: \begin_deeper \layout LyX-Code XO = \begin_inset Quotes eld \end_inset X \begin_inset Quotes erd \end_inset \end_deeper \end_deeper \layout Standard Notice that when we check if the space is used, we used square brackets [] to access the contents of the array. We use two sets because this is a two-dimensional array. The drawMove function referenced here is covered below \layout Section Drawing X's & O's \layout Standard Now that we can determine whose turn it is and where they clicked, we need to draw the X or O in the space. We're going to write a function called drawMove to handle this. drawMove takes four parameters. The first is a reference to the surface we want to draw on. The next two are the row and column we want to draw the move in. Note that the first row and column is numbered zero (0). Here is the drawMove function: \layout LyX-Code def drawMove (board, boardRow, boardCol, Piece): \begin_deeper \layout LyX-Code # draw an X or O (Piece) on the board at boardRow, boardCol \layout LyX-Code \hfill \layout LyX-Code # determine the center of the space \layout LyX-Code # (this works because our spaces are 100 pixels wide and the first one \layout LyX-Code # is numbered zero) \layout LyX-Code centerX = boardCol * 100 + 50 \layout LyX-Code centerY = boardRow * 100 + 50 \layout LyX-Code \hfill \layout LyX-Code # draw the piece \layout LyX-Code if (Piece == \begin_inset Quotes eld \end_inset O \begin_inset Quotes erd \end_inset ): \begin_deeper \layout LyX-Code # it's an O; draw a circle \layout LyX-Code pygame.draw.circle (board, (0,0,0), (centerX, centerY), 44, 2) \end_deeper \layout LyX-Code else: \begin_deeper \layout LyX-Code # it's an X \layout LyX-Code pygame.draw.line (board, (0,0,0), (centerX - 22, centerY - 22), \backslash \begin_deeper \layout LyX-Code (centerX + 22, centerY + 22), 2) \end_deeper \layout LyX-Code pygame.draw.line (board, (0,0,0), (centerX + 22, centerY - 22), \backslash \begin_deeper \layout LyX-Code (centerX - 22, centerY + 22), 2) \end_deeper \end_deeper \layout LyX-Code \hfill \layout LyX-Code # mark the board space as used \layout LyX-Code grid[boardRow][boardCol] = Piece \end_deeper \layout Standard First let's look at the pygame.draw.circle method. This method takes 5 parameters: the surface to draw on, the color to draw with (in RGB format), the center of the circle, the diameter, and the pen width. Note that the 44 pixel diameter was chosen rather arbitrarily as something that would fit in the square. \layout Standard Something new that we haven't seen before is the backslash ( \backslash ) at the end of the two pygame.draw.line lines. The backslash tells Python that the current line of code is continued onto the next line. \layout Section Determining The Winner \layout Standard One of our final steps is to check if anyone has won the game. To accomplish this we're going to examine the grid variable that we've declared to hold the board status. It's relatively easy to check for a winner in Tic-Tac-Toe; simply see if any one row, any column or a diagonal has all the same value. For example, we can check for horizontal winners with the following code: \layout LyX-Code for row in range(0,3): \begin_deeper \layout LyX-Code if ((grid[row][0] == grid[row][1] == grid[row][2]) and \backslash \begin_deeper \layout LyX-Code (grid([row][0] is not None)): \layout LyX-Code # this row won \layout LyX-Code winner = grid[row][0] \layout LyX-Code pygame.draw.line(board, (250,0,0), (0, (row+1)*100 - 50), \backslash \layout LyX-Code (300, (row + 1) * 100 - 50), 2) \layout LyX-Code break \end_deeper \end_deeper \layout Standard The range function in the for loop (first line) tells python to repeat the indented block, starting with row=0, then row=1, and finally row=2. The last line, break, tells Python that once we've found a winner, there's no need to check the other rows. We'll encapsulate the win-checking code into a function called gameWon. We're still going to pass in the board variable as the surface to draw the winning line on. \layout LyX-Code def gameWon(board): \layout LyX-Code # determine if anyone has won the game \layout LyX-Code # --------------------------------------------------------------- \layout LyX-Code # board : the game board surface \layout LyX-Code \layout LyX-Code global grid, winner \layout LyX-Code \layout LyX-Code # check for winning rows \layout LyX-Code for row in range (0, 3): \layout LyX-Code if ((grid [row][0] == grid[row][1] == grid[row][2]) and \backslash \layout LyX-Code (grid [row][0] is not None)): \layout LyX-Code # this row won \layout LyX-Code winner = grid[row][0] \layout LyX-Code pygame.draw.line (board, (250,0,0), (0, (row + 1)*100 - 50), \backslash \layout LyX-Code (300, (row + 1)*100 - 50), 2) \layout LyX-Code break \layout LyX-Code \layout LyX-Code # check for winning columns \layout LyX-Code for col in range (0, 3): \layout LyX-Code if (grid[0][col] == grid[1][col] == grid[2][col]) and \backslash \layout LyX-Code (grid[0][col] is not None): \layout LyX-Code # this column won \layout LyX-Code winner = grid[0][col] \layout LyX-Code pygame.draw.line (board, (250,0,0), ((col + 1)* 100 - 50, 0), \backslash \layout LyX-Code ((col + 1)* 100 - 50, 300), 2) \layout LyX-Code break \layout LyX-Code \layout LyX-Code # check for diagonal winners \layout LyX-Code if (grid[0][0] == grid[1][1] == grid[2][2]) and \backslash \layout LyX-Code (grid[0][0] is not None): \layout LyX-Code # game won diagonally left to right \layout LyX-Code winner = grid[0][0] \layout LyX-Code pygame.draw.line (board, (250,0,0), (50, 50), (250, 250), 2) \layout LyX-Code \layout LyX-Code if (grid[0][2] == grid[1][1] == grid[2][0]) and \backslash \layout LyX-Code (grid[0][2] is not None): \layout LyX-Code # game won diagonally right to left \layout LyX-Code winner = grid[0][2] \layout LyX-Code pygame.draw.line (board, (250,0,0), (250, 50), (50, 250), 2) \layout Standard Note that we refer to the global variable \series medium winner \series default in our function. This variable will store the winner (if any). Before we can use that variable, we need to initialize it. In Python, initializing a variable consists of assigning a value to it. We're going to initialize \series medium winner \series default by adding the following statement at the top of our program where we already initialized \series medium grid \series default and \series medium XO \series default . \layout LyX-Code winner = None \layout Standard Finally, we want to check for a winner after every move, so we'll add a call to gameWon to our main loop as well. \layout LyX-Code while (running == 1): \layout LyX-Code for event in pygame.event.get(): \layout LyX-Code if event.type is QUIT: \layout LyX-Code running = 0 \layout LyX-Code elif event.type is MOUSEBUTTONDOWN: \layout LyX-Code # the user clicked; place an X or O \layout LyX-Code clickBoard(board) \layout LyX-Code \layout LyX-Code # check for a winner \layout LyX-Code gameWon (board) \layout LyX-Code \layout LyX-Code # update the display \layout LyX-Code showBoard (ttt, board) \layout Section Displaying the Game Status \layout Standard At this point we have a playable game, but we can add a final piece of code to make it a little more \begin_inset Quotes eld \end_inset user-friendly \begin_inset Quotes erd \end_inset . In this step we're going to add code that draws the game \begin_inset Quotes eld \end_inset status \begin_inset Quotes erd \end_inset below the board. The status is text such as \begin_inset Quotes eld \end_inset x's turn \begin_inset Quotes erd \end_inset or \begin_inset Quotes eld \end_inset O won! \begin_inset Quotes erd \end_inset We have two global variables, XO and winner, which hold the current turn and the winner (if any) respectively. We'll begin our function as follows: \layout LyX-Code def drawStatus (board): \begin_deeper \layout LyX-Code # draw the status (i.e., player turn, etc) at the bottom of the board \layout LyX-Code # --------------------------------------------------------------- \layout LyX-Code # board : the initialized game board surface \layout LyX-Code \hfill \layout LyX-Code # gain access to global variables \layout LyX-Code global XO, winner \end_deeper \layout Standard Now that we have declared our function and have access to the global variables, we need to figure out what status message we're going to draw. There are two possible types of messages: the \begin_inset Quotes eld \end_inset you won \begin_inset Quotes erd \end_inset message and the \begin_inset Quotes eld \end_inset someone's turn \begin_inset Quotes erd \end_inset message. We'll determine the appropriate messsage with an if statement, and store the message in the variable \series medium message \series default . \layout LyX-Code \begin_deeper \layout LyX-Code # determine the status message \layout LyX-Code if (winner is None): \begin_deeper \layout LyX-Code message = XO + "'s turn" \end_deeper \layout LyX-Code else: \begin_deeper \layout LyX-Code message = winner + " won!" \end_deeper \end_deeper \layout Standard Now that we have the status message we need to display it on the game board. PyGame has three steps for displaying text on a surface. First, get the font you're working with. Second, render the text onto a new surface. Finally, copy (blit) that surface onto your destination surface. We'll accomplish that with the following code. \layout LyX-Code \begin_deeper \layout LyX-Code # render the status message \layout LyX-Code font = pygame.font.Font(None, 24) \layout LyX-Code text = font.render(message, 1, (0,0,0)) \layout LyX-Code \hfill \layout LyX-Code # copy the rendered message onto the board \layout LyX-Code board.fill ((250, 250, 250), (0, 300, 300, 25)) \layout LyX-Code board.blit (text, (10, 300)) \end_deeper \layout Standard The first line gets the current font, specifying we want a height of 24 pixels. The second line calls the text render function. The render function takes three parameters: the text to render (message), whether we want the text anti-aliased (smoothed; 1 = true), and an RGB foreground color. We call board.fill next in order to clear any text we've previously displayed. Finally, we call the blit function which copies one surface to another. \layout Standard The last thing we have to do is add a call to the drawStatus function. We want to update the status each time we draw the board, so we'll add the following line to the beginning of showBoard: \layout LyX-Code drawStatus(board) \layout Standard Our finished showBoard looks like this: \layout LyX-Code def showBoard (ttt, board): \layout LyX-Code # (re)draw the game board (board) on the screen (ttt) \begin_deeper \layout LyX-Code drawStatus(board) \layout LyX-Code ttt.blit (board, (0,0)) \layout LyX-Code pygame.display.flip() \end_deeper \layout Section Conclusion \layout Standard We now have a working tic-tac-toe game written in Python with the pyGame library. The source code is available on the F drive under Courses, pyGame. \layout Bibliography \bibitem {key-1} pyGame hompage: http://www.pygame.org \layout Bibliography \bibitem {key-2} pyGame Documentation: http://www.pygame.org/docs \layout Bibliography \bibitem {key-3} Python Language Reference: http://www.python.org/doc/current/ref \layout Bibliography \bibitem {key-4} Python Standard Library Reference: http://www.python.org/doc/lib \layout Bibliography \bibitem {key-5} The PyGame Code Repository: http://www.pygame.org/pcr \layout Bibliography \bibitem {key-6} O'Reilly Network on PyGame: http://www.onlamp.com/pub/a/python/2001/12/20/pygame.ht ml \the_end