Files
python-maze/src/analysis/analyzer.py
2025-11-20 22:58:11 -05:00

149 lines
4.4 KiB
Python

"""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