From 4465a756ef8f3077a858940221e0920790652f12 Mon Sep 17 00:00:00 2001 From: Bensuperpc Date: Sat, 24 Jan 2026 18:22:57 +0100 Subject: [PATCH] new1 --- include/astar/astar.hpp | 56 ++++++++----------- test/source/benchmark/astar_bench.cpp | 13 +++-- test/source/benchmark/path_finder_compare.cpp | 25 +++++---- test/source/benchmark/path_finder_visual.cpp | 15 ++--- test/source/test/astar_test.cpp | 47 ++++++++++++---- 5 files changed, 88 insertions(+), 68 deletions(-) diff --git a/include/astar/astar.hpp b/include/astar/astar.hpp index d6a1f76..aca2c29 100644 --- a/include/astar/astar.hpp +++ b/include/astar/astar.hpp @@ -74,36 +74,36 @@ class Node { }; 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(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(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; @@ -159,13 +159,13 @@ class AStarVirtual { std::function*)> _debugOpenNode; }; +using IsWalkableFn = std::function; template class AStar final : public AStarVirtual { public: explicit AStar() {} - - std::vector findPath(const Vec2i source, const Vec2i& target) { - if (target.x < 0 || target.x >= _worldSize.x || target.y < 0 || target.y >= _worldSize.y) { + std::vector 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 {}; } @@ -178,8 +178,8 @@ class AStar final : public AStarVirtual { std::unordered_map*, Vec2i::hash> openNodeMap; std::unordered_map*, Vec2i::hash> closedNodeMap; - openNodeVecPQueue.push(new Node(source)); - openNodeMap.insert({source, openNodeVecPQueue.top()}); + openNodeVecPQueue.push(new Node(start)); + openNodeMap.insert({start, openNodeVecPQueue.top()}); while (!openNodeVecPQueue.empty()) { currentNode = openNodeVecPQueue.top(); @@ -199,7 +199,7 @@ class AStar final : public AStarVirtual { for (size_t i = 0; i < AStarVirtual::_directionsCount; ++i) { Vec2i newPos = currentNode->pos + AStarVirtual::_directions[i]; - if (_obstacles.contains(newPos)) { + if (!isWalkable(newPos)) { continue; } @@ -207,7 +207,7 @@ class AStar final : public AStarVirtual { 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; } @@ -251,17 +251,7 @@ class AStar final : public AStarVirtual { return path; } - - void addObstacle(const Vec2i& pos) { _obstacles.insert(pos); } - void removeObstacle(const Vec2i& pos) { _obstacles.erase(pos); } - std::unordered_set& getObstacles() noexcept { return _obstacles; } - - void clear() { _obstacles.clear(); } - void setWorldSize(const Vec2i& worldSize_) noexcept { _worldSize = worldSize_; } - private: - std::unordered_set _obstacles; - Vec2i _worldSize = {0, 0}; }; // Fast AStar are faster than normal AStar but use more ram and direct access to the map @@ -271,7 +261,7 @@ class AStarFast final : public AStarVirtual { explicit AStarFast() : _isObstacleFunction([](MapElementType value) { return value == 1; }) {} // Same as AStar::findPath() but use direct access to the map - std::vector findPath(const Vec2i& source, const Vec2i& target, const std::vector& map, const Vec2i& worldSize) { + std::vector findPath(const Vec2i& start, const Vec2i& target, const std::vector& map, const Vec2i& worldSize) { if (target.x < 0 || target.x >= worldSize.x || target.y < 0 || target.y >= worldSize.y) { return {}; } @@ -284,8 +274,8 @@ class AStarFast final : public AStarVirtual { std::unordered_map*, Vec2i::hash> openNodeMap; std::unordered_map*, Vec2i::hash> closedNodeMap; - openNodeVecPQueue.push(new Node(source)); - openNodeMap.insert({source, openNodeVecPQueue.top()}); + openNodeVecPQueue.push(new Node(start)); + openNodeMap.insert({start, openNodeVecPQueue.top()}); while (!openNodeVecPQueue.empty()) { currentNode = openNodeVecPQueue.top(); diff --git a/test/source/benchmark/astar_bench.cpp b/test/source/benchmark/astar_bench.cpp index 7bb49e4..7139f08 100644 --- a/test/source/benchmark/astar_bench.cpp +++ b/test/source/benchmark/astar_bench.cpp @@ -49,7 +49,6 @@ static void astar_bench(benchmark::State& state) { AStar::AStar 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(x), static_cast(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 path; benchmark::DoNotOptimize(path); + std::function 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"); diff --git a/test/source/benchmark/path_finder_compare.cpp b/test/source/benchmark/path_finder_compare.cpp index 4253bb0..18f5253 100644 --- a/test/source/benchmark/path_finder_compare.cpp +++ b/test/source/benchmark/path_finder_compare.cpp @@ -53,7 +53,6 @@ auto main() -> int { generator_2.setMultiplier((uint32_t)multiplier); AStar::AStar 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(x), static_cast(y)}); } } } blocks[0] = 0; - pathFinder.removeObstacle({0, 0}); blocks[mapWidth * mapHeight - 1] = 0; - pathFinder.removeObstacle({mapWidth - 1, mapHeight - 1}); + + std::function 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) { diff --git a/test/source/benchmark/path_finder_visual.cpp b/test/source/benchmark/path_finder_visual.cpp index e45c36d..7ed4035 100644 --- a/test/source/benchmark/path_finder_visual.cpp +++ b/test/source/benchmark/path_finder_visual.cpp @@ -53,7 +53,6 @@ auto main() -> int { generator_2.setMultiplier((uint32_t)multiplier); AStar::AStar 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(x), static_cast(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 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; diff --git a/test/source/test/astar_test.cpp b/test/source/test/astar_test.cpp index 7add942..ec9f1e7 100644 --- a/test/source/test/astar_test.cpp +++ b/test/source/test/astar_test.cpp @@ -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 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 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 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 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); }