Skip to content
Advertisement

Sudoku backtracking solver bug

Ok, I’ve been scratching my head over this for a few hours now..

My goal was to code a sudoku solver that uses the backtracking method and to show the progress of the algorithm using pygame. For this I have to keep track of the events, I did it by appending them to a list named registre as is shown in the code:

def solver(self):
    self.t1 = time.time()
    if self.t1-self.t0 > self.solve_time_max:
        sys.exit(1)
    for i in range(self.grid.shape[0]):
        for j in range(self.grid.shape[1]):
            if self.grid[i][j]==0:
                for n in range(1,10):        
                    self.registre.append([n,i,j])
                    if self.verify(n,i,j):
                        self.grid[i][j]=n
                        
                        self.solver()
                        if 0 not in self.grid:
                            break
                    self.registre.append([0,i,j])
                    self.grid[i][j]=0
                    
                return self.grid

I actually succeeded and everything goes fine for most of the runs. But sometimes, for some reason I couldn’t identify, this happens :

    print(une_grille.grid0)
    print(une_grille.grid)
    print(une_grille.registre[:20])
   [[0 8 0 7 0 0 0 0 2]
    [5 0 0 0 0 9 0 0 0]
    [0 0 0 0 0 0 0 0 0]
    [0 0 0 0 0 0 0 0 0]
    [0 1 0 0 6 0 0 0 0]
    [4 0 0 9 0 0 0 0 0]
    [0 0 9 0 8 0 0 0 4]
    [2 0 0 0 0 0 0 8 0]
    [0 0 0 0 0 0 0 0 0]]
   [[1 8 3 7 4 5 6 9 2]
    [5 2 4 6 1 9 3 7 8]
    [6 9 7 2 3 8 1 4 5]
    [3 5 2 8 7 1 4 6 9]
    [9 1 8 3 6 4 2 5 7]
    [4 7 6 9 5 2 8 1 3]
    [7 3 9 1 8 6 5 2 4]
    [2 4 1 5 9 3 7 8 6]
    [8 6 5 4 2 7 9 3 1]]
    [[1, 0, 0], [1, 0, 1], [0, 0, 1], [2, 0, 1], [0, 0, 1], [3, 0, 1], [1, 0, 
    2], [0, 0, 2], [2, 0, 2], [0, 0, 2], [3, 0, 2], [0, 0, 2], [4, 0, 2], [0, 
    0, 2], [5, 0, 2], [1, 0, 3], [0, 0, 3], [2, 0, 3], [0, 0, 3], [3, 0, 3]]

What is printed is simply the initialized grid, the solved grid and the first 20 events in self.registre. For this run the displaying on pygame didn’t work, some numbers overlap themselves and others are left blank. I am almost sure it’s not a displaying problem since the displaying function uses the list registre and it works just fine for most of the other runs. Also I don’t understand these events.

Complete script :

import numpy as np
import random as rd
import time
import sys

class Grid():

"""
une Docstring

"""
def __init__(self, nval=15, dim=(9,9), tries_max=1000, init_time_max=5e-3, solve_time_max = 1):
    self.nval = nval+1
    self.dim = dim 
    self.t0 = 0
    self.t1 = 0
    self.tries_max = tries_max
    self.k = 0
    self.init_time_max = init_time_max
    self.solve_time_max = solve_time_max
    self.registre = []
    
    self.grid =  self.create_grid()   
    self.smthg = 0
    
    
def create_grid(self):
    for tries in range(self.tries_max):
        self.k = 0
        if tries == self.tries_max -1:
            print(f"Tried {self.tries_max} times, I have failed")
            sys.exit(1)
        self.grid0 = np.zeros([self.dim[0],self.dim[1]], dtype=int)
        try:
            self.grid0 = self.initialize_board()
        except SystemExit:
            print(f"TRY #{tries}: Spent too much time initializing board. Re-trying.")
            continue
        self.grid = np.copy(self.grid0)
        try:
            self.t0 = time.time()
            self.grid = self.solver()
            if 0 not in self.grid:
                print(f"Found grid with solution after n = {tries+1} tries!")
                return self.grid
            else:
                print(f"TRY #{tries} converged to null solution")
                continue
        except SystemExit:
            print(f"TRY #{tries} too much time spent trying to solve current grid, continuing")
            continue
    print("Maximum tries reached")
    
def initialize_board(self):
    for i in range(self.nval):
        rx = rd.randint(0, self.grid0.shape[0]-1)
        ry = rd.randint(0, self.grid0.shape[1]-1)
        cx = int(rx/3)
        cy = int(ry/3)
        time0 = time.time()
        while(self.grid0[rx][ry]==0):
            if time.time()-time0 > self.init_time_max:
                sys.exit(1)
            r = rd.randint(1, 9)
            if((r in self.grid0[rx,:]) or (r in self.grid0[:,ry]) or (r in self.grid0[3*cx:3*cx+3,3*cy:3*cy+3])):
                continue
            else:
                self.grid0[rx][ry] = r
    return self.grid0
        





def solver(self):
    self.t1 = time.time()
    if self.t1-self.t0 > self.solve_time_max:
        sys.exit(1)
    for i in range(self.grid.shape[0]):
        for j in range(self.grid.shape[1]):
            if self.grid[i][j]==0:
                for n in range(1,10):        
                    self.registre.append([n,i,j])
                    if self.verify(n,i,j):
                        self.grid[i][j]=n
                        
                        self.solver()
                        if 0 not in self.grid:
                            break
                    self.registre.append([0,i,j])
                    self.grid[i][j]=0
                    
                return self.grid
    
def verify(self, number, x, y):
    cx = int(x/3)
    cy = int(y/3)
    if((number in self.grid[x,:]) or (number in self.grid[:,y]) or (number in self.grid[3*cx:3*cx+3,3*cy:3*cy+3])):
        return False
    return True
    

game = Grid(nval = 35)
print(game.grid)
print(game.grid0)
print(game.registre[:20])

Another instance of the issue :

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

I would really appreciate it if you could help me with this.

Advertisement

Answer

After you added the code that repeats the generation of grids, it becomes clear that you don’t create a new instance of Grid, but mutate the existing one. In that process you should then take care to reset the state completely. You only do this partly, e.g. by resetting t0. But you don’t reset registre, and so when you print the first 20 items, you are actually looking at the log of the first attempt, not the successful one.

Advertisement