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