from abc import ABC, abstractmethod from collections import deque from heapq import heappush, heappop from typing import List, Dict, Optional from models import Cell, Maze class PathFindingStrategy(ABC): """Абстрактная стратегия поиска пути""" @abstractmethod def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: """Находит путь от start до exit_cell""" pass class BFSStrategy(PathFindingStrategy): """Поиск в ширину (BFS) - гарантирует кратчайший путь""" def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: queue = deque([start]) visited = {start} parent: Dict[Cell, Optional[Cell]] = {start: None} while queue: current = queue.popleft() if current == exit_cell: return self._reconstruct_path(parent, current) for neighbor in maze.get_neighbors(current): if neighbor not in visited: visited.add(neighbor) parent[neighbor] = current queue.append(neighbor) return [] # Путь не найден def _reconstruct_path(self, parent: Dict[Cell, Optional[Cell]], current: Cell) -> List[Cell]: """Восстанавливает путь от start до current""" path = [] while current is not None: path.append(current) current = parent.get(current) return list(reversed(path)) class DFSStrategy(PathFindingStrategy): """Поиск в глубину (DFS) - быстрый, но не обязательно кратчайший""" def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: stack = [(start, [start])] visited = {start} while stack: current, path = stack.pop() if current == exit_cell: return path for neighbor in maze.get_neighbors(current): if neighbor not in visited: visited.add(neighbor) stack.append((neighbor, path + [neighbor])) return [] class AStarStrategy(PathFindingStrategy): """A* поиск - оптимальный баланс скорости и кратчайшего пути""" def _heuristic(self, cell: Cell, exit_cell: Cell) -> int: """Манхэттенское расстояние (эвристика)""" return abs(cell.x - exit_cell.x) + abs(cell.y - exit_cell.y) def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: counter = 0 # для разрешения конфликтов в куче open_set = [(self._heuristic(start, exit_cell), counter, start)] g_score: Dict[Cell, float] = {start: 0} parent: Dict[Cell, Optional[Cell]] = {start: None} while open_set: _, _, current = heappop(open_set) if current == exit_cell: return self._reconstruct_path(parent, current) for neighbor in maze.get_neighbors(current): tentative_g = g_score[current] + 1 if neighbor not in g_score or tentative_g < g_score[neighbor]: parent[neighbor] = current g_score[neighbor] = tentative_g counter += 1 f = tentative_g + self._heuristic(neighbor, exit_cell) heappush(open_set, (f, counter, neighbor)) return [] def _reconstruct_path(self, parent: Dict[Cell, Optional[Cell]], current: Cell) -> List[Cell]: """Восстанавливает путь от start до current""" path = [] while current is not None: path.append(current) current = parent.get(current) return list(reversed(path))