diff --git a/AI/Nullkiller/Analyzers/ArmyManager.cpp b/AI/Nullkiller/Analyzers/ArmyManager.cpp index 63a572ebe..60f9b2e74 100644 --- a/AI/Nullkiller/Analyzers/ArmyManager.cpp +++ b/AI/Nullkiller/Analyzers/ArmyManager.cpp @@ -80,6 +80,12 @@ std::vector::iterator ArmyManager::getWeakestCreature(std::vector ArmyManager::getBestArmy(const IBonusBearer * armyCarrier, const CCreatureSet * target, const CCreatureSet * source) const { auto sortedSlots = getSortedSlots(target, source); @@ -94,7 +100,7 @@ std::vector ArmyManager::getBestArmy(const IBonusBearer * armyCarrier, std::vector 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 ArmyManager::getBestArmy(const IBonusBearer * armyCarrier, } } + newArmyInstance.updateMoraleBonusFromArmy(); + for(auto & slot : newArmyInstance.Slots()) { auto morale = slot.second->MoraleVal(); @@ -142,6 +150,10 @@ std::vector ArmyManager::getBestArmy(const IBonusBearer * armyCarrier, { multiplier += morale * 0.083f; } + else if(morale > 0) + { + multiplier += morale * 0.04f; + } newValue += multiplier * slot.second->getPower(); } diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index 40f47d701..4a40be2e4 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -20,12 +20,19 @@ #include "../../../lib/CPlayerState.h" std::shared_ptr> AISharedStorage::shared; +std::set commitedTiles; +std::set 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( - 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 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 AINodeStorage::calculateNeighbours( @@ -271,18 +294,39 @@ std::vector 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 heroes) for(auto & hero : heroes) { - uint64_t mask = 1 << actors.size(); + uint64_t mask = FirstActorMask << actors.size(); auto actor = std::make_shared(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(obj, mask)); } diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.h b/AI/Nullkiller/Pathfinding/AINodeStorage.h index 2db675faf..f1265cd6f 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.h +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.h @@ -30,6 +30,13 @@ struct AIPathNode : public CGPathNode const AIPathNode * chainOther; std::shared_ptr 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();