import json from pathlib import Path LEVELS_PER_DAT_FILE = 32 LEVEL_WIDTH = 32 LEVEL_HEIGHT = 32 LEVEL_SIZE = LEVEL_WIDTH * LEVEL_HEIGHT EXPECTED_DAT_SIZE = LEVELS_PER_DAT_FILE * LEVEL_SIZE PROJECT_ROOT = Path(__file__).resolve().parent.parent DEFAULT_DAT_PATH = PROJECT_ROOT / "assets" / "Rat" / "level.dat" DEFAULT_JSON_PATH = PROJECT_ROOT / "maze.json" MAP_EMPTY = 0 MAP_WALL = 1 MAP_TUNNEL = 2 def get_default_map_source(): if DEFAULT_DAT_PATH.exists(): return DEFAULT_DAT_PATH return DEFAULT_JSON_PATH class Map: """Classe che rappresenta la mappa del labirinto.""" def __init__(self, maze_file=None, level_index=0): self.source_path = self._resolve_source_path(maze_file) self.level_index = level_index self.tiles = self._load_tiles(self.source_path, level_index) self.matrix = [ [cell == MAP_WALL for cell in row] for row in self.tiles ] self.height = len(self.tiles) self.width = len(self.tiles[0]) def _resolve_source_path(self, maze_file): if maze_file is None: return get_default_map_source() candidate = Path(maze_file) if candidate.is_absolute(): return candidate if candidate.exists(): return candidate.resolve() project_candidate = PROJECT_ROOT / candidate if project_candidate.exists(): return project_candidate return project_candidate def _load_tiles(self, source_path, level_index): suffix = source_path.suffix.lower() if suffix == ".dat": return self._load_dat_level(source_path, level_index) return self._load_json_level(source_path) def _load_json_level(self, source_path): with source_path.open("r", encoding="utf-8") as file: matrix = json.load(file) return [ [MAP_WALL if cell else MAP_TUNNEL for cell in row] for row in matrix ] def _load_dat_level(self, source_path, level_index): raw_data = source_path.read_bytes() if len(raw_data) != EXPECTED_DAT_SIZE: raise ValueError( f"Invalid DAT size for {source_path}: expected {EXPECTED_DAT_SIZE} bytes, got {len(raw_data)}" ) normalized_level = level_index % LEVELS_PER_DAT_FILE level_offset = normalized_level * LEVEL_SIZE level_data = raw_data[level_offset:level_offset + LEVEL_SIZE] matrix = [] for row in range(LEVEL_HEIGHT): row_start = row * LEVEL_WIDTH raw_row = level_data[row_start:row_start + LEVEL_WIDTH] matrix.append(list(raw_row)) return matrix def in_bounds(self, x, y): return 0 <= x < self.width and 0 <= y < self.height def get_cell(self, x, y): return self.tiles[y][x] def is_wall(self, x, y): """Restituisce True se la cella รจ un muro, False altrimenti.""" return self.matrix[y][x] def is_traversable(self, x, y): return self.get_cell(x, y) != MAP_WALL def is_empty(self, x, y): return self.get_cell(x, y) == MAP_EMPTY def is_tunnel(self, x, y): return self.get_cell(x, y) == MAP_TUNNEL def get_tunnel_direction(self, x, y): directions = [ ("UP", 0, -1), ("DOWN", 0, 1), ("LEFT", -1, 0), ("RIGHT", 1, 0), ] traversable_neighbors = [] for direction, dx, dy in directions: nx = x + dx ny = y + dy if self.in_bounds(nx, ny) and self.is_traversable(nx, ny): traversable_neighbors.append(direction) if len(traversable_neighbors) == 1: return traversable_neighbors[0] if traversable_neighbors: return traversable_neighbors[0] return "UP"