import time from abc import ABC, abstractmethod class SearchStats: def __init__(self, time_ms, visited_cells, path_length, path): self.time_ms = time_ms self.visited_cells = visited_cells self.path_length = path_length self.path = path def __repr__(self): return (f"SearchStats(time={self.time_ms:.3f}ms, " f"visited={self.visited_cells}, " f"path_len={self.path_length})") class Observer(ABC): @abstractmethod def update(self, event, data=None): pass class ConsoleView(Observer): def update(self, event, data=None): if event == 'maze_loaded': print(f"\n[ConsoleView] Лабиринт загружен: " f"{data['width']}×{data['height']}") elif event == 'path_found': stats = data['stats'] strategy_name = data['strategy'] if stats.path_length > 0: print(f"\n[ConsoleView] [{strategy_name}] Путь найден! " f"Длина: {stats.path_length}, " f"Посещено клеток: {stats.visited_cells}, " f"Время: {stats.time_ms:.3f} мс") else: print(f"\n[ConsoleView] [{strategy_name}] Путь не найден. " f"Посещено клеток: {stats.visited_cells}") elif event == 'move': print(f"[ConsoleView] Игрок переместился в " f"({data['x']}, {data['y']})") 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 add_observer(self, observer): self._observers.append(observer) def _notify(self, event, data=None): for obs in self._observers: obs.update(event, data) def solve(self): if self.strategy is None: raise RuntimeError("Стратегия не задана. Используйте set_strategy().") start = time.perf_counter() path = self.strategy.find_path(self.maze, self.maze.start, self.maze.exit) end = time.perf_counter() stats = SearchStats( time_ms=(end - start) * 1000, visited_cells=getattr(self.strategy, 'visited_count', 0), path_length=len(path), path=path ) self._notify('path_found', { 'stats': stats, 'strategy': type(self.strategy).__name__ }) return stats class Command(ABC): @abstractmethod def execute(self): pass @abstractmethod def undo(self): pass class Player: def __init__(self, start_cell): self.current_cell = start_cell def move_to(self, cell): self.current_cell = cell class MoveCommand(Command): def __init__(self, player, target_cell, observers=None): self.player = player self.target_cell = target_cell self.previous_cell = None self._observers = observers or [] def execute(self): self.previous_cell = self.player.current_cell self.player.move_to(self.target_cell) for obs in self._observers: obs.update('move', {'x': self.target_cell.x, 'y': self.target_cell.y}) def undo(self): if self.previous_cell is not None: self.player.move_to(self.previous_cell) for obs in self._observers: obs.update('move', {'x': self.previous_cell.x, 'y': self.previous_cell.y})