diff --git a/src/algorithms/profile.py b/src/algorithms/profile.py index 457edac..0a71a64 100644 --- a/src/algorithms/profile.py +++ b/src/algorithms/profile.py @@ -7,7 +7,7 @@ from src.net import NetGame def test_run(i: int) -> float: - game = NetGame(5, 5, i) + game = NetGame(11, 11, i) solver = WFCSolver(game) a = time.perf_counter() for _ in solver.solve(): diff --git a/src/algorithms/wfc.py b/src/algorithms/wfc.py index a4d7508..6da1a82 100644 --- a/src/algorithms/wfc.py +++ b/src/algorithms/wfc.py @@ -1,9 +1,21 @@ from collections.abc import Generator +from copy import deepcopy from dataclasses import dataclass from enum import Enum, auto from src.netTypes import Direction, PieceType from src.net import NetGame +type RollbackType = list[ + tuple[ + list[tuple[int, int]], + list[list[Direction]], + set[Direction], + int, + int, + list[list[PieceConnectionState]], + ] +] + class ConnectionState(Enum): DISCONNECTED = auto() @@ -73,9 +85,16 @@ class WFCSolver: def get_possible_corner_directions(self, x: int, y: int) -> set[Direction]: cstates = self.connection_states[x][y] - connected = cstates.connected_or_unknown() - possible = connected.copy() - possible.update(map(lambda d: Direction((d - 1) % 4), connected)) + connected = cstates.directions_with_state(ConnectionState.CONNECTED) + if connected: + possible = {Direction.UP, Direction.DOWN, Direction.LEFT, Direction.RIGHT} + for direction in connected: + possible.intersection_update( + {direction, Direction((direction - 1) % 4)} + ) + else: + possible = cstates.directions_with_state(ConnectionState.UNKNOWN) + possible.update(map(lambda d: Direction((d - 1) % 4), possible.copy())) disconnected = cstates.directions_with_state(ConnectionState.DISCONNECTED) possible.difference_update(disconnected) possible.difference_update(map(lambda d: Direction((d - 1) % 4), disconnected)) @@ -117,20 +136,44 @@ class WFCSolver: ) return possible + def get_field_rotations(self) -> list[list[Direction]]: + return [[p.direction for p in col] for col in self.game.get_field()] + + def set_field_rotations(self, rotations: list[list[Direction]]): + for x, col in enumerate(rotations): + for y, d in enumerate(col): + self.game.set_direction(x, y, d) + def solve(self) -> Generator[None]: unfixed = list( ((i, j) for i in range(self.game.width) for j in range(self.game.height)) ) + rollback: RollbackType = [] while not self.game.solved(): yield - (x, y) = min(unfixed, key=lambda t: len(self.get_possible_directions(*t))) - if len(self.get_possible_directions(x, y)) == 0: - raise NotImplementedError( - "Irgendwo eine falsche Richtung gewählt, muss diese Logik noch schreiben" + if len(unfixed) == 0: + unfixed, rotations, dirs, x, y, self.connection_states = rollback.pop() + self.set_field_rotations(rotations) + else: + (x, y) = min( + unfixed, key=lambda t: len(self.get_possible_directions(*t)) + ) + dirs = self.get_possible_directions(x, y) + if len(dirs) == 0: + unfixed, rotations, dirs, x, y, self.connection_states = rollback.pop() + self.set_field_rotations(rotations) + direction = dirs.pop() + if len(dirs) > 0: + rollback.append( + ( + unfixed.copy(), + self.get_field_rotations(), + dirs, + x, + y, + deepcopy(self.connection_states), + ) ) - if len(self.get_possible_directions(x, y)) > 1: - print("AAAAAAAA") - direction = self.get_possible_directions(x, y).pop() self.game.set_direction(x, y, direction) self.game.lock(x, y) unfixed.remove((x, y)) diff --git a/src/net.py b/src/net.py index 1795991..fec28e8 100644 --- a/src/net.py +++ b/src/net.py @@ -20,10 +20,11 @@ class Grid: raise ValueError("Feldgrösse nicht erlaubt") with open(f"descriptions/{width}x{height}.txt") as f: lines = f.readlines() - if specific: + if specific is not None: selected = lines[specific].strip() else: selected = choice(lines).strip() + print(f"Seed: {lines.index(selected + "\n")}") self.pieces = parse_description(selected) def neighbors(self, x: int, y: int) -> list[Coordinate]: