I started using my own method of solving Sudoku puzzles. I keep a list of every unused number in each row, column, and box. I then loop over my open list of unsolved squares looking for a square that either only has 1 possible number that could be placed there or a row, column, or box that only has one square that could hold a specific number. When I find a solution to another square I remove the number I just found from the row, column, and box that the square is a part of.
Okay, that all works. That's the majority of my solve() method. At the very end of that method if the the board is unsolved I call depthFirst(). This should go through the remaining unsolved squares using the backtracking technique.
This is the board I am using for testing:
400000805
030000000
000700000
020000060
000080400
000010000
000603070
500200000
104000000
I compared my code against this solution and I can't see too many differences. Algorithm for solving Sudoku
Does anybody have any idea why the backtracking portion of my code isn't working?
Thanks!
import copy
# Only removes the element from the list if it exists in the list
def safeRemove(l, e):
if l.count(e) > 0:
l.remove(e)
# Represents the board and keeps track of everything needed for solving the puzzle
class Sudoku:
# 3x3 array of 3x3 arrays to represent the board
# board is filled with BoardPos objects that keep track of the square's value, row, column, and box the square is a part of
board = [[[[None for i in range(3)] for j in range(3)] for k in range(3)] for l in range(3)]
# Keeps track of all numbers that are not currently taken in each row, column, and box
rows = [[i for i in range(1, 10)] for j in range(9)]
cols = [[i for i in range(1, 10)] for j in range(9)]
boxes = [[i for i in range(1, 10)] for j in range(9)]
# Unsolved squares
openList = list()
# Keeps track of each square's current value (0 for unsolved) and which
# row, column, and box the square is a part of
class BoardPos:
def __init__(self, w, x, y, z):
self.a = w
self.r = x
self.c = y
self.b = z
# Use an algorith similar to DFS to solve the remainder of the puzzle
def depthFirst(self):
if len(self.openList) == 0:
self.board = boardState.board
return True
curSquare = self.openList[0]
possibilities = self.getPossibilities(curSquare)
# Save current state to return to
tempBoard = copy.deepcopy(self.board)
tempRows = copy.deepcopy(self.rows)
tempCols = copy.deepcopy(self.cols)
tempBoxes = copy.deepcopy(self.boxes)
tempOpenList = copy.deepcopy(self.openList)
for e in possibilities:
self.found(curSquare, e)
if self.depthFirst():
return True
# Restore to state of the last partial, possibile solution
self.board = tempBoard
self.rows = tempRows
self.cols = tempCols
self.boxes = tempBoxes
self.openList = tempOpenList
return False
# Set the square the value found and remove that value from the square's
# row, column, and box and remove the square from the open list
def found(self, index, el):
i, j, k, l = index
self.board[i][j][k][l].a = el
self.removeFound(index, el)
del self.openList[0]
# Remove the value from the square's row, column, and box
def removeFound(self, index, el):
i, j, k, l = index
safeRemove(self.rows[self.board[i][j][k][l].r], el)
safeRemove(self.cols[self.board[i][j][k][l].c], el)
safeRemove(self.boxes[self.board[i][j][k][l].b], el)
# Returns a list of all possible values for the square
def getPossibilities(self, index):
i, j, k, l = index
tempRow = self.rows[self.board[i][j][k][l].r]
tempCol = self.cols[self.board[i][j][k][l].c]
tempBox = self.boxes[self.board[i][j][k][l].b]
return list(set(tempRow) & set(tempCol) & set(tempBox))
# Returns the box of the square
def box(self, i, j, k, l):
return i * 3 + j
# Returns the row of the square
def row(self, i, j, k, l):
return i * 3 + k
# Returns the column of the square
def col(self, i, j, k, l):
return j * 3 + l
def main():
# Removed constructor to reduce code. Constructor just initialized the board with values from board.txt
puzzle = Sudoku("board.txt")
puzzle.depthFirst()
puzzle.printBoard()
if __name__ == "__main__":
main()