Linting
This commit is contained in:
@@ -1,51 +0,0 @@
|
|||||||
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()
|
|
||||||
35
benchmark/timing.py
Normal file
35
benchmark/timing.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import multiprocessing
|
||||||
|
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
|
||||||
|
|
||||||
|
from .util import run_benchmark
|
||||||
|
|
||||||
|
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:
|
||||||
|
with multiprocessing.Pool() as pool:
|
||||||
|
result = pool.imap_unordered(
|
||||||
|
run_benchmark, [Path(benchmark) for benchmark in benchmark_files]
|
||||||
|
)
|
||||||
|
for current_result in result:
|
||||||
|
print(current_result)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
41
benchmark/unsolved.py
Normal file
41
benchmark/unsolved.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import multiprocessing
|
||||||
|
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
|
||||||
|
|
||||||
|
from .util import run_benchmark
|
||||||
|
|
||||||
|
benchmark_files = [
|
||||||
|
"pictures/unsolved/tmp1ern14si.png",
|
||||||
|
"pictures/unsolved/tmp2_0vn4tl.png",
|
||||||
|
"pictures/unsolved/tmp32jmcnfp.png",
|
||||||
|
"pictures/unsolved/tmpcml2ldfl.png",
|
||||||
|
"pictures/unsolved/tmpd7rbwwdb.png",
|
||||||
|
"pictures/unsolved/tmpdudxuw0s.png",
|
||||||
|
"pictures/unsolved/tmpeplvz9bk.png",
|
||||||
|
"pictures/unsolved/tmph_esy__3.png",
|
||||||
|
"pictures/unsolved/tmpn95ueb7_.png",
|
||||||
|
"pictures/unsolved/tmpqzay4q08.png",
|
||||||
|
"pictures/unsolved/tmputbych59.png",
|
||||||
|
"pictures/unsolved/tmpx4uo6pg3.png",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
with multiprocessing.Pool() as pool:
|
||||||
|
result = pool.imap_unordered(
|
||||||
|
run_benchmark, [Path(benchmark) for benchmark in benchmark_files]
|
||||||
|
)
|
||||||
|
for current_result in result:
|
||||||
|
print(current_result)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
36
benchmark/util.py
Normal file
36
benchmark/util.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
def run_benchmark(benchmark: Path) -> str:
|
||||||
|
result = ""
|
||||||
|
result += f"{benchmark}:\n"
|
||||||
|
read_file_time = time.time()
|
||||||
|
image = cv2.imread(str(benchmark))
|
||||||
|
load_config_time = time.time()
|
||||||
|
result += f"\tLoad image: {load_config_time - read_file_time:5.2f}\n"
|
||||||
|
|
||||||
|
conf = configuration.load("test_config.zip")
|
||||||
|
parse_board_time = time.time()
|
||||||
|
result += f"\tLoad config: {parse_board_time - load_config_time:5.2f}\n"
|
||||||
|
|
||||||
|
board = parse_board(image, conf)
|
||||||
|
solve_time = time.time()
|
||||||
|
result += f"\tParse image: {solve_time - parse_board_time:5.2f}\n"
|
||||||
|
|
||||||
|
solution_iterator = next(solver.solve(board, timeout=10), None)
|
||||||
|
finished_time = time.time()
|
||||||
|
result += f"\tSolve board: {finished_time - solve_time:5.2f}\n"
|
||||||
|
|
||||||
|
assert board.check_correct()
|
||||||
|
if solution_iterator is None:
|
||||||
|
result += "\tSolution timed out\n"
|
||||||
|
else:
|
||||||
|
result += f"\tSolved in {len(list(solution_iterator))} steps\n"
|
||||||
|
return result
|
||||||
@@ -8,6 +8,7 @@ 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
|
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)
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -21,6 +22,18 @@ def drag(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dragSquare(
|
||||||
|
src: Tuple[int, int, int, int],
|
||||||
|
dst: Tuple[int, int, int, int],
|
||||||
|
offset: Tuple[int, int] = (0, 0),
|
||||||
|
) -> None:
|
||||||
|
drag(
|
||||||
|
(src[0] + (src[2] - src[0]) // 2, src[1] + (src[3] - src[1]) // 2),
|
||||||
|
(dst[0] + (dst[2] - dst[0]) // 2, dst[1] + (dst[3] - dst[1]) // 2),
|
||||||
|
offset,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def click(point: Tuple[int, int], offset: Tuple[int, int] = (0, 0)) -> None:
|
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.moveTo(x=point[0] + offset[0], y=point[1] + offset[1])
|
||||||
pyautogui.mouseDown()
|
pyautogui.mouseDown()
|
||||||
@@ -28,40 +41,49 @@ def click(point: Tuple[int, int], offset: Tuple[int, int] = (0, 0)) -> None:
|
|||||||
pyautogui.mouseUp()
|
pyautogui.mouseUp()
|
||||||
|
|
||||||
|
|
||||||
|
def clickSquare(
|
||||||
|
field: Tuple[int, int, int, int], offset: Tuple[int, int] = (0, 0)
|
||||||
|
) -> None:
|
||||||
|
click(
|
||||||
|
(field[0] + (field[2] - field[0]) // 2, field[1] + (field[3] - field[1]) // 2),
|
||||||
|
offset,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def handle_action(
|
def handle_action(
|
||||||
action: board_actions.Action,
|
action: board_actions.Action,
|
||||||
offset: Tuple[int, int],
|
offset: Tuple[int, int],
|
||||||
conf: configuration.Configuration,
|
conf: configuration.Configuration,
|
||||||
) -> None:
|
) -> None:
|
||||||
if isinstance(action, board_actions.MoveAction):
|
if isinstance(action, board_actions.MoveAction):
|
||||||
src_x, src_y, _, _ = adjustment.get_square(
|
src = adjustment.get_square(
|
||||||
conf.field_adjustment,
|
conf.field_adjustment,
|
||||||
index_x=action.source_id,
|
index_x=action.source_id,
|
||||||
index_y=action.source_row_index,
|
index_y=action.source_row_index,
|
||||||
)
|
)
|
||||||
dst_x, dst_y, _, _ = adjustment.get_square(
|
dst = adjustment.get_square(
|
||||||
conf.field_adjustment,
|
conf.field_adjustment,
|
||||||
index_x=action.destination_id,
|
index_x=action.destination_id,
|
||||||
index_y=action.destination_row_index,
|
index_y=action.destination_row_index,
|
||||||
)
|
)
|
||||||
drag((src_x, src_y), (dst_x, dst_y), offset)
|
dragSquare(src, dst, offset)
|
||||||
return
|
return
|
||||||
if isinstance(action, board_actions.HuaKillAction):
|
if isinstance(action, board_actions.HuaKillAction):
|
||||||
warnings.warn("Hua kill should be handled before handle_action")
|
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 = adjustment.get_square(
|
||||||
conf.field_adjustment,
|
conf.field_adjustment,
|
||||||
index_x=action.field_id,
|
index_x=action.field_id,
|
||||||
index_y=action.field_row_index,
|
index_y=action.field_row_index,
|
||||||
)
|
)
|
||||||
bunker_x, bunker_y, _, _ = adjustment.get_square(
|
bunker = adjustment.get_square(
|
||||||
conf.bunker_adjustment, index_x=action.bunker_id, index_y=0,
|
conf.bunker_adjustment, index_x=action.bunker_id, index_y=0,
|
||||||
)
|
)
|
||||||
if action.to_bunker:
|
if action.to_bunker:
|
||||||
drag((field_x, field_y), (bunker_x, bunker_y), offset)
|
dragSquare(field, bunker, offset)
|
||||||
else:
|
else:
|
||||||
drag((bunker_x, bunker_y), (field_x, field_y), offset)
|
dragSquare(bunker, field, offset)
|
||||||
return
|
return
|
||||||
if isinstance(action, board_actions.DragonKillAction):
|
if isinstance(action, board_actions.DragonKillAction):
|
||||||
dragon_sequence = [
|
dragon_sequence = [
|
||||||
@@ -69,34 +91,33 @@ def handle_action(
|
|||||||
board.SpecialCard.Fa,
|
board.SpecialCard.Fa,
|
||||||
board.SpecialCard.Bai,
|
board.SpecialCard.Bai,
|
||||||
]
|
]
|
||||||
field_x, field_y, size_x, size_y = adjustment.get_square(
|
field = adjustment.get_square(
|
||||||
conf.special_button_adjustment,
|
conf.special_button_adjustment,
|
||||||
index_x=0,
|
index_x=0,
|
||||||
index_y=dragon_sequence.index(action.dragon),
|
index_y=dragon_sequence.index(action.dragon),
|
||||||
)
|
)
|
||||||
click(
|
clickSquare(
|
||||||
(field_x + (size_x - field_x) // 2, field_y + (size_y - field_y) // 2),
|
field, offset,
|
||||||
offset,
|
|
||||||
)
|
)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
return
|
return
|
||||||
if isinstance(action, board_actions.GoalAction):
|
if isinstance(action, board_actions.GoalAction):
|
||||||
dst_x, dst_y, _, _ = adjustment.get_square(
|
dst = 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,
|
||||||
)
|
)
|
||||||
if action.source_position == board.Position.Field:
|
if action.source_position == board.Position.Field:
|
||||||
assert action.source_row_index is not None
|
assert action.source_row_index is not None
|
||||||
src_x, src_y, _, _ = adjustment.get_square(
|
src = adjustment.get_square(
|
||||||
conf.field_adjustment,
|
conf.field_adjustment,
|
||||||
index_x=action.source_id,
|
index_x=action.source_id,
|
||||||
index_y=action.source_row_index,
|
index_y=action.source_row_index,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
assert action.source_position == board.Position.Bunker
|
assert action.source_position == board.Position.Bunker
|
||||||
src_x, src_y, _, _ = adjustment.get_square(
|
src = adjustment.get_square(
|
||||||
conf.bunker_adjustment, index_x=action.source_id, index_y=0,
|
conf.bunker_adjustment, index_x=action.source_id, index_y=0,
|
||||||
)
|
)
|
||||||
drag((src_x, src_y), (dst_x, dst_y), offset)
|
dragSquare(src, dst, offset)
|
||||||
return
|
return
|
||||||
raise AssertionError("You forgot an Action type")
|
raise AssertionError("You forgot an Action type")
|
||||||
|
|
||||||
@@ -109,7 +130,7 @@ def handle_actions(
|
|||||||
automatic_count = 0
|
automatic_count = 0
|
||||||
for action in actions:
|
for action in actions:
|
||||||
print(action)
|
print(action)
|
||||||
if action.automatic():
|
if isinstance(action, board_actions.HuaKillAction):
|
||||||
automatic_count += 1
|
automatic_count += 1
|
||||||
else:
|
else:
|
||||||
time.sleep(0.5 * automatic_count)
|
time.sleep(0.5 * automatic_count)
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ def possible_goal_move_actions(
|
|||||||
if not (card.number == search_board.getGoal(card.suit) + 1):
|
if not (card.number == search_board.getGoal(card.suit) + 1):
|
||||||
continue
|
continue
|
||||||
obvious = all(
|
obvious = all(
|
||||||
search_board.getGoal(other_suit) >= card.number - 1
|
search_board.getGoal(other_suit) >= card.number - 2
|
||||||
for other_suit in set(board.NumberCard.Suit) - {card.suit}
|
for other_suit in set(board.NumberCard.Suit) - {card.suit}
|
||||||
)
|
)
|
||||||
yield board_actions.GoalAction(
|
yield board_actions.GoalAction(
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ def solve(
|
|||||||
|
|
||||||
iter_start = time.time()
|
iter_start = time.time()
|
||||||
count = 0
|
count = 0
|
||||||
while stack:
|
while len(stack) > 0:
|
||||||
|
|
||||||
count += 1
|
count += 1
|
||||||
if count > 5000:
|
if count > 5000:
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ import unittest
|
|||||||
|
|
||||||
from shenzhen_solitaire.board import NumberCard, Position
|
from shenzhen_solitaire.board import NumberCard, Position
|
||||||
from shenzhen_solitaire.solver import board_possibilities
|
from shenzhen_solitaire.solver import board_possibilities
|
||||||
from shenzhen_solitaire.solver.board_actions import (BunkerizeAction,
|
from shenzhen_solitaire.solver.board_actions import (
|
||||||
GoalAction, HuaKillAction,
|
BunkerizeAction,
|
||||||
MoveAction)
|
GoalAction,
|
||||||
|
HuaKillAction,
|
||||||
|
MoveAction,
|
||||||
|
)
|
||||||
|
|
||||||
from .boards import TEST_BOARD
|
from .boards import TEST_BOARD
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import os
|
||||||
|
|
||||||
import pyautogui
|
import pyautogui
|
||||||
import shenzhen_solitaire.card_detection.configuration as configuration
|
import shenzhen_solitaire.card_detection.configuration as configuration
|
||||||
@@ -28,14 +29,21 @@ def solve() -> None:
|
|||||||
conf = configuration.load("test_config.zip")
|
conf = configuration.load("test_config.zip")
|
||||||
board = parse_board(image, conf)
|
board = parse_board(image, conf)
|
||||||
assert board.check_correct()
|
assert board.check_correct()
|
||||||
solution_iterator = next(solver.solve(board, timeout=10), None)
|
solution_iterator = next(solver.solve(board, timeout=10, verbose=True), None)
|
||||||
if solution_iterator is None:
|
if solution_iterator is None:
|
||||||
clicker.click(NEW_BUTTON, OFFSET)
|
clicker.click(NEW_BUTTON, OFFSET)
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
|
fd, outfile = tempfile.mkstemp(
|
||||||
|
dir="E:/shenzhen-solitaire/unsolved", suffix=".png"
|
||||||
|
)
|
||||||
|
sock = os.fdopen(fd, "w")
|
||||||
|
sock.close()
|
||||||
|
cv2.imwrite(outfile, image)
|
||||||
return
|
return
|
||||||
solution=list(solution_iterator)
|
solution = list(solution_iterator)
|
||||||
print(f"Solved in {len(solution)} steps")
|
print(f"Solved in {len(solution)} steps")
|
||||||
clicker.handle_actions(solution, OFFSET, conf)
|
clicker.handle_actions(solution, OFFSET, conf)
|
||||||
|
print("Solved")
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
clicker.click(NEW_BUTTON, OFFSET)
|
clicker.click(NEW_BUTTON, OFFSET)
|
||||||
time.sleep(7)
|
time.sleep(7)
|
||||||
|
|||||||
Reference in New Issue
Block a user