From 4400453b60c75a14f8c3dc9e77c1b7d16ac88306 Mon Sep 17 00:00:00 2001 From: Dawson Friesenhahn Date: Sat, 23 Nov 2024 23:46:33 -0700 Subject: [PATCH 1/2] Updated Sudoku constructor to actually make use of difficulty argument --- sudoku/sudoku.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sudoku/sudoku.py b/sudoku/sudoku.py index a761979..6e2ebf6 100644 --- a/sudoku/sudoku.py +++ b/sudoku/sudoku.py @@ -227,6 +227,10 @@ def __init__(self, width = 3, height = None, board = None, difficulty = None, se shuffle(positions) self.board = [[(i + 1) if i == positions[j] else Sudoku._empty_cell_value for i in range(self.size)] for j in range(self.size)] + + new_Sudoku = self.difficulty(difficulty) + self.board = new_Sudoku.board + self.difficulty = new_Sudoku.difficulty def solve(self, assert_solvable = False): # type: (bool) -> Sudoku From 3ae695e2ce39f360b2ab6028e9b7a8d2ab95409a Mon Sep 17 00:00:00 2001 From: Dawson Friesenhahn Date: Tue, 26 Nov 2024 17:17:22 -0700 Subject: [PATCH 2/2] Updated implementation of validate() method so that it can't return True for an unsolvable puzzle. Added __constraints_satisfied() method to replace old validate() functionality --- sudoku/sudoku.py | 78 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/sudoku/sudoku.py b/sudoku/sudoku.py index 6e2ebf6..8a3ea24 100644 --- a/sudoku/sudoku.py +++ b/sudoku/sudoku.py @@ -240,8 +240,9 @@ def solve(self, assert_solvable = False): :param assert_solvable: Boolean for if you wish to raise an UnsolvableSodoku error when the board is invalid. Defaults to `false`. :raises UnsolvableSudoku: """ - solution = _SudokuSolver(self)._solve() if self.validate() else None - if solution: + solution = _SudokuSolver(self)._solve() if self.__constraints_satisfied() else None + solution_valid= solution.get_difficulty() !=-2 if solution else False + if solution_valid: return solution elif assert_solvable: raise UnsolvableSudoku('No solution found') @@ -264,30 +265,10 @@ def has_multiple_solutions(self): def validate(self): # type: () -> bool - row_numbers = [[False for _ in range(self.size)] - for _ in range(self.size)] - col_numbers = [[False for _ in range(self.size)] - for _ in range(self.size)] - box_numbers = [[False for _ in range(self.size)] - for _ in range(self.size)] - - for row in range(self.size): - for col in range(self.size): - cell = self.board[row][col] - box = (row // self.height) * self.height + (col // self.width) - if cell == Sudoku._empty_cell_value: - continue - elif isinstance(cell, int): - if row_numbers[row][cell - 1]: - return False - elif col_numbers[col][cell - 1]: - return False - elif box_numbers[box][cell - 1]: - return False - row_numbers[row][cell - 1] = True - col_numbers[col][cell - 1] = True - box_numbers[box][cell - 1] = True - return True + ''' + Returns true if this Sudoku board can be solved + ''' + return self.solve().get_difficulty() != -2 @ staticmethod def _copy_board(board): @@ -353,6 +334,51 @@ def show_full(self): Prints the puzzle to the terminal, with more information """ print(self.__str__()) + + def __constraints_satisfied(self) -> bool: + # type: () -> bool + """ + Returns true if there are no duplicated numbers in any row, column, or box. Note that this does not necessarily mean that the puzzle can be solved! For example, this puzzle will return true for _constraints_satisfied(), but no solution exists for this puzzle because there is nowhere for a 9 in the upper-left box. + + +-------+-------+-------+ + | 8 | 6 7 3 | 4 9 | + | 7 | | 8 3 | + | 3 | 9 8 | 5 7 | + +-------+-------+-------+ + | 2 | 7 | 9 3 8 | + | 8 9 | 3 | 7 2 | + | 7 3 | 8 2 9 | 4 1 | + +-------+-------+-------+ + | 8 | 6 7 | 3 9 2 | + | 3 7 5 | 9 2 | 8 6 | + | 9 2 6 | 3 8 | 5 7 | + +-------+-------+-------+ + """ + + row_numbers = [[False for _ in range(self.size)] + for _ in range(self.size)] + col_numbers = [[False for _ in range(self.size)] + for _ in range(self.size)] + box_numbers = [[False for _ in range(self.size)] + for _ in range(self.size)] + + for row in range(self.size): + for col in range(self.size): + cell = self.board[row][col] + box = (row // self.height) * self.height + (col // self.width) + if cell == Sudoku._empty_cell_value: + continue + elif isinstance(cell, int): + if row_numbers[row][cell - 1]: + return False + elif col_numbers[col][cell - 1]: + return False + elif box_numbers[box][cell - 1]: + return False + row_numbers[row][cell - 1] = True + col_numbers[col][cell - 1] = True + box_numbers[box][cell - 1] = True + return True def __format_board_ascii(self): # type: () -> str