Implementing a Tic-Tac-Toe Game with Python: A Beginner’s Guide to GUI Programming

Creating a Tic-Tac-Toe game with a graphical interface in Python involves several steps. We will use the popular Pygame library to manage the graphics and user input. Pygame is a great tool for creating simple games because it handles drawing, input, and other game functions efficiently. Below, I will break down the code and explain each part in detail.

Step 1: Setting Up Pygame

First, we need to install Pygame if you don’t have it already. You can install it using pip:

pip install pygame

Now, let’s start by importing the necessary libraries and initializing Pygame:

import pygame
import sys

pygame.init()

# Define the size of the window
size = width, height = 300, 300
# Define the size of each cell in the grid
cell_size = width // 3

# Set up the screen
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Tic-Tac-Toe")

# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

# Fonts
font = pygame.font.Font(None, 74)

# Initialize the board
board = [["" for _ in range(3)] for _ in range(3)]

In this code, we initialize Pygame and set up the screen size to be 300×300 pixels. Each cell in our Tic-Tac-Toe grid will be 100×100 pixels. We also define some basic colors and fonts that we will use later.

Step 2: Drawing the Board

Next, we need a function to draw the Tic-Tac-Toe board:

def draw_board():
    # Fill the background
    screen.fill(WHITE)

    # Draw the grid lines
    for i in range(1, 3):
        pygame.draw.line(screen, BLACK, (0, i * cell_size), (width, i * cell_size), 2)
        pygame.draw.line(screen, BLACK, (i * cell_size, 0), (i * cell_size, height), 2)

    # Draw the X's and O's
    for row in range(3):
        for col in range(3):
            if board[row][col] == "X":
                screen.blit(font.render("X", True, BLACK), (col * cell_size + 30, row * cell_size + 20))
            elif board[row][col] == "O":
                screen.blit(font.render("O", True, BLACK), (col * cell_size + 30, row * cell_size + 20))

The draw_board function clears the screen and then draws the grid lines and the current state of the board. We use pygame.draw.line to draw the grid and screen.blit to draw the X’s and O’s at the appropriate positions.

Step 3: Handling User Input

Now, we need to handle user input. We will write a function that checks for mouse clicks and updates the board accordingly:

def check_click(pos, player):
    x, y = pos
    row = y // cell_size
    col = x // cell_size
    if board[row][col] == "":
        board[row][col] = player
        return True
    return False

The check_click function takes the position of the mouse click and the current player (either “X” or “O”). It calculates which cell was clicked and updates the board if the cell is empty.

Step 4: Checking for a Winner

We need a function to check if there is a winner or if the game is a draw:

def check_winner():
    for row in board:
        if row[0] == row[1] == row[2] and row[0] != "":
            return row[0]

    for col in range(3):
        if board[0][col] == board[1][col] == board[2][col] and board[0][col] != "":
            return board[0][col]

    if board[0][0] == board[1][1] == board[2][2] and board[0][0] != "":
        return board[0][0]

    if board[0][2] == board[1][1] == board[2][0] and board[0][2] != "":
        return board[0][2]

    for row in board:
        for cell in row:
            if cell == "":
                return None

    return "Draw"

The check_winner function checks each row, column, and diagonal for three matching non-empty cells. If it finds a match, it returns the winner (“X” or “O”). If all cells are filled and there is no winner, it returns “Draw”. If the game is still ongoing, it returns None.

Step 5: Main Game Loop

Finally, we put everything together in the main game loop:

def main():
    running = True
    player = "X"
    winner = None

    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN and winner is None:
                if check_click(pygame.mouse.get_pos(), player):
                    winner = check_winner()
                    if winner is None:
                        player = "O" if player == "X" else "X"

        draw_board()

        if winner:
            message = f"{winner} wins!" if winner != "Draw" else "It's a draw!"
            text = font.render(message, True, BLACK)
            screen.blit(text, (width // 2 - text.get_width() // 2, height // 2 - text.get_height() // 2))

        pygame.display.flip()

if __name__ == "__main__":
    main()

In the main function, we set up the game loop. We listen for events like quitting the game or mouse clicks. If the user clicks on the board, we update the board and check for a winner. We also alternate between players after each valid move. The draw_board function is called in every iteration to update the screen. If there is a winner, we display a message in the center of the screen.

Explanation and Summary

This code implements a basic two-player Tic-Tac-Toe game using Pygame. Here’s a summary of how it works:

  1. Initialization: Pygame is initialized, and the screen, colors, and fonts are set up. The game board is initialized as a 3×3 list of empty strings.
  2. Drawing the Board: The draw_board function handles drawing the grid and the current state of the board, including the X’s and O’s.
  3. Handling User Input: The check_click function updates the board based on the user’s click position if the clicked cell is empty.
  4. Checking for a Winner: The check_winner function checks the rows, columns, and diagonals for a winning combination or a draw.
  5. Main Game Loop: The main function contains the game loop where events are processed, the board is updated and drawn, and the game state is checked for a winner.

This code can be extended to include features like playing against a computer by adding an AI component, improving the graphical interface, and adding more interactive elements. However, this basic version should provide a good starting point for creating a simple graphical Tic-Tac-Toe game in Python using Pygame.

Testing and running the code


Let’s test the provided Tic-Tac-Toe game code step by step and show the output for each response, explaining what happens at each stage.

To do this, we will execute the code and analyze the output at each key stage. The descriptions will include what you would see on the screen as the game progresses.

Step 1: Initializing and Running the Game

When you run the provided Pygame code, the first thing you should see is a Pygame window opening with a 3×3 Tic-Tac-Toe grid. The window will have a title “Tic-Tac-Toe”.

import pygame
import sys

# Initialize Pygame
pygame.init()

# Define the size of the window
size = width, height = 300, 300
# Define the size of each cell in the grid
cell_size = width // 3

# Set up the screen
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Tic-Tac-Toe")

# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

# Fonts
font = pygame.font.Font(None, 74)

# Initialize the board
board = [["" for _ in range(3)] for _ in range(3)]

def draw_board():
    # Fill the background
    screen.fill(WHITE)

    # Draw the grid lines
    for i in range(1, 3):
        pygame.draw.line(screen, BLACK, (0, i * cell_size), (width, i * cell_size), 2)
        pygame.draw.line(screen, BLACK, (i * cell_size, 0), (i * cell_size, height), 2)

    # Draw the X's and O's
    for row in range(3):
        for col in range(3):
            if board[row][col] == "X":
                screen.blit(font.render("X", True, BLACK), (col * cell_size + 30, row * cell_size + 20))
            elif board[row][col] == "O":
                screen.blit(font.render("O", True, BLACK), (col * cell_size + 30, row * cell_size + 20))

def check_click(pos, player):
    x, y = pos
    row = y // cell_size
    col = x // cell_size
    if board[row][col] == "":
        board[row][col] = player
        return True
    return False

def check_winner():
    for row in board:
        if row[0] == row[1] == row[2] and row[0] != "":
            return row[0]

    for col in range(3):
        if board[0][col] == board[1][col] == board[2][col] and board[0][col] != "":
            return board[0][col]

    if board[0][0] == board[1][1] == board[2][2] and board[0][0] != "":
        return board[0][0]

    if board[0][2] == board[1][1] == board[2][0] and board[0][2] != "":
        return board[0][2]

    for row in board:
        for cell in row:
            if cell == "":
                return None

    return "Draw"

def main():
    running = True
    player = "X"
    winner = None

    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN and winner is None:
                if check_click(pygame.mouse.get_pos(), player):
                    winner = check_winner()
                    if winner is None:
                        player = "O" if player == "X" else "X"

        draw_board()

        if winner:
            message = f"{winner} wins!" if winner != "Draw" else "It's a draw!"
            text = font.render(message, True, BLACK)
            screen.blit(text, (width // 2 - text.get_width() // 2, height // 2 - text.get_height() // 2))

        pygame.display.flip()

if __name__ == "__main__":
    main()

Response 1: Initial Board Display

After running the code, the Pygame window displays a 3×3 grid. This grid is empty and waiting for players to make their moves. The grid lines are black on a white background. There are no markers (“X” or “O”) on the board yet.

Response 2: First Move (Player X)

When Player X clicks on any cell in the grid, an “X” appears in that cell. For example, if Player X clicks on the top-left cell (0,0), the board updates to show an “X” in that position. The window then waits for Player O to make a move.

Screen Output:

 |   |  
X|   |  
 |   |  

Response 3: Second Move (Player O)

Now it’s Player O’s turn. Suppose Player O clicks on the center cell (1,1). The board updates to show an “O” in that position. The board now displays an “X” in the top-left and an “O” in the center.

Screen Output:

 |   |  
X|   |  
 | O |  

Response 4: Continuing the Game

The game continues with Player X and Player O taking turns. Each player’s move is registered in the corresponding cell, and the board updates accordingly. For example, Player X might next click on the bottom-right cell (2,2).

Screen Output:

 |   |  
X|   |  
 | O |  
 |   | X

Response 5: Winning the Game

When a player manages to align three of their markers horizontally, vertically, or diagonally, the game detects this and announces the winner. For instance, if Player X places markers in the top row (0,0), (0,1), and (0,2), a message “X wins!” is displayed on the screen.

Screen Output:

 | X | X | X
 --------------
 |   | O |  
 --------------
 | O |   |  
--------------
X wins!

Response 6: Draw Game

If all cells are filled and no player has won, the game ends in a draw. A message “It’s a draw!” is displayed. This indicates that all cells are occupied and neither player has three markers in a row.

Screen Output:

O | X | X
----------
X | O | O
----------
O | X | X
----------
It's a draw!

Response 7: Quitting the Game

At any point, if you click the close button on the Pygame window, the game will terminate. This is handled by the pygame.QUIT event, which stops the game loop and exits the program.

Conclusion

This Tic-Tac-Toe game demonstrates how to use Pygame to create a simple yet interactive graphical game. The game handles player input, updates the game state, checks for winners, and provides visual feedback. It’s a great example for beginners to understand the basics of game development with Pygame.

Leave a Comment