2026-rff_mp/skorohodovsa/task_2/source/view/command.py
2026-05-25 10:23:00 +03:00

159 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()