Compare commits
2 commits
91d222f17f
...
1b18b19c85
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b18b19c85 | |||
| 35859acb27 |
4 changed files with 62 additions and 15 deletions
|
|
@ -89,10 +89,13 @@ class Board:
|
||||||
old_row = self.rows[row]
|
old_row = self.rows[row]
|
||||||
self.rows[row] = old_row[:col] + value + old_row[col+1:]
|
self.rows[row] = old_row[:col] + value + old_row[col+1:]
|
||||||
|
|
||||||
def click(self, index: int) -> Self:
|
def click(self, index: int) -> Self | None:
|
||||||
# check is clickable
|
# check is clickable
|
||||||
row, col = index // self.width, index % self.width
|
row, col = index // self.width, index % self.width
|
||||||
assert self.rows[row][col] == "1", f"{index} is not clickable!"
|
|
||||||
|
if self.rows[row][col] != "1":
|
||||||
|
# button is not clickable
|
||||||
|
return None
|
||||||
|
|
||||||
# get neighbor positions
|
# get neighbor positions
|
||||||
nb_rc = [
|
nb_rc = [
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,23 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
from .board import Board
|
from .board import Board
|
||||||
|
from .solver import solve
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
board = Board.default_puzzle
|
solution = solve(
|
||||||
print(board)
|
Board.default_puzzle,
|
||||||
print(board.solution_class, board.binary_repr)
|
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} ...")
|
print(f"clicking button {btn} ...")
|
||||||
board.click(btn)
|
print(state)
|
||||||
print(board)
|
print(state.solution_class, state.binary_repr)
|
||||||
print(board.solution_class, board.binary_repr)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
39
pigeon_magnet_solver/solver.py
Normal file
39
pigeon_magnet_solver/solver.py
Normal file
|
|
@ -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
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import pytest
|
|
||||||
|
|
||||||
from pigeon_magnet_solver.board import Board
|
from pigeon_magnet_solver.board import Board
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -13,9 +11,11 @@ def test_default():
|
||||||
assert board.binary_repr == (4, 11, 3), "Wrong binary repr"
|
assert board.binary_repr == (4, 11, 3), "Wrong binary repr"
|
||||||
assert board.solution_class == (1, 3, 2), "Wrong solution class"
|
assert board.solution_class == (1, 3, 2), "Wrong solution class"
|
||||||
|
|
||||||
assert board.click(4).binary_repr == (4, 14, 3)
|
assert (board2 := board.click(4)) is not None
|
||||||
assert board.click(5).binary_repr == (4, 11, 6)
|
assert board2.binary_repr == (4, 14, 3)
|
||||||
|
|
||||||
|
assert (board2 := board.click(5)) is not None
|
||||||
|
assert board2.binary_repr == (4, 11, 6)
|
||||||
|
|
||||||
for idx in (3, 7):
|
for idx in (3, 7):
|
||||||
with pytest.raises(AssertionError):
|
assert board.click(idx) is None
|
||||||
board.click(idx)
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue