diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index b94531faa..963c95279 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -191,7 +191,7 @@ bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectIns const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos()); const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos()); - return ln->cost < rn->cost; + return ln->getCost() < rn->getCost(); } bool isSafeToVisit(HeroPtr h, crint3 tile) diff --git a/AI/VCAI/FuzzyEngines.cpp b/AI/VCAI/FuzzyEngines.cpp index ed5a1fa64..10c4e6c94 100644 --- a/AI/VCAI/FuzzyEngines.cpp +++ b/AI/VCAI/FuzzyEngines.cpp @@ -98,7 +98,7 @@ float HeroMovementGoalEngineBase::calculateTurnDistanceInputValue(const Goals::A else { auto pathInfo = ai->myCb->getPathsInfo(goal.hero.h)->getPathInfo(goal.tile); - return pathInfo->cost; + return pathInfo->getCost(); } } diff --git a/AI/VCAI/Pathfinding/AINodeStorage.cpp b/AI/VCAI/Pathfinding/AINodeStorage.cpp index 95af9d144..5ffac967c 100644 --- a/AI/VCAI/Pathfinding/AINodeStorage.cpp +++ b/AI/VCAI/Pathfinding/AINodeStorage.cpp @@ -119,7 +119,7 @@ CGPathNode * AINodeStorage::getInitialNode() initialNode->turns = 0; initialNode->moveRemains = hero->movement; initialNode->danger = 0; - initialNode->cost = 0.0; + initialNode->setCost(0.0); return initialNode; } @@ -146,7 +146,7 @@ void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInf { dstNode->moveRemains = destination.movementLeft; dstNode->turns = destination.turn; - dstNode->cost = destination.cost; + dstNode->setCost(destination.cost); dstNode->danger = srcNode->danger; dstNode->action = destination.action; dstNode->theNodeBefore = srcNode->theNodeBefore; @@ -305,7 +305,7 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode if(node.danger <= destinationNode->danger && destinationNode->chainMask == 1 && node.chainMask == 0) { - if(node.cost < destinationNode->cost) + if(node.getCost() < destinationNode->getCost()) { #ifdef VCMI_TRACE_PATHFINDER logAi->trace( @@ -349,7 +349,7 @@ std::vector AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) while(current != nullptr && current->coord != initialPos) { AIPathNodeInfo pathNode; - pathNode.cost = current->cost; + pathNode.cost = current->getCost(); pathNode.turns = current->turns; pathNode.danger = current->danger; pathNode.coord = current->coord; diff --git a/lib/CPathfinder.cpp b/lib/CPathfinder.cpp index 2a1bfaf37..4084673c0 100644 --- a/lib/CPathfinder.cpp +++ b/lib/CPathfinder.cpp @@ -161,7 +161,7 @@ CGPathNode * NodeStorage::getInitialNode() initialNode->turns = 0; initialNode->moveRemains = out.hero->movement; - initialNode->cost = 0.0; + initialNode->setCost(0.0); return initialNode; } @@ -169,7 +169,7 @@ CGPathNode * NodeStorage::getInitialNode() void NodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInfo & source) { assert(destination.node != source.node->theNodeBefore); //two tiles can't point to each other - destination.node->cost = destination.cost; + destination.node->setCost(destination.cost); destination.node->moveRemains = destination.movementLeft; destination.node->turns = destination.turn; destination.node->theNodeBefore = source.node; @@ -289,6 +289,28 @@ CPathfinder::CPathfinder( initializeGraph(); } + +void CPathfinder::push(CGPathNode * node) +{ + if(node && !node->inPQ) + { + node->inPQ = true; + node->pq = &this->pq; + auto handle = pq.push(node); + node->pqHandle = handle; + } +} + +CGPathNode * CPathfinder::topAndPop() +{ + auto node = pq.top(); + + pq.pop(); + node->inPQ = false; + node->pq = nullptr; + return node; +} + void CPathfinder::calculatePaths() { //logGlobal->info("Calculating paths for hero %s (adress %d) of player %d", hero->name, hero , hero->tempOwner); @@ -305,19 +327,19 @@ void CPathfinder::calculatePaths() if(isHeroPatrolLocked()) return; - pq.push(initialNode); + push(initialNode); + while(!pq.empty()) { - auto node = pq.top(); + auto node = topAndPop(); auto excludeOurHero = node->coord == initialNode->coord; source.setNode(gs, node, excludeOurHero); - pq.pop(); source.node->locked = true; int movement = source.node->moveRemains; uint8_t turn = source.node->turns; - float cost = source.node->cost; + float cost = source.node->getCost(); hlp->updateTurnInfo(turn); if(!movement) @@ -368,7 +390,7 @@ void CPathfinder::calculatePaths() } if(!destination.blocked) - pq.push(destination.node); + push(destination.node); } //neighbours loop @@ -403,7 +425,7 @@ void CPathfinder::calculatePaths() config->nodeStorage->commit(destination, source); if(destination.node->action == CGPathNode::TELEPORT_NORMAL) - pq.push(destination.node); + push(destination.node); } } } //queue loop @@ -1367,7 +1389,7 @@ bool CDestinationNodeInfo::isBetterWay() const if(node->turns == 0xff) //we haven't been here before return true; else - return cost < node->cost; //this route is faster + return cost < node->getCost(); //this route is faster } bool PathNodeInfo::isNodeObjectVisitable() const diff --git a/lib/CPathfinder.h b/lib/CPathfinder.h index b3652115b..906c333a4 100644 --- a/lib/CPathfinder.h +++ b/lib/CPathfinder.h @@ -14,7 +14,8 @@ #include "HeroBonus.h" #include "int3.h" -#include +#include + class CGHeroInstance; class CGObjectInstance; @@ -26,6 +27,17 @@ class CPathfinderHelper; class CPathfinder; class PathfinderConfig; + +template +struct DLL_LINKAGE NodeComparer +{ + STRONG_INLINE + bool operator()(const N * lhs, const N * rhs) const + { + return lhs->getCost() > rhs->getCost(); + } +}; + struct DLL_LINKAGE CGPathNode { typedef EPathfindingLayer ELayer; @@ -58,16 +70,17 @@ struct DLL_LINKAGE CGPathNode int3 coord; //coordinates ELayer layer; ui32 moveRemains; //remaining movement points after hero reaches the tile - float cost; //total cost of the path to this tile measured in turns with fractions ui8 turns; //how many turns we have to wait before reaching the tile - 0 means current turn EAccessibility accessible; ENodeAction action; bool locked; + bool inPQ; CGPathNode() : coord(-1), - layer(ELayer::WRONG) + layer(ELayer::WRONG), + pqHandle(nullptr) { reset(); } @@ -82,6 +95,36 @@ struct DLL_LINKAGE CGPathNode turns = 255; theNodeBefore = nullptr; action = UNKNOWN; + inPQ = false; + pq = nullptr; + } + + STRONG_INLINE + float getCost() const + { + return cost; + } + + STRONG_INLINE + void setCost(float value) + { + if(value == cost) + return; + + bool getUpNode = value < cost; + cost = value; + // If the node is in the heap, update the heap. + if(inPQ && pq != nullptr) + { + if(getUpNode) + { + pq->increase(this->pqHandle, this); + } + else + { + pq->decrease(this->pqHandle, this); + } + } } STRONG_INLINE @@ -105,6 +148,26 @@ struct DLL_LINKAGE CGPathNode { return turns < 255; } + + boost::heap::detail::node_handle + < + boost::heap::detail::marked_heap_node*, + boost::heap::detail::make_fibonacci_heap_base + < + CGPathNode *, + boost::parameter::aux::arg_list + < + boost::heap::compare>, + boost::parameter::aux::empty_arg_list + > + >::type, + CGPathNode *& + > pqHandle; + + boost::heap::fibonacci_heap< CGPathNode *, boost::heap::compare> >* pq; + +private: + float cost; //total cost of the path to this tile measured in turns with fractions }; struct DLL_LINKAGE CGPath @@ -415,15 +478,7 @@ private: } patrolState; std::unordered_set patrolTiles; - struct NodeComparer - { - STRONG_INLINE - bool operator()(const CGPathNode * lhs, const CGPathNode * rhs) const - { - return lhs->cost > rhs->cost; - } - }; - boost::heap::priority_queue > pq; + boost::heap::fibonacci_heap> > pq; PathNodeInfo source; //current (source) path node -> we took it from the queue CDestinationNodeInfo destination; //destination node -> it's a neighbour of source that we consider @@ -441,6 +496,12 @@ private: void initializePatrol(); void initializeGraph(); + + STRONG_INLINE + void push(CGPathNode * node); + + STRONG_INLINE + CGPathNode * topAndPop(); }; struct DLL_LINKAGE TurnInfo