From d49493b778d01281b134fa4a4bfcb097f3bc364c Mon Sep 17 00:00:00 2001 From: Alfred Baumann Date: Fri, 15 May 2026 21:27:13 +0200 Subject: [PATCH] =?UTF-8?q?parser=20f=C3=BCr=20die=20Descriptions,=20die?= =?UTF-8?q?=20Tatham's=20version=20generiert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interface.py | 64 +++++++++++++++++++++++ src/net.py | 132 ++++++++++++++--------------------------------- src/types.py | 57 ++++++++++++++++++++ 3 files changed, 161 insertions(+), 92 deletions(-) create mode 100644 src/interface.py create mode 100644 src/types.py diff --git a/src/interface.py b/src/interface.py new file mode 100644 index 0000000..ac777ba --- /dev/null +++ b/src/interface.py @@ -0,0 +1,64 @@ +from src.types import Piece, Direction, PieceType + + +# Tatham's board description format: +# {width}x{height}:{board} +# the board is a string of single-digit hex numbers +# where each number is a bitfield of which sides the piece is connected to +RIGHT = 0x1 +UP = 0x2 +LEFT = 0x4 +DOWN = 0x8 + + +def flip_direction(d: int) -> int: + return (d >> 2) | ((d << 2) & 0xC) + + +def direction_from_tatham(d: int) -> Direction: + if d == RIGHT: + return Direction.RIGHT + elif d == UP: + return Direction.UP + elif d == LEFT: + return Direction.LEFT + else: + return Direction.DOWN + + +def corner_direction_from_sides(sides: int) -> Direction: + if sides == 0x3: + return Direction.UP + elif sides == 0x6: + return Direction.LEFT + elif sides == 0xC: + return Direction.DOWN + else: + return Direction.RIGHT + + +def parse_description(desc: str) -> list[list[Piece]]: + dimensions, field = desc.split(":") + width, height = map(int, dimensions.split("x")) + parsed: list[list[Piece]] = [] + for y in range(height): + line: list[Piece] = [] + for x in range(width): + value = int(field[x * width + y], 16) + connected_sides = value.bit_count() + if connected_sides == 1: + ptype = PieceType.NODE + direction = direction_from_tatham(value) + elif connected_sides == 2: + if value == 0x5 or value >> 1 == 0x5: + ptype = PieceType.STRAIGHT + direction = direction_from_tatham(value & 0x3) + else: + ptype = PieceType.CORNER + direction = corner_direction_from_sides(value) + else: + ptype = PieceType.T_JUNCTION + direction = direction_from_tatham(flip_direction(~value & 0xF)) + line.append(Piece(ptype, direction)) + parsed.append(line) + return parsed diff --git a/src/net.py b/src/net.py index 861f4ff..bbb06b4 100644 --- a/src/net.py +++ b/src/net.py @@ -2,101 +2,49 @@ Kernlogik des Spiels """ -from enum import IntEnum, auto - -type Coordinate = tuple[int, int] +from src.types import Piece, Direction +from src.interface import parse_description -class PieceType(IntEnum): - T_JUNCTION = auto() # Direction is the piece at the "stem" of the T - STRAIGHT = auto() - CORNER = auto() # Direction is the more counter-clockwise connection - NODE = auto() +# DEMO_FIELD = [ +# [ +# Piece(PieceType.NODE), +# Piece(PieceType.NODE), +# Piece(PieceType.T_JUNCTION), +# Piece(PieceType.NODE), +# Piece(PieceType.NODE), +# ], +# [ +# Piece(PieceType.STRAIGHT), +# Piece(PieceType.NODE), +# Piece(PieceType.T_JUNCTION), +# Piece(PieceType.STRAIGHT), +# Piece(PieceType.CORNER), +# ], +# [ +# Piece(PieceType.T_JUNCTION), +# Piece(PieceType.T_JUNCTION), +# Piece(PieceType.T_JUNCTION), +# Piece(PieceType.T_JUNCTION), +# Piece(PieceType.NODE), +# ], +# [ +# Piece(PieceType.STRAIGHT), +# Piece(PieceType.NODE), +# Piece(PieceType.T_JUNCTION), +# Piece(PieceType.T_JUNCTION), +# Piece(PieceType.NODE), +# ], +# [ +# Piece(PieceType.NODE), +# Piece(PieceType.NODE), +# Piece(PieceType.CORNER), +# Piece(PieceType.CORNER), +# Piece(PieceType.CORNER), +# ], +# ] - -class Direction(IntEnum): - UP = 0 - RIGHT = auto() - DOWN = auto() - LEFT = auto() - - @staticmethod - def from_offset(dx: int, dy: int) -> Direction: - if (dx != 0 and dy != 0) or abs(dx + dy) != 1: - raise ValueError(f"({dx}, {dy}) is not a valid direction offset") - if dx != 0: - return Direction(dx % 4) - else: - return Direction(dy + 1) - - -class Piece: - type: PieceType - direction: Direction = Direction.UP - locked: bool = False - - def __init__(self, type: PieceType) -> None: - self.type = type - - def connected_directions(self) -> list[Direction]: - match self.type: - case PieceType.NODE: - return [self.direction] - case PieceType.CORNER: - return [self.direction, Direction((self.direction + 1) % 4)] - case PieceType.STRAIGHT: - return [self.direction, Direction((self.direction + 2) % 4)] - case PieceType.T_JUNCTION: - return [ - self.direction, - Direction((self.direction + 1) % 4), - Direction((self.direction - 1) % 4), - ] - - def turn_cw(self) -> None: - self.direction = Direction((self.direction + 1) % 4) - - def turn_ccw(self) -> None: - self.direction = Direction((self.direction - 1) % 4) - - -DEMO_FIELD = [ - [ - Piece(PieceType.NODE), - Piece(PieceType.NODE), - Piece(PieceType.T_JUNCTION), - Piece(PieceType.NODE), - Piece(PieceType.NODE), - ], - [ - Piece(PieceType.STRAIGHT), - Piece(PieceType.NODE), - Piece(PieceType.T_JUNCTION), - Piece(PieceType.STRAIGHT), - Piece(PieceType.CORNER), - ], - [ - Piece(PieceType.T_JUNCTION), - Piece(PieceType.T_JUNCTION), - Piece(PieceType.T_JUNCTION), - Piece(PieceType.T_JUNCTION), - Piece(PieceType.NODE), - ], - [ - Piece(PieceType.STRAIGHT), - Piece(PieceType.NODE), - Piece(PieceType.T_JUNCTION), - Piece(PieceType.T_JUNCTION), - Piece(PieceType.NODE), - ], - [ - Piece(PieceType.NODE), - Piece(PieceType.NODE), - Piece(PieceType.CORNER), - Piece(PieceType.CORNER), - Piece(PieceType.CORNER), - ], -] +DEMO_FIELD = parse_description("5x5:c7634887c213e5b8db3e69282") class Grid: diff --git a/src/types.py b/src/types.py new file mode 100644 index 0000000..da2fb87 --- /dev/null +++ b/src/types.py @@ -0,0 +1,57 @@ +from enum import IntEnum, auto + +type Coordinate = tuple[int, int] + + +class PieceType(IntEnum): + T_JUNCTION = auto() # Direction is the piece at the "stem" of the T + STRAIGHT = auto() + CORNER = auto() # Direction is the more counter-clockwise connection + NODE = auto() + + +class Direction(IntEnum): + UP = 0 + RIGHT = auto() + DOWN = auto() + LEFT = auto() + + @staticmethod + def from_offset(dx: int, dy: int) -> Direction: + if (dx != 0 and dy != 0) or abs(dx + dy) != 1: + raise ValueError(f"({dx}, {dy}) is not a valid direction offset") + if dx != 0: + return Direction(dx % 4) + else: + return Direction(dy + 1) + + +class Piece: + type: PieceType + direction: Direction + locked: bool = False + + def __init__(self, type: PieceType, direction: Direction = Direction.UP) -> None: + self.type = type + self.direction = direction + + def connected_directions(self) -> list[Direction]: + match self.type: + case PieceType.NODE: + return [self.direction] + case PieceType.CORNER: + return [self.direction, Direction((self.direction + 1) % 4)] + case PieceType.STRAIGHT: + return [self.direction, Direction((self.direction + 2) % 4)] + case PieceType.T_JUNCTION: + return [ + self.direction, + Direction((self.direction + 1) % 4), + Direction((self.direction - 1) % 4), + ] + + def turn_cw(self) -> None: + self.direction = Direction((self.direction + 1) % 4) + + def turn_ccw(self) -> None: + self.direction = Direction((self.direction - 1) % 4)