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

159 lines
5.3 KiB
Python
Raw Normal View History

2026-05-24 15:00:14 +00:00
from abc import ABC, abstractmethod
from typing import Optional
from source.models.base import Maze, Cell
# ---------------------------------------------------------------------------- #
# Игрок #
# ---------------------------------------------------------------------------- #
2026-05-25 07:23:00 +00:00
2026-05-24 15:00:14 +00:00
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})"
# ---------------------------------------------------------------------------- #
# Интерфейс команды #
# ---------------------------------------------------------------------------- #
2026-05-25 07:23:00 +00:00
2026-05-24 15:00:14 +00:00
class Command(ABC):
"""Интерфейс команды с поддержкой отмены."""
@abstractmethod
def execute(self) -> bool:
"""Выполняет команду.
Returns:
True если команда выполнена успешно, False иначе.
"""
@abstractmethod
def undo(self) -> None:
"""Отменяет команду, восстанавливая предыдущее состояние."""
# ---------------------------------------------------------------------------- #
# Команда перемещения #
# ---------------------------------------------------------------------------- #
DIRECTIONS = {
"w": (0, -1),
2026-05-25 07:23:00 +00:00
"s": (0, 1),
2026-05-24 15:00:14 +00:00
"a": (-1, 0),
2026-05-25 07:23:00 +00:00
"d": (1, 0),
2026-05-24 15:00:14 +00:00
}
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:
2026-05-25 07:23:00 +00:00
raise ValueError(
f"Неизвестное направление '{direction}'. Используй: w/a/s/d"
)
2026-05-24 15:00:14 +00:00
2026-05-25 07:23:00 +00:00
self._player = player
2026-05-24 15:00:14 +00:00
self._direction = direction
2026-05-25 07:23:00 +00:00
self._maze = maze
2026-05-24 15:00:14 +00:00
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
2026-05-25 07:23:00 +00:00
self._prev_cell = self._player.cell
2026-05-24 15:00:14 +00:00
self._player.cell = target
return True
def undo(self) -> None:
"""Возвращает игрока на предыдущую клетку."""
if self._prev_cell is not None:
self._player.cell = self._prev_cell
# ---------------------------------------------------------------------------- #
# История команд #
# ---------------------------------------------------------------------------- #
2026-05-25 07:23:00 +00:00
2026-05-24 15:00:14 +00:00
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:
"""Очищает историю команд."""
2026-05-25 07:23:00 +00:00
self._history.clear()