127 lines
4.9 KiB
Python
127 lines
4.9 KiB
Python
import time
|
||
from dataclasses import dataclass
|
||
from typing import List, Optional
|
||
|
||
@dataclass
|
||
#Статистика поиска пути
|
||
class SearchStats:
|
||
time_ms: float #Время выполнения в миллисекундах
|
||
visited_cells: int #Количество посещенных клеток
|
||
path_length: int #Длина найденного пути (0 если путь не найден)
|
||
path_found: bool #Найден ли путь
|
||
|
||
def __repr__(self):
|
||
status = "Найден" if self.path_found else "Не найден"
|
||
return f"Stats({status}, время={self.time_ms:.2f}мс, посещено={self.visited_cells}, длина={self.path_length})"
|
||
|
||
def to_dict(self):
|
||
"""Преобразует статистику в словарь для CSV"""
|
||
return {
|
||
'time_ms': f"{self.time_ms:.2f}",
|
||
'visited_cells': self.visited_cells,
|
||
'path_length': self.path_length,
|
||
'path_found': self.path_found
|
||
}
|
||
|
||
|
||
class MazeSolver:
|
||
"""Оркестратор для решения лабиринта"""
|
||
#Инициализация решателя лабиринта
|
||
def __init__(self, maze, strategy=None):
|
||
self.maze = maze
|
||
self._strategy = strategy
|
||
self._last_path = None
|
||
|
||
#Динамическая смена стратегии поиска
|
||
def set_strategy(self, strategy):
|
||
self._strategy = strategy
|
||
print(f"Стратегия изменена на: {strategy.name}")
|
||
|
||
def solve(self) -> SearchStats:
|
||
if self._strategy is None:
|
||
raise ValueError("Стратегия не установлена! Используйте set_strategy()")
|
||
|
||
if self.maze.start is None:
|
||
raise ValueError("В лабиринте нет стартовой клетки!")
|
||
|
||
if self.maze.exit is None:
|
||
raise ValueError("В лабиринте нет выходной клетки!")
|
||
|
||
# Замер времени
|
||
start_time = time.perf_counter()
|
||
|
||
# Поиск пути
|
||
path = self._strategy.find_path(self.maze, self.maze.start, self.maze.exit)
|
||
|
||
end_time = time.perf_counter()
|
||
time_ms = (end_time - start_time) * 1000
|
||
|
||
self._last_path = path
|
||
|
||
return SearchStats(
|
||
time_ms=time_ms,
|
||
visited_cells=self._strategy.visited_count,
|
||
path_length=len(path),
|
||
path_found=len(path) > 0
|
||
)
|
||
|
||
def get_path(self) -> Optional[List]:
|
||
"""Возвращает последний найденный путь"""
|
||
return self._last_path
|
||
|
||
#Выводит лабиринт с найденным путем
|
||
def print_maze_with_path(self):
|
||
if not self._last_path:
|
||
print("Путь еще не найден. Сначала вызовите solve()")
|
||
return
|
||
|
||
path_set = set(self._last_path)
|
||
|
||
print(f"\n {' ' * 4}", end="")
|
||
for x in range(self.maze.width):
|
||
print(f"{x} ", end="")
|
||
print()
|
||
|
||
for y in range(self.maze.height):
|
||
print(f" {y} │ ", end="")
|
||
for x in range(self.maze.width):
|
||
cell = self.maze.get_cell(x, y)
|
||
if cell == self.maze.start:
|
||
print("S ", end="")
|
||
elif cell == self.maze.exit:
|
||
print("E ", end="")
|
||
elif cell in path_set:
|
||
print("● ", end="")
|
||
elif cell.is_wall:
|
||
print("# ", end="")
|
||
else:
|
||
print("· ", end="")
|
||
print()
|
||
|
||
print("\n Условные обозначения:")
|
||
print(" # - стена · - проход ● - путь")
|
||
print(" S - старт E - выход")
|
||
|
||
#Сравнивает несколько стратегий на одном лабиринте
|
||
def compare_strategies(self, strategies: List) -> dict:
|
||
results = {}
|
||
|
||
print(f"СРАВНЕНИЕ СТРАТЕГИЙ")
|
||
print(f"Лабиринт: {self.maze.width}x{self.maze.height}")
|
||
|
||
for strategy in strategies:
|
||
self.set_strategy(strategy)
|
||
stats = self.solve()
|
||
results[strategy.name] = {
|
||
'stats': stats,
|
||
'path': self.get_path()
|
||
}
|
||
|
||
# Вывод результатов
|
||
status = "OK" if stats.path_found else "BULLSHIT"
|
||
print(f"\n {status} {strategy.name}:")
|
||
print(f" Время: {stats.time_ms:.2f} мс")
|
||
print(f" Посещено клеток: {stats.visited_cells}")
|
||
print(f" Длина пути: {stats.path_length}")
|
||
|
||
return results |