187 lines
3.7 KiB
Python
187 lines
3.7 KiB
Python
|
|
from abc import ABC, abstractmethod
|
||
|
|
|
||
|
|
from collections import deque
|
||
|
|
import heapq
|
||
|
|
|
||
|
|
|
||
|
|
class PathFindingStrategy(ABC):
|
||
|
|
|
||
|
|
@abstractmethod
|
||
|
|
def find_path(self, maze, start, exit_cell):
|
||
|
|
pass
|
||
|
|
|
||
|
|
|
||
|
|
class BFSStrategy(PathFindingStrategy):
|
||
|
|
|
||
|
|
def find_path(self, maze, start, exit_cell):
|
||
|
|
|
||
|
|
queue = deque([start])
|
||
|
|
|
||
|
|
visited = set()
|
||
|
|
visited.add((start.x, start.y))
|
||
|
|
|
||
|
|
parent = {}
|
||
|
|
|
||
|
|
while queue:
|
||
|
|
|
||
|
|
current = queue.popleft()
|
||
|
|
|
||
|
|
if current == exit_cell:
|
||
|
|
return self.restore_path(parent, start, exit_cell), len(visited)
|
||
|
|
|
||
|
|
for neighbor in maze.get_neighbors(current):
|
||
|
|
|
||
|
|
key = (neighbor.x, neighbor.y)
|
||
|
|
|
||
|
|
if key not in visited:
|
||
|
|
|
||
|
|
visited.add(key)
|
||
|
|
|
||
|
|
parent[key] = current
|
||
|
|
|
||
|
|
queue.append(neighbor)
|
||
|
|
|
||
|
|
return [], len(visited)
|
||
|
|
|
||
|
|
def restore_path(self, parent, start, exit_cell):
|
||
|
|
|
||
|
|
path = []
|
||
|
|
|
||
|
|
current = exit_cell
|
||
|
|
|
||
|
|
while current != start:
|
||
|
|
|
||
|
|
path.append(current)
|
||
|
|
|
||
|
|
current = parent[(current.x, current.y)]
|
||
|
|
|
||
|
|
path.append(start)
|
||
|
|
|
||
|
|
path.reverse()
|
||
|
|
|
||
|
|
return path
|
||
|
|
|
||
|
|
|
||
|
|
class DFSStrategy(PathFindingStrategy):
|
||
|
|
|
||
|
|
def find_path(self, maze, start, exit_cell):
|
||
|
|
|
||
|
|
stack = [start]
|
||
|
|
|
||
|
|
visited = set()
|
||
|
|
visited.add((start.x, start.y))
|
||
|
|
|
||
|
|
parent = {}
|
||
|
|
|
||
|
|
while stack:
|
||
|
|
|
||
|
|
current = stack.pop()
|
||
|
|
|
||
|
|
if current == exit_cell:
|
||
|
|
return self.restore_path(parent, start, exit_cell), len(visited)
|
||
|
|
|
||
|
|
for neighbor in maze.get_neighbors(current):
|
||
|
|
|
||
|
|
key = (neighbor.x, neighbor.y)
|
||
|
|
|
||
|
|
if key not in visited:
|
||
|
|
|
||
|
|
visited.add(key)
|
||
|
|
|
||
|
|
parent[key] = current
|
||
|
|
|
||
|
|
stack.append(neighbor)
|
||
|
|
|
||
|
|
return [], len(visited)
|
||
|
|
|
||
|
|
def restore_path(self, parent, start, exit_cell):
|
||
|
|
|
||
|
|
path = []
|
||
|
|
|
||
|
|
current = exit_cell
|
||
|
|
|
||
|
|
while current != start:
|
||
|
|
|
||
|
|
path.append(current)
|
||
|
|
|
||
|
|
current = parent[(current.x, current.y)]
|
||
|
|
|
||
|
|
path.append(start)
|
||
|
|
|
||
|
|
path.reverse()
|
||
|
|
|
||
|
|
return path
|
||
|
|
|
||
|
|
|
||
|
|
class AStarStrategy(PathFindingStrategy):
|
||
|
|
|
||
|
|
def heuristic(self, cell, exit_cell):
|
||
|
|
|
||
|
|
return abs(cell.x - exit_cell.x) + abs(cell.y - exit_cell.y)
|
||
|
|
|
||
|
|
def find_path(self, maze, start, exit_cell):
|
||
|
|
|
||
|
|
heap = []
|
||
|
|
|
||
|
|
heapq.heappush(heap, (0, start.x, start.y, start))
|
||
|
|
|
||
|
|
visited = set()
|
||
|
|
|
||
|
|
parent = {}
|
||
|
|
|
||
|
|
g_score = {
|
||
|
|
(start.x, start.y): 0
|
||
|
|
}
|
||
|
|
|
||
|
|
while heap:
|
||
|
|
|
||
|
|
_, _, _, current = heapq.heappop(heap)
|
||
|
|
|
||
|
|
key_current = (current.x, current.y)
|
||
|
|
|
||
|
|
if key_current in visited:
|
||
|
|
continue
|
||
|
|
|
||
|
|
visited.add(key_current)
|
||
|
|
|
||
|
|
if current == exit_cell:
|
||
|
|
return self.restore_path(parent, start, exit_cell), len(visited)
|
||
|
|
|
||
|
|
for neighbor in maze.get_neighbors(current):
|
||
|
|
|
||
|
|
key = (neighbor.x, neighbor.y)
|
||
|
|
|
||
|
|
tentative = g_score[key_current] + 1
|
||
|
|
|
||
|
|
if key not in g_score or tentative < g_score[key]:
|
||
|
|
|
||
|
|
g_score[key] = tentative
|
||
|
|
|
||
|
|
priority = tentative + self.heuristic(neighbor, exit_cell)
|
||
|
|
|
||
|
|
heapq.heappush(
|
||
|
|
heap,
|
||
|
|
(priority, neighbor.x, neighbor.y, neighbor)
|
||
|
|
)
|
||
|
|
|
||
|
|
parent[key] = current
|
||
|
|
|
||
|
|
return [], len(visited)
|
||
|
|
|
||
|
|
def restore_path(self, parent, start, exit_cell):
|
||
|
|
|
||
|
|
path = []
|
||
|
|
|
||
|
|
current = exit_cell
|
||
|
|
|
||
|
|
while current != start:
|
||
|
|
|
||
|
|
path.append(current)
|
||
|
|
|
||
|
|
current = parent[(current.x, current.y)]
|
||
|
|
|
||
|
|
path.append(start)
|
||
|
|
|
||
|
|
path.reverse()
|
||
|
|
|
||
|
|
return path
|