1
0
forked from UNN/2026-rff_mp
2026-rff_mp/SobolevNS/docs/data/task2_maze/maze_solver/strategies.py

180 lines
5.7 KiB
Python
Raw Normal View History

2026-05-22 10:42:42 +00:00
"""
maze_solver/strategies.py - паттерн Strategy.
Каждая стратегия реализует один и тот же интерфейс PathFindingStrategy
с методом find_path(maze, start, exit_), возвращающим:
{'path': [Cell, ...], 'visited': int}
Стратегии не модифицируют сам лабиринт.
"""
from abc import ABC, abstractmethod
from collections import deque
import heapq
# ---------- интерфейс стратегии ----------
class PathFindingStrategy(ABC):
name = "Strategy"
@abstractmethod
def find_path(self, maze, start, exit_):
"""Возвращает dict с ключами 'path' (list[Cell]) и 'visited' (int).
Если пути нет - path = []."""
# ---------- общая утилита: восстановление пути ----------
def _reconstruct(parents, start, end):
"""Восстанавливает путь по словарю предшественников {(x,y): Cell|None}."""
path = []
cur = end
while cur is not None:
path.append(cur)
cur = parents.get((cur.x, cur.y))
path.reverse()
if path and path[0] is start:
return path
return []
# ---------- BFS ----------
class BFSStrategy(PathFindingStrategy):
"""Поиск в ширину. Гарантирует кратчайший путь по числу шагов
(когда веса всех клеток равны)."""
name = "BFS"
def find_path(self, maze, start, exit_):
queue = deque([start])
parents = {(start.x, start.y): None}
visited = 1
while queue:
cell = queue.popleft()
if cell is exit_:
return {"path": _reconstruct(parents, start, exit_),
"visited": visited}
for nb in maze.get_neighbors(cell):
key = (nb.x, nb.y)
if key not in parents:
parents[key] = cell
visited += 1
queue.append(nb)
return {"path": [], "visited": visited}
# ---------- DFS ----------
class DFSStrategy(PathFindingStrategy):
"""Поиск в глубину. Не гарантирует кратчайший путь, но прост и быстр."""
name = "DFS"
def find_path(self, maze, start, exit_):
stack = [start]
parents = {(start.x, start.y): None}
visited = 1
while stack:
cell = stack.pop()
if cell is exit_:
return {"path": _reconstruct(parents, start, exit_),
"visited": visited}
for nb in maze.get_neighbors(cell):
key = (nb.x, nb.y)
if key not in parents:
parents[key] = cell
visited += 1
stack.append(nb)
return {"path": [], "visited": visited}
# ---------- A* ----------
def _manhattan(a, b):
return abs(a.x - b.x) + abs(a.y - b.y)
class AStarStrategy(PathFindingStrategy):
"""A*-поиск с манхэттенской эвристикой. Учитывает вес клеток (weight)."""
name = "A*"
def find_path(self, maze, start, exit_):
# f = g + h; в куче храним (f, tie, cell)
g_score = {(start.x, start.y): 0}
parents = {(start.x, start.y): None}
tie = 0
heap = [(_manhattan(start, exit_), tie, start)]
visited = 0
closed = set()
while heap:
f, _, cell = heapq.heappop(heap)
key = (cell.x, cell.y)
if key in closed:
continue
closed.add(key)
visited += 1
if cell is exit_:
return {"path": _reconstruct(parents, start, exit_),
"visited": visited}
for nb in maze.get_neighbors(cell):
nb_key = (nb.x, nb.y)
tentative_g = g_score[key] + nb.weight
if tentative_g < g_score.get(nb_key, float("inf")):
g_score[nb_key] = tentative_g
parents[nb_key] = cell
tie += 1
heapq.heappush(heap,
(tentative_g + _manhattan(nb, exit_), tie, nb))
return {"path": [], "visited": visited}
# ---------- Дейкстра ----------
class DijkstraStrategy(PathFindingStrategy):
"""Дейкстра - оптимальный путь с учётом веса клеток.
На немодифицированном лабиринте (все веса = 1) совпадает с BFS."""
name = "Dijkstra"
def find_path(self, maze, start, exit_):
dist = {(start.x, start.y): 0}
parents = {(start.x, start.y): None}
tie = 0
heap = [(0, tie, start)]
visited = 0
closed = set()
while heap:
d, _, cell = heapq.heappop(heap)
key = (cell.x, cell.y)
if key in closed:
continue
closed.add(key)
visited += 1
if cell is exit_:
return {"path": _reconstruct(parents, start, exit_),
"visited": visited}
for nb in maze.get_neighbors(cell):
nb_key = (nb.x, nb.y)
nd = d + nb.weight
if nd < dist.get(nb_key, float("inf")):
dist[nb_key] = nd
parents[nb_key] = cell
tie += 1
heapq.heappush(heap, (nd, tie, nb))
return {"path": [], "visited": visited}
STRATEGIES = {
"BFS": BFSStrategy,
"DFS": DFSStrategy,
"A*": AStarStrategy,
"Dijkstra": DijkstraStrategy,
}