1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-09-16 09:26:28 +02:00

CPathfinderHelper: implement TurnInfo to avoid bonus re-checking

TurnInfo contain information about what movement-related bonuses and MP hero going to have on certain turn.
This way we can collect this information once for each turn so it's huge performance boost.
This commit is contained in:
ArseniyShestakov
2015-11-10 14:26:45 +03:00
parent 2ef9d7c3ec
commit 9a63735c24
4 changed files with 87 additions and 32 deletions

View File

@@ -48,9 +48,10 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance
throw std::runtime_error("Wrong checksum"); throw std::runtime_error("Wrong checksum");
} }
if(hero->getBonusAtTurn(Bonus::FLYING_MOVEMENT)) hlp = new CPathfinderHelper(hero);
if(hlp->ti->bonusFlying)
options.useFlying = true; options.useFlying = true;
if(hero->getBonusAtTurn(Bonus::WATER_WALKING)) if(hlp->ti->bonusWaterWalking)
options.useWaterWalking = true; options.useWaterWalking = true;
if(CGWhirlpool::isProtected(hero)) if(CGWhirlpool::isProtected(hero))
options.useTeleportWhirlpool = true; options.useTeleportWhirlpool = true;
@@ -61,14 +62,6 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance
void CPathfinder::calculatePaths() void CPathfinder::calculatePaths()
{ {
int maxMovePointsLand = hero->maxMovePoints(true);
int maxMovePointsWater = hero->maxMovePoints(false);
auto maxMovePoints = [&](CGPathNode *cp) -> int
{
return cp->layer == ELayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
};
auto passOneTurnLimitCheck = [&](bool shouldCheck) -> bool auto passOneTurnLimitCheck = [&](bool shouldCheck) -> bool
{ {
if(options.oneTurnSpecialLayersLimit && shouldCheck) if(options.oneTurnSpecialLayersLimit && shouldCheck)
@@ -109,10 +102,11 @@ void CPathfinder::calculatePaths()
cp->locked = true; cp->locked = true;
int movement = cp->moveRemains, turn = cp->turns; int movement = cp->moveRemains, turn = cp->turns;
hlp->updateTurnInfo(turn);
if(!movement) if(!movement)
{ {
movement = maxMovePoints(cp); hlp->updateTurnInfo(++turn);
turn++; movement = hlp->getMaxMovePoints(cp->layer);
} }
//add accessible neighbouring nodes to the queue //add accessible neighbouring nodes to the queue
@@ -143,7 +137,7 @@ void CPathfinder::calculatePaths()
if(!isMovementToDestPossible()) if(!isMovementToDestPossible())
continue; continue;
int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement); int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement, hlp->ti);
int remains = movement - cost; int remains = movement - cost;
if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK) if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK)
{ {
@@ -154,9 +148,9 @@ void CPathfinder::calculatePaths()
if(remains < 0) if(remains < 0)
{ {
//occurs rarely, when hero with low movepoints tries to leave the road //occurs rarely, when hero with low movepoints tries to leave the road
turnAtNextTile++; hlp->updateTurnInfo(++turnAtNextTile);
int moveAtNextTile = maxMovePoints(cp); int moveAtNextTile = hlp->getMaxMovePoints(i);
cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile); //cost must be updated, movement points changed :( cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile, hlp->ti); //cost must be updated, movement points changed :(
remains = moveAtNextTile - cost; remains = moveAtNextTile - cost;
} }
@@ -282,13 +276,13 @@ bool CPathfinder::isLayerAvailable(const ELayer &layer, const int &turn) const
switch(layer) switch(layer)
{ {
case ELayer::AIR: case ELayer::AIR:
if(!hero->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn)) if(!hlp->ti->bonusFlying)
return false; return false;
break; break;
case ELayer::WATER: case ELayer::WATER:
if(!hero->getBonusAtTurn(Bonus::WATER_WALKING, turn)) if(!hlp->ti->bonusWaterWalking)
return false; return false;
break; break;
} }
@@ -625,6 +619,43 @@ bool CPathfinder::canVisitObject() const
return cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL; return cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL;
} }
CPathfinderHelper::CPathfinderHelper(const CGHeroInstance * Hero)
: ti(nullptr), hero(Hero)
{
turnsInfo.reserve(16);
updateTurnInfo();
}
void CPathfinderHelper::updateTurnInfo(const int &turn)
{
if(!ti || ti->turn != turn)
{
if(turn < turnsInfo.size())
ti = turnsInfo[turn];
else
{
ti = getTurnInfo(hero, turn);
turnsInfo.push_back(ti);
}
}
}
int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer &layer) const
{
return layer == EPathfindingLayer::SAIL ? ti->maxMovePointsWater : ti->maxMovePointsLand;
}
TurnInfo * CPathfinderHelper::getTurnInfo(const CGHeroInstance * h, const int &turn)
{
auto turnInfo = new TurnInfo;
turnInfo->turn = turn;
turnInfo->maxMovePointsLand = h->maxMovePoints(true);
turnInfo->maxMovePointsWater = h->maxMovePoints(false);
turnInfo->bonusFlying = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn);
turnInfo->bonusWaterWalking = h->getBonusAtTurn(Bonus::WATER_WALKING, turn);
return turnInfo;
}
void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing) void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing)
{ {
static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0), static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
@@ -664,27 +695,28 @@ void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile &srct,
} }
} }
int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints, const int &turn, const bool &checkLast) int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints, const TurnInfo * ti, const bool &checkLast)
{ {
if(src == dst) //same tile if(src == dst) //same tile
return 0; return 0;
auto s = h->cb->getTile(src), d = h->cb->getTile(dst); if(!ti)
int ret = h->getTileCost(*d, *s, turn); ti = getTurnInfo(h);
auto flyBonus = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn); auto s = h->cb->getTile(src), d = h->cb->getTile(dst);
auto waterWalkingBonus = h->getBonusAtTurn(Bonus::WATER_WALKING, turn); int ret = h->getTileCost(*d, *s, ti);
if(d->blocked && flyBonus)
if(d->blocked && ti->bonusFlying)
{ {
ret *= (100.0 + flyBonus->val) / 100.0; ret *= (100.0 + ti->bonusFlying->val) / 100.0;
} }
else if(d->terType == ETerrainType::WATER) else if(d->terType == ETerrainType::WATER)
{ {
if(h->boat && s->hasFavourableWinds() && d->hasFavourableWinds()) //Favourable Winds if(h->boat && s->hasFavourableWinds() && d->hasFavourableWinds()) //Favourable Winds
ret *= 0.666; ret *= 0.666;
else if(!h->boat && waterWalkingBonus) else if(!h->boat && ti->bonusWaterWalking)
{ {
ret *= (100.0 + waterWalkingBonus->val) / 100.0; ret *= (100.0 + ti->bonusWaterWalking->val) / 100.0;
} }
} }
@@ -705,7 +737,7 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &src
getNeighbours(h->cb->gameState(), *d, dst, vec, s->terType != ETerrainType::WATER, true); getNeighbours(h->cb->gameState(), *d, dst, vec, s->terType != ETerrainType::WATER, true);
for(auto & elem : vec) for(auto & elem : vec)
{ {
int fcost = getMovementCost(h, dst, elem, left, turn, false); int fcost = getMovementCost(h, dst, elem, left, ti, false);
if(fcost <= left) if(fcost <= left)
return ret; return ret;
} }

View File

@@ -20,6 +20,7 @@
class CGHeroInstance; class CGHeroInstance;
class CGObjectInstance; class CGObjectInstance;
struct TerrainTile; struct TerrainTile;
class CPathfinderHelper;
struct DLL_LINKAGE CGPathNode struct DLL_LINKAGE CGPathNode
{ {
@@ -128,6 +129,7 @@ private:
CPathsInfo &out; CPathsInfo &out;
const CGHeroInstance *hero; const CGHeroInstance *hero;
CPathfinderHelper * hlp;
struct NodeComparer struct NodeComparer
{ {
@@ -179,11 +181,31 @@ private:
}; };
struct TurnInfo
{
int turn;
int maxMovePointsLand;
int maxMovePointsWater;
const Bonus * bonusFlying;
const Bonus * bonusWaterWalking;
};
class DLL_LINKAGE CPathfinderHelper class DLL_LINKAGE CPathfinderHelper
{ {
public: public:
TurnInfo * ti;
const CGHeroInstance * hero;
CPathfinderHelper(const CGHeroInstance * Hero);
void updateTurnInfo(const int &turn = 0);
int getMaxMovePoints(const EPathfindingLayer &layer) const;
static TurnInfo * getTurnInfo(const CGHeroInstance * h, const int &turn = 0);
static void getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing); static void getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing);
static int getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints =- 1, const int &turn = 0, const bool &checkLast = true); static int getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints =- 1, const TurnInfo * ti = nullptr, const bool &checkLast = true);
static int getMovementCost(const CGHeroInstance * h, const int3 &dst); static int getMovementCost(const CGHeroInstance * h, const int3 &dst);
private:
std::vector<TurnInfo *> turnsInfo;
}; };

View File

@@ -56,7 +56,7 @@ static int lowestSpeed(const CGHeroInstance * chi)
return ret; return ret;
} }
ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from, const int &turn) const ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from, const TurnInfo * ti) const
{ {
unsigned ret = GameConstants::BASE_MOVEMENT_COST; unsigned ret = GameConstants::BASE_MOVEMENT_COST;
@@ -80,7 +80,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro
break; break;
} }
} }
else if(!getBonusAtTurn(Bonus::NO_TERRAIN_PENALTY, turn, from.terType)) else if(!getBonusAtTurn(Bonus::NO_TERRAIN_PENALTY, ti->turn, from.terType))
{ {
// NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army. // NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army.
// This is clearly bug in H3 however intended behaviour is not clear. // This is clearly bug in H3 however intended behaviour is not clear.

View File

@@ -21,6 +21,7 @@ class CHero;
class CGBoat; class CGBoat;
class CGTownInstance; class CGTownInstance;
struct TerrainTile; struct TerrainTile;
struct TurnInfo;
class CGHeroPlaceholder : public CGObjectInstance class CGHeroPlaceholder : public CGObjectInstance
{ {
@@ -129,7 +130,7 @@ public:
EAlignment::EAlignment getAlignment() const; EAlignment::EAlignment getAlignment() const;
const std::string &getBiography() const; const std::string &getBiography() const;
bool needsLastStack()const override; bool needsLastStack()const override;
ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from, const int &turn) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from, const TurnInfo * ti) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
ui32 getLowestCreatureSpeed() const; ui32 getLowestCreatureSpeed() const;
int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation' int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day