Skip to content

Commit

Permalink
Use std::optional for A* path return types
Browse files Browse the repository at this point in the history
  • Loading branch information
orzechow committed Oct 4, 2024
1 parent 704d91b commit 063ae1e
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 21 deletions.
9 changes: 7 additions & 2 deletions demo/include/demo/change_dot_cluster_behavior.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ class ChangeDotClusterBehavior : public arbitration_graphs::Behavior<Command> {
}

Command getCommand(const Time& /*time*/) override {
Path pathToTargetClusterCenter = environmentModel_->pathTo(targetCluster_->center);
return Command{pathToTargetClusterCenter};
std::optional<Path> pathToTargetClusterCenter = environmentModel_->pathTo(targetCluster_->center);

if (!pathToTargetClusterCenter) {
throw std::runtime_error("Failed to compute path to target cluster. Can not provide a sensible command.");
}

return Command{pathToTargetClusterCenter.value()};
}

bool checkInvocationCondition(const Time& /*time*/) const override;
Expand Down
2 changes: 1 addition & 1 deletion demo/include/demo/environment_model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class EnvironmentModel {
return astar_.mazeDistance(start, goal);
}

Path pathTo(const Position& goal) {
std::optional<Path> pathTo(const Position& goal) {
return astar_.shortestPath(pacmanPosition(), goal);
}

Expand Down
4 changes: 2 additions & 2 deletions demo/include/utils/astar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class AStar {
/**
* @brief Returns the shortest path from the start to the goal position considering the maze geometry.
*/
Path shortestPath(const Position& start, const Position& goal) const;
std::optional<Path> shortestPath(const Position& start, const Position& goal) const;

/**
* @brief Returns the path from a given start position to the closest dot.
Expand All @@ -82,7 +82,7 @@ class AStar {
* Will expand the path backwards until no more predecessor relationship is available. If the cell at the goal
* position does not have a predecessor, the path will be empty.
*/
Path pathTo(const AStarMazeAdapter& maze, const Position& goal) const;
Path extractPathTo(const AStarMazeAdapter& maze, const Position& goal) const;

/**
* @brief Approximates the distance of a given cell to a goal while considering a shortcut via the tunnel.
Expand Down
16 changes: 8 additions & 8 deletions demo/src/astar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ int AStar::mazeDistance(const Position& start, const Position& goal) const {
return distanceCache_.cached({start, goal}).value();
}

Path path = shortestPath(start, goal);
int pathLength = static_cast<int>(path.size());
std::optional<Path> path = shortestPath(start, goal);
int pathLength = path ? static_cast<int>(path->size()) : NoPathFound;

distanceCache_.cache({start, goal}, pathLength);
return pathLength;
}

Path AStar::shortestPath(const Position& start, const Position& goal) const {
std::optional<Path> AStar::shortestPath(const Position& start, const Position& goal) const {
// There is a "virtual" position outside of the maze that entities are on when entering the tunnel. We accept a
// small error in the distance computation by neglecting this and wrapping the position to be on either end of the
// tunnel.
Expand All @@ -39,12 +39,12 @@ Path AStar::shortestPath(const Position& start, const Position& goal) const {

while (!openSet.empty()) {
if (openSet.top().position == wrappedGoal) {
return pathTo(mazeAdapter, openSet.top().position);
return extractPathTo(mazeAdapter, openSet.top().position);
}
expandCell(openSet, mazeAdapter, heuristic);
}

return {};
return std::nullopt;
}

std::optional<Path> AStar::pathToClosestDot(const Position& start) const {
Expand All @@ -70,12 +70,12 @@ std::optional<Path> AStar::pathToClosestDot(const Position& start) const {
// Unfortunately, the pacman simulation will handle the dot consumption after the move, therefore we need to
// explicitly exclude the start position from the search.
if (openSet.top().type == TileType::DOT && openSet.top().position != start) {
return pathTo(mazeAdapter, openSet.top().position);
return extractPathTo(mazeAdapter, openSet.top().position);
}
expandCell(openSet, mazeAdapter, heuristic);
}

return {};
return std::nullopt;
}

void AStar::expandCell(Set& openSet, AStarMazeAdapter& mazeAdapter, const HeuristicFunction& heuristic) const {
Expand Down Expand Up @@ -108,7 +108,7 @@ void AStar::expandCell(Set& openSet, AStarMazeAdapter& mazeAdapter, const Heuris
}
}

Path AStar::pathTo(const AStarMazeAdapter& maze, const Position& goal) const {
Path AStar::extractPathTo(const AStarMazeAdapter& maze, const Position& goal) const {
Path path;
Cell current = maze.cell(goal);
while (current.moveFromPredecessor) {
Expand Down
19 changes: 11 additions & 8 deletions demo/test/astar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,12 @@ TEST_F(AStarTest, path) {
environmentModel_->setMaze({5, 5}, str);

AStar astar(environmentModel_->maze());
Path path = astar.shortestPath({2, 1}, {3, 3});
std::optional<Path> path = astar.shortestPath({2, 1}, {3, 3});
Path targetPath = {Direction::LEFT, Direction::DOWN, Direction::DOWN, Direction::RIGHT, Direction::RIGHT};
ASSERT_EQ(path.size(), targetPath.size());
ASSERT_TRUE(path.has_value());
ASSERT_EQ(path->size(), targetPath.size());
for (int i = 0; i < targetPath.size(); i++) {
EXPECT_EQ(path.at(i), targetPath.at(i));
EXPECT_EQ(path->at(i), targetPath.at(i));
}
}

Expand All @@ -112,13 +113,15 @@ TEST_F(AStarTest, pathWithTunnel) {
environmentModel_->setMaze({5, 5}, str);

AStar astar(environmentModel_->maze());
Path path = astar.shortestPath({0, 2}, {4, 2});
ASSERT_EQ(path.size(), 1);
EXPECT_EQ(path.front(), demo::Direction::LEFT);
std::optional<Path> path = astar.shortestPath({0, 2}, {4, 2});
ASSERT_TRUE(path.has_value());
ASSERT_EQ(path->size(), 1);
EXPECT_EQ(path->front(), demo::Direction::LEFT);

path = astar.shortestPath({4, 2}, {0, 2});
ASSERT_EQ(path.size(), 1);
EXPECT_EQ(path.front(), demo::Direction::RIGHT);
ASSERT_TRUE(path.has_value());
ASSERT_EQ(path->size(), 1);
EXPECT_EQ(path->front(), demo::Direction::RIGHT);
}

TEST_F(AStarTest, pathToClosestDot) {
Expand Down

0 comments on commit 063ae1e

Please sign in to comment.