During a Python webinar, one of my students asked me about creating a Tic Tac Toe game. After exploring more about this topic, I found a way to do it. In this tutorial, I will explain to you how to create a Tic Tac Toe Game using Pygame.
What is the use of the grid in Python Pygame
In Python Pygame, a grid is used to organize the game window into a structured layout of cells or blocks. This grid system helps in several ways:
Visual Structure: It provides a clear visual structure for the player, making the gameplay more understandable and organized.
Positioning and Movement: It allows for easy positioning of game elements like characters, obstacles, or items by snapping them to fixed grid cells. This is especially useful for games with tile-based maps or grid-based movement (e.g., snake games, puzzle games).
Collision Detection: Managing collisions becomes simpler because you can check interactions on a cell-by-cell basis rather than pixel-by-pixel.
Game Logic Simplification: Implementing game rules, pathfinding, or map generation is easier when the game world is divided into a grid.
Example:
import pygame
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
WIDTH = 20
HEIGHT = 20
MARGIN = 5
grid = []
for row in range(10):
grid.append([])
for column in range(10):
grid[row].append(0)
grid[1][5] = 1
pygame.init()
window_size = [255, 255]
scr = pygame.display.set_mode(window_size)
pygame.display.set_caption("Grid")
done = False
clock = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
column = pos[0] // (WIDTH + MARGIN)
row = pos[1] // (HEIGHT + MARGIN)
grid[row][column] = 1
print("Click ", pos, "Grid coordinates: ", row, column)
scr.fill(black)
for row in range(10):
for column in range(10):
color = white
if grid[row][column] == 1:
color = red
pygame.draw.rect(scr,
color,
[(MARGIN + WIDTH) * column + MARGIN,
(MARGIN + HEIGHT) * row + MARGIN,
WIDTH,
HEIGHT])
clock.tick(50)
pygame.display.flip()
pygame.quit()In this output, we can see that the new window appears with the grid in Python PyGame. The red box indicates the position in the grid that was clicked by the mouse.

In the screenshot below, you can see the mouse position by clicking and the coordinates.

Check out Validate Email Addresses in Python
Create a Tic Tac Toe Game using Python Pygame
Here, we will explain the easy way to code the tic-tac-toe game in Python using PyGame. It is recommended to go through the steps below.
Step1.
- Firstly, we will import pygame, sys, and NumPy Python libraries to build this game.
- Set the width and height of the game window. Also, set the background color as BG_COLOR = (20, 200, 160)
- Set the LINE_COLOR, CIRCLE_COLOR, and CROSS_COLOR according to your choice.
- To create a board, we will use np.zeros().
- pygame.draw.line() is used for drawing a horizontal line and a vertical line.
import pygame, sys
import numpy as np
pygame.init()
WIDTH = 600
HEIGHT = 600
LINE_WIDTH = 15
WIN_LINE_WIDTH = 15
BOARD_ROWS = 3
BOARD_COLS = 3
SQUARE_SIZE = 200
CIRCLE_RADIUS = 60
CIRCLE_WIDTH = 15
CROSS_WIDTH = 25
SPACE = 55
BG_COLOR = (20, 200, 160)
LINE_COLOR = (23, 145, 135)
CIRCLE_COLOR = (239, 231, 200)
CROSS_COLOR = (66, 66, 66)
screen = pygame.display.set_mode( (WIDTH, HEIGHT) )
pygame.display.set_caption( 'TIC TAC TOE' )
screen.fill( BG_COLOR )
board = np.zeros( (BOARD_ROWS, BOARD_COLS) )
def draw_lines():
pygame.draw.line( screen, LINE_COLOR, (0, SQUARE_SIZE), (WIDTH, SQUARE_SIZE), LINE_WIDTH )
pygame.draw.line( screen, LINE_COLOR, (0, 2 * SQUARE_SIZE), (WIDTH, 2 * SQUARE_SIZE), LINE_WIDTH )
pygame.draw.line( screen, LINE_COLOR, (SQUARE_SIZE, 0), (SQUARE_SIZE, HEIGHT), LINE_WIDTH )
pygame.draw.line( screen, LINE_COLOR, (2 * SQUARE_SIZE, 0), (2 * SQUARE_SIZE, HEIGHT), LINE_WIDTH )Step2:
- def draw_figures() is used for drawing the circle and line for the cross.
- def mark_square() function is used for marking on the board, and it will have 3 parameters.
- def available_square() will return true if the square is available, and it’s going to return false if not available.
- def is_board_full() function will return true if the board is full, and if it’s not full, then it will return false. And it will loop through rows and columns.
- If board[row][col] == 0, that means we have found the empty square, and it will return false, and if we don’t find the empty square, then it will return true.
def draw_figures():
for row in range(BOARD_ROWS):
for col in range(BOARD_COLS):
if board[row][col] == 1:
pygame.draw.circle( screen, CIRCLE_COLOR, (int( col * SQUARE_SIZE + SQUARE_SIZE//2 ), int( row * SQUARE_SIZE + SQUARE_SIZE//2 )), CIRCLE_RADIUS, CIRCLE_WIDTH )
elif board[row][col] == 2:
pygame.draw.line( screen, CROSS_COLOR, (col * SQUARE_SIZE + SPACE, row * SQUARE_SIZE + SQUARE_SIZE - SPACE), (col * SQUARE_SIZE + SQUARE_SIZE - SPACE, row * SQUARE_SIZE + SPACE), CROSS_WIDTH )
pygame.draw.line( screen, CROSS_COLOR, (col * SQUARE_SIZE + SPACE, row * SQUARE_SIZE + SPACE), (col * SQUARE_SIZE + SQUARE_SIZE - SPACE, row * SQUARE_SIZE + SQUARE_SIZE - SPACE), CROSS_WIDTH )
def mark_square(row, col, player):
board[row][col] = player
def available_square(row, col):
return board[row][col] == 0
def is_board_full():
for row in range(BOARD_ROWS):
for col in range(BOARD_COLS):
if board[row][col] == 0:
return False
return TrueStep3:
- Here, def check_win(player) function is used. Also, the for col in range(BOARD_COLS) is used to check the vertical winning line.
- for row in range(BOARD_ROWS) is used to check the horizontal winning line.
- Now, we will check for a diagonal win for ascending draw_asc_diagonal(player) and for descending draw_desc_diagonal(player).
- def draw_vertical_winning_line(col, player) is used to draw a vertical winning line.
- if player == 1 then circle color, elif player == 2 then cross color.
def check_win(player):
for col in range(BOARD_COLS):
if board[0][col] == player and board[1][col] == player and board[2][col] == player:
draw_vertical_winning_line(col, player)
return True
for row in range(BOARD_ROWS):
if board[row][0] == player and board[row][1] == player and board[row][2] == player:
draw_horizontal_winning_line(row, player)
return True
if board[2][0] == player and board[1][1] == player and board[0][2] == player:
draw_asc_diagonal(player)
return True
if board[0][0] == player and board[1][1] == player and board[2][2] == player:
draw_desc_diagonal(player)
return True
return False
def draw_vertical_winning_line(col, player):
posX = col * SQUARE_SIZE + SQUARE_SIZE//2
if player == 1:
color = CIRCLE_COLOR
elif player == 2:
color = CROSS_COLOR
pygame.draw.line( screen, color, (posX, 15), (posX, HEIGHT - 15), LINE_WIDTH )Step4:
- def draw_horizontal_winning_line() this function is used for drawing a horizontal winning line.
- if player == 1 then circle color, elif player == 2 then cross color.
- def draw_asc_diagonal(player) for drawing a winning line for the ascending diagonal.
- if player == 1 then circle color, elif player == 2 then cross color
def draw_horizontal_winning_line(row, player):
posY = row * SQUARE_SIZE + SQUARE_SIZE//2
if player == 1:
color = CIRCLE_COLOR
elif player == 2:
color = CROSS_COLOR
pygame.draw.line( screen, color, (15, posY), (WIDTH - 15, posY), WIN_LINE_WIDTH )
def draw_asc_diagonal(player):
if player == 1:
color = CIRCLE_COLOR
elif player == 2:
color = CROSS_COLOR
pygame.draw.line( screen, color, (15, HEIGHT - 15), (WIDTH - 15, 15), WIN_LINE_WIDTH )Step5:
- def draw_desc_diagonal(player) is used for drawing a descending winning diagonal.
- if player == 1 then circle color, elif player == 2 then cross color.
- pygame.draw.line() is used for drawing a line.
- def restart() this function is called when your game ends and you want to play again.
def draw_desc_diagonal(player):
if player == 1:
color = CIRCLE_COLOR
elif player == 2:
color = CROSS_COLOR
pygame.draw.line( screen, color, (15, 15), (WIDTH - 15, HEIGHT - 15), WIN_LINE_WIDTH )
def restart():
screen.fill( BG_COLOR )
draw_lines()
for row in range(BOARD_ROWS):
for col in range(BOARD_COLS):
board[row][col] = 0Step6:
- Now, we need to set the game_over = False, if the player wins, then set it to True, and the game is over.
- While True, the main loop starts.
- Call the function check_win in the main loop.
- We used the sys library of Python to exit the game.
- But if the mouse is pressed, the event.get() will return “MOUSEBUTTONDOWN” and call to user_click(). To know the exact coordinates of the board where the user has clicked.
- if event.key == pygame.K_r then restart the game by pressing “r” and start playing
draw_lines()
player = 1
game_over = False
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN and not game_over:
mouseX = event.pos[0]
mouseY = event.pos[1]
clicked_row = int(mouseY // SQUARE_SIZE)
clicked_col = int(mouseX // SQUARE_SIZE)
if available_square( clicked_row, clicked_col ):
mark_square( clicked_row, clicked_col, player )
if check_win( player ):
game_over = True
player = player % 2 + 1
draw_figures()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
restart()
player = 1
game_over = False
pygame.display.update()Complete Code for the Tac-Tac-Toe Game
import pygame, sys
import numpy as np
pygame.init()
WIDTH = 600
HEIGHT = 600
LINE_WIDTH = 15
WIN_LINE_WIDTH = 15
BOARD_ROWS = 3
BOARD_COLS = 3
SQUARE_SIZE = 200
CIRCLE_RADIUS = 60
CIRCLE_WIDTH = 15
CROSS_WIDTH = 25
SPACE = 55
RED = (255, 0, 0)
BG_COLOR = (20, 200, 160)
LINE_COLOR = (23, 145, 135)
CIRCLE_COLOR = (239, 231, 200)
CROSS_COLOR = (66, 66, 66)
screen = pygame.display.set_mode( (WIDTH, HEIGHT) )
pygame.display.set_caption( 'TIC TAC TOE' )
screen.fill( BG_COLOR )
board = np.zeros( (BOARD_ROWS, BOARD_COLS) )
def draw_lines():
pygame.draw.line( screen, LINE_COLOR, (0, SQUARE_SIZE), (WIDTH, SQUARE_SIZE), LINE_WIDTH )
pygame.draw.line( screen, LINE_COLOR, (0, 2 * SQUARE_SIZE), (WIDTH, 2 * SQUARE_SIZE), LINE_WIDTH )
pygame.draw.line( screen, LINE_COLOR, (SQUARE_SIZE, 0), (SQUARE_SIZE, HEIGHT), LINE_WIDTH )
pygame.draw.line( screen, LINE_COLOR, (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:
pygame.draw.circle( screen, CIRCLE_COLOR, (int( col * SQUARE_SIZE + SQUARE_SIZE//2 ), int( row * SQUARE_SIZE + SQUARE_SIZE//2 )), CIRCLE_RADIUS, CIRCLE_WIDTH )
elif board[row][col] == 2:
pygame.draw.line( screen, CROSS_COLOR, (col * SQUARE_SIZE + SPACE, row * SQUARE_SIZE + SQUARE_SIZE - SPACE), (col * SQUARE_SIZE + SQUARE_SIZE - SPACE, row * SQUARE_SIZE + SPACE), CROSS_WIDTH )
pygame.draw.line( screen, CROSS_COLOR, (col * SQUARE_SIZE + SPACE, row * SQUARE_SIZE + SPACE), (col * SQUARE_SIZE + SQUARE_SIZE - SPACE, row * SQUARE_SIZE + SQUARE_SIZE - SPACE), CROSS_WIDTH )
def mark_square(row, col, player):
board[row][col] = player
def available_square(row, col):
return board[row][col] == 0
def is_board_full():
for row in range(BOARD_ROWS):
for col in range(BOARD_COLS):
if board[row][col] == 0:
return False
return True
def check_win(player):
for col in range(BOARD_COLS):
if board[0][col] == player and board[1][col] == player and board[2][col] == player:
draw_vertical_winning_line(col, player)
return True
for row in range(BOARD_ROWS):
if board[row][0] == player and board[row][1] == player and board[row][2] == player:
draw_horizontal_winning_line(row, player)
return True
if board[2][0] == player and board[1][1] == player and board[0][2] == player:
draw_asc_diagonal(player)
return True
if board[0][0] == player and board[1][1] == player and board[2][2] == player:
draw_desc_diagonal(player)
return True
return False
def draw_vertical_winning_line(col, player):
posX = col * SQUARE_SIZE + SQUARE_SIZE//2
if player == 1:
color = CIRCLE_COLOR
elif player == 2:
color = CROSS_COLOR
pygame.draw.line( screen, color, (posX, 15), (posX, HEIGHT - 15), LINE_WIDTH )
def draw_horizontal_winning_line(row, player):
posY = row * SQUARE_SIZE + SQUARE_SIZE//2
if player == 1:
color = CIRCLE_COLOR
elif player == 2:
color = CROSS_COLOR
pygame.draw.line( screen, color, (15, posY), (WIDTH - 15, posY), WIN_LINE_WIDTH )
def draw_asc_diagonal(player):
if player == 1:
color = CIRCLE_COLOR
elif player == 2:
color = CROSS_COLOR
pygame.draw.line( screen, color, (15, HEIGHT - 15), (WIDTH - 15, 15), WIN_LINE_WIDTH )
def draw_desc_diagonal(player):
if player == 1:
color = CIRCLE_COLOR
elif player == 2:
color = CROSS_COLOR
pygame.draw.line( screen, color, (15, 15), (WIDTH - 15, HEIGHT - 15), WIN_LINE_WIDTH )
def restart():
screen.fill( BG_COLOR )
draw_lines()
for row in range(BOARD_ROWS):
for col in range(BOARD_COLS):
board[row][col] = 0
draw_lines()
player = 1
game_over = False
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN and not game_over:
mouseX = event.pos[0]
mouseY = event.pos[1]
clicked_row = int(mouseY // SQUARE_SIZE)
clicked_col = int(mouseX // SQUARE_SIZE)
if available_square( clicked_row, clicked_col ):
mark_square( clicked_row, clicked_col, player )
if check_win( player ):
game_over = True
player = player % 2 + 1
draw_figures()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
restart()
player = 1
game_over = False
pygame.display.update()Output1:
In the output below, you can see that “X” wins the game.

Output2:
In the output below, you can see that “O” wins the game.

Output3:
In the output below, you can see that no one wins the game.

I hope now you can create a grid game like the tic tac toe game in Python, and also we learned how to use the pygame and sys module. Here, we learned about how to create a tic-tac-toe game in Python using Pygame, and we have the full source code with an explanation.
You may like the following Python tutorials:

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.