Skip to content

Commit 676c674

Browse files
committed
add sudoku game
1 parent 0e625fa commit 676c674

File tree

9 files changed

+498
-0
lines changed

9 files changed

+498
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ This is a repository of all the tutorials of [The Python Code](https://www.thepy
321321
- [How to Make a Flappy Bird Game in Python](https://thepythoncode.com/article/make-a-flappy-bird-game-python). ([code](gui-programming/flappy-bird-game))
322322
- [How to Create a Pong Game in Python](https://thepythoncode.com/article/build-a-pong-game-in-python). ([code](gui-programming/pong-game))
323323
- [How to Create a Space Invaders Game in Python](https://thepythoncode.com/article/make-a-space-invader-game-in-python). ([code](gui-programming/space-invaders-game))
324+
- [How to Build a Sudoku Game with Python](https://thepythoncode.com/article/make-a-sudoku-game-in-python). ([code](gui-programming/sudoku-game))
324325

325326

326327
For any feedback, please consider pulling requests.

gui-programming/sudoku-game/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# [How to Build a Sudoku Game with Python](https://thepythoncode.com/article/make-a-sudoku-game-in-python)

gui-programming/sudoku-game/cell.py

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import pygame
2+
from settings import convert_list
3+
4+
pygame.font.init()
5+
6+
class Cell:
7+
def __init__(self, row, col, cell_size, value, is_correct_guess = None):
8+
self.row = row
9+
self.col = col
10+
self.cell_size = cell_size
11+
self.width = self.cell_size[0]
12+
self.height = self.cell_size[1]
13+
self.abs_x = row * self.width
14+
self.abs_y = col * self.height
15+
16+
self.value = value
17+
self.is_correct_guess = is_correct_guess
18+
self.guesses = None if self.value != 0 else [0 for x in range(9)]
19+
20+
self.color = pygame.Color("white")
21+
self.font = pygame.font.SysFont('monospace', self.cell_size[0])
22+
self.g_font = pygame.font.SysFont('monospace', (cell_size[0] // 3))
23+
24+
self.rect = pygame.Rect(self.abs_x,self.abs_y,self.width,self.height)
25+
26+
27+
def update(self, screen, SRN = None):
28+
pygame.draw.rect(screen, self.color, self.rect)
29+
30+
if self.value != 0:
31+
font_color = pygame.Color("black") if self.is_correct_guess else pygame.Color("red")
32+
num_val = self.font.render(str(self.value), True, font_color)
33+
screen.blit(num_val, (self.abs_x, self.abs_y))
34+
35+
elif self.value == 0 and self.guesses != None:
36+
cv_list = convert_list(self.guesses, [SRN, SRN, SRN])
37+
for y in range(SRN):
38+
for x in range(SRN):
39+
num_txt = " "
40+
if cv_list[y][x] != 0:
41+
num_txt = cv_list[y][x]
42+
num_txt = self.g_font.render(str(num_txt), True, pygame.Color("orange"))
43+
abs_x = (self.abs_x + ((self.width // SRN) * x))
44+
abs_y = (self.abs_y + ((self.height // SRN) * y))
45+
abs_pos = (abs_x, abs_y)
46+
screen.blit(num_txt, abs_pos)

gui-programming/sudoku-game/clock.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import pygame, time
2+
from settings import CELL_SIZE
3+
4+
pygame.font.init()
5+
6+
class Clock:
7+
def __init__(self):
8+
self.start_time = None
9+
self.elapsed_time = 0
10+
self.font = pygame.font.SysFont("monospace", CELL_SIZE[0])
11+
self.message_color = pygame.Color("black")
12+
13+
# Start the timer
14+
def start_timer(self):
15+
self.start_time = time.time()
16+
17+
# Update the timer
18+
def update_timer(self):
19+
if self.start_time is not None:
20+
self.elapsed_time = time.time() - self.start_time
21+
22+
# Display the timer
23+
def display_timer(self):
24+
secs = int(self.elapsed_time % 60)
25+
mins = int(self.elapsed_time / 60)
26+
my_time = self.font.render(f"{mins:02}:{secs:02}", True, self.message_color)
27+
return my_time
28+
29+
# Stop the timer
30+
def stop_timer(self):
31+
self.start_time = None

gui-programming/sudoku-game/main.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import pygame, sys
2+
from settings import WIDTH, HEIGHT, CELL_SIZE
3+
from table import Table
4+
5+
pygame.init()
6+
7+
screen = pygame.display.set_mode((WIDTH, HEIGHT + (CELL_SIZE[1] * 3)))
8+
pygame.display.set_caption("Sudoku")
9+
10+
pygame.font.init()
11+
12+
class Main:
13+
def __init__(self, screen):
14+
self.screen = screen
15+
self.FPS = pygame.time.Clock()
16+
self.lives_font = pygame.font.SysFont("monospace", CELL_SIZE[0] // 2)
17+
self.message_font = pygame.font.SysFont('Bauhaus 93', (CELL_SIZE[0]))
18+
self.color = pygame.Color("darkgreen")
19+
20+
def main(self):
21+
table = Table(self.screen)
22+
23+
while True:
24+
self.screen.fill("gray")
25+
for event in pygame.event.get():
26+
if event.type == pygame.QUIT:
27+
pygame.quit()
28+
sys.exit()
29+
if event.type == pygame.MOUSEBUTTONDOWN:
30+
if not table.game_over:
31+
table.handle_mouse_click(event.pos)
32+
33+
# lower screen display
34+
if not table.game_over:
35+
my_lives = self.lives_font.render(f"Lives Left: {table.lives}", True, pygame.Color("black"))
36+
self.screen.blit(my_lives, ((WIDTH // table.SRN) - (CELL_SIZE[0] // 2), HEIGHT + (CELL_SIZE[1] * 2.2)))
37+
38+
else:
39+
if table.lives <= 0:
40+
message = self.message_font.render("GAME OVER!!", True, pygame.Color("red"))
41+
self.screen.blit(message, (CELL_SIZE[0] + (CELL_SIZE[0] // 2), HEIGHT + (CELL_SIZE[1] * 2)))
42+
elif table.lives > 0:
43+
message = self.message_font.render("You Made It!!!", True, self.color)
44+
self.screen.blit(message, (CELL_SIZE[0] , HEIGHT + (CELL_SIZE[1] * 2)))
45+
46+
table.update()
47+
pygame.display.flip()
48+
self.FPS.tick(30)
49+
50+
51+
if __name__ == "__main__":
52+
play = Main(screen)
53+
play.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pygame
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from itertools import islice
2+
3+
4+
WIDTH, HEIGHT = 450, 450
5+
6+
N_CELLS = 9
7+
8+
CELL_SIZE = (WIDTH // N_CELLS, HEIGHT // N_CELLS)
9+
10+
# Convert 1D list to 2D list
11+
def convert_list(lst, var_lst):
12+
it = iter(lst)
13+
return [list(islice(it, i)) for i in var_lst]

gui-programming/sudoku-game/sudoku.py

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import random
2+
import math
3+
import copy
4+
5+
class Sudoku:
6+
def __init__(self, N, E):
7+
self.N = N
8+
self.E = E
9+
10+
# compute square root of N
11+
self.SRN = int(math.sqrt(N))
12+
self.table = [[0 for x in range(N)] for y in range(N)]
13+
self.answerable_table = None
14+
15+
self._generate_table()
16+
17+
def _generate_table(self):
18+
# fill the subgroups diagonally table/matrices
19+
self.fill_diagonal()
20+
21+
# fill remaining empty subgroups
22+
self.fill_remaining(0, self.SRN)
23+
24+
# Remove random Key digits to make game
25+
self.remove_digits()
26+
27+
def fill_diagonal(self):
28+
for x in range(0, self.N, self.SRN):
29+
self.fill_cell(x, x)
30+
31+
def not_in_subgroup(self, rowstart, colstart, num):
32+
for x in range(self.SRN):
33+
for y in range(self.SRN):
34+
if self.table[rowstart + x][colstart + y] == num:
35+
return False
36+
return True
37+
38+
def fill_cell(self, row, col):
39+
num = 0
40+
for x in range(self.SRN):
41+
for y in range(self.SRN):
42+
while True:
43+
num = self.random_generator(self.N)
44+
if self.not_in_subgroup(row, col, num):
45+
break
46+
self.table[row + x][col + y] = num
47+
48+
def random_generator(self, num):
49+
return math.floor(random.random() * num + 1)
50+
51+
def safe_position(self, row, col, num):
52+
return (self.not_in_row(row, num) and self.not_in_col(col, num) and self.not_in_subgroup(row - row % self.SRN, col - col % self.SRN, num))
53+
54+
def not_in_row(self, row, num):
55+
for col in range(self.N):
56+
if self.table[row][col] == num:
57+
return False
58+
return True
59+
60+
def not_in_col(self, col, num):
61+
for row in range(self.N):
62+
if self.table[row][col] == num:
63+
return False
64+
return True
65+
66+
67+
def fill_remaining(self, row, col):
68+
# check if we have reached the end of the matrix
69+
if row == self.N - 1 and col == self.N:
70+
return True
71+
72+
# move to the next row if we have reached the end of the current row
73+
if col == self.N:
74+
row += 1
75+
col = 0
76+
77+
# skip cells that are already filled
78+
if self.table[row][col] != 0:
79+
return self.fill_remaining(row, col + 1)
80+
81+
# try filling the current cell with a valid value
82+
for num in range(1, self.N + 1):
83+
if self.safe_position(row, col, num):
84+
self.table[row][col] = num
85+
if self.fill_remaining(row, col + 1):
86+
return True
87+
self.table[row][col] = 0
88+
89+
# no valid value was found, so backtrack
90+
return False
91+
92+
def remove_digits(self):
93+
count = self.E
94+
95+
# replicates the table so we can have a filled and pre-filled copy
96+
self.answerable_table = copy.deepcopy(self.table)
97+
98+
# removing random numbers to create the puzzle sheet
99+
while (count != 0):
100+
row = self.random_generator(self.N) - 1
101+
col = self.random_generator(self.N) - 1
102+
if (self.answerable_table[row][col] != 0):
103+
count -= 1
104+
self.answerable_table[row][col] = 0
105+
106+
107+
def puzzle_table(self):
108+
return self.answerable_table
109+
110+
def puzzle_answers(self):
111+
return self.table
112+
113+
114+
def printSudoku(self):
115+
for row in range(self.N):
116+
for col in range(self.N):
117+
print(self.table[row][col], end=" ")
118+
print()
119+
120+
print("")
121+
122+
for row in range(self.N):
123+
for col in range(self.N):
124+
print(self.answerable_table[row][col], end=" ")
125+
print()
126+
127+
128+
if __name__ == "__main__":
129+
N = 9
130+
E = (N * N) // 2
131+
sudoku = Sudoku(N, E)
132+
sudoku.printSudoku()

0 commit comments

Comments
 (0)