mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
NK: fix AI speed
This commit is contained in:
parent
a2dbb6c8ac
commit
2434fe3f6f
@ -766,10 +766,10 @@ void AIGateway::makeTurn()
|
|||||||
logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn.");
|
logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/*catch (std::exception & e)
|
catch (std::exception & e)
|
||||||
{
|
{
|
||||||
logAi->debug("Making turn thread has caught an exception: %s", e.what());
|
logAi->debug("Making turn thread has caught an exception: %s", e.what());
|
||||||
}*/
|
}
|
||||||
|
|
||||||
endTurn();
|
endTurn();
|
||||||
}
|
}
|
||||||
|
@ -120,18 +120,23 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons
|
|||||||
|
|
||||||
auto blocker = blockers.front();
|
auto blocker = blockers.front();
|
||||||
|
|
||||||
|
if(isObjectPassable(ai, blocker))
|
||||||
|
continue;
|
||||||
|
|
||||||
if(blocker->ID == Obj::GARRISON
|
if(blocker->ID == Obj::GARRISON
|
||||||
|| blocker->ID == Obj::MONSTER
|
|| blocker->ID == Obj::GARRISON2)
|
||||||
|| blocker->ID == Obj::GARRISON2
|
|
||||||
|| blocker->ID == Obj::BORDERGUARD
|
|
||||||
|| blocker->ID == Obj::BORDER_GATE
|
|
||||||
|| blocker->ID == Obj::SHIPYARD)
|
|
||||||
{
|
{
|
||||||
if(!isObjectPassable(ai, blocker))
|
if(dynamic_cast<const CArmedInstance *>(blocker)->getArmyStrength() == 0)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
return blocker;
|
return blocker;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(blocker->ID == Obj::QUEST_GUARD && node->actionIsBlocked)
|
if(blocker->ID == Obj::MONSTER
|
||||||
|
|| blocker->ID == Obj::BORDERGUARD
|
||||||
|
|| blocker->ID == Obj::BORDER_GATE
|
||||||
|
|| blocker->ID == Obj::SHIPYARD
|
||||||
|
|| (blocker->ID == Obj::QUEST_GUARD && node->actionIsBlocked))
|
||||||
{
|
{
|
||||||
return blocker;
|
return blocker;
|
||||||
}
|
}
|
||||||
|
@ -47,14 +47,14 @@ Goals::TGoalVec DefenceBehavior::decompose() const
|
|||||||
|
|
||||||
void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const
|
void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const
|
||||||
{
|
{
|
||||||
logAi->debug("Evaluating defence for %s", town->name);
|
logAi->trace("Evaluating defence for %s", town->name);
|
||||||
|
|
||||||
auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
|
auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
|
||||||
auto treats = { treatNode.fastestDanger, treatNode.maximumDanger };
|
auto treats = { treatNode.fastestDanger, treatNode.maximumDanger };
|
||||||
|
|
||||||
if(!treatNode.fastestDanger.hero)
|
if(!treatNode.fastestDanger.hero)
|
||||||
{
|
{
|
||||||
logAi->debug("No treat found for town %s", town->name);
|
logAi->trace("No treat found for town %s", town->name);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logAi->debug(
|
logAi->trace(
|
||||||
"Hero %s in garrison of town %s is suposed to defend the town",
|
"Hero %s in garrison of town %s is suposed to defend the town",
|
||||||
town->garrisonHero->name,
|
town->garrisonHero->name,
|
||||||
town->name);
|
town->name);
|
||||||
@ -85,7 +85,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
|
|
||||||
if(reinforcement)
|
if(reinforcement)
|
||||||
{
|
{
|
||||||
logAi->debug("Town %s can buy defence army %lld", town->name, reinforcement);
|
logAi->trace("Town %s can buy defence army %lld", town->name, reinforcement);
|
||||||
tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f)));
|
tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
|
|
||||||
for(auto & treat : treats)
|
for(auto & treat : treats)
|
||||||
{
|
{
|
||||||
logAi->debug(
|
logAi->trace(
|
||||||
"Town %s has treat %lld in %s turns, hero: %s",
|
"Town %s has treat %lld in %s turns, hero: %s",
|
||||||
town->name,
|
town->name,
|
||||||
treat.danger,
|
treat.danger,
|
||||||
@ -104,6 +104,12 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
|
|
||||||
for(AIPath & path : paths)
|
for(AIPath & path : paths)
|
||||||
{
|
{
|
||||||
|
if(town->visitingHero && path.targetHero != town->visitingHero.get())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(town->visitingHero && path.getHeroStrength() < town->visitingHero->getHeroStrength())
|
||||||
|
continue;
|
||||||
|
|
||||||
if(path.getHeroStrength() > treat.danger)
|
if(path.getHeroStrength() > treat.danger)
|
||||||
{
|
{
|
||||||
if((path.turn() <= treat.turn && dayOfWeek + treat.turn < 6 && isSafeToVisit(path.targetHero, path.heroArmy, treat.danger))
|
if((path.turn() <= treat.turn && dayOfWeek + treat.turn < 6 && isSafeToVisit(path.targetHero, path.heroArmy, treat.danger))
|
||||||
@ -111,11 +117,13 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
|| path.turn() < treat.turn - 1
|
|| path.turn() < treat.turn - 1
|
||||||
|| (path.turn() < treat.turn && treat.turn >= 2))
|
|| (path.turn() < treat.turn && treat.turn >= 2))
|
||||||
{
|
{
|
||||||
logAi->debug(
|
#if AI_TRACE_LEVEL >= 1
|
||||||
|
logAi->trace(
|
||||||
"Hero %s can eliminate danger for town %s using path %s.",
|
"Hero %s can eliminate danger for town %s using path %s.",
|
||||||
path.targetHero->name,
|
path.targetHero->name,
|
||||||
town->name,
|
town->name,
|
||||||
path.toString());
|
path.toString());
|
||||||
|
#endif
|
||||||
|
|
||||||
treatIsUnderControl = true;
|
treatIsUnderControl = true;
|
||||||
break;
|
break;
|
||||||
@ -140,7 +148,9 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
|
|
||||||
if(cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES)
|
if(cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES)
|
||||||
{
|
{
|
||||||
logAi->debug("Hero %s can be recruited to defend %s", hero->name, town->name);
|
#if AI_TRACE_LEVEL >= 1
|
||||||
|
logAi->trace("Hero %s can be recruited to defend %s", hero->name, town->name);
|
||||||
|
#endif
|
||||||
tasks.push_back(Goals::sptr(Goals::RecruitHero(town, hero).setpriority(1)));
|
tasks.push_back(Goals::sptr(Goals::RecruitHero(town, hero).setpriority(1)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -174,7 +184,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
|
|
||||||
if(paths.empty())
|
if(paths.empty())
|
||||||
{
|
{
|
||||||
logAi->debug("No ways to defend town %s", town->name);
|
logAi->trace("No ways to defend town %s", town->name);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -197,9 +207,11 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
#endif
|
#endif
|
||||||
if(path.turn() <= treat.turn - 2)
|
if(path.turn() <= treat.turn - 2)
|
||||||
{
|
{
|
||||||
|
#if AI_TRACE_LEVEL >= 1
|
||||||
logAi->trace("Deffer defence of %s by %s because he has enough time to rich the town next trun",
|
logAi->trace("Deffer defence of %s by %s because he has enough time to rich the town next trun",
|
||||||
town->name,
|
town->name,
|
||||||
path.targetHero->name);
|
path.targetHero->name);
|
||||||
|
#endif
|
||||||
|
|
||||||
defferedPaths[path.targetHero].push_back(i);
|
defferedPaths[path.targetHero].push_back(i);
|
||||||
|
|
||||||
|
@ -116,9 +116,10 @@ void Nullkiller::resetAiState()
|
|||||||
playerID = ai->playerID;
|
playerID = ai->playerID;
|
||||||
lockedHeroes.clear();
|
lockedHeroes.clear();
|
||||||
dangerHitMap->reset();
|
dangerHitMap->reset();
|
||||||
|
useHeroChain = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nullkiller::updateAiState(int pass)
|
void Nullkiller::updateAiState(int pass, bool fast)
|
||||||
{
|
{
|
||||||
boost::this_thread::interruption_point();
|
boost::this_thread::interruption_point();
|
||||||
|
|
||||||
@ -126,39 +127,42 @@ void Nullkiller::updateAiState(int pass)
|
|||||||
|
|
||||||
activeHero = nullptr;
|
activeHero = nullptr;
|
||||||
|
|
||||||
memory->removeInvisibleObjects(cb.get());
|
if(!fast)
|
||||||
|
|
||||||
dangerHitMap->updateHitMap();
|
|
||||||
|
|
||||||
boost::this_thread::interruption_point();
|
|
||||||
|
|
||||||
heroManager->update();
|
|
||||||
logAi->trace("Updating paths");
|
|
||||||
|
|
||||||
std::map<const CGHeroInstance *, HeroRole> activeHeroes;
|
|
||||||
|
|
||||||
for(auto hero : cb->getHeroesInfo())
|
|
||||||
{
|
{
|
||||||
if(getHeroLockedReason(hero) == HeroLockedReason::DEFENCE)
|
memory->removeInvisibleObjects(cb.get());
|
||||||
continue;
|
|
||||||
|
|
||||||
activeHeroes[hero] = heroManager->getHeroRole(hero);
|
dangerHitMap->updateHitMap();
|
||||||
|
|
||||||
|
boost::this_thread::interruption_point();
|
||||||
|
|
||||||
|
heroManager->update();
|
||||||
|
logAi->trace("Updating paths");
|
||||||
|
|
||||||
|
std::map<const CGHeroInstance *, HeroRole> activeHeroes;
|
||||||
|
|
||||||
|
for(auto hero : cb->getHeroesInfo())
|
||||||
|
{
|
||||||
|
if(getHeroLockedReason(hero) == HeroLockedReason::DEFENCE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
activeHeroes[hero] = heroManager->getHeroRole(hero);
|
||||||
|
}
|
||||||
|
|
||||||
|
PathfinderSettings cfg;
|
||||||
|
cfg.useHeroChain = useHeroChain;
|
||||||
|
cfg.scoutTurnDistanceLimit = SCOUT_TURN_DISTANCE_LIMIT;
|
||||||
|
|
||||||
|
if(scanDepth != ScanDepth::FULL)
|
||||||
|
{
|
||||||
|
cfg.mainTurnDistanceLimit = MAIN_TURN_DISTANCE_LIMIT * ((int)scanDepth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pathfinder->updatePaths(activeHeroes, cfg);
|
||||||
|
|
||||||
|
objectClusterizer->clusterize();
|
||||||
}
|
}
|
||||||
|
|
||||||
PathfinderSettings cfg;
|
|
||||||
cfg.useHeroChain = true;
|
|
||||||
cfg.scoutTurnDistanceLimit = SCOUT_TURN_DISTANCE_LIMIT;
|
|
||||||
|
|
||||||
if(scanDepth != ScanDepth::FULL)
|
|
||||||
{
|
|
||||||
cfg.mainTurnDistanceLimit = MAIN_TURN_DISTANCE_LIMIT * ((int)scanDepth + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pathfinder->updatePaths(activeHeroes, cfg);
|
|
||||||
|
|
||||||
armyManager->update();
|
armyManager->update();
|
||||||
|
|
||||||
objectClusterizer->clusterize();
|
|
||||||
buildAnalyzer->update();
|
buildAnalyzer->update();
|
||||||
decomposer->reset();
|
decomposer->reset();
|
||||||
|
|
||||||
@ -213,13 +217,30 @@ void Nullkiller::makeTurn()
|
|||||||
{
|
{
|
||||||
updateAiState(i);
|
updateAiState(i);
|
||||||
|
|
||||||
|
Goals::TTask bestTask = taskptr(Goals::Invalid());
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Goals::TTaskVec fastTasks = {
|
||||||
|
choseBestTask(sptr(BuyArmyBehavior()), 1),
|
||||||
|
choseBestTask(sptr(RecruitHeroBehavior()), 1),
|
||||||
|
choseBestTask(sptr(BuildingBehavior()), 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
bestTask = choseBestTask(fastTasks);
|
||||||
|
|
||||||
|
if(bestTask->priority >= 1)
|
||||||
|
{
|
||||||
|
executeTask(bestTask);
|
||||||
|
updateAiState(i, true);
|
||||||
|
}
|
||||||
|
} while(bestTask->priority >= 1);
|
||||||
|
|
||||||
Goals::TTaskVec bestTasks = {
|
Goals::TTaskVec bestTasks = {
|
||||||
choseBestTask(sptr(BuyArmyBehavior()), 1),
|
bestTask,
|
||||||
choseBestTask(sptr(CaptureObjectsBehavior()), 1),
|
choseBestTask(sptr(CaptureObjectsBehavior()), 1),
|
||||||
choseBestTask(sptr(ClusterBehavior()), MAX_DEPTH),
|
choseBestTask(sptr(ClusterBehavior()), MAX_DEPTH),
|
||||||
choseBestTask(sptr(RecruitHeroBehavior()), 1),
|
|
||||||
choseBestTask(sptr(DefenceBehavior()), MAX_DEPTH),
|
choseBestTask(sptr(DefenceBehavior()), MAX_DEPTH),
|
||||||
choseBestTask(sptr(BuildingBehavior()), 1),
|
|
||||||
choseBestTask(sptr(GatherArmyBehavior()), MAX_DEPTH)
|
choseBestTask(sptr(GatherArmyBehavior()), MAX_DEPTH)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -228,19 +249,25 @@ void Nullkiller::makeTurn()
|
|||||||
bestTasks.push_back(choseBestTask(sptr(StartupBehavior()), 1));
|
bestTasks.push_back(choseBestTask(sptr(StartupBehavior()), 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TTask bestTask = choseBestTask(bestTasks);
|
bestTask = choseBestTask(bestTasks);
|
||||||
|
|
||||||
HeroPtr hero = bestTask->getHero();
|
HeroPtr hero = bestTask->getHero();
|
||||||
|
|
||||||
|
HeroRole heroRole = HeroRole::MAIN;
|
||||||
|
|
||||||
|
if(hero.validAndSet())
|
||||||
|
heroRole = heroManager->getHeroRole(hero);
|
||||||
|
|
||||||
|
if(heroRole != HeroRole::MAIN || bestTask->getHeroExchangeCount() <= 1)
|
||||||
|
useHeroChain = false;
|
||||||
|
|
||||||
if(bestTask->priority < NEXT_SCAN_MIN_PRIORITY
|
if(bestTask->priority < NEXT_SCAN_MIN_PRIORITY
|
||||||
&& scanDepth != ScanDepth::FULL)
|
&& scanDepth != ScanDepth::FULL)
|
||||||
{
|
{
|
||||||
HeroRole heroRole = HeroRole::MAIN;
|
|
||||||
|
|
||||||
if(hero.validAndSet())
|
|
||||||
heroRole = heroManager->getHeroRole(hero);
|
|
||||||
|
|
||||||
if(heroRole == HeroRole::MAIN || bestTask->priority < MIN_PRIORITY)
|
if(heroRole == HeroRole::MAIN || bestTask->priority < MIN_PRIORITY)
|
||||||
{
|
{
|
||||||
|
useHeroChain = false;
|
||||||
|
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
"Goal %s has too low priority %f so increasing scan depth",
|
"Goal %s has too low priority %f so increasing scan depth",
|
||||||
bestTask->toString(),
|
bestTask->toString(),
|
||||||
@ -258,26 +285,31 @@ void Nullkiller::makeTurn()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string taskDescr = bestTask->toString();
|
executeTask(bestTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boost::this_thread::interruption_point();
|
void Nullkiller::executeTask(Goals::TTask task)
|
||||||
logAi->debug("Trying to realize %s (value %2.3f)", taskDescr, bestTask->priority);
|
{
|
||||||
|
std::string taskDescr = task->toString();
|
||||||
|
|
||||||
try
|
boost::this_thread::interruption_point();
|
||||||
{
|
logAi->debug("Trying to realize %s (value %2.3f)", taskDescr, task->priority);
|
||||||
bestTask->accept(ai.get());
|
|
||||||
}
|
|
||||||
catch(goalFulfilledException &)
|
|
||||||
{
|
|
||||||
logAi->trace("Task %s completed", bestTask->toString());
|
|
||||||
}
|
|
||||||
catch(std::exception & e)
|
|
||||||
{
|
|
||||||
logAi->debug("Failed to realize subgoal of type %s, I will stop.", taskDescr);
|
|
||||||
logAi->debug("The error message was: %s", e.what());
|
|
||||||
|
|
||||||
return;
|
try
|
||||||
}
|
{
|
||||||
|
task->accept(ai.get());
|
||||||
|
}
|
||||||
|
catch(goalFulfilledException &)
|
||||||
|
{
|
||||||
|
logAi->trace("Task %s completed", task->toString());
|
||||||
|
}
|
||||||
|
catch(std::exception & e)
|
||||||
|
{
|
||||||
|
logAi->debug("Failed to realize subgoal of type %s, I will stop.", taskDescr);
|
||||||
|
logAi->debug("The error message was: %s", e.what());
|
||||||
|
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ private:
|
|||||||
std::map<const CGHeroInstance *, HeroLockedReason> lockedHeroes;
|
std::map<const CGHeroInstance *, HeroLockedReason> lockedHeroes;
|
||||||
ScanDepth scanDepth;
|
ScanDepth scanDepth;
|
||||||
TResources lockedResources;
|
TResources lockedResources;
|
||||||
|
bool useHeroChain;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::unique_ptr<DangerHitMapAnalyzer> dangerHitMap;
|
std::unique_ptr<DangerHitMapAnalyzer> dangerHitMap;
|
||||||
@ -86,7 +87,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void resetAiState();
|
void resetAiState();
|
||||||
void updateAiState(int pass);
|
void updateAiState(int pass, bool fast = false);
|
||||||
Goals::TTask choseBestTask(Goals::TSubgoal behavior, int decompositionMaxDepth) const;
|
Goals::TTask choseBestTask(Goals::TSubgoal behavior, int decompositionMaxDepth) const;
|
||||||
Goals::TTask choseBestTask(Goals::TTaskVec & tasks) const;
|
Goals::TTask choseBestTask(Goals::TTaskVec & tasks) const;
|
||||||
|
void executeTask(Goals::TTask task);
|
||||||
};
|
};
|
||||||
|
@ -162,6 +162,7 @@ namespace Goals
|
|||||||
virtual std::string toString() const = 0;
|
virtual std::string toString() const = 0;
|
||||||
virtual HeroPtr getHero() const = 0;
|
virtual HeroPtr getHero() const = 0;
|
||||||
virtual ~ITask() {}
|
virtual ~ITask() {}
|
||||||
|
virtual int getHeroExchangeCount() const = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +90,8 @@ namespace Goals
|
|||||||
virtual bool isElementar() const override { return true; }
|
virtual bool isElementar() const override { return true; }
|
||||||
|
|
||||||
virtual HeroPtr getHero() const override { return AbstractGoal::hero; }
|
virtual HeroPtr getHero() const override { return AbstractGoal::hero; }
|
||||||
|
|
||||||
|
virtual int getHeroExchangeCount() const override { return 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_EXPORT Invalid : public ElementarGoal<Invalid>
|
class DLL_EXPORT Invalid : public ElementarGoal<Invalid>
|
||||||
|
@ -73,3 +73,8 @@ bool Composition::isElementar() const
|
|||||||
{
|
{
|
||||||
return subtasks.back()->isElementar();
|
return subtasks.back()->isElementar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Composition::getHeroExchangeCount() const
|
||||||
|
{
|
||||||
|
return isElementar() ? taskptr(*subtasks.back())->getHeroExchangeCount() : 0;
|
||||||
|
}
|
@ -36,5 +36,6 @@ namespace Goals
|
|||||||
Composition & addNext(TSubgoal goal);
|
Composition & addNext(TSubgoal goal);
|
||||||
virtual TGoalVec decompose() const override;
|
virtual TGoalVec decompose() const override;
|
||||||
virtual bool isElementar() const override;
|
virtual bool isElementar() const override;
|
||||||
|
virtual int getHeroExchangeCount() const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ namespace Goals
|
|||||||
virtual bool operator==(const ExecuteHeroChain & other) const override;
|
virtual bool operator==(const ExecuteHeroChain & other) const override;
|
||||||
const AIPath & getPath() const { return chainPath; }
|
const AIPath & getPath() const { return chainPath; }
|
||||||
|
|
||||||
|
virtual int getHeroExchangeCount() const override { return chainPath.exchangeCount; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool moveHeroToTile(const CGHeroInstance * hero, const int3 & tile);
|
bool moveHeroToTile(const CGHeroInstance * hero, const int3 & tile);
|
||||||
};
|
};
|
||||||
|
@ -23,23 +23,17 @@ std::shared_ptr<boost::multi_array<AIPathNode, 5>> AISharedStorage::shared;
|
|||||||
std::set<int3> commitedTiles;
|
std::set<int3> commitedTiles;
|
||||||
std::set<int3> commitedTilesInitial;
|
std::set<int3> commitedTilesInitial;
|
||||||
|
|
||||||
#ifdef ENVIRONMENT64
|
|
||||||
const int BUCKET_COUNT = 11;
|
|
||||||
#else
|
|
||||||
const int BUCKET_COUNT = 7;
|
|
||||||
#endif // ENVIRONMENT64
|
|
||||||
|
|
||||||
const uint64_t FirstActorMask = 1;
|
const uint64_t FirstActorMask = 1;
|
||||||
const int BUCKET_SIZE = GameConstants::MAX_HEROES_PER_PLAYER;
|
|
||||||
const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE;
|
|
||||||
const uint64_t MIN_ARMY_STRENGTH_FOR_CHAIN = 5000;
|
const uint64_t MIN_ARMY_STRENGTH_FOR_CHAIN = 5000;
|
||||||
const uint64_t MIN_ARMY_STRENGTH_FOR_NEXT_ACTOR = 1000;
|
const uint64_t MIN_ARMY_STRENGTH_FOR_NEXT_ACTOR = 1000;
|
||||||
|
const uint64_t CHAIN_MAX_DEPTH = 4;
|
||||||
|
|
||||||
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[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][NUM_CHAINS]));
|
boost::extents[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][AIPathfinding::NUM_CHAINS]));
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes = shared;
|
nodes = shared;
|
||||||
@ -139,16 +133,16 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
|
|||||||
const EPathfindingLayer layer,
|
const EPathfindingLayer layer,
|
||||||
const ChainActor * actor)
|
const ChainActor * actor)
|
||||||
{
|
{
|
||||||
int bucketIndex = ((uintptr_t)actor) % BUCKET_COUNT;
|
int bucketIndex = ((uintptr_t)actor) % AIPathfinding::BUCKET_COUNT;
|
||||||
int bucketOffset = bucketIndex * BUCKET_SIZE;
|
int bucketOffset = bucketIndex * AIPathfinding::BUCKET_SIZE;
|
||||||
auto chains = nodes.get(pos, layer); //FIXME: chain was the innermost layer
|
auto chains = nodes.get(pos, layer);
|
||||||
|
|
||||||
if(chains[0].blocked())
|
if(chains[0].blocked())
|
||||||
{
|
{
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto i = BUCKET_SIZE - 1; i >= 0; i--)
|
for(auto i = AIPathfinding::BUCKET_SIZE - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
AIPathNode & node = chains[i + bucketOffset];
|
AIPathNode & node = chains[i + bucketOffset];
|
||||||
|
|
||||||
@ -171,8 +165,9 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
|
|||||||
std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
|
std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
|
||||||
{
|
{
|
||||||
if(heroChainPass)
|
if(heroChainPass)
|
||||||
{
|
{
|
||||||
calculateTownPortalTeleportations(heroChain);
|
if(heroChainTurn == 0)
|
||||||
|
calculateTownPortalTeleportations(heroChain);
|
||||||
|
|
||||||
return heroChain;
|
return heroChain;
|
||||||
}
|
}
|
||||||
@ -207,7 +202,8 @@ std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateTownPortalTeleportations(initialNodes);
|
if(heroChainTurn == 0)
|
||||||
|
calculateTownPortalTeleportations(initialNodes);
|
||||||
|
|
||||||
return initialNodes;
|
return initialNodes;
|
||||||
}
|
}
|
||||||
@ -406,8 +402,8 @@ public:
|
|||||||
AINodeStorage & storage, AISharedStorage & nodes, const std::vector<int3> & tiles, uint64_t chainMask, int heroChainTurn)
|
AINodeStorage & storage, AISharedStorage & nodes, const std::vector<int3> & tiles, uint64_t chainMask, int heroChainTurn)
|
||||||
:existingChains(), newChains(), delayedWork(), nodes(nodes), storage(storage), chainMask(chainMask), heroChainTurn(heroChainTurn), heroChain(), tiles(tiles)
|
:existingChains(), newChains(), delayedWork(), nodes(nodes), storage(storage), chainMask(chainMask), heroChainTurn(heroChainTurn), heroChain(), tiles(tiles)
|
||||||
{
|
{
|
||||||
existingChains.reserve(NUM_CHAINS);
|
existingChains.reserve(AIPathfinding::NUM_CHAINS);
|
||||||
newChains.reserve(NUM_CHAINS);
|
newChains.reserve(AIPathfinding::NUM_CHAINS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(const blocked_range<size_t>& r)
|
void execute(const blocked_range<size_t>& r)
|
||||||
@ -623,6 +619,9 @@ void HeroChainCalculationTask::calculateHeroChain(
|
|||||||
if((node->actor->chainMask & chainMask) == 0 && (srcNode->actor->chainMask & chainMask) == 0)
|
if((node->actor->chainMask & chainMask) == 0 && (srcNode->actor->chainMask & chainMask) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if(node->actor->actorExchangeCount + srcNode->actor->actorExchangeCount > CHAIN_MAX_DEPTH)
|
||||||
|
continue;
|
||||||
|
|
||||||
if(node->action == CGPathNode::ENodeAction::BATTLE
|
if(node->action == CGPathNode::ENodeAction::BATTLE
|
||||||
|| node->action == CGPathNode::ENodeAction::TELEPORT_BATTLE
|
|| node->action == CGPathNode::ENodeAction::TELEPORT_BATTLE
|
||||||
|| node->action == CGPathNode::ENodeAction::TELEPORT_NORMAL
|
|| node->action == CGPathNode::ENodeAction::TELEPORT_NORMAL
|
||||||
@ -994,8 +993,6 @@ struct TowmPortalFinder
|
|||||||
|
|
||||||
CGPathNode * getBestInitialNodeForTownPortal(const CGTownInstance * targetTown)
|
CGPathNode * getBestInitialNodeForTownPortal(const CGTownInstance * targetTown)
|
||||||
{
|
{
|
||||||
CGPathNode * bestNode = nullptr;
|
|
||||||
|
|
||||||
for(CGPathNode * node : initialNodes)
|
for(CGPathNode * node : initialNodes)
|
||||||
{
|
{
|
||||||
auto aiNode = nodeStorage->getAINode(node);
|
auto aiNode = nodeStorage->getAINode(node);
|
||||||
@ -1018,11 +1015,10 @@ struct TowmPortalFinder
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!bestNode || bestNode->getCost() > node->getCost())
|
return node;
|
||||||
bestNode = node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bestNode;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<AIPathNode *> createTownPortalNode(const CGTownInstance * targetTown)
|
boost::optional<AIPathNode *> createTownPortalNode(const CGTownInstance * targetTown)
|
||||||
@ -1060,6 +1056,55 @@ struct TowmPortalFinder
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class TVector>
|
||||||
|
void AINodeStorage::calculateTownPortal(
|
||||||
|
const ChainActor * actor,
|
||||||
|
const std::map<const CGHeroInstance *, int> & maskMap,
|
||||||
|
const std::vector<CGPathNode *> & initialNodes,
|
||||||
|
TVector & output)
|
||||||
|
{
|
||||||
|
auto towns = cb->getTownsInfo(false);
|
||||||
|
|
||||||
|
vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
|
||||||
|
{
|
||||||
|
return cb->getPlayerRelations(actor->hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!towns.size())
|
||||||
|
{
|
||||||
|
return; // no towns no need to run loop further
|
||||||
|
}
|
||||||
|
|
||||||
|
TowmPortalFinder townPortalFinder(actor, initialNodes, towns, this);
|
||||||
|
|
||||||
|
if(townPortalFinder.actorCanCastTownPortal())
|
||||||
|
{
|
||||||
|
for(const CGTownInstance * targetTown : towns)
|
||||||
|
{
|
||||||
|
// TODO: allow to hide visiting hero in garrison
|
||||||
|
if(targetTown->visitingHero)
|
||||||
|
{
|
||||||
|
auto basicMask = maskMap.at(targetTown->visitingHero.get());
|
||||||
|
bool heroIsInChain = (actor->chainMask & basicMask) != 0;
|
||||||
|
bool sameActorInTown = actor->chainMask == basicMask;
|
||||||
|
|
||||||
|
if(sameActorInTown || !heroIsInChain)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto nodeOptional = townPortalFinder.createTownPortalNode(targetTown);
|
||||||
|
|
||||||
|
if(nodeOptional)
|
||||||
|
{
|
||||||
|
#if PATHFINDER_TRACE_LEVEL >= 1
|
||||||
|
logAi->trace("Adding town portal node at %s", targetTown->name);
|
||||||
|
#endif
|
||||||
|
output.push_back(nodeOptional.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *> & initialNodes)
|
void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *> & initialNodes)
|
||||||
{
|
{
|
||||||
std::set<const ChainActor *> actorsOfInitial;
|
std::set<const ChainActor *> actorsOfInitial;
|
||||||
@ -1068,7 +1113,8 @@ void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *>
|
|||||||
{
|
{
|
||||||
auto aiNode = getAINode(node);
|
auto aiNode = getAINode(node);
|
||||||
|
|
||||||
actorsOfInitial.insert(aiNode->actor->baseActor);
|
if(aiNode->actor->hero)
|
||||||
|
actorsOfInitial.insert(aiNode->actor->baseActor);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<const CGHeroInstance *, int> maskMap;
|
std::map<const CGHeroInstance *, int> maskMap;
|
||||||
@ -1079,50 +1125,28 @@ void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *>
|
|||||||
maskMap[basicActor->hero] = basicActor->chainMask;
|
maskMap[basicActor->hero] = basicActor->chainMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const ChainActor * actor : actorsOfInitial)
|
boost::sort(initialNodes, NodeComparer<CGPathNode>());
|
||||||
|
|
||||||
|
std::vector<const ChainActor *> actorsVector(actorsOfInitial.begin(), actorsOfInitial.end());
|
||||||
|
tbb::concurrent_vector<CGPathNode *> output;
|
||||||
|
|
||||||
|
if(actorsVector.size() * initialNodes.size() > 1000)
|
||||||
{
|
{
|
||||||
if(!actor->hero)
|
parallel_for(blocked_range<size_t>(0, actorsVector.size()), [&](const blocked_range<size_t> & r)
|
||||||
continue;
|
|
||||||
|
|
||||||
auto towns = cb->getTownsInfo(false);
|
|
||||||
|
|
||||||
vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
|
|
||||||
{
|
|
||||||
return cb->getPlayerRelations(actor->hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES;
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!towns.size())
|
|
||||||
{
|
|
||||||
return; // no towns no need to run loop further
|
|
||||||
}
|
|
||||||
|
|
||||||
TowmPortalFinder townPortalFinder(actor, initialNodes, towns, this);
|
|
||||||
|
|
||||||
if(townPortalFinder.actorCanCastTownPortal())
|
|
||||||
{
|
|
||||||
for(const CGTownInstance * targetTown : towns)
|
|
||||||
{
|
{
|
||||||
// TODO: allow to hide visiting hero in garrison
|
for(int i = r.begin(); i != r.end(); i++)
|
||||||
if(targetTown->visitingHero)
|
|
||||||
{
|
{
|
||||||
auto basicMask = maskMap[targetTown->visitingHero.get()];
|
calculateTownPortal(actorsVector[i], maskMap, initialNodes, output);
|
||||||
bool heroIsInChain = (actor->chainMask & basicMask) != 0;
|
|
||||||
bool sameActorInTown = actor->chainMask == basicMask;
|
|
||||||
|
|
||||||
if(sameActorInTown || !heroIsInChain)
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
auto nodeOptional = townPortalFinder.createTownPortalNode(targetTown);
|
std::copy(output.begin(), output.end(), std::back_inserter(initialNodes));
|
||||||
|
}
|
||||||
if(nodeOptional)
|
else
|
||||||
{
|
{
|
||||||
#if PATHFINDER_TRACE_LEVEL >= 1
|
for(auto actor : actorsVector)
|
||||||
logAi->trace("Adding town portal node at %s", targetTown->name);
|
{
|
||||||
#endif
|
calculateTownPortal(actor, maskMap, initialNodes, initialNodes);
|
||||||
initialNodes.push_back(nodeOptional.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1206,7 +1230,7 @@ bool AINodeStorage::hasBetterChain(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if AI_TRACE_LEVEL >= 2
|
#if PATHFINDER_TRACE_LEVEL >= 2
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
"Block ineficient move because of stronger hero %s->%s, hero: %s[%X], army %lld, mp diff: %i",
|
"Block ineficient move because of stronger hero %s->%s, hero: %s[%X], army %lld, mp diff: %i",
|
||||||
source->coord.toString(),
|
source->coord.toString(),
|
||||||
@ -1244,7 +1268,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
|
|||||||
{
|
{
|
||||||
std::vector<AIPath> paths;
|
std::vector<AIPath> paths;
|
||||||
|
|
||||||
paths.reserve(NUM_CHAINS / 4);
|
paths.reserve(AIPathfinding::NUM_CHAINS / 4);
|
||||||
|
|
||||||
auto chains = nodes.get(pos, isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL);
|
auto chains = nodes.get(pos, isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL);
|
||||||
|
|
||||||
|
@ -25,10 +25,16 @@
|
|||||||
|
|
||||||
namespace AIPathfinding
|
namespace AIPathfinding
|
||||||
{
|
{
|
||||||
const int BUCKET_COUNT = 11;
|
#ifdef ENVIRONMENT64
|
||||||
const int BUCKET_SIZE = GameConstants::MAX_HEROES_PER_PLAYER;
|
const int BUCKET_COUNT = 7;
|
||||||
|
#else
|
||||||
|
const int BUCKET_COUNT = 5;
|
||||||
|
#endif // ENVIRONMENT64
|
||||||
|
|
||||||
|
const int BUCKET_SIZE = 5;
|
||||||
const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE;
|
const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE;
|
||||||
const int THREAD_COUNT = 8;
|
const int THREAD_COUNT = 8;
|
||||||
|
const int CHAIN_MAX_DEPTH = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AIPathNode : public CGPathNode
|
struct AIPathNode : public CGPathNode
|
||||||
@ -239,7 +245,14 @@ public:
|
|||||||
return ((uintptr_t)actor * 395) % AIPathfinding::BUCKET_COUNT;
|
return ((uintptr_t)actor * 395) % AIPathfinding::BUCKET_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void calculateTownPortalTeleportations(std::vector<CGPathNode *> & neighbours);
|
void calculateTownPortalTeleportations(std::vector<CGPathNode *> & neighbours);
|
||||||
void fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const;
|
void fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class TVector>
|
||||||
|
void calculateTownPortal(
|
||||||
|
const ChainActor * actor,
|
||||||
|
const std::map<const CGHeroInstance *, int> & maskMap,
|
||||||
|
const std::vector<CGPathNode *> & initialNodes,
|
||||||
|
TVector & output);
|
||||||
};
|
};
|
||||||
|
@ -275,6 +275,9 @@ ExchangeResult HeroExchangeMap::tryExchangeNoLock(const ChainActor * other)
|
|||||||
|
|
||||||
if(!differentMasks) return result;
|
if(!differentMasks) return result;
|
||||||
|
|
||||||
|
if(actor->allowSpellCast || other->allowSpellCast)
|
||||||
|
return result;
|
||||||
|
|
||||||
TResources resources = ai->cb->getResourceAmount();
|
TResources resources = ai->cb->getResourceAmount();
|
||||||
|
|
||||||
if(!resources.canAfford(actor->armyCost + other->armyCost))
|
if(!resources.canAfford(actor->armyCost + other->armyCost))
|
||||||
|
@ -315,11 +315,28 @@ void CVCMIServer::threadHandleClient(std::shared_ptr<CConnection> c)
|
|||||||
setThreadName("CVCMIServer::handleConnection");
|
setThreadName("CVCMIServer::handleConnection");
|
||||||
c->enterLobbyConnectionMode();
|
c->enterLobbyConnectionMode();
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
#endif
|
||||||
while(c->connected)
|
while(c->connected)
|
||||||
{
|
{
|
||||||
CPack * pack = c->retrievePack();
|
CPack * pack;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
pack = c->retrievePack();
|
||||||
|
}
|
||||||
|
catch(boost::system::system_error & e)
|
||||||
|
{
|
||||||
|
logNetwork->error("Network error receiving a pack. Connection %s dies. What happened: %s", c->toString(), e.what());
|
||||||
|
|
||||||
|
if(state != EServerState::LOBBY)
|
||||||
|
gh->handleClientDisconnection(c);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if(auto lobbyPack = dynamic_ptr_cast<CPackForLobby>(pack))
|
if(auto lobbyPack = dynamic_ptr_cast<CPackForLobby>(pack))
|
||||||
{
|
{
|
||||||
handleReceivedPack(std::unique_ptr<CPackForLobby>(lobbyPack));
|
handleReceivedPack(std::unique_ptr<CPackForLobby>(lobbyPack));
|
||||||
@ -329,14 +346,8 @@ void CVCMIServer::threadHandleClient(std::shared_ptr<CConnection> c)
|
|||||||
gh->handleReceivedPack(serverPack);
|
gh->handleReceivedPack(serverPack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
#ifndef _MSC_VER
|
||||||
catch(boost::system::system_error & e)
|
}
|
||||||
{
|
|
||||||
(void)e;
|
|
||||||
if(state != EServerState::LOBBY)
|
|
||||||
gh->handleClientDisconnection(c);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
catch(const std::exception & e)
|
catch(const std::exception & e)
|
||||||
{
|
{
|
||||||
(void)e;
|
(void)e;
|
||||||
@ -348,7 +359,8 @@ void CVCMIServer::threadHandleClient(std::shared_ptr<CConnection> c)
|
|||||||
state = EServerState::SHUTDOWN;
|
state = EServerState::SHUTDOWN;
|
||||||
handleException();
|
handleException();
|
||||||
throw;
|
throw;
|
||||||
}*/
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
boost::unique_lock<boost::recursive_mutex> queueLock(mx);
|
boost::unique_lock<boost::recursive_mutex> queueLock(mx);
|
||||||
// if(state != ENDING_AND_STARTING_GAME)
|
// if(state != ENDING_AND_STARTING_GAME)
|
||||||
|
Loading…
Reference in New Issue
Block a user