from abc import ABC, abstractmethod from typing import List, Dict, Optional from collections import deque import heapq from maze_model import Maze, Cell class PathFindingStrategy(ABC): @abstractmethod def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: pass @abstractmethod def get_name(self) -> str: pass class BFSStrategy(PathFindingStrategy): def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: if start == exit_cell: return [start] queue = deque([start]) came_from: Dict[Cell, Optional[Cell]] = {start: None} while queue: current = queue.popleft() 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 [] def _reconstruct_path(self, came_from, start, exit_cell): path = [] current = exit_cell while current is not None: path.append(current) current = came_from.get(current) path.reverse() return path def get_name(self) -> str: return "BFS (Поиск в ширину)" class DFSStrategy(PathFindingStrategy): def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: if start == exit_cell: return [start] stack = [start] came_from: Dict[Cell, Optional[Cell]] = {start: None} while stack: current = stack.pop() 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 [] def _reconstruct_path(self, came_from, start, exit_cell): path = [] current = exit_cell while current is not None: path.append(current) current = came_from.get(current) path.reverse() return path def get_name(self) -> str: return "DFS (Поиск в глубину)" class AStarStrategy(PathFindingStrategy): def _heuristic(self, cell: Cell, target: Cell) -> int: return abs(cell.x - target.x) + abs(cell.y - target.y) def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: if start == exit_cell: return [start] counter = 0 open_set = [(0, counter, start)] g_score: Dict[Cell, float] = {start: 0} came_from: Dict[Cell, Optional[Cell]] = {start: None} open_set_cells = {start} while open_set: _, _, current = heapq.heappop(open_set) open_set_cells.remove(current) if current == exit_cell: return self._reconstruct_path(came_from, start, exit_cell) 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]: came_from[neighbor] = current g_score[neighbor] = tentative_g f = tentative_g + self._heuristic(neighbor, exit_cell) if neighbor not in open_set_cells: counter += 1 heapq.heappush(open_set, (f, counter, neighbor)) open_set_cells.add(neighbor) return [] def _reconstruct_path(self, came_from, start, exit_cell): path = [] current = exit_cell while current is not None: path.append(current) current = came_from.get(current) path.reverse() return path def get_name(self) -> str: return "A* (A-Star)"