5 этап

This commit is contained in:
Dima 2026-05-23 19:51:32 +03:00
parent 23012e3e84
commit 9c066a3e03

View File

@ -320,4 +320,349 @@ class MazeSolver:
path_length=len(path) if path else 0 path_length=len(path) if path else 0
) )
return path, stats 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
})