From f3a6257cd4f4399eb49abc5b0c8eadb67d9310a4 Mon Sep 17 00:00:00 2001 From: Alfred Baumann Date: Tue, 9 Jun 2026 16:16:44 +0200 Subject: [PATCH] anfang des wave function collapse algorithmus --- src/algorithms/wfc.py | 122 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 src/algorithms/wfc.py diff --git a/src/algorithms/wfc.py b/src/algorithms/wfc.py new file mode 100644 index 0000000..3e76979 --- /dev/null +++ b/src/algorithms/wfc.py @@ -0,0 +1,122 @@ +from netTypes import Direction, PieceType +from src.net import NetGame + + +class WFCSolver: + game: NetGame + observed: list[list[bool]] + possible_connections: list[list[set[Direction]]] + + def __init__(self, game: NetGame) -> None: + self.game = game + self.observed = [[False] * self.game.width for _ in range(self.game.height)] + self.possible_connections = [ + [ + {Direction.UP, Direction.RIGHT, Direction.DOWN, Direction.LEFT} + for _ in range(self.game.width) + ] + for _ in range(self.game.width) + ] + + # Es kann nicht mit dem Rand des Feldes eine Verbindung bestehen + for column in self.possible_connections: + column[0].remove(Direction.UP) + column[-1].remove(Direction.DOWN) + + for i in range(len(self.possible_connections[0])): + self.possible_connections[0][i].remove(Direction.LEFT) + + for i in range(len(self.possible_connections[-1])): + self.possible_connections[-1][i].remove(Direction.RIGHT) + + def get_possible_directions(self, x: int, y: int) -> set[Direction]: + ptype = self.game.get_piece(x, y).type + dirs = self.possible_connections[x][y] + if ptype == PieceType.NODE: + return dirs + elif ptype == PieceType.CORNER: + possible: set[Direction] = set() + if dirs.union({Direction.UP, Direction.RIGHT}): + possible.add(Direction.UP) + if dirs.union({Direction.RIGHT, Direction.DOWN}): + possible.add(Direction.RIGHT) + if dirs.union({Direction.DOWN, Direction.LEFT}): + possible.add(Direction.DOWN) + if dirs.union({Direction.LEFT, Direction.UP}): + possible.add(Direction.LEFT) + return possible + elif ptype == PieceType.STRAIGHT: + if len(dirs) == 4: + return dirs + elif {Direction.UP, Direction.DOWN}.issubset(dirs): + return {Direction.UP} + elif {Direction.LEFT, Direction.RIGHT}.issubset(dirs): + return {Direction.RIGHT} + else: + return set() + else: + if len(dirs) == 4: + return dirs + elif len(dirs) == 3: + not_connected = ( + {Direction.UP, Direction.DOWN, Direction.LEFT, Direction.RIGHT} + .difference(dirs) + .pop() + ) + return {Direction((not_connected + 2) % 4)} + else: + return set() + + def get_possible_direction_count(self, x: int, y: int) -> int: + ptype = self.game.get_piece(x, y).type + connectable = self.possible_connections[x][y] + camount = len(connectable) + if camount == 4: + return 4 + if ptype == PieceType.NODE: + return camount + elif ptype == PieceType.CORNER: + if camount == 3: + return 2 + elif ( + camount == 2 + and connectable != {Direction.UP, Direction.DOWN} + and connectable != {Direction.LEFT, Direction.RIGHT} + ): # 2 nicht-gegenüberliegende seiten + return 1 + else: + return 0 + elif ptype == PieceType.STRAIGHT: + if camount == 4: + return 4 + elif ( + camount == 3 + or connectable == {Direction.UP, Direction.DOWN} + or connectable == {Direction.LEFT, Direction.RIGHT} + ): + return 1 + else: + return 0 + else: # T-JUNCTION + if camount == 4: + return 4 + elif camount == 3: + return 1 + else: + return 0 + + def solve(self) -> None: + coordinates = list( + ((i, j) for i in range(self.game.width) for j in range(self.game.height)) + ) + while not self.game.solved(): + (x, y) = min( + coordinates, key=lambda t: self.get_possible_direction_count(*t) + ) + if self.get_possible_direction_count(x, y) == 0: + raise NotImplementedError( + "Irgendwo eine falsche Richtung gewählt, muss diese Logik noch schreiben" + ) + direction = self.get_possible_directions(x, y).pop() + self.game.set_direction(x, y, direction) + # TODO: Nachbaren updaten