Added timeout to solver

This commit is contained in:
Lukas Wölfer
2020-02-12 01:20:20 +01:00
parent 8c43ae4eb0
commit 4cd25719e2
3 changed files with 38 additions and 29 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)