2026-rff_mp/famutdinovmd/strategies.py

107 lines
4.0 KiB
Python

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))