Compare commits

..

6 Commits

Author SHA1 Message Date
Bensuperpc 4465a756ef new1 2026-01-24 18:22:57 +01:00
Bensuperpc 511816adea test 2026-01-24 15:53:16 +01:00
Bensuperpc d3247bbc54 Improve perf with static lambda function
Signed-off-by: Bensuperpc <bensuperpc@gmail.com>
2024-06-21 21:51:56 +02:00
Bensuperpc 82f9877897 Change header guard pragma once to more standard ifndef
Signed-off-by: Bensuperpc <bensuperpc@gmail.com>
2024-06-21 21:32:41 +02:00
Bensuperpc 3520477222 Merge remote-tracking branch 'origin/main' 2024-06-21 21:28:46 +02:00
Bensuperpc f10202da96 Improve Templates name and readme
Signed-off-by: Bensuperpc <bensuperpc@gmail.com>
2024-06-21 21:28:11 +02:00
8 changed files with 178 additions and 141 deletions
+1 -1
View File
@@ -226,7 +226,7 @@
{
"name": "dev-common",
"hidden": true,
"inherits": ["dev-mode", "clang-tidy", "cppcheck"],
"inherits": ["dev-mode"],
"cacheVariables": {
"BUILD_MCSS_DOCS": "ON"
}
+16 -2
View File
@@ -1,6 +1,6 @@
# astar
Fast and easy to use header only 2D astar algorithm library in C++20.
Fast and easy to use standalone header only 2D astar algorithm library in C++20.
I made it for learning how the astar algorithm works, try to make the fastest, tested and configurable as possible for my needs (future games and works).
@@ -24,6 +24,20 @@ It is an [astar algorithm](https://en.wikipedia.org/wiki/A*_search_algorithm), t
* [x] Debug mode in template argument and lambda function
* [x] Support direct access and not access to the map
* [x] Unit tests and benchmarks
* [ ] Working CI (WIP)
### Heuristic function
You can set the heuristic function to calculate the distance between two points and return the cost.
| Heuristic | C++ Function | Description |
|-----------|--------------|-------------|
| euclidean | AStar::Heuristic::euclidean | Default |
| manhattan | AStar::Heuristic::manhattan | |
| octagonal | AStar::Heuristic::octagonal | |
| chebyshev | AStar::Heuristic::chebyshev | |
| euclideanNoSQR | AStar::Heuristic::euclideanNoSQR | |
| dijkstra | AStar::Heuristic::dijkstra | Always return 0 |
# How to use it
@@ -182,7 +196,7 @@ Sources and references that I have used to make this library.
* [a-star](https://www.ce.unipr.it/people/medici/a-star.html)$
* [A* Search Algorithm](https://yuminlee2.medium.com/a-search-algorithm-42c1a13fcf9f)
## Bench others astar implementations
## Others astar implementations
The list of others astar implementations that I have benchmarked to compare the performance of my implementation.
+4 -4
View File
@@ -20,8 +20,8 @@ function(add_example NAME)
add_dependencies(run-examples "run_${NAME}")
endfunction()
add_example(basic_example)
add_example(debug_example)
add_example(basic_fast_example)
# add_example(basic_example)
# add_example(debug_example)
# add_example(basic_fast_example)
add_folders(Example)
# add_folders(Example)
+88 -95
View File
@@ -3,7 +3,8 @@
* MIT License
*/
#pragma once
#ifndef BEN_ASTAR_HPP
#define BEN_ASTAR_HPP
#include <algorithm>
#include <cmath>
@@ -18,22 +19,22 @@
#include <unordered_set>
#include <vector>
template <typename T>
concept ArithmeticType = std::is_arithmetic<T>::value;
template <typename CoordinateType>
concept ArithmeticType = std::is_arithmetic<CoordinateType>::value;
template <typename T>
concept IntegerType = std::is_integral<T>::value;
template <typename CoordinateType>
concept IntegerType = std::is_integral<CoordinateType>::value;
template <typename T>
concept FloatingPointType = std::is_floating_point<T>::value;
template <typename CoordinateType>
concept FloatingPointType = std::is_floating_point<CoordinateType>::value;
namespace AStar {
template <IntegerType T = int32_t>
template <IntegerType CoordinateType = int32_t>
class Vec2 {
public:
Vec2() = default;
Vec2(T x_, T y_) : x(x_), y(y_) {}
Vec2(CoordinateType x_, CoordinateType y_) : x(x_), y(y_) {}
bool operator==(const Vec2& pos) const noexcept { return (x == pos.x && y == pos.y); }
Vec2 operator=(const Vec2& pos) noexcept {
@@ -49,67 +50,67 @@ class Vec2 {
size_t operator()(const Vec2& pos) const noexcept { return std::hash<size_t>()(pos.x ^ (pos.y << 4)); }
};
T x = 0;
T y = 0;
CoordinateType x = 0;
CoordinateType y = 0;
};
typedef Vec2<int32_t> Vec2i;
template <IntegerType T = uint32_t>
template <IntegerType CoordinateType = uint32_t>
class Node {
public:
explicit Node() : pos(Vec2i(0, 0)), parentNode(nullptr) {}
explicit Node(const Vec2i& pos, Node* parent = nullptr) : pos(pos), parentNode(parent) {}
explicit Node(const Vec2i& pos, const T pathCost, const T heuristicCost, Node* parent = nullptr)
explicit Node(const Vec2i& pos, const CoordinateType pathCost, const CoordinateType heuristicCost, Node* parent = nullptr)
: pathCost(pathCost), heuristicCost(heuristicCost), pos(pos), parentNode(parent) {}
inline T getTotalCost() const noexcept { return pathCost + heuristicCost; }
inline CoordinateType getTotalCost() const noexcept { return pathCost + heuristicCost; }
struct hash {
size_t operator()(const Node* node) const noexcept { return std::hash<size_t>()(node->pos.x ^ (node->pos.y << 4)); }
};
T pathCost = 0;
T heuristicCost = 0;
CoordinateType pathCost = 0;
CoordinateType heuristicCost = 0;
Vec2i pos = {0, 0};
Node* parentNode = nullptr;
};
namespace Heuristic {
static inline Vec2i deltaVec(const Vec2i& source, const Vec2i& target) noexcept {
return {std::abs(source.x - target.x), std::abs(source.y - target.y)};
static inline Vec2i deltaVec(const Vec2i& start, const Vec2i& target) noexcept {
return {std::abs(start.x - target.x), std::abs(start.y - target.y)};
}
static inline uint32_t manhattan(const Vec2i& source, const Vec2i& target, const uint32_t weight) noexcept {
auto delta = deltaVec(source, target);
static inline uint32_t manhattan(const Vec2i& start, const Vec2i& target, const uint32_t weight) noexcept {
auto delta = deltaVec(start, target);
return weight * (delta.x + delta.y);
}
static inline uint32_t octagonal(const Vec2i& source, const Vec2i& target, const uint32_t weight) noexcept {
auto delta = deltaVec(source, target);
static inline uint32_t octagonal(const Vec2i& start, const Vec2i& target, const uint32_t weight) noexcept {
auto delta = deltaVec(start, target);
return weight * (delta.x + delta.y) + (-6) * std::min(delta.x, delta.y);
}
static inline uint32_t euclidean(const Vec2i& source, const Vec2i& target, const uint32_t weight) noexcept {
auto delta = deltaVec(source, target);
static inline uint32_t euclidean(const Vec2i& start, const Vec2i& target, const uint32_t weight) noexcept {
auto delta = deltaVec(start, target);
return weight * static_cast<uint32_t>(std::sqrt(std::pow(delta.x, 2) + std::pow(delta.y, 2)));
}
static inline uint32_t chebyshev(const Vec2i& source, const Vec2i& target, const uint32_t weight) noexcept {
auto delta = deltaVec(source, target);
static inline uint32_t chebyshev(const Vec2i& start, const Vec2i& target, const uint32_t weight) noexcept {
auto delta = deltaVec(start, target);
return weight * std::max(delta.x, delta.y);
}
static inline uint32_t euclideanNoSQR(const Vec2i& source, const Vec2i& target, const uint32_t weight) noexcept {
auto delta = deltaVec(source, target);
static inline uint32_t euclideanNoSQR(const Vec2i& start, const Vec2i& target, const uint32_t weight) noexcept {
auto delta = deltaVec(start, target);
return weight * static_cast<uint32_t>(std::pow(delta.x, 2) + std::pow(delta.y, 2));
}
static constexpr uint32_t dijkstra([[maybe_unused]] const Vec2i& source,
static constexpr uint32_t dijkstra([[maybe_unused]] const Vec2i& start,
[[maybe_unused]] const Vec2i& target,
const uint32_t weight = 0) noexcept {
return 0;
}
}; // namespace Heuristic
template <IntegerType T = uint32_t, bool enableDebug = false>
template <IntegerType CoordinateType = uint32_t, bool enableDebug = false>
class AStarVirtual {
public:
explicit AStarVirtual()
@@ -117,8 +118,8 @@ class AStarVirtual {
_directionsCount(4),
_heuristicWeight(10),
_mouvemementCost(10),
_debugCurrentNode([](Node<T>*) {}),
_debugOpenNode([](Node<T>*) {}) {
_debugCurrentNode([](Node<CoordinateType>*) {}),
_debugOpenNode([](Node<CoordinateType>*) {}) {
_directions = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
}
void setHeuristic(const std::function<uint32_t(Vec2i, Vec2i, uint32_t)>& heuristic) {
@@ -143,48 +144,48 @@ class AStarVirtual {
std::vector<Vec2i>& getDirections() noexcept { return _directions; }
void setDebugCurrentNode(const std::function<void(Node<T>*)>& debugCurrentNode) noexcept { _debugCurrentNode = debugCurrentNode; }
void setDebugOpenNode(const std::function<void(Node<T>*)>& debugOpenNode) noexcept { _debugOpenNode = debugOpenNode; }
void setDebugCurrentNode(const std::function<void(Node<CoordinateType>*)>& debugCurrentNode) noexcept { _debugCurrentNode = debugCurrentNode; }
void setDebugOpenNode(const std::function<void(Node<CoordinateType>*)>& debugOpenNode) noexcept { _debugOpenNode = debugOpenNode; }
protected:
std::function<uint32_t(Vec2i, Vec2i, uint32_t)> _heuristicFunction;
std::vector<Vec2i> _directions;
size_t _directionsCount;
T _heuristicWeight;
CoordinateType _heuristicWeight;
size_t _mouvemementCost = 10;
// Only used if enableDebug is true
std::function<void(Node<T>*)> _debugCurrentNode;
std::function<void(Node<T>*)> _debugOpenNode;
std::function<void(Node<CoordinateType>*)> _debugCurrentNode;
std::function<void(Node<CoordinateType>*)> _debugOpenNode;
};
template <IntegerType T = uint32_t, bool enableDebug = false>
class AStar final : public AStarVirtual<T, enableDebug> {
using IsWalkableFn = std::function<bool(Vec2i)>;
template <IntegerType CoordinateType = uint32_t, bool enableDebug = false>
class AStar final : public AStarVirtual<CoordinateType, enableDebug> {
public:
explicit AStar() {}
std::vector<Vec2i> findPath(const Vec2i source, const Vec2i& target) {
if (target.x < 0 || target.x >= _worldSize.x || target.y < 0 || target.y >= _worldSize.y) {
std::vector<Vec2i> findPath(const Vec2i start, const Vec2i target, const Vec2i worldSize, IsWalkableFn isWalkable) {
if (target.x < 0 || target.x >= worldSize.x || target.y < 0 || target.y >= worldSize.y) {
return {};
}
Node<T>* currentNode = nullptr;
Node<CoordinateType>* currentNode = nullptr;
auto compareFn = [](const Node<T>* a, const Node<T>* b) { return a->getTotalCost() > b->getTotalCost(); };
std::priority_queue<Node<T>*, std::vector<Node<T>*>, decltype(compareFn)> openNodeVecPQueue =
std::priority_queue<Node<T>*, std::vector<Node<T>*>, decltype(compareFn)>(compareFn);
static auto compareFn = [](const Node<CoordinateType>* a, const Node<CoordinateType>* b) { return a->getTotalCost() > b->getTotalCost(); };
std::priority_queue<Node<CoordinateType>*, std::vector<Node<CoordinateType>*>, decltype(compareFn)> openNodeVecPQueue =
std::priority_queue<Node<CoordinateType>*, std::vector<Node<CoordinateType>*>, decltype(compareFn)>(compareFn);
std::unordered_map<Vec2i, Node<T>*, Vec2i::hash> openNodeMap;
std::unordered_map<Vec2i, Node<T>*, Vec2i::hash> closedNodeMap;
std::unordered_map<Vec2i, Node<CoordinateType>*, Vec2i::hash> openNodeMap;
std::unordered_map<Vec2i, Node<CoordinateType>*, Vec2i::hash> closedNodeMap;
openNodeVecPQueue.push(new Node<T>(source));
openNodeMap.insert({source, openNodeVecPQueue.top()});
openNodeVecPQueue.push(new Node<CoordinateType>(start));
openNodeMap.insert({start, openNodeVecPQueue.top()});
while (!openNodeVecPQueue.empty()) {
currentNode = openNodeVecPQueue.top();
if constexpr (enableDebug) {
AStarVirtual<T, enableDebug>::_debugCurrentNode(currentNode);
AStarVirtual<CoordinateType, enableDebug>::_debugCurrentNode(currentNode);
}
if (currentNode->pos == target) {
@@ -195,10 +196,10 @@ class AStar final : public AStarVirtual<T, enableDebug> {
openNodeMap.erase(currentNode->pos);
closedNodeMap.insert({currentNode->pos, currentNode});
for (size_t i = 0; i < AStarVirtual<T, enableDebug>::_directionsCount; ++i) {
Vec2i newPos = currentNode->pos + AStarVirtual<T, enableDebug>::_directions[i];
for (size_t i = 0; i < AStarVirtual<CoordinateType, enableDebug>::_directionsCount; ++i) {
Vec2i newPos = currentNode->pos + AStarVirtual<CoordinateType, enableDebug>::_directions[i];
if (_obstacles.contains(newPos)) {
if (!isWalkable(newPos)) {
continue;
}
@@ -206,18 +207,18 @@ class AStar final : public AStarVirtual<T, enableDebug> {
continue;
}
if (newPos.x < 0 || newPos.x >= _worldSize.x || newPos.y < 0 || newPos.y >= _worldSize.y) {
if (newPos.x < 0 || newPos.x >= worldSize.x || newPos.y < 0 || newPos.y >= worldSize.y) {
continue;
}
T nextCost = currentNode->pathCost + AStarVirtual<T, enableDebug>::_mouvemementCost;
Node<T>* nextNode = openNodeMap.find(newPos) != openNodeMap.end() ? openNodeMap[newPos] : nullptr;
CoordinateType nextCost = currentNode->pathCost + AStarVirtual<CoordinateType, enableDebug>::_mouvemementCost;
Node<CoordinateType>* nextNode = openNodeMap.find(newPos) != openNodeMap.end() ? openNodeMap[newPos] : nullptr;
if (nextNode == nullptr) {
nextNode = new Node<T>(newPos, currentNode);
nextNode = new Node<CoordinateType>(newPos, currentNode);
nextNode->pathCost = nextCost;
nextNode->heuristicCost = static_cast<T>(AStarVirtual<T, enableDebug>::_heuristicFunction(
nextNode->pos, target, AStarVirtual<T, enableDebug>::_heuristicWeight));
nextNode->heuristicCost = static_cast<CoordinateType>(AStarVirtual<CoordinateType, enableDebug>::_heuristicFunction(
nextNode->pos, target, AStarVirtual<CoordinateType, enableDebug>::_heuristicWeight));
openNodeVecPQueue.push(nextNode);
openNodeMap.insert({nextNode->pos, nextNode});
} else if (nextCost < nextNode->pathCost) {
@@ -226,7 +227,7 @@ class AStar final : public AStarVirtual<T, enableDebug> {
}
if constexpr (enableDebug) {
AStarVirtual<T, enableDebug>::_debugOpenNode(nextNode);
AStarVirtual<CoordinateType, enableDebug>::_debugOpenNode(nextNode);
}
}
}
@@ -250,47 +251,37 @@ class AStar final : public AStarVirtual<T, enableDebug> {
return path;
}
void addObstacle(const Vec2i& pos) { _obstacles.insert(pos); }
void removeObstacle(const Vec2i& pos) { _obstacles.erase(pos); }
std::unordered_set<Vec2i, Vec2i::hash>& getObstacles() noexcept { return _obstacles; }
void clear() { _obstacles.clear(); }
void setWorldSize(const Vec2i& worldSize_) noexcept { _worldSize = worldSize_; }
private:
std::unordered_set<Vec2i, Vec2i::hash> _obstacles;
Vec2i _worldSize = {0, 0};
};
// Fast AStar are faster than normal AStar but use more ram and direct access to the map
template <IntegerType T = uint32_t, bool enableDebug = false, IntegerType U = uint32_t>
class AStarFast final : public AStarVirtual<T, enableDebug> {
template <IntegerType CoordinateType = uint32_t, bool enableDebug = false, IntegerType MapElementType = uint32_t>
class AStarFast final : public AStarVirtual<CoordinateType, enableDebug> {
public:
explicit AStarFast() : _isObstacleFunction([](U value) { return value == 1; }) {}
explicit AStarFast() : _isObstacleFunction([](MapElementType value) { return value == 1; }) {}
// Same as AStar::findPath() but use direct access to the map
std::vector<Vec2i> findPath(const Vec2i& source, const Vec2i& target, const std::vector<U>& map, const Vec2i& worldSize) {
std::vector<Vec2i> findPath(const Vec2i& start, const Vec2i& target, const std::vector<MapElementType>& map, const Vec2i& worldSize) {
if (target.x < 0 || target.x >= worldSize.x || target.y < 0 || target.y >= worldSize.y) {
return {};
}
Node<T>* currentNode = nullptr;
Node<CoordinateType>* currentNode = nullptr;
auto compareFn = [](const Node<T>* a, const Node<T>* b) { return a->getTotalCost() > b->getTotalCost(); };
std::priority_queue<Node<T>*, std::vector<Node<T>*>, decltype(compareFn)> openNodeVecPQueue =
std::priority_queue<Node<T>*, std::vector<Node<T>*>, decltype(compareFn)>(compareFn);
std::unordered_map<Vec2i, Node<T>*, Vec2i::hash> openNodeMap;
std::unordered_map<Vec2i, Node<T>*, Vec2i::hash> closedNodeMap;
static auto compareFn = [](const Node<CoordinateType>* a, const Node<CoordinateType>* b) { return a->getTotalCost() > b->getTotalCost(); };
std::priority_queue<Node<CoordinateType>*, std::vector<Node<CoordinateType>*>, decltype(compareFn)> openNodeVecPQueue =
std::priority_queue<Node<CoordinateType>*, std::vector<Node<CoordinateType>*>, decltype(compareFn)>(compareFn);
std::unordered_map<Vec2i, Node<CoordinateType>*, Vec2i::hash> openNodeMap;
std::unordered_map<Vec2i, Node<CoordinateType>*, Vec2i::hash> closedNodeMap;
openNodeVecPQueue.push(new Node<T>(source));
openNodeMap.insert({source, openNodeVecPQueue.top()});
openNodeVecPQueue.push(new Node<CoordinateType>(start));
openNodeMap.insert({start, openNodeVecPQueue.top()});
while (!openNodeVecPQueue.empty()) {
currentNode = openNodeVecPQueue.top();
if constexpr (enableDebug) {
AStarVirtual<T, enableDebug>::_debugCurrentNode(currentNode);
AStarVirtual<CoordinateType, enableDebug>::_debugCurrentNode(currentNode);
}
if (currentNode->pos == target) {
@@ -301,8 +292,8 @@ class AStarFast final : public AStarVirtual<T, enableDebug> {
openNodeMap.erase(currentNode->pos);
closedNodeMap.insert({currentNode->pos, currentNode});
for (size_t i = 0; i < AStarVirtual<T, enableDebug>::_directionsCount; ++i) {
Vec2i newPos = currentNode->pos + AStarVirtual<T, enableDebug>::_directions[i];
for (size_t i = 0; i < AStarVirtual<CoordinateType, enableDebug>::_directionsCount; ++i) {
Vec2i newPos = currentNode->pos + AStarVirtual<CoordinateType, enableDebug>::_directions[i];
if (_isObstacleFunction(map[newPos.x + newPos.y * worldSize.x])) {
continue;
@@ -316,13 +307,13 @@ class AStarFast final : public AStarVirtual<T, enableDebug> {
continue;
}
T nextCost = currentNode->pathCost + AStarVirtual<T, enableDebug>::_mouvemementCost;
Node<T>* nextNode = openNodeMap.find(newPos) != openNodeMap.end() ? openNodeMap[newPos] : nullptr;
CoordinateType nextCost = currentNode->pathCost + AStarVirtual<CoordinateType, enableDebug>::_mouvemementCost;
Node<CoordinateType>* nextNode = openNodeMap.find(newPos) != openNodeMap.end() ? openNodeMap[newPos] : nullptr;
if (nextNode == nullptr) {
nextNode = new Node<T>(newPos, currentNode);
nextNode = new Node<CoordinateType>(newPos, currentNode);
nextNode->pathCost = nextCost;
nextNode->heuristicCost = static_cast<T>(AStarVirtual<T, enableDebug>::_heuristicFunction(
nextNode->pos, target, AStarVirtual<T, enableDebug>::_heuristicWeight));
nextNode->heuristicCost = static_cast<CoordinateType>(AStarVirtual<CoordinateType, enableDebug>::_heuristicFunction(
nextNode->pos, target, AStarVirtual<CoordinateType, enableDebug>::_heuristicWeight));
openNodeVecPQueue.push(nextNode);
openNodeMap.insert({nextNode->pos, nextNode});
} else if (nextCost < nextNode->pathCost) [[likely]] {
@@ -331,7 +322,7 @@ class AStarFast final : public AStarVirtual<T, enableDebug> {
}
if constexpr (enableDebug) {
AStarVirtual<T, enableDebug>::_debugOpenNode(nextNode);
AStarVirtual<CoordinateType, enableDebug>::_debugOpenNode(nextNode);
}
}
}
@@ -355,11 +346,13 @@ class AStarFast final : public AStarVirtual<T, enableDebug> {
return path;
}
void setObstacle(const std::function<bool(U)>& isObstacleFunction) noexcept { _isObstacleFunction = isObstacleFunction; }
std::function<bool(U)>& getObstacle() noexcept { return _isObstacleFunction; }
void setObstacle(const std::function<bool(MapElementType)>& isObstacleFunction) noexcept { _isObstacleFunction = isObstacleFunction; }
std::function<bool(MapElementType)>& getObstacle() noexcept { return _isObstacleFunction; }
private:
std::function<bool(U)> _isObstacleFunction;
std::function<bool(MapElementType)> _isObstacleFunction;
};
} // namespace AStar
#endif
+12 -9
View File
@@ -18,7 +18,7 @@ static void DoSetup([[maybe_unused]] const benchmark::State& state) {}
static void DoTeardown([[maybe_unused]] const benchmark::State& state) {}
template <IntegerType T>
template <IntegerType CoordinateType>
static void astar_bench(benchmark::State& state) {
auto range = state.range(0);
@@ -47,9 +47,8 @@ static void astar_bench(benchmark::State& state) {
std::vector<uint8_t> blocks = std::vector<uint8_t>(mapWidth * mapHeight, 0);
benchmark::DoNotOptimize(blocks);
AStar::AStar<T, false> pathFinder;
AStar::AStar<CoordinateType, false> pathFinder;
benchmark::DoNotOptimize(pathFinder);
pathFinder.setWorldSize({mapWidth, mapHeight});
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
pathFinder.setDiagonalMovement(true);
@@ -62,15 +61,12 @@ static void astar_bench(benchmark::State& state) {
blocks[index] = 0;
} else {
blocks[index] = 1;
pathFinder.addObstacle({static_cast<int32_t>(x), static_cast<int32_t>(y)});
}
}
}
blocks[0] = 0;
pathFinder.removeObstacle({0, 0});
blocks[mapWidth * mapHeight - 1] = 0;
pathFinder.removeObstacle({mapWidth - 1, mapHeight - 1});
AStar::Vec2i source(0, 0);
AStar::Vec2i target(mapWidth - 1, mapHeight - 1);
@@ -78,8 +74,15 @@ static void astar_bench(benchmark::State& state) {
std::vector<AStar::Vec2i> path;
benchmark::DoNotOptimize(path);
std::function<bool(AStar::Vec2i)> isWalkable = [&blocks, mapWidth, mapHeight](AStar::Vec2i pos) {
if (pos.x < 0 || pos.x >= mapWidth || pos.y < 0 || pos.y >= mapHeight) {
return false;
}
return blocks[pos.x + pos.y * mapWidth] == 0;
};
for (auto _ : state) {
path = pathFinder.findPath(source, target);
path = pathFinder.findPath(source, target, {mapWidth, mapHeight}, isWalkable);
state.PauseTiming();
if (path.size() == 0) {
state.SkipWithError("No path found");
@@ -104,7 +107,7 @@ BENCHMARK(astar_bench<uint32_t>)
->UseRealTime()
->Repetitions(repetitions);
template <IntegerType T>
template <IntegerType CoordinateType>
static void astar_bench_fast(benchmark::State& state) {
auto range = state.range(0);
@@ -133,7 +136,7 @@ static void astar_bench_fast(benchmark::State& state) {
std::vector<uint32_t> blocks = std::vector<uint32_t>(mapWidth * mapHeight, 0);
benchmark::DoNotOptimize(blocks);
AStar::AStarFast<T, false, uint32_t> pathFinder;
AStar::AStarFast<CoordinateType, false, uint32_t> pathFinder;
benchmark::DoNotOptimize(pathFinder);
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
pathFinder.setDiagonalMovement(true);
+13 -12
View File
@@ -53,7 +53,6 @@ auto main() -> int {
generator_2.setMultiplier((uint32_t)multiplier);
AStar::AStar<uint32_t, false> pathFinder;
pathFinder.setWorldSize({mapWidth, mapHeight});
pathFinder.setHeuristic(AStar::Heuristic::manhattan);
pathFinder.setDiagonalMovement(true);
@@ -95,8 +94,6 @@ auto main() -> int {
generator_2.setWeightedStrength(weighted_strength);
generator_2.setMultiplier((uint32_t)multiplier);
pathFinder.clear();
heightmap = generator_2.generate2dMeightmap(0, 0, 0, screenWidth, 0, screenHeight);
for (uint64_t x = 0; x < mapWidth; x++) {
@@ -108,18 +105,22 @@ auto main() -> int {
blocks[index] = 0;
} else {
blocks[index] = 1;
pathFinder.addObstacle({static_cast<int32_t>(x), static_cast<int32_t>(y)});
}
}
}
blocks[0] = 0;
pathFinder.removeObstacle({0, 0});
blocks[mapWidth * mapHeight - 1] = 0;
pathFinder.removeObstacle({mapWidth - 1, mapHeight - 1});
std::function<bool(AStar::Vec2i)> isWalkable = [&blocks, mapWidth, mapHeight](AStar::Vec2i pos) {
if (pos.x < 0 || pos.x >= mapWidth || pos.y < 0 || pos.y >= mapHeight) {
return false;
}
return blocks[pos.x + pos.y * mapWidth] == 0;
};
pathFinder.setHeuristic(AStar::Heuristic::manhattan);
auto start1 = std::chrono::high_resolution_clock::now();
auto path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1});
auto path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1}, {mapWidth, mapHeight}, isWalkable);
auto stop1 = std::chrono::high_resolution_clock::now();
if (path.empty()) {
std::cout << "Path not found" << std::endl;
@@ -134,7 +135,7 @@ auto main() -> int {
}
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1});
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1}, {mapWidth, mapHeight}, isWalkable);
euclideanPathSize = path.size();
for (auto& i : path) {
@@ -143,7 +144,7 @@ auto main() -> int {
}
pathFinder.setHeuristic(AStar::Heuristic::octagonal);
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1});
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1}, {mapWidth, mapHeight}, isWalkable);
octagonalPathSize = path.size();
for (auto& i : path) {
@@ -152,7 +153,7 @@ auto main() -> int {
}
pathFinder.setHeuristic(AStar::Heuristic::chebyshev);
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1});
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1}, {mapWidth, mapHeight}, isWalkable);
chebyshevPathSize = path.size();
for (auto& i : path) {
@@ -161,7 +162,7 @@ auto main() -> int {
}
pathFinder.setHeuristic(AStar::Heuristic::euclideanNoSQR);
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1});
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1}, {mapWidth, mapHeight}, isWalkable);
euclideanNoSQRPathSize = path.size();
for (auto& i : path) {
@@ -170,7 +171,7 @@ auto main() -> int {
}
pathFinder.setHeuristic(AStar::Heuristic::dijkstra);
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1});
path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1}, {mapWidth, mapHeight}, isWalkable);
dijkstraPathSize = path.size();
for (auto& i : path) {
+8 -7
View File
@@ -53,7 +53,6 @@ auto main() -> int {
generator_2.setMultiplier((uint32_t)multiplier);
AStar::AStar<uint32_t, true> pathFinder;
pathFinder.setWorldSize({mapWidth, mapHeight});
pathFinder.setHeuristic(AStar::Heuristic::manhattan);
pathFinder.setDiagonalMovement(true);
@@ -90,8 +89,6 @@ auto main() -> int {
generator_2.setWeightedStrength(weighted_strength);
generator_2.setMultiplier((uint32_t)multiplier);
pathFinder.clear();
heightmap = generator_2.generate2dMeightmap(0, 0, 0, screenWidth, 0, screenHeight);
for (uint64_t x = 0; x < mapWidth; x++) {
@@ -103,14 +100,11 @@ auto main() -> int {
blocks[index] = 0;
} else {
blocks[index] = 1;
pathFinder.addObstacle({static_cast<int32_t>(x), static_cast<int32_t>(y)});
}
}
}
blocks[0] = 0;
pathFinder.removeObstacle({0, 0});
blocks[mapWidth * mapHeight - 1] = 0;
pathFinder.removeObstacle({mapWidth - 1, mapHeight - 1});
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
@@ -128,8 +122,15 @@ auto main() -> int {
pathFinder.setDebugOpenNode(debugOpenNode);
std::function<bool(AStar::Vec2i)> isWalkable = [&blocks, mapWidth, mapHeight](AStar::Vec2i pos) {
if (pos.x < 0 || pos.x >= mapWidth || pos.y < 0 || pos.y >= mapHeight) {
return false;
}
return blocks[pos.x + pos.y * mapWidth] == 0;
};
auto start1 = std::chrono::high_resolution_clock::now();
auto path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1});
auto path = pathFinder.findPath({0, 0}, {mapWidth - 1, mapHeight - 1}, {mapWidth, mapHeight}, isWalkable);
auto stop1 = std::chrono::high_resolution_clock::now();
if (path.empty()) {
std::cout << "Path not found" << std::endl;
+36 -11
View File
@@ -6,15 +6,23 @@ TEST(AStar, basic_path_1) {
int mapWidth = 4;
int mapHeight = 4;
AStar::AStar pathFinder;
pathFinder.setWorldSize({mapWidth, mapWidth});
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
pathFinder.setDiagonalMovement(true);
AStar::Vec2i source(0, 0);
AStar::Vec2i target(mapWidth - 1, mapHeight - 1);
std::function<bool(AStar::Vec2i)> isWalkable = [&pathFinder](AStar::Vec2i pos) {
if (pos.x < 0 || pos.x >= 4 || pos.y < 0 || pos.y >= 4) {
return false;
}
return true;
};
std::cout << "AStar::AStar pathFinder;" << std::endl;
auto path = pathFinder.findPath(source, target);
auto path = pathFinder.findPath(source, target, {mapWidth, mapHeight}, isWalkable);
EXPECT_EQ(path.size(), 4);
@@ -28,14 +36,20 @@ TEST(AStar, basic_path_2) {
int mapWidth = 10;
int mapHeight = 10;
AStar::AStar pathFinder;
pathFinder.setWorldSize({mapWidth, mapWidth});
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
pathFinder.setDiagonalMovement(true);
AStar::Vec2i source(0, 0);
AStar::Vec2i target(mapWidth - 1, mapHeight - 1);
auto path = pathFinder.findPath(source, target);
std::function<bool(AStar::Vec2i)> isWalkable = [&pathFinder](AStar::Vec2i pos) {
if (pos.x < 0 || pos.x >= 10 || pos.y < 0 || pos.y >= 10) {
return false;
}
return true;
};
auto path = pathFinder.findPath(source, target, {mapWidth, mapHeight}, isWalkable);
EXPECT_EQ(path.size(), 10);
@@ -49,14 +63,20 @@ TEST(AStar, basic_diagonal_path_wrong_1) {
int mapWidth = 10;
int mapHeight = 10;
AStar::AStar pathFinder;
pathFinder.setWorldSize({mapWidth, mapHeight});
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
pathFinder.setDiagonalMovement(true);
AStar::Vec2i source(0, 0);
AStar::Vec2i target(19, 19);
auto path = pathFinder.findPath(source, target);
std::function<bool(AStar::Vec2i)> isWalkable = [&pathFinder](AStar::Vec2i pos) {
if (pos.x < 0 || pos.x >= 10 || pos.y < 0 || pos.y >= 10) {
return false;
}
return true;
};
auto path = pathFinder.findPath(source, target, {10, 10}, isWalkable);
EXPECT_EQ(path.size(), 0);
}
@@ -65,18 +85,23 @@ TEST(AStar, basic_diagonal_path_wrong_2) {
int mapWidth = 10;
int mapHeight = 10;
AStar::AStar pathFinder;
pathFinder.setWorldSize({mapWidth, mapHeight});
pathFinder.setHeuristic(AStar::Heuristic::euclidean);
pathFinder.setDiagonalMovement(true);
pathFinder.addObstacle({0, 1});
pathFinder.addObstacle({1, 1});
pathFinder.addObstacle({1, 0});
std::function<bool(AStar::Vec2i)> isWalkable = [&pathFinder](AStar::Vec2i pos) {
if (pos.x < 0 || pos.x >= 10 || pos.y < 0 || pos.y >= 10) {
return false;
}
if ((pos.x == 0 && pos.y == 1) || (pos.x == 1 && pos.y == 1) || (pos.x == 1 && pos.y == 0)) {
return false;
}
return true;
};
AStar::Vec2i source(0, 0);
AStar::Vec2i target(9, 9);
auto path = pathFinder.findPath(source, target);
auto path = pathFinder.findPath(source, target, {10, 10}, isWalkable);
EXPECT_EQ(path.size(), 0);
}