Hey everyone! Today, we’re going to dive into the exciting world of game development using Python and a super fun library called Pygame. If you’ve ever wanted to create your own games but felt intimidated, Tic-Tac-Toe is the perfect starting point. It’s simple enough to understand but teaches you many core game development concepts.
We’ll be building a classic Tic-Tac-Toe game where two players can take turns marking ‘X’s and ‘O’s on a 3×3 grid, right on your computer screen! You’ll learn how to draw graphics, handle mouse clicks, and figure out when someone wins.
What is Pygame?
Before we jump into coding, let’s briefly talk about Pygame.
- Pygame is a set of Python modules (think of them as toolkits) designed specifically for writing video games. It gives you easy ways to draw shapes and images, play sounds, and react to user inputs like keyboard presses or mouse clicks. It’s a fantastic library for beginners because it simplifies many complex parts of game creation.
Getting Started: Setting Up Your Environment
First things first, you need Python installed on your computer. If you don’t have it, head over to the official Python website and download the latest version.
Once Python is ready, open your command prompt or terminal and install Pygame. This is usually a one-line command:
pip install pygame
pip: This is Python’s package installer, a tool that helps you install and manage software packages (like Pygame) written in Python.
If everything goes well, you’re all set to start coding!
Our Game Plan: How We’ll Build Tic-Tac-Toe
Building a game, even a simple one, involves several steps. Here’s our roadmap:
- Initialize Pygame and Set Up the Window: We’ll get Pygame ready and create the window where our game will appear.
- Draw the Game Board: We need a visual 3×3 grid for players to mark their moves.
- Manage Game State: Keep track of whose turn it is, what’s on the board, and if the game is over.
- Handle Player Clicks: Detect where a player clicks and update the board with ‘X’ or ‘O’.
- Draw ‘X’s and ‘O’s: Visually represent the player’s moves on the board.
- Check for a Winner or Draw: Determine if a player has won or if the game is a draw.
- Display Messages: Show who won or if it’s a draw, and offer a way to restart.
- The Main Game Loop: This is the heart of any game, constantly updating and drawing everything.
Let’s start coding!
Step-by-Step Implementation
We’ll build our game piece by piece. You can create a new Python file (e.g., tic_tac_toe.py) and follow along.
1. Basic Setup and Window Creation
First, we import Pygame, initialize it, and set up our game window.
import pygame
import sys
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
WIDTH, HEIGHT = 600, 600
LINE_WIDTH = 10
BOARD_ROWS, BOARD_COLS = 3, 3
SQUARE_SIZE = WIDTH // BOARD_COLS # Each square will be 200x200 pixels
CIRCLE_RADIUS = SQUARE_SIZE // 3
CIRCLE_WIDTH = 15
CROSS_WIDTH = 25
SPACE = SQUARE_SIZE // 4 # Space for X and O not to touch edges
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Tic-Tac-Toe!")
screen.fill(WHITE)
board = [[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
player = 1 # Player 1 is 'X', Player 2 is 'O'
game_over = False
winner = None
pygame.init(): This function gets all the Pygame modules ready to be used. You should always call it at the beginning of your Pygame programs.pygame.display.set_mode((width, height)): This creates a display Surface (the window where all our graphics will appear) with the specified width and height.pygame.display.set_caption(): Sets the title that appears at the top of your game window.screen.fill(color): Fills the entire screen Surface with a solid color.
2. Drawing the Game Board
Next, let’s draw the lines that form our 3×3 Tic-Tac-Toe grid.
def draw_board():
# Horizontal lines
pygame.draw.line(screen, BLACK, (0, SQUARE_SIZE), (WIDTH, SQUARE_SIZE), LINE_WIDTH)
pygame.draw.line(screen, BLACK, (0, 2 * SQUARE_SIZE), (WIDTH, 2 * SQUARE_SIZE), LINE_WIDTH)
# Vertical lines
pygame.draw.line(screen, BLACK, (SQUARE_SIZE, 0), (SQUARE_SIZE, HEIGHT), LINE_WIDTH)
pygame.draw.line(screen, BLACK, (2 * SQUARE_SIZE, 0), (2 * SQUARE_SIZE, HEIGHT), LINE_WIDTH)
pygame.draw.line(surface, color, start_pos, end_pos, width): This function draws a straight line on a givensurface(ourscreen) with a specificcolor, from astart_poscoordinate to anend_poscoordinate, and with a certainwidth.
3. Drawing ‘X’s and ‘O’s
Now we need functions to draw the ‘X’ and ‘O’ marks when players make their moves.
def draw_figures():
for row in range(BOARD_ROWS):
for col in range(BOARD_COLS):
if board[row][col] == 1: # Player 1 (X)
# Draw an 'X'
pygame.draw.line(screen, BLUE, (col * SQUARE_SIZE + SPACE, row * SQUARE_SIZE + SPACE),
(col * SQUARE_SIZE + SQUARE_SIZE - SPACE, row * SQUARE_SIZE + SQUARE_SIZE - SPACE), CROSS_WIDTH)
pygame.draw.line(screen, BLUE, (col * SQUARE_SIZE + SQUARE_SIZE - SPACE, row * SQUARE_SIZE + SPACE),
(col * SQUARE_SIZE + SPACE, row * SQUARE_SIZE + SQUARE_SIZE - SPACE), CROSS_WIDTH)
elif board[row][col] == 2: # Player 2 (O)
# Draw an 'O'
pygame.draw.circle(screen, RED, (int(col * SQUARE_SIZE + SQUARE_SIZE // 2),
int(row * SQUARE_SIZE + SQUARE_SIZE // 2)), CIRCLE_RADIUS, CIRCLE_WIDTH)
pygame.draw.circle(surface, color, center_pos, radius, width): Draws a circle.center_posis the (x, y) coordinate of the circle’s center,radiusis its size, andwidthis the thickness of the line used to draw it (0 for filled).
4. Checking for a Winner or Draw
This is where the game logic comes in. We need to check all possible winning combinations (rows, columns, and diagonals).
def check_win(player_val):
global game_over, winner
# Check horizontal win
for row in range(BOARD_ROWS):
if board[row][0] == player_val and board[row][1] == player_val and board[row][2] == player_val:
game_over = True
winner = player_val
pygame.draw.line(screen, GREEN, (0, row * SQUARE_SIZE + SQUARE_SIZE // 2),
(WIDTH, row * SQUARE_SIZE + SQUARE_SIZE // 2), LINE_WIDTH)
return True
# Check vertical win
for col in range(BOARD_COLS):
if board[0][col] == player_val and board[1][col] == player_val and board[2][col] == player_val:
game_over = True
winner = player_val
pygame.draw.line(screen, GREEN, (col * SQUARE_SIZE + SQUARE_SIZE // 2, 0),
(col * SQUARE_SIZE + SQUARE_SIZE // 2, HEIGHT), LINE_WIDTH)
return True
# Check ascending diagonal win
if board[2][0] == player_val and board[1][1] == player_val and board[0][2] == player_val:
game_over = True
winner = player_val
pygame.draw.line(screen, GREEN, (SPACE, HEIGHT - SPACE), (WIDTH - SPACE, SPACE), LINE_WIDTH)
return True
# Check descending diagonal win
if board[0][0] == player_val and board[1][1] == player_val and board[2][2] == player_val:
game_over = True
winner = player_val
pygame.draw.line(screen, GREEN, (SPACE, SPACE), (WIDTH - SPACE, HEIGHT - SPACE), LINE_WIDTH)
return True
return False
def check_draw():
for row in range(BOARD_ROWS):
for col in range(BOARD_COLS):
if board[row][col] == 0: # If any square is empty, it's not a draw yet
return False
return True # If no square is empty and no winner, it's a draw
5. Displaying Game Messages
We need to show messages like “Player X Wins!” or “It’s a Draw!”.
def display_message(message):
font = pygame.font.Font(None, 80) # None for default font, 80 for font size
text = font.render(message, True, BLACK) # Render the text: (text, antialias, color)
text_rect = text.get_rect(center=(WIDTH // 2, HEIGHT // 2)) # Get rectangle for centering
screen.blit(text, text_rect) # Draw the text onto the screen
# Add a smaller message for restarting
small_font = pygame.font.Font(None, 40)
restart_text = small_font.render("Press 'R' to Restart", True, GRAY)
restart_text_rect = restart_text.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 50))
screen.blit(restart_text, restart_text_rect)
pygame.font.Font(None, size): Creates a font object.Noneuses Pygame’s default font.font.render(text, antialias, color): Renders text into a new Surface.antialiassmooths out the edges of the text.screen.blit(source_surface, dest_position): Draws one image (source_surface) onto another (screen) at a specificdest_position.
6. Resetting the Game
When the game ends, players might want to play again.
def restart_game():
global board, player, game_over, winner
board = [[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
player = 1
game_over = False
winner = None
screen.fill(WHITE) # Clear the screen
draw_board() # Redraw the empty board
7. The Main Game Loop
This is the continuous loop that keeps our game running, handling events, updating the screen, and drawing everything.
running = True
draw_board()
while running:
for event in pygame.event.get(): # Check for all events (user actions)
if event.type == pygame.QUIT: # If the user clicks the 'X' to close the window
running = False
sys.exit() # Exit the program
if event.type == pygame.MOUSEBUTTONDOWN and not game_over:
mouseX = event.pos[0] # x-coordinate of mouse click
mouseY = event.pos[1] # y-coordinate of mouse click
# Determine which square was clicked
clicked_col = mouseX // SQUARE_SIZE
clicked_row = mouseY // SQUARE_SIZE
# Make sure click is within board bounds and the cell is empty
if 0 <= clicked_row < BOARD_ROWS and 0 <= clicked_col < BOARD_COLS and board[clicked_row][clicked_col] == 0:
board[clicked_row][clicked_col] = player # Place current player's mark
if check_win(player):
message = f"Player {winner} Wins!"
elif check_draw():
game_over = True
message = "It's a Draw!"
else:
# Switch player for the next turn
player = 1 if player == 2 else 2 # If player was 2, switch to 1; otherwise, switch to 2
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r: # Check if 'R' key is pressed
restart_game()
# Always redraw everything in the loop
screen.fill(WHITE) # Clear the screen each frame
draw_board() # Draw the grid lines
draw_figures() # Draw X's and O's
if game_over:
if winner:
display_message(f"Player {winner} Wins!")
else:
display_message("It's a Draw!")
pygame.display.update() # Update the full display Surface to the screen
while running:: This loop continues as long asrunningisTrue. Most of your game logic and drawing will happen inside this loop.pygame.event.get(): This function fetches all the user events (like mouse clicks, keyboard presses, window closing) that have happened since the last call.event.type == pygame.QUIT: Checks if the user clicked the close button of the window.pygame.MOUSEBUTTONDOWN: This event occurs when a mouse button is pressed down.pygame.display.update(): This is crucial! It takes everything you’ve drawn on thescreenSurface and actually displays it on your computer monitor. Without this, you wouldn’t see any changes.
Putting It All Together (Full Code)
Here’s the complete code for our simple Tic-Tac-Toe game. You can copy and paste this into a tic_tac_toe.py file and run it!
import pygame
import sys
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
WIDTH, HEIGHT = 600, 600
LINE_WIDTH = 10
BOARD_ROWS, BOARD_COLS = 3, 3
SQUARE_SIZE = WIDTH // BOARD_COLS # Each square will be 200x200 pixels
CIRCLE_RADIUS = SQUARE_SIZE // 3
CIRCLE_WIDTH = 15
CROSS_WIDTH = 25
SPACE = SQUARE_SIZE // 4 # Space for X and O not to touch edges
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Tic-Tac-Toe!")
screen.fill(WHITE)
board = [[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
player = 1 # Player 1 is 'X', Player 2 is 'O'
game_over = False
winner = None
def draw_board():
# Horizontal lines
pygame.draw.line(screen, BLACK, (0, SQUARE_SIZE), (WIDTH, SQUARE_SIZE), LINE_WIDTH)
pygame.draw.line(screen, BLACK, (0, 2 * SQUARE_SIZE), (WIDTH, 2 * SQUARE_SIZE), LINE_WIDTH)
# Vertical lines
pygame.draw.line(screen, BLACK, (SQUARE_SIZE, 0), (SQUARE_SIZE, HEIGHT), LINE_WIDTH)
pygame.draw.line(screen, BLACK, (2 * SQUARE_SIZE, 0), (2 * SQUARE_SIZE, HEIGHT), LINE_WIDTH)
def draw_figures():
for row in range(BOARD_ROWS):
for col in range(BOARD_COLS):
if board[row][col] == 1: # Player 1 (X)
# Draw an 'X'
pygame.draw.line(screen, BLUE, (col * SQUARE_SIZE + SPACE, row * SQUARE_SIZE + SPACE),
(col * SQUARE_SIZE + SQUARE_SIZE - SPACE, row * SQUARE_SIZE + SQUARE_SIZE - SPACE), CROSS_WIDTH)
pygame.draw.line(screen, BLUE, (col * SQUARE_SIZE + SQUARE_SIZE - SPACE, row * SQUARE_SIZE + SPACE),
(col * SQUARE_SIZE + SPACE, row * SQUARE_SIZE + SQUARE_SIZE - SPACE), CROSS_WIDTH)
elif board[row][col] == 2: # Player 2 (O)
# Draw an 'O'
pygame.draw.circle(screen, RED, (int(col * SQUARE_SIZE + SQUARE_SIZE // 2),
int(row * SQUARE_SIZE + SQUARE_SIZE // 2)), CIRCLE_RADIUS, CIRCLE_WIDTH)
def check_win(player_val):
global game_over, winner
# Check horizontal win
for row in range(BOARD_ROWS):
if board[row][0] == player_val and board[row][1] == player_val and board[row][2] == player_val:
game_over = True
winner = player_val
pygame.draw.line(screen, GREEN, (0, row * SQUARE_SIZE + SQUARE_SIZE // 2),
(WIDTH, row * SQUARE_SIZE + SQUARE_SIZE // 2), LINE_WIDTH)
return True
# Check vertical win
for col in range(BOARD_COLS):
if board[0][col] == player_val and board[1][col] == player_val and board[2][col] == player_val:
game_over = True
winner = player_val
pygame.draw.line(screen, GREEN, (col * SQUARE_SIZE + SQUARE_SIZE // 2, 0),
(col * SQUARE_SIZE + SQUARE_SIZE // 2, HEIGHT), LINE_WIDTH)
return True
# Check ascending diagonal win (bottom-left to top-right)
if board[2][0] == player_val and board[1][1] == player_val and board[0][2] == player_val:
game_over = True
winner = player_val
pygame.draw.line(screen, GREEN, (SPACE, HEIGHT - SPACE), (WIDTH - SPACE, SPACE), LINE_WIDTH)
return True
# Check descending diagonal win (top-left to bottom-right)
if board[0][0] == player_val and board[1][1] == player_val and board[2][2] == player_val:
game_over = True
winner = player_val
pygame.draw.line(screen, GREEN, (SPACE, SPACE), (WIDTH - SPACE, HEIGHT - SPACE), LINE_WIDTH)
return True
return False
def check_draw():
for row in range(BOARD_ROWS):
for col in range(BOARD_COLS):
if board[row][col] == 0: # If any square is empty, it's not a draw yet
return False
# If no winner and no empty squares, it's a draw
return True
def display_message(message):
font = pygame.font.Font(None, 80) # None for default font, 80 for font size
text = font.render(message, True, BLACK) # Render the text: (text, antialias, color)
text_rect = text.get_rect(center=(WIDTH // 2, HEIGHT // 2)) # Get rectangle for centering
screen.blit(text, text_rect) # Draw the text onto the screen
# Add a smaller message for restarting
small_font = pygame.font.Font(None, 40)
restart_text = small_font.render("Press 'R' to Restart", True, GRAY)
restart_text_rect = restart_text.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 50))
screen.blit(restart_text, restart_text_rect)
def restart_game():
global board, player, game_over, winner
board = [[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
player = 1
game_over = False
winner = None
screen.fill(WHITE) # Clear the screen
draw_board() # Redraw the empty board
running = True
draw_board()
while running:
for event in pygame.event.get(): # Check for all events (user actions)
if event.type == pygame.QUIT: # If the user clicks the 'X' to close the window
running = False
sys.exit() # Exit the program gracefully
if event.type == pygame.MOUSEBUTTONDOWN and not game_over:
mouseX = event.pos[0] # x-coordinate of mouse click
mouseY = event.pos[1] # y-coordinate of mouse click
# Determine which square was clicked
clicked_col = mouseX // SQUARE_SIZE
clicked_row = mouseY // SQUARE_SIZE
# Make sure click is within board bounds and the cell is empty
if 0 <= clicked_row < BOARD_ROWS and 0 <= clicked_col < BOARD_COLS and board[clicked_row][clicked_col] == 0:
board[clicked_row][clicked_col] = player # Place current player's mark
if check_win(player):
# Winner determined, message set inside check_win
pass
elif check_draw():
game_over = True
winner = None # No specific winner in a draw
else:
# Switch player for the next turn
player = 1 if player == 2 else 2 # If player was 2, switch to 1; otherwise, switch to 2
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r: # Check if 'R' key is pressed
restart_game()
# Always redraw everything in the loop
screen.fill(WHITE) # Clear the screen each frame before drawing
draw_board() # Draw the grid lines
draw_figures() # Draw X's and O's
if game_over:
if winner:
display_message(f"Player {winner} Wins!")
else:
display_message("It's a Draw!")
pygame.display.update() # Update the full display Surface to the screen
Conclusion
Congratulations! You’ve just created your very own interactive Tic-Tac-Toe game using Pygame. You’ve learned how to:
- Set up a Pygame window.
- Draw shapes and lines to create your game board and player marks.
- Handle mouse clicks and keyboard presses.
- Implement game logic for turns, wins, and draws.
- Display text messages to the player.
This is a fantastic foundation for further game development. Don’t stop here! Try experimenting with:
- Adding sound effects for moves and wins.
- Creating a simple AI opponent.
- Making the game visually more appealing with different colors or images.
- Adding a scoreboard.
The possibilities are endless. Keep coding, keep experimenting, and most importantly, keep having fun!
Leave a Reply
You must be logged in to post a comment.