2026-rff_mp/ivanchenkoam/maze_project/strategies.py

148 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Паттерн Strategy - алгоритмы поиска пути"""
from abc import ABC, abstractmethod
from collections import deque
import heapq
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]:
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]:
if start == exit_cell:
return [start]
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, start, exit_cell)
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]],
start: Cell, exit_cell: Cell) -> List[Cell]:
path = []
current = exit_cell
while current is not None:
path.append(current)
current = parent.get(current)
path.reverse()
return path
class DFSStrategy(PathFindingStrategy):
"""Поиск в глубину (быстрый, но не гарантирует кратчайший путь)"""
@property
def name(self) -> str:
return "DFS"
def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]:
if start == exit_cell:
return [start]
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* с манхэттенской эвристикой"""
@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]:
if start == exit_cell:
return [start]
counter = 0
open_set = [(0, counter, start)]
came_from: Dict[Cell, Optional[Cell]] = {start: None}
g_score = {start: 0}
f_score = {start: self._heuristic(start, exit_cell)}
closed_set = set()
while open_set:
current_f, _, current = heapq.heappop(open_set)
if current in closed_set:
continue
if current == exit_cell:
return self._reconstruct_path(came_from, start, exit_cell)
closed_set.add(current)
for neighbor in maze.get_neighbors(current):
if neighbor in closed_set:
continue
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)
counter += 1
heapq.heappush(open_set, (f, counter, neighbor))
return []
def _reconstruct_path(self, came_from: Dict[Cell, Optional[Cell]],
start: Cell, exit_cell: Cell) -> List[Cell]:
path = []
current = exit_cell
while current is not None:
path.append(current)
current = came_from.get(current)
path.reverse()
return path