1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-04-27 12:22:45 +02:00

Nullkiller: improve perofrmance of AI pathfinding

This commit is contained in:
Andrii Danylchenko 2021-05-16 14:56:53 +03:00 committed by Andrii Danylchenko
parent cf4484e071
commit f0ceaf5852
3 changed files with 95 additions and 23 deletions

View File

@ -80,6 +80,12 @@ std::vector<SlotInfo>::iterator ArmyManager::getWeakestCreature(std::vector<Slot
return weakest; 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 std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source) const
{ {
auto sortedSlots = getSortedSlots(target, source); auto sortedSlots = getSortedSlots(target, source);
@ -94,7 +100,7 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
std::vector<SlotInfo> resultingArmy; std::vector<SlotInfo> resultingArmy;
uint64_t armyValue = 0; uint64_t armyValue = 0;
CArmedInstance newArmyInstance; TemporaryArmy newArmyInstance;
auto bonusModifiers = armyCarrier->getBonuses(Selector::type(Bonus::MORALE)); auto bonusModifiers = armyCarrier->getBonuses(Selector::type(Bonus::MORALE));
for(auto bonus : *bonusModifiers) for(auto bonus : *bonusModifiers)
@ -133,6 +139,8 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
} }
} }
newArmyInstance.updateMoraleBonusFromArmy();
for(auto & slot : newArmyInstance.Slots()) for(auto & slot : newArmyInstance.Slots())
{ {
auto morale = slot.second->MoraleVal(); auto morale = slot.second->MoraleVal();
@ -142,6 +150,10 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
{ {
multiplier += morale * 0.083f; multiplier += morale * 0.083f;
} }
else if(morale > 0)
{
multiplier += morale * 0.04f;
}
newValue += multiplier * slot.second->getPower(); newValue += multiplier * slot.second->getPower();
} }

View File

@ -20,12 +20,19 @@
#include "../../../lib/CPlayerState.h" #include "../../../lib/CPlayerState.h"
std::shared_ptr<boost::multi_array<AIPathNode, 5>> AISharedStorage::shared; 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) AISharedStorage::AISharedStorage(int3 sizes)
{ {
if(!shared){ if(!shared){
shared.reset(new boost::multi_array<AIPathNode, 5>( 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; nodes = shared;
@ -121,8 +128,19 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
const EPathfindingLayer layer, const EPathfindingLayer layer,
const ChainActor * actor) 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) if(node.actor == actor)
{ {
return &node; return &node;
@ -243,6 +261,11 @@ void AINodeStorage::commit(
destination->actor->chainMask, destination->actor->chainMask,
destination->actor->armyValue); destination->actor->armyValue);
#endif #endif
if(destination->turns <= heroChainTurn)
{
commitedTiles.insert(destination->coord);
}
} }
std::vector<CGPathNode *> AINodeStorage::calculateNeighbours( std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
@ -271,18 +294,39 @@ std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
return neighbours; return neighbours;
} }
EPathfindingLayer phisycalLayers[2] = {EPathfindingLayer::LAND, EPathfindingLayer::SAIL};
bool AINodeStorage::increaseHeroChainTurnLimit() bool AINodeStorage::increaseHeroChainTurnLimit()
{ {
if(heroChainTurn >= heroChainMaxTurns) if(heroChainTurn >= heroChainMaxTurns)
return false; return false;
heroChainTurn++; 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; return true;
} }
EPathfindingLayer phisycalLayers[2] = {EPathfindingLayer::LAND, EPathfindingLayer::SAIL};
bool AINodeStorage::calculateHeroChainFinal() bool AINodeStorage::calculateHeroChainFinal()
{ {
heroChainPass = EHeroChainPass::FINAL; heroChainPass = EHeroChainPass::FINAL;
@ -294,6 +338,8 @@ bool AINodeStorage::calculateHeroChainFinal()
{ {
auto chains = nodes.get(pos, layer); auto chains = nodes.get(pos, layer);
if(!chains[0].blocked())
{
for(AIPathNode & node : chains) for(AIPathNode & node : chains)
{ {
if(node.turns > heroChainTurn if(node.turns > heroChainTurn
@ -305,6 +351,7 @@ bool AINodeStorage::calculateHeroChainFinal()
heroChain.push_back(&node); heroChain.push_back(&node);
} }
} }
}
}); });
} }
@ -322,14 +369,18 @@ bool AINodeStorage::calculateHeroChain()
existingChains.reserve(NUM_CHAINS); existingChains.reserve(NUM_CHAINS);
newChains.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); auto chains = nodes.get(pos, layer);
existingChains.resize(0); // fast cut inactive nodes
newChains.resize(0); if(chains[0].blocked())
continue;
existingChains.clear();
newChains.clear();
for(AIPathNode & node : chains) for(AIPathNode & node : chains)
{ {
@ -347,8 +398,10 @@ bool AINodeStorage::calculateHeroChain()
cleanupInefectiveChains(newChains); cleanupInefectiveChains(newChains);
addHeroChain(newChains); addHeroChain(newChains);
});
} }
}
commitedTiles.clear();
return heroChain.size(); return heroChain.size();
} }
@ -364,6 +417,7 @@ bool AINodeStorage::selectFirstActor()
}); });
chainMask = strongest->chainMask; chainMask = strongest->chainMask;
commitedTilesInitial = commitedTiles;
return true; return true;
} }
@ -395,6 +449,7 @@ bool AINodeStorage::selectNextActor()
if(nextActor != actors.end()) if(nextActor != actors.end())
{ {
chainMask = nextActor->get()->chainMask; chainMask = nextActor->get()->chainMask;
commitedTiles = commitedTilesInitial;
return true; return true;
} }
@ -657,7 +712,7 @@ void AINodeStorage::setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes)
for(auto & hero : 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); auto actor = std::make_shared<HeroActor>(hero.first, hero.second, mask, ai);
if(actor->hero->tempOwner != ai->playerID) if(actor->hero->tempOwner != ai->playerID)
@ -678,7 +733,7 @@ void AINodeStorage::setTownsAndDwellings(
{ {
for(auto town : towns) 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 // TODO: investigate logix of second condition || ai->nullkiller->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE
// check defence imrove // check defence imrove
@ -695,7 +750,7 @@ void AINodeStorage::setTownsAndDwellings(
{ {
if(obj->ID == Obj::HILL_FORT) 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)); actors.push_back(std::make_shared<HillFortActor>(obj, mask));
} }

View File

@ -30,6 +30,13 @@ struct AIPathNode : public CGPathNode
const AIPathNode * chainOther; const AIPathNode * chainOther;
std::shared_ptr<const SpecialAction> specialAction; std::shared_ptr<const SpecialAction> specialAction;
const ChainActor * actor; const ChainActor * actor;
STRONG_INLINE
bool blocked() const
{
return accessible == CGPathNode::EAccessibility::NOT_SET
|| accessible == CGPathNode::EAccessibility::BLOCKED;
}
}; };
struct AIPathNodeInfo struct AIPathNodeInfo
@ -144,8 +151,6 @@ private:
public: 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. /// 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;
AINodeStorage(const Nullkiller * ai, const int3 & sizes); AINodeStorage(const Nullkiller * ai, const int3 & sizes);
~AINodeStorage(); ~AINodeStorage();