From 90d1962fe139735621d4fb1c9dcf48719cf73614 Mon Sep 17 00:00:00 2001 From: blessedefe4real <234967906+blessedefe4real@users.noreply.github.com> Date: Sat, 7 Nov 2026 10:44:34 +0000 Subject: [PATCH] Return an empty path from A* when the goal is unreachable When the open set was exhausted, AStarPlanner.planning only broke out of the search loop and then reconstructed a path from the goal node, whose parent was never set. It returned [gx], [gy], a one-point "path" at the goal, so callers could not tell failure from success. Return empty lists instead, as proposed for Dijkstra in #1418. Add regression tests for an unreachable goal and for start equal to goal. --- PathPlanning/AStar/a_star.py | 3 ++- tests/test_a_star_unreachable_goal.py | 35 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/test_a_star_unreachable_goal.py diff --git a/PathPlanning/AStar/a_star.py b/PathPlanning/AStar/a_star.py index 6d20350432..dcf08da610 100644 --- a/PathPlanning/AStar/a_star.py +++ b/PathPlanning/AStar/a_star.py @@ -61,6 +61,7 @@ def planning(self, sx, sy, gx, gy): output: rx: x position list of the final path ry: y position list of the final path + Both lists are empty when the goal cannot be reached. """ start_node = self.Node(self.calc_xy_index(sx, self.min_x), @@ -74,7 +75,7 @@ def planning(self, sx, sy, gx, gy): while True: if len(open_set) == 0: print("Open set is empty..") - break + return [], [] c_id = min( open_set, diff --git a/tests/test_a_star_unreachable_goal.py b/tests/test_a_star_unreachable_goal.py new file mode 100644 index 0000000000..98b87b6423 --- /dev/null +++ b/tests/test_a_star_unreachable_goal.py @@ -0,0 +1,35 @@ +import conftest +from PathPlanning.AStar import a_star as m + + +def test_unreachable_goal_returns_empty_path(): + m.show_animation = False + # Square boundary from 0 to 10 with an inner wall at x = 5. + ox, oy = [], [] + for i in range(11): + ox.extend([0.0, 10.0, float(i), float(i), 5.0]) + oy.extend([float(i), float(i), 0.0, 10.0, float(i)]) + planner = m.AStarPlanner(ox, oy, 1.0, 0.5) + + rx, ry = planner.planning(2.0, 5.0, 8.0, 5.0) + + assert rx == [] + assert ry == [] + + +def test_start_equal_to_goal_returns_single_point_path(): + m.show_animation = False + ox, oy = [], [] + for i in range(11): + ox.extend([0.0, 10.0, float(i), float(i)]) + oy.extend([float(i), float(i), 0.0, 10.0]) + planner = m.AStarPlanner(ox, oy, 1.0, 0.5) + + rx, ry = planner.planning(2.0, 5.0, 2.0, 5.0) + + assert rx == [2.0] + assert ry == [5.0] + + +if __name__ == '__main__': + conftest.run_this_test(__file__)