diff --git a/include/omath/pathfinding/Astar.hpp b/include/omath/pathfinding/Astar.hpp index 921375c..31aa127 100644 --- a/include/omath/pathfinding/Astar.hpp +++ b/include/omath/pathfinding/Astar.hpp @@ -9,11 +9,17 @@ namespace omath::pathfinding { + struct PathNode; class Astar final { public: [[nodiscard]] static std::vector FindPath(const Vector3& start, const Vector3& end, const NavigationMesh& navMesh); + private: + [[nodiscard]] + static std::vector ReconstructFinalPath(const std::unordered_map& closedList, const Vector3& current); + [[nodiscard]] + static auto GetPerfectNode(const std::unordered_map& openList, const Vector3& endVertex); }; } \ No newline at end of file diff --git a/source/pathfinding/Astar.cpp b/source/pathfinding/Astar.cpp index 37ec8e1..1ea69a6 100644 --- a/source/pathfinding/Astar.cpp +++ b/source/pathfinding/Astar.cpp @@ -3,10 +3,10 @@ // #include "omath/pathfinding/Astar.hpp" +#include #include #include #include -#include namespace omath::pathfinding @@ -18,45 +18,83 @@ namespace omath::pathfinding }; - std::vector Astar::FindPath(const Vector3 &start, const Vector3 &end, const NavigationMesh &navMesh) + std::vector Astar::ReconstructFinalPath(const std::unordered_map& closedList, + const Vector3& current) + { + std::vector path; + std::optional currentOpt = current; + + while (currentOpt) + { + path.push_back(*currentOpt); + + auto it = closedList.find(*currentOpt); + + if (it == closedList.end()) + break; + + currentOpt = it->second.cameFrom; + } + + std::ranges::reverse(path); + return path; + } + auto Astar::GetPerfectNode(const std::unordered_map& openList, const Vector3& endVertex) + { + return std::ranges::min_element(openList, + [&endVertex](const auto& a, const auto& b) + { + const float fA = a.second.gCost + a.first.DistTo(endVertex); + const float fB = b.second.gCost + b.first.DistTo(endVertex); + return fA < fB; + }); + } + + std::vector Astar::FindPath(const Vector3& start, const Vector3& end, const NavigationMesh& navMesh) { std::unordered_map closedList; std::unordered_map openList; - const auto startVertex = navMesh.GetClosestVertex(start).value(); - const auto endVertex = navMesh.GetClosestVertex(end).value(); + auto maybeStartVertex = navMesh.GetClosestVertex(start); + auto maybeEndVertex = navMesh.GetClosestVertex(end); + + if (!maybeStartVertex || !maybeEndVertex) + return {}; + + const auto startVertex = maybeStartVertex.value(); + const auto endVertex = maybeEndVertex.value(); + openList.emplace(startVertex, PathNode{std::nullopt, 0.f}); while (!openList.empty()) { - const auto perfectVertex = *std::ranges::min_element(openList, - [&endVertex](const auto& a, const auto& b) -> bool - { - const auto aCost = a.second.gCost + a.first.DistTo(endVertex); - const auto bCost = b.second.gCost + b.first.DistTo(endVertex); - return aCost < bCost; - }); + auto currentIt = GetPerfectNode(openList, endVertex); - closedList.emplace(perfectVertex); - openList.erase(perfectVertex.first); + const auto current = currentIt->first; + const auto currentNode = currentIt->second; - for (const auto& neighbor : navMesh.GetNeighbors(perfectVertex.first)) - if (!closedList.contains(neighbor)) - openList.emplace(neighbor, PathNode{perfectVertex.first, neighbor.DistTo(perfectVertex.first) + perfectVertex.second.gCost}); + if (current == endVertex) + return ReconstructFinalPath(closedList, current); - if (perfectVertex.first != endVertex) - continue; + closedList.emplace(current, currentNode); + openList.erase(currentIt); - std::vector path = {}; + for (const auto& neighbor: navMesh.GetNeighbors(current)) + { + if (closedList.contains(neighbor)) + continue; - for (std::optional current = perfectVertex.first; current; current = closedList.at(*current).cameFrom ) - path.push_back(current.value()); + const float tentativeGCost = currentNode.gCost + neighbor.DistTo(current); - return path; + const auto openIt = openList.find(neighbor); + + if (openIt == openList.end() || tentativeGCost < openIt->second.gCost) + openList[neighbor] = PathNode{current, tentativeGCost}; + } } return {}; } -} +} // namespace omath::pathfinding