1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +02:00

Nullkiller AI: new prioritization engine

This commit is contained in:
Andrii Danylchenko 2021-05-16 14:13:40 +03:00 committed by Andrii Danylchenko
parent 66ed1a2901
commit b261734905
7 changed files with 55 additions and 11 deletions

View File

@ -97,7 +97,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
continue;
auto hero = path.targetHero;
auto danger = path.getTotalDanger(hero);
auto danger = path.getTotalDanger();
if(danger == 0 && path.exchangeCount > 1)
continue;

View File

@ -206,6 +206,14 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
path.movementCost(),
path.toString());
#endif
if(path.turn() <= treat.turn - 2)
{
logAi->trace("Deffer defence of %s by %s because he has enough time to rich the town next trun",
town->name,
path.targetHero->name);
continue;
}
float priority = basicPriority
+ std::min(SAFE_ATTACK_CONSTANT, (float)path.getHeroStrength() / treat.danger) / (treat.turn + 1);

View File

@ -26,9 +26,9 @@ using namespace Goals;
ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj)
:CGoal(Goals::EXECUTE_HERO_CHAIN), chainPath(path)
{
evaluationContext.danger = path.getTotalDanger(hero);
evaluationContext.danger = path.getTotalDanger();
evaluationContext.movementCost = path.movementCost();
evaluationContext.armyLoss = path.armyLoss;
evaluationContext.armyLoss = path.getTotalArmyLoss();
evaluationContext.heroStrength = path.getHeroStrength();
hero = path.targetHero;
tile = path.targetTile();
@ -112,7 +112,28 @@ void ExecuteHeroChain::accept(VCAI * ai)
}
}
Goals::VisitTile(node.coord).sethero(hero).accept(ai);
try
{
Goals::VisitTile(node.coord).sethero(hero).accept(ai);
}
catch(cannotFulfillGoalException)
{
if(hero->movement > 0)
{
CGPath path;
bool isOk = cb->getPathsInfo(hero.get())->getPath(path, node.coord);
if(isOk && path.nodes.back().turns > 0)
{
logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero.name, hero->movement, node.coord.toString());
ai->nullkiller->lockHero(hero.get());
return;
}
}
throw;
}
}
if(node.turns == 0)

View File

@ -875,6 +875,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
path.heroArmy = node.actor->creatureSet;
path.armyLoss = node.armyLoss;
path.targetObjectDanger = evaluateDanger(pos, path.targetHero);
path.targetObjectArmyLoss = evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), path.targetObjectDanger);
path.chainMask = node.actor->chainMask;
path.exchangeCount = node.actor->actorExchangeCount;
@ -985,7 +986,7 @@ uint64_t AIPath::getHeroStrength() const
return targetHero->getFightingStrength() * heroArmy->getArmyStrength();
}
uint64_t AIPath::getTotalDanger(HeroPtr hero) const
uint64_t AIPath::getTotalDanger() const
{
uint64_t pathDanger = getPathDanger();
uint64_t danger = pathDanger > targetObjectDanger ? pathDanger : targetObjectDanger;
@ -993,6 +994,11 @@ uint64_t AIPath::getTotalDanger(HeroPtr hero) const
return danger;
}
uint64_t AIPath::getTotalArmyLoss() const
{
return armyLoss + targetObjectArmyLoss;
}
std::string AIPath::toString()
{
std::stringstream str;

View File

@ -49,6 +49,7 @@ struct AIPath
std::shared_ptr<const ISpecialAction> specialAction;
uint64_t targetObjectDanger;
uint64_t armyLoss;
uint64_t targetObjectArmyLoss;
const CGHeroInstance * targetHero;
const CCreatureSet * heroArmy;
uint64_t chainMask;
@ -60,7 +61,10 @@ struct AIPath
uint64_t getPathDanger() const;
/// Gets danger of path including danger of visiting the target object like creature bank
uint64_t getTotalDanger(HeroPtr hero) const;
uint64_t getTotalDanger() const;
/// Gets danger of path including danger of visiting the target object like creature bank
uint64_t getTotalArmyLoss() const;
int3 firstTileToGet() const;
int3 targetTile() const;
@ -168,6 +172,13 @@ public:
return dangerEvaluator->evaluateDanger(tile, hero, ai);
}
uint64_t evaluateArmyLoss(const CGHeroInstance * hero, uint64_t armyValue, uint64_t danger) const
{
double ratio = (double)danger / (armyValue * hero->getFightingStrength());
return (uint64_t)(armyValue * ratio * ratio * ratio);
}
private:
STRONG_INLINE
void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility);

View File

@ -154,7 +154,7 @@ Goals::TGoalVec PathfindingManager::findPaths(
#endif
if(ai->isTileNotReserved(hero.get(), firstTileToGet))
{
danger = path.getTotalDanger(hero);
danger = path.getTotalDanger();
if(isSafeToVisit(hero, path.heroArmy, danger))
{
@ -178,7 +178,7 @@ Goals::TGoalVec PathfindingManager::findPaths(
solution->evaluationContext.danger = danger;
solution->evaluationContext.movementCost += path.movementCost();
solution->evaluationContext.armyLoss += path.armyLoss;
solution->evaluationContext.armyLoss += path.getTotalArmyLoss();
solution->evaluationContext.heroStrength = path.getHeroStrength();
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace("It's safe for %s to visit tile %s with danger %s, loss %s, army strength %s, goal %s",

View File

@ -163,9 +163,7 @@ namespace AIPathfinding
auto hero = nodeStorage->getHero(source.node);
auto danger = nodeStorage->evaluateDanger(destination.coord, hero);
double actualArmyValue = srcNode->actor->armyValue - srcNode->armyLoss;
double ratio = (double)danger / (actualArmyValue * hero->getFightingStrength());
uint64_t loss = (uint64_t)(actualArmyValue * ratio * ratio * ratio);
double loss = nodeStorage->evaluateArmyLoss(hero, actualArmyValue, danger);
if(loss < actualArmyValue)
{