2026-rff_mp/SimonovaMS/lab2/visualization.py
2026-05-02 06:49:44 +03:00

160 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)