Added timeout to solver
This commit is contained in:
@@ -6,7 +6,7 @@ import shenzhen_solitaire.board as board
|
|||||||
import shenzhen_solitaire.card_detection.adjustment as adjustment
|
import shenzhen_solitaire.card_detection.adjustment as adjustment
|
||||||
import shenzhen_solitaire.card_detection.configuration as configuration
|
import shenzhen_solitaire.card_detection.configuration as configuration
|
||||||
import shenzhen_solitaire.solver.board_actions as board_actions
|
import shenzhen_solitaire.solver.board_actions as board_actions
|
||||||
|
import warnings
|
||||||
|
|
||||||
def drag(
|
def drag(
|
||||||
src: Tuple[int, int], dst: Tuple[int, int], offset: Tuple[int, int] = (0, 0)
|
src: Tuple[int, int], dst: Tuple[int, int], offset: Tuple[int, int] = (0, 0)
|
||||||
@@ -47,7 +47,7 @@ def handle_action(
|
|||||||
drag((src_x, src_y), (dst_x, dst_y), offset)
|
drag((src_x, src_y), (dst_x, dst_y), offset)
|
||||||
return
|
return
|
||||||
if isinstance(action, board_actions.HuaKillAction):
|
if isinstance(action, board_actions.HuaKillAction):
|
||||||
time.sleep(1)
|
warnings.warn("Hua kill should be handled before handle_action")
|
||||||
return
|
return
|
||||||
if isinstance(action, board_actions.BunkerizeAction):
|
if isinstance(action, board_actions.BunkerizeAction):
|
||||||
field_x, field_y, _, _ = adjustment.get_square(
|
field_x, field_y, _, _ = adjustment.get_square(
|
||||||
@@ -81,9 +81,6 @@ def handle_action(
|
|||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
return
|
return
|
||||||
if isinstance(action, board_actions.GoalAction):
|
if isinstance(action, board_actions.GoalAction):
|
||||||
if action.obvious:
|
|
||||||
time.sleep(1)
|
|
||||||
return
|
|
||||||
dst_x, dst_y, _, _ = adjustment.get_square(
|
dst_x, dst_y, _, _ = adjustment.get_square(
|
||||||
conf.goal_adjustment, index_x=action.goal_id, index_y=0,
|
conf.goal_adjustment, index_x=action.goal_id, index_y=0,
|
||||||
)
|
)
|
||||||
@@ -103,11 +100,23 @@ def handle_action(
|
|||||||
return
|
return
|
||||||
raise AssertionError("You forgot an Action type")
|
raise AssertionError("You forgot an Action type")
|
||||||
|
|
||||||
|
def automatic(action: board_actions.Action) -> bool:
|
||||||
|
if isinstance(action, board_actions.HuaKillAction):
|
||||||
|
return True
|
||||||
|
if isinstance(action, board_actions.GoalAction) and action.obvious:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def handle_actions(
|
def handle_actions(
|
||||||
actions: List[board_actions.Action],
|
actions: List[board_actions.Action],
|
||||||
offset: Tuple[int, int],
|
offset: Tuple[int, int],
|
||||||
conf: configuration.Configuration,
|
conf: configuration.Configuration,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
automatic_count = 0
|
||||||
for action in actions:
|
for action in actions:
|
||||||
|
if automatic(action):
|
||||||
|
automatic_count += 1
|
||||||
|
else:
|
||||||
|
time.sleep(0.5 * automatic_count)
|
||||||
|
automatic_count = 0
|
||||||
handle_action(action, offset, conf)
|
handle_action(action, offset, conf)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
"""Contains solver for solitaire"""
|
"""Contains solver for solitaire"""
|
||||||
import typing
|
import typing
|
||||||
from typing import Iterator, List, Optional
|
from typing import Iterator, List, Optional
|
||||||
|
import time
|
||||||
|
|
||||||
from ..board import Board
|
from ..board import Board
|
||||||
from . import board_actions
|
from . import board_actions
|
||||||
from .board_actions import (DragonKillAction, GoalAction, HuaKillAction,
|
from .board_actions import DragonKillAction, GoalAction, HuaKillAction, MoveAction
|
||||||
MoveAction)
|
|
||||||
from .board_possibilities import possible_actions
|
from .board_possibilities import possible_actions
|
||||||
|
|
||||||
|
|
||||||
@@ -50,7 +50,9 @@ class ActionStack:
|
|||||||
return len(self.index_stack)
|
return len(self.index_stack)
|
||||||
|
|
||||||
|
|
||||||
def solve(board: Board) -> Iterator[List[board_actions.Action]]:
|
def solve(
|
||||||
|
board: Board, *, timeout: Optional[float] = None
|
||||||
|
) -> Iterator[List[board_actions.Action]]:
|
||||||
"""Solve a solitaire puzzle"""
|
"""Solve a solitaire puzzle"""
|
||||||
state_set = {board.state_identifier}
|
state_set = {board.state_identifier}
|
||||||
stack = ActionStack()
|
stack = ActionStack()
|
||||||
@@ -77,12 +79,15 @@ def solve(board: Board) -> Iterator[List[board_actions.Action]]:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
iter_start = time.time()
|
||||||
count = 0
|
count = 0
|
||||||
while stack:
|
while stack:
|
||||||
count += 1
|
count += 1
|
||||||
if count > 5000:
|
if count > 5000:
|
||||||
count = 0
|
count = 0
|
||||||
print(f"{len(stack)} {board.goal}")
|
print(f"{time.time() - iter_start} {len(stack)} {board.goal}")
|
||||||
|
if timeout is not None and time.time() - iter_start > timeout:
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
# _limit_stack_size(80)
|
# _limit_stack_size(80)
|
||||||
|
|
||||||
@@ -100,6 +105,7 @@ def solve(board: Board) -> Iterator[List[board_actions.Action]]:
|
|||||||
|
|
||||||
if board.solved():
|
if board.solved():
|
||||||
yield stack.action_stack
|
yield stack.action_stack
|
||||||
|
iter_start = time.time()
|
||||||
stack.action_stack[-1].undo(board)
|
stack.action_stack[-1].undo(board)
|
||||||
while isinstance(
|
while isinstance(
|
||||||
stack.action_stack[-1], (GoalAction, HuaKillAction, DragonKillAction)
|
stack.action_stack[-1], (GoalAction, HuaKillAction, DragonKillAction)
|
||||||
|
|||||||
@@ -16,32 +16,26 @@ SIZE = (2560, 1440)
|
|||||||
NEW_BUTTON = (1900, 1100)
|
NEW_BUTTON = (1900, 1100)
|
||||||
|
|
||||||
|
|
||||||
def debug_screenshot(image):
|
|
||||||
cv2.namedWindow("Name", cv2.WINDOW_KEEPRATIO)
|
|
||||||
cv2.imshow("Name", image)
|
|
||||||
cv2.waitKey(0)
|
|
||||||
input()
|
|
||||||
cv2.destroyAllWindows()
|
|
||||||
|
|
||||||
|
|
||||||
def solve() -> None:
|
def solve() -> None:
|
||||||
screenshot_dir = Path(tempfile.mkdtemp())
|
with tempfile.TemporaryDirectory() as screenshot_dir:
|
||||||
screenshot_file = screenshot_dir / "screenshot.png"
|
screenshot_file = Path(screenshot_dir) / "screenshot.png"
|
||||||
screenshot = pyautogui.screenshot(region=(*OFFSET, *SIZE))
|
screenshot = pyautogui.screenshot(region=(*OFFSET, *SIZE))
|
||||||
screenshot.save(screenshot_file)
|
screenshot.save(screenshot_file)
|
||||||
image = cv2.imread(str(screenshot_file))
|
image = cv2.imread(str(screenshot_file))
|
||||||
# debug_screenshot()
|
|
||||||
|
|
||||||
print("Solving")
|
print("Solving")
|
||||||
conf = configuration.load("test_config.zip")
|
conf = configuration.load("test_config.zip")
|
||||||
board = parse_board(image, conf)
|
board = parse_board(image, conf)
|
||||||
print(board)
|
assert board.check_correct()
|
||||||
solution = list(next(solver.solve(board)))
|
try:
|
||||||
print(*solution, sep="\n")
|
solution = list(next(solver.solve(board, timeout=10)))
|
||||||
time.sleep(1)
|
except StopIteration:
|
||||||
|
clicker.click(NEW_BUTTON, OFFSET)
|
||||||
|
time.sleep(10)
|
||||||
|
return
|
||||||
|
print(f"Solved in {len(solution)} steps")
|
||||||
for step in solution:
|
for step in solution:
|
||||||
print(step)
|
print(step)
|
||||||
# time.sleep(0.5)
|
|
||||||
clicker.handle_action(step, OFFSET, conf)
|
clicker.handle_action(step, OFFSET, conf)
|
||||||
clicker.click(NEW_BUTTON, OFFSET)
|
clicker.click(NEW_BUTTON, OFFSET)
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
|
|||||||
Reference in New Issue
Block a user