How to Solve Sudoku with Python



Photo by John Morgan on Unsplash

Sudoku puzzles can range from criminally easy to extremely difficult. With python however, we can solve (most) sudoku puzzles within seconds. In this article, we’ll develop an algorithm to solve a sudoku puzzle.

Representation

For this problem we’ll need two main components. Firstly, a way to represent our crossword puzzle. Ideally, we’ll need a data structure with a quick access and modification time. We’ll use a 2d array for this project. Let’s get this setup in python:

grid = [
    [0,0,0, 0,0,0, 0,0,0],
    [0,0,0, 0,0,0, 0,0,0],
    [0,0,0, 0,0,0, 0,0,0],

    [0,0,0, 0,0,0, 0,0,0],
    [0,0,0, 0,0,0, 0,0,0],
    [0,0,0, 0,0,0, 0,0,0],
    
    [0,0,0, 0,0,0, 0,0,0],
    [0,0,0, 0,0,0, 0,0,0],
    [0,0,0, 0,0,0, 0,0,0],
]

This will initialise our empty grid, with zeros in every square. That’s our representation pretty much sorted, so we can move onto actually solving a given crossword puzzle.

Utility functions

We’re going to create some utility functions which will greatly help our solution down the line. Each of these will perform checks for the standard rules of sudoku.

Lets start by checking whether a number is in a given row. This is very simple, as we have each row stored within our grid array.

def inRow(num, row):
    return num in grid[row]

Now, we can check columns. This will be slightly more tricky, but still fairly straightforward. We just need to check the same column in each row, and return True if we find it.

def inColumn(num, column):
    for item in range(9):
        if grid[item][column] == num:
            return True
    return False

Finally, we need to check whether a number exists already within a given square. We first calculate the square to check, and then check all locations within that square. Add import math to the top of your python file, and add the below function.

def inSquare(num, row, column):
    # Get correct square
    startRow = 3 * math.floor(row / 3)
    startCol = 3 * math.floor(column / 3)

    # Check every value in square
    for i in range(startRow, startRow + 3):
        for j in range (startCol, startCol + 3):
            if grid[i][j] == num:
                return True
    return False

Now these are done, we need a function to determine whether we can place a number in a certain position. We will create a possible function to do this.

def possible(num, row, column):
    return not(inRow(num, row) or inColumn(num, column) or inSquare(num, row, column))

We check to see whether the number is already in the given row or column, and return True if it is not present.

With these three utility functions, it is fairly straightforward to solve the sudoku.

Solving the puzzle

We will now create our recursive solve function to complete the sudoku. We iterate through each square in the grid, checking whether we can place any number between 1 and 9 in the location. If there are no possible numbers to place in this location, we backtrack and try a different number.

def solve():
    for i in range(9):
        for j in range(9):
            if grid[i][j] == 0:
                for num in range(1, 10):
                    if possible(num, i, j):
                        grid[i][j] = num
                        solve()
                        grid[i][j] = 0
                return
    for item in grid:
        print(item)
    exit()

solve()

When we have found a solution, we output the solution and exit the program. Without this, the program would find all possible solutions, which could take some time…

With that, our program is complete! We have successfully written a program to solve any sudoku puzzle. Feel free to expand this algorithm, and build a fleshed out Sudoku Solver.

Full code

For the full code, check Github or see below:

import math

#Example sudoku 
grid = [
   [0,0,6, 0,4,0, 0,9,7],
   [0,4,0, 7,3,0, 0,1,0],
   [0,1,7, 0,9,2, 0,3,0],

   [6,0,0, 0,7,0, 0,8,0],
   [1,0,5, 0,6,0, 9,0,3],
   [0,2,0, 0,1,0, 0,0,6],

   [0,5,0, 9,8,0, 1,6,0],
   [0,9,0, 0,5,6, 0,7,0],
   [8,6,0, 0,2,0, 3,0,0],
]

# Check whether the number is already in the current row
def inRow(num, row):
    return num in grid[row]

# Check whether the number is already in the current column
def inColumn(num, column):
    for item in range(9):
        if grid[item][column] == num:
            return True
    return False

def inSquare(num, row, column):
    # Get correct square
    startRow = 3 * math.floor(row / 3)
    startCol = 3 * math.floor(column / 3)

    # Check every value in square
    for i in range(startRow, startRow + 3):
        for j in range (startCol, startCol + 3):
            if grid[i][j] == num:
                return True
    return False

# Check whether a number is possible
def possible(num, row, column):
    return not(inRow(num, row) or inColumn(num, column) or inSquare(num, row, column))


# code to actually solve the puzzle
def solve():
    for i in range(9):
        for j in range(9):
            if grid[i][j] == 0:
                for num in range(1, 10):
                    if possible(num, i, j):
                        grid[i][j] = num
                        solve()
                        grid[i][j] = 0
                return
    for item in grid:
        print(item)
    
    exit()
    
    

solve()