242 lines
7.8 KiB
Python
242 lines
7.8 KiB
Python
from typing import Dict, List, Set, Tuple
|
|
import yaml
|
|
import dacite
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass
|
|
class FingerTrick:
|
|
name: str
|
|
move: str
|
|
grip_pre_blacklist: Dict[str, List[str]]
|
|
grip_pre: Dict[str, str]
|
|
grip_post: Dict[str, str]
|
|
score: float
|
|
|
|
|
|
@dataclass
|
|
class Grip:
|
|
finger: str
|
|
pre: str
|
|
post: str
|
|
score: float
|
|
|
|
|
|
@dataclass
|
|
class Regrip:
|
|
finger: str
|
|
start: str
|
|
end: str
|
|
score: float
|
|
|
|
|
|
@dataclass
|
|
class Finger:
|
|
name: str
|
|
default_score: int
|
|
home_grips: List[str]
|
|
grips: Dict[str, List[str]]
|
|
regrips: List[Regrip]
|
|
|
|
|
|
@dataclass
|
|
class Config:
|
|
fingers: List[Finger]
|
|
finger_tricks: List[FingerTrick]
|
|
|
|
|
|
@dataclass
|
|
class Definitions:
|
|
fingers: List[Finger]
|
|
finger_tricks: List[FingerTrick]
|
|
finger_regrips: List[Regrip]
|
|
|
|
|
|
def build_missing_regrips(
|
|
finger_regrips: Dict[Tuple[str, str], Regrip], finger: Finger, grips: Set[str]
|
|
) -> Dict[Tuple[str, str], Regrip]:
|
|
# this might just be the worst piece of code I have ever written
|
|
# fill in missing regrips so every pair of grips has a regrip
|
|
for grip_start in grips:
|
|
for grip_end in grips:
|
|
if grip_start != grip_end:
|
|
if (grip_start, grip_end) not in finger_regrips:
|
|
# TODO: this only works for regrips which are 2 apart
|
|
possible_start_regrips = {
|
|
grips: regrip
|
|
for grips, regrip in finger_regrips.items()
|
|
if grip_start == regrip.start
|
|
}
|
|
possible_end_regrips = {
|
|
grips: regrip
|
|
for grips, regrip in finger_regrips.items()
|
|
if grip_end == regrip.end
|
|
}
|
|
for start_regrip in possible_start_regrips.values():
|
|
for end_regrip in possible_end_regrips.values():
|
|
# if start grips ends at the beginning of end grip
|
|
if start_regrip.end == end_regrip.start:
|
|
finger_regrips[(start_regrip.start, end_regrip.end)] = (
|
|
Regrip(
|
|
finger=finger.name,
|
|
start=start_regrip.start,
|
|
end=end_regrip.end,
|
|
score=start_regrip.score + end_regrip.score,
|
|
)
|
|
)
|
|
return finger_regrips
|
|
|
|
|
|
def build_regrips_from_fingers(fingers: List[Finger]) -> List[Regrip]:
|
|
regrips = []
|
|
for finger in fingers:
|
|
finger_regrips = {}
|
|
for grip_set in finger.grips.values():
|
|
# reate a dict with distances of every two elements in a list
|
|
for i, grip_start in enumerate(grip_set):
|
|
for j, grip_end in enumerate(grip_set):
|
|
if grip_start != grip_end:
|
|
finger_regrips[(grip_start, grip_end)] = Regrip(
|
|
start=grip_start,
|
|
end=grip_end,
|
|
score=abs(j - i) * finger.default_score,
|
|
finger=finger.name,
|
|
)
|
|
for regrip in finger.regrips:
|
|
finger_regrips[(regrip.start, regrip.end)] = regrip
|
|
|
|
grips = {grip for grip_set in finger.grips.values() for grip in grip_set}
|
|
finger_regrips = build_missing_regrips(finger_regrips, finger, grips)
|
|
regrips.extend(finger_regrips.values())
|
|
|
|
return regrips
|
|
|
|
|
|
def load_definitions(file_path: str) -> Definitions:
|
|
with open(file_path, "r") as f:
|
|
data_dict = yaml.safe_load(f.read())
|
|
config = dacite.from_dict(data_class=Config, data=data_dict)
|
|
return Definitions(
|
|
fingers=config.fingers,
|
|
finger_tricks=config.finger_tricks,
|
|
finger_regrips=build_regrips_from_fingers(config.fingers),
|
|
)
|
|
|
|
|
|
def grip_correct(current_grip: Dict[str, str], required_grip: Dict[str, str]):
|
|
return all(item in current_grip.items() for item in required_grip.items())
|
|
|
|
|
|
@dataclass
|
|
class FingerTrickWithRegrip:
|
|
finger_trick: FingerTrick
|
|
regrips: List[Regrip]
|
|
|
|
def score(self) -> float:
|
|
score = 0.0
|
|
score += self.finger_trick.score
|
|
if self.regrips:
|
|
longest_regrip = max(self.regrips, key=lambda regrip: regrip.score)
|
|
score += longest_regrip.score
|
|
return score
|
|
|
|
|
|
@dataclass
|
|
class Algorithm:
|
|
score: float
|
|
finger_tricks_with_regrips: List[FingerTrickWithRegrip]
|
|
|
|
|
|
def find_finger_trick_regrips(
|
|
regrips: List[Regrip], finger_trick: FingerTrick, grip: Dict[str, str]
|
|
) -> List[Regrip]:
|
|
alg_regrips = []
|
|
for finger in finger_trick.grip_pre.keys():
|
|
current_location = grip[finger]
|
|
desired_location = finger_trick.grip_pre.get(finger)
|
|
blacklisted_locations = finger_trick.grip_pre_blacklist.get(finger, [])
|
|
if current_location != desired_location:
|
|
# print("FINGA", finger)
|
|
# print("CURRENT", current_location)
|
|
# print("DESIRED", desired_location)
|
|
# print("BLACK", blacklisted_locations)
|
|
# __import__("pprint").pprint(regrips)
|
|
alg_regrip = next(
|
|
(
|
|
regrip
|
|
for regrip in regrips
|
|
if regrip.finger == finger
|
|
and regrip.start == current_location
|
|
and regrip.end == desired_location
|
|
and regrip.start not in blacklisted_locations
|
|
),
|
|
)
|
|
if alg_regrip:
|
|
alg_regrips.append(alg_regrip)
|
|
return alg_regrips
|
|
|
|
|
|
def generate_home_grip(fingers: List[Finger]) -> Dict[str, str]:
|
|
# TODO: make this smarter
|
|
home_grip = {}
|
|
for finger in fingers:
|
|
home_grip[finger.name] = finger.home_grips[0]
|
|
return home_grip
|
|
|
|
|
|
def generate_finger_tricks(
|
|
definitions: Definitions, moves: List[str]
|
|
) -> List[FingerTrickWithRegrip]:
|
|
grip = generate_home_grip(definitions.fingers)
|
|
alg: List[FingerTrickWithRegrip] = []
|
|
for move in moves:
|
|
# print("current grip:", grip)
|
|
# prit("current move:", move)
|
|
possible_finger_tricks = [
|
|
finger_trick
|
|
for finger_trick in definitions.finger_tricks
|
|
if move == finger_trick.move
|
|
]
|
|
# print("possible finger tricks:", possible_finger_tricks)
|
|
finger_tricks_with_regrips = [
|
|
FingerTrickWithRegrip(
|
|
finger_trick=finger_trick,
|
|
regrips=find_finger_trick_regrips(
|
|
definitions.finger_regrips, finger_trick, grip
|
|
),
|
|
)
|
|
for finger_trick in possible_finger_tricks
|
|
]
|
|
best_finger_trick = min(
|
|
finger_tricks_with_regrips, key=lambda item: item.score()
|
|
)
|
|
# print("best finger trick:", best_finger_trick)
|
|
# apply regrips
|
|
for regrip in best_finger_trick.regrips:
|
|
grip[regrip.finger] = regrip.end
|
|
# apply move
|
|
grip.update(best_finger_trick.finger_trick.grip_post)
|
|
alg.append(best_finger_trick)
|
|
|
|
# TODO: think about this
|
|
# don't count the first regrip
|
|
# alg[0].finger_trick.score = 0
|
|
return alg
|
|
|
|
|
|
def build_pretty_string_from_finger_tricks_with_regrips(
|
|
finger_tricks_with_regrips: List[FingerTrickWithRegrip],
|
|
) -> List[str]:
|
|
elems = []
|
|
|
|
for finger_trick_with_regrips in finger_tricks_with_regrips:
|
|
for regrip in finger_trick_with_regrips.regrips:
|
|
elems.append(
|
|
f"regrip {regrip.finger} from {regrip.start} to {regrip.end} ({regrip.score})"
|
|
)
|
|
elems.append(
|
|
f"{finger_trick_with_regrips.finger_trick.name} ({finger_trick_with_regrips.finger_trick.score})"
|
|
)
|
|
|
|
return elems
|