""" Этап 5.1: Паттерн Observer — уведомления об изменении состояния. Зачем Observer? MazeSolver не знает, кто хочет получать уведомления о событиях. Он просто оповещает всех подписчиков. ConsoleView можно заменить на GUI-вью или логгер без изменения MazeSolver. """ from abc import ABC, abstractmethod from maze_model import Maze, Cell class Observer(ABC): """Интерфейс наблюдателя.""" @abstractmethod def update(self, event: dict) -> None: """ event — словарь с ключом "type": "maze_loaded" — лабиринт загружен "path_found" — путь найден "no_path" — путь не найден """ ... class ConsoleView(Observer): """ Наблюдатель: выводит лабиринт и путь в консоль. Символы: # — стена . — проход S — старт E — выход * — найденный путь @ — текущее положение игрока """ def update(self, event: dict) -> None: event_type = event.get("type") if event_type == "maze_loaded": print("\n[ConsoleView] Лабиринт загружен:") self.render(event["maze"]) elif event_type == "path_found": print("\n[ConsoleView] Путь найден!") self.render(event["maze"], path=event.get("path"), player=event.get("player")) elif event_type == "no_path": print("\n[ConsoleView] Путь не найден.") elif event_type == "move": print(f"\n[ConsoleView] Игрок переместился в ({event['x']}, {event['y']})") self.render(event["maze"], path=event.get("path"), player=event.get("player")) def render(self, maze: Maze, path: list[Cell] | None = None, player: Cell | None = None) -> None: path_set = set(path) if path else set() for y in range(maze.height): row_str = "" for x in range(maze.width): cell = maze.get_cell(x, y) if player and cell == player: row_str += "@" elif cell.is_start: row_str += "S" elif cell.is_exit: row_str += "E" elif cell in path_set: row_str += "*" elif cell.is_wall: row_str += "#" else: row_str += "." print(row_str)