mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
AI: inefective chain cancellation
This commit is contained in:
parent
0e328ab3c2
commit
f44eaf8132
@ -245,15 +245,18 @@ bool AINodeStorage::calculateHeroChain()
|
||||
heroChainPass = true;
|
||||
heroChain.resize(0);
|
||||
|
||||
std::vector<AIPathNode *> buffer;
|
||||
std::vector<AIPathNode *> existingChains;
|
||||
std::vector<ExchangeCandidate> newChains;
|
||||
|
||||
buffer.reserve(NUM_CHAINS);
|
||||
existingChains.reserve(NUM_CHAINS);
|
||||
newChains.reserve(NUM_CHAINS);
|
||||
|
||||
foreach_tile_pos([&](const int3 & pos) {
|
||||
auto layer = EPathfindingLayer::LAND;
|
||||
auto chains = nodes[pos.x][pos.y][pos.z][layer];
|
||||
|
||||
buffer.resize(0);
|
||||
existingChains.resize(0);
|
||||
newChains.resize(0);
|
||||
|
||||
for(AIPathNode & node : chains)
|
||||
{
|
||||
@ -261,22 +264,40 @@ bool AINodeStorage::calculateHeroChain()
|
||||
logAi->trace(node.actor->toString());
|
||||
|
||||
if(node.turns <= heroChainMaxTurns && node.action != CGPathNode::ENodeAction::UNKNOWN)
|
||||
buffer.push_back(&node);
|
||||
existingChains.push_back(&node);
|
||||
}
|
||||
|
||||
for(AIPathNode * node : buffer)
|
||||
for(AIPathNode * node : existingChains)
|
||||
{
|
||||
if(node->actor->hero)
|
||||
{
|
||||
addHeroChain(node, buffer);
|
||||
calculateHeroChain(node, existingChains, newChains);
|
||||
}
|
||||
}
|
||||
|
||||
cleanupInefectiveChains(newChains);
|
||||
addHeroChain(newChains);
|
||||
});
|
||||
|
||||
return heroChain.size();
|
||||
}
|
||||
|
||||
void AINodeStorage::addHeroChain(AIPathNode * srcNode, std::vector<AIPathNode *> variants)
|
||||
void AINodeStorage::cleanupInefectiveChains(std::vector<ExchangeCandidate> & result) const
|
||||
{
|
||||
vstd::erase_if(result, [&](ExchangeCandidate & chainInfo) -> bool
|
||||
{
|
||||
auto pos = chainInfo.coord;
|
||||
auto chains = nodes[pos.x][pos.y][pos.z][EPathfindingLayer::LAND];
|
||||
|
||||
return hasBetterChain(chainInfo.carrierParent, &chainInfo, chains)
|
||||
|| hasBetterChain(chainInfo.carrierParent, &chainInfo, result);
|
||||
});
|
||||
}
|
||||
|
||||
void AINodeStorage::calculateHeroChain(
|
||||
AIPathNode * srcNode,
|
||||
const std::vector<AIPathNode *> & variants,
|
||||
std::vector<ExchangeCandidate> & result) const
|
||||
{
|
||||
for(AIPathNode * node : variants)
|
||||
{
|
||||
@ -296,12 +317,14 @@ void AINodeStorage::addHeroChain(AIPathNode * srcNode, std::vector<AIPathNode *>
|
||||
srcNode->coord.toString());
|
||||
#endif
|
||||
|
||||
addHeroChain(srcNode, node);
|
||||
//addHeroChain(&node, srcNode);
|
||||
calculateHeroChain(srcNode, node, result);
|
||||
}
|
||||
}
|
||||
|
||||
void AINodeStorage::addHeroChain(AIPathNode * carrier, AIPathNode * other)
|
||||
void AINodeStorage::calculateHeroChain(
|
||||
AIPathNode * carrier,
|
||||
AIPathNode * other,
|
||||
std::vector<ExchangeCandidate> & result) const
|
||||
{
|
||||
if(!carrier->actor->isMovable)
|
||||
return;
|
||||
@ -330,6 +353,18 @@ void AINodeStorage::addHeroChain(AIPathNode * carrier, AIPathNode * other)
|
||||
}
|
||||
|
||||
auto newActor = carrier->actor->exchange(other->actor);
|
||||
|
||||
result.push_back(calculateExchange(newActor, carrier, other));
|
||||
}
|
||||
}
|
||||
|
||||
void AINodeStorage::addHeroChain(const std::vector<ExchangeCandidate> & result)
|
||||
{
|
||||
for(const ExchangeCandidate & chainInfo : result)
|
||||
{
|
||||
auto carrier = chainInfo.carrierParent;
|
||||
auto newActor = chainInfo.actor;
|
||||
auto other = chainInfo.otherParent;
|
||||
auto chainNodeOptional = getOrCreateNode(carrier->coord, carrier->layer, newActor);
|
||||
|
||||
if(!chainNodeOptional)
|
||||
@ -337,74 +372,81 @@ void AINodeStorage::addHeroChain(AIPathNode * carrier, AIPathNode * other)
|
||||
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||
logAi->trace("Exchange at %s can not allocate node. Blocked.", carrier->coord.toString());
|
||||
#endif
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto chainNode = chainNodeOptional.get();
|
||||
auto exchangeNode = chainNodeOptional.get();
|
||||
|
||||
if(chainNode->action != CGPathNode::ENodeAction::UNKNOWN)
|
||||
if(exchangeNode->action != CGPathNode::ENodeAction::UNKNOWN)
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||
logAi->trace("Exchange at %s node is already in use. Blocked.", carrier->coord.toString());
|
||||
#endif
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(commitExchange(chainNode, carrier, other))
|
||||
if(exchangeNode->turns != 0xFF && exchangeNode->cost < chainInfo.cost)
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||
logAi->trace(
|
||||
"Chain accepted at %s %s -> %s, mask %i, cost %f",
|
||||
chainNode->coord.toString(),
|
||||
other->actor->toString(),
|
||||
chainNode->actor->toString(),
|
||||
chainNode->actor->chainMask,
|
||||
chainNode->cost);
|
||||
"Exchange at %s is is not effective enough. %f < %f",
|
||||
exchangeNode->coord.toString(),
|
||||
exchangeNode->cost,
|
||||
chainInfo.cost);
|
||||
#endif
|
||||
heroChain.push_back(chainNode);
|
||||
continue;
|
||||
}
|
||||
|
||||
commit(exchangeNode, carrier, carrier->action, chainInfo.turns, chainInfo.moveRemains, chainInfo.cost);
|
||||
|
||||
exchangeNode->chainOther = other;
|
||||
exchangeNode->armyLoss = chainInfo.armyLoss;
|
||||
|
||||
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||
logAi->trace(
|
||||
"Chain accepted at %s %s -> %s, mask %i, cost %f",
|
||||
exchangeNode->coord.toString(),
|
||||
other->actor->toString(),
|
||||
exchangeNode->actor->toString(),
|
||||
exchangeNode->actor->chainMask,
|
||||
exchangeNode->cost);
|
||||
#endif
|
||||
heroChain.push_back(exchangeNode);
|
||||
}
|
||||
}
|
||||
|
||||
bool AINodeStorage::commitExchange(
|
||||
AIPathNode * exchangeNode,
|
||||
ExchangeCandidate AINodeStorage::calculateExchange(
|
||||
ChainActor * exchangeActor,
|
||||
AIPathNode * carrierParentNode,
|
||||
AIPathNode * otherParentNode) const
|
||||
{
|
||||
ExchangeCandidate candidate;
|
||||
|
||||
auto carrierActor = carrierParentNode->actor;
|
||||
auto exchangeActor = exchangeNode->actor;
|
||||
auto otherActor = otherParentNode->actor;
|
||||
|
||||
auto armyLoss = carrierParentNode->armyLoss + otherParentNode->armyLoss;
|
||||
auto turns = carrierParentNode->turns;
|
||||
auto cost = carrierParentNode->cost + otherParentNode->cost / 1000.0;
|
||||
auto movementLeft = carrierParentNode->moveRemains;
|
||||
candidate.layer = carrierParentNode->layer;
|
||||
candidate.coord = carrierParentNode->coord;
|
||||
candidate.carrierParent = carrierParentNode;
|
||||
candidate.otherParent = otherParentNode;
|
||||
candidate.actor = exchangeActor;
|
||||
candidate.armyLoss = carrierParentNode->armyLoss + otherParentNode->armyLoss;
|
||||
candidate.turns = carrierParentNode->turns;
|
||||
candidate.cost = carrierParentNode->cost + otherParentNode->cost / 1000.0;
|
||||
candidate.moveRemains = carrierParentNode->moveRemains;
|
||||
|
||||
if(carrierParentNode->turns < otherParentNode->turns)
|
||||
{
|
||||
int moveRemains = exchangeActor->hero->maxMovePoints(exchangeNode->layer);
|
||||
int moveRemains = exchangeActor->hero->maxMovePoints(carrierParentNode->layer);
|
||||
float waitingCost = otherParentNode->turns - carrierParentNode->turns - 1
|
||||
+ carrierParentNode->moveRemains / (float)moveRemains;
|
||||
|
||||
turns = otherParentNode->turns;
|
||||
cost += waitingCost;
|
||||
movementLeft = moveRemains;
|
||||
candidate.turns = otherParentNode->turns;
|
||||
candidate.cost += waitingCost;
|
||||
candidate.moveRemains = moveRemains;
|
||||
}
|
||||
|
||||
if(exchangeNode->turns != 0xFF && exchangeNode->cost < cost)
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||
logAi->trace("Exchange at %s is is not effective enough. %f < %f", exchangeNode->coord.toString(), exchangeNode->cost, cost);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
commit(exchangeNode, carrierParentNode, carrierParentNode->action, turns, movementLeft, cost);
|
||||
|
||||
exchangeNode->chainOther = otherParentNode;
|
||||
exchangeNode->armyLoss = armyLoss;
|
||||
|
||||
return true;
|
||||
return candidate;
|
||||
}
|
||||
|
||||
const CGHeroInstance * AINodeStorage::getHero(const CGPathNode * node) const
|
||||
@ -587,13 +629,23 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode
|
||||
{
|
||||
auto pos = destination.coord;
|
||||
auto chains = nodes[pos.x][pos.y][pos.z][EPathfindingLayer::LAND];
|
||||
auto destinationNode = getAINode(destination.node);
|
||||
|
||||
return hasBetterChain(source.node, getAINode(destination.node), chains);
|
||||
}
|
||||
|
||||
template<class NodeRange>
|
||||
bool AINodeStorage::hasBetterChain(
|
||||
const CGPathNode * source,
|
||||
const AIPathNode * destinationNode,
|
||||
const NodeRange & chains) const
|
||||
{
|
||||
auto dstActor = destinationNode->actor;
|
||||
|
||||
for(const AIPathNode & node : chains)
|
||||
{
|
||||
auto sameNode = node.actor == destinationNode->actor;
|
||||
|
||||
if(sameNode || node.action == CGPathNode::ENodeAction::UNKNOWN)
|
||||
if(sameNode || node.action == CGPathNode::ENodeAction::UNKNOWN || !node.actor->hero)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -605,14 +657,26 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace(
|
||||
"Block ineficient move %s:->%s, mask=%i, mp diff: %i",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString(),
|
||||
source->coord.toString(),
|
||||
destinationNode->coord.toString(),
|
||||
destinationNode->actor->chainMask,
|
||||
node.moveRemains - destinationNode->moveRemains);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(dstActor->actorExchangeCount == 1)
|
||||
continue;
|
||||
|
||||
auto nodeActor = node.actor;
|
||||
|
||||
if(nodeActor->armyValue - node.armyLoss >= dstActor->armyValue - destinationNode->armyLoss
|
||||
&& nodeActor->heroFightingStrength >= dstActor->heroFightingStrength
|
||||
&& node.cost >= destinationNode->cost)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -68,6 +68,12 @@ struct AIPath
|
||||
uint64_t getHeroStrength() const;
|
||||
};
|
||||
|
||||
struct ExchangeCandidate : public AIPathNode
|
||||
{
|
||||
AIPathNode * carrierParent;
|
||||
AIPathNode * otherParent;
|
||||
};
|
||||
|
||||
class AINodeStorage : public INodeStorage
|
||||
{
|
||||
private:
|
||||
@ -110,6 +116,13 @@ public:
|
||||
void updateAINode(CGPathNode * node, std::function<void (AIPathNode *)> updater);
|
||||
|
||||
bool hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const;
|
||||
|
||||
template<class NodeRange>
|
||||
bool hasBetterChain(
|
||||
const CGPathNode * source,
|
||||
const AIPathNode * destinationNode,
|
||||
const NodeRange & chains) const;
|
||||
|
||||
boost::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, const ChainActor * actor);
|
||||
std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const;
|
||||
bool isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const;
|
||||
@ -130,8 +143,20 @@ public:
|
||||
private:
|
||||
STRONG_INLINE
|
||||
void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility);
|
||||
void addHeroChain(AIPathNode * srcNode, std::vector<AIPathNode *> variants);
|
||||
void addHeroChain(AIPathNode * carrier, AIPathNode * other);
|
||||
|
||||
void calculateHeroChain(
|
||||
AIPathNode * srcNode,
|
||||
const std::vector<AIPathNode *> & variants,
|
||||
std::vector<ExchangeCandidate> & result) const;
|
||||
|
||||
void calculateHeroChain(
|
||||
AIPathNode * carrier,
|
||||
AIPathNode * other,
|
||||
std::vector<ExchangeCandidate> & result) const;
|
||||
|
||||
void cleanupInefectiveChains(std::vector<ExchangeCandidate> & result) const;
|
||||
void addHeroChain(const std::vector<ExchangeCandidate> & result);
|
||||
|
||||
void calculateTownPortalTeleportations(const PathNodeInfo & source, std::vector<CGPathNode *> & neighbours);
|
||||
void fillChainInfo(const AIPathNode * node, AIPath & path) const;
|
||||
void commit(
|
||||
@ -142,8 +167,8 @@ private:
|
||||
int movementLeft,
|
||||
float cost) const;
|
||||
|
||||
bool AINodeStorage::commitExchange(
|
||||
AIPathNode * exchangeNode,
|
||||
ExchangeCandidate calculateExchange(
|
||||
ChainActor * exchangeActor,
|
||||
AIPathNode * carrierParentNode,
|
||||
AIPathNode * otherParentNode) const;
|
||||
};
|
||||
|
@ -18,25 +18,28 @@
|
||||
|
||||
ChainActor::ChainActor(const CGHeroInstance * hero, uint64_t chainMask)
|
||||
:hero(hero), isMovable(true), chainMask(chainMask), creatureSet(hero),
|
||||
baseActor(this), carrierParent(nullptr), otherParent(nullptr)
|
||||
baseActor(this), carrierParent(nullptr), otherParent(nullptr), actorExchangeCount(1)
|
||||
{
|
||||
initialPosition = hero->visitablePos();
|
||||
layer = hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND;
|
||||
initialMovement = hero->movement;
|
||||
initialTurn = 0;
|
||||
armyValue = hero->getArmyStrength();
|
||||
heroFightingStrength = hero->getFightingStrength();
|
||||
}
|
||||
|
||||
ChainActor::ChainActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * heroArmy)
|
||||
:hero(carrier->hero), isMovable(true), creatureSet(heroArmy), chainMask(carrier->chainMask | other->chainMask),
|
||||
baseActor(this), carrierParent(carrier), otherParent(other)
|
||||
baseActor(this), carrierParent(carrier), otherParent(other), heroFightingStrength(carrier->heroFightingStrength),
|
||||
actorExchangeCount(carrier->actorExchangeCount + other->actorExchangeCount)
|
||||
{
|
||||
armyValue = heroArmy->getArmyStrength();
|
||||
}
|
||||
|
||||
ChainActor::ChainActor(const CGObjectInstance * obj, const CCreatureSet * creatureSet, uint64_t chainMask, int initialTurn)
|
||||
:hero(nullptr), isMovable(false), creatureSet(creatureSet), chainMask(chainMask),
|
||||
baseActor(this), carrierParent(nullptr), otherParent(nullptr), initialTurn(initialTurn), initialMovement(0)
|
||||
baseActor(this), carrierParent(nullptr), otherParent(nullptr), initialTurn(initialTurn), initialMovement(0),
|
||||
heroFightingStrength(0), actorExchangeCount(1)
|
||||
{
|
||||
initialPosition = obj->visitablePos();
|
||||
layer = EPathfindingLayer::LAND;
|
||||
@ -77,6 +80,7 @@ void ChainActor::setBaseActor(HeroActor * base)
|
||||
chainMask = base->chainMask;
|
||||
creatureSet = base->creatureSet;
|
||||
isMovable = base->isMovable;
|
||||
heroFightingStrength = base->heroFightingStrength;
|
||||
}
|
||||
|
||||
void HeroActor::setupSpecialActors()
|
||||
|
@ -44,6 +44,8 @@ public:
|
||||
uint32_t initialMovement;
|
||||
uint32_t initialTurn;
|
||||
uint64_t armyValue;
|
||||
float heroFightingStrength;
|
||||
uint8_t actorExchangeCount;
|
||||
|
||||
ChainActor(){}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user