diff --git a/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp b/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp index 7eb4e3ce9..37f749cff 100644 --- a/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp +++ b/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp @@ -44,7 +44,7 @@ void DangerHitMapAnalyzer::updateHitMap() for(auto pair : heroes) { - ai->pathfinder->updatePaths(pair.second, false); + ai->pathfinder->updatePaths(pair.second, PathfinderSettings()); foreach_tile_pos([&](const int3 & pos) { diff --git a/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h b/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h index db2882bed..7d2e70661 100644 --- a/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h +++ b/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h @@ -15,13 +15,13 @@ struct HitMapInfo { uint64_t danger; uint8_t turn; - const CGHeroInstance * hero; + HeroPtr hero; void reset() { danger = 0; turn = 255; - hero = nullptr; + hero = HeroPtr(); } }; diff --git a/AI/Nullkiller/Engine/Nullkiller.cpp b/AI/Nullkiller/Engine/Nullkiller.cpp index e082092a4..556b6f998 100644 --- a/AI/Nullkiller/Engine/Nullkiller.cpp +++ b/AI/Nullkiller/Engine/Nullkiller.cpp @@ -25,6 +25,12 @@ extern boost::thread_specific_ptr ai; using namespace Goals; +#if AI_TRACE_LEVEL >= 1 +#define MAXPASS 1000000 +#else +#define MAXPASS 30 +#endif + Nullkiller::Nullkiller() { memory.reset(new AIMemory()); @@ -152,7 +158,7 @@ void Nullkiller::resetAiState() dangerHitMap->reset(); } -void Nullkiller::updateAiState() +void Nullkiller::updateAiState(int pass) { auto start = boost::chrono::high_resolution_clock::now(); @@ -174,7 +180,10 @@ void Nullkiller::updateAiState() activeHeroes[hero] = heroManager->getHeroRole(hero); } - pathfinder->updatePaths(activeHeroes, true); + PathfinderSettings cfg; + cfg.useHeroChain = true; + + pathfinder->updatePaths(activeHeroes, cfg); armyManager->update(); @@ -226,9 +235,9 @@ void Nullkiller::makeTurn() { resetAiState(); - while(true) + for(int i = 1; i <= MAXPASS; i++) { - updateAiState(); + updateAiState(i); Goals::TTaskVec bestTasks = { choseBestTask(sptr(BuyArmyBehavior())), diff --git a/AI/Nullkiller/Engine/Nullkiller.h b/AI/Nullkiller/Engine/Nullkiller.h index 407f0da1c..b690a01f3 100644 --- a/AI/Nullkiller/Engine/Nullkiller.h +++ b/AI/Nullkiller/Engine/Nullkiller.h @@ -68,7 +68,7 @@ public: private: void resetAiState(); - void updateAiState(); + void updateAiState(int pass); Goals::TTask choseBestTask(Goals::TSubgoal behavior) const; Goals::TTask choseBestTask(Goals::TTaskVec & tasks) const; }; diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index 93e795af9..24b9f1e75 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -84,6 +84,7 @@ void AINodeStorage::clear() heroChainPass = EHeroChainPass::INITIAL; heroChainTurn = 0; heroChainMaxTurns = 1; + scoutTurnDistanceLimit = 255; } const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const @@ -625,12 +626,12 @@ bool AINodeStorage::isDistanceLimitReached(const PathNodeInfo & source, CDestina if(heroChainPass == EHeroChainPass::FINAL) { - if(aiNode->actor->heroRole == HeroRole::SCOUT && destination.node->turns > 3) + if(aiNode->actor->heroRole == HeroRole::SCOUT && destination.node->turns > scoutTurnDistanceLimit) return true; } else if(heroChainPass == EHeroChainPass::INITIAL) { - if(aiNode->actor->heroRole == HeroRole::SCOUT && destination.node->turns > 5) + if(aiNode->actor->heroRole == HeroRole::SCOUT && destination.node->turns > scoutTurnDistanceLimit) return true; } diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.h b/AI/Nullkiller/Pathfinding/AINodeStorage.h index fe350617f..dd5ff2fde 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.h +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.h @@ -116,6 +116,7 @@ private: int heroChainTurn; int heroChainMaxTurns; PlayerColor playerID; + 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. @@ -174,6 +175,7 @@ public: std::vector getChainInfo(const int3 & pos, bool isOnLand) const; bool isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const; void setHeroes(std::map heroes); + void setScoutTurnDistanceLimit(uint8_t distanceLimit) { scoutTurnDistanceLimit = distanceLimit; } void setTownsAndDwellings( const std::vector & towns, const std::set & visitableObjs); diff --git a/AI/Nullkiller/Pathfinding/AIPathfinder.cpp b/AI/Nullkiller/Pathfinding/AIPathfinder.cpp index 2fb61c0fa..f0dbab692 100644 --- a/AI/Nullkiller/Pathfinding/AIPathfinder.cpp +++ b/AI/Nullkiller/Pathfinding/AIPathfinder.cpp @@ -42,7 +42,7 @@ std::vector AIPathfinder::getPathInfo(const int3 & tile) const return storage->getChainInfo(tile, !tileInfo->isWater()); } -void AIPathfinder::updatePaths(std::map heroes, bool useHeroChain) +void AIPathfinder::updatePaths(std::map heroes, PathfinderSettings pathfinderSettings) { if(!storage) { @@ -55,8 +55,9 @@ void AIPathfinder::updatePaths(std::map heroes storage->clear(); storage->setHeroes(heroes); + storage->setScoutTurnDistanceLimit(pathfinderSettings.scoutTurnDistanceLimit); - if(useHeroChain) + if(pathfinderSettings.useHeroChain) { storage->setTownsAndDwellings(cb->getTownsInfo(), ai->memory->visitableObjs); } @@ -66,7 +67,7 @@ void AIPathfinder::updatePaths(std::map heroes logAi->trace("Recalculate paths pass %d", pass++); cb->calculatePaths(config); - if(!useHeroChain) + if(!pathfinderSettings.useHeroChain) return; do diff --git a/AI/Nullkiller/Pathfinding/AIPathfinder.h b/AI/Nullkiller/Pathfinding/AIPathfinder.h index 1bc4edbbc..e637489f2 100644 --- a/AI/Nullkiller/Pathfinding/AIPathfinder.h +++ b/AI/Nullkiller/Pathfinding/AIPathfinder.h @@ -15,6 +15,17 @@ class Nullkiller; +struct PathfinderSettings +{ + bool useHeroChain; + uint8_t scoutTurnDistanceLimit; + + PathfinderSettings() + :useHeroChain(false), + scoutTurnDistanceLimit(255) + { } +}; + class AIPathfinder { private: @@ -26,6 +37,6 @@ public: AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai); std::vector getPathInfo(const int3 & tile) const; bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const; - void updatePaths(std::map heroes, bool useHeroChain = false); + void updatePaths(std::map heroes, PathfinderSettings pathfinderSettings); void init(); }; diff --git a/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp b/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp index c910ad551..45183d856 100644 --- a/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp +++ b/AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp @@ -135,6 +135,13 @@ namespace AIPathfinding return false; } + auto danger = nodeStorage->evaluateDanger(destination.coord, nodeStorage->getHero(destination.node), true); + + if(danger) + { + return bypassBattle(source, destination, pathfinderConfig, pathfinderHelper); + } + return true; }