mirror of
https://github.com/vcmi/vcmi.git
synced 2025-02-03 13:01:33 +02:00
Nullkiller: stabilization+clasterization improvements+fuzzy fear
This commit is contained in:
parent
75b8ee74fa
commit
32fb465823
@ -99,6 +99,12 @@ uint64_t DangerHitMapAnalyzer::enemyCanKillOurHeroesAlongThePath(const AIPath &
|
||||
const HitMapNode & DangerHitMapAnalyzer::getObjectTreat(const CGObjectInstance * obj) const
|
||||
{
|
||||
auto tile = obj->visitablePos();
|
||||
|
||||
return getTileTreat(tile);
|
||||
}
|
||||
|
||||
const HitMapNode & DangerHitMapAnalyzer::getTileTreat(const int3 & tile) const
|
||||
{
|
||||
const HitMapNode & info = hitMap[tile.x][tile.y][tile.z];
|
||||
|
||||
return info;
|
||||
|
@ -51,6 +51,7 @@ public:
|
||||
void updateHitMap();
|
||||
uint64_t enemyCanKillOurHeroesAlongThePath(const AIPath & path) const;
|
||||
const HitMapNode & getObjectTreat(const CGObjectInstance * obj) const;
|
||||
const HitMapNode & getTileTreat(const int3 & tile) const;
|
||||
const std::set<const CGObjectInstance *> & getOneTurnAccessibleObjects(const CGHeroInstance * enemy) const;
|
||||
void reset();
|
||||
};
|
||||
|
@ -92,7 +92,7 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons
|
||||
{
|
||||
auto guardPos = ai->cb->getGuardingCreaturePosition(node->coord);
|
||||
auto blockers = ai->cb->getVisitableObjs(node->coord);
|
||||
|
||||
|
||||
if(guardPos.valid())
|
||||
{
|
||||
auto guard = ai->cb->getTopObj(ai->cb->getGuardingCreaturePosition(node->coord));
|
||||
@ -103,6 +103,16 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons
|
||||
}
|
||||
}
|
||||
|
||||
if(node->specialAction && node->actionIsBlocked)
|
||||
{
|
||||
auto blockerObject = node->specialAction->targetObject();
|
||||
|
||||
if(blockerObject)
|
||||
{
|
||||
blockers.push_back(blockerObject);
|
||||
}
|
||||
}
|
||||
|
||||
if(blockers.empty())
|
||||
continue;
|
||||
|
||||
@ -113,7 +123,8 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons
|
||||
|| blocker->ID == Obj::GARRISON2
|
||||
|| blocker->ID == Obj::BORDERGUARD
|
||||
|| blocker->ID == Obj::QUEST_GUARD
|
||||
|| blocker->ID == Obj::BORDER_GATE)
|
||||
|| blocker->ID == Obj::BORDER_GATE
|
||||
|| blocker->ID == Obj::SHIPYARD)
|
||||
{
|
||||
if(!isObjectPassable(blocker))
|
||||
return blocker;
|
||||
@ -201,9 +212,15 @@ void ObjectClusterizer::clusterize()
|
||||
|
||||
bool added = false;
|
||||
bool directlyAccessible = false;
|
||||
std::set<const CGHeroInstance *> heroesProcessed;
|
||||
|
||||
for(auto & path : paths)
|
||||
{
|
||||
if(vstd::contains(heroesProcessed, path.targetHero))
|
||||
continue;
|
||||
|
||||
heroesProcessed.insert(path.targetHero);
|
||||
|
||||
if(path.nodes.size() > 1)
|
||||
{
|
||||
auto blocker = getBlocker(path);
|
||||
@ -236,10 +253,13 @@ void ObjectClusterizer::clusterize()
|
||||
|
||||
if(!added || directlyAccessible)
|
||||
{
|
||||
if(paths.front().turn() <= 2)
|
||||
nearObjects.addObject(obj, paths.front(), 0);
|
||||
AIPath & shortestPath = paths.front();
|
||||
float priority = ai->priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(shortestPath, obj)));
|
||||
|
||||
if(shortestPath.turn() <= 2 || priority > 0.6f)
|
||||
nearObjects.addObject(obj, shortestPath, 0);
|
||||
else
|
||||
farObjects.addObject(obj, paths.front(), 0);
|
||||
farObjects.addObject(obj, shortestPath, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ uint64_t townArmyIncome(const CGTownInstance * town)
|
||||
|
||||
void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const
|
||||
{
|
||||
auto basicPriority = 0.3f + std::sqrt(townArmyIncome(town) / 20000.0f)
|
||||
auto basicPriority = 0.3f + std::sqrt(townArmyIncome(town) / 40000.0f)
|
||||
+ town->dailyIncome()[Res::GOLD] / 10000.0f;
|
||||
|
||||
logAi->debug("Evaluating defence for %s, basic priority %f", town->name, basicPriority);
|
||||
@ -124,7 +124,8 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
{
|
||||
if(path.turn() <= treat.turn && dayOfWeek + treat.turn < 6 && isSafeToVisit(path.targetHero, path.heroArmy, treat.danger)
|
||||
|| path.exchangeCount == 1 && path.turn() < treat.turn
|
||||
|| path.turn() < treat.turn - 1)
|
||||
|| path.turn() < treat.turn - 1
|
||||
|| path.turn() < treat.turn && treat.turn >= 2)
|
||||
{
|
||||
logAi->debug(
|
||||
"Hero %s can eliminate danger for town %s using path %s.",
|
||||
@ -194,6 +195,9 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<Goals::ExecuteHeroChain> pathsToDefend;
|
||||
std::map<const CGHeroInstance *, std::vector<AIPath>> defferedPaths;
|
||||
|
||||
for(AIPath & path : paths)
|
||||
{
|
||||
#if AI_TRACE_LEVEL >= 1
|
||||
@ -211,11 +215,16 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
town->name,
|
||||
path.targetHero->name);
|
||||
|
||||
defferedPaths[path.targetHero].push_back(path);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
float priority = basicPriority
|
||||
+ std::min(SAFE_ATTACK_CONSTANT, (float)path.getHeroStrength() / treat.danger) / (treat.turn + 1);
|
||||
float priority = basicPriority * std::min(SAFE_ATTACK_CONSTANT, (float)path.getHeroStrength() / treat.danger)
|
||||
- treat.turn * 0.2f;
|
||||
|
||||
if(treat.turn < path.turn())
|
||||
priority /= (path.turn() - treat.turn) * 2;
|
||||
|
||||
if(path.targetHero == town->visitingHero && path.exchangeCount == 1)
|
||||
{
|
||||
@ -231,7 +240,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
continue;
|
||||
}
|
||||
|
||||
if(path.turn() <= treat.turn && path.getHeroStrength() * SAFE_ATTACK_CONSTANT >= treat.danger)
|
||||
if(treat.turn == 0 || path.turn() <= treat.turn && path.getHeroStrength() * SAFE_ATTACK_CONSTANT >= treat.danger)
|
||||
{
|
||||
if(ai->nullkiller->arePathHeroesLocked(path))
|
||||
{
|
||||
@ -245,27 +254,33 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
||||
continue;
|
||||
}
|
||||
|
||||
pathsToDefend.push_back(Goals::ExecuteHeroChain(path, town).setpriority(priority));
|
||||
}
|
||||
}
|
||||
|
||||
for(Goals::ExecuteHeroChain & chain : pathsToDefend)
|
||||
{
|
||||
auto path = chain.getPath();
|
||||
|
||||
for(AIPath & defferedPath : defferedPaths[path.targetHero])
|
||||
{
|
||||
if(defferedPath.getHeroStrength() >= path.getHeroStrength()
|
||||
&& defferedPath.turn() <= path.turn())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#if AI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Move %s to defend town %s with priority %f",
|
||||
path.targetHero->name,
|
||||
town->name,
|
||||
priority);
|
||||
logAi->trace("Move %s to defend town %s with priority %f",
|
||||
path.targetHero->name,
|
||||
town->name,
|
||||
chain.priority);
|
||||
#endif
|
||||
|
||||
tasks.push_back(Goals::sptr(Goals::ExecuteHeroChain(path, town).setpriority(priority)));
|
||||
}
|
||||
tasks.push_back(Goals::sptr(chain));
|
||||
}
|
||||
}
|
||||
|
||||
logAi->debug("Found %d tasks", tasks.size());
|
||||
|
||||
/*for(auto & treat : treats)
|
||||
{
|
||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(treat.hero->visitablePos());
|
||||
|
||||
for(AIPath & path : paths)
|
||||
{
|
||||
tasks.push_back(Goals::sptr(Goals::ExecuteHeroChain(path)));
|
||||
}
|
||||
}*/
|
||||
}
|
@ -90,7 +90,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
|
||||
if(path.getFirstBlockedAction())
|
||||
{
|
||||
#if AI_TRACE_LEVEL >= 2
|
||||
// TODO: decomposition?
|
||||
// TODO: decomposition
|
||||
logAi->trace("Ignore path. Action is blocked.");
|
||||
#endif
|
||||
continue;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "StartupBehavior.h"
|
||||
#include "../VCAI.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../Goals/BuildThis.h"
|
||||
#include "../Goals/RecruitHero.h"
|
||||
#include "../Goals/ExecuteHeroChain.h"
|
||||
#include "../Goals/ExchangeSwapTownHeroes.h"
|
||||
@ -123,6 +124,14 @@ Goals::TGoalVec StartupBehavior::decompose() const
|
||||
});
|
||||
}
|
||||
|
||||
if(!startupTown->hasBuilt(BuildingID::TAVERN)
|
||||
&& cb->canBuildStructure(startupTown, BuildingID::TAVERN) == EBuildingState::ALLOWED)
|
||||
{
|
||||
tasks.push_back(Goals::sptr(Goals::BuildThis(BuildingID::TAVERN, startupTown).setpriority(100)));
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
bool canRecruitHero = needToRecruitHero(startupTown);
|
||||
auto closestHero = getNearestHero(startupTown);
|
||||
|
||||
|
@ -43,7 +43,8 @@ EvaluationContext::EvaluationContext(const Nullkiller * ai)
|
||||
heroRole(HeroRole::SCOUT),
|
||||
turn(0),
|
||||
strategicalValue(0),
|
||||
evaluator(ai)
|
||||
evaluator(ai),
|
||||
enemyHeroDangerRatio(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -71,6 +72,7 @@ void PriorityEvaluator::initVisitTile()
|
||||
strategicalValueVariable = engine->getInputVariable("strategicalValue");
|
||||
goldPreasureVariable = engine->getInputVariable("goldPreasure");
|
||||
goldCostVariable = engine->getInputVariable("goldCost");
|
||||
fearVariable = engine->getInputVariable("fear");
|
||||
value = engine->getOutputVariable("Value");
|
||||
}
|
||||
|
||||
@ -381,6 +383,19 @@ float RewardEvaluator::getSkillReward(const CGObjectInstance * target, const CGH
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t RewardEvaluator::getEnemyHeroDanger(const AIPath & path) const
|
||||
{
|
||||
auto & treatNode = ai->dangerHitMap->getTileTreat(path.targetTile());
|
||||
|
||||
if(treatNode.maximumDanger.danger == 0)
|
||||
return 0;
|
||||
|
||||
if(treatNode.maximumDanger.turn <= path.turn())
|
||||
return treatNode.maximumDanger.danger;
|
||||
|
||||
return treatNode.fastestDanger.turn <= path.turn() ? treatNode.fastestDanger.danger : 0;
|
||||
}
|
||||
|
||||
int32_t getArmyCost(const CArmedInstance * army)
|
||||
{
|
||||
int32_t value = 0;
|
||||
@ -485,6 +500,7 @@ public:
|
||||
evaluationContext.skillReward += evaluationContext.evaluator.getSkillReward(target, hero, evaluationContext.heroRole);
|
||||
evaluationContext.strategicalValue += evaluationContext.evaluator.getStrategicalValue(target);
|
||||
evaluationContext.goldCost += evaluationContext.evaluator.getGoldCost(target, hero, army);
|
||||
vstd::amax(evaluationContext.enemyHeroDangerRatio, evaluationContext.evaluator.getEnemyHeroDanger(path) / (double)path.getHeroStrength());
|
||||
vstd::amax(evaluationContext.turn, path.turn());
|
||||
}
|
||||
};
|
||||
@ -659,6 +675,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
|
||||
goldPreasureVariable->setValue(ai->buildAnalyzer->getGoldPreasure());
|
||||
goldCostVariable->setValue(evaluationContext.goldCost / ((float)cb->getResourceAmount(Res::GOLD) + (float)ai->buildAnalyzer->getDailyIncome()[Res::GOLD] + 1.0f));
|
||||
turnVariable->setValue(evaluationContext.turn);
|
||||
fearVariable->setValue(evaluationContext.enemyHeroDangerRatio);
|
||||
|
||||
engine->process();
|
||||
//engine.process(VISIT_TILE); //TODO: Process only Visit_Tile
|
||||
@ -671,7 +688,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
|
||||
assert(result >= 0);
|
||||
|
||||
#ifdef AI_TRACE_LEVEL >= 1
|
||||
logAi->trace("Evaluated %s, loss: %f, turn: %d, turns main: %f, scout: %f, gold: %d, cost: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, cwr: %f, result %f",
|
||||
logAi->trace("Evaluated %s, loss: %f, turn: %d, turns main: %f, scout: %f, gold: %d, cost: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, cwr: %f, fear: %f, result %f",
|
||||
task->toString(),
|
||||
evaluationContext.armyLossPersentage,
|
||||
(int)evaluationContext.turn,
|
||||
@ -684,6 +701,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
|
||||
evaluationContext.heroRole == HeroRole::MAIN ? "main" : "scout",
|
||||
evaluationContext.strategicalValue,
|
||||
evaluationContext.closestWayRatio,
|
||||
evaluationContext.enemyHeroDangerRatio,
|
||||
result);
|
||||
#endif
|
||||
|
||||
|
@ -30,6 +30,7 @@ public:
|
||||
float getSkillReward(const CGObjectInstance * target, const CGHeroInstance * hero, HeroRole role) const;
|
||||
int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero) const;
|
||||
uint64_t getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const;
|
||||
uint64_t getEnemyHeroDanger(const AIPath & path) const;
|
||||
};
|
||||
|
||||
struct DLL_EXPORT EvaluationContext
|
||||
@ -48,6 +49,7 @@ struct DLL_EXPORT EvaluationContext
|
||||
HeroRole heroRole;
|
||||
uint8_t turn;
|
||||
RewardEvaluator evaluator;
|
||||
float enemyHeroDangerRatio;
|
||||
|
||||
EvaluationContext(const Nullkiller * ai);
|
||||
};
|
||||
@ -87,6 +89,7 @@ private:
|
||||
fl::InputVariable * closestHeroRatioVariable;
|
||||
fl::InputVariable * goldPreasureVariable;
|
||||
fl::InputVariable * goldCostVariable;
|
||||
fl::InputVariable * fearVariable;
|
||||
fl::OutputVariable * value;
|
||||
std::vector<std::shared_ptr<IEvaluationContextBuilder>> evaluationContextBuilders;
|
||||
|
||||
|
@ -92,7 +92,7 @@ void ExecuteHeroChain::accept(VCAI * ai)
|
||||
}
|
||||
}
|
||||
|
||||
if(node.turns == 0 && node.coord != hero->visitablePos())
|
||||
if(node.turns == 0 && node.coord != hero->visitablePos())
|
||||
{
|
||||
auto targetNode = cb->getPathsInfo(hero)->getPathInfo(node.coord);
|
||||
|
||||
|
@ -36,32 +36,6 @@ namespace AIPathfinding
|
||||
return sptr(Goals::Invalid());
|
||||
}
|
||||
|
||||
const ChainActor * BuildBoatAction::getActor(const ChainActor * sourceActor) const
|
||||
{
|
||||
return sourceActor->resourceActor;
|
||||
}
|
||||
|
||||
void SummonBoatAction::execute(const CGHeroInstance * hero) const
|
||||
{
|
||||
Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT).accept(ai.get());
|
||||
}
|
||||
|
||||
const ChainActor * SummonBoatAction::getActor(const ChainActor * sourceActor) const
|
||||
{
|
||||
return sourceActor->castActor;
|
||||
}
|
||||
|
||||
void SummonBoatAction::applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
AIPathNode * dstMode,
|
||||
const AIPathNode * srcNode) const
|
||||
{
|
||||
dstMode->manaCost = srcNode->manaCost + getManaCost(hero);
|
||||
dstMode->theNodeBefore = source.node;
|
||||
}
|
||||
|
||||
bool BuildBoatAction::canAct(const AIPathNode * source) const
|
||||
{
|
||||
auto hero = source->actor->hero;
|
||||
@ -90,6 +64,37 @@ namespace AIPathfinding
|
||||
return true;
|
||||
}
|
||||
|
||||
const CGObjectInstance * BuildBoatAction::targetObject() const
|
||||
{
|
||||
return shipyard->o;
|
||||
}
|
||||
|
||||
const ChainActor * BuildBoatAction::getActor(const ChainActor * sourceActor) const
|
||||
{
|
||||
return sourceActor->resourceActor;
|
||||
}
|
||||
|
||||
void SummonBoatAction::execute(const CGHeroInstance * hero) const
|
||||
{
|
||||
Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT).accept(ai.get());
|
||||
}
|
||||
|
||||
const ChainActor * SummonBoatAction::getActor(const ChainActor * sourceActor) const
|
||||
{
|
||||
return sourceActor->castActor;
|
||||
}
|
||||
|
||||
void SummonBoatAction::applyOnDestination(
|
||||
const CGHeroInstance * hero,
|
||||
CDestinationNodeInfo & destination,
|
||||
const PathNodeInfo & source,
|
||||
AIPathNode * dstMode,
|
||||
const AIPathNode * srcNode) const
|
||||
{
|
||||
dstMode->manaCost = srcNode->manaCost + getManaCost(hero);
|
||||
dstMode->theNodeBefore = source.node;
|
||||
}
|
||||
|
||||
std::string BuildBoatAction::toString() const
|
||||
{
|
||||
return "Build Boat at " + shipyard->o->getObjectName();
|
||||
|
@ -67,5 +67,7 @@ namespace AIPathfinding
|
||||
virtual const ChainActor * getActor(const ChainActor * sourceActor) const override;
|
||||
|
||||
virtual std::string toString() const override;
|
||||
|
||||
virtual const CGObjectInstance * targetObject() const override;
|
||||
};
|
||||
}
|
@ -37,4 +37,6 @@ public:
|
||||
}
|
||||
|
||||
virtual std::string toString() const = 0;
|
||||
|
||||
virtual const CGObjectInstance * targetObject() const { return nullptr; }
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user