from abc import ABC, abstractmethod from collections import deque import heapq from classes import Cell, Maze class PathFindingStrategy(ABC): """Интерфейс стратегии поиска пути.""" @abstractmethod def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> list[Cell]: ... # Вспомогательный метод восстановления пути по словарю предшественников @staticmethod def _reconstruct_path(came_from: dict, start: Cell, goal: Cell) -> list[Cell]: path = [] current = goal while current != start: path.append(current) current = came_from[current] path.append(start) path.reverse() return path # ── BFS ────────────────────────────────────────────────────────────────────── class BFSStrategy(PathFindingStrategy): def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> list[Cell]: queue = deque([start]) came_from: dict[Cell, Cell | None] = {start: None} self.visited_count = 0 while queue: current = queue.popleft() self.visited_count += 1 if current == exit_cell: return self._reconstruct_path(came_from, start, exit_cell) for neighbor in maze.get_neighbors(current): if neighbor not in came_from: came_from[neighbor] = current queue.append(neighbor) return [] # путь не найден # ── DFS ────────────────────────────────────────────────────────────────────── class DFSStrategy(PathFindingStrategy): def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> list[Cell]: stack = [start] came_from: dict[Cell, Cell | None] = {start: None} self.visited_count = 0 while stack: current = stack.pop() self.visited_count += 1 if current == exit_cell: return self._reconstruct_path(came_from, start, exit_cell) for neighbor in maze.get_neighbors(current): if neighbor not in came_from: came_from[neighbor] = current stack.append(neighbor) return [] # ── A* ─────────────────────────────────────────────────────────────────────── class AStarStrategy(PathFindingStrategy): """A* с манхэттенской эвристикой""" def __init__(self): self.visited_count = 0 def _heuristic(self, a: Cell, b: Cell) -> int: return abs(a.x - b.x) + abs(a.y - b.y) def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> list[Cell]: g_score = {start: 0} parent: dict[Cell] = {start: None} open_heap = [(self._heuristic(start, exit_cell), 0, start)] closed_set: set[Cell] = set() # уже обработанные клетки self.visited_count = 0 counter = 0 # счётчик для устранения неоднозначности while open_heap: _, _, current = heapq.heappop(open_heap) if current in closed_set: continue closed_set.add(current) self.visited_count += 1 if current == exit_cell: return self._reconstruct_path(parent, start, exit_cell) for neighbor in maze.get_neighbors(current): if neighbor in closed_set: continue tentative_g = g_score[current] if tentative_g < g_score.get(neighbor, float('inf')): g_score[neighbor] = tentative_g parent[neighbor] = current f = tentative_g + self._heuristic(neighbor, exit_cell) counter += 1 heapq.heappush(open_heap, (f, counter, neighbor)) return []