diff --git a/src/algorithms/profile.py b/src/algorithms/profile.py index 7383547..457edac 100644 --- a/src/algorithms/profile.py +++ b/src/algorithms/profile.py @@ -1,13 +1,14 @@ import time from multiprocessing import Pool +from src.algorithms.wfc import WFCSolver from src.algorithms.bruteforce import BruteForceSolver from src.net import NetGame -def test_run() -> float: - game = NetGame(3, 3) - solver = BruteForceSolver(game) +def test_run(i: int) -> float: + game = NetGame(5, 5, i) + solver = WFCSolver(game) a = time.perf_counter() for _ in solver.solve(): pass @@ -18,7 +19,7 @@ def test_run() -> float: if __name__ == "__main__": total = 0 with Pool() as p: - processes = [p.apply_async(test_run) for _ in range(100)] + processes = [p.apply_async(test_run, (i,)) for i in range(1000)] for proc in processes: total += proc.get() - print(total) + print(total / 1000) diff --git a/src/algorithms/wfc.py b/src/algorithms/wfc.py index 3e76979..dc06671 100644 --- a/src/algorithms/wfc.py +++ b/src/algorithms/wfc.py @@ -1,4 +1,5 @@ -from netTypes import Direction, PieceType +from collections.abc import Generator +from src.netTypes import Direction, PieceType from src.net import NetGame @@ -36,13 +37,13 @@ class WFCSolver: return dirs elif ptype == PieceType.CORNER: possible: set[Direction] = set() - if dirs.union({Direction.UP, Direction.RIGHT}): + if dirs.issuperset({Direction.UP, Direction.RIGHT}): possible.add(Direction.UP) - if dirs.union({Direction.RIGHT, Direction.DOWN}): + if dirs.issuperset({Direction.RIGHT, Direction.DOWN}): possible.add(Direction.RIGHT) - if dirs.union({Direction.DOWN, Direction.LEFT}): + if dirs.issuperset({Direction.DOWN, Direction.LEFT}): possible.add(Direction.DOWN) - if dirs.union({Direction.LEFT, Direction.UP}): + if dirs.issuperset({Direction.LEFT, Direction.UP}): possible.add(Direction.LEFT) return possible elif ptype == PieceType.STRAIGHT: @@ -67,56 +68,33 @@ class WFCSolver: 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: + def solve(self) -> Generator[None]: coordinates = list( ((i, j) for i in range(self.game.width) for j in range(self.game.height)) ) while not self.game.solved(): + yield (x, y) = min( - coordinates, key=lambda t: self.get_possible_direction_count(*t) + coordinates, key=lambda t: len(self.get_possible_directions(*t)) ) - if self.get_possible_direction_count(x, y) == 0: + if len(self.get_possible_directions(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 + self.game.lock(x, y) + coordinates.remove((x, y)) + for nx, ny in self.game.neighbors(x, y): + dx = nx - x + dy = ny - y + ndir = Direction.from_offset(dx, dy) + if ndir not in self.game.get_piece(x, y).connected_directions(): + try: + self.possible_connections[nx][ny].remove( + Direction((ndir + 2) % 4) + ) + except KeyError: + pass + elif self.game.get_piece(nx, ny).type == PieceType.NODE: + self.possible_connections[nx][ny] = {Direction((ndir + 2) % 4)} diff --git a/src/net.py b/src/net.py index 3e43ee7..1795991 100644 --- a/src/net.py +++ b/src/net.py @@ -13,14 +13,17 @@ class Grid: width: int height: int - def __init__(self, width: int, height: int) -> None: + def __init__(self, width: int, height: int, specific: int | None = None) -> None: self.height = height self.width = width if width != height or width not in [3, 5, 7, 9, 11, 13]: raise ValueError("Feldgrösse nicht erlaubt") with open(f"descriptions/{width}x{height}.txt") as f: lines = f.readlines() - selected = choice(lines).strip() + if specific: + selected = lines[specific].strip() + else: + selected = choice(lines).strip() self.pieces = parse_description(selected) def neighbors(self, x: int, y: int) -> list[Coordinate]: @@ -71,8 +74,8 @@ class NetGame: width: int height: int - def __init__(self, width: int, height: int) -> None: - self._grid = Grid(width, height) + def __init__(self, width: int, height: int, specific: int | None = None) -> None: + self._grid = Grid(width, height, specific) self.width = width self.height = height @@ -94,5 +97,8 @@ class NetGame: def set_direction(self, x: int, y: int, dir: Direction) -> None: self._grid.pieces[x][y].direction = dir + def neighbors(self, x: int, y: int) -> list[Coordinate]: + return self._grid.neighbors(x, y) + def solved(self): return self._grid.solved()