103 lines
3.5 KiB
Python
103 lines
3.5 KiB
Python
|
|
#!/usr/bin/env python
|
|||
|
|
# coding: utf-8
|
|||
|
|
|
|||
|
|
# In[ ]:
|
|||
|
|
|
|||
|
|
|
|||
|
|
import time
|
|||
|
|
from typing import List, Optional
|
|||
|
|
from dataclasses import dataclass, field
|
|||
|
|
from modelsMaze import Maze
|
|||
|
|
from modelsCell import Cell
|
|||
|
|
from strategiesPathfinding_strategy import PathFindingStrategy
|
|||
|
|
from visualizationObserver import Observer
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class SearchStats:
|
|||
|
|
"""Статистика поиска."""
|
|||
|
|
algorithm_name: str
|
|||
|
|
time_ms: float
|
|||
|
|
visited_cells: int
|
|||
|
|
path_length: int
|
|||
|
|
path_found: bool = True
|
|||
|
|
|
|||
|
|
class MazeSolver:
|
|||
|
|
"""
|
|||
|
|
Оркестратор для решения лабиринта.
|
|||
|
|
Использует паттерн Strategy для алгоритмов поиска.
|
|||
|
|
Поддерживает Observer для уведомлений.
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def __init__(self, maze: Maze, strategy: Optional[PathFindingStrategy] = None):
|
|||
|
|
self.maze = maze
|
|||
|
|
self._strategy = strategy
|
|||
|
|
self._observers: List[Observer] = []
|
|||
|
|
self._last_path: List[Cell] = []
|
|||
|
|
self._last_stats: Optional[SearchStats] = None
|
|||
|
|
|
|||
|
|
def set_strategy(self, strategy: PathFindingStrategy) -> None:
|
|||
|
|
"""Динамическая смена стратегии."""
|
|||
|
|
self._strategy = strategy
|
|||
|
|
self._notify(f"Стратегия изменена на {strategy.name}")
|
|||
|
|
|
|||
|
|
def attach(self, observer: Observer) -> None:
|
|||
|
|
"""Подписать наблюдателя."""
|
|||
|
|
self._observers.append(observer)
|
|||
|
|
|
|||
|
|
def detach(self, observer: Observer) -> None:
|
|||
|
|
"""Отписать наблюдателя."""
|
|||
|
|
if observer in self._observers:
|
|||
|
|
self._observers.remove(observer)
|
|||
|
|
|
|||
|
|
def _notify(self, event: str) -> None:
|
|||
|
|
"""Уведомить всех наблюдателей."""
|
|||
|
|
for observer in self._observers:
|
|||
|
|
observer.update(event)
|
|||
|
|
|
|||
|
|
def solve(self) -> List[Cell]:
|
|||
|
|
"""
|
|||
|
|
Выполнить поиск пути с текущей стратегией.
|
|||
|
|
Возвращает путь (список клеток).
|
|||
|
|
"""
|
|||
|
|
if self._strategy is None:
|
|||
|
|
raise ValueError("Стратегия не установлена")
|
|||
|
|
|
|||
|
|
if not self.maze.start_cell or not self.maze.exit_cell:
|
|||
|
|
raise ValueError("Лабиринт не имеет старта или выхода")
|
|||
|
|
|
|||
|
|
self._notify(f"Начинаем поиск пути с использованием {self._strategy.name}...")
|
|||
|
|
|
|||
|
|
start_time = time.perf_counter()
|
|||
|
|
path = self._strategy.find_path(self.maze, self.maze.start_cell, self.maze.exit_cell)
|
|||
|
|
end_time = time.perf_counter()
|
|||
|
|
|
|||
|
|
time_ms = (end_time - start_time) * 1000
|
|||
|
|
|
|||
|
|
# Получаем количество посещённых клеток из стратегии
|
|||
|
|
visited_cells = getattr(self._strategy, 'last_visited_count', 0)
|
|||
|
|
|
|||
|
|
self._last_path = path
|
|||
|
|
self._last_stats = SearchStats(
|
|||
|
|
algorithm_name=self._strategy.name,
|
|||
|
|
time_ms=time_ms,
|
|||
|
|
visited_cells=visited_cells,
|
|||
|
|
path_length=len(path),
|
|||
|
|
path_found=len(path) > 0
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if path:
|
|||
|
|
self._notify(f"Путь найден! Длина: {len(path)}, время: {time_ms:.2f} мс, посещено: {visited_cells}")
|
|||
|
|
else:
|
|||
|
|
self._notify(f"Путь не найден! Время: {time_ms:.2f} мс, посещено: {visited_cells}")
|
|||
|
|
|
|||
|
|
return path
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def last_path(self) -> List[Cell]:
|
|||
|
|
return self._last_path
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def last_stats(self) -> Optional[SearchStats]:
|
|||
|
|
return self._last_stats
|
|||
|
|
|