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

218 lines
4.4 KiB
Python
Raw Normal View History

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