From 9c066a3e03873cb6af33816b8f85c968d17f032d Mon Sep 17 00:00:00 2001 From: Dima Date: Sat, 23 May 2026 19:51:32 +0300 Subject: [PATCH] =?UTF-8?q?5=20=D1=8D=D1=82=D0=B0=D0=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nikolaevda/task2/Zadanie2.py | 347 ++++++++++++++++++++++++++++++++++- 1 file changed, 346 insertions(+), 1 deletion(-) diff --git a/nikolaevda/task2/Zadanie2.py b/nikolaevda/task2/Zadanie2.py index 05605f0..cfe968f 100644 --- a/nikolaevda/task2/Zadanie2.py +++ b/nikolaevda/task2/Zadanie2.py @@ -320,4 +320,349 @@ class MazeSolver: path_length=len(path) if path else 0 ) - return path, stats \ No newline at end of file + return path, stats + +# ==================== ЭТАП 5: НАБЛЮДАТЕЛЬ (OBSERVER) ==================== + +class Observer: + """Интерфейс наблюдателя""" + + def update(self, event, data): + raise NotImplementedError + + +class ConsoleDisplay(Observer): + """Консольная визуализация - наблюдатель""" + + def __init__(self): + self._last_path = None + self._last_maze = None + + def update(self, event, data): + if event == "maze_loaded": + self._draw_maze(data) + elif event == "path_found": + self._last_path = data + self._show_path(data) + elif event == "player_moved": + self._draw_maze_with_player(data) + + def _draw_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): + line = "" + for x in range(maze.width): + cell = maze.get_cell(x, y) + if cell == maze.start: + line += "S " + elif cell == maze.exit: + line += "E " + elif cell.is_wall: + line += "# " + else: + line += ". " + print(line) + + print("=" * (maze.width * 2 + 4)) + print("S - старт E - выход # - стена . - проход") + + def _draw_maze_with_player(self, game_state): + """Отрисовка лабиринта с игроком""" + maze = game_state['maze'] + player = game_state['player'] + + 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): + line = "" + for x in range(maze.width): + cell = maze.get_cell(x, y) + if player and cell == player.get_position(): + line += "P " + elif cell == maze.start: + line += "S " + elif cell == maze.exit: + line += "E " + elif cell.is_wall: + line += "# " + else: + line += ". " + print(line) + + print("=" * (maze.width * 2 + 4)) + if player: + pos = player.get_position() + print(f"Игрок: ({pos.x}, {pos.y})") + print("S - старт E - выход # - стена . - проход P - игрок") + + def _show_path(self, path): + """Показ информации о найденном пути""" + if not path: + print("\n Путь не найден!") + return + print(f"\n Путь найден! Длина: {len(path)} клеток") + + +# ==================== ЭТАП 5: КОМАНДА (COMMAND) ==================== + +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._dx, self._dy = direction + self._maze = maze + self._executed = False + self._prev_position = None + + def execute(self): + """Выполнение перемещения""" + if self._executed: + return False + + pos = self._player.get_position() + new_x = pos.x + self._dx + new_y = pos.y + self._dy + target = self._maze.get_cell(new_x, new_y) + + if target and target.is_passable(): + self._prev_position = pos + self._player.set_position(target) + self._executed = True + return True + return False + + def undo(self): + """Отмена перемещения""" + if not self._executed or self._prev_position is None: + return False + + self._player.set_position(self._prev_position) + self._executed = False + return True + + def get_name(self): + dir_names = {(-1, 0): "ВЛЕВО", (1, 0): "ВПРАВО", (0, -1): "ВВЕРХ", (0, 1): "ВНИЗ"} + return f"Перемещение {dir_names.get((self._dx, self._dy), 'НЕИЗВЕСТНО')}" + + +class CommandInvoker: + """Инвокер команд (история для undo/redo)""" + + def __init__(self): + self._history = [] + self._redo_stack = [] + + def execute(self, command): + """Выполнение команды с сохранением в истории""" + if command.execute(): + self._history.append(command) + self._redo_stack.clear() + return True + return False + + def undo(self): + """Отмена последней команды""" + if not self._history: + return False + + command = self._history.pop() + if command.undo(): + self._redo_stack.append(command) + return True + return False + + def redo(self): + """Повтор отменённой команды""" + if not self._redo_stack: + return False + + command = self._redo_stack.pop() + if command.execute(): + self._history.append(command) + return True + return False + + def get_history_size(self): + return len(self._history) + + +# ==================== ЭТАП 5: ИГРОК ==================== + +class Player: + """Игрок, перемещающийся по лабиринту""" + + def __init__(self, start_cell): + self._position = start_cell + self._start = start_cell + + def get_position(self): + return self._position + + def set_position(self, cell): + self._position = cell + + def reset(self): + self._position = self._start + + def is_at_exit(self, maze): + return self._position == maze.exit + + def get_steps_count(self, invoker): + return invoker.get_history_size() + + + +class GameController: + """контроллер, объединяющий все компоненты""" + + def __init__(self, maze): + self.maze = maze + self.player = Player(maze.start) + self.solver = MazeSolver(maze) + self.invoker = CommandInvoker() + self.view = ConsoleDisplay() + + def run(self): + """запуск интерактивного режима""" + self.view.update("maze_loaded", self.maze) + + print("УПРАВЛЕНИЕ:") + print(" H/J/K/L или ←/↓/↑/→ - движение") + print(" U - отменить ход") + print(" R - повторить ход") + print(" B - BFS поиск пути") + print(" D - DFS поиск пути") + print(" A - A* поиск пути") + print(" P - показать путь") + print(" Q - выход") + + path = None + last_strategy_name = "" + + while True: + cmd = input("\nКоманда > ").lower() + + if cmd == 'q': + print("До свидания!") + break + + elif cmd in ['h', 'j', 'k', 'l']: + dir_map = {'h': (-1, 0), 'l': (1, 0), 'k': (0, -1), 'j': (0, 1)} + command = MoveCommand(self.player, dir_map[cmd], self.maze) + + if self.invoker.execute(command): + self.view.update("player_moved", { + 'maze': self.maze, + 'player': self.player + }) + + if self.player.is_at_exit(self.maze): + print(f"\n *** ПОБЕДА! ВЫХОД ДОСТИГНУТ за {self.player.get_steps_count(self.invoker)} шагов! ***") + break + else: + print(" Стена! Нельзя пройти.") + + elif cmd == 'u': + if self.invoker.undo(): + self.view.update("player_moved", { + 'maze': self.maze, + 'player': self.player + }) + print(" Отменено") + else: + print(" Нечего отменять") + + elif cmd == 'r': + if self.invoker.redo(): + self.view.update("player_moved", { + 'maze': self.maze, + 'player': self.player + }) + print(" Повторено") + else: + print(" Нечего повторять") + + elif cmd == 'b': + self.solver.setStrategy(BFSStrategy()) + start_time = time.perf_counter() + path, stats = self.solver.solve() + self.view.update("path_found", path) + print(f" BFS: {stats}") + last_strategy_name = "BFS" + + elif cmd == 'd': + self.solver.setStrategy(DFSStrategy()) + path, stats = self.solver.solve() + self.view.update("path_found", path) + print(f" DFS: {stats}") + last_strategy_name = "DFS" + + elif cmd == 'a': + self.solver.setStrategy(AStarStrategy()) + path, stats = self.solver.solve() + self.view.update("path_found", path) + print(f" A*: {stats}") + last_strategy_name = "A*" + + elif cmd == 'p': + if path: + self._show_path_on_maze(path) + else: + print(" Сначала найдите путь (B, D или A)") + + else: + print(" Неизвестная команда") + + def _show_path_on_maze(self, path): + """показать путь на лабиринте""" + os.system('cls' if os.name == 'nt' else 'clear') + print("=" * (self.maze.width * 2 + 4)) + print("ЛАБИРИНТ С ПУТЁМ (* - путь)") + print("=" * (self.maze.width * 2 + 4)) + + path_set = set(path) + + for y in range(self.maze.height): + line = "" + for x in range(self.maze.width): + cell = self.maze.get_cell(x, y) + if cell == self.player.get_position(): + line += "P " + elif cell == self.maze.start: + line += "S " + elif cell == self.maze.exit: + line += "E " + elif cell in path_set and cell.is_passable(): + line += "* " + elif cell.is_wall: + line += "# " + else: + line += ". " + print(line) + + print("=" * (self.maze.width * 2 + 4)) + print("S - старт E - выход # - стена . - проход * - путь P - игрок") + input("\nНажмите Enter для продолжения...") + self.view.update("player_moved", { + 'maze': self.maze, + 'player': self.player + }) \ No newline at end of file