def steps(sudoku: Sudoku) -> Iterator[Step]: _sudoku = Sudoku(*sudoku.cells(), box_size=sudoku.box_size) all_techniques: Tuple[Type[Technique], ...] = ( techniques.LoneSingle, techniques.HiddenSingle, techniques.NakedPair, techniques.NakedTriplet, techniques.LockedCandidate, techniques.XYWing, techniques.UniqueRectangle, ) for step in techniques.BulkPencilMarking(_sudoku): _sudoku.update(step.changes) yield step while not _sudoku.is_solved(): for technique in all_techniques: try: step = technique(_sudoku).first() except techniques.NotFound: continue else: _sudoku.update(step.changes) yield step break else: raise exceptions.Unsolvable
def _get_correct_cells(self) -> List[Cell]: sudoku = Sudoku(*[c for c in self.sudoku.cells() if c.value], box_size=self.sudoku.box_size) all_techniques = ( BulkPencilMarking, techniques.NakedPair, techniques.NakedTriplet, techniques.LockedCandidate, techniques.XYWing, techniques.UniqueRectangle, ) for technique in all_techniques: for result in technique(sudoku): sudoku.update(result.changes) return [cell for cell in sudoku.cells() if cell.candidates]
def eliminate(sudoku: Sudoku) -> Sudoku: _sudoku = Sudoku(*sudoku.cells(), box_size=sudoku.box_size) all_techniques = ( techniques.LoneSingle, techniques.HiddenSingle, ) for step in techniques.BulkPencilMarking(_sudoku): _sudoku.update(step.changes) has_result = True while has_result: for technique in all_techniques: has_result = False for step in technique(_sudoku): _sudoku.update(step.changes) has_result = True return _sudoku
def rank(sudoku: Sudoku) -> int: total_solutions = 0 total_branch_factor = 0 def count(sudoku: Sudoku) -> None: nonlocal total_solutions nonlocal total_branch_factor _sudoku = solvers.eliminate(sudoku) cells = sorted( (cell for cell in _sudoku.cells() if not cell.value), key=operator.attrgetter("candidates"), ) for cell in cells: branch_factor = len(cell.candidates) for candidate in cell.candidates: _sudoku.update([Cell(position=cell.position, value=candidate)]) try: count(_sudoku) except (exceptions.InvalidSudoku, exceptions.NoCandidates): pass else: total_solutions += 1 if total_solutions > 1: raise exceptions.MultipleSolutions total_branch_factor += pow(branch_factor - 1, 2) _sudoku.update([cell]) else: raise exceptions.NoCandidates try: count(sudoku) except exceptions.NoCandidates: pass return (total_branch_factor * 100) + sum(1 for c in sudoku.cells() if not c.value)