Introduction
In this project, we will create a Sudoku game using Python and the Pygame library. The game will generate a Sudoku grid of the specified difficulty level and let players solve the puzzle by filling in the empty cells with numbers. The game will provide features like selecting difficulty, highlighting selected cells, and checking if the grid is complete.
👀 Preview

🎯 Tasks
In this project, you will learn:
- How to import required libraries
- How to initialize PyGame
- How to define colors
- How to set game window dimensions and title
- How to create the game window
- How to load fonts
- How to generate a Sudoku grid
- How to solve the Sudoku grid using backtracking algorithm
- How to remove numbers from the grid based on difficulty
- How to draw the Sudoku grid on the game window
- How to check if the grid is fully filled
- How to get the cell coordinates under mouse position
- How to select the difficulty level
- How to implement the main game loop
🏆 Achievements
After completing this project, you will be able to:
- Use the Pygame library for game development in Python
- Generate a Sudoku grid of a specified difficulty level
- Solve a Sudoku grid using the backtracking algorithm
- Handle mouse and keyboard events in Pygame
- Draw shapes and text on the game window
- Implement the main game loop in Pygame
Create the Project Files
To begin, create a file named sudoku_game.py and open it in your preferred text editor or integrated development environment (IDE).
cd ~/project
touch sudoku_game.py
Import Required Libraries
Import the required libraries at the beginning of the file. We need the pygame and random libraries for this game.
import pygame
import random
Install the pygame library using the pip command.
sudo pip install pygame
Initialize PyGame
Initialize the Pygame library to set up the game window.
pygame.init()
Define Colors
Define the colors to be used in the game. We will use the RGB format for colors.
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (200, 200, 200)
BLUE = (0, 0, 255)
LIGHT_BLUE = (100, 100, 255)
Set Game Window Dimensions and Title
Set the dimensions of the game window and set a title for the game.
WINDOW_SIZE = (550, 550)
CELL_SIZE = WINDOW_SIZE[0] // 9
pygame.display.set_caption("Sudoku Game")
Create the Game Window
Create the game window using the specified dimensions.
screen = pygame.display.set_mode(WINDOW_SIZE)
Load Fonts
We will need to load fonts to display numbers on the game screen. Load two fonts, one for the larger numbers and one for the smaller numbers.
font_large = pygame.font.SysFont("calibri", 50)
font_small = pygame.font.SysFont("calibri", 30)
Generate Sudoku Grid
Create a function generate_sudoku(difficulty) that generates a new Sudoku grid of the specified difficulty level. The function should return the generated grid.
def generate_sudoku(difficulty):
## Function code goes here
pass
Fill Diagonal Subgrids
Inside the generate_sudoku function, fill the diagonal subgrids of the grid with random numbers. This ensures that each subgrid contains numbers from 1 to 9 without repetition.
## Fill diagonal subgrids
for i in range(0, 9, 3):
nums = random.sample(range(1, 10), 3)
for j in range(3):
grid[i + j][i + j] = nums[j]
Solve Sudoku Grid
Create a function solve_sudoku(grid) that solves the Sudoku grid using the backtracking algorithm. The function should return True if the grid is solvable and False otherwise.
def solve_sudoku(grid):
## Function code goes here
pass
Find Empty Cell
Inside the solve_sudoku function, create a helper function find_empty_cell(grid) that returns the coordinates of the next empty cell in the grid. If there are no empty cells, return None.
def find_empty_cell(grid):
for row in range(9):
for col in range(9):
if grid[row][col] == 0:
return (row, col)
return None
Check Valid Move
Inside the solve_sudoku function, create a helper function is_valid_move(grid, row, col, num) that checks if placing a number in a cell is a valid move. The function should return True if the move is valid and False otherwise.
def is_valid_move(grid, row, col, num):
## Function code goes here
pass
Solve Sudoku Grid (Continued)
Inside the solve_sudoku function, use the helper functions find_empty_cell and is_valid_move to implement the backtracking algorithm. If a solution is found, return True. If the current number leads to an invalid solution, backtrack by setting the current cell to 0.
## Try filling the empty cell with numbers from 1 to 9
for num in range(1, 10):
if is_valid_move(grid, row, col, num):
grid[row][col] = num
if solve_sudoku(grid):
return True
## If the current number leads to an invalid solution, backtrack
grid[row][col] = 0
return False
Remove Numbers Based on Difficulty
Inside the generate_sudoku function, remove numbers from the grid based on the specified difficulty level. The difficulty levels are: 1 (easy), 2 (medium), 3 (hard). The number of numbers to remove is calculated as num_to_remove = 45 + 10 * difficulty.
## Remove numbers based on difficulty level
num_to_remove = 45 + 10 * difficulty
for _ in range(num_to_remove):
row = random.randint(0, 8)
col = random.randint(0, 8)
grid[row][col] = 0
return grid
Draw Grid
Create a function draw_grid(grid, selected_cell) that draws the Sudoku grid on the game window. This function should be responsible for drawing cells, numbers, and highlighting the selected cell.
def draw_grid(grid, selected_cell):
## Function code goes here
pass
Fill Cells and Draw Numbers
Inside the draw_grid function, loop through the grid and draw the cells and numbers using the Pygame functions pygame.draw.rect and screen.blit.
## Draw the cells
for row in range(9):
for col in range(9):
cell_rect = pygame.Rect(
col * CELL_SIZE, row * CELL_SIZE, CELL_SIZE, CELL_SIZE
)
pygame.draw.rect(screen, GRAY, cell_rect)
## Draw the numbers
if grid[row][col] != 0:
number = font_small.render(str(grid[row][col]), True, BLACK)
text_rect = number.get_rect(
center=(
col * CELL_SIZE + CELL_SIZE // 2,
row * CELL_SIZE + CELL_SIZE // 2,
)
)
screen.blit(number, text_rect)
Highlight Selected Cell
Inside the draw_grid function, highlight the selected cell if it is not None using the pygame.draw.rect function.
## Highlight the selected cell
if (row, col) == selected_cell:
pygame.draw.rect(screen, LIGHT_BLUE, cell_rect, 3)
Draw Grid Lines
Inside the draw_grid function, draw the lines to create the grid using the pygame.draw.line function.
## Draw the lines
for i in range(10):
if i % 3 == 0:
thickness = 4
else:
thickness = 1
pygame.draw.line(
screen,
BLACK,
(0, i * CELL_SIZE),
(WINDOW_SIZE[0], i * CELL_SIZE),
thickness,
)
pygame.draw.line(
screen,
BLACK,
(i * CELL_SIZE, 0),
(i * CELL_SIZE, WINDOW_SIZE[1]),
thickness,
)
Update the Display
Inside the draw_grid function, update the display using the pygame.display.update function.
pygame.display.update()
Check If Grid Is Full
Create a function is_grid_full(grid) that checks if the Sudoku grid is fully filled. This can be done by looping through the grid and checking if any cell contains 0.
def is_grid_full(grid):
## Function code goes here
pass
Get Cell Under Mouse
Create a function get_cell_under_mouse(pos) that returns the cell coordinates under the mouse position. This can be calculated by dividing the mouse position by the cell size.
def get_cell_under_mouse(pos):
## Function code goes here
pass
Select Difficulty
Create a function select_difficulty() that allows the player to select the difficulty level before starting the game. This function should display the difficulty options and return the selected difficulty level.
def select_difficulty():
## Function code goes here
pass
Implement Difficulty Selection
Inside the select_difficulty function, implement the difficulty selection logic using the Pygame functions pygame.mouse.get_pos and pygame.event.get.
## Display difficulty selection text
title_text = font_large.render("Select Difficulty", True, BLACK)
screen.blit(title_text, (110, 200))
## Display difficulty options
option_y = 300
for difficulty, label in difficulties.items():
option_text = font_small.render(f"{difficulty}. {label}", True, BLACK)
text_rect = option_text.get_rect(center=(WINDOW_SIZE[0] // 2, option_y))
screen.blit(option_text, text_rect)
option_y += 70
pygame.display.update()
## Wait for difficulty selection
difficulty_selected = False
difficulty = 1
while not difficulty_selected:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: ## Left mouse button
pos = pygame.mouse.get_pos()
if 180 <= pos[0] <= 380:
if 300 <= pos[1] <= 350:
difficulty = 1
difficulty_selected = True
elif 370 <= pos[1] <= 420:
difficulty = 2
difficulty_selected = True
elif 440 <= pos[1] <= 490:
difficulty = 3
difficulty_selected = True
return difficulty
Main Game Loop
Create the main game loop by defining a function main().
def main():
## Function code goes here
pass
Select Difficulty and Generate Grid
Inside the main function, select the difficulty level by calling select_difficulty() and return if the difficulty is None. Then, generate a Sudoku grid using the selected difficulty level by calling generate_sudoku(difficulty).
## Select the difficulty level
difficulty = select_difficulty()
if difficulty is None:
return
## Generate a new Sudoku grid
grid = generate_sudoku(difficulty)
Game Loop Variables
Inside the main function, create game loop variables selected_cell and running. Set selected_cell to None and running to True.
selected_cell = None
running = True
Enable Key Repeat
Inside the main function, enable key repeat using the pygame.key.set_repeat function. We set the delay and interval to 100 milliseconds to make it easier for players to fill cells.
## Enable key repeat
pygame.key.set_repeat(100, 100)
Handle Events
Inside the game loop, handle events using the pygame.event.get function. Check for quit events, mouse button events, and key events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
## Handle mouse button events
pass
elif event.type == pygame.KEYDOWN:
## Handle key events
pass
Handle Mouse Button Events
Inside the event handling for mouse button events, check if the left mouse button is clicked or the right mouse button is clicked.
if event.button == 1: ## Left mouse button
## Handle left mouse button click
pass
elif event.button == 3: ## Right mouse button
## Handle right mouse button click
pass
Handle Left Mouse Button Click
Inside the left mouse button click handling, get the cell coordinates under the mouse position by calling get_cell_under_mouse(pos).
## Get the cell under the mouse position
pos = pygame.mouse.get_pos()
row, col = get_cell_under_mouse(pos)
Handle Left Mouse Button Click (Continued)
Inside the left mouse button click handling, select the cell if it's empty by checking if grid[row][col] == 0.
## Select the cell if it's empty
if grid[row][col] == 0:
selected_cell = (row, col)
Handle Right Mouse Button Click
Inside the right mouse button click handling, clear the selected cell if it's not None and it's empty by checking if grid[selected_cell[0]][selected_cell[1]] == 0.
if selected_cell:
## Clear the selected cell if it's empty
if grid[selected_cell[0]][selected_cell[1]] == 0:
grid[selected_cell[0]][selected_cell[1]] = 0
Handle Key Events
Inside the event handling for key events, check which number key is pressed and set the value of the selected cell accordingly.
if selected_cell:
## Get the key pressed
if event.key == pygame.K_1:
grid[selected_cell[0]][selected_cell[1]] = 1
elif event.key == pygame.K_2:
grid[selected_cell[0]][selected_cell[1]] = 2
elif event.key == pygame.K_3:
grid[selected_cell[0]][selected_cell[1]] = 3
elif event.key == pygame.K_4:
grid[selected_cell[0]][selected_cell[1]] = 4
elif event.key == pygame.K_5:
grid[selected_cell[0]][selected_cell[1]] = 5
elif event.key == pygame.K_6:
grid[selected_cell[0]][selected_cell[1]] = 6
elif event.key == pygame.K_7:
grid[selected_cell[0]][selected_cell[1]] = 7
elif event.key == pygame.K_8:
grid[selected_cell[0]][selected_cell[1]] = 8
elif event.key == pygame.K_9:
grid[selected_cell[0]][selected_cell[1]] = 9
Draw Grid and Check Completion
Inside the game loop, draw the grid by calling draw_grid(grid, selected_cell). Then, check if the grid is complete by calling is_grid_full(grid).
## Draw the grid
draw_grid(grid, selected_cell)
## Check if the grid is complete
if is_grid_full(grid):
print("Congratulations! You solved the Sudoku puzzle.")
Update Display and Quit
After the game loop, update the display using the pygame.display.update function and quit the game using pygame.quit().
## Update the display
pygame.display.update()
## Quit the game
pygame.quit()
Run the Game
Finally, add a condition to check if the current file is the main entry point of the program. If it is, run the game by calling the main() function.
if __name__ == "__main__":
main()
Run the game using the python command.
python sudoku_game.py

Summary
Congratulations! You have successfully created a Sudoku game using Python and the Pygame library. The game allows players to select a difficulty level, fill in the empty cells with numbers, and checks if the grid is complete. Have fun playing and solving Sudoku puzzles!



