from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Optional from source.models.base import Maze, Cell from source.settings import cell_mapping # ---------------------------------------------------------------------------- # # События # # ---------------------------------------------------------------------------- # @dataclass class Event: """Событие, передаваемое наблюдателям. Attributes: type: Тип события ('maze_loaded', 'path_found', 'move', 'no_path'). payload: Дополнительные данные события. """ type: str payload: dict = None # ---------------------------------------------------------------------------- # # Интерфейс наблюдателя # # ---------------------------------------------------------------------------- # class Observer(ABC): """Интерфейс наблюдателя за событиями лабиринта.""" @abstractmethod def update(self, event: Event) -> None: """Обрабатывает входящее событие. Args: event: Объект события с типом и данными. """ # --------------------------------------------------------------------------- # Консольный наблюдатель # --------------------------------------------------------------------------- class ConsoleView(Observer): """Отображает состояние лабиринта и события в консоли.""" # Символ игрока на карте PLAYER_SYMBOL = "P" PATH_SYMBOL = "·" def update(self, event: Event) -> None: """Реагирует на события и выводит информацию в консоль. Args: event: Объект события. """ match event.type: case "maze_loaded": print("Лабиринт загружен.") self.render(event.payload["maze"]) case "path_found": print(f"Путь найден! Длина: {event.payload['length']} шагов.") self.render( event.payload["maze"], path=event.payload["path"], ) case "no_path": print("Путь не найден.") case "move": print(f"Ход: {event.payload['direction']}") self.render( event.payload["maze"], player=event.payload["player_cell"], path=event.payload.get("path"), ) case _: print(f"[событие] {event.type}") def render( self, maze: Maze, player: Optional[Cell] = None, path: Optional[list[Cell]] = None, ) -> None: """Рисует лабиринт в консоли. Путь отмечается символом '·', позиция игрока — 'P'. Args: maze: Объект лабиринта. player: Текущая клетка игрока (опционально). path: Список клеток найденного пути (опционально). """ path_set = set(path) if path else set() rows, cols = maze.shape print("+" + "─" * cols + "+") for y in range(rows): row_str = "|" for x in range(cols): cell = maze[y, x] if player and cell is player: row_str += self.PLAYER_SYMBOL elif cell in path_set: row_str += self.PATH_SYMBOL else: row_str += str(cell) row_str += "|" print(row_str) print("+" + "─" * cols + "+")