Anfang spiellogik

This commit is contained in:
CheesePlated
2026-05-14 14:52:05 +02:00
parent a25bd700c9
commit 5fedf513fc

178
src/net.py Normal file
View File

@@ -0,0 +1,178 @@
"""
Kernlogik des Spiels
"""
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 = auto()
LEFT = auto()
DOWN = auto()
RIGHT = 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),
],
]
class Grid:
pieces: list[list[Piece]]
width: int
height: int
def __init__(self, width: int, height: int) -> None:
self.height = height
self.width = width
self.pieces = (
DEMO_FIELD # TODO: Field generation or import from Tatham's version
)
def neighbors(self, x: int, y: int) -> list[Coordinate]:
neighbors: list[Coordinate] = []
for dx, dy in ((-1, 0), (1, 0), (0, -1), (0, 1)):
if (
x + dx < 0
or x + dx >= self.width
or y + dy < 0
or y + dy >= self.height
):
continue
neighbors.append((x + dx, y + dy))
return neighbors
def solved(self) -> bool:
connected = [([False] * self.height) for _ in range(self.width)]
connected = self._solve_floodfill(0, 0, connected)
return all(map(lambda x: all(x), connected))
def _solve_floodfill(
self, x: int, y: int, connected: list[list[bool]]
) -> list[list[bool]]:
connected[x][y] = True
connectedNeighbors: list[Coordinate] = []
for nx, ny in self.neighbors(x, y):
dx = x - nx
dy = y - ny
if (
Direction.from_offset(-dx, -dy)
in self.pieces[x + dx][y + dy].connected_directions()
):
connectedNeighbors.append((x + dx, y + dy))
for nx, ny in connectedNeighbors:
if connected[nx][ny]:
continue
connected = self._solve_floodfill(nx, ny, connected)
return connected
class NetGame:
_grid: Grid
def __init__(self, width: int, height: int) -> None:
self._grid = Grid(width, height)
def get_field(self) -> list[list[Piece]]:
return self._grid.pieces
def get_piece(self, x: int, y: int) -> Piece:
return self._grid.pieces[x][y]
def turn_cw(self, x: int, y: int) -> None:
self._grid.pieces[x][y].turn_cw()
def turn_ccw(self, x: int, y: int) -> None:
self._grid.pieces[x][y].turn_ccw()
def solved(self):
return self._grid.solved()
g = NetGame(5, 5)
print(g.solved())