116 lines
4.0 KiB
Python
116 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 + "+")
|