Compare commits

2 Commits

Author SHA1 Message Date
Jan Mrna
7c848cd09f Removed DFS, because it sucks 2025-09-21 16:34:31 +02:00
Jan Mrna
5f517c6707 Fixed swapped start/stop points 2025-09-21 16:33:29 +02:00

View File

@@ -210,44 +210,6 @@ class PathFinderBase(ABC):
return self._elapsed_time_ns, self._visited_node_count
class DFS(PathFinderBase):
"""
Recursive depth-first search; returns first path it finds
Not very efficient performance and memory-wise,
also returns very sub-optimal paths
"""
name = "Depth First Search"
def _CalculatePath(self,
point: Point2D,
end_point: Point2D,
path: Optional[list[Point2D]] = None,
visited: Optional[set[Point2D]] = None) -> Optional[Path]:
if visited is None:
visited = set()
if path is None:
path = list()
if self._map is None:
return None # to make mypy happy
# We don't need to know cost in this case, but we still want to track
# how many nodes we've visited
_ = self._map.Visit(point)
# we keep visited nodes in separate list and set,
# as membership check is faster for set than for list,
# but set is not ordered
visited.add(point)
path.append(point)
if point == end_point:
return path
for neighbor in self._map.GetNeighbours(point):
if neighbor not in visited:
res = self._CalculatePath(neighbor, end_point, path, visited)
if res:
return res
return None
class BFS(PathFinderBase):
"""
Iterative breadth-first search
@@ -265,9 +227,9 @@ class BFS(PathFinderBase):
def _CalculatePath(self, start_point: Point2D, end_point: Point2D) -> Optional[Path]:
frontier: Queue[Point2D] = Queue()
frontier.put(end_point) # we start the computation from the end point
self._came_from: dict[Point2D, Optional[Point2D]] = { end_point: None }
self._distance: dict[Point2D, float] = { end_point: 0.0 }
frontier.put(start_point)
self._came_from: dict[Point2D, Optional[Point2D]] = { start_point: None }
self._distance: dict[Point2D, float] = { start_point: 0.0 }
# build flow field
early_exit = False
@@ -279,17 +241,18 @@ class BFS(PathFinderBase):
self._distance[next_point] = self._distance[current] + 1.0
_ = self._map.Visit(next_point) # visit only to track visited node count
self._came_from[next_point] = current
if next_point == start_point:
if next_point == end_point:
# early exit - if you want to build the whole flow field, remove this
early_exit = True
break
# find actual path
path: Path = []
current = start_point
current = end_point
path.append(current)
while self._came_from[current] is not None:
current = self._came_from[current]
path.append(current)
path.reverse()
return path
@@ -304,13 +267,13 @@ class DijkstraAlgorithm(PathFinderBase):
def _CalculatePath(self, start_point: Point2D, end_point: Point2D) -> Optional[Path]:
frontier: PriorityQueue[PrioritizedItem] = PriorityQueue()
came_from: dict[Point2D, Optional[Point2D]] = {end_point: None} # we start from end node
cost_so_far: dict[Point2D, float] = {end_point: 0.0}
came_from: dict[Point2D, Optional[Point2D]] = {start_point: None}
cost_so_far: dict[Point2D, float] = {start_point: 0.0}
frontier.put(PrioritizedItem(end_point, 0.0))
frontier.put(PrioritizedItem(start_point, 0.0))
while not frontier.empty():
current = frontier.get().item
if current == start_point:
if current == end_point:
# early exit - remove if you want to build the whole flow map
break
for next_point in self._map.GetNeighbours(current):
@@ -322,11 +285,12 @@ class DijkstraAlgorithm(PathFinderBase):
came_from[next_point] = current
# build the actual path
path: Path = []
current = start_point
current = end_point
path.append(current)
while came_from[current] is not None:
current = came_from[current]
path.append(current)
path.reverse()
return path
@@ -350,27 +314,27 @@ class GBFS(PathFinderBase):
def _CalculatePath(self, start_point: Point2D, end_point: Point2D) -> Optional[Path]:
frontier: PriorityQueue[PrioritizedItem] = PriorityQueue()
came_from: dict[Point2D, Optional[Point2D]] = {end_point: None}
came_from: dict[Point2D, Optional[Point2D]] = {start_point: None}
frontier.put(PrioritizedItem(end_point, 0.0)) # we start from the end
frontier.put(PrioritizedItem(start_point, 0.0))
# create the flow field
while not frontier.empty():
current = frontier.get().item
if current == start_point:
if current == end_point:
# early exit
break
for next_point in self._map.GetNeighbours(current):
if next_point not in came_from:
priority = self.heuristic(start_point, next_point)
priority = self.heuristic(end_point, next_point)
frontier.put(PrioritizedItem(next_point, priority))
_ = self._map.Visit(next_point) # visit only to track visited node count
came_from[next_point] = current
# create the actual path
path: Path = [start_point]
current = start_point
path: Path = [end_point]
while came_from[current] is not None:
current = came_from[current]
path.append(current)
path.reverse()
return path
@@ -394,28 +358,29 @@ class A_star(PathFinderBase):
def _CalculatePath(self, start_point: Point2D, end_point: Point2D) -> Optional[Path]:
frontier: PriorityQueue[PrioritizedItem] = PriorityQueue()
came_from: dict[Point2D, Optional[Point2D]] = { end_point: None }
cost_so_far: dict[Point2D, float] = { end_point: 0.0 }
came_from: dict[Point2D, Optional[Point2D]] = { start_point: None }
cost_so_far: dict[Point2D, float] = { start_point: 0.0 }
frontier.put(PrioritizedItem(end_point, 0.0))
frontier.put(PrioritizedItem(start_point, 0.0))
while not frontier.empty():
current = frontier.get().item
if current == start_point:
if current == end_point:
# early exit
break
for next_point in self._map.GetNeighbours(current):
new_cost = cost_so_far[current] + self._map.Visit(next_point)
if next_point not in cost_so_far or new_cost < cost_so_far[next_point]:
cost_so_far[next_point] = new_cost
priority = new_cost + self.heuristic(start_point, next_point)
priority = new_cost + self.heuristic(end_point, next_point)
frontier.put(PrioritizedItem(next_point, priority))
came_from[next_point] = current
# create the actual path
path: Path = [start_point]
current = start_point
path: Path = [end_point]
current = end_point
while came_from[current] is not None:
current = came_from[current]
path.append(current)
path.reverse()
return path
#
@@ -431,7 +396,6 @@ def main():
end_point: Point2D = Point2D((1,1))
path_finder_classes: list[type[PathFinderBase]] = [
DFS,
BFS,
DijkstraAlgorithm,
GBFS,