2026-rff_mp/SorokinAD/[2]lab_2/strategies.py

218 lines
4.4 KiB
Python

from abc import ABC, abstractmethod
from collections import deque
from heapq import heappush, heappop
from maze import Maze
from cell import Cell
class PathFindingStrategy(ABC):
@abstractmethod
def find_path(
self,
maze: Maze,
start: Cell,
exit: Cell
) -> tuple[list[Cell], int]:
pass
# =========================================================
# BFS
# =========================================================
class BFSStrategy(PathFindingStrategy):
def find_path(self, maze, start, exit):
queue = deque([start])
visited = {start}
parent = {}
visited_count = 0
while queue:
current = queue.popleft()
visited_count += 1
if current == exit:
path = self._restore_path(parent, start, exit)
return path, visited_count
for neighbor in maze.get_neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parent[neighbor] = current
queue.append(neighbor)
return [], visited_count
@staticmethod
def _restore_path(parent, start, exit):
path = []
current = exit
while current != start:
path.append(current)
current = parent[current]
path.append(start)
path.reverse()
return path
# =========================================================
# DFS
# =========================================================
class DFSStrategy(PathFindingStrategy):
def find_path(self, maze, start, exit):
stack = [start]
visited = {start}
parent = {}
visited_count = 0
while stack:
current = stack.pop()
visited_count += 1
if current == exit:
path = self._restore_path(parent, start, exit)
return path, visited_count
for neighbor in maze.get_neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parent[neighbor] = current
stack.append(neighbor)
return [], visited_count
@staticmethod
def _restore_path(parent, start, exit):
path = []
current = exit
while current != start:
path.append(current)
current = parent[current]
path.append(start)
path.reverse()
return path
# =========================================================
# A*
# =========================================================
class AStarStrategy(PathFindingStrategy):
def heuristic(self, cell: Cell, exit: Cell):
return abs(cell.x - exit.x) + abs(cell.y - exit.y)
def find_path(self, maze, start, exit):
open_set = []
heappush(open_set, (0, start.x, start.y, start))
g_score = {
start: 0
}
parent = {}
visited = set()
visited_count = 0
while open_set:
_, _, _, current = heappop(open_set)
if current in visited:
continue
visited.add(current)
visited_count += 1
if current == exit:
path = self._restore_path(parent, start, exit)
return path, visited_count
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]
):
g_score[neighbor] = tentative_g
f_score = tentative_g + self.heuristic(
neighbor,
exit
)
parent[neighbor] = current
heappush(
open_set,
(
f_score,
neighbor.x,
neighbor.y,
neighbor
)
)
return [], visited_count
@staticmethod
def _restore_path(parent, start, exit):
path = []
current = exit
while current != start:
path.append(current)
current = parent[current]
path.append(start)
path.reverse()
return path