From fa726867f14dd67a24bb3c0deca7f6aa908942d1 Mon Sep 17 00:00:00 2001 From: wraitii Date: Wed, 24 Apr 2019 19:07:32 +0000 Subject: [PATCH] Const-Correct the long range pathfinder Debug variables are kept mutable as one otherwise has to use a complex system and this is really not worth it. Reviewed By: vladislavbelov Differential Revision: https://code.wildfiregames.com/D1491 This was SVN commit r22219. --- source/simulation2/helpers/LongPathfinder.cpp | 23 +++++----- source/simulation2/helpers/LongPathfinder.h | 44 +++++++++++-------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/source/simulation2/helpers/LongPathfinder.cpp b/source/simulation2/helpers/LongPathfinder.cpp index fd1fe972ad..8c842fafca 100644 --- a/source/simulation2/helpers/LongPathfinder.cpp +++ b/source/simulation2/helpers/LongPathfinder.cpp @@ -388,7 +388,7 @@ LongPathfinder::~LongPathfinder() // Calculate heuristic cost from tile i,j to goal // (This ought to be an underestimate for correctness) -PathCost LongPathfinder::CalculateHeuristic(int i, int j, int iGoal, int jGoal) +PathCost LongPathfinder::CalculateHeuristic(int i, int j, int iGoal, int jGoal) const { int di = abs(i - iGoal); int dj = abs(j - jGoal); @@ -397,7 +397,7 @@ PathCost LongPathfinder::CalculateHeuristic(int i, int j, int iGoal, int jGoal) } // Do the A* processing for a neighbour tile i,j. -void LongPathfinder::ProcessNeighbour(int pi, int pj, int i, int j, PathCost pg, PathfinderState& state) +void LongPathfinder::ProcessNeighbour(int pi, int pj, int i, int j, PathCost pg, PathfinderState& state) const { // Reject impassable tiles if (!PASSABLE(i, j)) @@ -501,7 +501,7 @@ inline bool OnTheWay(int i, int j, int di, int dj, int iGoal, int jGoal) } -void LongPathfinder::AddJumpedHoriz(int i, int j, int di, PathCost g, PathfinderState& state, bool detectGoal) +void LongPathfinder::AddJumpedHoriz(int i, int j, int di, PathCost g, PathfinderState& state, bool detectGoal) const { if (m_UseJPSCache) { @@ -543,7 +543,7 @@ void LongPathfinder::AddJumpedHoriz(int i, int j, int di, PathCost g, Pathfinder } // Returns the i-coordinate of the jump point if it exists, else returns i -int LongPathfinder::HasJumpedHoriz(int i, int j, int di, PathfinderState& state, bool detectGoal) +int LongPathfinder::HasJumpedHoriz(int i, int j, int di, PathfinderState& state, bool detectGoal) const { if (m_UseJPSCache) { @@ -579,7 +579,7 @@ int LongPathfinder::HasJumpedHoriz(int i, int j, int di, PathfinderState& state, } } -void LongPathfinder::AddJumpedVert(int i, int j, int dj, PathCost g, PathfinderState& state, bool detectGoal) +void LongPathfinder::AddJumpedVert(int i, int j, int dj, PathCost g, PathfinderState& state, bool detectGoal) const { if (m_UseJPSCache) { @@ -621,7 +621,7 @@ void LongPathfinder::AddJumpedVert(int i, int j, int dj, PathCost g, PathfinderS } // Returns the j-coordinate of the jump point if it exists, else returns j -int LongPathfinder::HasJumpedVert(int i, int j, int dj, PathfinderState& state, bool detectGoal) +int LongPathfinder::HasJumpedVert(int i, int j, int dj, PathfinderState& state, bool detectGoal) const { if (m_UseJPSCache) { @@ -661,7 +661,7 @@ int LongPathfinder::HasJumpedVert(int i, int j, int dj, PathfinderState& state, * We never cache diagonal jump points - they're usually so frequent that * a linear search is about as cheap and avoids the setup cost and memory cost. */ -void LongPathfinder::AddJumpedDiag(int i, int j, int di, int dj, PathCost g, PathfinderState& state) +void LongPathfinder::AddJumpedDiag(int i, int j, int di, int dj, PathCost g, PathfinderState& state) const { // ProcessNeighbour(i, j, i + di, j + dj, g, state); // return; @@ -712,13 +712,16 @@ void LongPathfinder::AddJumpedDiag(int i, int j, int di, int dj, PathCost g, Pat } } -void LongPathfinder::ComputeJPSPath(entity_pos_t x0, entity_pos_t z0, const PathGoal& origGoal, pass_class_t passClass, WaypointPath& path) +void LongPathfinder::ComputeJPSPath(entity_pos_t x0, entity_pos_t z0, const PathGoal& origGoal, pass_class_t passClass, WaypointPath& path) const { PROFILE("ComputePathJPS"); PROFILE2_IFSPIKE("ComputePathJPS", 0.0002); PathfinderState state = { 0 }; - state.jpc = m_JumpPointCache[passClass].get(); + auto it = m_JumpPointCache.find(passClass); + ENSURE(it != m_JumpPointCache.end()); + state.jpc = it->second.get(); + if (m_UseJPSCache && !state.jpc) { state.jpc = new JumpPointCache; @@ -905,7 +908,7 @@ void LongPathfinder::ComputeJPSPath(entity_pos_t x0, entity_pos_t z0, const Path #undef PASSABLE -void LongPathfinder::ImprovePathWaypoints(WaypointPath& path, pass_class_t passClass, entity_pos_t maxDist, entity_pos_t x0, entity_pos_t z0) +void LongPathfinder::ImprovePathWaypoints(WaypointPath& path, pass_class_t passClass, entity_pos_t maxDist, entity_pos_t x0, entity_pos_t z0) const { if (path.m_Waypoints.empty()) return; diff --git a/source/simulation2/helpers/LongPathfinder.h b/source/simulation2/helpers/LongPathfinder.h index 2b89565081..208d72f00c 100644 --- a/source/simulation2/helpers/LongPathfinder.h +++ b/source/simulation2/helpers/LongPathfinder.h @@ -217,7 +217,7 @@ public: * along the path. */ void ComputePath(entity_pos_t x0, entity_pos_t z0, const PathGoal& origGoal, - pass_class_t passClass, WaypointPath& path) + pass_class_t passClass, WaypointPath& path) const { if (!m_Grid) { @@ -250,34 +250,36 @@ public: Grid* m_Grid; u16 m_GridSize; - // Debugging - output from last pathfind operation: - LongOverlay* m_DebugOverlay; - PathfindTileGrid* m_DebugGrid; - u32 m_DebugSteps; - double m_DebugTime; - PathGoal m_DebugGoal; - WaypointPath* m_DebugPath; - pass_class_t m_DebugPassClass; + // Debugging - output from last pathfind operation. + // mutable as making these const would require a lot of boilerplate code + // and they do not change the behavioural const-ness of the pathfinder. + mutable LongOverlay* m_DebugOverlay; + mutable PathfindTileGrid* m_DebugGrid; + mutable u32 m_DebugSteps; + mutable double m_DebugTime; + mutable PathGoal m_DebugGoal; + mutable WaypointPath* m_DebugPath; + mutable pass_class_t m_DebugPassClass; private: - PathCost CalculateHeuristic(int i, int j, int iGoal, int jGoal); - void ProcessNeighbour(int pi, int pj, int i, int j, PathCost pg, PathfinderState& state); + PathCost CalculateHeuristic(int i, int j, int iGoal, int jGoal) const; + void ProcessNeighbour(int pi, int pj, int i, int j, PathCost pg, PathfinderState& state) const; /** * JPS algorithm helper functions * @param detectGoal is not used if m_UseJPSCache is true */ - void AddJumpedHoriz(int i, int j, int di, PathCost g, PathfinderState& state, bool detectGoal); - int HasJumpedHoriz(int i, int j, int di, PathfinderState& state, bool detectGoal); - void AddJumpedVert(int i, int j, int dj, PathCost g, PathfinderState& state, bool detectGoal); - int HasJumpedVert(int i, int j, int dj, PathfinderState& state, bool detectGoal); - void AddJumpedDiag(int i, int j, int di, int dj, PathCost g, PathfinderState& state); + void AddJumpedHoriz(int i, int j, int di, PathCost g, PathfinderState& state, bool detectGoal) const; + int HasJumpedHoriz(int i, int j, int di, PathfinderState& state, bool detectGoal) const; + void AddJumpedVert(int i, int j, int dj, PathCost g, PathfinderState& state, bool detectGoal) const; + int HasJumpedVert(int i, int j, int dj, PathfinderState& state, bool detectGoal) const; + void AddJumpedDiag(int i, int j, int di, int dj, PathCost g, PathfinderState& state) const; /** * See LongPathfinder.cpp for implementation details * TODO: cleanup documentation */ - void ComputeJPSPath(entity_pos_t x0, entity_pos_t z0, const PathGoal& origGoal, pass_class_t passClass, WaypointPath& path); + void ComputeJPSPath(entity_pos_t x0, entity_pos_t z0, const PathGoal& origGoal, pass_class_t passClass, WaypointPath& path) const; void GetDebugDataJPS(u32& steps, double& time, Grid& grid) const; // Helper functions for ComputePath @@ -290,7 +292,7 @@ private: * If @param maxDist is non-zero, path waypoints will be espaced by at most @param maxDist. * In that case the distance between (x0, z0) and the first waypoint will also be made less than maxDist. */ - void ImprovePathWaypoints(WaypointPath& path, pass_class_t passClass, entity_pos_t maxDist, entity_pos_t x0, entity_pos_t z0); + void ImprovePathWaypoints(WaypointPath& path, pass_class_t passClass, entity_pos_t maxDist, entity_pos_t x0, entity_pos_t z0) const; /** * Generate a passability map, stored in the 16th bit of navcells, based on passClass, @@ -299,7 +301,11 @@ private: void GenerateSpecialMap(pass_class_t passClass, std::vector excludedRegions); bool m_UseJPSCache; - std::map > m_JumpPointCache; + // Mutable may be used here as caching does not change the external const-ness of the Long Range pathfinder. + // This is thread-safe as it is order independent (no change in the output of the function for a given set of params). + // Obviously, this means that the cache should actually be a cache and not return different results + // from what would happen if things hadn't been cached. + mutable std::map > m_JumpPointCache; HierarchicalPathfinder m_PathfinderHier; };