2026-rff_mp/skorohodovsa/task_2/source/view/observer.py

116 lines
4.0 KiB
Python
Raw Normal View History

2026-05-24 15:00:14 +00:00
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
# ---------------------------------------------------------------------------- #
# События #
# ---------------------------------------------------------------------------- #
2026-05-25 07:23:00 +00:00
2026-05-24 15:00:14 +00:00
@dataclass
class Event:
"""Событие, передаваемое наблюдателям.
Attributes:
type: Тип события ('maze_loaded', 'path_found', 'move', 'no_path').
payload: Дополнительные данные события.
"""
2026-05-25 07:23:00 +00:00
2026-05-24 15:00:14 +00:00
type: str
payload: dict = None
# ---------------------------------------------------------------------------- #
# Интерфейс наблюдателя #
# ---------------------------------------------------------------------------- #
2026-05-25 07:23:00 +00:00
2026-05-24 15:00:14 +00:00
class Observer(ABC):
"""Интерфейс наблюдателя за событиями лабиринта."""
@abstractmethod
def update(self, event: Event) -> None:
"""Обрабатывает входящее событие.
Args:
event: Объект события с типом и данными.
"""
# ---------------------------------------------------------------------------
# Консольный наблюдатель
# ---------------------------------------------------------------------------
2026-05-25 07:23:00 +00:00
2026-05-24 15:00:14 +00:00
class ConsoleView(Observer):
"""Отображает состояние лабиринта и события в консоли."""
# Символ игрока на карте
PLAYER_SYMBOL = "P"
2026-05-25 07:23:00 +00:00
PATH_SYMBOL = "·"
2026-05-24 15:00:14 +00:00
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)
2026-05-25 07:23:00 +00:00
print("+" + "" * cols + "+")