1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-26 03:52:01 +02:00

Nullkiller: async hero chain calculation

This commit is contained in:
Andrii Danylchenko 2021-05-16 15:08:49 +03:00 committed by Andrii Danylchenko
parent 0265de77fa
commit 66843b22d3
3 changed files with 149 additions and 68 deletions

View File

@ -401,11 +401,12 @@ private:
int heroChainTurn;
std::vector<CGPathNode *> heroChain;
const std::vector<int3> & tiles;
std::vector<DelayedWork> delayedWork;
public:
HeroChainCalculationTask(
AINodeStorage & storage, AISharedStorage & nodes, const std::vector<int3> & tiles, uint64_t chainMask, int heroChainTurn)
:existingChains(), newChains(), 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);
newChains.reserve(NUM_CHAINS);
@ -444,6 +445,22 @@ public:
}
}
for(auto delayed = delayedWork.begin(); delayed != delayedWork.end();)
{
auto newActor = delayed->carrier->actor->tryExchangeNoLock(delayed->other->actor);
if(!newActor.lockAcquired) continue;
if(newActor.actor)
{
newChains.push_back(calculateExchange(newActor.actor, delayed->carrier, delayed->other));
}
delayed++;
}
delayedWork.clear();
cleanupInefectiveChains(newChains);
addHeroChain(newChains);
}
@ -483,11 +500,34 @@ bool AINodeStorage::calculateHeroChain()
CCreature::DisableChildLinkage = true;
auto r = blocked_range<size_t>(0, data.size());
HeroChainCalculationTask task(*this, nodes, data, chainMask, heroChainTurn);
if(data.size() > 100)
{
std::mutex resultMutex;
task.execute(r);
task.flushResult(heroChain);
std::random_shuffle(data.begin(), data.end());
parallel_for(blocked_range<size_t>(0, data.size()), [&](const blocked_range<size_t>& r)
{
//auto r = blocked_range<size_t>(0, data.size());
HeroChainCalculationTask task(*this, nodes, data, chainMask, heroChainTurn);
task.execute(r);
{
std::lock_guard<std::mutex> resultLock(resultMutex);
task.flushResult(heroChain);
}
});
}
else
{
auto r = blocked_range<size_t>(0, data.size());
HeroChainCalculationTask task(*this, nodes, data, chainMask, heroChainTurn);
task.execute(r);
task.flushResult(heroChain);\
}
CCreature::DisableChildLinkage = false;
@ -666,9 +706,10 @@ void HeroChainCalculationTask::calculateHeroChain(
}
}
auto newActor = carrier->actor->tryExchange(other->actor);
auto newActor = carrier->actor->tryExchangeNoLock(other->actor);
if(newActor) result.push_back(calculateExchange(newActor, carrier, other));
if(!newActor.lockAcquired) delayedWork.push_back(DelayedWork(carrier, other));
if(newActor.actor) result.push_back(calculateExchange(newActor.actor, carrier, other));
}
}

View File

@ -145,11 +145,11 @@ void HeroActor::setupSpecialActors()
}
}
ChainActor * ChainActor::tryExchange(const ChainActor * specialActor, const ChainActor * other) const
ExchangeResult ChainActor::tryExchangeNoLock(const ChainActor * specialActor, const ChainActor * other) const
{
if(!isMovable) return nullptr;
if(!isMovable) return ExchangeResult();
return baseActor->tryExchange(specialActor, other);
return baseActor->tryExchangeNoLock(specialActor, other);
}
namespace vstd
@ -169,12 +169,12 @@ namespace vstd
}
}
ChainActor * HeroActor::tryExchange(const ChainActor * specialActor, const ChainActor * other) const
ExchangeResult HeroActor::tryExchangeNoLock(const ChainActor * specialActor, const ChainActor * other) const
{
const ChainActor * otherBase = other->baseActor;
HeroActor * result = exchangeMap->tryExchange(otherBase);
ExchangeResult result = exchangeMap->tryExchangeNoLock(otherBase);
if(!result) return nullptr;
if(!result.actor || !result.lockAcquired) return result;
if(specialActor == this)
return result;
@ -184,7 +184,9 @@ ChainActor * HeroActor::tryExchange(const ChainActor * specialActor, const Chain
return &actor == specialActor;
});
return &result->specialActors[index];
result.actor = &(dynamic_cast<HeroActor *>(result.actor)->specialActors[index]);
return result;
}
HeroExchangeMap::HeroExchangeMap(const HeroActor * actor, const Nullkiller * ai)
@ -205,40 +207,67 @@ HeroExchangeMap::~HeroExchangeMap()
exchangeMap.clear();
}
HeroActor * HeroExchangeMap::tryExchange(const ChainActor * other)
ExchangeResult HeroExchangeMap::tryExchangeNoLock(const ChainActor * other)
{
auto position = exchangeMap.find(other);
ExchangeResult result;
if(position != exchangeMap.end())
{
return position->second;
boost::shared_lock<boost::shared_mutex> lock(sync, boost::try_to_lock);
if(!lock.owns_lock())
{
result.lockAcquired = false;
return result;
}
auto position = exchangeMap.find(other);
if(position != exchangeMap.end())
{
result.actor = position->second;
return result;
}
}
auto inserted = exchangeMap.insert(std::pair<const ChainActor *, HeroActor *>(other, nullptr));
if(!inserted.second)
{
return inserted.first->second; // already inserted
}
boost::unique_lock<boost::shared_mutex> uniqueLock(sync, boost::try_to_lock);
position = inserted.first;
if(!uniqueLock.owns_lock())
{
result.lockAcquired = false;
auto differentMasks = (actor->chainMask & other->chainMask) == 0;
return result;
}
if(!differentMasks) return nullptr;
auto inserted = exchangeMap.insert(std::pair<const ChainActor *, HeroActor *>(other, nullptr));
TResources resources = ai->cb->getResourceAmount();
if(!inserted.second)
{
result.actor = inserted.first->second;
if(!resources.canAfford(actor->armyCost + other->armyCost))
{
return result; // already inserted
}
auto position = inserted.first;
auto differentMasks = (actor->chainMask & other->chainMask) == 0;
if(!differentMasks) return result;
TResources resources = ai->cb->getResourceAmount();
if(!resources.canAfford(actor->armyCost + other->armyCost))
{
#if PATHFINDER_TRACE_LEVEL >= 2
logAi->trace(
"Can not afford exchange because of total cost %s but we have %s",
(actor->armyCost + other->armyCost).toString(),
resources.toString());
logAi->trace(
"Can not afford exchange because of total cost %s but we have %s",
(actor->armyCost + other->armyCost).toString(),
resources.toString());
#endif
return nullptr;
}
return result;
}
if(other->isMovable && other->armyValue <= actor->armyValue / 10 && other->armyValue < MIN_ARMY_STRENGTH_FOR_CHAIN)
return nullptr;
@ -247,52 +276,54 @@ HeroActor * HeroExchangeMap::tryExchange(const ChainActor * other)
HeroExchangeArmy * upgradedInitialArmy = tryUpgrade(actor->creatureSet, other->getActorObject(), availableResources);
HeroExchangeArmy * newArmy;
if(other->creatureSet->Slots().size())
{
if(upgradedInitialArmy)
if(other->creatureSet->Slots().size())
{
newArmy = pickBestCreatures(upgradedInitialArmy, other->creatureSet);
newArmy->armyCost = upgradedInitialArmy->armyCost;
newArmy->requireBuyArmy = upgradedInitialArmy->requireBuyArmy;
if(upgradedInitialArmy)
{
newArmy = pickBestCreatures(upgradedInitialArmy, other->creatureSet);
newArmy->armyCost = upgradedInitialArmy->armyCost;
newArmy->requireBuyArmy = upgradedInitialArmy->requireBuyArmy;
delete upgradedInitialArmy;
delete upgradedInitialArmy;
}
else
{
newArmy = pickBestCreatures(actor->creatureSet, other->creatureSet);
}
}
else
{
newArmy = pickBestCreatures(actor->creatureSet, other->creatureSet);
newArmy = upgradedInitialArmy;
}
}
else
{
newArmy = upgradedInitialArmy;
}
if(!newArmy) return nullptr;
if(!newArmy) return result;
auto reinforcement = newArmy->getArmyStrength() - actor->creatureSet->getArmyStrength();
auto reinforcement = newArmy->getArmyStrength() - actor->creatureSet->getArmyStrength();
#if PATHFINDER_TRACE_LEVEL >= 2
logAi->trace(
"Exchange %s->%s reinforcement: %d, %f%%",
actor->toString(),
other->toString(),
reinforcement,
100.0f * reinforcement / actor->armyValue);
logAi->trace(
"Exchange %s->%s reinforcement: %d, %f%%",
actor->toString(),
other->toString(),
reinforcement,
100.0f * reinforcement / actor->armyValue);
#endif
if(reinforcement <= actor->armyValue / 10 && reinforcement < MIN_ARMY_STRENGTH_FOR_CHAIN)
{
delete newArmy;
return nullptr;
return result;
}
HeroActor * exchanged = new HeroActor(actor, other, newArmy, ai);
exchanged->armyCost += newArmy->armyCost;
result.actor = exchanged;
exchangeMap[other] = exchanged;
return result;
}
HeroActor * exchanged = new HeroActor(actor, other, newArmy, ai);
exchanged->armyCost += newArmy->armyCost;
position->second = exchanged;
return exchanged;
}
HeroExchangeArmy * HeroExchangeMap::tryUpgrade(

View File

@ -17,6 +17,7 @@
extern const uint64_t MIN_ARMY_STRENGTH_FOR_CHAIN;
class ChainActor;
class HeroActor;
class Nullkiller;
@ -33,6 +34,14 @@ public:
}
};
struct ExchangeResult
{
bool lockAcquired;
ChainActor * actor;
ExchangeResult() : lockAcquired(true), actor(nullptr) {}
};
class ChainActor
{
protected:
@ -68,12 +77,12 @@ public:
ChainActor(){}
virtual std::string toString() const;
ChainActor * tryExchange(const ChainActor * other) const { return tryExchange(this, other); }
ExchangeResult tryExchangeNoLock(const ChainActor * other) const { return tryExchangeNoLock(this, other); }
void setBaseActor(HeroActor * base);
virtual const CGObjectInstance * getActorObject() const { return hero; }
protected:
virtual ChainActor * tryExchange(const ChainActor * specialActor, const ChainActor * other) const;
virtual ExchangeResult tryExchangeNoLock(const ChainActor * specialActor, const ChainActor * other) const;
};
class HeroExchangeMap
@ -88,7 +97,7 @@ public:
HeroExchangeMap(const HeroActor * actor, const Nullkiller * ai);
~HeroExchangeMap();
HeroActor * tryExchange(const ChainActor * other);
ExchangeResult tryExchangeNoLock(const ChainActor * other);
private:
HeroExchangeArmy * pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const;
@ -114,7 +123,7 @@ public:
HeroActor(const ChainActor * carrier, const ChainActor * other, const HeroExchangeArmy * army, const Nullkiller * ai);
protected:
virtual ChainActor * tryExchange(const ChainActor * specialActor, const ChainActor * other) const override;
virtual ExchangeResult tryExchangeNoLock(const ChainActor * specialActor, const ChainActor * other) const override;
};
class ObjectActor : public ChainActor