2026-rff_mp/SobolevNS/docs/data/task2_maze/maze_solver/solver.py
2026-05-22 13:42:42 +03:00

103 lines
3.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
maze_solver/solver.py - оркестратор MazeSolver + паттерн Observer.
MazeSolver знает лабиринт и текущую стратегию (Strategy). Перед поиском
он уведомляет наблюдателей (Observer) о старте, после поиска - о результате.
"""
import time
from abc import ABC, abstractmethod
# ---------- Observer ----------
class Observer(ABC):
"""Интерфейс наблюдателя."""
@abstractmethod
def update(self, event):
"""event - dict с ключом 'type' и сопровождающими данными."""
class ConsoleView(Observer):
"""Простой текстовый наблюдатель."""
def __init__(self, verbose=True):
self.verbose = verbose
def update(self, event):
if not self.verbose:
return
t = event["type"]
if t == "maze_loaded":
m = event["maze"]
print(f"[ConsoleView] лабиринт {m.width}x{m.height} загружен")
elif t == "search_start":
print(f"[ConsoleView] старт поиска: {event['strategy']}")
elif t == "search_end":
stats = event["stats"]
print(f"[ConsoleView] поиск окончен: путь={stats['path_length']}, "
f"посещено={stats['visited']}, время={stats['elapsed_ms']:.3f} мс")
elif t == "move":
print(f"[ConsoleView] игрок -> ({event['x']},{event['y']})")
elif t == "path_found":
print("[ConsoleView] путь найден")
elif t == "no_path":
print("[ConsoleView] пути нет")
# ---------- MazeSolver ----------
class SearchStats(dict):
"""Простой dict-подобный контейнер статистики поиска."""
pass
class MazeSolver:
def __init__(self, maze, strategy=None):
self.maze = maze
self.strategy = strategy
self._observers = []
def set_strategy(self, strategy):
self.strategy = strategy
def attach(self, observer):
self._observers.append(observer)
def detach(self, observer):
self._observers.remove(observer)
def _notify(self, event):
for obs in self._observers:
obs.update(event)
def solve(self):
if self.strategy is None:
raise RuntimeError("Стратегия не задана")
if self.maze.start is None or self.maze.exit_ is None:
raise RuntimeError("В лабиринте нет старта или выхода")
self._notify({"type": "search_start", "strategy": self.strategy.name})
t0 = time.perf_counter()
result = self.strategy.find_path(self.maze,
self.maze.start,
self.maze.exit_)
elapsed = (time.perf_counter() - t0) * 1000.0
path = result["path"]
stats = SearchStats(
strategy=self.strategy.name,
elapsed_ms=elapsed,
visited=result["visited"],
path_length=len(path),
path=path,
)
self._notify({"type": "search_end", "stats": stats})
if path:
self._notify({"type": "path_found"})
else:
self._notify({"type": "no_path"})
return stats