diff --git a/pigeon_magnet_solver/main.py b/pigeon_magnet_solver/main.py index c5ac5a0..6bc55db 100755 --- a/pigeon_magnet_solver/main.py +++ b/pigeon_magnet_solver/main.py @@ -1,18 +1,23 @@ #!/usr/bin/python3 from .board import Board +from .solver import solve def main() -> None: - board = Board.default_puzzle - print(board) - print(board.solution_class, board.binary_repr) + solution = solve( + Board.default_puzzle, + lambda state: state.solution_class == (3, 1, 2), + ) - for btn in (2, 4, 5, 7): # 2 is not a regular button, but alas + if solution is None: + print("Unsolvable!") + return + + for btn, state in solution: print(f"clicking button {btn} ...") - board.click(btn) - print(board) - print(board.solution_class, board.binary_repr) + print(state) + print(state.solution_class, state.binary_repr) if __name__ == "__main__": diff --git a/pigeon_magnet_solver/solver.py b/pigeon_magnet_solver/solver.py new file mode 100644 index 0000000..aba9f95 --- /dev/null +++ b/pigeon_magnet_solver/solver.py @@ -0,0 +1,39 @@ +from typing import Callable + +from .board import Board + + +def solve( + state: Board, + is_solved: Callable[[Board], bool], + *, + history: list[tuple[int, Board]] | None = None, +) -> list[tuple[int, Board]] | None: + + if history is None: + # create starting state + history = [(-1, state)] + + if is_solved(state): + return history + + for button in state.buttons: + # try to click all buttons + next = state.click(button) + + if next is not None: + # click successful + binary_history = ( + state.binary_repr + for _, state in history + ) + + if next.binary_repr not in binary_history: + # "next" is a new state + solution = solve( + next, is_solved, + history=[*history, (button, next)] + ) + + if solution is not None: + return solution