Sudoku Solver Using Python

Create a Sudoku Solver Using Python

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.

Sudoku Solver

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 “” (or any other name you prefer).

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

Create a Sudoku Solver Using Python

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.canvas.bind("<Button 1>",
        #Solve button
        self.btn_solve = tk.Button(master,text='Solve', command=self.wrapper, width=8)
        self.btn_solve.grid(row=1, padx=5, pady=5)
        #Generate button
        self.btn_gen = tk.Button(master,text='Generate', command=self.Generate, width=8)
        self.btn_gen.grid(row=1, column=1, padx=5, pady=5, sticky=tk.E)
        #Difficulty selector
        self.set_difficulty = tk.IntVar(master,1)
        self.difficulty_selector = tk.OptionMenu(master,self.set_difficulty,1,2,3,4,5)
        self.difficulty_selector.grid(row=1, column=2, pady=5, sticky=tk.W)
        #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):
                #Draw thicker black lines for seperating 3x3 boxes
                #Draw normal thin dark-grey lines
            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):
            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)
            #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
            #Canvas window initilized, delete and reset it to current position
        #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
    def keyPressed(self, event):
        val = self.t.get().strip()
            #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!")
            #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

    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
        if value==0:
            text=' '
        #Update display value by using item id
        #Update the dict
        self.grid[(x,y)] = t

    def getCell(self, x:int, y:int):
        #Returns info of cell at 'x' row 'y' column
        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]
                    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):

    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):
            for j in range(9):

    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
        #Lock the buttons and start solving
        #After solving, set the buttons back to 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
        #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.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
                    #The value chose earlier is wrong, so backtrack
        #Cannot find any number, so return false (backtrack)
        return False
    def Generate(self, level=1):
        #Disable the buttons
        #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:
            for j in range(3):
                t_pos = int(i/3)*3+j
                n_pos = (i%3)*3
                t[t_pos] = nos[n_pos+j]
        #Clean up
        #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
        #Solve to get the completed puzzle
        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:
        #Set the fonts right
        for i in g.keys():
            cell = g[i]
            if cell[1]:
                #Editable cells have Times-14-regular font
                #Non-editable cells have Times-15-bold font
        #Finally, lift the cover for the user to see the puzzle
        #and set the buttons back to 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.resizable(False, False)
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 :
    [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]


Output for the Sudoku Solver 👇👇



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💝

#Sudoku Solver using Python


Leave a Reply