2026-rff_mp/zhigalovrd/lab2/strategies.py

91 lines
3.3 KiB
Python
Raw Normal View History

from abc import ABC, abstractmethod
from collections import deque
from typing import List, Tuple, Dict, Optional
import heapq
from maze import Maze, Cell
class PathFindingStrategy(ABC):
@abstractmethod
def find_path(self, maze: Maze, start: Optional[Cell], exit: Optional[Cell]) -> Tuple[List[Cell], int]:
pass
class BFSStrategy(PathFindingStrategy):
def find_path(self, maze: Maze, start: Optional[Cell], exit: Optional[Cell]) -> Tuple[List[Cell], int]:
if start is None or exit is None:
return [], 0
queue = deque([start])
visited = {start}
parent = {start: None}
while queue:
current = queue.popleft()
if current is exit:
return self._reconstruct_path(parent, exit), len(visited)
for nb in maze.get_neighbors(current):
if nb not in visited:
visited.add(nb)
parent[nb] = current
queue.append(nb)
return [], len(visited)
def _reconstruct_path(self, parent: Dict[Cell, Optional[Cell]], end: Cell) -> List[Cell]:
path = []
cur = end
while cur is not None:
path.append(cur)
cur = parent[cur]
path.reverse()
return path
class DFSStrategy(PathFindingStrategy):
def find_path(self, maze: Maze, start: Optional[Cell], exit: Optional[Cell]) -> Tuple[List[Cell], int]:
if start is None or exit is None:
return [], 0
stack = [(start, [start])]
visited = {start}
while stack:
current, path = stack.pop()
if current is exit:
return path, len(visited)
for nb in maze.get_neighbors(current):
if nb not in visited:
visited.add(nb)
stack.append((nb, path + [nb]))
return [], len(visited)
class AStarStrategy(PathFindingStrategy):
@staticmethod
def heuristic(a: Cell, b: Cell) -> int:
return abs(a.x - b.x) + abs(a.y - b.y)
def find_path(self, maze: Maze, start: Optional[Cell], exit: Optional[Cell]) -> Tuple[List[Cell], int]:
if start is None or exit is None:
return [], 0
open_set = []
heapq.heappush(open_set, (0, start))
came_from = {}
g_score = {start: 0}
f_score = {start: self.heuristic(start, exit)}
visited_count = 0
while open_set:
_, current = heapq.heappop(open_set)
visited_count += 1
if current is exit:
path = self._reconstruct_path(came_from, exit)
return path, visited_count
for nb in maze.get_neighbors(current):
tentative_g = g_score[current] + 1
if nb not in g_score or tentative_g < g_score[nb]:
came_from[nb] = current
g_score[nb] = tentative_g
f_score[nb] = tentative_g + self.heuristic(nb, exit)
heapq.heappush(open_set, (f_score[nb], nb))
return [], visited_count
def _reconstruct_path(self, came_from: Dict[Cell, Cell], current: Cell) -> List[Cell]:
path = [current]
while current in came_from:
current = came_from[current]
path.append(current)
path.reverse()
return path