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