Mr. Yergler
Dr. Ceder
16 May 2002
Canterbury School
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:
# pyGame Tutorial
# Mr. Yergler
# 13 May 2002
import pygame
from pygame.locals import *
pygame.init()
ttt = pygame.display.set_mode((300,325))
pygame.display.set_caption = ('Tic-Tac-Toe')
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.
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.
Note that the empty parenthesis after pygame.init() are required. The double parenthesis around the window dimensions, ((300,325)), are also required.
In order to have a working Windows program we need to add some code to handle ``events'', 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.
To respond to events, we use a while loop. This basic loop is present below
#set up our loop control variable
running = 1
while (running==1):
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 ``package'' of commands we can use from our main program. In Python functions begin with the def keyword. We're going to call our function initBoard. The initial function declaration is shown below:
# as a variable
# ----------------
# ttt : a properly initialized display variable
return background
To actually initialize the board, we use the following code:
background = background.convert()
background.fill ((250,250,250))
# vertical lines...
pygame.draw.line (background, (0,0,0), (100,0), (100,300), 2)
pygame.draw.line (background, (0,0,0), (200,0), (200,300), 2)
# horizontal lines...
pygame.draw.line (background, (0,0,0), (0,100), (300,100), 2)
pygame.draw.line (background, (0,0,0), (0,200), (300,200), 2)
Our finished initBoard function is shown below:
# as a variable
# ----------------
# ttt : a properly initialized
# display variable
background = background.convert()
background.fill ((250,250,250))
# draw the grid lines
# vertical lines...
pygame.draw.line (background, (0,0,0), (100,0), (100,300), 2)
pygame.draw.line (background, (0,0,0), (200,0), (200,300), 2)
# horizontal lines...
pygame.draw.line (background, (0,0,0), (0,100), (300,100), 2)
pygame.draw.line (background, (0,0,0), (0,200), (300,200), 2)
return background
board = initBoard (ttt)
At this point we have a function (initBoard) which draws the game board on a ``surface'' 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.
ttt.blit (board, (0,0))
pygame.display.flip()
for event in pygame.event.get():
showBoard (ttt, board)
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
XO = ``X''
# declare an empty grid
grid = [ [ None, None, None ],
[ None, None, None ],
[ None, None, None ] ]
Next we need to add a handler to the main event loop. Modify the while loop as shown below:
for event in pygame.event.get():
#update the display
showBoard (ttt, board)
PyGame provides a method to get the coordinates a user clicked at by using the following code:
if (mouseY < 100):
if (mouseX < 100):
return (row, col)
global grid, XO
(mouseX, mouseY) = pygame.mouse.get_pos()
(row, col) = boardPos (mouseX, mouseY)
if ((grid[row][col] == ``X'') or (grid[row][col] == ``O'')):
return
drawMove (board, row, col, XO)
if (XO == ``X''):
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:
# determine the center of the space
# (this works because our spaces are 100 pixels wide and the first one
# is numbered zero)
centerX = boardCol * 100 + 50
centerY = boardRow * 100 + 50
# draw the piece
if (Piece == ``O''):
pygame.draw.circle (board, (0,0,0), (centerX, centerY), 44, 2)
pygame.draw.line (board, (0,0,0), (centerX - 22, centerY - 22), \
# mark the board space as used
grid[boardRow][boardCol] = Piece
Something new that we haven't seen before is the 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.
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:
# this row won
winner = grid[row][0]
pygame.draw.line(board, (250,0,0), (0, (row+1)*100 - 50), \
(300, (row + 1) * 100 - 50), 2)
break
# determine if anyone has won the game
# --------------------------------
# board : the game board surface
global grid, winner
# check for winning rows
for row in range (0, 3):
if ((grid [row][0] == grid[row][1] == grid[row][2]) and \
(grid [row][0] is not None)):
# this row won
winner = grid[row][0]
pygame.draw.line (board, (250,0,0), (0, (row + 1)*100 - 50), \
(300, (row + 1)*100 - 50), 2)
break
# check for winning columns
for col in range (0, 3):
if (grid[0][col] == grid[1][col] == grid[2][col]) and \
(grid[0][col] is not None):
# this column won
winner = grid[0][col]
pygame.draw.line (board, (250,0,0), ((col + 1)* 100 - 50, 0), \
((col + 1)* 100 - 50, 300), 2)
break
# check for diagonal winners
if (grid[0][0] == grid[1][1] == grid[2][2]) and \
(grid[0][0] is not None):
# game won diagonally left to right
winner = grid[0][0]
pygame.draw.line (board, (250,0,0), (50, 50), (250, 250), 2)
if (grid[0][2] == grid[1][1] == grid[2][0]) and \
(grid[0][2] is not None):
# game won diagonally right to left
winner = grid[0][2]
pygame.draw.line (board, (250,0,0), (250, 50), (50, 250), 2)
for event in pygame.event.get():
if event.type is QUIT:
running = 0
elif event.type is MOUSEBUTTONDOWN:
# the user clicked; place an X or O
clickBoard(board)
# check for a winner
gameWon (board)
# update the display
showBoard (ttt, board)
At this point we have a playable game, but we can add a final piece of code to make it a little more ``user-friendly''. In this step we're going to add code that draws the game ``status'' below the board. The status is text such as ``x's turn'' or ``O won!'' 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:
# --------------------------------
# board : the initialized game board surface
# gain access to global variables
global XO, winner
if (winner is None):
font = pygame.font.Font(None, 24)
text = font.render(message, 1, (0,0,0))
# copy the rendered message onto the board
board.fill ((250, 250, 250), (0, 300, 300, 25))
board.blit (text, (10, 300))
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:
# (re)draw the game board (board) on the screen (ttt)
ttt.blit (board, (0,0))
pygame.display.flip()
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.
This document was generated using the LaTeX2HTML translator Version 2002-2 (1.70)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -no_subdir -split 0 -show_section_numbers /tmp/lyx_tmpdir15550Mkiwrh/lyx_tmpbuf0/ttt-tutorial.tex
The translation was initiated by on 2003-03-12