forked from UNN/2026-rff_mp
159 lines
5.3 KiB
Python
159 lines
5.3 KiB
Python
from abc import ABC, abstractmethod
|
||
from typing import Optional
|
||
|
||
from source.models.base import Maze, Cell
|
||
|
||
|
||
# ---------------------------------------------------------------------------- #
|
||
# Игрок #
|
||
# ---------------------------------------------------------------------------- #
|
||
|
||
|
||
class Player:
|
||
"""Хранит текущее положение игрока в лабиринте.
|
||
|
||
Attributes:
|
||
cell: Текущая клетка игрока.
|
||
"""
|
||
|
||
def __init__(self, cell: Cell) -> None:
|
||
"""Инициализирует игрока на заданной клетке.
|
||
|
||
Args:
|
||
cell: Начальная клетка игрока.
|
||
"""
|
||
self.cell = cell
|
||
|
||
def __repr__(self) -> str:
|
||
return f"Player(x={self.cell.x}, y={self.cell.y})"
|
||
|
||
|
||
# ---------------------------------------------------------------------------- #
|
||
# Интерфейс команды #
|
||
# ---------------------------------------------------------------------------- #
|
||
|
||
|
||
class Command(ABC):
|
||
"""Интерфейс команды с поддержкой отмены."""
|
||
|
||
@abstractmethod
|
||
def execute(self) -> bool:
|
||
"""Выполняет команду.
|
||
|
||
Returns:
|
||
True если команда выполнена успешно, False иначе.
|
||
"""
|
||
|
||
@abstractmethod
|
||
def undo(self) -> None:
|
||
"""Отменяет команду, восстанавливая предыдущее состояние."""
|
||
|
||
|
||
# ---------------------------------------------------------------------------- #
|
||
# Команда перемещения #
|
||
# ---------------------------------------------------------------------------- #
|
||
|
||
DIRECTIONS = {
|
||
"w": (0, -1),
|
||
"s": (0, 1),
|
||
"a": (-1, 0),
|
||
"d": (1, 0),
|
||
}
|
||
|
||
|
||
class MoveCommand(Command):
|
||
"""Перемещает игрока в заданном направлении.
|
||
|
||
Сохраняет предыдущую клетку для возможности отмены хода.
|
||
"""
|
||
|
||
def __init__(self, player: Player, direction: str, maze: Maze) -> None:
|
||
"""Инициализирует команду перемещения.
|
||
|
||
Args:
|
||
player: Объект игрока.
|
||
direction: Направление ('w', 'a', 's', 'd').
|
||
maze: Объект лабиринта для проверки проходимости.
|
||
|
||
Raises:
|
||
ValueError: Если направление не распознано.
|
||
"""
|
||
if direction not in DIRECTIONS:
|
||
raise ValueError(
|
||
f"Неизвестное направление '{direction}'. Используй: w/a/s/d"
|
||
)
|
||
|
||
self._player = player
|
||
self._direction = direction
|
||
self._maze = maze
|
||
self._prev_cell: Optional[Cell] = None
|
||
|
||
def execute(self) -> bool:
|
||
"""Перемещает игрока если целевая клетка проходима.
|
||
|
||
Returns:
|
||
True если перемещение выполнено, False если клетка непроходима.
|
||
"""
|
||
dx, dy = DIRECTIONS[self._direction]
|
||
target = self._maze.get_cell(
|
||
self._player.cell.x + dx,
|
||
self._player.cell.y + dy,
|
||
)
|
||
|
||
if target is None or not target.is_possible():
|
||
return False
|
||
|
||
self._prev_cell = self._player.cell
|
||
self._player.cell = target
|
||
return True
|
||
|
||
def undo(self) -> None:
|
||
"""Возвращает игрока на предыдущую клетку."""
|
||
if self._prev_cell is not None:
|
||
self._player.cell = self._prev_cell
|
||
|
||
|
||
# ---------------------------------------------------------------------------- #
|
||
# История команд #
|
||
# ---------------------------------------------------------------------------- #
|
||
|
||
|
||
class CommandHistory:
|
||
"""Хранит историю выполненных команд и позволяет отменять их.
|
||
|
||
Example:
|
||
history = CommandHistory()
|
||
cmd = MoveCommand(player, 'w', maze)
|
||
if cmd.execute():
|
||
history.push(cmd)
|
||
|
||
history.undo() # отменяет последний успешный ход
|
||
"""
|
||
|
||
def __init__(self) -> None:
|
||
self._history: list[Command] = []
|
||
|
||
def push(self, command: Command) -> None:
|
||
"""Добавляет выполненную команду в историю.
|
||
|
||
Args:
|
||
command: Успешно выполненная команда.
|
||
"""
|
||
self._history.append(command)
|
||
|
||
def undo(self) -> bool:
|
||
"""Отменяет последнюю команду из истории.
|
||
|
||
Returns:
|
||
True если отмена выполнена, False если история пуста.
|
||
"""
|
||
if not self._history:
|
||
print("Нечего отменять.")
|
||
return False
|
||
self._history.pop().undo()
|
||
return True
|
||
|
||
def clear(self) -> None:
|
||
"""Очищает историю команд."""
|
||
self._history.clear()
|