"""Maze analysis tools for calculating statistics and metrics.""" from typing import Dict, List from ..core.cell import Cell from ..core.maze import Maze class MazeAnalyzer: """Analyzes mazes to compute various metrics and statistics.""" @staticmethod def analyze(maze: Maze) -> Dict: """Perform comprehensive analysis of a maze. Args: maze: Maze to analyze Returns: Dictionary with analysis results """ dead_ends = MazeAnalyzer.count_dead_ends(maze) longest_path_info = MazeAnalyzer.find_longest_path(maze) branching_factor = MazeAnalyzer.calculate_branching_factor(maze) return { 'dimensions': f"{maze.rows}x{maze.cols}", 'total_cells': maze.rows * maze.cols, 'algorithm': maze.algorithm_used, 'generation_time_ms': maze.generation_time_ms, 'seed': maze.seed, 'dead_ends': dead_ends, 'dead_end_percentage': (dead_ends / (maze.rows * maze.cols)) * 100, 'longest_path_length': longest_path_info['length'], 'longest_path_start': longest_path_info['start'], 'longest_path_end': longest_path_info['end'], 'average_branching_factor': branching_factor } @staticmethod def count_dead_ends(maze: Maze) -> int: """Count the number of dead ends in the maze. A dead end is a cell with only one open passage. Args: maze: Maze to analyze Returns: Number of dead ends """ dead_end_count = 0 for row in maze.grid: for cell in row: # Count open passages open_passages = sum(1 for wall in cell.walls.values() if not wall) if open_passages == 1: dead_end_count += 1 return dead_end_count @staticmethod def find_longest_path(maze: Maze) -> Dict: """Find the longest path in the maze. Args: maze: Maze to analyze Returns: Dictionary with longest path info """ max_length = 0 max_start = (0, 0) max_end = (0, 0) # Try BFS from each cell to find longest path for start_row in maze.grid: for start_cell in start_row: maze.reset_visited() distances = MazeAnalyzer._bfs_distances(maze, start_cell) for end_cell, distance in distances.items(): if distance > max_length: max_length = distance max_start = (start_cell.row, start_cell.col) max_end = (end_cell.row, end_cell.col) maze.reset_visited() return { 'length': max_length, 'start': max_start, 'end': max_end } @staticmethod def _bfs_distances(maze: Maze, start: Cell) -> Dict[Cell, int]: """Calculate distances from start cell using BFS. Args: maze: The maze start: Starting cell Returns: Dictionary mapping cells to their distance from start """ from collections import deque queue = deque([(start, 0)]) distances = {start: 0} start.visited = True while queue: current, dist = queue.popleft() neighbors = maze.get_neighbors(current) for neighbor, direction in neighbors: if not neighbor.visited and not current.has_wall(direction): neighbor.visited = True distances[neighbor] = dist + 1 queue.append((neighbor, dist + 1)) return distances @staticmethod def calculate_branching_factor(maze: Maze) -> float: """Calculate the average branching factor of the maze. Branching factor is the average number of passages per cell. Args: maze: Maze to analyze Returns: Average branching factor """ total_passages = 0 cell_count = 0 for row in maze.grid: for cell in row: # Count open passages open_passages = sum(1 for wall in cell.walls.values() if not wall) total_passages += open_passages cell_count += 1 return total_passages / cell_count if cell_count > 0 else 0