2026-rff_mp/sobininaas/Задание2/pathfinding.py

145 lines
4.0 KiB
Python

from abc import ABC, abstractmethod
from typing import List
from collections import deque
import heapq
from maze_core import Maze, Cell
class SearchStrategy(ABC):
@abstractmethod
def find_path(self, maze: Maze, start: Cell, goal: Cell) -> List[Cell]:
pass
@property
@abstractmethod
def visited_count(self) -> int:
pass
class BFSSearch(SearchStrategy):
def __init__(self):
self._visited = 0
def find_path(self, maze: Maze, start: Cell, goal: Cell) -> List[Cell]:
self._visited = 0
if start == goal:
return [start]
visited = {start}
queue = deque([start])
start.prev = None
while queue:
curr = queue.popleft()
self._visited += 1
if curr == goal:
return self._build_path(curr)
for nb in maze.neighbors(curr):
if nb not in visited:
visited.add(nb)
nb.prev = curr
queue.append(nb)
return []
def _build_path(self, end: Cell) -> List[Cell]:
path = []
while end:
path.append(end)
end = end.prev
return path[::-1]
@property
def visited_count(self) -> int:
return self._visited
class DFSSearch(SearchStrategy):
def __init__(self):
self._visited = 0
def find_path(self, maze: Maze, start: Cell, goal: Cell) -> List[Cell]:
self._visited = 0
if start == goal:
return [start]
visited = set()
stack = [start]
start.prev = None
while stack:
curr = stack.pop()
if curr in visited:
continue
visited.add(curr)
self._visited += 1
if curr == goal:
return self._build_path(curr)
for nb in maze.neighbors(curr):
if nb not in visited:
nb.prev = curr
stack.append(nb)
return []
def _build_path(self, end: Cell) -> List[Cell]:
path = []
while end:
path.append(end)
end = end.prev
return path[::-1]
@property
def visited_count(self) -> int:
return self._visited
class AStarSearch(SearchStrategy):
def __init__(self):
self._visited = 0
def find_path(self, maze: Maze, start: Cell, goal: Cell) -> List[Cell]:
self._visited = 0
if start == goal:
return [start]
counter = 0
open_set = [(self._h(start, goal), counter, start)]
came_from = {}
g_score = {start: 0}
open_hash = {start}
start.prev = None
while open_set:
_, _, curr = heapq.heappop(open_set)
open_hash.discard(curr)
self._visited += 1
if curr == goal:
return self._build_path(curr, came_from)
for nb in maze.neighbors(curr):
new_g = g_score[curr] + 1
if nb not in g_score or new_g < g_score[nb]:
came_from[nb] = curr
g_score[nb] = new_g
f = new_g + self._h(nb, goal)
if nb not in open_hash:
counter += 1
heapq.heappush(open_set, (f, counter, nb))
open_hash.add(nb)
nb.prev = curr
return []
def _h(self, a: Cell, b: Cell) -> float:
return abs(a.x - b.x) + abs(a.y - b.y)
def _build_path(self, end: Cell, came_from: dict) -> List[Cell]:
path = [end]
while end in came_from:
end = came_from[end]
path.append(end)
return path[::-1]
@property
def visited_count(self) -> int:
return self._visited