2026-rff_mp/shahovaa/zadanie 2/maze_solver/commands.py
2026-05-19 22:39:51 +03:00

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