Worked on assistant

This commit is contained in:
Lukas Wölfer
2020-06-12 22:40:58 +02:00
parent f4ac445f61
commit b5d74d1ac0
14 changed files with 112 additions and 883 deletions

View File

@@ -105,7 +105,6 @@ def _save_adjustments(zip_file: zipfile.ZipFile, conf: Configuration) -> None:
adjustments[SPECIAL_BUTTON_ADJUSTMENT_KEY] = dataclasses.asdict(
conf.special_button_adjustment
)
print(adjustments)
zip_file.writestr(
ADJUSTMENT_FILE_NAME, json.dumps(adjustments),
)

View File

@@ -1,13 +1,14 @@
import time
from typing import List, Tuple
from typing import List, Tuple, Dict, Any
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
from dataclasses import dataclass
def drag(
src: Tuple[int, int], dst: Tuple[int, int], offset: Tuple[int, int] = (0, 0)
@@ -50,80 +51,73 @@ def clickSquare(
)
def handle_action(
action: board_actions.Action,
offset: Tuple[int, int],
conf: configuration.Configuration,
) -> None:
if isinstance(action, board_actions.MoveAction):
src = adjustment.get_square(
conf.field_adjustment,
index_x=action.source_id,
index_y=action.source_row_index,
@dataclass
class DragAction:
source: Tuple[int, int]
destination: Tuple[int, int]
@dataclass
class ClickAction:
destination: Tuple[int, int]
class WaitAction:
pass
def _parse_field(
field: Dict[str, Any], conf: configuration.Configuration
) -> Tuple[int, int]:
return (
int(field["column"]) * conf.field_adjustment.dx + conf.field_adjustment.x,
int(field["row"]) * conf.field_adjustment.dy + conf.field_adjustment.y,
)
def parse_action(action: Dict[str, Any], conf: configuration.Configuration):
assert len(action) == 1
action_name, info = next(iter(action.items()))
action_name = action_name.lower()
if action_name == "bunkerize":
field = _parse_field(info["field_position"], conf)
bunker = (
int(info["bunker_slot_index"]) * conf.bunker_adjustment.dx
+ conf.bunker_adjustment.x,
conf.bunker_adjustment.y,
)
dst = adjustment.get_square(
conf.field_adjustment,
index_x=action.destination_id,
index_y=action.destination_row_index,
)
dragSquare(src, dst, 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 = adjustment.get_square(
conf.field_adjustment,
index_x=action.field_id,
index_y=action.field_row_index,
)
bunker = adjustment.get_square(
conf.bunker_adjustment, index_x=action.bunker_id, index_y=0,
)
if action.to_bunker:
dragSquare(field, bunker, offset)
if str(info["to_bunker"]).lower() == "true":
return DragAction(source=field, destination=bunker)
else:
dragSquare(bunker, field, offset)
return
if isinstance(action, board_actions.DragonKillAction):
dragon_sequence = [
board.SpecialCard.Zhong,
board.SpecialCard.Fa,
board.SpecialCard.Bai,
]
field = adjustment.get_square(
conf.special_button_adjustment,
index_x=0,
index_y=dragon_sequence.index(action.dragon),
return DragAction(source=bunker, destination=field)
elif action_name == "move":
return DragAction(
source=_parse_field(info["source"], conf),
destination=_parse_field(info["source"], conf),
)
clickSquare(
field, offset,
elif action_name == "dragonkill":
return ClickAction()
elif action_name == "goal":
goal = (
int(info["goal_slot_index"]) * conf.goal_adjustment.dx
+ conf.goal_adjustment.x,
conf.goal_adjustment.y,
)
time.sleep(1)
return
if isinstance(action, board_actions.GoalAction):
dst = 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 = adjustment.get_square(
conf.field_adjustment,
index_x=action.source_id,
index_y=action.source_row_index,
)
if "Field" in info["source"]:
source = _parse_field(info["source"]["Field"], conf)
else:
assert action.source_position == board.Position.Bunker
src = adjustment.get_square(
conf.bunker_adjustment, index_x=action.source_id, index_y=0,
source = (
int(info["source"]["Bunker"]["slot_index"]) * conf.bunker_adjustment.dx
+ conf.bunker_adjustment.x,
conf.bunker_adjustment.y,
)
dragSquare(src, dst, offset)
return
raise AssertionError("You forgot an Action type")
return DragAction(source=source, destination=goal)
elif action_name == "huakill":
return WaitAction()
def handle_actions(
actions: List[board_actions.Action],
actions: List[Dict[str, Dict[str, Any]]],
offset: Tuple[int, int],
conf: configuration.Configuration,
) -> None:

View File

@@ -1,217 +0,0 @@
"""Contains actions that can be used on the board"""
from dataclasses import dataclass
from typing import List, Optional, Tuple
from .. import board
class Action:
"""Base class for a card move action on a solitaire board"""
_before_state: int = 0
_after_state: int = 0
def _apply(self, action_board: board.Board) -> None:
pass
def _undo(self, action_board: board.Board) -> None:
pass
def apply(self, action_board: board.Board) -> None:
"""Apply action to board"""
if __debug__:
self._before_state = action_board.state_identifier
self._apply(action_board)
if __debug__:
self._after_state = action_board.state_identifier
def undo(self, action_board: board.Board) -> None:
"""Undo action to board"""
assert action_board.state_identifier == self._after_state
self._undo(action_board)
assert action_board.state_identifier == self._before_state
def automatic(self) -> bool:
if isinstance(self, HuaKillAction):
return True
if isinstance(self, GoalAction) and self.obvious:
return True
return False
@dataclass
class GoalAction(Action):
"""Move card from field to goal"""
card: board.NumberCard
source_id: int
source_row_index: Optional[int]
source_position: board.Position
goal_id: int
obvious: bool
def _apply(self, action_board: board.Board) -> None:
"""Do action"""
assert action_board.getGoalId(self.card.suit) == self.goal_id
assert action_board.getGoal(self.card.suit) + 1 == self.card.number
if self.source_position == board.Position.Field:
assert action_board.field[self.source_id][-1] == self.card
action_board.field[self.source_id].pop()
action_board.incGoal(self.card.suit)
elif self.source_position == board.Position.Bunker:
assert action_board.bunker[self.source_id] == self.card
action_board.bunker[self.source_id] = None
action_board.incGoal(self.card.suit)
else:
raise RuntimeError("Unknown position")
def _undo(self, action_board: board.Board) -> None:
"""Undo action"""
assert action_board.getGoalId(self.card.suit) == self.goal_id
assert action_board.getGoal(self.card.suit) == self.card.number
if self.source_position == board.Position.Field:
action_board.field[self.source_id].append(self.card)
elif self.source_position == board.Position.Bunker:
assert action_board.bunker[self.source_id] is None
action_board.bunker[self.source_id] = self.card
else:
raise RuntimeError("Unknown position")
action_board.setGoal(self.card.suit, action_board.getGoal(self.card.suit) - 1)
@dataclass
class BunkerizeAction(Action):
"""Move card from bunker to field"""
card: board.Card
bunker_id: int
field_id: int
field_row_index: int
to_bunker: bool
def _move_from_bunker(self, action_board: board.Board) -> None:
assert action_board.bunker[self.bunker_id] == self.card
action_board.bunker[self.bunker_id] = None
action_board.field[self.field_id].append(self.card)
def _move_to_bunker(self, action_board: board.Board) -> None:
assert action_board.field[self.field_id][-1] == self.card
assert action_board.bunker[self.bunker_id] is None
action_board.bunker[self.bunker_id] = self.card
action_board.field[self.field_id].pop()
def _apply(self, action_board: board.Board) -> None:
"""Do action"""
if self.to_bunker:
self._move_to_bunker(action_board)
else:
self._move_from_bunker(action_board)
def _undo(self, action_board: board.Board) -> None:
"""Undo action"""
if self.to_bunker:
self._move_from_bunker(action_board)
else:
self._move_to_bunker(action_board)
@dataclass
class MoveAction(Action):
"""Moving a card from one field stack to another"""
cards: List[board.Card]
source_id: int
source_row_index: int
destination_id: int
destination_row_index: int
def _shift(self, action_board: board.Board, source: int, dest: int) -> None:
"""Shift a card from the field id 'source' to field id 'dest'"""
for stack_offset, card in enumerate(self.cards, start=-len(self.cards)):
assert action_board.field[source][stack_offset] == card
action_board.field[source] = action_board.field[source][: -len(self.cards)]
action_board.field[dest].extend(self.cards)
def _apply(self, action_board: board.Board) -> None:
"""Do action"""
if action_board.field[self.destination_id]:
dest_card = action_board.field[self.destination_id][-1]
if not all(isinstance(x, board.NumberCard) for x in self.cards):
raise AssertionError()
if not isinstance(dest_card, board.NumberCard):
raise AssertionError()
if not isinstance(self.cards[0], board.NumberCard):
raise AssertionError()
if dest_card.suit == self.cards[0].suit:
raise AssertionError()
if dest_card.number != self.cards[0].number + 1:
raise AssertionError()
self._shift(action_board, self.source_id, self.destination_id)
def _undo(self, action_board: board.Board) -> None:
"""Undo action"""
self._shift(action_board, self.destination_id, self.source_id)
@dataclass
class DragonKillAction(Action):
"""Removing four dragons from the top of the stacks to a bunker"""
dragon: board.SpecialCard
source_stacks: List[Tuple[board.Position, int]]
destination_bunker_id: int
def _apply(self, action_board: board.Board) -> None:
"""Do action"""
assert (
action_board.bunker[self.destination_bunker_id] is None
or action_board.bunker[self.destination_bunker_id] == self.dragon
)
assert len(self.source_stacks) == 4
for position, index in self.source_stacks:
if position == board.Position.Field:
assert action_board.field[index]
assert action_board.field[index][-1] == self.dragon
action_board.field[index].pop()
elif position == board.Position.Bunker:
assert action_board.bunker[index] == self.dragon
action_board.bunker[index] = None
else:
raise RuntimeError("Can only kill dragons in field and bunker")
action_board.bunker[self.destination_bunker_id] = (self.dragon, 4)
def _undo(self, action_board: board.Board) -> None:
"""Undo action"""
assert action_board.bunker[self.destination_bunker_id] == (self.dragon, 4)
assert len(self.source_stacks) == 4
action_board.bunker[self.destination_bunker_id] = None
for position, index in self.source_stacks:
if position == board.Position.Field:
action_board.field[index].append(self.dragon)
elif position == board.Position.Bunker:
action_board.bunker[index] = self.dragon
else:
raise RuntimeError("Can only kill dragons in field and bunker")
@dataclass
class HuaKillAction(Action):
"""Remove the flower card"""
source_field_id: int
source_field_row_index: int
def _apply(self, action_board: board.Board) -> None:
"""Do action"""
assert not action_board.flower_gone
assert action_board.field[self.source_field_id][-1] == board.SpecialCard.Hua
action_board.field[self.source_field_id].pop()
action_board.flower_gone = True
def _undo(self, action_board: board.Board) -> None:
"""Undo action"""
assert action_board.flower_gone
action_board.field[self.source_field_id].append(board.SpecialCard.Hua)
action_board.flower_gone = False

View File

@@ -1,234 +0,0 @@
"""Contains function to iterate different kinds of possible actions"""
from typing import Iterator, List, Tuple
from .. import board
from . import board_actions
def possible_huakill_action(
search_board: board.Board,
) -> Iterator[board_actions.HuaKillAction]:
"""Check if the flowercard can be eliminated"""
for index, stack in enumerate(search_board.field):
if stack and stack[-1] == board.SpecialCard.Hua:
yield board_actions.HuaKillAction(
source_field_id=index, source_field_row_index=len(stack) - 1
)
def possible_dragonkill_actions(
search_board: board.Board,
) -> Iterator[board_actions.DragonKillAction]:
"""Enumerate all possible dragon kills"""
possible_dragons = [
board.SpecialCard.Zhong,
board.SpecialCard.Fa,
board.SpecialCard.Bai,
]
if not any(x is None for x in search_board.bunker):
new_possible_dragons = []
for dragon in possible_dragons:
if any(x == dragon for x in search_board.bunker):
new_possible_dragons.append(dragon)
possible_dragons = new_possible_dragons
for dragon in possible_dragons:
bunker_dragons = [i for i, d in enumerate(search_board.bunker) if d == dragon]
field_dragons = [
i for i, f in enumerate(search_board.field) if f if f[-1] == dragon
]
if len(bunker_dragons) + len(field_dragons) != 4:
continue
destination_bunker_id = 0
if bunker_dragons:
destination_bunker_id = bunker_dragons[0]
else:
destination_bunker_id = [
i for i, x in enumerate(search_board.bunker) if x is None
][0]
source_stacks = [(board.Position.Bunker, i) for i in bunker_dragons]
source_stacks.extend([(board.Position.Field, i) for i in field_dragons])
yield board_actions.DragonKillAction(
dragon=dragon,
source_stacks=source_stacks,
destination_bunker_id=destination_bunker_id,
)
def possible_bunkerize_actions(
search_board: board.Board,
) -> Iterator[board_actions.BunkerizeAction]:
"""Enumerates all possible card moves from the field to the bunker"""
open_bunker_list = [i for i, x in enumerate(search_board.bunker) if x is None]
if not open_bunker_list:
return
open_bunker = open_bunker_list[0]
for index, stack in enumerate(search_board.field):
if not stack:
continue
yield board_actions.BunkerizeAction(
card=stack[-1],
field_id=index,
field_row_index=len(stack) - 1,
bunker_id=open_bunker,
to_bunker=True,
)
def possible_debunkerize_actions(
search_board: board.Board,
) -> Iterator[board_actions.BunkerizeAction]:
"""Enumerates all possible card moves from the bunker to the field"""
bunker_number_cards = [
(i, x)
for i, x in enumerate(search_board.bunker)
if isinstance(x, board.NumberCard)
]
for index, card in bunker_number_cards:
for other_index, other_stack in enumerate(search_board.field):
if not other_stack:
continue
if not isinstance(other_stack[-1], board.NumberCard):
continue
if other_stack[-1].suit == card.suit:
continue
if other_stack[-1].number != card.number + 1:
continue
yield board_actions.BunkerizeAction(
card=card,
bunker_id=index,
field_id=other_index,
field_row_index=len(other_stack),
to_bunker=False,
)
def possible_goal_move_actions(
search_board: board.Board,
) -> Iterator[board_actions.GoalAction]:
"""Enumerates all possible moves from anywhere to the goal"""
field_cards = [
(board.Position.Field, index, stack[-1])
for index, stack in enumerate(search_board.field)
if stack
]
bunker_cards = [
(board.Position.Bunker, index, card)
for index, card in enumerate(search_board.bunker)
]
top_cards = [
x for x in field_cards + bunker_cards if isinstance(x[2], board.NumberCard)
]
top_cards = [
x for x in top_cards if x[2].number == search_board.getGoal(x[2].suit) + 1
]
result = []
for source, index, card in top_cards:
obvious = all(
search_board.getGoal(other_suit) >= card.number - 2
for other_suit in set(board.NumberCard.Suit) - {card.suit}
)
result.append(
board_actions.GoalAction(
card=card,
source_id=index,
source_row_index=len(search_board.field[index]) - 1
if source == board.Position.Field
else None,
source_position=source,
goal_id=search_board.getGoalId(card.suit),
obvious=obvious,
)
)
break
yield from sorted(result, key=lambda x: x.card.number)
def _can_stack(bottom: board.Card, top: board.Card) -> bool:
if not isinstance(bottom, board.NumberCard):
return False
if not isinstance(top, board.NumberCard):
return False
if bottom.suit == top.suit:
return False
if bottom.number != top.number + 1:
return False
return True
def _get_cardstacks(search_board: board.Board) -> List[List[board.Card]]:
"""Returns all cards on one stack that can be moved at once"""
result: List[List[board.Card]] = []
for stack in search_board.field:
result.append([])
if not stack:
continue
result[-1].append(stack[-1])
for card in stack[-2::-1]:
if not _can_stack(card, result[-1][0]):
break
if not isinstance(card, board.NumberCard):
break
result[-1].insert(0, card)
return result
def possible_field_move_actions(
search_board: board.Board,
) -> Iterator[board_actions.MoveAction]:
"""Enumerate all possible move actions
from one field stack to another field stack"""
first_empty_field_id = -1
cardstacks = [x for x in enumerate(_get_cardstacks(search_board)) if x[1]]
cardstacks = sorted(cardstacks, key=lambda x: len(x[1]))
substacks: List[Tuple[int, List[board.Card]]] = []
for index, stack in cardstacks:
substacks.extend(
(index, substack) for substack in (stack[i:] for i in range(len(stack)))
)
for source_index, source_substack in substacks:
for destination_index, destination_stack in enumerate(search_board.field):
if source_index == destination_index:
continue
if destination_stack:
if not _can_stack(destination_stack[-1], source_substack[0]):
continue
elif len(source_substack) == len(search_board.field[source_index]):
continue
elif first_empty_field_id == -1:
first_empty_field_id = destination_index
elif destination_index != first_empty_field_id:
continue
yield board_actions.MoveAction(
cards=source_substack,
source_id=source_index,
source_row_index=len(search_board.field[source_index])
- len(source_substack),
destination_id=destination_index,
destination_row_index=len(destination_stack),
)
def possible_actions(search_board: board.Board) -> List[board_actions.Action]:
"""Enumerate all possible actions on the current search_board"""
result: List[board_actions.Action] = [
*list(possible_huakill_action(search_board)),
*list(possible_dragonkill_actions(search_board)),
*list(possible_goal_move_actions(search_board)),
*list(possible_debunkerize_actions(search_board)),
*list(possible_field_move_actions(search_board)),
*list(possible_bunkerize_actions(search_board)),
]
for action in result:
if action.automatic():
return [action]
return result

View File

@@ -1,129 +0,0 @@
"""Contains solver for solitaire"""
import typing
from typing import Iterator, List, Optional
import time
from dataclasses import dataclass
from ..board import Board
from . import board_actions
from .board_actions import DragonKillAction, GoalAction, HuaKillAction, MoveAction
from .board_possibilities import possible_actions
@dataclass
class ActionStackFrame:
iterator: Iterator[board_actions.Action]
last_action: Optional[board_actions.Action]
state: int
def next(self) -> Optional[board_actions.Action]:
"""Get next iteration of top action iterator"""
try:
self.last_action = next(self.iterator)
except StopIteration:
return None
return self.last_action
class ActionStack:
"""Stack of chosen actions on the board"""
def __init__(self) -> None:
self.frames: List[ActionStackFrame] = []
def push(self, board: Board) -> None:
"""Append another board state to stack"""
self.frames.append(
ActionStackFrame(
iterator=iter(possible_actions(board)),
last_action=None,
state=board.state_identifier,
)
)
@property
def top(self) -> ActionStackFrame:
"""Get next iteration of top action iterator"""
return self.frames[-1]
def pop(self) -> None:
"""Pop one action from stack"""
self.frames.pop()
def __len__(self) -> int:
return len(self.frames)
def solve(
board: Board, *, timeout: Optional[float] = None, verbose: bool = False
) -> Iterator[List[board_actions.Action]]:
"""Solve a solitaire puzzle"""
state_set = {board.state_identifier}
stack = ActionStack()
stack.push(board)
def _limit_stack_size(stack_size: int) -> None:
if len(stack) == stack_size:
stack.pop()
assert stack.top.last_action is not None
stack.top.last_action.undo(board)
assert board.state_identifier in state_set
def _backtrack_action() -> None:
stack.pop()
assert stack.top.last_action is not None
stack.top.last_action.undo(board)
assert board.state_identifier in state_set
def _skip_loop_move(action: board_actions.Action) -> bool:
if not isinstance(action, MoveAction):
return False
for frame in stack.frames[-2::-1]:
if not isinstance(frame.last_action, MoveAction):
continue
if frame.last_action.cards == action.cards:
return True
return False
iter_start = time.time()
count = 0
while len(stack) > 0:
count += 1
if count > 5000:
count = 0
if verbose:
print(f"{time.time() - iter_start} {len(stack)} {board.goal}")
if timeout is not None and time.time() - iter_start > timeout:
return
# _limit_stack_size(80)
assert board.state_identifier == stack.top.state
action = stack.top.next()
if action is None:
_backtrack_action()
continue
if _skip_loop_move(action):
continue
action.apply(board)
if board.solved():
assert all(x.last_action is not None for x in stack.frames)
yield [
typing.cast(board_actions.Action, x.last_action) for x in stack.frames
]
iter_start = time.time()
action.undo(board)
assert board.state_identifier in state_set
continue
if board.state_identifier in state_set:
action.undo(board)
assert board.state_identifier in state_set
continue
state_set.add(board.state_identifier)
stack.push(board)

View File

@@ -1,19 +1,19 @@
import argparse
import os
import subprocess
import tempfile
import time
from pathlib import Path
from typing import List
from typing import List, Dict, Any
import json
import cv2
import numpy as np
import pyautogui
import shenzhen_solitaire.card_detection.configuration as configuration
import shenzhen_solitaire.clicker as clicker
import shenzhen_solitaire.solver.solver as solver
from shenzhen_solitaire.board import Board
from shenzhen_solitaire.card_detection.board_parser import parse_start_board
from shenzhen_solitaire.solver.board_actions import Action
OFFSET = (0, 0)
# SIZE = (2560, 1440)
@@ -22,39 +22,29 @@ NEW_BUTTON = (1900, 1100)
SAVE_UNSOLVED = False
UNSOLVED_DIR = "E:/shenzhen-solitaire/unsolved"
SOLVER_PATH = '/home/lukas/documents/coding/rust/shenzhen-solitaire/target/release/solver'
def extern_solve(board: Board) -> List[Dict[str, Any]]:
result = subprocess.run([SOLVER_PATH], input=board.to_json(), capture_output=True, text=True)
return json.loads(result.stdout)
def extern_solve(board: Board) -> List[Action]:
pass
def solve(conf: configuration.Configuration) -> None:
def take_screenshot() :
with tempfile.TemporaryDirectory(prefix="shenzhen_solitaire") as screenshot_dir:
print("Taking screenshot")
screenshot_file = Path(screenshot_dir) / "screenshot.png"
screenshot = pyautogui.screenshot(region=(*OFFSET, *SIZE))
screenshot.save(screenshot_file)
image = cv2.imread(str(screenshot_file))
input()
return image
print("Solving")
def solve(conf: configuration.Configuration) -> None:
image = take_screenshot()
board = parse_start_board(image, conf)
print(board.to_json())
assert board.check_correct()
input()
solution_iterator = next(solver.solve(board, timeout=10, verbose=True), None)
if solution_iterator is None:
clicker.click(NEW_BUTTON, OFFSET)
time.sleep(10)
if SAVE_UNSOLVED:
fd, outfile = tempfile.mkstemp(dir=UNSOLVED_DIR, suffix=".png")
sock = os.fdopen(fd, "w")
sock.close()
cv2.imwrite(outfile, image)
return
solution = list(solution_iterator)
print(f"Solved in {len(solution)} steps")
clicker.handle_actions(solution, OFFSET, conf)
actions = extern_solve(board)
assert 0
print(f"Solved in {len(actions)} steps")
clicker.handle_actions(actions, OFFSET, conf)
print("Solved")
time.sleep(2)
clicker.click(NEW_BUTTON, OFFSET)
@@ -62,8 +52,18 @@ def solve(conf: configuration.Configuration) -> None:
def main() -> None:
parser = argparse.ArgumentParser(
description="Solve board"
)
parser.add_argument(
"config_path",
type=str,
help="Config path",
)
args = parser.parse_args()
time.sleep(3)
conf = configuration.load("test_config.zip")
conf = configuration.load(args.config_path)
while True:
solve(conf)

View File

@@ -1,41 +0,0 @@
import copy
import dataclasses
import json
import cv2
import numpy as np
import shenzhen_solitaire.card_detection.adjustment as adjustment
import shenzhen_solitaire.card_detection.card_finder as card_finder
from shenzhen_solitaire.card_detection.configuration import Configuration
import argparse
def main() -> None:
"""Generate a configuration"""
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('screenshot_path', metavar='screenshot_path', type=str,
help='Path to the screenshot')
args = parser.parse_args()
print(args.screenshot_path)
image = cv2.imread(args.screenshot_path)
border_adjustment = adjustment.adjust_squares(image, count_x=8, count_y=13)
border_square_pos = adjustment.adjust_squares(
image, count_x=1, count_y=1, adjustment=copy.deepcopy(border_adjustment)
)
border_square = card_finder.get_field_squares(image, border_square_pos, 1, 1)
empty_square_pos = adjustment.adjust_squares(
image, count_x=1, count_y=1, adjustment=copy.deepcopy(border_adjustment)
)
empty_square = card_finder.get_field_squares(image, empty_square_pos, 1, 1)
cv2.imwrite("/tmp/border_square.png", border_square[0])
cv2.imwrite("/tmp/empty_square.png", empty_square[0])
print(json.dumps(dataclasses.asdict(border_adjustment)))
if __name__ == "__main__":
main()

View File

@@ -1,43 +0,0 @@
import copy
import dataclasses
import json
import cv2
import numpy as np
import shenzhen_solitaire.card_detection.adjustment as adjustment
import shenzhen_solitaire.card_detection.card_finder as card_finder
from shenzhen_solitaire.card_detection.configuration import Configuration
def main() -> None:
"""Generate a configuration"""
image = cv2.imread("pictures/specific/BunkerCards.jpg")
bunker_adjustment = adjustment.adjust_squares(
image,
count_x=3,
count_y=1,
adjustment=adjustment.Adjustment(
**{"x": 730, "y": 310, "w": 19, "h": 21, "dx": 152, "dy": 0}
),
)
print(json.dumps(dataclasses.asdict(bunker_adjustment)))
back_image = cv2.imread("pictures/specific/BaiShiny.jpg")
back_squares = card_finder.get_field_squares(
back_image, count_x=1, count_y=3, adjustment=copy.deepcopy(bunker_adjustment)
)
green_image = cv2.imread("pictures/20190809172213_1.jpg")
green_squares = card_finder.get_field_squares(
green_image, count_x=1, count_y=3, adjustment=copy.deepcopy(bunker_adjustment)
)
cv2.imwrite("/tmp/bunker_green_1.png", green_squares[0])
cv2.imwrite("/tmp/bunker_green_2.png", green_squares[1])
cv2.imwrite("/tmp/bunker_green_3.png", green_squares[2])
if __name__ == "__main__":
main()

View File

@@ -2,7 +2,7 @@ import argparse
import cv2
import numpy as np
import copy
from shenzhen_solitaire.card_detection import configuration, adjustment, card_finder
from shenzhen_solitaire.card_detection.configuration import Configuration
@@ -31,11 +31,19 @@ def main() -> None:
args = parser.parse_args()
image = cv2.imread(args.screenshot_path)
conf = configuration.load(args.config_path)
squares = card_finder.get_field_squares(image, conf.field_adjustment, 5, 8)
catalogue = card_finder.catalogue_cards(squares)
conf.catalogue.extend(catalogue)
conf.card_border.extend(
card_finder.get_field_squares(image, conf.border_adjustment, 1, 1)
)
empty_adjust = copy.deepcopy(conf.border_adjustment)
empty_adjust.y = empty_adjust.y + 4 * empty_adjust.dy
conf.empty_card.extend(card_finder.get_field_squares(image, empty_adjust, 1, 1))
conf.green_card.extend(
card_finder.get_field_squares(image, conf.bunker_adjustment, 1, 3)
)
@@ -45,7 +53,7 @@ def main() -> None:
conf.green_card.extend(
card_finder.get_field_squares(image, conf.hua_adjustment, 1, 1)
)
conf.catalogue.extend(catalogue)
configuration.save(conf, args.config_path)

View File

@@ -1,43 +0,0 @@
import argparse
import cv2
import numpy as np
from shenzhen_solitaire.card_detection import configuration, adjustment, card_finder
from shenzhen_solitaire.card_detection.configuration import Configuration
def main() -> None:
"""Generate a configuration"""
parser = argparse.ArgumentParser(
description="Generate pictures for symbols, "
"requires screenshot of field with no moved cards, "
"so 8 columns of 5 cards each"
)
parser.add_argument(
"screenshot_path",
metavar="screenshot_path",
type=str,
help="Path to the screenshot",
)
parser.add_argument(
"--conf",
dest="config_path",
type=str,
default="config.zip",
help="Path to existing config to be merged, or new config",
)
args = parser.parse_args()
print(args.screenshot_path)
image = cv2.imread(args.screenshot_path)
adj = adjustment.adjust_field(image)
squares = card_finder.get_field_squares(image, adj, 5, 8)
catalogue = card_finder.catalogue_cards(squares)
generated_config = Configuration(field_adjustment=adj, catalogue=catalogue, meta={})
configuration.save(generated_config, args.config_path)
if __name__ == "__main__":
main()

View File

@@ -1,38 +0,0 @@
import copy
import dataclasses
import json
import cv2
import numpy as np
import shenzhen_solitaire.card_detection.adjustment as adjustment
import shenzhen_solitaire.card_detection.card_finder as card_finder
from shenzhen_solitaire.card_detection.configuration import Configuration
def main() -> None:
"""Generate a configuration"""
image = cv2.imread("pictures/specific/BaiShiny.jpg")
goal_adjustment = adjustment.adjust_squares(
image,
count_x=3,
count_y=1,
adjustment=adjustment.Adjustment(
**{"x": 1490, "y": 310, "w": 19, "h": 21, "dx": 152, "dy": 0}
),
)
print(json.dumps(dataclasses.asdict(goal_adjustment)))
green_image = cv2.imread("pictures/20190809172213_1.jpg")
green_squares = card_finder.get_field_squares(
green_image, count_x=1, count_y=3, adjustment=copy.deepcopy(goal_adjustment)
)
cv2.imwrite("/tmp/goal_green_1.png", green_squares[0])
cv2.imwrite("/tmp/goal_green_2.png", green_squares[1])
cv2.imwrite("/tmp/goal_green_3.png", green_squares[2])
if __name__ == "__main__":
main()

View File

@@ -1,34 +0,0 @@
import copy
import dataclasses
import json
import cv2
import numpy as np
import shenzhen_solitaire.card_detection.adjustment as adjustment
import shenzhen_solitaire.card_detection.card_finder as card_finder
from shenzhen_solitaire.card_detection.configuration import Configuration
def main() -> None:
"""Generate a configuration"""
image = cv2.imread("pictures/specific/BunkerCards.jpg")
hua_adjustment = adjustment.adjust_squares(
image,
count_x=1,
count_y=1,
adjustment=adjustment.Adjustment(
**{"x": 1299, "y": 314, "w": 19, "h": 21, "dx": 0, "dy": 0}
),
)
print(json.dumps(dataclasses.asdict(hua_adjustment)))
green_image = cv2.imread("pictures/specific/ZhongShiny.jpg")
hua_green = card_finder.get_field_squares(
green_image, hua_adjustment, count_x=1, count_y=1
)
cv2.imwrite("/tmp/hua_green.png", hua_green[0])
if __name__ == "__main__":
main()

View File

@@ -1,14 +1,21 @@
from shenzhen_solitaire.card_detection.board_parser import parse_to_json
import shenzhen_solitaire.card_detection.configuration as configuration
import cv2
import argparse
import sys
import cv2
import shenzhen_solitaire.card_detection.configuration as configuration
from shenzhen_solitaire.card_detection.board_parser import parse_to_json
def main() -> None:
if len(sys.argv) < 2:
print("Give filename pls")
return
image = cv2.imread(str(sys.argv[1]))
parser = argparse.ArgumentParser(description="Parse board to json")
parser.add_argument("board_path", type=str, help="Path to image of board")
parser.add_argument(
"--config", dest="config_path", type=str, help="Config path",
)
args = parser.parse_args()
image = cv2.imread(args.board_path)
conf = configuration.load("test_config.zip")