1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-04-13 11:40:38 +02:00

Nullkiller: parallel object clusterization, stabilization

This commit is contained in:
Andrii Danylchenko 2021-05-16 15:08:56 +03:00 committed by Andrii Danylchenko
parent 66843b22d3
commit 9a203b8af9
14 changed files with 332 additions and 165 deletions

View File

@ -115,6 +115,32 @@ const CGHeroInstance * HeroPtr::get(bool doWeExpectNull) const
return h;
}
const CGHeroInstance * HeroPtr::get(CCallback * cb, bool doWeExpectNull) const
{
//TODO? check if these all assertions every time we get info about hero affect efficiency
//
//behave terribly when attempting unauthorized access to hero that is not ours (or was lost)
assert(doWeExpectNull || h);
if(h)
{
auto obj = cb->getObj(hid);
//const bool owned = obj && obj->tempOwner == ai->playerID;
if(doWeExpectNull && !obj)
{
return nullptr;
}
else
{
assert(obj);
//assert(owned);
}
}
return h;
}
const CGHeroInstance * HeroPtr::operator->() const
{
return get();
@ -251,6 +277,11 @@ bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
return false;
}
bool isObjectPassable(const Nullkiller * ai, const CGObjectInstance * obj)
{
return isObjectPassable(obj, ai->playerID, ai->cb->getPlayerRelations(obj->tempOwner, ai->playerID));
}
bool isObjectPassable(const CGObjectInstance * obj)
{
return isObjectPassable(obj, ai->playerID, cb->getPlayerRelations(obj->tempOwner, ai->playerID));
@ -355,7 +386,7 @@ uint64_t timeElapsed(boost::chrono::time_point<boost::chrono::steady_clock> star
}
// todo: move to obj manager
bool shouldVisit(const CGHeroInstance * h, const CGObjectInstance * obj)
bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObjectInstance * obj)
{
switch(obj->ID)
{
@ -364,7 +395,7 @@ bool shouldVisit(const CGHeroInstance * h, const CGObjectInstance * obj)
return obj->tempOwner != h->tempOwner; //do not visit our towns at random
case Obj::BORDER_GATE:
{
for(auto q : ai->myCb->getMyQuests())
for(auto q : ai->cb->getMyQuests())
{
if(q.obj == obj)
{
@ -378,7 +409,7 @@ bool shouldVisit(const CGHeroInstance * h, const CGObjectInstance * obj)
case Obj::SEER_HUT:
case Obj::QUEST_GUARD:
{
for(auto q : ai->myCb->getMyQuests())
for(auto q : ai->cb->getMyQuests())
{
if(q.obj == obj)
{
@ -403,7 +434,7 @@ bool shouldVisit(const CGHeroInstance * h, const CGObjectInstance * obj)
{
if(level.first
&& h->getSlotFor(CreatureID(c)) != SlotID()
&& cb->getResourceAmount().canAfford(c.toCreature()->cost))
&& ai->cb->getResourceAmount().canAfford(c.toCreature()->cost))
{
return true;
}
@ -429,7 +460,7 @@ bool shouldVisit(const CGHeroInstance * h, const CGObjectInstance * obj)
case Obj::SCHOOL_OF_MAGIC:
case Obj::SCHOOL_OF_WAR:
{
if(cb->getResourceAmount(Res::GOLD) < 1000)
if(ai->cb->getResourceAmount(Res::GOLD) < 1000)
return false;
break;
}
@ -439,10 +470,10 @@ bool shouldVisit(const CGHeroInstance * h, const CGObjectInstance * obj)
break;
case Obj::TREE_OF_KNOWLEDGE:
{
if(ai->nullkiller->heroManager->getHeroRole(h) == HeroRole::SCOUT)
if(ai->heroManager->getHeroRole(h) == HeroRole::SCOUT)
return false;
TResources myRes = cb->getResourceAmount();
TResources myRes = ai->cb->getResourceAmount();
if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10)
return false;
break;
@ -450,14 +481,14 @@ bool shouldVisit(const CGHeroInstance * h, const CGObjectInstance * obj)
case Obj::MAGIC_WELL:
return h->mana < h->manaLimit();
case Obj::PRISON:
return ai->myCb->getHeroesInfo().size() < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER;
return ai->cb->getHeroesInfo().size() < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER;
case Obj::TAVERN:
{
//TODO: make AI actually recruit heroes
//TODO: only on request
if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
if(ai->cb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
return false;
else if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
else if(ai->cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
return false;
break;
}

View File

@ -18,8 +18,12 @@
#include "../../lib/mapObjects/CObjectHandler.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/CPathfinder.h"
#include <tbb/tbb.h>
using namespace tbb;
class CCallback;
class Nullkiller;
struct creInfo;
typedef const int3 & crint3;
@ -72,6 +76,7 @@ public:
}
const CGHeroInstance * get(bool doWeExpectNull = false) const;
const CGHeroInstance * get(CCallback * cb, bool doWeExpectNull = false) const;
bool validAndSet() const;
@ -168,6 +173,7 @@ void foreach_neighbour(CCallback * cbp, const int3 & pos, std::function<void(CCa
bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater);
bool isObjectPassable(const CGObjectInstance * obj);
bool isObjectPassable(const Nullkiller * ai, const CGObjectInstance * obj);
bool isObjectPassable(const CGObjectInstance * obj, PlayerColor playerColor, PlayerRelations::PlayerRelations objectRelations);
bool isBlockVisitObj(const int3 & pos);
@ -184,7 +190,27 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
uint64_t timeElapsed(boost::chrono::time_point<boost::chrono::steady_clock> start);
// todo: move to obj manager
bool shouldVisit(const CGHeroInstance * h, const CGObjectInstance * obj);
bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObjectInstance * obj);
template<typename TFunc>
void pforeachTilePos(crint3 mapSize, TFunc fn)
{
parallel_for(blocked_range<size_t>(0, mapSize.x), [&](const blocked_range<size_t>& r)
{
int3 pos;
for(pos.x = r.begin(); pos.x != r.end(); ++pos.x)
{
for(pos.y = 0; pos.y < mapSize.y; ++pos.y)
{
for(pos.z = 0; pos.z < mapSize.z; ++pos.z)
{
fn(pos);
}
}
}
});
}
class CDistanceSorter
{
@ -197,3 +223,75 @@ public:
}
bool operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const;
};
template <class T>
class SharedPool
{
public:
struct External_Deleter
{
explicit External_Deleter(std::weak_ptr<SharedPool<T>* > pool)
: pool(pool)
{
}
void operator()(T * ptr)
{
std::unique_ptr<T> uptr(ptr);
if(auto pool_ptr = pool.lock())
{
(*pool_ptr.get())->add(std::move(uptr));
}
}
private:
std::weak_ptr<SharedPool<T>* > pool;
};
public:
using ptr_type = std::unique_ptr<T, External_Deleter>;
SharedPool(std::function<std::unique_ptr<T>()> elementFactory)
: elementFactory(elementFactory), pool(), sync(), instance_tracker(new SharedPool<T>*(this))
{}
void add(std::unique_ptr<T> t)
{
std::lock_guard<std::mutex> lock(sync);
pool.push_back(std::move(t));
}
ptr_type acquire()
{
std::lock_guard<std::mutex> lock(sync);
bool poolIsEmpty = pool.empty();
T * element = poolIsEmpty
? elementFactory().release()
: pool.back().release();
ptr_type tmp(
element,
External_Deleter(std::weak_ptr<SharedPool<T>*>(instance_tracker)));
if(!poolIsEmpty) pool.pop_back();
return std::move(tmp);
}
bool empty() const
{
return pool.empty();
}
size_t size() const
{
return pool.size();
}
private:
std::vector<std::unique_ptr<T>> pool;
std::function<std::unique_ptr<T>()> elementFactory;
std::shared_ptr<SharedPool<T> *> instance_tracker;
std::mutex sync;
};

View File

@ -12,8 +12,6 @@
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../Engine/Nullkiller.h"
extern boost::thread_specific_ptr<CCallback> cb;
void BuildAnalyzer::updateTownDwellings(TownDevelopmentInfo & developmentInfo)
{
auto townInfo = developmentInfo.town->town;
@ -71,7 +69,7 @@ void BuildAnalyzer::updateOtherBuildings(TownDevelopmentInfo & developmentInfo)
{BuildingID::TOWN_HALL, BuildingID::CITY_HALL, BuildingID::CAPITOL}
};
if(developmentInfo.existingDwellings.size() >= 2 && cb->getDate(Date::DAY_OF_WEEK) > boost::date_time::Friday)
if(developmentInfo.existingDwellings.size() >= 2 && ai->cb->getDate(Date::DAY_OF_WEEK) > boost::date_time::Friday)
{
otherBuildings.push_back({BuildingID::CITADEL, BuildingID::CASTLE});
}
@ -99,7 +97,7 @@ int32_t convertToGold(const TResources & res)
TResources BuildAnalyzer::getResourcesRequiredNow() const
{
auto resourcesAvailable = cb->getResourceAmount();
auto resourcesAvailable = ai->cb->getResourceAmount();
auto result = requiredResources - resourcesAvailable;
result.positive();
@ -109,7 +107,7 @@ TResources BuildAnalyzer::getResourcesRequiredNow() const
TResources BuildAnalyzer::getTotalResourcesRequired() const
{
auto resourcesAvailable = cb->getResourceAmount();
auto resourcesAvailable = ai->cb->getResourceAmount();
auto result = totalDevelopmentCost - resourcesAvailable;
result.positive();
@ -125,7 +123,7 @@ void BuildAnalyzer::update()
reset();
auto towns = cb->getTownsInfo();
auto towns = ai->cb->getTownsInfo();
for(const CGTownInstance* town : towns)
{
@ -159,7 +157,7 @@ void BuildAnalyzer::update()
updateDailyIncome();
goldPreasure = (float)armyCost[Res::GOLD] / (1 + cb->getResourceAmount(Res::GOLD) + (float)dailyIncome[Res::GOLD] * 7.0f);
goldPreasure = (float)armyCost[Res::GOLD] / (1 + ai->cb->getResourceAmount(Res::GOLD) + (float)dailyIncome[Res::GOLD] * 7.0f);
logAi->trace("Gold preasure: %f", goldPreasure);
}
@ -203,7 +201,7 @@ BuildingInfo BuildAnalyzer::getBuildingOrPrerequisite(
if(!town->hasBuilt(building))
{
auto canBuild = cb->canBuildStructure(town, building);
auto canBuild = ai->cb->canBuildStructure(town, building);
if(canBuild == EBuildingState::ALLOWED)
{
@ -262,8 +260,8 @@ BuildingInfo BuildAnalyzer::getBuildingOrPrerequisite(
void BuildAnalyzer::updateDailyIncome()
{
auto objects = cb->getMyObjects();
auto towns = cb->getTownsInfo();
auto objects = ai->cb->getMyObjects();
auto towns = ai->cb->getTownsInfo();
dailyIncome = TResources();

View File

@ -51,7 +51,7 @@ void DangerHitMapAnalyzer::updateHitMap()
boost::this_thread::interruption_point();
foreach_tile_pos([&](const int3 & pos)
pforeachTilePos(mapSize, [&](const int3 & pos)
{
for(AIPath & path : ai->pathfinder->getPathInfo(pos))
{

View File

@ -16,14 +16,16 @@
void ObjectCluster::addObject(const CGObjectInstance * obj, const AIPath & path, float priority)
{
auto & info = objects[obj];
ClusterObjects::accessor info;
if(info.priority < priority)
objects.insert(info, ClusterObjects::value_type(obj, ClusterObjectInfo()));
if(info->second.priority < priority)
{
info.priority = priority;
info.movementCost = path.movementCost() - path.firstNode().cost;
info.danger = path.targetObjectDanger;
info.turn = path.turn();
info->second.priority = priority;
info->second.movementCost = path.movementCost() - path.firstNode().cost;
info->second.danger = path.targetObjectDanger;
info->second.turn = path.turn();
}
}
@ -125,7 +127,7 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons
|| blocker->ID == Obj::BORDER_GATE
|| blocker->ID == Obj::SHIPYARD)
{
if(!isObjectPassable(blocker))
if(!isObjectPassable(ai, blocker))
return blocker;
}
@ -206,130 +208,134 @@ void ObjectClusterizer::clusterize()
logAi->debug("Begin object clusterization");
for(const CGObjectInstance * obj : ai->memory->visitableObjs)
std::vector<const CGObjectInstance *> objs(
ai->memory->visitableObjs.begin(),
ai->memory->visitableObjs.end());
parallel_for(blocked_range<size_t>(0, objs.size()), [&](const blocked_range<size_t> & r)
{
if(!shouldVisitObject(obj))
continue;
auto priorityEvaluator = ai->priorityEvaluators->acquire();
for(int i = r.begin(); i != r.end(); i++)
{
auto obj = objs[i];
if(!shouldVisitObject(obj))
return;
#if AI_TRACE_LEVEL >= 2
logAi->trace("Check object %s%s.", obj->getObjectName(), obj->visitablePos().toString());
logAi->trace("Check object %s%s.", obj->getObjectName(), obj->visitablePos().toString());
#endif
auto paths = ai->pathfinder->getPathInfo(obj->visitablePos());
auto paths = ai->pathfinder->getPathInfo(obj->visitablePos());
if(paths.empty())
{
#if AI_TRACE_LEVEL >= 2
logAi->trace("No paths found.");
#endif
continue;
}
std::sort(paths.begin(), paths.end(), [](const AIPath & p1, const AIPath & p2) -> bool
{
return p1.movementCost() < p2.movementCost();
});
if(vstd::contains(ignoreObjects, obj->ID))
{
farObjects.addObject(obj, paths.front(), 0);
#if AI_TRACE_LEVEL >= 2
logAi->trace("Object ignored. Moved to far objects with path %s", paths.front().toString());
#endif
continue;
}
std::set<const CGHeroInstance *> heroesProcessed;
for(auto & path : paths)
{
#if AI_TRACE_LEVEL >= 2
logAi->trace("Checking path %s", path.toString());
#endif
if(!shouldVisit(path.targetHero, obj))
if(paths.empty())
{
#if AI_TRACE_LEVEL >= 2
logAi->trace("Hero %s does not need to visit %s", path.targetHero->name, obj->getObjectName());
logAi->trace("No paths found.");
#endif
continue;
}
if(path.nodes.size() > 1)
std::sort(paths.begin(), paths.end(), [](const AIPath & p1, const AIPath & p2) -> bool
{
auto blocker = getBlocker(path);
return p1.movementCost() < p2.movementCost();
});
if(blocker)
{
if(vstd::contains(heroesProcessed, path.targetHero))
{
#if AI_TRACE_LEVEL >= 2
logAi->trace("Hero %s is already processed.", path.targetHero->name);
#endif
continue;
}
heroesProcessed.insert(path.targetHero);
auto cluster = blockedObjects[blocker];
if(!cluster)
{
cluster.reset(new ObjectCluster(blocker));
blockedObjects[blocker] = cluster;
}
float priority = ai->priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
if(priority < MIN_PRIORITY)
continue;
cluster->addObject(obj, path, priority);
if(vstd::contains(ignoreObjects, obj->ID))
{
farObjects.addObject(obj, paths.front(), 0);
#if AI_TRACE_LEVEL >= 2
logAi->trace("Path added to cluster %s%s", blocker->getObjectName(), blocker->visitablePos().toString());
logAi->trace("Object ignored. Moved to far objects with path %s", paths.front().toString());
#endif
continue;
}
std::set<const CGHeroInstance *> heroesProcessed;
for(auto & path : paths)
{
#if AI_TRACE_LEVEL >= 2
logAi->trace("Checking path %s", path.toString());
#endif
if(!shouldVisit(ai, path.targetHero, obj))
{
#if AI_TRACE_LEVEL >= 2
logAi->trace("Hero %s does not need to visit %s", path.targetHero->name, obj->getObjectName());
#endif
continue;
}
}
heroesProcessed.insert(path.targetHero);
float priority = ai->priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
if(path.nodes.size() > 1)
{
auto blocker = getBlocker(path);
if(priority < MIN_PRIORITY)
continue;
bool interestingObject = path.turn() <= 2 || priority > 0.5f;
if(blocker)
{
if(vstd::contains(heroesProcessed, path.targetHero))
{
#if AI_TRACE_LEVEL >= 2
logAi->trace("Hero %s is already processed.", path.targetHero->name);
#endif
continue;
}
if(interestingObject)
{
nearObjects.addObject(obj, path, priority);
}
else
{
farObjects.addObject(obj, path, priority);
}
heroesProcessed.insert(path.targetHero);
float priority = priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
if(priority < MIN_PRIORITY)
continue;
ClusterMap::accessor cluster;
blockedObjects.insert(
cluster,
ClusterMap::value_type(blocker, std::make_shared<ObjectCluster>(blocker)));
cluster->second->addObject(obj, path, priority);
#if AI_TRACE_LEVEL >= 2
logAi->trace("Path %s added to %s objects. Turn: %d, priority: %f",
path.toString(),
interestingObject ? "near" : "far",
path.turn(),
priority);
logAi->trace("Path added to cluster %s%s", blocker->getObjectName(), blocker->visitablePos().toString());
#endif
}
}
continue;
}
}
vstd::erase_if(blockedObjects, [](std::pair<const CGObjectInstance *, std::shared_ptr<ObjectCluster>> pair) -> bool
{
return pair.second->objects.empty();
heroesProcessed.insert(path.targetHero);
float priority = priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
if(priority < MIN_PRIORITY)
continue;
bool interestingObject = path.turn() <= 2 || priority > 0.5f;
if(interestingObject)
{
nearObjects.addObject(obj, path, priority);
}
else
{
farObjects.addObject(obj, path, priority);
}
#if AI_TRACE_LEVEL >= 2
logAi->trace("Path %s added to %s objects. Turn: %d, priority: %f",
path.toString(),
interestingObject ? "near" : "far",
path.turn(),
priority);
#endif
}
}
});
logAi->trace("Near objects count: %i", nearObjects.objects.size());
logAi->trace("Far objects count: %i", farObjects.objects.size());
for(auto pair : blockedObjects)
{
logAi->trace("Cluster %s %s count: %i", pair.first->getObjectName(), pair.first->visitablePos().toString(), pair.second->objects.size());

View File

@ -19,10 +19,12 @@ struct ClusterObjectInfo
uint8_t turn;
};
typedef tbb::concurrent_hash_map<const CGObjectInstance *, ClusterObjectInfo> ClusterObjects;
struct ObjectCluster
{
public:
std::map<const CGObjectInstance *, ClusterObjectInfo> objects;
ClusterObjects objects;
const CGObjectInstance * blocker;
void reset()
@ -45,12 +47,14 @@ public:
const CGObjectInstance * calculateCenter() const;
};
typedef tbb::concurrent_hash_map<const CGObjectInstance *, std::shared_ptr<ObjectCluster>> ClusterMap;
class ObjectClusterizer
{
private:
ObjectCluster nearObjects;
ObjectCluster farObjects;
std::map<const CGObjectInstance *, std::shared_ptr<ObjectCluster>> blockedObjects;
ClusterMap blockedObjects;
const Nullkiller * ai;
public:

View File

@ -71,7 +71,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath>
continue;
}
if(objToVisit && !shouldVisit(path.targetHero, objToVisit))
if(objToVisit && !shouldVisit(ai->nullkiller.get(), path.targetHero, objToVisit))
continue;
auto hero = path.targetHero;

View File

@ -43,6 +43,13 @@ void Nullkiller::init(std::shared_ptr<CCallback> cb, PlayerColor playerID)
this->playerID = playerID;
priorityEvaluator.reset(new PriorityEvaluator(this));
priorityEvaluators.reset(
new SharedPool<PriorityEvaluator>(
[&]()->std::unique_ptr<PriorityEvaluator>
{
return std::make_unique<PriorityEvaluator>(this);
}));
dangerHitMap.reset(new DangerHitMapAnalyzer(this));
buildAnalyzer.reset(new BuildAnalyzer(this));
objectClusterizer.reset(new ObjectClusterizer(this));
@ -224,16 +231,23 @@ void Nullkiller::makeTurn()
HeroPtr hero = bestTask->getHero();
if(bestTask->priority < NEXT_SCAN_MIN_PRIORITY
&& hero.validAndSet()
&& heroManager->getHeroRole(hero) == HeroRole::MAIN
&& scanDepth != ScanDepth::FULL)
{
logAi->trace(
"Goal %s has too low priority %f so increasing scan depth",
bestTask->toString(),
bestTask->priority);
scanDepth = (ScanDepth)((int)scanDepth + 1);
continue;
HeroRole heroRole = HeroRole::MAIN;
if(hero.validAndSet())
heroRole = heroManager->getHeroRole(hero);
if(heroRole == HeroRole::MAIN)
{
logAi->trace(
"Goal %s has too low priority %f so increasing scan depth",
bestTask->toString(),
bestTask->priority);
scanDepth = (ScanDepth)((int)scanDepth + 1);
continue;
}
}
if(bestTask->priority < MIN_PRIORITY)

View File

@ -9,8 +9,6 @@
*/
#pragma once
#include <boost/asio.hpp>
#include "PriorityEvaluator.h"
#include "FuzzyHelper.h"
#include "AIMemory.h"
@ -58,6 +56,7 @@ public:
std::unique_ptr<BuildAnalyzer> buildAnalyzer;
std::unique_ptr<ObjectClusterizer> objectClusterizer;
std::unique_ptr<PriorityEvaluator> priorityEvaluator;
std::unique_ptr<SharedPool<PriorityEvaluator>> priorityEvaluators;
std::unique_ptr<AIPathfinder> pathfinder;
std::unique_ptr<HeroManager> heroManager;
std::unique_ptr<ArmyManager> armyManager;

View File

@ -29,8 +29,6 @@
#define MIN_AI_STRENGHT (0.5f) //lower when combat AI gets smarter
#define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us
extern boost::thread_specific_ptr<CCallback> cb;
EvaluationContext::EvaluationContext(const Nullkiller * ai)
: movementCost(0.0),
manaCost(0),
@ -78,7 +76,7 @@ void PriorityEvaluator::initVisitTile()
value = engine->getOutputVariable("Value");
}
int32_t estimateTownIncome(const CGObjectInstance * target, const CGHeroInstance * hero)
int32_t estimateTownIncome(CCallback * cb, const CGObjectInstance * target, const CGHeroInstance * hero)
{
auto relations = cb->getPlayerRelations(hero->tempOwner, target->tempOwner);
@ -116,7 +114,7 @@ uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHero
return result;
}
uint64_t getDwellingScore(const CGObjectInstance * target, bool checkGold)
uint64_t getDwellingScore(CCallback * cb, const CGObjectInstance * target, bool checkGold)
{
auto dwelling = dynamic_cast<const CGDwelling *>(target);
uint64_t score = 0;
@ -207,14 +205,14 @@ uint64_t RewardEvaluator::getArmyReward(
case Obj::TOWN:
return target->tempOwner == PlayerColor::NEUTRAL ? 1000 : 10000;
case Obj::HILL_FORT:
return ai->armyManager->calculateCreateresUpgrade(army, target, cb->getResourceAmount()).upgradeValue;
return ai->armyManager->calculateCreateresUpgrade(army, target, ai->cb->getResourceAmount()).upgradeValue;
case Obj::CREATURE_BANK:
return getCreatureBankArmyReward(target, hero);
case Obj::CREATURE_GENERATOR1:
case Obj::CREATURE_GENERATOR2:
case Obj::CREATURE_GENERATOR3:
case Obj::CREATURE_GENERATOR4:
return getDwellingScore(target, checkGold);
return getDwellingScore(ai->cb.get(), target, checkGold);
case Obj::CRYPT:
case Obj::SHIPWRECK:
case Obj::SHIPWRECK_SURVIVOR:
@ -225,7 +223,7 @@ uint64_t RewardEvaluator::getArmyReward(
case Obj::DRAGON_UTOPIA:
return 10000;
case Obj::HERO:
return cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
? enemyArmyEliminationRewardRatio * dynamic_cast<const CGHeroInstance *>(target)->getArmyStrength()
: 0;
default:
@ -241,7 +239,7 @@ int RewardEvaluator::getGoldCost(const CGObjectInstance * target, const CGHeroIn
switch(target->ID)
{
case Obj::HILL_FORT:
return ai->armyManager->calculateCreateresUpgrade(army, target, cb->getResourceAmount()).upgradeCost[Res::GOLD];
return ai->armyManager->calculateCreateresUpgrade(army, target, ai->cb->getResourceAmount()).upgradeCost[Res::GOLD];
case Obj::SCHOOL_OF_MAGIC:
case Obj::SCHOOL_OF_WAR:
return 1000;
@ -325,7 +323,7 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons
: 0.5f;
case Obj::HERO:
return cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
? getEnemyHeroStrategicalValue(dynamic_cast<const CGHeroInstance *>(target))
: 0;
@ -380,7 +378,7 @@ float RewardEvaluator::getSkillReward(const CGObjectInstance * target, const CGH
case Obj::WITCH_HUT:
return evaluateWitchHutSkillScore(dynamic_cast<const CGWitchHut *>(target), hero, role);
case Obj::HERO:
return cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
? enemyHeroEliminationSkillRewardRatio * dynamic_cast<const CGHeroInstance *>(target)->level
: 0;
default:
@ -438,7 +436,7 @@ int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CG
case Obj::WATER_WHEEL:
return 1000;
case Obj::TOWN:
return dailyIncomeMultiplier * estimateTownIncome(target, hero);
return dailyIncomeMultiplier * estimateTownIncome(ai->cb.get(), target, hero);
case Obj::MINE:
case Obj::ABANDONED_MINE:
return dailyIncomeMultiplier * (isGold ? 1000 : 75);
@ -459,7 +457,7 @@ int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CG
case Obj::SEA_CHEST:
return 1500;
case Obj::HERO:
return cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
? heroEliminationBonus + enemyArmyEliminationGoldRewardRatio * getArmyCost(dynamic_cast<const CGHeroInstance *>(target))
: 0;
default:
@ -550,7 +548,12 @@ public:
class ExecuteHeroChainEvaluationContextBuilder : public IEvaluationContextBuilder
{
private:
const Nullkiller * ai;
public:
ExecuteHeroChainEvaluationContextBuilder(const Nullkiller * ai) : ai(ai) {}
virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal task) const override
{
if(task->goalType != Goals::EXECUTE_HERO_CHAIN)
@ -578,14 +581,14 @@ public:
}
auto heroPtr = task->hero;
auto day = cb->getDate(Date::DAY);
auto hero = heroPtr.get();
auto day = ai->cb->getDate(Date::DAY);
auto hero = heroPtr.get(ai->cb.get());
bool checkGold = evaluationContext.danger == 0;
auto army = path.heroArmy;
const CGObjectInstance * target = cb->getObj((ObjectInstanceID)task->objid, false);
const CGObjectInstance * target = ai->cb->getObj((ObjectInstanceID)task->objid, false);
if (target && cb->getPlayerRelations(target->tempOwner, hero->tempOwner) == PlayerRelations::ENEMIES)
if (target && ai->cb->getPlayerRelations(target->tempOwner, hero->tempOwner) == PlayerRelations::ENEMIES)
{
evaluationContext.goldReward += evaluationContext.evaluator.getGoldReward(target, hero);
evaluationContext.armyReward += evaluationContext.evaluator.getArmyReward(target, hero, army, checkGold);
@ -603,7 +606,12 @@ public:
class ClusterEvaluationContextBuilder : public IEvaluationContextBuilder
{
private:
const Nullkiller * ai;
public:
ClusterEvaluationContextBuilder(const Nullkiller * ai) : ai(ai) {}
virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal task) const override
{
if(task->goalType != Goals::UNLOCK_CLUSTER)
@ -627,7 +635,7 @@ public:
for(auto objInfo : objects)
{
auto target = objInfo.first;
auto day = cb->getDate(Date::DAY);
auto day = ai->cb->getDate(Date::DAY);
bool checkGold = objInfo.second.danger == 0;
auto army = hero;
@ -709,9 +717,9 @@ PriorityEvaluator::PriorityEvaluator(const Nullkiller * ai)
:ai(ai)
{
initVisitTile();
evaluationContextBuilders.push_back(std::make_shared<ExecuteHeroChainEvaluationContextBuilder>());
evaluationContextBuilders.push_back(std::make_shared<ExecuteHeroChainEvaluationContextBuilder>(ai));
evaluationContextBuilders.push_back(std::make_shared<BuildThisEvaluationContextBuilder>());
evaluationContextBuilders.push_back(std::make_shared<ClusterEvaluationContextBuilder>());
evaluationContextBuilders.push_back(std::make_shared<ClusterEvaluationContextBuilder>(ai));
evaluationContextBuilders.push_back(std::make_shared<HeroExchangeEvaluator>());
evaluationContextBuilders.push_back(std::make_shared<ArmyUpgradeEvaluator>());
evaluationContextBuilders.push_back(std::make_shared<DefendTownEvaluator>());
@ -769,7 +777,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
closestHeroRatioVariable->setValue(evaluationContext.closestWayRatio);
strategicalValueVariable->setValue(evaluationContext.strategicalValue);
goldPreasureVariable->setValue(ai->buildAnalyzer->getGoldPreasure());
goldCostVariable->setValue(evaluationContext.goldCost / ((float)cb->getResourceAmount(Res::GOLD) + (float)ai->buildAnalyzer->getDailyIncome()[Res::GOLD] + 1.0f));
goldCostVariable->setValue(evaluationContext.goldCost / ((float)ai->cb->getResourceAmount(Res::GOLD) + (float)ai->buildAnalyzer->getDailyIncome()[Res::GOLD] + 1.0f));
turnVariable->setValue(evaluationContext.turn);
fearVariable->setValue(evaluationContext.enemyHeroDangerRatio);

View File

@ -8,7 +8,6 @@
*
*/
#include "StdInc.h"
#include <tbb/tbb.h>
#include "AINodeStorage.h"
#include "Actions/TownPortalAction.h"
#include "../Goals/Goals.h"
@ -20,8 +19,6 @@
#include "../../../lib/PathfinderUtil.h"
#include "../../../lib/CPlayerState.h"
using namespace tbb;
std::shared_ptr<boost::multi_array<AIPathNode, 5>> AISharedStorage::shared;
std::set<int3> commitedTiles;
std::set<int3> commitedTilesInitial;
@ -504,7 +501,7 @@ bool AINodeStorage::calculateHeroChain()
{
std::mutex resultMutex;
std::random_shuffle(data.begin(), data.end());
std::random_shuffle(data.begin(), data.end());
parallel_for(blocked_range<size_t>(0, data.size()), [&](const blocked_range<size_t>& r)
{
@ -526,7 +523,7 @@ bool AINodeStorage::calculateHeroChain()
HeroChainCalculationTask task(*this, nodes, data, chainMask, heroChainTurn);
task.execute(r);
task.flushResult(heroChain);\
task.flushResult(heroChain);
}
CCreature::DisableChildLinkage = false;

View File

@ -51,10 +51,11 @@ namespace AIPathfinding
{
private:
const IShipyard * shipyard;
const CPlayerSpecificInfoCallback * cb;
public:
BuildBoatAction(const IShipyard * shipyard)
: shipyard(shipyard)
BuildBoatAction(const CPlayerSpecificInfoCallback * cb, const IShipyard * shipyard)
: cb(cb), shipyard(shipyard)
{
}

View File

@ -196,15 +196,26 @@ HeroExchangeMap::HeroExchangeMap(const HeroActor * actor, const Nullkiller * ai)
HeroExchangeMap::~HeroExchangeMap()
{
CCreature::DisableChildLinkage = true;
for(auto & exchange : exchangeMap)
{
if(!exchange.second) continue;
delete exchange.second->creatureSet;
}
CCreature::DisableChildLinkage = false;
for(auto & exchange : exchangeMap)
{
if(!exchange.second) continue;
delete exchange.second;
}
exchangeMap.clear();
}
ExchangeResult HeroExchangeMap::tryExchangeNoLock(const ChainActor * other)
@ -270,7 +281,7 @@ ExchangeResult HeroExchangeMap::tryExchangeNoLock(const ChainActor * other)
}
if(other->isMovable && other->armyValue <= actor->armyValue / 10 && other->armyValue < MIN_ARMY_STRENGTH_FOR_CHAIN)
return nullptr;
return result;
TResources availableResources = resources - actor->armyCost - other->armyCost;
HeroExchangeArmy * upgradedInitialArmy = tryUpgrade(actor->creatureSet, other->getActorObject(), availableResources);

View File

@ -69,7 +69,7 @@ namespace AIPathfinding
if(shipyard->shipyardStatus() == IShipyard::GOOD)
{
int3 boatLocation = shipyard->bestLocation();
virtualBoats[boatLocation] = std::make_shared<BuildBoatAction>(shipyard);
virtualBoats[boatLocation] = std::make_shared<BuildBoatAction>(cb, shipyard);
logAi->debug("Virtual boat added at %s", boatLocation.toString());
}
}