1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-21 21:17:49 +02:00

Nullkiller: fix freeze on accessing guarded object

This commit is contained in:
Andrii Danylchenko 2021-05-16 14:56:21 +03:00 committed by Andrii Danylchenko
parent 5bfe71c8f3
commit 645c393e25
9 changed files with 45 additions and 14 deletions

View File

@ -44,7 +44,7 @@ void DangerHitMapAnalyzer::updateHitMap()
for(auto pair : heroes) for(auto pair : heroes)
{ {
ai->pathfinder->updatePaths(pair.second, false); ai->pathfinder->updatePaths(pair.second, PathfinderSettings());
foreach_tile_pos([&](const int3 & pos) foreach_tile_pos([&](const int3 & pos)
{ {

View File

@ -15,13 +15,13 @@ struct HitMapInfo
{ {
uint64_t danger; uint64_t danger;
uint8_t turn; uint8_t turn;
const CGHeroInstance * hero; HeroPtr hero;
void reset() void reset()
{ {
danger = 0; danger = 0;
turn = 255; turn = 255;
hero = nullptr; hero = HeroPtr();
} }
}; };

View File

@ -25,6 +25,12 @@ extern boost::thread_specific_ptr<VCAI> ai;
using namespace Goals; using namespace Goals;
#if AI_TRACE_LEVEL >= 1
#define MAXPASS 1000000
#else
#define MAXPASS 30
#endif
Nullkiller::Nullkiller() Nullkiller::Nullkiller()
{ {
memory.reset(new AIMemory()); memory.reset(new AIMemory());
@ -152,7 +158,7 @@ void Nullkiller::resetAiState()
dangerHitMap->reset(); dangerHitMap->reset();
} }
void Nullkiller::updateAiState() void Nullkiller::updateAiState(int pass)
{ {
auto start = boost::chrono::high_resolution_clock::now(); auto start = boost::chrono::high_resolution_clock::now();
@ -174,7 +180,10 @@ void Nullkiller::updateAiState()
activeHeroes[hero] = heroManager->getHeroRole(hero); activeHeroes[hero] = heroManager->getHeroRole(hero);
} }
pathfinder->updatePaths(activeHeroes, true); PathfinderSettings cfg;
cfg.useHeroChain = true;
pathfinder->updatePaths(activeHeroes, cfg);
armyManager->update(); armyManager->update();
@ -226,9 +235,9 @@ void Nullkiller::makeTurn()
{ {
resetAiState(); resetAiState();
while(true) for(int i = 1; i <= MAXPASS; i++)
{ {
updateAiState(); updateAiState(i);
Goals::TTaskVec bestTasks = { Goals::TTaskVec bestTasks = {
choseBestTask(sptr(BuyArmyBehavior())), choseBestTask(sptr(BuyArmyBehavior())),

View File

@ -68,7 +68,7 @@ public:
private: private:
void resetAiState(); void resetAiState();
void updateAiState(); void updateAiState(int pass);
Goals::TTask choseBestTask(Goals::TSubgoal behavior) const; Goals::TTask choseBestTask(Goals::TSubgoal behavior) const;
Goals::TTask choseBestTask(Goals::TTaskVec & tasks) const; Goals::TTask choseBestTask(Goals::TTaskVec & tasks) const;
}; };

View File

@ -84,6 +84,7 @@ void AINodeStorage::clear()
heroChainPass = EHeroChainPass::INITIAL; heroChainPass = EHeroChainPass::INITIAL;
heroChainTurn = 0; heroChainTurn = 0;
heroChainMaxTurns = 1; heroChainMaxTurns = 1;
scoutTurnDistanceLimit = 255;
} }
const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const
@ -625,12 +626,12 @@ bool AINodeStorage::isDistanceLimitReached(const PathNodeInfo & source, CDestina
if(heroChainPass == EHeroChainPass::FINAL) 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; return true;
} }
else if(heroChainPass == EHeroChainPass::INITIAL) 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; return true;
} }

View File

@ -116,6 +116,7 @@ private:
int heroChainTurn; int heroChainTurn;
int heroChainMaxTurns; int heroChainMaxTurns;
PlayerColor playerID; PlayerColor playerID;
uint8_t scoutTurnDistanceLimit;
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.
@ -174,6 +175,7 @@ public:
std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const; std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const;
bool isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const; bool isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const;
void setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes); void setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes);
void setScoutTurnDistanceLimit(uint8_t distanceLimit) { scoutTurnDistanceLimit = distanceLimit; }
void setTownsAndDwellings( void setTownsAndDwellings(
const std::vector<const CGTownInstance *> & towns, const std::vector<const CGTownInstance *> & towns,
const std::set<const CGObjectInstance *> & visitableObjs); const std::set<const CGObjectInstance *> & visitableObjs);

View File

@ -42,7 +42,7 @@ std::vector<AIPath> AIPathfinder::getPathInfo(const int3 & tile) const
return storage->getChainInfo(tile, !tileInfo->isWater()); return storage->getChainInfo(tile, !tileInfo->isWater());
} }
void AIPathfinder::updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes, bool useHeroChain) void AIPathfinder::updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes, PathfinderSettings pathfinderSettings)
{ {
if(!storage) if(!storage)
{ {
@ -55,8 +55,9 @@ void AIPathfinder::updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes
storage->clear(); storage->clear();
storage->setHeroes(heroes); storage->setHeroes(heroes);
storage->setScoutTurnDistanceLimit(pathfinderSettings.scoutTurnDistanceLimit);
if(useHeroChain) if(pathfinderSettings.useHeroChain)
{ {
storage->setTownsAndDwellings(cb->getTownsInfo(), ai->memory->visitableObjs); storage->setTownsAndDwellings(cb->getTownsInfo(), ai->memory->visitableObjs);
} }
@ -66,7 +67,7 @@ void AIPathfinder::updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes
logAi->trace("Recalculate paths pass %d", pass++); logAi->trace("Recalculate paths pass %d", pass++);
cb->calculatePaths(config); cb->calculatePaths(config);
if(!useHeroChain) if(!pathfinderSettings.useHeroChain)
return; return;
do do

View File

@ -15,6 +15,17 @@
class Nullkiller; class Nullkiller;
struct PathfinderSettings
{
bool useHeroChain;
uint8_t scoutTurnDistanceLimit;
PathfinderSettings()
:useHeroChain(false),
scoutTurnDistanceLimit(255)
{ }
};
class AIPathfinder class AIPathfinder
{ {
private: private:
@ -26,6 +37,6 @@ public:
AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai); AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai);
std::vector<AIPath> getPathInfo(const int3 & tile) const; std::vector<AIPath> getPathInfo(const int3 & tile) const;
bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const; bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const;
void updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes, bool useHeroChain = false); void updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes, PathfinderSettings pathfinderSettings);
void init(); void init();
}; };

View File

@ -135,6 +135,13 @@ namespace AIPathfinding
return false; return false;
} }
auto danger = nodeStorage->evaluateDanger(destination.coord, nodeStorage->getHero(destination.node), true);
if(danger)
{
return bypassBattle(source, destination, pathfinderConfig, pathfinderHelper);
}
return true; return true;
} }