from __future__ import annotations from abc import ABC, abstractmethod from dataclasses import dataclass from enum import Enum from .models import Cell, Maze class Direction(Enum): UP = (0, -1) RIGHT = (1, 0) DOWN = (0, 1) LEFT = (-1, 0) @classmethod def from_key(cls, key: str) -> "Direction": mapping = { "w": cls.UP, "d": cls.RIGHT, "s": cls.DOWN, "a": cls.LEFT, } try: return mapping[key.lower()] except KeyError as exc: raise ValueError("Use W/A/S/D for movement") from exc @dataclass class Player: maze: Maze current_cell: Cell @classmethod def at_start(cls, maze: Maze) -> "Player": return cls(maze=maze, current_cell=maze.start) def move_to(self, cell: Cell) -> None: if not cell.is_passable(): raise ValueError("Player cannot move into a wall") self.current_cell = cell class Command(ABC): @abstractmethod def execute(self) -> bool: raise NotImplementedError @abstractmethod def undo(self) -> bool: raise NotImplementedError class MoveCommand(Command): def __init__(self, player: Player, direction: Direction) -> None: self.player = player self.direction = direction self.previous_cell: Cell | None = None self.executed = False def execute(self) -> bool: dx, dy = self.direction.value current = self.player.current_cell target = self.player.maze.get_cell(current.x + dx, current.y + dy) if target is None or not target.is_passable(): return False self.previous_cell = current self.player.move_to(target) self.executed = True return True def undo(self) -> bool: if not self.executed or self.previous_cell is None: return False self.player.move_to(self.previous_cell) self.executed = False return True