mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
NKAI: improve army gathering
This commit is contained in:
parent
8b0c7b6601
commit
148c3436df
@ -590,13 +590,18 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
|
||||
|
||||
if(hero.validAndSet() && target.valid() && objects.size())
|
||||
{
|
||||
auto objType = objects.back()->ID;
|
||||
|
||||
auto topObj = objects.front()->id == hero->id ? objects.back() : objects.front();
|
||||
auto objType = topObj->ID; // top object should be our hero
|
||||
auto goalObjectID = nullkiller->getTargetObject();
|
||||
auto ratio = (float)nullkiller->dangerEvaluator->evaluateDanger(target, hero.get()) / (float)hero->getTotalStrength();
|
||||
bool dangerUnknown = ratio == 0;
|
||||
bool dangerTooHigh = ratio > (1 / SAFE_ATTACK_CONSTANT);
|
||||
|
||||
answer = objects.back()->id == nullkiller->getTargetObject(); // no if we do not aim to visit this object
|
||||
answer = topObj->id == goalObjectID; // no if we do not aim to visit this object
|
||||
logAi->trace("Query hook: %s(%s) by %s danger ratio %f", target.toString(), topObj->getObjectName(), hero.name, ratio);
|
||||
|
||||
if(cb->getObj(goalObjectID, false))
|
||||
{
|
||||
logAi->trace("AI expected %s", cb->getObj(goalObjectID, false)->getObjectName());
|
||||
}
|
||||
|
||||
if(objType == Obj::BORDERGUARD || objType == Obj::QUEST_GUARD)
|
||||
{
|
||||
@ -604,9 +609,10 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
|
||||
}
|
||||
else if(objType == Obj::ARTIFACT || objType == Obj::RESOURCE)
|
||||
{
|
||||
logAi->trace("Guarded object query hook: %s by %s danger ratio %f", target.toString(), hero.name, ratio);
|
||||
bool dangerUnknown = ratio == 0;
|
||||
bool dangerTooHigh = ratio > (1 / SAFE_ATTACK_CONSTANT);
|
||||
|
||||
answer = dangerUnknown || dangerTooHigh;
|
||||
answer = !dangerUnknown && !dangerTooHigh;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ void HeroManager::update()
|
||||
return scores.at(h1) > scores.at(h2);
|
||||
};
|
||||
|
||||
int globalMainCount = std::min(((int)myHeroes.size() + 2) / 3, cb->getMapSize().x / 100 + 1);
|
||||
int globalMainCount = std::min(((int)myHeroes.size() + 2) / 3, cb->getMapSize().x / 50 + 1);
|
||||
|
||||
std::sort(myHeroes.begin(), myHeroes.end(), scoreSort);
|
||||
|
||||
|
@ -81,7 +81,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath>
|
||||
auto hero = path.targetHero;
|
||||
auto danger = path.getTotalDanger();
|
||||
|
||||
if(ai->nullkiller->heroManager->getHeroRole(hero) == HeroRole::SCOUT && danger == 0 && path.exchangeCount > 1)
|
||||
if(ai->nullkiller->heroManager->getHeroRole(hero) == HeroRole::SCOUT && path.exchangeCount > 1)
|
||||
continue;
|
||||
|
||||
auto firstBlockedAction = path.getFirstBlockedAction();
|
||||
@ -126,8 +126,13 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath>
|
||||
|
||||
sharedPtr.reset(newWay);
|
||||
|
||||
if(!closestWay || closestWay->movementCost() > path.movementCost())
|
||||
auto heroRole = ai->nullkiller->heroManager->getHeroRole(path.targetHero);
|
||||
|
||||
if(heroRole == HeroRole::SCOUT
|
||||
&& (!closestWay || closestWay->movementCost() > path.movementCost()))
|
||||
{
|
||||
closestWay = &path;
|
||||
}
|
||||
|
||||
if(!ai->nullkiller->arePathHeroesLocked(path))
|
||||
{
|
||||
@ -137,11 +142,13 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath>
|
||||
}
|
||||
}
|
||||
|
||||
assert(closestWay || waysToVisitObj.empty());
|
||||
for(auto way : waysToVisitObj)
|
||||
if(closestWay)
|
||||
{
|
||||
way->closestWayRatio
|
||||
= closestWay->movementCost() / way->getPath().movementCost();
|
||||
for(auto way : waysToVisitObj)
|
||||
{
|
||||
way->closestWayRatio
|
||||
= closestWay->movementCost() / way->getPath().movementCost();
|
||||
}
|
||||
}
|
||||
|
||||
return tasks;
|
||||
|
@ -45,8 +45,7 @@ Goals::TGoalVec GatherArmyBehavior::decompose() const
|
||||
|
||||
for(const CGHeroInstance * hero : heroes)
|
||||
{
|
||||
if(ai->nullkiller->heroManager->getHeroRole(hero) == HeroRole::MAIN
|
||||
&& hero->getArmyStrength() >= 300)
|
||||
if(ai->nullkiller->heroManager->getHeroRole(hero) == HeroRole::MAIN)
|
||||
{
|
||||
vstd::concatenate(tasks, deliverArmyToHero(hero));
|
||||
}
|
||||
@ -70,13 +69,6 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
|
||||
#if NKAI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Checking ways to gaher army for hero %s, %s", hero->getObjectName(), pos.toString());
|
||||
#endif
|
||||
if(ai->nullkiller->isHeroLocked(hero))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Skipping locked hero %s, %s", hero->getObjectName(), pos.toString());
|
||||
#endif
|
||||
return tasks;
|
||||
}
|
||||
|
||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(pos);
|
||||
|
||||
@ -92,6 +84,14 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
|
||||
|
||||
if(path.containsHero(hero)) continue;
|
||||
|
||||
if(path.turn() == 0 && hero->inTownGarrison)
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Skipping garnisoned hero %s, %s", hero->getObjectName(), pos.toString());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
@ -124,14 +124,32 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
|
||||
// avoid trying to move bigger army to the weaker one.
|
||||
if(armyValue > 1)
|
||||
{
|
||||
bool hasOtherMainInPath = false;
|
||||
|
||||
for(auto node : path.nodes)
|
||||
{
|
||||
if(!node.targetHero) continue;
|
||||
|
||||
auto heroRole = ai->nullkiller->heroManager->getHeroRole(node.targetHero);
|
||||
|
||||
if(heroRole == HeroRole::MAIN)
|
||||
{
|
||||
hasOtherMainInPath = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(hasOtherMainInPath)
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Army value is too large.");
|
||||
logAi->trace("Army value is too large.");
|
||||
#endif
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto danger = path.getTotalDanger();
|
||||
|
||||
auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
|
||||
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
@ -194,7 +212,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Path found %s", path.toString());
|
||||
#endif
|
||||
if(upgrader->visitingHero != path.targetHero)
|
||||
if(upgrader->visitingHero && upgrader->visitingHero.get() != path.targetHero)
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Ignore path. Town has visiting hero.");
|
||||
@ -219,7 +237,10 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
|
||||
auto heroRole = ai->nullkiller->heroManager->getHeroRole(path.targetHero);
|
||||
|
||||
if(heroRole == HeroRole::SCOUT
|
||||
&& ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
|
||||
@ -228,16 +249,22 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
||||
}
|
||||
|
||||
auto upgrade = ai->nullkiller->armyManager->calculateCreaturesUpgrade(path.heroArmy, upgrader, availableResources);
|
||||
auto armyValue = (float)upgrade.upgradeValue / path.getHeroStrength();
|
||||
|
||||
if(ai->nullkiller->heroManager->getHeroRole(path.targetHero) == HeroRole::MAIN)
|
||||
{
|
||||
upgrade.upgradeValue +=
|
||||
ai->nullkiller->armyManager->howManyReinforcementsCanGet(path.targetHero, path.heroArmy, upgrader);
|
||||
upgrade.upgradeValue +=
|
||||
ai->nullkiller->armyManager->howManyReinforcementsCanGet(path.targetHero, path.heroArmy, upgrader);
|
||||
}
|
||||
|
||||
if(armyValue < 0.1f || upgrade.upgradeValue < 300) // avoid small upgrades
|
||||
auto armyValue = (float)upgrade.upgradeValue / path.getHeroStrength();
|
||||
|
||||
if(armyValue < 0.25f || upgrade.upgradeValue < 300) // avoid small upgrades
|
||||
{
|
||||
#if NKAI_TRACE_LEVEL >= 2
|
||||
logAi->trace("Ignore path. Army value is too small (%f)", armyValue);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
auto danger = path.getTotalDanger();
|
||||
|
||||
|
@ -115,8 +115,20 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj)
|
||||
{
|
||||
case Obj::TOWN:
|
||||
{
|
||||
const CGTownInstance * cre = dynamic_cast<const CGTownInstance *>(obj);
|
||||
return cre->getUpperArmy()->getArmyStrength();
|
||||
const CGTownInstance * town = dynamic_cast<const CGTownInstance *>(obj);
|
||||
auto danger = town->getUpperArmy()->getArmyStrength();
|
||||
|
||||
if(danger || town->visitingHero)
|
||||
{
|
||||
auto fortLevel = town->fortLevel();
|
||||
|
||||
if(fortLevel == CGTownInstance::EFortLevel::CASTLE)
|
||||
danger += 10000;
|
||||
else if(fortLevel == CGTownInstance::EFortLevel::CITADEL)
|
||||
danger += 4000;
|
||||
}
|
||||
|
||||
return danger;
|
||||
}
|
||||
case Obj::ARTIFACT:
|
||||
case Obj::RESOURCE:
|
||||
|
@ -222,7 +222,7 @@ void Nullkiller::makeTurn()
|
||||
boost::lock_guard<boost::mutex> sharedStorageLock(AISharedStorage::locker);
|
||||
|
||||
const int MAX_DEPTH = 10;
|
||||
const int FAST_TASK_MINIMAL_PRIORITY = 0.7;
|
||||
const float FAST_TASK_MINIMAL_PRIORITY = 0.7;
|
||||
|
||||
resetAiState();
|
||||
|
||||
|
@ -81,6 +81,12 @@ void PriorityEvaluator::initVisitTile()
|
||||
value = engine->getOutputVariable("Value");
|
||||
}
|
||||
|
||||
bool isAnotherAi(const CGObjectInstance * obj, const CPlayerSpecificInfoCallback & cb)
|
||||
{
|
||||
return obj->getOwner().isValidPlayer()
|
||||
&& cb.getStartInfo()->getIthPlayersSettings(obj->getOwner()).isControlledByAI();
|
||||
}
|
||||
|
||||
int32_t estimateTownIncome(CCallback * cb, const CGObjectInstance * target, const CGHeroInstance * hero)
|
||||
{
|
||||
auto relations = cb->getPlayerRelations(hero->tempOwner, target->tempOwner);
|
||||
@ -88,15 +94,17 @@ int32_t estimateTownIncome(CCallback * cb, const CGObjectInstance * target, cons
|
||||
if(relations != PlayerRelations::ENEMIES)
|
||||
return 0; // if we already own it, no additional reward will be received by just visiting it
|
||||
|
||||
auto booster = isAnotherAi(target, *cb) ? 1 : 2;
|
||||
|
||||
auto town = cb->getTown(target->id);
|
||||
auto fortLevel = town->fortLevel();
|
||||
|
||||
if(town->hasCapitol()) return 4000;
|
||||
if(town->hasCapitol()) return booster * 2000;
|
||||
|
||||
// probably well developed town will have city hall
|
||||
if(fortLevel == CGTownInstance::CASTLE) return 1500;
|
||||
if(fortLevel == CGTownInstance::CASTLE) return booster * 750;
|
||||
|
||||
return town->hasFort() && town->tempOwner != PlayerColor::NEUTRAL ? 1000 : 500;
|
||||
return booster * (town->hasFort() && town->tempOwner != PlayerColor::NEUTRAL ? booster * 500 : 250);
|
||||
}
|
||||
|
||||
TResources getCreatureBankResources(const CGObjectInstance * target, const CGHeroInstance * hero)
|
||||
@ -247,11 +255,12 @@ uint64_t RewardEvaluator::getArmyReward(
|
||||
{
|
||||
auto town = dynamic_cast<const CGTownInstance *>(target);
|
||||
auto fortLevel = town->fortLevel();
|
||||
auto booster = isAnotherAi(town, *ai->cb) ? 1 : 2;
|
||||
|
||||
if(fortLevel < CGTownInstance::CITADEL)
|
||||
return town->hasFort() ? 1000 : 0;
|
||||
return town->hasFort() ? booster * 500 : 0;
|
||||
else
|
||||
return fortLevel == CGTownInstance::CASTLE ? 10000 : 4000;
|
||||
return booster * (fortLevel == CGTownInstance::CASTLE ? 5000 : 2000);
|
||||
}
|
||||
|
||||
case Obj::HILL_FORT:
|
||||
@ -395,13 +404,14 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons
|
||||
|
||||
auto town = dynamic_cast<const CGTownInstance *>(target);
|
||||
auto fortLevel = town->fortLevel();
|
||||
auto booster = isAnotherAi(town, *ai->cb) ? 0.3 : 1;
|
||||
|
||||
if(town->hasCapitol()) return 1;
|
||||
|
||||
if(fortLevel < CGTownInstance::CITADEL)
|
||||
return town->hasFort() ? 0.6 : 0.4;
|
||||
return booster * (town->hasFort() ? 0.6 : 0.4);
|
||||
else
|
||||
return fortLevel == CGTownInstance::CASTLE ? 0.9 : 0.8;
|
||||
return booster * (fortLevel == CGTownInstance::CASTLE ? 0.9 : 0.8);
|
||||
}
|
||||
|
||||
case Obj::HERO:
|
||||
@ -579,6 +589,7 @@ public:
|
||||
uint64_t upgradeValue = armyUpgrade.getUpgradeValue();
|
||||
|
||||
evaluationContext.armyReward += upgradeValue;
|
||||
evaluationContext.strategicalValue += upgradeValue / armyUpgrade.hero->getTotalStrength();
|
||||
}
|
||||
};
|
||||
|
||||
@ -590,8 +601,7 @@ void addTileDanger(EvaluationContext & evaluationContext, const int3 & tile, uin
|
||||
{
|
||||
auto dangerRatio = enemyDanger.danger / (double)ourStrength;
|
||||
auto enemyHero = evaluationContext.evaluator.ai->cb->getObj(enemyDanger.hero.hid, false);
|
||||
bool isAI =enemyHero
|
||||
&& evaluationContext.evaluator.ai->cb->getStartInfo()->getIthPlayersSettings(enemyHero->getOwner()).isControlledByAI();
|
||||
bool isAI = enemyHero && isAnotherAi(enemyHero, *evaluationContext.evaluator.ai->cb);
|
||||
|
||||
if(isAI)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user