# Create a Sudoku Solver Using Python

Hello coder, welcome to the codewithrandom blog. In this blog, we will Create a Sudoku Solver Using Python. We will be using the backtracking algorithm to solve the puzzle. The backtracking algorithm is a brute-force algorithm that works by trying out all possible solutions and backtracking when it reaches a dead end.

## What is Sudoku Solver ?🤔🤔

Sudoku is a logic-based puzzle game that involves filling a 9×9 grid with numbers from 1 to 9, with each row, column, and 3×3 subgrid containing each number only once. The game starts with a partially filled grid, and the objective is to fill the remaining empty cells with the correct numbers.

Now, let’s get started on building our Sudoku Solver using Python.

### step-by-step guide on how to build a Sudoku Solver using Python:

step 1: open any python code Editor.

step 2: Required Modules.

For this Sudoku Solver project, we need to install Tkinter  Gui. You can install these packages in your terminal.

`\$ pip install tk`

step 3: Copy the code for Sudoku Solver using Python, which I provided Below in this article, and save it in a file named “main.py” (or any other name you prefer).

step 4: Run this  python file main.py to start the Sudoku Solver.

#### Complete Source Code For the Sudoku Solver (copy the code and run )👇👇👇

```import tkinter as tk
from tkinter import font
# from time import sleep
import random

count = 0

class Sudoku:
#Canvas background
canvas_bg = "#fafafa" #impure white
#Grid lines
line_normal = "#4f4f4f" #dark grey
line_thick = "#000000" #pure black
#cell highlight box
hbox_green = "#15fa00" #light green
hbox_red = "#d61111" #red

def __init__(self, master):
#A record of all cells and their attributes
self.grid = {}
#A small edit window which will be initilized and displayed on click
self.e = None
self.canvas_width = 300
self.canvas_height = 300
#The sudoku grid
self.canvas = tk.Canvas(master,bg=self.canvas_bg, width=self.canvas_width, height=self.canvas_height)
self.t = tk.Entry(self.canvas)
self.t.bind("<KeyRelease>",self.keyPressed)
self.canvas.grid(columnspan=3)
self.canvas.bind("<Button 1>",self.click)
#Solve button
self.btn_solve = tk.Button(master,text='Solve', command=self.wrapper, width=8)
#Generate button
self.btn_gen = tk.Button(master,text='Generate', command=self.Generate, width=8)
#Difficulty selector
self.set_difficulty = tk.IntVar(master,1)
#Individual cell width and height
self.cell_width = self.canvas_width/9
self.cell_height = self.canvas_height/9
#Draw vertical lines
for x in range(1,9):
width=1
fill=self.line_normal
if(x%3==0):
#Draw thicker black lines for seperating 3x3 boxes
width=2
fill=self.line_thick
else:
#Draw normal thin dark-grey lines
width=1
fill=self.line_normal
self.canvas.create_line(self.cell_width*x, 0, self.cell_width*x, self.canvas_height, width=width, fill=fill)
#Draw horizontal lines in the same way
for y in range(1,9):
width=1
fill=self.line_normal
if(y%3==0):
width=2
fill=self.line_thick
else:
width=1
fill=self.line_normal
self.canvas.create_line(0, self.cell_height*y, self.canvas_width, self.cell_height*y, width=width, fill=fill)

def click(self, eventorigin):
x = eventorigin.x
y = eventorigin.y
#Calcilate top-left x,y coords of cell clicked by mouse
rect_x = int(x/self.cell_width)*self.cell_width
rect_y = int(y/self.cell_height)*self.cell_height
#Coords for drawing a square to highlight clicked cell
coords = [rect_x,rect_y,rect_x+self.cell_width,rect_y,rect_x+self.cell_width,rect_y+self.cell_height,rect_x,rect_y+self.cell_height]
# For some stupid reason, this line below didn't work as expected. So I had to choose the hard way.
# h_box = self.canvas.create_rectangle(rect_x, rect_y, self.cell_width, self.cell_height, outline="#15fa00", width=3)
#Get cell info
editable = self.getCell(x/self.cell_width,y/self.cell_height)[1]
if editable:
#It's a cell you can edit
#Show a green box highlight and edit
h_box = self.canvas.create_polygon(coords, outline=self.hbox_green, fill='', width=3)
self.edit(rect_x, rect_y)
else:
#It's a cell containing a clue number, cannot edit
#Show a red box highlight
h_box = self.canvas.create_polygon(coords, outline=self.hbox_red, fill='', width=3)
self.canvas.after(200,lambda : self.canvas.delete(h_box))

def edit(self,cordx:int,cordy:int):
#Create a entry inside a small canvas window
#make sure it's actuall initilized before deleting it
if self.e is None:
#Not initilized, else block skipped
pass
else:
#Canvas window initilized, delete and reset it to current position
self.canvas.delete(self.e)
#Create a mini edit window that just fits the cell
self.e = self.canvas.create_window(cordx+1,cordy+1,window=self.t,width=self.cell_width-1,height=self.cell_height-2,anchor=tk.NW)
#Clean up
self.t.delete(0,tk.END)
self.t.focus_set()

def keyPressed(self, event):
val = self.t.get().strip()
try:
#If input is a number between 1-9, this won't raise any errors
val = int(val)
if(val>9 or val<0):
raise ValueError
except ValueError:
print("Invalid input!")
self.t.delete(0,tk.END)
else:
#Get x,y coords of edit window and calculate cell row,column values
x,y = (self.t.winfo_x())/self.cell_width,(self.t.winfo_y())/self.cell_height
#Update cell with new value
self.updateCell(val,x,y)
self.canvas.delete(self.e)

def updateCell(self,value,x,y,editable=True):
#Get cell information stored in dict self.grid
t = self.getCell(x,y)
#Update values
t[0] = value
t[1] = editable
text=value
if value==0:
text=' '
#Update display value by using item id
self.canvas.itemconfigure(t[2],text=text)
self.canvas.update()
#Update the dict
self.grid[(x,y)] = t

def getCell(self, x:int, y:int):
#Returns info of cell at 'x' row 'y' column
x=int(x)
y=int(y)
val = self.grid[(x,y)]
return val

def populate(self, X:[[]]):
#Populates the sudoku grid with given 9x9 matrix and also store it in a dict
c = self.canvas
#The bookeeping is managed as shown below
'''Dict->(X,Y) : [value,True/Flase,id]
^        ^        ^     ^
X,Y coords  value  editable  object id'''
for i in range(9):
for j in range(9):
#Calculate x,y position of center of cell
text_x = j*self.cell_width+self.cell_width/2
text_y = i*self.cell_height+self.cell_height/2
val = X[i][j]
if val == 0:
t = c.create_text(text_x,text_y,text=' ',font=('Times', 14))
self.grid[(j,i)] = [ val, True, t]
else:
t = c.create_text(text_x,text_y,text=val,font=('Times', 15, 'bold'))
self.grid[(j,i)] = [ val, False, t]

def clearGrid(self):
#Utility function to clear the grid, this will also wipe out the puzzle from memory
for i in range(9):
for j in range(9):
self.updateCell(0,i,j)

def getValue(self, row:int, col:int):
#Return value at row, column
return self.grid[(row,col)][0]

def printGrid(self):
#Utility function to print the grid
for i in range(9):
x=[]
for j in range(9):
x.append(self.getValue(j,i))
print(x)

def wrapper(self):
#A small wrapper funtion that performs some small tasks before solving
global count
#Reset the count
count = 0
#Delete edit boxes if any
self.canvas.delete(self.e)
#Lock the buttons and start solving
self.btn_gen.configure(state='disabled')
self.btn_solve.configure(state='disabled')
self.solve()
#After solving, set the buttons back to normal
self.btn_gen.configure(state='normal')
self.btn_solve.configure(state='normal')

def solve(self):
global count
#Start by finding an empty cell
x,y = self.findEmpty()
#If no cells are empty, our job is done
if (x,y)==(None,None):
#Print the no. of times solve() was called
print("Recursed", count, "times.")
return True

#Keep a track of the number of function calls
count+=1

#Try putting in numbers from 1-9
for i in range(1,10):
#Check if number will satisfy sub-grid rule and row-column rule
if self.is_SubGrid_Safe(i,x,y) and self.is_Cell_Safe(i,x,y):
#Yes, then update the cell
self.updateCell(i,x,y,False)
# self.canvas.after(10,self.updateCell(i,x,y))
#Now repeat for remaining cells
nxt = self.solve()
if nxt:
#All went well, so return true
return True
else:
#The value chose earlier is wrong, so backtrack
self.updateCell(0,x,y,True)

#Cannot find any number, so return false (backtrack)
return False

def Generate(self, level=1):
#Disable the buttons
self.btn_solve.configure(state='disabled')
self.btn_gen.configure(state='disabled')
#Generate random puzzle with difficulty level 'level'
#Start by generate diagonal sub-grids with randomly shuffled nos from 1-9
nos = list(range(1,10))
rand_grid = []
for i in range(9):
if i%3==0:
random.shuffle(nos)
t=[0]*9
for j in range(3):
t_pos = int(i/3)*3+j
n_pos = (i%3)*3
t[t_pos] = nos[n_pos+j]
rand_grid.append(t)
#Clean up
self.clearGrid()
#Cover up the window with a label
cover_label = tk.Label(text="GENERATING",font=('Arial',16))
cover = self.canvas.create_window(0,0,window=cover_label,width=self.canvas_width,height=self.canvas_height,anchor=tk.NW)
#Populate with the diagonal sub-grids
self.populate(rand_grid)
#Solve to get the completed puzzle
self.solve()
global count
#Reset count
count = 0
#Remove random numbers based on set difficulty level, this needs work. Any Math wizards around?
level = self.set_difficulty.get()
if level<=2: level+=2
for i in range(9):
for j in range(9):
remove = level>random.randint(1,5)
if remove:
self.updateCell(0,i,j)

#Set the fonts right
g=self.grid
for i in g.keys():
cell = g[i]
if cell[1]:
#Editable cells have Times-14-regular font
self.canvas.itemconfigure(cell[2],font=('Times',14))
else:
#Non-editable cells have Times-15-bold font
self.canvas.itemconfigure(cell[2],font=('Times',15,'bold'))
#Finally, lift the cover for the user to see the puzzle
self.canvas.delete(cover)
#and set the buttons back to normal
self.btn_solve.configure(state='normal')
self.btn_gen.configure(state='normal')

def findEmpty(self):
#Utility function to find an empty cell
for i in range(9):
for j in range(9):
cell_val = self.getCell(j,i)[1]
if cell_val:
return (j,i)
return (None,None)

def is_SubGrid_Safe(self,val,x,y)->bool:
#Checks if the sub-grid rule is satisfied for the number 'val' at given row 'x',column 'y'
#Figure out the sub grid x,y
sgrid_x = int(x/3)*3
sgrid_y = int(y/3)*3
#Search the sub-grid
for i in range(sgrid_x,sgrid_x+3):
for j in range(sgrid_y,sgrid_y+3):
#Check only non-editable cells, ignore cells edited by user
if val==self.getValue(i,j) and not self.getCell(i,j)[1]:
#This number already exists, rule violated
return False
#No duplicate number found in sub-grid, rule intact
return True

def is_Cell_Safe(self,val,x,y)->bool:
#Check if the number 'val' already exists in the row 'x' or column 'y'
for i in range(9):
#Check only non-editable cells, ignore cells edited by user
if val==self.getValue(x,i) and not self.getCell(x,i)[1]:
return False
if val==self.getValue(i,y) and not self.getCell(i,y)[1]:
return False
#Row-column rule intact
return True

####################################Sudoku Solver using Python
master = tk.Tk()
master.title("PyDoku")
master.resizable(False, False)
game=Sudoku(master)
ex1= [
[3, 0, 6, 5, 0, 8, 4, 0, 0],
[5, 2, 0, 0, 0, 0, 0, 0, 0],
[0, 8, 7, 0, 0, 0, 0, 3, 1],
[0, 0, 3, 0, 1, 0, 0, 8, 0],
[9, 0, 0, 8, 6, 3, 0, 0, 5],
[0, 5, 0, 0, 9, 0, 6, 0, 0],
[1, 3, 0, 0, 0, 0, 2, 5, 0],
[0, 0, 0, 0, 0, 0, 0, 7, 4],
[0, 0, 5, 2, 0, 6, 3, 0, 0]
]
#Here's an extreme puzzel, ref : https://www.sudokuwiki.org/Daily_Sudoku
ex2=[
[0, 5, 0, 0, 0, 0, 0, 0, 0],
[3, 0, 8, 0, 7, 0, 2, 0, 0],
[0, 0, 9, 3, 0, 6, 8, 0, 0],
[0, 8, 0, 0, 0, 9, 5, 0, 0],
[9, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 3, 8, 0, 0, 0, 9, 0],
[0, 0, 6, 5, 0, 7, 3, 0, 0],
[0, 0, 1, 0, 4, 0, 6, 0, 7],
[0, 0, 0, 0, 0, 0, 0, 4, 0]
]

game.populate(ex1)
tk.mainloop()```

Output for the Sudoku Solver 👇👇

conclusion

Hurray! You have successfully Build a Sudoku Solver using Python. creating a Sudoku Solver using Python is a great way to challenge your programming skills and create a useful tool for solving Sudoku puzzles. With this tutorial, you should now have a good understanding of how to build a basic Sudoku Solver using Python. You can further customize and improve the solver by adding additional features such as error checking, difficulty levels, and user interfaces.  Hope you enjoyed building Sudoku Solver using Python! Visit our homepage and you get lot’s of projects💝