In an effort to teach myself some python and programming in general I made a Sudoku Solver (as many others have done before me from what I can tell). The one thing that bugs me a little is my use of deepcopy which I think I can avoid by implementing __eq__ in my classes.
My questions:
- Is this understanable?
- How Pythonic is this?
- Are there any glaring bugs / convention mistakes / missuses of structures?
- How can I introduce unit testing?
Full code (400 lines) can be found here.
class SudokuCell:
"""Most Basic Sudoku Cell
-Contains 9 Possible values, initialized at true
-Can return the possible cases
"""
def __init__(self):
self.values = {1: True,2:True,3:True,4:True,5:True,6:True,7:True,8:True,9:True}
def check_cell(self):
"""return all values still possible"""
out = []
for k,d in self.values.items():
if d==True:
out.append(k)
return out
def set_cell(self, val):
"""set all values to False except val"""
for k,d in self.values.items():
if k != val:
self.values[k] = False
self.values[val]=True
def reset_cell(self):
self.values = {1: True,2:True,3:True,4:True,5:True,6:True,7:True,8:True,9:True}
class SudokuGrid:
def __init__(self):
self.Cells = []
for x in range(0,81):
Cell = SudokuCell()
self.Cells.append(Cell)
self.groups = self.__define_groups__()
def __eq__(self, other):
return self.Cells == other.Cells
def print_Grid(self):
"""print Sudoku Grid"""
values=self.__str_fill__()
print '-------------------'
for x in range (0,3):
print '|'+values[x*9] +' '+ values[x*9+1] +' ' + values [x*9+2] + '|'+values[x*9+3] +' '+ values[x*9+4] +' ' + values [x*9+5] + '|'+values[x*9+6] +' '+ values[x*9+7] +' ' + values [x*9+8] + '|'
print '*-----*-----*-----*'
for x in range (3,6):
print '|'+values[x*9] +' '+ values[x*9+1] +' ' + values [x*9+2] + '|'+values[x*9+3] +' '+ values[x*9+4] +' ' + values [x*9+5] + '|'+values[x*9+6] +' '+ values[x*9+7] +' ' + values [x*9+8] + '|'
print '*-----*-----*-----*'
for x in range (6,9):
print '|'+values[x*9] +' '+ values[x*9+1] +' ' + values [x*9+2] + '|'+values[x*9+3] +' '+ values[x*9+4] +' ' + values [x*9+5] + '|'+values[x*9+6] +' '+ values[x*9+7] +' ' + values [x*9+8] + '|'
print '-------------------'
def check_gridsolved(self):
for x in self.Cells:
if len(x.check_cell())!=1:
return False
return True
def __define_groups__(self):
"""we need to know how the grid is formed"""
groups=[]
#rows
for x in range(0,9):
groups.append([x*9,x*9+1,x*9+2,x*9+3,x*9+4,x*9+5,x*9+6,x*9+7,x*9+8])
#collumns
for x in range(0,9):
groups.append([x,x+9,x+18,x+27,x+36,x+45,x+54,x+63,x+72])
#squares 1
for x in range(0,3):
groups.append([x*3,x*3+1,x*3+2,x*3+9,x*3+10,x*3+11,x*3+18,x*3+19,x*3+20])
#squares 2
for x in range(9,12):
groups.append([x*3,x*3+1,x*3+2,x*3+9,x*3+10,x*3+11,x*3+18,x*3+19,x*3+20])
#squares 3
for x in range(18,21):
groups.append([x*3,x*3+1,x*3+2,x*3+9,x*3+10,x*3+11,x*3+18,x*3+19,x*3+20])
return groups
def __str_fill__(self):
"""get Row for Print"""
out =[]
i=0
for x in self.Cells:
out.append('*')
if len(x.check_cell())==1:
out[i]=str(x.check_cell()[0])
i+=1
return out
class Sudoku_Solver():
'''This class is an iterative non-exhaustive search algorithm for the SudokuGrid'''
def __init__(self):
self.Branch = collections.OrderedDict() #{} #stores the Branch info
self.inputGrid = SudokuGrid()
self.workingGrid = SudokuGrid()
self.solved = False
self.unsolvable = False
self.solutionGrids = []
def load(self,SudokuGrid):
self.inputGrid = copy.deepcopy(SudokuGrid)
self.workingGrid = copy.deepcopy(SudokuGrid)
def simple_solver(self):
iteration = 1
laststate = SudokuGrid()
while ((laststate == self.workingGrid)==False): #
laststate = copy.deepcopy(self.workingGrid) #want to get rid of deepcopy...
iteration += 1
if 0==self.__slv_iterate_truncation__():
#print 'er'
return 0 #cannot solve
if 0==self.__slv_iterate_singlefind__():
#print 'bad eval'
return 0 #cannot solve
if iteration > 30:
print 'ERROR too many iterations'
break
return iteration
def solve(self):
iteration = 0
simple_iteration = self.simple_solver()
if (simple_iteration)==0:
self.unsolvable = True
if (self.workingGrid.check_gridsolved()==False): #start branching
self.workingGrid.print_Grid()
print len(Solver.Branch)
self.Branch.update(self.__slv_find_firstchoice__()) #seed
self.__slv_iterate_over_branch()
self.solutionGrids.append(self.workingGrid)
self.workingGrid.print_Grid()
'''MORE CODE'''
Functionality: basic block is a SudokuCell
which can be a value from 1 to 9. A SudokuGrid
contains 81 cells as the traditional puzzle. Groups signify the rules of the game (i.e Groups in which a number can only occur once). I have a simple solver which iterates through the groups and checks for inconsistencies (values in a cell that shouldn't be elsewhere) and single values (only one cell has this). After that it is just continuing guesswork. The solver works on all the sudokus I have tried (maybe 5).
I also wanted to learn about unit testing unfortunately had so many implementation questions and too little time to really consider it, but I am thinking of introducing that next.
object
. i.e.class SudokuGrid:
should beclass SudokuGrid(object):
\$\endgroup\$__str__
instead ofprint_Grid
forSudokuGrid
. docs.python.org/2/reference/datamodel.html#special-method-names \$\endgroup\$