Anfang spiellogik
This commit is contained in:
178
src/net.py
Normal file
178
src/net.py
Normal 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())
|
||||||
Reference in New Issue
Block a user