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_GridforSudokuGrid. docs.python.org/2/reference/datamodel.html#special-method-names \$\endgroup\$