I am doing a Sudoku puzzle solver for homework, but am encountering some difficulty. The code right now cycles past the solution, although it does reach it for easy puzzles, and for harder puzzles, it gets stuck with several 9’s for no apparent reason. I would appreciate any help on this. (check_cell determines if the placement is valid or not.)
- Is backtracking implemented correctly in this code, and if not, how would that be fixed?
- How can I stop the solver from freezing? It solves around 3 rows and then freezes, changing most of the values to 9s.
Some code:
def solve_helper(self, row, col):
# Try placing a number in each column of current row
board = self.the_board
if board[row][col] != 0:
?????
elif board[row][col] == 0:
for i in range(1,10):
print("Setting value at i with ") + str (i) + (" located at " ) + str(row) + str(col)
self.set_cell(row, col, i)
self.guesses = self.guesses + 1
if self.check_cell(row, col):
if self.solve_helper(row, col): return True
else:
self.set_cell(row, col, 0)
else:
return self.mover(row,col)
return False
def mover(self, row, col):
if col + 1 != 9:
return self.solve_helper(row, (col+1))
elif row + 1 != 9:
print "Moving to row" + str(row + 1)
return self.solve_helper((row+1),0)
else:
print "SOLUTION FOUND"
return True
The trouble you’re having is that some of your recursive calls are not returning the results properly, so your solution, when it is found, gets forgotten about a few levels up the recursive stack. Here’s the first fix that you need, adding
returnto the recursive calls made inmover:You also need something similar in the special case of your
solve_helperfunction where you skip over pre-solved cells. The end of the function should be:Edit:
Ok, I’ve found a few more issues in the code. Two of them are logic issues with the solver, and one is a display issue that doesn’t cause any real problems other than looking strange during the solving.
The issues:
solve_helpercalling itself, rather than callingmover. This makes it take an extra function call before moving (though I think it may not actually break the solver).solve_helpersets a cell to 9, but then in backtracked to (after some later cells couldn’t be solved), the 9 doesn’t get reset to zero before backtracking further.The first issue is easy to fix. Just change the
solve_helpercall to amovercall instead. That’s actually what you had in the original code you put in the question. Callingsolve_helperdirectly doesn’t actually get the wrong result (sincesolve_helperwill skip the already filled out cell the second time), but it adds an unnecessary extra function call to each level of your recursion.The second issue is a little more complicated, and this is where you are getting stuck on some boards. What you need to do is move the line that does
self.set_cell(row, col, 0)out of theelseblock it is currently in. In fact, you can actually move it outside of the loop entirely, if you want (since it only really is necessary if you’re backtracking after none of the values for the current cell worked). Here is what I think this is the best arrangement of the for loop (also moving thereturn Falsestatement up):Finally, fixing the display issue requires two changes. First, get rid of the conditional in
set_cell. You want to update the display always. Next, inupdate_textfield, move thedeletecall outside of theifblock so that it always happens (leave theinsertunder theif). This makes it so that setting a cell to zero will erase the previous value, but not make it display an actual 0 character (it will show nothing).I think this should do it. Note that the algorithm you’re using is still pretty slow. Solving a board I found on the internet in a quick Google search took 122482 guesses and more than 5 minutes, but it did finally work. Other boards (especially those that require 8s or 9s in the first few open spaces) may take even longer.