How to create Sudoku Pygame using Python3

Hello The Codezine Mates, welcome back to our Pygame Series, today we are going to build another game using pygame. This day I am going to show you how you can create a Python Sudoku game. Interesting, isn’t it?
What is Sudoku Game: It is a puzzle-based strategy game in which players place the numbers 1 to 9 into a grid-shaped box consisting of 9 squares. It is divided into 9 smaller squares in such a way that every number shows once in each line. Simply speaking, it is is a logic-based, multi number-placement puzzle game.
Requirements for the Game:
- A fast processing & high graphics PC with a Python environment installed. Typically, You can choose Mac Book, Asus Zenbook, Dell Inspiron, or any Pc with a high-level processor.
- A Code Ide or Code Editor like an atom or vs code.
- A notebook with a pen for writing important points.
- Your concentration.
Working:
Our GUI game is coded to allow a particular player to solve within a defined time. If a player can solve it within the given then a player wins & an alert pops out and the opposite happens if the player loses. It also grants a user to solve the puzzle automatically if he is unable to solve it.
Steps to build a Sudoku Game:
- Firstly, make a folder named any in your laptop & drag it to your code ide.
- Secondly, open your command prompt(CMD) & install the Pygame package by typing Pip install Pygame command.[It will permit your system to built pygame based game]
- Thirdly, make a file called main.py.
- Now, Set your codes which are given under the section to your respective files.
- Lastly, run your Pygame by typing Python main.py in your Command. That’s it you have your game.
Code
# main.py
import pygame
import time
pygame.font.init()
#Defining Board Size
class Grid:
board = [
[7, 8, 0, 4, 0, 0, 1, 2, 0],
[6, 0, 0, 0, 7, 5, 0, 0, 9],
[0, 0, 0, 6, 0, 1, 0, 7, 8],
[0, 0, 7, 0, 4, 0, 2, 6, 0],
[0, 0, 1, 0, 5, 0, 9, 3, 0],
[9, 0, 4, 0, 6, 0, 0, 0, 5],
[0, 7, 0, 3, 0, 0, 0, 1, 2],
[1, 2, 0, 0, 0, 7, 4, 0, 0],
[0, 4, 9, 2, 0, 6, 0, 0, 7]
]
#defining titles
def __init__(self, rows, cols, width, height, win):
self.rows = rows
self.cols = cols
self.cubes = [[Cube(self.board[i][j], i, j, width, height) for j in range(cols)] for i in range(rows)]
self.width = width
self.height = height
self.model = None
self.update_model()
self.selected = None
self.win = win
def update_model(self):
self.model = [[self.cubes[i][j].value for j in range(self.cols)] for i in range(self.rows)]
def place(self, val):
row, col = self.selected
if self.cubes.value == 0:
self.cubes.set(val)
self.update_model()
if valid(self.model, val, (row,col)) and self.solve():
return True
else:
self.cubes.set(0)
self.cubes.set_temp(0)
self.update_model()
return False
def sketch(self, val):
row, col = self.selected
self.cubes.set_temp(val)
def draw(self):
# Drawing Grid Lines
gap = self.width / 9
for i in range(self.rows+1):
if i % 3 == 0 and i != 0:
thick = 4
else:
thick = 1
pygame.draw.line(self.win, (0,0,0), (0, i*gap), (self.width, i*gap), thick)
pygame.draw.line(self.win, (0, 0, 0), (i * gap, 0), (i * gap, self.height), thick)
# Drawing Cubes
for i in range(self.rows):
for j in range(self.cols):
self.cubes[i][j].draw(self.win)
def select(self, row, col):
# Reset all other
for i in range(self.rows):
for j in range(self.cols):
self.cubes[i][j].selected = False
self.cubes.selected = True
self.selected = (row, col)
def clear(self):
row, col = self.selected
if self.cubes.value == 0:
self.cubes.set_temp(0)
def click(self, pos):
"""
:param: pos
:return: (row, col)
"""
if pos[0] < self.width and pos[1] < self.height:
gap = self.width / 9
x = pos[0] // gap
y = pos[1] // gap
return (int(y),int(x))
else:
return None
def is_finished(self):
for i in range(self.rows):
for j in range(self.cols):
if self.cubes[i][j].value == 0:
return False
return True
def solve(self):
find = find_empty(self.model)
if not find:
return True
else:
row, col = find
for i in range(1, 10):
if valid(self.model, i, (row, col)):
self.model = i
if self.solve():
return True
self.model = 0
return False
def solve_gui(self):
find = find_empty(self.model)
if not find:
return True
else:
row, col = find
for i in range(1, 10):
if valid(self.model, i, (row, col)):
self.model = i
self.cubes.set(i)
self.cubes.draw_change(self.win, True)
self.update_model()
pygame.display.update()
pygame.time.delay(100)
if self.solve_gui():
return True
self.model = 0
self.cubes.set(0)
self.update_model()
self.cubes.draw_change(self.win, False)
pygame.display.update()
pygame.time.delay(100)
return False
class Cube:
rows = 9
cols = 9
def __init__(self, value, row, col, width, height):
self.value = value
self.temp = 0
self.row = row
self.col = col
self.width = width
self.height = height
self.selected = False
def draw(self, win):
fnt = pygame.font.SysFont("comicsans", 40)
gap = self.width / 9
x = self.col * gap
y = self.row * gap
if self.temp != 0 and self.value == 0:
text = fnt.render(str(self.temp), 1, (128,128,128))
win.blit(text, (x+5, y+5))
elif not(self.value == 0):
text = fnt.render(str(self.value), 1, (0, 0, 0))
win.blit(text, (x + (gap/2 - text.get_width()/2), y + (gap/2 - text.get_height()/2)))
if self.selected:
pygame.draw.rect(win, (255,0,0), (x,y, gap ,gap), 3)
def draw_change(self, win, g=True):
fnt = pygame.font.SysFont("comicsans", 40)
gap = self.width / 9
x = self.col * gap
y = self.row * gap
pygame.draw.rect(win, (255, 255, 255), (x, y, gap, gap), 0)
text = fnt.render(str(self.value), 1, (0, 0, 0))
win.blit(text, (x + (gap / 2 - text.get_width() / 2), y + (gap / 2 - text.get_height() / 2)))
if g:
pygame.draw.rect(win, (0, 255, 0), (x, y, gap, gap), 3)
else:
pygame.draw.rect(win, (255, 0, 0), (x, y, gap, gap), 3)
def set(self, val):
self.value = val
def set_temp(self, val):
self.temp = val
def find_empty(bo):
for i in range(len(bo)):
for j in range(len(bo[0])):
if bo[i][j] == 0:
return (i, j) # row, col
return None
def valid(bo, num, pos):
# Check row
for i in range(len(bo[0])):
if bo[pos[0]][i] == num and pos[1] != i:
return False
# Check column
for i in range(len(bo)):
if bo[i][pos[1]] == num and pos[0] != i:
return False
# Check box
box_x = pos[1] // 3
box_y = pos[0] // 3
for i in range(box_y*3, box_y*3 + 3):
for j in range(box_x * 3, box_x*3 + 3):
if bo[i][j] == num and (i,j) != pos:
return False
return True
def redraw_window(win, board, time, strikes):
win.fill((255,255,255))
# Draw time
fnt = pygame.font.SysFont("comicsans", 40)
text = fnt.render("Time: " + format_time(time), 1, (0,0,0))
win.blit(text, (540 - 160, 560))
# Draw Strikes
text = fnt.render("X " * strikes, 1, (255, 0, 0))
win.blit(text, (20, 560))
# Draw grid and board
board.draw()
def format_time(secs):
sec = secs%60
minute = secs//60
hour = minute//60
mat = " " + str(minute) + ":" + str(sec)
return mat
#Title Portion
def main():
win = pygame.display.set_mode((540,600))
pygame.display.set_caption("Sudoku Pygame (Press Space to do auto solve) ")
board = Grid(9, 9, 540, 540, win)
key = None
run = True
start = time.time()
strikes = 0
while run:
play_time = round(time.time() - start)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_1:
key = 1
if event.key == pygame.K_2:
key = 2
if event.key == pygame.K_3:
key = 3
if event.key == pygame.K_4:
key = 4
if event.key == pygame.K_5:
key = 5
if event.key == pygame.K_6:
key = 6
if event.key == pygame.K_7:
key = 7
if event.key == pygame.K_8:
key = 8
if event.key == pygame.K_9:
key = 9
if event.key == pygame.K_DELETE:
board.clear()
key = None
if event.key == pygame.K_SPACE:
board.solve_gui()
# Click Event
if event.key == pygame.K_RETURN:
i, j = board.selected
if board.cubes[i][j].temp != 0:
if board.place(board.cubes[i][j].temp):
print("Success")
else:
print("Wrong")
strikes += 1
key = None
if board.is_finished():
print("Game over")
if event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
clicked = board.click(pos)
if clicked:
board.select(clicked[0], clicked[1])
key = None
if board.selected and key != None:
board.sketch(key)
redraw_window(win, board, play_time, strikes)
pygame.display.update()
main()
pygame.quit()
Conclusion
Though, It is a simple beginner-friendly Game that is easy for any beginner to built & understand it is important to follow the steps carefully. So, that you don’t get any error. Lastly, if you find useful please share it. Thank You.
Thank you so much for writing about Python. Great Article.
Thanks for the code but I got some errors when I run it. You sometimes forgot the indices [row][col] or [i][j] after cubes. But I still get an error after on line 305: i, j = board.selected
TypeError: cannot unpack non-iterable NoneType object
If I enter a number it is not centered but on the left, top corner. Is that intended?
You could use the package py-sudoku to generate random puzzles.
Nice code for the solver: I didn’t thought that’s so easy with a recursive function!!