forked from UNN/2026-rff_mp
80 lines
2.0 KiB
Python
80 lines
2.0 KiB
Python
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
|