# Основной модуль: оркестратор MazeSolver, Observer для визуализации, # Player, Command для пошагового управления, интерактивная игра import sys import time import os from maze_core import TextFileMazeBuilder, Maze from pathfinding import BFSStrategy, DFSStrategy, AStarStrategy class SearchStats: def __init__(self, time_ms, visited_cells, path_length): self.time_ms = time_ms self.visited_cells = visited_cells self.path_length = path_length class Observer: def update(self, event_type, data): raise NotImplementedError class ConsoleView(Observer): def __init__(self, player=None): self._last_path = None self._player = player def update(self, event_type, data): if event_type == "maze_loaded": self.render_maze(data) elif event_type == "path_found": self._last_path = data self.render_path(data) elif event_type == "player_moved": self.render_maze_with_player(data) def render_maze(self, maze): os.system('cls' if os.name == 'nt' else 'clear') print("=" * (maze.width * 2 + 4)) print(" ЛАБИРИНТ") print("=" * (maze.width * 2 + 4)) for y in range(maze.height): print(" ", end='') for x in range(maze.width): cell = maze.get_cell(x, y) if cell == maze.start: print('S', end=' ') elif cell == maze.exit: print('E', end=' ') elif cell.is_wall: print('#', end=' ') else: print('.', end=' ') print() print("=" * (maze.width * 2 + 4)) print(" S - вход E - выход # - стена . - проход") def render_maze_with_player(self, maze): os.system('cls' if os.name == 'nt' else 'clear') print("=" * (maze.width * 2 + 4)) print(" ЛАБИРИНТ (P - игрок)") print("=" * (maze.width * 2 + 4)) for y in range(maze.height): print(" ", end='') for x in range(maze.width): cell = maze.get_cell(x, y) if self._player and cell == self._player.current: print('P', end=' ') elif cell == maze.start: print('S', end=' ') elif cell == maze.exit: print('E', end=' ') elif cell.is_wall: print('#', end=' ') else: print('.', end=' ') print() print("=" * (maze.width * 2 + 4)) print(f" Позиция игрока: ({self._player.current.x}, {self._player.current.y})") print(" S - вход E - выход # - стена . - проход P - игрок") def render_path(self, path): if not path: print("\n Путь не найден!") return print(f"\n Путь найден! Длина: {len(path)}") class Player: def __init__(self, start_cell, maze): self._current = start_cell self._previous = None self._maze = maze @property def current(self): return self._current def move_to(self, cell): if cell and cell.is_passable(): self._previous = self._current self._current = cell return True return False def undo_move(self): if self._previous: self._current, self._previous = self._previous, None return True return False class Command: def execute(self): raise NotImplementedError def undo(self): raise NotImplementedError class MoveCommand(Command): def __init__(self, player, direction, maze): self._player = player self._direction = direction self._maze = maze self._executed = False def execute(self): dx, dy = self._direction new_x = self._player.current.x + dx new_y = self._player.current.y + dy target_cell = self._maze.get_cell(new_x, new_y) if target_cell and target_cell.is_passable(): self._player.move_to(target_cell) self._executed = True return True return False def undo(self): if self._executed: self._player.undo_move() self._executed = False return True return False class MazeSolver: def __init__(self, maze): self._maze = maze self._strategy = None self._observers = [] def attach(self, observer): self._observers.append(observer) def notify(self, event_type, data): for observer in self._observers: observer.update(event_type, data) def set_strategy(self, strategy): self._strategy = strategy def solve(self): if self._strategy is None: return None start_time = time.perf_counter() path = self._strategy.find_path(self._maze, self._maze.start, self._maze.exit) end_time = time.perf_counter() time_ms = (end_time - start_time) * 1000 self.notify("path_found", path) return SearchStats(time_ms, self._strategy.get_visited_count(), len(path)) def main(): builder = TextFileMazeBuilder() # Загрузка лабиринта из файла if len(sys.argv) > 1: maze = builder.build_from_file(sys.argv[1]) else: maze = builder.build_from_file("maze1.txt") # Создание игрока и визуализации player = Player(maze.start, maze) view = ConsoleView(player) view.render_maze(maze) # Создание решателя solver = MazeSolver(maze) solver.attach(view) print("\n УПРАВЛЕНИЕ:") print(" H (влево) J (вниз) K (вверх) L (вправо)") print(" U - отменить ход Q - выход") print("\n АВТО-ПОИСК:") print(" B - BFS (поиск в ширину)") print(" D - DFS (поиск в глубину)") print(" A - A* (звездочка)") print("\n" + "=" * 50) command_stack = [] while True: key = input("\n Команда > ").lower() if key == 'q': print("\n До свидания!") break elif key == 'b': solver.set_strategy(BFSStrategy()) stats = solver.solve() print(f"\n BFS: время={stats.time_ms:.3f}мс, посещено={stats.visited_cells}, длина={stats.path_length}") elif key == 'd': solver.set_strategy(DFSStrategy()) stats = solver.solve() print(f"\n DFS: время={stats.time_ms:.3f}мс, посещено={stats.visited_cells}, длина={stats.path_length}") elif key == 'a': solver.set_strategy(AStarStrategy()) stats = solver.solve() print(f"\n A*: время={stats.time_ms:.3f}мс, посещено={stats.visited_cells}, длина={stats.path_length}") elif key in ['h', 'j', 'k', 'l']: dirs = {'h': (-1, 0), 'l': (1, 0), 'k': (0, -1), 'j': (0, 1)} cmd = MoveCommand(player, dirs[key], maze) if cmd.execute(): command_stack.append(cmd) view.render_maze_with_player(maze) if player.current == maze.exit: print("\n ПОЗДРАВЛЯЮ! ВЫ НАШЛИ ВЫХОД!") print(f" Всего ходов: {len(command_stack)}") break else: print("\n Туда нельзя – там стена!") elif key == 'u': if command_stack: cmd = command_stack.pop() cmd.undo() view.render_maze_with_player(maze) print("\n Ход отменён") else: print("\n Нечего отменять") else: print("\n Неизвестная команда. Используйте H,J,K,L для движения, U для отмены, Q для выхода") if __name__ == "__main__": main()