Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# VSCode
.vs/
86 changes: 86 additions & 0 deletions data/src/HexBug/data/special_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

VALID_MASK_PATTERN = re.compile(r"[-v]+")
VALID_HEXFLOW_COPY_MASK_PATTERN = re.compile(r"[-n]+")
VALID_SWIZZLE_PATTERN = re.compile(r"[xyz]{3}")


class SpecialHandlerInfo(BaseModel):
Expand Down Expand Up @@ -657,3 +658,88 @@ def parse_tail_depth(value: str):
if all(c == "-" for c in value):
return len(value)
raise ValueError(f"Invalid tail depth (expected an integer or dashes): {value}")


class HextrapatsSwizzlingSpecialHandler(SpecialHandler[str]):
prefix = "eeeeqaawddea"

@override
def try_match(self, registry: HexBugRegistry, pattern: HexPattern) -> str | None:
if pattern.signature.startswith(self.prefix):
flat_dir = pattern.direction.rotated_by(HexAngle.BACK)

result = ""
side = 0

for direction in itertools.islice(pattern.iter_directions(), 13, None):
match direction.angle_from(flat_dir):
case HexAngle.FORWARD if side == 0:
result += "Y"
case HexAngle.RIGHT:
if side == 0:
side = 1
elif side == -1:
side = 0
result += "X"
case HexAngle.LEFT:
if side == 0:
side = -1
elif side == 1:
side = 0
result += "Z"
case _:
return None

if side != 0 or len(result) != 3:
return None

return result

def generate_pattern(
self,
registry: HexBugRegistry,
value: str,
) -> tuple[str, HexPattern]:
value = value.lower().strip()
if not VALID_SWIZZLE_PATTERN.fullmatch(value):
raise ValueError(
f"Invalid swizzle (expected exactly 3 of x, y, or z): {value}"
)

signature = self.prefix
current = HexDir.NORTH_EAST

# Surely there's a better way to do this
for c in value:
match c:
case "x":
match current:
case HexDir.NORTH_EAST:
signature += "wd"
case HexDir.EAST:
signature += "qd"
case HexDir.SOUTH_EAST:
signature += "ad"
current = HexDir.SOUTH_EAST
case "y":
match current:
case HexDir.NORTH_EAST:
signature += "e"
case HexDir.EAST:
signature += "w"
case HexDir.SOUTH_EAST:
signature += "q"
current = HexDir.EAST
case "z":
match current:
case HexDir.NORTH_EAST:
signature += "da"
case HexDir.EAST:
signature += "ea"
case HexDir.SOUTH_EAST:
signature += "wa"
current = HexDir.NORTH_EAST
case _:
raise RuntimeError("unreachable")

return value, HexPattern(HexDir.WEST, signature)
47 changes: 47 additions & 0 deletions data/test/data/test_special_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from HexBug.data.registry import HexBugRegistry
from HexBug.data.special_handlers import (
HexFlowCopyMaskSpecialHandler,
HextrapatsSwizzlingSpecialHandler,
MaskSpecialHandler,
)

Expand Down Expand Up @@ -130,3 +131,49 @@ def generate_pattern(
registry = cast(HexBugRegistry, None) # lie
_, pattern = special_handler.generate_pattern(registry, value)
assert pattern.signature == want_signature


def describe_HextrapatsSwizzlingSpecialHandler():
@pytest.fixture()
def special_handler() -> HextrapatsSwizzlingSpecialHandler:
return HextrapatsSwizzlingSpecialHandler(
id=ResourceLocation("hextrapats", "vec/swizzle"),
)

patterns = [
("eeeeqaawddeawdadad", "XXX"),
("eeeeqaawddeaeww", "YYY"),
("eeeeqaawddeadadada", "ZZZ"),
("eeeeqaawddeawdqea", "XYZ"),
("eeeeqaawddeaeeae", "YZY"),
("eeeeqaawddeawdadwa", "XXZ"),
]

@pytest.mark.parametrize("direction", HexDir)
@pytest.mark.parametrize(
["signature", "want_value"],
[*patterns, ("eeeeqaawddeadaww", None), ("eeeeqaawddeaeweawd", None)],
ids=lambda v: f"'{v}'",
)
def try_match(
special_handler: HextrapatsSwizzlingSpecialHandler,
direction: HexDir,
signature: str,
want_value: str | None,
):
registry = cast(HexBugRegistry, None) # lie
pattern = HexPattern(direction, signature)
assert special_handler.try_match(registry, pattern) == want_value

@pytest.mark.parametrize(
["value", "want_signature"],
[(value, signature) for (signature, value) in patterns],
)
def generate_pattern(
special_handler: HextrapatsSwizzlingSpecialHandler,
value: str,
want_signature: str,
):
registry = cast(HexBugRegistry, None) # lie
_, pattern = special_handler.generate_pattern(registry, value)
assert pattern.signature == want_signature