1
0
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:
Andrii Danylchenko 2022-09-06 21:14:22 +03:00
parent a2dbb6c8ac
commit 2434fe3f6f
14 changed files with 264 additions and 150 deletions

View File

@ -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();
} }

View File

@ -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;
} }

View File

@ -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);

View File

@ -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;
} }
} }

View File

@ -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);
}; };

View File

@ -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;
}; };
} }

View File

@ -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>

View File

@ -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;
}

View File

@ -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;
}; };
} }

View File

@ -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);
}; };

View File

@ -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);

View File

@ -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);
}; };

View File

@ -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))

View File

@ -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)