Add A* pathfinding implementation in util
This commit is contained in:
parent
d112a9cbf8
commit
09ecce5e6d
1 changed files with 142 additions and 0 deletions
142
libs/util/include/psemek/util/find_path.hpp
Normal file
142
libs/util/include/psemek/util/find_path.hpp
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#include <deque>
|
||||
#include <optional>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
template <typename Cost, typename Node, typename NeighboursFn, typename HeuristicFn>
|
||||
struct pathfinder
|
||||
{
|
||||
struct node_compare
|
||||
{
|
||||
std::unordered_map<Node, Cost> const & priority;
|
||||
|
||||
bool operator()(Node const & n1, Node const & n2) const
|
||||
{
|
||||
auto const p1 = priority.at(n1);
|
||||
auto const p2 = priority.at(n2);
|
||||
|
||||
if (p1 < p2)
|
||||
return true;
|
||||
if (p1 > p2)
|
||||
return false;
|
||||
return n1 < n2;
|
||||
}
|
||||
};
|
||||
|
||||
NeighboursFn node_neighbours;
|
||||
HeuristicFn heuristic;
|
||||
|
||||
std::unordered_map<Node, Cost> cost;
|
||||
std::unordered_map<Node, Cost> priority;
|
||||
std::unordered_map<Node, Node> previous;
|
||||
std::set<Node, node_compare> queue;
|
||||
|
||||
pathfinder(NeighboursFn && node_neighbours, HeuristicFn && heuristic)
|
||||
: node_neighbours(std::forward<NeighboursFn>(node_neighbours))
|
||||
, heuristic(std::forward<HeuristicFn>(heuristic))
|
||||
, queue(node_compare{priority})
|
||||
{}
|
||||
|
||||
void reset()
|
||||
{
|
||||
queue.clear();
|
||||
previous.clear();
|
||||
priority.clear();
|
||||
cost.clear();
|
||||
}
|
||||
|
||||
void init(Node const & start)
|
||||
{
|
||||
cost[start] = Cost{};
|
||||
priority[start] = cost[start] + heuristic(start);
|
||||
queue.insert(start);
|
||||
}
|
||||
|
||||
bool finished() const
|
||||
{
|
||||
return queue.empty();
|
||||
}
|
||||
|
||||
Node step()
|
||||
{
|
||||
Node const node = *queue.begin();
|
||||
queue.erase(queue.begin());
|
||||
|
||||
auto const node_cost = cost[node];
|
||||
|
||||
node_neighbours(node, [&](Node const & neighbour, Cost const & edge_cost){
|
||||
Cost const new_cost = node_cost + edge_cost;
|
||||
|
||||
auto it = cost.find(neighbour);
|
||||
|
||||
if (it == cost.end() || new_cost < it->second)
|
||||
{
|
||||
if (it != cost.end())
|
||||
queue.erase(neighbour);
|
||||
|
||||
cost[neighbour] = new_cost;
|
||||
priority[neighbour] = new_cost + heuristic(neighbour);
|
||||
previous[neighbour] = node;
|
||||
queue.insert(neighbour);
|
||||
}
|
||||
});
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
bool found(Node const & end) const
|
||||
{
|
||||
return cost.contains(end);
|
||||
}
|
||||
|
||||
// Doesn't return the starting node!
|
||||
template <typename Iterator>
|
||||
Iterator path(Node const & start, Node const & end, Iterator it) const
|
||||
{
|
||||
Node node = end;
|
||||
while (true)
|
||||
{
|
||||
*it++ = node;
|
||||
node = previous.at(node);
|
||||
if (node == start)
|
||||
break;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
std::deque<Node> path(Node const & start, Node const & end) const
|
||||
{
|
||||
std::deque<Node> result;
|
||||
path(start, end, std::front_inserter(result));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Cost, typename Node, typename NeighboursFn, typename HeuristicFn>
|
||||
std::optional<std::deque<Node>> find_path(Node const & start, Node const & end, NeighboursFn && node_neighbours, HeuristicFn && heuristic)
|
||||
{
|
||||
pathfinder<Cost, Node, NeighboursFn, HeuristicFn> helper(std::forward<NeighboursFn>(node_neighbours), std::forward<HeuristicFn>(heuristic));
|
||||
|
||||
helper.init(start);
|
||||
bool found = false;
|
||||
while (!helper.finished())
|
||||
{
|
||||
if (helper.step() == end)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
return helper.path(start, end);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue