mirror of
https://github.com/vcmi/vcmi.git
synced 2025-04-13 11:40:38 +02:00
Nullkiller: improve perofrmance of AI pathfinding
This commit is contained in:
parent
cf4484e071
commit
f0ceaf5852
@ -80,6 +80,12 @@ std::vector<SlotInfo>::iterator ArmyManager::getWeakestCreature(std::vector<Slot
|
||||
return weakest;
|
||||
}
|
||||
|
||||
class TemporaryArmy : public CArmedInstance
|
||||
{
|
||||
public:
|
||||
void armyChanged() override {}
|
||||
};
|
||||
|
||||
std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source) const
|
||||
{
|
||||
auto sortedSlots = getSortedSlots(target, source);
|
||||
@ -94,7 +100,7 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
||||
std::vector<SlotInfo> resultingArmy;
|
||||
uint64_t armyValue = 0;
|
||||
|
||||
CArmedInstance newArmyInstance;
|
||||
TemporaryArmy newArmyInstance;
|
||||
auto bonusModifiers = armyCarrier->getBonuses(Selector::type(Bonus::MORALE));
|
||||
|
||||
for(auto bonus : *bonusModifiers)
|
||||
@ -133,6 +139,8 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
||||
}
|
||||
}
|
||||
|
||||
newArmyInstance.updateMoraleBonusFromArmy();
|
||||
|
||||
for(auto & slot : newArmyInstance.Slots())
|
||||
{
|
||||
auto morale = slot.second->MoraleVal();
|
||||
@ -142,6 +150,10 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
|
||||
{
|
||||
multiplier += morale * 0.083f;
|
||||
}
|
||||
else if(morale > 0)
|
||||
{
|
||||
multiplier += morale * 0.04f;
|
||||
}
|
||||
|
||||
newValue += multiplier * slot.second->getPower();
|
||||
}
|
||||
|
@ -20,12 +20,19 @@
|
||||
#include "../../../lib/CPlayerState.h"
|
||||
|
||||
std::shared_ptr<boost::multi_array<AIPathNode, 5>> AISharedStorage::shared;
|
||||
std::set<int3> commitedTiles;
|
||||
std::set<int3> commitedTilesInitial;
|
||||
|
||||
const uint64_t FirstActorMask = 1;
|
||||
const int BUCKET_COUNT = 11;
|
||||
const int BUCKET_SIZE = GameConstants::MAX_HEROES_PER_PLAYER;
|
||||
const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE;
|
||||
|
||||
AISharedStorage::AISharedStorage(int3 sizes)
|
||||
{
|
||||
if(!shared){
|
||||
shared.reset(new boost::multi_array<AIPathNode, 5>(
|
||||
boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS][AINodeStorage::NUM_CHAINS]));
|
||||
boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS][NUM_CHAINS]));
|
||||
}
|
||||
|
||||
nodes = shared;
|
||||
@ -121,8 +128,19 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
|
||||
const EPathfindingLayer layer,
|
||||
const ChainActor * actor)
|
||||
{
|
||||
for(AIPathNode & node : nodes.get(pos, layer))
|
||||
int bucketIndex = ((uintptr_t)actor) % BUCKET_COUNT;
|
||||
int bucketOffset = bucketIndex * BUCKET_SIZE;
|
||||
auto chains = nodes.get(pos, layer);
|
||||
|
||||
if(chains[0].blocked())
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
for(auto i = BUCKET_SIZE - 1; i >= 0; i--)
|
||||
{
|
||||
AIPathNode & node = chains[i + bucketOffset];
|
||||
|
||||
if(node.actor == actor)
|
||||
{
|
||||
return &node;
|
||||
@ -243,6 +261,11 @@ void AINodeStorage::commit(
|
||||
destination->actor->chainMask,
|
||||
destination->actor->armyValue);
|
||||
#endif
|
||||
|
||||
if(destination->turns <= heroChainTurn)
|
||||
{
|
||||
commitedTiles.insert(destination->coord);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
|
||||
@ -271,18 +294,39 @@ std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
|
||||
return neighbours;
|
||||
}
|
||||
|
||||
EPathfindingLayer phisycalLayers[2] = {EPathfindingLayer::LAND, EPathfindingLayer::SAIL};
|
||||
|
||||
bool AINodeStorage::increaseHeroChainTurnLimit()
|
||||
{
|
||||
if(heroChainTurn >= heroChainMaxTurns)
|
||||
return false;
|
||||
|
||||
heroChainTurn++;
|
||||
commitedTiles.clear();
|
||||
|
||||
for(auto layer : phisycalLayers)
|
||||
{
|
||||
foreach_tile_pos([&](const int3 & pos)
|
||||
{
|
||||
auto chains = nodes.get(pos, layer);
|
||||
|
||||
if(!chains[0].blocked())
|
||||
{
|
||||
for(AIPathNode & node : chains)
|
||||
{
|
||||
if(node.turns <= heroChainTurn && node.action != CGPathNode::ENodeAction::UNKNOWN)
|
||||
{
|
||||
commitedTiles.insert(pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EPathfindingLayer phisycalLayers[2] = {EPathfindingLayer::LAND, EPathfindingLayer::SAIL};
|
||||
|
||||
bool AINodeStorage::calculateHeroChainFinal()
|
||||
{
|
||||
heroChainPass = EHeroChainPass::FINAL;
|
||||
@ -294,15 +338,18 @@ bool AINodeStorage::calculateHeroChainFinal()
|
||||
{
|
||||
auto chains = nodes.get(pos, layer);
|
||||
|
||||
for(AIPathNode & node : chains)
|
||||
if(!chains[0].blocked())
|
||||
{
|
||||
if(node.turns > heroChainTurn
|
||||
&& !node.locked
|
||||
&& node.action != CGPathNode::ENodeAction::UNKNOWN
|
||||
&& node.actor->actorExchangeCount > 1
|
||||
&& !hasBetterChain(&node, &node, chains))
|
||||
for(AIPathNode & node : chains)
|
||||
{
|
||||
heroChain.push_back(&node);
|
||||
if(node.turns > heroChainTurn
|
||||
&& !node.locked
|
||||
&& node.action != CGPathNode::ENodeAction::UNKNOWN
|
||||
&& node.actor->actorExchangeCount > 1
|
||||
&& !hasBetterChain(&node, &node, chains))
|
||||
{
|
||||
heroChain.push_back(&node);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -322,14 +369,18 @@ bool AINodeStorage::calculateHeroChain()
|
||||
existingChains.reserve(NUM_CHAINS);
|
||||
newChains.reserve(NUM_CHAINS);
|
||||
|
||||
for(auto layer : phisycalLayers)
|
||||
for(auto & pos : commitedTiles)
|
||||
{
|
||||
foreach_tile_pos([&](const int3 & pos)
|
||||
for(auto layer : phisycalLayers)
|
||||
{
|
||||
auto chains = nodes.get(pos, layer);
|
||||
|
||||
existingChains.resize(0);
|
||||
newChains.resize(0);
|
||||
// fast cut inactive nodes
|
||||
if(chains[0].blocked())
|
||||
continue;
|
||||
|
||||
existingChains.clear();
|
||||
newChains.clear();
|
||||
|
||||
for(AIPathNode & node : chains)
|
||||
{
|
||||
@ -347,9 +398,11 @@ bool AINodeStorage::calculateHeroChain()
|
||||
|
||||
cleanupInefectiveChains(newChains);
|
||||
addHeroChain(newChains);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
commitedTiles.clear();
|
||||
|
||||
return heroChain.size();
|
||||
}
|
||||
|
||||
@ -364,6 +417,7 @@ bool AINodeStorage::selectFirstActor()
|
||||
});
|
||||
|
||||
chainMask = strongest->chainMask;
|
||||
commitedTilesInitial = commitedTiles;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -395,6 +449,7 @@ bool AINodeStorage::selectNextActor()
|
||||
if(nextActor != actors.end())
|
||||
{
|
||||
chainMask = nextActor->get()->chainMask;
|
||||
commitedTiles = commitedTilesInitial;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -657,7 +712,7 @@ void AINodeStorage::setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes)
|
||||
|
||||
for(auto & hero : heroes)
|
||||
{
|
||||
uint64_t mask = 1 << actors.size();
|
||||
uint64_t mask = FirstActorMask << actors.size();
|
||||
auto actor = std::make_shared<HeroActor>(hero.first, hero.second, mask, ai);
|
||||
|
||||
if(actor->hero->tempOwner != ai->playerID)
|
||||
@ -678,7 +733,7 @@ void AINodeStorage::setTownsAndDwellings(
|
||||
{
|
||||
for(auto town : towns)
|
||||
{
|
||||
uint64_t mask = 1 << actors.size();
|
||||
uint64_t mask = FirstActorMask << actors.size();
|
||||
|
||||
// TODO: investigate logix of second condition || ai->nullkiller->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE
|
||||
// check defence imrove
|
||||
@ -695,7 +750,7 @@ void AINodeStorage::setTownsAndDwellings(
|
||||
{
|
||||
if(obj->ID == Obj::HILL_FORT)
|
||||
{
|
||||
uint64_t mask = 1 << actors.size();
|
||||
uint64_t mask = FirstActorMask << actors.size();
|
||||
|
||||
actors.push_back(std::make_shared<HillFortActor>(obj, mask));
|
||||
}
|
||||
|
@ -30,6 +30,13 @@ struct AIPathNode : public CGPathNode
|
||||
const AIPathNode * chainOther;
|
||||
std::shared_ptr<const SpecialAction> specialAction;
|
||||
const ChainActor * actor;
|
||||
|
||||
STRONG_INLINE
|
||||
bool blocked() const
|
||||
{
|
||||
return accessible == CGPathNode::EAccessibility::NOT_SET
|
||||
|| accessible == CGPathNode::EAccessibility::BLOCKED;
|
||||
}
|
||||
};
|
||||
|
||||
struct AIPathNodeInfo
|
||||
@ -143,9 +150,7 @@ private:
|
||||
uint8_t scoutTurnDistanceLimit;
|
||||
|
||||
public:
|
||||
/// more than 1 chain layer for each hero allows us to have more than 1 path to each tile so we can chose more optimal one.
|
||||
static const int NUM_CHAINS = 10 * GameConstants::MAX_HEROES_PER_PLAYER;
|
||||
|
||||
/// more than 1 chain layer for each hero allows us to have more than 1 path to each tile so we can chose more optimal one.
|
||||
AINodeStorage(const Nullkiller * ai, const int3 & sizes);
|
||||
~AINodeStorage();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user