def _random_initial_cells(box_size: BoxSize) -> List[Cell]: size = box_size.width * box_size.length all_values = set(range(1, size + 1)) values = random.sample(all_values, k=size) box_values = [ values[i * box_size.length:i * box_size.length + box_size.length] for i in range(box_size.width) ] while True: # pragma: no branch row_values = random.sample(all_values - set(box_values[0]), k=box_size.length) used_values = [sorted(box_values[i]) for i in range(1, box_size.width)] if sorted(row_values) not in used_values: break row_values += random.sample(all_values.difference(box_values[0], row_values), k=box_size.length) return [ Cell( position=Position(row=i, column=j, box=box_size.sequential(i, j)), value=box_values[i][j], ) for i in range(box_size.width) for j in range(box_size.length) ] + [ Cell( position=Position(row=0, column=i, box=box_size.sequential(0, i)), value=value, ) for i, value in enumerate(row_values, start=box_size.length) ]
def test_locked_candidate_not_found(): sudoku = make_sudoku_with_marks( [ [2, 0, 0, 5, 9, 3, 1, 0, 0], [5, 0, 1, 0, 0, 2, 3, 0, 0], [3, 9, 7, 6, 4, 1, 8, 2, 5], [6, 0, 4, 0, 3, 8, 9, 0, 0], [8, 1, 0, 0, 0, 0, 0, 3, 6], [7, 3, 9, 0, 0, 6, 0, 0, 8], [1, 7, 0, 3, 0, 4, 6, 0, 0], [9, 0, 0, 0, 1, 5, 7, 4, 3], [4, 0, 3, 0, 6, 0, 0, 0, 1], ], box_size=BoxSize(3, 3), ) sudoku.update([ Cell(position=Position(1, 1, 0), candidates={4, 6}), Cell(position=Position(1, 7, 2), candidates={6, 9}), Cell(position=Position(1, 8, 2), candidates={4, 9}), Cell(position=Position(3, 3, 4), candidates={1, 2}), Cell(position=Position(8, 1, 6), candidates={2, 5, 8}), Cell(position=Position(8, 3, 7), candidates={7, 9}), Cell(position=Position(8, 5, 7), candidates={7, 9}), Cell(position=Position(8, 6, 8), candidates={2, 5}), Cell(position=Position(8, 7, 8), candidates={5, 8}), ]) with pytest.raises(techniques.NotFound): techniques.LockedCandidate(sudoku).first()
def test_locked_candidate_in_a_row(): sudoku = make_sudoku_with_marks( [ [9, 0, 0, 5, 3, 1, 2, 8, 0], [0, 2, 5, 7, 0, 4, 9, 3, 0], [0, 3, 0, 0, 0, 2, 0, 4, 0], [4, 8, 1, 2, 5, 7, 6, 9, 3], [3, 5, 9, 0, 0, 8, 0, 0, 2], [7, 6, 2, 3, 1, 9, 8, 5, 4], [0, 1, 0, 0, 0, 0, 0, 6, 8], [6, 0, 8, 1, 0, 5, 3, 0, 0], [0, 0, 3, 8, 7, 6, 0, 0, 0], ], box_size=BoxSize(3, 3), ) locked_candidate = techniques.LockedCandidate(sudoku).first() assert locked_candidate.combination.cells == [ Cell(position=Position(6, 3, 7), candidates={4, 9}), Cell(position=Position(6, 4, 7), candidates={2, 4, 9}), ] assert locked_candidate.combination.values == [9] assert locked_candidate.changes == [ Cell(position=Position(7, 4, 7), candidates={2, 4}) ]
def test_locked_candidate_in_a_box(): sudoku = make_sudoku_with_marks( [ [2, 0, 0, 5, 9, 3, 1, 0, 0], [5, 0, 1, 0, 0, 2, 3, 0, 0], [3, 9, 7, 6, 4, 1, 8, 2, 5], [6, 0, 4, 0, 3, 8, 9, 0, 0], [8, 1, 0, 0, 0, 0, 0, 3, 6], [7, 3, 9, 0, 0, 6, 0, 0, 8], [1, 7, 0, 3, 0, 4, 6, 0, 0], [9, 0, 0, 0, 1, 5, 7, 4, 3], [4, 0, 3, 0, 6, 0, 0, 0, 1], ], box_size=BoxSize(3, 3), ) sudoku.update([ Cell(position=Position(1, 1, 0), candidates={4, 6}), Cell(position=Position(1, 7, 2), candidates={6, 9}), Cell(position=Position(1, 8, 2), candidates={4, 9}), Cell(position=Position(8, 3, 7), candidates={7, 9}), Cell(position=Position(8, 7, 8), candidates={5, 8}), ]) locked_candidate = techniques.LockedCandidate(sudoku).first() assert locked_candidate.combination.cells == [ Cell(position=Position(3, 7, 5), candidates={1, 5, 7}), Cell(position=Position(3, 8, 5), candidates={2, 7}), ] assert locked_candidate.combination.values == [7] assert locked_candidate.changes == [ Cell(position=Position(3, 3, 4), candidates={1, 2}) ]
def test_naked_triplet(): sudoku = make_sudoku_with_marks( [ [9, 2, 6, 7, 3, 5, 4, 0, 0], [0, 0, 0, 6, 4, 9, 7, 2, 5], [7, 4, 5, 8, 2, 1, 9, 3, 6], [0, 1, 0, 0, 0, 8, 6, 4, 0], [5, 0, 4, 0, 0, 0, 8, 0, 0], [8, 6, 9, 1, 7, 4, 3, 5, 2], [0, 0, 0, 0, 8, 0, 0, 0, 0], [0, 0, 0, 9, 1, 0, 0, 6, 0], [0, 0, 0, 4, 0, 3, 1, 0, 0], ], box_size=BoxSize(3, 3), ) naked_triplet = techniques.NakedTriplet(sudoku).first() assert naked_triplet.combination.cells == [ Cell(position=Position(6, 7, 8), candidates={7, 9}), Cell(position=Position(8, 7, 8), candidates={7, 8, 9}), Cell(position=Position(8, 8, 8), candidates={7, 8, 9}), ] assert naked_triplet.combination.values == [7, 8, 9] by_position = operator.attrgetter("position") assert sorted(naked_triplet.changes, key=by_position) == [ Cell(position=Position(6, 8, 8), candidates={3, 4}), Cell(position=Position(7, 8, 8), candidates={3, 4}), ]
def test_naked_pair(): sudoku = make_sudoku_with_marks( [ [9, 2, 6, 7, 3, 5, 4, 0, 0], [0, 0, 0, 6, 4, 9, 7, 2, 5], [7, 4, 5, 8, 2, 1, 9, 3, 6], [0, 1, 0, 0, 0, 8, 6, 4, 0], [5, 0, 4, 0, 0, 0, 8, 0, 0], [8, 6, 9, 1, 7, 4, 3, 5, 2], [0, 0, 0, 0, 8, 0, 0, 0, 0], [0, 0, 0, 9, 1, 0, 0, 6, 0], [0, 0, 0, 4, 0, 3, 1, 0, 0], ], box_size=BoxSize(3, 3), ) naked_pair = techniques.NakedPair(sudoku).first() assert naked_pair.combination.cells == [ Cell(position=Position(6, 3, 7), candidates={2, 5}), Cell(position=Position(6, 6, 8), candidates={2, 5}), ] assert naked_pair.combination.values == [2, 5] by_position = operator.attrgetter("position") assert sorted(naked_pair.changes, key=by_position) == [ Cell(position=Position(6, 0, 6), candidates={1, 3, 4, 6}), Cell(position=Position(6, 1, 6), candidates={3, 7, 9}), Cell(position=Position(6, 2, 6), candidates={1, 3, 7}), Cell(position=Position(6, 5, 7), candidates={6, 7}), ]
def test_hidden_single(): sudoku = make_sudoku_with_marks( [ [9, 0, 6, 7, 0, 5, 0, 0, 0], [0, 0, 0, 0, 0, 9, 0, 2, 5], [7, 4, 0, 0, 0, 1, 0, 0, 0], [0, 1, 0, 0, 0, 0, 6, 4, 0], [5, 0, 0, 0, 0, 0, 0, 0, 0], [8, 6, 9, 1, 0, 0, 3, 0, 0], [0, 0, 0, 0, 8, 0, 0, 0, 0], [0, 0, 0, 9, 0, 0, 0, 6, 0], [0, 0, 0, 4, 0, 3, 1, 0, 0], ], box_size=BoxSize(3, 3), ) hidden_single = techniques.HiddenSingle(sudoku).first() assert hidden_single.combination.cells == [ Cell(position=Position(1, 6, 2), candidates={4, 7, 8}) ] assert hidden_single.combination.values == [7] by_position = operator.attrgetter("position") assert sorted(hidden_single.changes, key=by_position) == [ Cell(position=Position(1, 6, 2), value=7), Cell(position=Position(4, 6, 5), candidates={2, 8, 9}), Cell(position=Position(6, 6, 8), candidates={2, 4, 5, 9}), Cell(position=Position(7, 6, 8), candidates={2, 4, 5, 8}), ]
def test_xy_wing(): sudoku = make_sudoku_with_marks( [ [2, 0, 0, 5, 9, 3, 1, 0, 0], [5, 0, 1, 0, 0, 2, 3, 0, 0], [3, 9, 7, 6, 4, 1, 8, 2, 5], [6, 0, 4, 0, 3, 8, 9, 0, 0], [8, 1, 0, 0, 0, 0, 0, 3, 6], [7, 3, 9, 0, 0, 6, 0, 0, 8], [1, 7, 0, 3, 0, 4, 6, 0, 0], [9, 0, 0, 0, 1, 5, 7, 4, 3], [4, 0, 3, 0, 6, 0, 0, 0, 1], ], box_size=BoxSize(3, 3), ) sudoku.update([Cell(position=Position(3, 3, 4), candidates={1, 2})]) xy_wing = techniques.XYWing(sudoku).first() assert xy_wing.combination.cells == [ Cell(position=Position(3, 3, 4), candidates={1, 2}), Cell(position=Position(5, 4, 4), candidates={2, 5}), Cell(position=Position(5, 7, 5), candidates={1, 5}), ] assert xy_wing.combination.values == [1] assert xy_wing.changes == [ Cell(position=Position(3, 7, 5), candidates={5, 7}), Cell(position=Position(5, 3, 4), candidates={2, 4}), ]
def test_unique_rectangle_block_apart(): sudoku = make_sudoku_with_marks( [ [9, 0, 1, 0, 3, 0, 5, 0, 0], [0, 0, 7, 8, 1, 9, 0, 0, 0], [0, 2, 0, 4, 5, 6, 1, 9, 7], [1, 0, 2, 9, 0, 0, 6, 0, 5], [0, 0, 0, 6, 7, 1, 0, 2, 4], [6, 0, 0, 3, 2, 5, 8, 0, 0], [0, 1, 5, 0, 9, 0, 4, 6, 3], [0, 0, 6, 5, 0, 0, 0, 0, 0], [0, 0, 0, 1, 6, 3, 0, 5, 0], ], box_size=BoxSize(3, 3), ) unique_rectangle = techniques.UniqueRectangle(sudoku).first() assert unique_rectangle.combination.cells == [ Cell(position=Position(0, 3, 1), candidates={2, 7}), Cell(position=Position(0, 5, 1), candidates={2, 7}), Cell(position=Position(6, 3, 7), candidates={2, 7}), Cell(position=Position(6, 5, 7), candidates={2, 7, 8}), ] assert unique_rectangle.changes == [ Cell(position=Position(6, 5, 7), candidates={8}) ]
def test_lone_single(): sudoku = make_sudoku_with_marks( [ [0, 0, 0, 0, 9, 0, 1, 0, 0], [0, 0, 0, 0, 0, 2, 3, 0, 0], [0, 0, 7, 0, 0, 1, 8, 2, 5], [6, 0, 4, 0, 3, 8, 9, 0, 0], [8, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 9, 0, 0, 0, 0, 0, 8], [1, 7, 0, 0, 0, 0, 6, 0, 0], [9, 0, 0, 0, 1, 0, 7, 4, 3], [4, 0, 3, 0, 6, 0, 0, 0, 1], ], box_size=BoxSize(3, 3), ) lone_single = techniques.LoneSingle(sudoku).first() assert lone_single.combination.cells == [ Cell(position=Position(1, 0, 0), candidates={5}) ] assert lone_single.combination.values == [5] by_position = operator.attrgetter("position") assert sorted(lone_single.changes, key=by_position) == [ Cell(position=Position(0, 0, 0), candidates={2, 3}), Cell(position=Position(0, 1, 0), candidates={2, 3, 4, 6, 8}), Cell(position=Position(0, 2, 0), candidates={2, 6, 8}), Cell(position=Position(1, 0, 0), value=5), Cell(position=Position(1, 1, 0), candidates={4, 6, 8, 9}), Cell(position=Position(1, 2, 0), candidates={1, 6, 8}), Cell(position=Position(1, 3, 1), candidates={4, 6, 7, 8}), Cell(position=Position(1, 4, 1), candidates={4, 7, 8}), Cell(position=Position(5, 0, 3), candidates={2, 3, 7}), ]
def test_steps(): given = Sudoku.from_list( [ [0, 0, 0, 0, 9, 0, 1, 0, 0], [0, 0, 0, 0, 0, 2, 3, 0, 0], [0, 0, 7, 0, 0, 1, 8, 2, 5], [6, 0, 4, 0, 3, 8, 9, 0, 0], [8, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 9, 0, 0, 0, 0, 0, 8], [1, 7, 0, 0, 0, 0, 6, 0, 0], [9, 0, 0, 0, 1, 0, 7, 4, 3], [4, 0, 3, 0, 6, 0, 0, 0, 1], ], box_size=BoxSize(3, 3), ) assert [step.combination.name for step in solvers.steps(given)] == [ "Bulk Pencil Marking", *["Lone Single"] * 8, *["Hidden Single"] * 7, "Lone Single", "Hidden Single", *["Naked Pair"] * 3, "Locked Candidate", "XY Wing", *["Hidden Single"] * 2, "Unique Rectangle", "Hidden Single", *["Lone Single"] * 2, "Hidden Single", *["Lone Single"] * 28, ]
def test_backtrack(): given = Sudoku.from_list( [ [0, 0, 0, 8, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 4, 3], [5, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 7, 0, 8, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 2, 0, 0, 3, 0, 0, 0, 0], [6, 0, 0, 0, 0, 0, 0, 7, 5], [0, 0, 3, 4, 0, 0, 0, 0, 0], [0, 0, 0, 2, 0, 0, 6, 0, 0], ], box_size=BoxSize(3, 3), ) solution = solvers.backtrack(given) assert solution.is_solved() is True assert solution.is_valid() is True assert str(solution) == ("237841569" "186795243" "594326718" "315674892" "469582137" "728139456" "642918375" "853467921" "971253684")
def test_eliminate(): given = Sudoku.from_list( [ [0, 0, 0, 0, 9, 0, 1, 0, 0], [0, 0, 0, 0, 0, 2, 3, 0, 0], [0, 0, 7, 0, 0, 1, 8, 2, 5], [6, 0, 4, 0, 3, 8, 9, 0, 0], [8, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 9, 0, 0, 0, 0, 0, 8], [1, 7, 0, 0, 0, 0, 6, 0, 0], [9, 0, 0, 0, 1, 0, 7, 4, 3], [4, 0, 3, 0, 6, 0, 0, 0, 1], ], box_size=BoxSize(3, 3), ) solution = solvers.eliminate(given) assert str(solution) == ("200593100" "501002300" "397641825" "604038900" "810000036" "739006008" "170304600" "900015743" "403060001")
def test_unique_rectangle_not_found(): sudoku = make_sudoku_with_marks( [ [9, 2, 6, 7, 3, 5, 4, 0, 0], [0, 0, 0, 6, 4, 9, 7, 2, 5], [7, 4, 5, 8, 2, 1, 9, 3, 6], [0, 1, 0, 0, 0, 8, 6, 4, 0], [5, 0, 4, 0, 0, 0, 8, 0, 0], [8, 6, 9, 1, 7, 4, 3, 5, 2], [0, 0, 0, 0, 8, 0, 0, 0, 0], [0, 0, 0, 9, 1, 0, 0, 6, 0], [0, 0, 0, 4, 0, 3, 1, 0, 0], ], box_size=BoxSize(3, 3), ) sudoku.update([ Cell(position=Position(6, 0, 6), candidates={1, 6}), Cell(position=Position(6, 8, 8), candidates={3, 4}), Cell(position=Position(7, 0, 6), candidates={3, 4}), Cell(position=Position(7, 8, 8), candidates={3, 4}), ]) with pytest.raises(techniques.NotFound): techniques.UniqueRectangle(sudoku).first()
def test_unique_rectangle(): sudoku = make_sudoku_with_marks( [ [0, 6, 0, 8, 0, 2, 3, 7, 1], [3, 0, 7, 1, 6, 5, 8, 0, 4], [0, 8, 1, 3, 7, 0, 5, 6, 0], [8, 7, 4, 9, 2, 3, 1, 5, 6], [9, 1, 3, 6, 5, 8, 2, 4, 7], [6, 0, 0, 4, 1, 7, 9, 3, 8], [0, 3, 8, 0, 0, 0, 6, 1, 5], [0, 0, 6, 0, 8, 1, 4, 0, 3], [1, 4, 0, 5, 3, 6, 7, 8, 0], ], box_size=BoxSize(3, 3), ) assert len(list(techniques.UniqueRectangle(sudoku))) == 1 unique_rectangle = techniques.UniqueRectangle(sudoku).first() assert unique_rectangle.combination.cells == [ Cell(position=Position(6, 0, 6), candidates={2, 7}), Cell(position=Position(6, 3, 7), candidates={2, 7}), Cell(position=Position(7, 0, 6), candidates={2, 5, 7}), Cell(position=Position(7, 3, 7), candidates={2, 7}), ] assert unique_rectangle.combination.values == [2, 7] assert unique_rectangle.changes == [ Cell(position=Position(7, 0, 6), candidates={5}), ]
def get_object(self): queryset = self.filter_queryset(self.get_queryset()) instance = queryset.random() if not instance: raise exceptions.NotFound return SudokuGrid.from_string( instance.puzzle, box_size=BoxSize(instance.box_width, instance.box_length), )
def test_random_initial_cells(): box_size = BoxSize(3, 3) cells = generators._random_initial_cells(box_size) assert len(cells) == 15 sudoku = Sudoku(*cells, box_size=box_size) assert sudoku.is_valid() is True assert sudoku.is_solved() is False
def test_plain(): expected = ( "┌─────────┬─────────┬─────────╥─────────┬─────────┬─────────╥─────────┬─────────┬─────────┐\n" # noqa: E501 "│ 2 3 │ 2 3 │ 2 ║ 3 │ │ 3 ║ │ │ │\n" # noqa: E501 "│ 5 │ 4 5 6 │ 5 6 ║ 4 5 6 │ 9 │ 4 5 6 ║ 1 │ 6 │ 4 6 │\n" # noqa: E501 "│ │ 8 │ 8 ║ 7 8 │ │ 7 ║ │ 7 │ 7 │\n" # noqa: E501 "├─────────┼─────────┼─────────╫─────────┼─────────┼─────────╫─────────┼─────────┼─────────┤\n" # noqa: E501 "│ │ │ 1 ║ │ │ ║ │ │ │\n" # noqa: E501 "│ 5 │ 4 5 6 │ 5 6 ║ 4 5 6 │ 4 5 │ 2 ║ 3 │ 6 │ 4 6 │\n" # noqa: E501 "│ │ 8 9 │ 8 ║ 7 8 │ 7 8 │ ║ │ 7 9 │ 7 9 │\n" # noqa: E501 "├─────────┼─────────┼─────────╫─────────┼─────────┼─────────╫─────────┼─────────┼─────────┤\n" # noqa: E501 "│ 3 │ 3 │ ║ 3 │ │ ║ │ │ │\n" # noqa: E501 "│ │ 4 6 │ 7 ║ 4 6 │ 4 │ 1 ║ 8 │ 2 │ 5 │\n" # noqa: E501 "│ │ 9 │ ║ │ │ ║ │ │ │\n" # noqa: E501 "╞═════════╪═════════╪═════════╬═════════╪═════════╪═════════╬═════════╪═════════╪═════════╡\n" # noqa: E501 "│ │ 2 │ ║ 1 2 │ │ ║ │ 1 │ 2 │\n" # noqa: E501 "│ 6 │ 5 │ 4 ║ 5 │ 3 │ 8 ║ 9 │ 5 │ │\n" # noqa: E501 "│ │ │ ║ 7 │ │ ║ │ 7 │ 7 │\n" # noqa: E501 "├─────────┼─────────┼─────────╫─────────┼─────────┼─────────╫─────────┼─────────┼─────────┤\n" # noqa: E501 "│ │ │ 2 ║ 2 │ 2 │ ║ 2 │ 3 │ 2 │\n" # noqa: E501 "│ 8 │ 1 │ 5 ║ 4 5 6 │ 4 5 │ 4 5 6 ║ 4 5 │ 5 6 │ 4 6 │\n" # noqa: E501 "│ │ │ ║ 7 9 │ 7 │ 7 9 ║ │ 7 │ 7 │\n" # noqa: E501 "├─────────┼─────────┼─────────╫─────────┼─────────┼─────────╫─────────┼─────────┼─────────┤\n" # noqa: E501 "│ 2 3 │ 2 3 │ ║ 1 2 │ 2 │ ║ 2 │ 1 3 │ │\n" # noqa: E501 "│ 5 │ 5 │ 9 ║ 4 5 6 │ 4 5 │ 4 5 6 ║ 4 5 │ 5 6 │ 8 │\n" # noqa: E501 "│ 7 │ │ ║ 7 │ 7 │ 7 ║ │ 7 │ │\n" # noqa: E501 "╞═════════╪═════════╪═════════╬═════════╪═════════╪═════════╬═════════╪═════════╪═════════╡\n" # noqa: E501 "│ │ │ 2 ║ 2 3 │ 2 │ 3 ║ │ │ 2 │\n" # noqa: E501 "│ 1 │ 7 │ 5 ║ 4 5 │ 4 5 │ 4 5 ║ 6 │ 5 │ │\n" # noqa: E501 "│ │ │ 8 ║ 8 9 │ 8 │ 9 ║ │ 8 9 │ 9 │\n" # noqa: E501 "├─────────┼─────────┼─────────╫─────────┼─────────┼─────────╫─────────┼─────────┼─────────┤\n" # noqa: E501 "│ │ 2 │ 2 ║ 2 │ │ ║ │ │ │\n" # noqa: E501 "│ 9 │ 5 6 │ 5 6 ║ 5 │ 1 │ 5 ║ 7 │ 4 │ 3 │\n" # noqa: E501 "│ │ 8 │ 8 ║ 8 │ │ ║ │ │ │\n" # noqa: E501 "├─────────┼─────────┼─────────╫─────────┼─────────┼─────────╫─────────┼─────────┼─────────┤\n" # noqa: E501 "│ │ 2 │ ║ 2 │ │ ║ 2 │ │ │\n" # noqa: E501 "│ 4 │ 5 │ 3 ║ 5 │ 6 │ 5 ║ 5 │ 5 │ 1 │\n" # noqa: E501 "│ │ 8 │ ║ 7 8 9 │ │ 7 9 ║ │ 8 9 │ │\n" # noqa: E501 "└─────────┴─────────┴─────────╨─────────┴─────────┴─────────╨─────────┴─────────┴─────────┘" # noqa: E501 ) sudoku = Sudoku.from_list( [ [0, 0, 0, 0, 9, 0, 1, 0, 0], [0, 0, 0, 0, 0, 2, 3, 0, 0], [0, 0, 7, 0, 0, 1, 8, 2, 5], [6, 0, 4, 0, 3, 8, 9, 0, 0], [8, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 9, 0, 0, 0, 0, 0, 8], [1, 7, 0, 0, 0, 0, 6, 0, 0], [9, 0, 0, 0, 1, 0, 7, 4, 3], [4, 0, 3, 0, 6, 0, 0, 0, 1], ], box_size=BoxSize(3, 3), ) sudoku.update(techniques.BulkPencilMarking(sudoku).first().changes) assert renderers.plain(sudoku) == expected
def test_sudoku(): sudoku = Sudoku( Cell(position=Position(0, 0, 0), value=2), Cell(position=Position(0, 1, 0), candidates=set()), box_size=BoxSize(3, 3), ) assert sudoku[0, 0].value == 2 assert sudoku[0, 1].candidates == set() assert sudoku[0, 2].value is None assert len(sudoku[0, 2].candidates) == 0
def create(self, validated_data): return Sudoku( *[ Cell( position=Position(*cell["position"]), value=cell["value"], candidates=set(cell["candidates"]), ) for cell in validated_data["cells"] ], box_size=BoxSize(*validated_data["box_size"]), )
def test_rank_sudoku_with_multiple_solutions(): puzzle = [ [8, 1, 0, 0, 0, 0, 6, 7, 9], [0, 0, 0, 6, 7, 9, 0, 2, 0], [0, 0, 0, 1, 2, 8, 3, 0, 0], [0, 3, 4, 0, 5, 7, 0, 0, 0], [2, 0, 0, 0, 0, 0, 7, 0, 4], [0, 0, 0, 0, 0, 6, 0, 0, 0], [0, 0, 3, 7, 0, 1, 0, 6, 2], [0, 0, 0, 0, 0, 0, 4, 0, 0], [0, 0, 1, 0, 3, 0, 0, 8, 0], ] sudoku = Sudoku.from_list(puzzle, box_size=BoxSize(3, 3)) with pytest.raises(exceptions.MultipleSolutions): stats.rank(sudoku)
def sudoku(): return Sudoku.from_list( [ [0, 0, 7, 0, 3, 0, 8, 0, 0], [0, 0, 0, 2, 0, 5, 0, 0, 0], [4, 0, 0, 9, 0, 6, 0, 0, 1], [0, 4, 3, 0, 0, 0, 2, 1, 0], [1, 0, 0, 0, 0, 0, 0, 0, 5], [0, 5, 8, 0, 0, 0, 6, 7, 0], [5, 0, 0, 1, 0, 8, 0, 0, 9], [0, 0, 0, 5, 0, 3, 0, 0, 0], [0, 0, 2, 0, 9, 0, 5, 0, 0], ], box_size=BoxSize(3, 3), )
def test_from_string(sudoku_12x12): sudoku = Sudoku.from_string( "300974B1068C" "800692C0B430" "00040365020A" "C561BA000923" "284A6073C0B5" "7B93C0504006" "A4072B309000" "0900000CA060" "031C08A9270B" "4000A0276019" "92A030100070" "060B0C000052", box_size=BoxSize(3, 4), ) assert list(sudoku.cells()) == list(sudoku_12x12.cells())
def test_hidden_single_not_found(): sudoku = make_sudoku_with_marks( [ [9, 2, 6, 7, 3, 5, 4, 0, 0], [0, 0, 0, 6, 4, 9, 7, 2, 5], [7, 4, 5, 8, 2, 1, 9, 3, 6], [0, 1, 0, 0, 0, 8, 6, 4, 0], [5, 0, 4, 0, 0, 0, 8, 0, 0], [8, 6, 9, 1, 7, 4, 3, 5, 2], [0, 0, 0, 0, 8, 0, 0, 0, 0], [0, 0, 0, 9, 1, 0, 0, 6, 0], [0, 0, 0, 4, 0, 3, 1, 0, 0], ], box_size=BoxSize(3, 3), ) with pytest.raises(techniques.NotFound): techniques.HiddenSingle(sudoku).first()
def test_colorful(): sudoku = Sudoku.from_list( [ [0, 0, 0, 0, 9, 0, 1, 0, 0], [0, 0, 0, 0, 0, 2, 3, 0, 0], [0, 0, 7, 0, 0, 1, 8, 2, 5], [6, 0, 4, 0, 3, 8, 9, 0, 0], [8, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 9, 0, 0, 0, 0, 0, 8], [1, 7, 0, 0, 0, 0, 6, 0, 0], [9, 0, 0, 0, 1, 0, 7, 4, 3], [4, 0, 3, 0, 6, 0, 0, 0, 1], ], box_size=BoxSize(3, 3), ) sudoku.update(techniques.BulkPencilMarking(sudoku).first().changes) assert "\033[93m2\033[0m" in renderers.colorful(sudoku)
def test_xy_wing_not_found(): sudoku = make_sudoku_with_marks( [ [2, 0, 0, 5, 9, 3, 1, 0, 0], [5, 0, 1, 0, 0, 2, 3, 0, 0], [3, 9, 7, 6, 4, 1, 8, 2, 5], [6, 0, 4, 0, 3, 8, 9, 0, 0], [8, 1, 0, 0, 0, 0, 0, 3, 6], [7, 3, 9, 0, 0, 6, 0, 0, 8], [1, 7, 0, 3, 0, 4, 6, 0, 0], [9, 0, 0, 0, 1, 5, 7, 4, 3], [4, 0, 3, 0, 6, 0, 0, 0, 1], ], box_size=BoxSize(3, 3), ) with pytest.raises(techniques.NotFound): techniques.XYWing(sudoku).first()
def test_steps_raises_unsolvable(): given = Sudoku.from_list( [ [7, 0, 0, 0, 8, 2, 5, 0, 0], [0, 5, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 9, 0, 7, 0, 2, 6], [0, 0, 8, 0, 9, 0, 0, 7, 5], [3, 0, 0, 6, 7, 5, 0, 0, 0], [0, 0, 0, 0, 2, 0, 0, 9, 0], [9, 0, 1, 0, 0, 3, 0, 0, 0], [0, 0, 0, 0, 6, 0, 0, 0, 3], [6, 0, 2, 0, 0, 0, 0, 0, 0], ], box_size=BoxSize(3, 3), ) with pytest.raises(exceptions.Unsolvable): list(solvers.steps(given))
def test_lone_single_not_found(): sudoku = make_sudoku_with_marks( [ [9, 0, 6, 7, 0, 5, 0, 0, 0], [0, 0, 0, 0, 0, 9, 0, 2, 5], [7, 4, 0, 0, 0, 1, 0, 0, 0], [0, 1, 0, 0, 0, 0, 6, 4, 0], [5, 0, 0, 0, 0, 0, 0, 0, 0], [8, 6, 9, 1, 0, 0, 3, 0, 0], [0, 0, 0, 0, 8, 0, 0, 0, 0], [0, 0, 0, 9, 0, 0, 0, 6, 0], [0, 0, 0, 4, 0, 3, 1, 0, 0], ], box_size=BoxSize(3, 3), ) with pytest.raises(techniques.NotFound): techniques.LoneSingle(sudoku).first()
def sudoku_12x12(): return Sudoku.from_list( [ [3, 0, 0, 9, 7, 4, 11, 1, 0, 6, 8, 12], [8, 0, 0, 6, 9, 2, 12, 0, 11, 4, 3, 0], [0, 0, 0, 4, 0, 3, 6, 5, 0, 2, 0, 10], [12, 5, 6, 1, 11, 10, 0, 0, 0, 9, 2, 3], [2, 8, 4, 10, 6, 0, 7, 3, 12, 0, 11, 5], [7, 11, 9, 3, 12, 0, 5, 0, 4, 0, 0, 6], [10, 4, 0, 7, 2, 11, 3, 0, 9, 0, 0, 0], [0, 9, 0, 0, 0, 0, 0, 12, 10, 0, 6, 0], [0, 3, 1, 12, 0, 8, 10, 9, 2, 7, 0, 11], [4, 0, 0, 0, 10, 0, 2, 7, 6, 0, 1, 9], [9, 2, 10, 0, 3, 0, 1, 0, 0, 0, 7, 0], [0, 6, 0, 11, 0, 12, 0, 0, 0, 0, 5, 2], ], box_size=BoxSize(3, 4), )
def random_sudoku(avg_rank: int = 150, box_size: BoxSize = BoxSize(3, 3)) -> Sudoku: sudoku = Sudoku(*_random_initial_cells(box_size), box_size=box_size) solution = solvers.backtrack(sudoku) iterations = min(avg_rank, MAX_ITERATIONS) for i in range(iterations): size = random.randint(1, 2) rows = [random.randint(0, solution.size - 1) for _ in range(size)] columns = [random.randint(0, solution.size - 1) for _ in range(size)] cells = [solution[row, column] for (row, column) in zip(rows, columns)] if all(cell.value for cell in cells): solution.update([Cell(position=cell.position) for cell in cells]) try: stats.rank(solution) except exceptions.MultipleSolutions: solution.update(cells) return solution