from abc import ABC, abstractmethod from typing import List, Dict, Optional, Tuple 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 @property @abstractmethod def name(self) -> str: pass class BFSStrategy(PathFindingStrategy):#в ширину @property def name(self) -> str: return "BFS" def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: path, _ = self._find_path_with_stats(maze, start, exit_cell) return path def _find_path_with_stats(self, maze: Maze, start: Cell, exit_cell: Cell) -> tuple: if start == exit_cell: return [start], 1 from collections import deque queue = deque([start]) visited = {start} parent = {start: None} while queue: current = queue.popleft() if current == exit_cell: return self._reconstruct_path(parent, exit_cell), len(visited) for neighbor in maze.get_neighbors(current): if neighbor not in visited: visited.add(neighbor) parent[neighbor] = current queue.append(neighbor) return [], len(visited) def _reconstruct_path(self, parent: dict, exit_cell: Cell) -> List[Cell]: path = [] current = exit_cell while current is not None: path.append(current) current = parent[current] return list(reversed(path)) class DFSStrategy(PathFindingStrategy):#в глубину @property def name(self) -> str: return "DFS" def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: path, _ = self._find_path_with_stats(maze, start, exit_cell) return path def _find_path_with_stats(self, maze: Maze, start: Cell, exit_cell: Cell) -> tuple: if start == exit_cell: return [start], 1 stack = [(start, [start])] visited = {start} while stack: current, path = stack.pop() if current == exit_cell: return path, len(visited) for neighbor in maze.get_neighbors(current): if neighbor not in visited: visited.add(neighbor) stack.append((neighbor, path + [neighbor])) return [], len(visited) class AStarStrategy(PathFindingStrategy): #A* @property def name(self) -> str: return "A*" 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]: path, _ = self._find_path_with_stats(maze, start, exit_cell) return path def _find_path_with_stats(self, maze: Maze, start: Cell, exit_cell: Cell) -> tuple: import heapq if start == exit_cell: return [start], 1 counter = 0 open_set = [(0, counter, start)] came_from = {} visited = {start} g_score = {start: 0} f_score = {start: self._heuristic(start, exit_cell)} while open_set: current = heapq.heappop(open_set)[2] if current == exit_cell: return self._reconstruct_path(came_from, exit_cell), len(visited) for neighbor in maze.get_neighbors(current): visited.add(neighbor) 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_score[neighbor] = tentative_g + self._heuristic(neighbor, exit_cell) counter += 1 heapq.heappush(open_set, (f_score[neighbor], counter, neighbor)) return [], len(visited) def _reconstruct_path(self, came_from: dict, current: Cell) -> List[Cell]: path = [current] while current in came_from: current = came_from[current] path.append(current) return list(reversed(path))