def solveProblem(self, problem: Sudoku): # Fill in all the possible tiles with educated predictions. while True: if self.applyNakedSingle(problem): continue if self.applyNakedTuple(problem): continue break # Check if we find the solution after applying Sudoku. if self.goalTest(problem): return problem # Save updated state of the problem before making guesses with candidates. beforeUpdateProblem = copy.deepcopy(problem) # Backtrack from populated sudoku problem tile = self.findMostConstrainedTile(problem) for index in range(len(tile.candidates)): if not problem.isConflicting(tile.candidates[index], rowIndex=tile.rowIndex, colIndex=tile.colIndex): problem.assignValue(value=tile.candidates[index], row=tile.rowIndex, col=tile.colIndex) updatedProblem = self.solveProblem(problem) if self.goalTest(updatedProblem): return updatedProblem else: problem = beforeUpdateProblem tile = problem.state[tile.rowIndex][tile.colIndex] # No candidates, Hit dead end return None