1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

Use the same cost value in Pathfinder and fuzzy evaluations.

This commit is contained in:
AlexVinS
2019-01-15 08:52:55 +03:00
parent 4b5910c2f4
commit da20aa2388
14 changed files with 96 additions and 143 deletions

View File

@@ -191,15 +191,7 @@ bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectIns
const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos()); const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos());
const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos()); const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos());
if(ln->turns != rn->turns) return ln->cost < rn->cost;
return ln->turns < rn->turns;
return (ln->moveRemains > rn->moveRemains);
}
bool compareMovement(HeroPtr lhs, HeroPtr rhs)
{
return lhs->movement > rhs->movement;
} }
ui64 evaluateDanger(crint3 tile) ui64 evaluateDanger(crint3 tile)
@@ -410,6 +402,7 @@ bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should
auto gate = dynamic_cast<const CGKeys *>(cb->getTile(tileToHit)->topVisitableObj()); auto gate = dynamic_cast<const CGKeys *>(cb->getTile(tileToHit)->topVisitableObj());
return !gate->passableFor(ai->playerID); return !gate->passableFor(ai->playerID);
} }
bool isBlockVisitObj(const int3 & pos) bool isBlockVisitObj(const int3 & pos)
{ {
if(auto obj = cb->getTopObj(pos)) if(auto obj = cb->getTopObj(pos))
@@ -439,7 +432,6 @@ creInfo infoFromDC(const dwellingContent & dc)
return ci; return ci;
} }
ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t) ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t)
{ {
ui64 aivalue = 0; ui64 aivalue = 0;
@@ -529,14 +521,3 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
else else
return false; return false;
} }
uint32_t distanceToTile(const CGHeroInstance * hero, int3 pos)
{
auto pathInfo = cb->getPathsInfo(hero)->getPathInfo(pos);
uint32_t totalMovementPoints = pathInfo->turns * hero->maxMovePoints(true) + hero->movement;
if(totalMovementPoints < pathInfo->moveRemains) // should not be but who knows
return 0;
return totalMovementPoints - pathInfo->moveRemains;
}

View File

@@ -172,13 +172,11 @@ bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic
bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength); bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength);
bool isSafeToVisit(HeroPtr h, crint3 tile); bool isSafeToVisit(HeroPtr h, crint3 tile);
bool compareMovement(HeroPtr lhs, HeroPtr rhs);
bool compareHeroStrength(HeroPtr h1, HeroPtr h2); bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2); bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);
bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2); bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2);
ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t); ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t);
ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t); ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t);
uint32_t distanceToTile(const CGHeroInstance * hero, int3 pos);
class CDistanceSorter class CDistanceSorter
{ {

View File

@@ -89,14 +89,15 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army)
float HeroMovementGoalEngineBase::calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const float HeroMovementGoalEngineBase::calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const
{ {
const float maxMovePoints = (float)goal.hero->maxMovePoints(true); if(goal.evaluationContext.movementCost > 0)
if(goal.evaluationContext.movementCost != 0)
{ {
return goal.evaluationContext.movementCost / maxMovePoints; return goal.evaluationContext.movementCost;
}
else
{
auto pathInfo = ai->myCb->getPathsInfo(goal.hero.h)->getPathInfo(goal.tile);
return pathInfo->cost;
} }
return distanceToTile(goal.hero.h, goal.tile) / maxMovePoints;
} }
TacticalAdvantageEngine::TacticalAdvantageEngine() TacticalAdvantageEngine::TacticalAdvantageEngine()

View File

@@ -91,12 +91,14 @@ namespace Goals
struct DLL_EXPORT EvaluationContext struct DLL_EXPORT EvaluationContext
{ {
uint64_t movementCost; float movementCost;
int manaCost; int manaCost;
uint64_t danger; uint64_t danger;
EvaluationContext() EvaluationContext()
:movementCost(0), danger(0), manaCost(0) : movementCost(0.0),
manaCost(0),
danger(0)
{ {
} }
}; };

View File

@@ -253,11 +253,12 @@ TSubgoal Explore::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h) con
if(dstToRevealedTiles.empty()) //yes, it DID happen! if(dstToRevealedTiles.empty()) //yes, it DID happen!
return sptr(Invalid()); return sptr(Invalid());
auto paths = cb->getPathsInfo(h.get());
auto best = dstToRevealedTiles.begin(); auto best = dstToRevealedTiles.begin();
for(auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++) for(auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++)
{ {
const CGPathNode * pn = cb->getPathsInfo(h.get())->getPathInfo(i->first); const CGPathNode * pn = paths->getPathInfo(i->first);
//const TerrainTile *t = cb->getTile(i->first);
if(best->second < i->second && pn->reachable() && pn->accessible == CGPathNode::ACCESSIBLE) if(best->second < i->second && pn->reachable() && pn->accessible == CGPathNode::ACCESSIBLE)
best = i; best = i;
} }
@@ -336,7 +337,7 @@ TSubgoal Explore::explorationScanRange(HeroPtr h, std::vector<int3> & range) con
auto waysToVisit = aip->ah->howToVisitTile(h, tile, allowGatherArmy); auto waysToVisit = aip->ah->howToVisitTile(h, tile, allowGatherArmy);
for(auto goal : waysToVisit) for(auto goal : waysToVisit)
{ {
if(goal->evaluationContext.movementCost == 0) // should not happen if(goal->evaluationContext.movementCost <= 0.0) // should not happen
continue; continue;
float ourValue = (float)tilesDiscovered * tilesDiscovered / goal->evaluationContext.movementCost; float ourValue = (float)tilesDiscovered * tilesDiscovered / goal->evaluationContext.movementCost;
@@ -374,7 +375,7 @@ TSubgoal Explore::exploreNearestNeighbour(HeroPtr h) const
//look for nearby objs -> visit them if they're close enough //look for nearby objs -> visit them if they're close enough
const int DIST_LIMIT = 3; const int DIST_LIMIT = 3;
const int MP_LIMIT = DIST_LIMIT * 150; // approximate cost of diagonal movement const float COST_LIMIT = .2; //todo: fine tune
std::vector<const CGObjectInstance *> nearbyVisitableObjs; std::vector<const CGObjectInstance *> nearbyVisitableObjs;
for(int x = hpos.x - DIST_LIMIT; x <= hpos.x + DIST_LIMIT; ++x) //get only local objects instead of all possible objects on the map for(int x = hpos.x - DIST_LIMIT; x <= hpos.x + DIST_LIMIT; ++x) //get only local objects instead of all possible objects on the map
@@ -383,7 +384,7 @@ TSubgoal Explore::exploreNearestNeighbour(HeroPtr h) const
{ {
for(auto obj : cb->getVisitableObjs(int3(x, y, hpos.z), false)) for(auto obj : cb->getVisitableObjs(int3(x, y, hpos.z), false))
{ {
if(ai->isGoodForVisit(obj, h, MP_LIMIT)) if(ai->isGoodForVisit(obj, h, COST_LIMIT))
{ {
nearbyVisitableObjs.push_back(obj); nearbyVisitableObjs.push_back(obj);
} }

View File

@@ -120,6 +120,7 @@ CGPathNode * AINodeStorage::getInitialNode()
initialNode->turns = 0; initialNode->turns = 0;
initialNode->moveRemains = hero->movement; initialNode->moveRemains = hero->movement;
initialNode->danger = 0; initialNode->danger = 0;
initialNode->cost = 0.0;
return initialNode; return initialNode;
} }
@@ -146,6 +147,7 @@ void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInf
{ {
dstNode->moveRemains = destination.movementLeft; dstNode->moveRemains = destination.movementLeft;
dstNode->turns = destination.turn; dstNode->turns = destination.turn;
dstNode->cost = destination.cost;
dstNode->danger = srcNode->danger; dstNode->danger = srcNode->danger;
dstNode->action = destination.action; dstNode->action = destination.action;
dstNode->theNodeBefore = srcNode->theNodeBefore; dstNode->theNodeBefore = srcNode->theNodeBefore;
@@ -322,8 +324,7 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode
if(node.danger <= destinationNode->danger && destinationNode->chainMask == 1 && node.chainMask == 0) if(node.danger <= destinationNode->danger && destinationNode->chainMask == 1 && node.chainMask == 0)
{ {
if(node.turns < destinationNode->turns if(node.cost < destinationNode->cost)
|| (node.turns == destinationNode->turns && node.moveRemains >= destinationNode->moveRemains))
{ {
#ifdef VCMI_TRACE_PATHFINDER #ifdef VCMI_TRACE_PATHFINDER
logAi->trace( logAi->trace(
@@ -343,7 +344,6 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode
bool AINodeStorage::isTileAccessible(int3 pos, const EPathfindingLayer layer) const bool AINodeStorage::isTileAccessible(int3 pos, const EPathfindingLayer layer) const
{ {
std::vector<AIPath> paths;
auto chains = nodes[pos.x][pos.y][pos.z][layer]; auto chains = nodes[pos.x][pos.y][pos.z][layer];
for(const AIPathNode & node : chains) for(const AIPathNode & node : chains)
@@ -376,9 +376,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(int3 pos, bool isOnLand) const
while(current != nullptr && current->coord != initialPos) while(current != nullptr && current->coord != initialPos)
{ {
AIPathNodeInfo pathNode; AIPathNodeInfo pathNode;
pathNode.cost = current->cost;
pathNode.movementPointsLeft = current->moveRemains;
pathNode.movementPointsUsed = (int)(current->turns * hero->maxMovePoints(true) + hero->movement) - (int)current->moveRemains;
pathNode.turns = current->turns; pathNode.turns = current->turns;
pathNode.danger = current->danger; pathNode.danger = current->danger;
pathNode.coord = current->coord; pathNode.coord = current->coord;
@@ -420,15 +418,15 @@ uint64_t AIPath::getPathDanger() const
return 0; return 0;
} }
uint32_t AIPath::movementCost() const float AIPath::movementCost() const
{ {
if(nodes.size()) if(nodes.size())
{ {
return nodes.front().movementPointsUsed; return nodes.front().cost;
} }
// TODO: boost:optional? // TODO: boost:optional?
return 0; return 0.0;
} }
uint64_t AIPath::getTotalDanger(HeroPtr hero) const uint64_t AIPath::getTotalDanger(HeroPtr hero) const

View File

@@ -42,8 +42,7 @@ struct AIPathNode : public CGPathNode
struct AIPathNodeInfo struct AIPathNodeInfo
{ {
uint32_t movementPointsLeft; float cost;
uint32_t movementPointsUsed;
int turns; int turns;
int3 coord; int3 coord;
uint64_t danger; uint64_t danger;
@@ -64,7 +63,7 @@ struct AIPath
int3 firstTileToGet() const; int3 firstTileToGet() const;
uint32_t movementCost() const; float movementCost() const;
}; };
class AINodeStorage : public INodeStorage class AINodeStorage : public INodeStorage

View File

@@ -65,31 +65,6 @@ struct SetGlobalState
#define NET_EVENT_HANDLER SET_GLOBAL_STATE(this) #define NET_EVENT_HANDLER SET_GLOBAL_STATE(this)
#define MAKING_TURN SET_GLOBAL_STATE(this) #define MAKING_TURN SET_GLOBAL_STATE(this)
void foreach_tile(std::vector<std::vector<std::vector<unsigned char>>> & vectors, std::function<void(unsigned char & in)> foo)
{
for(auto & vector : vectors)
{
for(auto j = vector.begin(); j != vector.end(); j++)
{
for(auto & elem : *j)
foo(elem);
}
}
}
struct ObjInfo
{
int3 pos;
std::string name;
ObjInfo(){}
ObjInfo(const CGObjectInstance * obj)
: pos(obj->pos), name(obj->getObjectName())
{
}
};
std::map<const CGObjectInstance *, ObjInfo> helperObjInfo;
VCAI::VCAI() VCAI::VCAI()
{ {
LOG_TRACE(logAi); LOG_TRACE(logAi);
@@ -1303,12 +1278,12 @@ void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruit
} }
} }
bool VCAI::isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<uint32_t> movementCostLimit) bool VCAI::isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit)
{ {
int3 op = obj->visitablePos(); int3 op = obj->visitablePos();
auto paths = ah->getPathsToTile(h, op); auto paths = ah->getPathsToTile(h, op);
for(auto path : paths) for(const auto & path : paths)
{ {
if(movementCostLimit && movementCostLimit.get() < path.movementCost()) if(movementCostLimit && movementCostLimit.get() < path.movementCost())
return false; return false;
@@ -1424,17 +1399,18 @@ void VCAI::wander(HeroPtr h)
}); });
int pass = 0; int pass = 0;
std::vector<boost::optional<ui32>> distanceLimits = { std::vector<boost::optional<float>> distanceLimits =
h->movement, {
h->movement + h->maxMovePoints(true), 1.0,
2.0,
boost::none boost::none
}; };
while(!dests.size() && pass < distanceLimits.size()) while(!dests.size() && pass < distanceLimits.size())
{ {
boost::optional<ui32> distanceLimit = distanceLimits[pass]; auto & distanceLimit = distanceLimits[pass];
logAi->debug("Looking for wander destination pass=%i, distance limit=%i", pass, distanceLimit.get_value_or(-1)); logAi->debug("Looking for wander destination pass=%i, cost limit=%f", pass, distanceLimit.get_value_or(-1.0));
vstd::copy_if(visitableObjs, std::back_inserter(dests), [&](ObjectIdRef obj) -> bool vstd::copy_if(visitableObjs, std::back_inserter(dests), [&](ObjectIdRef obj) -> bool
{ {
@@ -1773,7 +1749,6 @@ std::vector<const CGObjectInstance *> VCAI::getFlaggedObjects() const
void VCAI::addVisitableObj(const CGObjectInstance * obj) void VCAI::addVisitableObj(const CGObjectInstance * obj)
{ {
visitableObjs.insert(obj); visitableObjs.insert(obj);
helperObjInfo[obj] = ObjInfo(obj);
// All teleport objects seen automatically assigned to appropriate channels // All teleport objects seen automatically assigned to appropriate channels
auto teleportObj = dynamic_cast<const CGTeleport *>(obj); auto teleportObj = dynamic_cast<const CGTeleport *>(obj);

View File

@@ -210,7 +210,7 @@ public:
void completeGoal(Goals::TSubgoal goal); //safely removes goal from reserved hero void completeGoal(Goals::TSubgoal goal); //safely removes goal from reserved hero
void recruitHero(const CGTownInstance * t, bool throwing = false); void recruitHero(const CGTownInstance * t, bool throwing = false);
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<uint32_t> movementCostLimit = boost::none); bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit = boost::none);
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) const; bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) const;
//void recruitCreatures(const CGTownInstance * t); //void recruitCreatures(const CGTownInstance * t);
void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter); void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter);

View File

@@ -161,6 +161,7 @@ CGPathNode * NodeStorage::getInitialNode()
initialNode->turns = 0; initialNode->turns = 0;
initialNode->moveRemains = out.hero->movement; initialNode->moveRemains = out.hero->movement;
initialNode->cost = 0.0;
return initialNode; return initialNode;
} }
@@ -168,6 +169,7 @@ CGPathNode * NodeStorage::getInitialNode()
void NodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInfo & source) void NodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInfo & source)
{ {
assert(destination.node != source.node->theNodeBefore); //two tiles can't point to each other assert(destination.node != source.node->theNodeBefore); //two tiles can't point to each other
destination.node->cost = destination.cost;
destination.node->moveRemains = destination.movementLeft; destination.node->moveRemains = destination.movementLeft;
destination.node->turns = destination.turn; destination.node->turns = destination.turn;
destination.node->theNodeBefore = source.node; destination.node->theNodeBefore = source.node;
@@ -197,24 +199,36 @@ void MovementCostRule::process(
const PathfinderConfig * pathfinderConfig, const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const CPathfinderHelper * pathfinderHelper) const
{ {
int turnAtNextTile = destination.turn, moveAtNextTile = destination.movementLeft; float costAtNextTile = destination.cost;
int turnAtNextTile = destination.turn;
int moveAtNextTile = destination.movementLeft;
int cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile); int cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile);
int remains = moveAtNextTile - cost; int remains = moveAtNextTile - cost;
int maxMovePoints = pathfinderHelper->getMaxMovePoints(destination.node->layer);
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
costAtNextTile += static_cast<float>(moveAtNextTile) / maxMovePoints;//we spent all points of current turn
pathfinderHelper->updateTurnInfo(++turnAtNextTile); pathfinderHelper->updateTurnInfo(++turnAtNextTile);
moveAtNextTile = pathfinderHelper->getMaxMovePoints(destination.node->layer);
maxMovePoints = pathfinderHelper->getMaxMovePoints(destination.node->layer);
moveAtNextTile = maxMovePoints;
cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile); //cost must be updated, movement points changed :( cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile); //cost must be updated, movement points changed :(
remains = moveAtNextTile - cost; remains = moveAtNextTile - cost;
} }
if(destination.action == CGPathNode::EMBARK || destination.action == CGPathNode::DISEMBARK) if(destination.action == CGPathNode::EMBARK || destination.action == CGPathNode::DISEMBARK)
{ {
/// FREE_SHIP_BOARDING bonus only remove additional penalty /// FREE_SHIP_BOARDING bonus only remove additional penalty
/// land <-> sail transition still cost movement points as normal movement /// land <-> sail transition still cost movement points as normal movement
remains = pathfinderHelper->movementPointsAfterEmbark(moveAtNextTile, cost, destination.action - 1); remains = pathfinderHelper->movementPointsAfterEmbark(moveAtNextTile, cost, (destination.action == CGPathNode::DISEMBARK));
cost = moveAtNextTile - remains;
} }
costAtNextTile += static_cast<float>(cost) / maxMovePoints;
destination.cost = costAtNextTile;
destination.turn = turnAtNextTile; destination.turn = turnAtNextTile;
destination.movementLeft = remains; destination.movementLeft = remains;
@@ -301,7 +315,10 @@ void CPathfinder::calculatePaths()
pq.pop(); pq.pop();
source.node->locked = true; source.node->locked = true;
int movement = source.node->moveRemains, turn = source.node->turns; int movement = source.node->moveRemains;
uint8_t turn = source.node->turns;
float cost = source.node->cost;
hlp->updateTurnInfo(turn); hlp->updateTurnInfo(turn);
if(!movement) if(!movement)
{ {
@@ -336,7 +353,7 @@ void CPathfinder::calculatePaths()
destination.turn = turn; destination.turn = turn;
destination.movementLeft = movement; destination.movementLeft = movement;
destination.cost = cost;
destination.guarded = isDestinationGuarded(); destination.guarded = isDestinationGuarded();
destination.isGuardianTile = destination.guarded && isDestinationGuardian(); destination.isGuardianTile = destination.guarded && isDestinationGuardian();
if(destination.nodeObject) if(destination.nodeObject)
@@ -378,6 +395,7 @@ void CPathfinder::calculatePaths()
destination.setNode(gs, teleportNode); destination.setNode(gs, teleportNode);
destination.turn = turn; destination.turn = turn;
destination.movementLeft = movement; destination.movementLeft = movement;
destination.cost = cost;
if(destination.isBetterWay()) if(destination.isBetterWay())
{ {
@@ -951,14 +969,9 @@ bool CPathfinderHelper::addTeleportWhirlpool(const CGWhirlpool * obj) const
return options.useTeleportWhirlpool && hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION) && obj; return options.useTeleportWhirlpool && hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION) && obj;
} }
int CPathfinderHelper::getHeroMaxMovementPoints(EPathfindingLayer layer) const int CPathfinderHelper::movementPointsAfterEmbark(int movement, int basicCost, bool disembark) const
{ {
return hero->maxMovePoints(layer); return hero->movementPointsAfterEmbark(movement, basicCost, disembark, getTurnInfo());
}
int CPathfinderHelper::movementPointsAfterEmbark(int movement, int turn, int action) const
{
return hero->movementPointsAfterEmbark(movement, turn, action, getTurnInfo());
} }
bool CPathfinderHelper::passOneTurnLimitCheck(const PathNodeInfo & source) const bool CPathfinderHelper::passOneTurnLimitCheck(const PathNodeInfo & source) const
@@ -1058,9 +1071,9 @@ int TurnInfo::valOfBonuses(Bonus::BonusType type, int subtype) const
int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const
{ {
if(maxMovePointsLand == -1) if(maxMovePointsLand == -1)
maxMovePointsLand = hero->maxMovePoints(true, this); maxMovePointsLand = hero->maxMovePointsCached(true, this);
if(maxMovePointsWater == -1) if(maxMovePointsWater == -1)
maxMovePointsWater = hero->maxMovePoints(false, this); maxMovePointsWater = hero->maxMovePointsCached(false, this);
return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand; return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
} }
@@ -1304,15 +1317,6 @@ bool CPathsInfo::getPath(CGPath & out, const int3 & dst) const
return true; return true;
} }
int CPathsInfo::getDistance(const int3 & tile) const
{
CGPath ret;
if(getPath(ret, tile))
return ret.nodes.size();
else
return 255;
}
const CGPathNode * CPathsInfo::getNode(const int3 & coord) const const CGPathNode * CPathsInfo::getNode(const int3 & coord) const
{ {
auto landNode = &nodes[coord.x][coord.y][coord.z][ELayer::LAND]; auto landNode = &nodes[coord.x][coord.y][coord.z][ELayer::LAND];
@@ -1344,7 +1348,9 @@ void PathNodeInfo::setNode(CGameState * gs, CGPathNode * n, bool excludeTopObjec
} }
CDestinationNodeInfo::CDestinationNodeInfo() CDestinationNodeInfo::CDestinationNodeInfo()
: PathNodeInfo(), blocked(false), action(CGPathNode::ENodeAction::UNKNOWN) : PathNodeInfo(),
blocked(false),
action(CGPathNode::ENodeAction::UNKNOWN)
{ {
} }
@@ -1360,13 +1366,8 @@ bool CDestinationNodeInfo::isBetterWay() const
{ {
if(node->turns == 0xff) //we haven't been here before if(node->turns == 0xff) //we haven't been here before
return true; return true;
else if(node->turns > turn) else
return true; return cost < node->cost; //this route is faster
else if(node->turns >= turn && node->moveRemains < movementLeft) //this route is faster
return true;
return false;
} }
bool PathNodeInfo::isNodeObjectVisitable() const bool PathNodeInfo::isNodeObjectVisitable() const

View File

@@ -49,22 +49,24 @@ struct DLL_LINKAGE CGPathNode
NOT_SET = 0, NOT_SET = 0,
ACCESSIBLE = 1, //tile can be entered and passed ACCESSIBLE = 1, //tile can be entered and passed
VISITABLE, //tile can be entered as the last tile in path VISITABLE, //tile can be entered as the last tile in path
BLOCKVIS, //visitable from neighbouring tile but not passable BLOCKVIS, //visitable from neighboring tile but not passable
FLYABLE, //can only be accessed in air layer FLYABLE, //can only be accessed in air layer
BLOCKED //tile can't be entered nor visited BLOCKED //tile can't be entered nor visited
}; };
CGPathNode * theNodeBefore; CGPathNode * theNodeBefore;
int3 coord; //coordinates int3 coord; //coordinates
ui32 moveRemains; //remaining tiles after hero reaches the tile
ui8 turns; //how many turns we have to wait before reachng the tile - 0 means current turn
ELayer layer; 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; EAccessibility accessible;
ENodeAction action; ENodeAction action;
bool locked; bool locked;
CGPathNode() CGPathNode()
: coord(int3(-1, -1, -1)), : coord(-1),
layer(ELayer::WRONG) layer(ELayer::WRONG)
{ {
reset(); reset();
@@ -76,6 +78,7 @@ struct DLL_LINKAGE CGPathNode
locked = false; locked = false;
accessible = NOT_SET; accessible = NOT_SET;
moveRemains = 0; moveRemains = 0;
cost = std::numeric_limits<float>::max();
turns = 255; turns = 255;
theNodeBefore = nullptr; theNodeBefore = nullptr;
action = UNKNOWN; action = UNKNOWN;
@@ -126,7 +129,6 @@ struct DLL_LINKAGE CPathsInfo
~CPathsInfo(); ~CPathsInfo();
const CGPathNode * getPathInfo(const int3 & tile) const; const CGPathNode * getPathInfo(const int3 & tile) const;
bool getPath(CGPath & out, const int3 & dst) const; bool getPath(CGPath & out, const int3 & dst) const;
int getDistance(const int3 & tile) const;
const CGPathNode * getNode(const int3 & coord) const; const CGPathNode * getNode(const int3 & coord) const;
STRONG_INLINE STRONG_INLINE
@@ -157,6 +159,7 @@ struct DLL_LINKAGE CDestinationNodeInfo : public PathNodeInfo
CGPathNode::ENodeAction action; CGPathNode::ENodeAction action;
int turn; int turn;
int movementLeft; int movementLeft;
float cost; //same as CGPathNode::cost
bool blocked; bool blocked;
bool isGuardianTile; bool isGuardianTile;
@@ -414,14 +417,10 @@ private:
struct NodeComparer struct NodeComparer
{ {
STRONG_INLINE
bool operator()(const CGPathNode * lhs, const CGPathNode * rhs) const bool operator()(const CGPathNode * lhs, const CGPathNode * rhs) const
{ {
if(rhs->turns > lhs->turns) return lhs->cost > rhs->cost;
return false;
else if(rhs->turns == lhs->turns && rhs->moveRemains <= lhs->moveRemains)
return false;
return true;
} }
}; };
boost::heap::priority_queue<CGPathNode *, boost::heap::compare<NodeComparer> > pq; boost::heap::priority_queue<CGPathNode *, boost::heap::compare<NodeComparer> > pq;
@@ -532,7 +531,6 @@ public:
); );
} }
int getHeroMaxMovementPoints(EPathfindingLayer layer) const; int movementPointsAfterEmbark(int movement, int basicCost, bool disembark) const;
int movementPointsAfterEmbark(int movement, int cost, int action) const;
bool passOneTurnLimitCheck(const PathNodeInfo & source) const; bool passOneTurnLimitCheck(const PathNodeInfo & source) const;
}; };

View File

@@ -193,15 +193,14 @@ bool CGHeroInstance::canLearnSkill() const
return secSkills.size() < GameConstants::SKILL_PER_HERO; return secSkills.size() < GameConstants::SKILL_PER_HERO;
} }
int CGHeroInstance::maxMovePoints(bool onLand, const TurnInfo * ti) const int CGHeroInstance::maxMovePoints(bool onLand) const
{ {
bool localTi = false; TurnInfo ti(this);
if(!ti) return maxMovePointsCached(onLand, &ti);
{
localTi = true;
ti = new TurnInfo(this);
} }
int CGHeroInstance::maxMovePointsCached(bool onLand, const TurnInfo * ti) const
{
int base; int base;
if(onLand) if(onLand)
@@ -225,9 +224,6 @@ int CGHeroInstance::maxMovePoints(bool onLand, const TurnInfo * ti) const
const int subtype = onLand ? SecondarySkill::LOGISTICS : SecondarySkill::NAVIGATION; const int subtype = onLand ? SecondarySkill::LOGISTICS : SecondarySkill::NAVIGATION;
const double modifier = ti->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0; const double modifier = ti->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0;
if(localTi)
delete ti;
return int(base * (1 + modifier)) + bonus; return int(base * (1 + modifier)) + bonus;
} }

View File

@@ -195,7 +195,10 @@ public:
void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
void levelUp(std::vector<SecondarySkill> skills); void levelUp(std::vector<SecondarySkill> skills);
int maxMovePoints(bool onLand, const TurnInfo * ti = nullptr) const; int maxMovePoints(bool onLand) const;
//cached version is much faster, TurnInfo construction is costly
int maxMovePointsCached(bool onLand, const TurnInfo * ti) const;
int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false, const TurnInfo * ti = nullptr) const; int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false, const TurnInfo * ti = nullptr) const;
static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest

View File

@@ -1750,7 +1750,7 @@ void CGameHandler::newTurn()
hth.id = h->id; hth.id = h->id;
auto ti = make_unique<TurnInfo>(h, 1); auto ti = make_unique<TurnInfo>(h, 1);
// TODO: this code executed when bonuses of previous day not yet updated (this happen in NewTurn::applyGs). See issue 2356 // TODO: this code executed when bonuses of previous day not yet updated (this happen in NewTurn::applyGs). See issue 2356
hth.move = h->maxMovePoints(gs->map->getTile(h->getPosition(false)).terType != ETerrainType::WATER, ti.get()); hth.move = h->maxMovePointsCached(gs->map->getTile(h->getPosition(false)).terType != ETerrainType::WATER, ti.get());
hth.mana = h->getManaNewTurn(); hth.mana = h->getManaNewTurn();
n.heroes.insert(hth); n.heroes.insert(hth);