diff --git a/shenzhen_solitaire/clicker/main.py b/shenzhen_solitaire/clicker/main.py index 573bed0..9fe0009 100644 --- a/shenzhen_solitaire/clicker/main.py +++ b/shenzhen_solitaire/clicker/main.py @@ -6,7 +6,7 @@ import shenzhen_solitaire.board as board import shenzhen_solitaire.card_detection.adjustment as adjustment import shenzhen_solitaire.card_detection.configuration as configuration import shenzhen_solitaire.solver.board_actions as board_actions - +import warnings def drag( 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) return if isinstance(action, board_actions.HuaKillAction): - time.sleep(1) + warnings.warn("Hua kill should be handled before handle_action") return if isinstance(action, board_actions.BunkerizeAction): field_x, field_y, _, _ = adjustment.get_square( @@ -81,9 +81,6 @@ def handle_action( time.sleep(0.5) return if isinstance(action, board_actions.GoalAction): - if action.obvious: - time.sleep(1) - return dst_x, dst_y, _, _ = adjustment.get_square( conf.goal_adjustment, index_x=action.goal_id, index_y=0, ) @@ -103,11 +100,23 @@ def handle_action( return 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( actions: List[board_actions.Action], offset: Tuple[int, int], conf: configuration.Configuration, ) -> None: + automatic_count = 0 for action in actions: - handle_action(action, offset, conf) + if automatic(action): + automatic_count += 1 + else: + time.sleep(0.5 * automatic_count) + automatic_count = 0 + handle_action(action, offset, conf) diff --git a/shenzhen_solitaire/solver/solver.py b/shenzhen_solitaire/solver/solver.py index b1ff81e..aaa7e33 100644 --- a/shenzhen_solitaire/solver/solver.py +++ b/shenzhen_solitaire/solver/solver.py @@ -1,11 +1,11 @@ """Contains solver for solitaire""" import typing from typing import Iterator, List, Optional +import time from ..board import Board from . import board_actions -from .board_actions import (DragonKillAction, GoalAction, HuaKillAction, - MoveAction) +from .board_actions import DragonKillAction, GoalAction, HuaKillAction, MoveAction from .board_possibilities import possible_actions @@ -50,7 +50,9 @@ class ActionStack: 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""" state_set = {board.state_identifier} stack = ActionStack() @@ -77,12 +79,15 @@ def solve(board: Board) -> Iterator[List[board_actions.Action]]: return True return False + iter_start = time.time() count = 0 while stack: count += 1 if count > 5000: 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) @@ -100,6 +105,7 @@ def solve(board: Board) -> Iterator[List[board_actions.Action]]: if board.solved(): yield stack.action_stack + iter_start = time.time() stack.action_stack[-1].undo(board) while isinstance( stack.action_stack[-1], (GoalAction, HuaKillAction, DragonKillAction) diff --git a/tools/assistant.py b/tools/assistant.py index 420868a..971816a 100644 --- a/tools/assistant.py +++ b/tools/assistant.py @@ -16,32 +16,26 @@ SIZE = (2560, 1440) 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: - screenshot_dir = Path(tempfile.mkdtemp()) - screenshot_file = screenshot_dir / "screenshot.png" - screenshot = pyautogui.screenshot(region=(*OFFSET, *SIZE)) - screenshot.save(screenshot_file) - image = cv2.imread(str(screenshot_file)) - # debug_screenshot() + with tempfile.TemporaryDirectory() as screenshot_dir: + screenshot_file = Path(screenshot_dir) / "screenshot.png" + screenshot = pyautogui.screenshot(region=(*OFFSET, *SIZE)) + screenshot.save(screenshot_file) + image = cv2.imread(str(screenshot_file)) print("Solving") conf = configuration.load("test_config.zip") board = parse_board(image, conf) - print(board) - solution = list(next(solver.solve(board))) - print(*solution, sep="\n") - time.sleep(1) + assert board.check_correct() + try: + solution = list(next(solver.solve(board, timeout=10))) + except StopIteration: + clicker.click(NEW_BUTTON, OFFSET) + time.sleep(10) + return + print(f"Solved in {len(solution)} steps") for step in solution: print(step) - # time.sleep(0.5) clicker.handle_action(step, OFFSET, conf) clicker.click(NEW_BUTTON, OFFSET) time.sleep(10)