1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +02:00

Merge pull request #647 from ShubusCorporation/shc_fix_pathfinder

Fix exception while calculating paths
This commit is contained in:
Alexander Shishkin 2020-09-22 13:23:00 +03:00 committed by GitHub
commit 15d9c4438c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 110 additions and 27 deletions

View File

@ -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)

View File

@ -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();
}
}

View File

@ -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<AIPath> 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;

View File

@ -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

View File

@ -14,7 +14,8 @@
#include "HeroBonus.h"
#include "int3.h"
#include <boost/heap/priority_queue.hpp>
#include <boost/heap/fibonacci_heap.hpp>
class CGHeroInstance;
class CGObjectInstance;
@ -26,6 +27,17 @@ class CPathfinderHelper;
class CPathfinder;
class PathfinderConfig;
template<typename N>
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<CGPathNode *>*,
boost::heap::detail::make_fibonacci_heap_base
<
CGPathNode *,
boost::parameter::aux::arg_list
<
boost::heap::compare<NodeComparer<CGPathNode>>,
boost::parameter::aux::empty_arg_list
>
>::type,
CGPathNode *&
> pqHandle;
boost::heap::fibonacci_heap< CGPathNode *, boost::heap::compare<NodeComparer<CGPathNode>> >* 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<int3, ShashInt3> patrolTiles;
struct NodeComparer
{
STRONG_INLINE
bool operator()(const CGPathNode * lhs, const CGPathNode * rhs) const
{
return lhs->cost > rhs->cost;
}
};
boost::heap::priority_queue<CGPathNode *, boost::heap::compare<NodeComparer> > pq;
boost::heap::fibonacci_heap<CGPathNode *, boost::heap::compare<NodeComparer<CGPathNode>> > 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