forked from UNN/2026-rff_mp
111 lines
4.0 KiB
Python
111 lines
4.0 KiB
Python
|
|
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 + "+")
|