forked from UNN/2026-rff_mp
160 lines
4.4 KiB
Python
160 lines
4.4 KiB
Python
from abc import ABC, abstractmethod
|
||
from typing import List, Optional, Set
|
||
from enum import Enum
|
||
from maze_model import Maze, Cell
|
||
|
||
|
||
class EventType(Enum):
|
||
PATH_FOUND = "path_found"
|
||
MOVE = "move"
|
||
MAZE_LOADED = "maze_loaded"
|
||
SOLVE_START = "solve_start"
|
||
SOLVE_END = "solve_end"
|
||
|
||
|
||
class Observer(ABC):
|
||
|
||
@abstractmethod
|
||
def update(self, event_type: EventType, data: any) -> None:
|
||
pass
|
||
|
||
|
||
class ConsoleView(Observer):
|
||
|
||
def __init__(self):
|
||
self.last_path: Optional[List[Cell]] = None
|
||
|
||
def update(self, event_type: EventType, data: any) -> None:
|
||
if event_type == EventType.MAZE_LOADED:
|
||
print("Лабиринт загружен")
|
||
elif event_type == EventType.SOLVE_START:
|
||
print("Начинается поиск пути...")
|
||
elif event_type == EventType.SOLVE_END:
|
||
print(f"Поиск завершён. Статистика: {data}")
|
||
elif event_type == EventType.PATH_FOUND:
|
||
self.last_path = data
|
||
|
||
def render(self, maze: Maze, player_pos: Optional[Cell] = None,
|
||
path: Optional[List[Cell]] = None) -> None: #рисует лаб
|
||
import os
|
||
os.system('cls' if os.name == 'nt' else 'clear')
|
||
|
||
path_set = set(path) if path else set()
|
||
|
||
# Верх
|
||
print("┌" + "─" * maze.width + "┐")
|
||
|
||
for y in range(maze.height):
|
||
line = "│"
|
||
for x in range(maze.width):
|
||
cell = maze.get_cell(x, y)
|
||
if player_pos and player_pos.x == x and player_pos.y == y:
|
||
line += "P"
|
||
elif cell == maze.start:
|
||
line += "S"
|
||
elif cell == maze.exit:
|
||
line += "E"
|
||
elif cell is not None and cell.is_wall:
|
||
line += "#"
|
||
elif path and cell in path_set:
|
||
line += "."
|
||
else:
|
||
line += " "
|
||
line += "│"
|
||
print(line)
|
||
|
||
# Низ
|
||
print("└" + "─" * maze.width + "┘")
|
||
|
||
if path:
|
||
print(f"\nПуть найден! Длина: {len(path)} шагов")
|
||
elif path == []:
|
||
print("\nПуть не найден:(")
|
||
|
||
|
||
class Player:
|
||
|
||
def __init__(self, start_cell: Cell):
|
||
self.current_cell = start_cell
|
||
|
||
def move_to(self, cell: Cell) -> None:
|
||
self.current_cell = cell
|
||
|
||
def get_position(self) -> Cell:
|
||
return self.current_cell
|
||
|
||
|
||
class Direction(Enum):
|
||
UP = (0, -1)
|
||
DOWN = (0, 1)
|
||
LEFT = (-1, 0)
|
||
RIGHT = (1, 0)
|
||
|
||
|
||
class Command(ABC):
|
||
|
||
@abstractmethod
|
||
def execute(self) -> None:
|
||
pass
|
||
|
||
@abstractmethod
|
||
def undo(self) -> None:
|
||
pass
|
||
|
||
|
||
class MoveCommand(Command):
|
||
|
||
def __init__(self, player: Player, maze: Maze, direction: Direction):
|
||
self.player = player
|
||
self.maze = maze
|
||
self.direction = direction
|
||
self.previous_cell = player.current_cell
|
||
|
||
def execute(self) -> None:
|
||
dx, dy = self.direction.value
|
||
new_x = self.player.current_cell.x + dx
|
||
new_y = self.player.current_cell.y + dy
|
||
|
||
new_cell = self.maze.get_cell(new_x, new_y)
|
||
if new_cell and new_cell.is_passable():
|
||
self.previous_cell = self.player.current_cell
|
||
self.player.move_to(new_cell)
|
||
return True
|
||
return False
|
||
|
||
def undo(self) -> None:
|
||
self.player.move_to(self.previous_cell)
|
||
|
||
|
||
class GameController:
|
||
|
||
def __init__(self, maze: Maze, view: ConsoleView):
|
||
if maze.start is None:
|
||
raise ValueError("Лабиринт не имеет стартовой клетки")
|
||
|
||
self.maze = maze
|
||
self.view = view
|
||
self.player = Player(maze.start)
|
||
self.command_history: List[Command] = []
|
||
self.found_path: Optional[List[Cell]] = None
|
||
|
||
def move(self, direction: Direction) -> bool:
|
||
command = MoveCommand(self.player, self.maze, direction)
|
||
if command.execute():
|
||
self.command_history.append(command)
|
||
self._render()
|
||
return True
|
||
return False
|
||
|
||
def undo(self) -> None:
|
||
if self.command_history:
|
||
command = self.command_history.pop()
|
||
command.undo()
|
||
self._render()
|
||
|
||
def set_path(self, path: List[Cell]) -> None:
|
||
self.found_path = path
|
||
self._render()
|
||
|
||
def _render(self) -> None:
|
||
self.view.render(self.maze, self.player.get_position(), self.found_path) |