Added benchmark
This commit is contained in:
0
benchmark/__init__.py
Normal file
0
benchmark/__init__.py
Normal file
51
benchmark/__main__.py
Normal file
51
benchmark/__main__.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import tempfile
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
import shenzhen_solitaire.card_detection.configuration as configuration
|
||||
import shenzhen_solitaire.solver.solver as solver
|
||||
from shenzhen_solitaire.card_detection.board_parser import parse_board
|
||||
|
||||
benchmark_files = [
|
||||
"pictures/20190809172206_1.jpg",
|
||||
"pictures/20190809172213_1.jpg",
|
||||
"pictures/20190809172219_1.jpg",
|
||||
"pictures/20190809172225_1.jpg",
|
||||
"pictures/20190809172232_1.jpg",
|
||||
"pictures/20190809172238_1.jpg",
|
||||
]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
for benchmark in benchmark_files:
|
||||
print(f"{benchmark}:")
|
||||
read_file_time = time.time()
|
||||
|
||||
image = cv2.imread(benchmark)
|
||||
load_config_time = time.time()
|
||||
print(f"Load image: {load_config_time - read_file_time:5.2f}")
|
||||
|
||||
conf = configuration.load("test_config.zip")
|
||||
parse_board_time = time.time()
|
||||
print(f"Load config: {parse_board_time - load_config_time:5.2f}")
|
||||
|
||||
board = parse_board(image, conf)
|
||||
solve_time = time.time()
|
||||
print(f"Parse image: {solve_time - parse_board_time:5.2f}")
|
||||
|
||||
solution_iterator = next(solver.solve(board, timeout=10), None)
|
||||
finished_time = time.time()
|
||||
print(f"Solve board: {finished_time - solve_time:5.2f}")
|
||||
|
||||
assert board.check_correct()
|
||||
if solution_iterator is None:
|
||||
print("Solution timed out")
|
||||
else:
|
||||
print(f"Solved in {len(list(solution_iterator))} steps")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,123 @@
|
||||
import time
|
||||
from typing import List, Tuple
|
||||
|
||||
import pyautogui
|
||||
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)
|
||||
) -> None:
|
||||
|
||||
pyautogui.moveTo(x=src[0] + offset[0], y=src[1] + offset[1])
|
||||
pyautogui.dragTo(
|
||||
x=dst[0] + offset[0],
|
||||
y=dst[1] + offset[1],
|
||||
duration=0.4,
|
||||
tween=lambda x: 0 if x < 0.5 else 1,
|
||||
)
|
||||
|
||||
|
||||
def click(point: Tuple[int, int], offset: Tuple[int, int] = (0, 0)) -> None:
|
||||
pyautogui.moveTo(x=point[0] + offset[0], y=point[1] + offset[1])
|
||||
pyautogui.mouseDown()
|
||||
time.sleep(0.2)
|
||||
pyautogui.mouseUp()
|
||||
|
||||
|
||||
def handle_action(
|
||||
action: board_actions.Action,
|
||||
offset: Tuple[int, int],
|
||||
conf: configuration.Configuration,
|
||||
) -> None:
|
||||
if isinstance(action, board_actions.MoveAction):
|
||||
src_x, src_y, _, _ = adjustment.get_square(
|
||||
conf.field_adjustment,
|
||||
index_x=action.source_id,
|
||||
index_y=action.source_row_index,
|
||||
)
|
||||
dst_x, dst_y, _, _ = adjustment.get_square(
|
||||
conf.field_adjustment,
|
||||
index_x=action.destination_id,
|
||||
index_y=action.destination_row_index,
|
||||
)
|
||||
drag((src_x, src_y), (dst_x, dst_y), offset)
|
||||
return
|
||||
if isinstance(action, board_actions.HuaKillAction):
|
||||
warnings.warn("Hua kill should be handled before handle_action")
|
||||
return
|
||||
if isinstance(action, board_actions.BunkerizeAction):
|
||||
field_x, field_y, _, _ = adjustment.get_square(
|
||||
conf.field_adjustment,
|
||||
index_x=action.field_id,
|
||||
index_y=action.field_row_index,
|
||||
)
|
||||
bunker_x, bunker_y, _, _ = adjustment.get_square(
|
||||
conf.bunker_adjustment, index_x=action.bunker_id, index_y=0,
|
||||
)
|
||||
if action.to_bunker:
|
||||
drag((field_x, field_y), (bunker_x, bunker_y), offset)
|
||||
else:
|
||||
drag((bunker_x, bunker_y), (field_x, field_y), offset)
|
||||
return
|
||||
if isinstance(action, board_actions.DragonKillAction):
|
||||
dragon_sequence = [
|
||||
board.SpecialCard.Zhong,
|
||||
board.SpecialCard.Fa,
|
||||
board.SpecialCard.Bai,
|
||||
]
|
||||
field_x, field_y, size_x, size_y = adjustment.get_square(
|
||||
conf.special_button_adjustment,
|
||||
index_x=0,
|
||||
index_y=dragon_sequence.index(action.dragon),
|
||||
)
|
||||
click(
|
||||
(field_x + (size_x - field_x) // 2, field_y + (size_y - field_y) // 2),
|
||||
offset,
|
||||
)
|
||||
time.sleep(0.5)
|
||||
return
|
||||
if isinstance(action, board_actions.GoalAction):
|
||||
dst_x, dst_y, _, _ = adjustment.get_square(
|
||||
conf.goal_adjustment, index_x=action.goal_id, index_y=0,
|
||||
)
|
||||
if action.source_position == board.Position.Field:
|
||||
assert action.source_row_index is not None
|
||||
src_x, src_y, _, _ = adjustment.get_square(
|
||||
conf.field_adjustment,
|
||||
index_x=action.source_id,
|
||||
index_y=action.source_row_index,
|
||||
)
|
||||
else:
|
||||
assert action.source_position == board.Position.Bunker
|
||||
src_x, src_y, _, _ = adjustment.get_square(
|
||||
conf.bunker_adjustment, index_x=action.source_id, index_y=0,
|
||||
)
|
||||
drag((src_x, src_y), (dst_x, dst_y), offset)
|
||||
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:
|
||||
print(action)
|
||||
if automatic(action):
|
||||
automatic_count += 1
|
||||
else:
|
||||
time.sleep(0.5 * automatic_count)
|
||||
automatic_count = 0
|
||||
handle_action(action, offset, conf)
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
import time
|
||||
from typing import List, Tuple
|
||||
|
||||
import pyautogui
|
||||
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)
|
||||
) -> None:
|
||||
|
||||
pyautogui.moveTo(x=src[0] + offset[0], y=src[1] + offset[1])
|
||||
pyautogui.dragTo(
|
||||
x=dst[0] + offset[0],
|
||||
y=dst[1] + offset[1],
|
||||
duration=0.4,
|
||||
tween=lambda x: 0 if x < 0.5 else 1,
|
||||
)
|
||||
|
||||
|
||||
def click(point: Tuple[int, int], offset: Tuple[int, int] = (0, 0)) -> None:
|
||||
pyautogui.moveTo(x=point[0] + offset[0], y=point[1] + offset[1])
|
||||
pyautogui.mouseDown()
|
||||
time.sleep(0.2)
|
||||
pyautogui.mouseUp()
|
||||
|
||||
|
||||
def handle_action(
|
||||
action: board_actions.Action,
|
||||
offset: Tuple[int, int],
|
||||
conf: configuration.Configuration,
|
||||
) -> None:
|
||||
if isinstance(action, board_actions.MoveAction):
|
||||
src_x, src_y, _, _ = adjustment.get_square(
|
||||
conf.field_adjustment,
|
||||
index_x=action.source_id,
|
||||
index_y=action.source_row_index,
|
||||
)
|
||||
dst_x, dst_y, _, _ = adjustment.get_square(
|
||||
conf.field_adjustment,
|
||||
index_x=action.destination_id,
|
||||
index_y=action.destination_row_index,
|
||||
)
|
||||
drag((src_x, src_y), (dst_x, dst_y), offset)
|
||||
return
|
||||
if isinstance(action, board_actions.HuaKillAction):
|
||||
warnings.warn("Hua kill should be handled before handle_action")
|
||||
return
|
||||
if isinstance(action, board_actions.BunkerizeAction):
|
||||
field_x, field_y, _, _ = adjustment.get_square(
|
||||
conf.field_adjustment,
|
||||
index_x=action.field_id,
|
||||
index_y=action.field_row_index,
|
||||
)
|
||||
bunker_x, bunker_y, _, _ = adjustment.get_square(
|
||||
conf.bunker_adjustment, index_x=action.bunker_id, index_y=0,
|
||||
)
|
||||
if action.to_bunker:
|
||||
drag((field_x, field_y), (bunker_x, bunker_y), offset)
|
||||
else:
|
||||
drag((bunker_x, bunker_y), (field_x, field_y), offset)
|
||||
return
|
||||
if isinstance(action, board_actions.DragonKillAction):
|
||||
dragon_sequence = [
|
||||
board.SpecialCard.Zhong,
|
||||
board.SpecialCard.Fa,
|
||||
board.SpecialCard.Bai,
|
||||
]
|
||||
field_x, field_y, size_x, size_y = adjustment.get_square(
|
||||
conf.special_button_adjustment,
|
||||
index_x=0,
|
||||
index_y=dragon_sequence.index(action.dragon),
|
||||
)
|
||||
click(
|
||||
(field_x + (size_x - field_x) // 2, field_y + (size_y - field_y) // 2),
|
||||
offset,
|
||||
)
|
||||
time.sleep(0.5)
|
||||
return
|
||||
if isinstance(action, board_actions.GoalAction):
|
||||
dst_x, dst_y, _, _ = adjustment.get_square(
|
||||
conf.goal_adjustment, index_x=action.goal_id, index_y=0,
|
||||
)
|
||||
if action.source_position == board.Position.Field:
|
||||
assert action.source_row_index is not None
|
||||
src_x, src_y, _, _ = adjustment.get_square(
|
||||
conf.field_adjustment,
|
||||
index_x=action.source_id,
|
||||
index_y=action.source_row_index,
|
||||
)
|
||||
else:
|
||||
assert action.source_position == board.Position.Bunker
|
||||
src_x, src_y, _, _ = adjustment.get_square(
|
||||
conf.bunker_adjustment, index_x=action.source_id, index_y=0,
|
||||
)
|
||||
drag((src_x, src_y), (dst_x, dst_y), offset)
|
||||
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:
|
||||
print(action)
|
||||
if automatic(action):
|
||||
automatic_count += 1
|
||||
else:
|
||||
time.sleep(0.5 * automatic_count)
|
||||
automatic_count = 0
|
||||
handle_action(action, offset, conf)
|
||||
@@ -51,7 +51,7 @@ class ActionStack:
|
||||
|
||||
|
||||
def solve(
|
||||
board: Board, *, timeout: Optional[float] = None
|
||||
board: Board, *, timeout: Optional[float] = None, verbose: bool = False
|
||||
) -> Iterator[List[board_actions.Action]]:
|
||||
"""Solve a solitaire puzzle"""
|
||||
state_set = {board.state_identifier}
|
||||
@@ -85,7 +85,8 @@ def solve(
|
||||
count += 1
|
||||
if count > 5000:
|
||||
count = 0
|
||||
print(f"{time.time() - iter_start} {len(stack)} {board.goal}")
|
||||
if verbose:
|
||||
print(f"{time.time() - iter_start} {len(stack)} {board.goal}")
|
||||
if timeout is not None and time.time() - iter_start > timeout:
|
||||
return
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import numpy as np
|
||||
|
||||
import pyautogui
|
||||
import shenzhen_solitaire.card_detection.configuration as configuration
|
||||
import shenzhen_solitaire.clicker.main as clicker
|
||||
import shenzhen_solitaire.clicker as clicker
|
||||
import shenzhen_solitaire.solver.solver as solver
|
||||
from shenzhen_solitaire.card_detection.board_parser import parse_board
|
||||
|
||||
|
||||
Reference in New Issue
Block a user