1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-25 21:38:59 +02:00

nkai: fix freezes

This commit is contained in:
Andrii Danylchenko 2023-07-28 14:17:01 +03:00
parent c93bb0a502
commit 6490c65490
5 changed files with 78 additions and 45 deletions

View File

@ -229,6 +229,31 @@ const CGHeroInstance * HeroManager::findHeroWithGrail() const
return nullptr;
}
const CGHeroInstance * HeroManager::findWeakHeroToDismiss(uint64_t armyLimit) const
{
const CGHeroInstance * weakestHero = nullptr;
auto myHeroes = ai->cb->getHeroesInfo();
for(auto existingHero : myHeroes)
{
if(ai->isHeroLocked(existingHero)
|| existingHero->getArmyStrength() >armyLimit
|| getHeroRole(existingHero) == HeroRole::MAIN
|| existingHero->movementPointsRemaining()
|| existingHero->artifactsWorn.size() > (existingHero->hasSpellbook() ? 2 : 1))
{
continue;
}
if(!weakestHero || weakestHero->getFightingStrength() > existingHero->getFightingStrength())
{
weakestHero = existingHero;
}
}
return weakestHero;
}
SecondarySkillScoreMap::SecondarySkillScoreMap(std::map<SecondarySkill, float> scoreMap)
:scoreMap(scoreMap)
{

View File

@ -33,6 +33,7 @@ public:
virtual bool canRecruitHero(const CGTownInstance * t = nullptr) const = 0;
virtual bool heroCapReached() const = 0;
virtual const CGHeroInstance * findHeroWithGrail() const = 0;
virtual const CGHeroInstance * findWeakHeroToDismiss(uint64_t armyLimit) const = 0;
};
class DLL_EXPORT ISecondarySkillRule
@ -74,6 +75,7 @@ public:
bool canRecruitHero(const CGTownInstance * t = nullptr) const override;
bool heroCapReached() const override;
const CGHeroInstance * findHeroWithGrail() const override;
const CGHeroInstance * findWeakHeroToDismiss(uint64_t armyLimit) const override;
private:
float evaluateFightingStrength(const CGHeroInstance * hero) const;

View File

@ -121,16 +121,31 @@ bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoa
return true;
}
if(!town->visitingHero && cb->getHeroCount(ai->playerID, false) < GameConstants::MAX_HEROES_PER_PLAYER)
if(!town->visitingHero)
{
logAi->trace(
"Extracting hero %s from garrison of town %s",
town->garrisonHero->getNameTranslated(),
town->getNameTranslated());
if(cb->getHeroCount(ai->playerID, false) < GameConstants::MAX_HEROES_PER_PLAYER)
{
logAi->trace(
"Extracting hero %s from garrison of town %s",
town->garrisonHero->getNameTranslated(),
town->getNameTranslated());
tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, nullptr).setpriority(5)));
tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, nullptr).setpriority(5)));
return true;
return true;
}
else if(ai->nullkiller->heroManager->getHeroRole(town->garrisonHero.get()) == HeroRole::MAIN)
{
auto armyDismissLimit = 1000;
auto heroToDismiss = ai->nullkiller->heroManager->findWeakHeroToDismiss(armyDismissLimit);
if(heroToDismiss)
{
tasks.push_back(Goals::sptr(Goals::DismissHero(heroToDismiss).setpriority(5)));
return true;
}
}
}
return false;
@ -141,14 +156,6 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
logAi->trace("Evaluating defence for %s", town->getNameTranslated());
auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
if(!treatNode.fastestDanger.hero)
{
logAi->trace("No treat found for town %s", town->getNameTranslated());
return;
}
std::vector<HitMapInfo> treats = ai->nullkiller->dangerHitMap->getTownTreats(town);
treats.push_back(treatNode.fastestDanger); // no guarantee that fastest danger will be there
@ -157,6 +164,13 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
{
return;
}
if(!treatNode.fastestDanger.hero)
{
logAi->trace("No treat found for town %s", town->getNameTranslated());
return;
}
uint64_t reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
@ -224,11 +238,6 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
if(path.getHeroStrength() < townDefenseStrength)
continue;
}
else
{
if(town->visitingHero)
continue;
}
if(path.turn() <= treat.turn - 2)
{
@ -440,27 +449,10 @@ void DefenceBehavior::evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitM
}
else if(ai->nullkiller->heroManager->heroCapReached())
{
const CGHeroInstance * weakestHero = nullptr;
heroToDismiss = ai->nullkiller->heroManager->findWeakHeroToDismiss(hero->getArmyStrength());
for(auto existingHero : myHeroes)
{
if(ai->nullkiller->isHeroLocked(existingHero)
|| existingHero->getArmyStrength() > hero->getArmyStrength()
|| ai->nullkiller->heroManager->getHeroRole(existingHero) == HeroRole::MAIN
|| existingHero->movementPointsRemaining()
|| existingHero->artifactsWorn.size() > (existingHero->hasSpellbook() ? 2 : 1))
continue;
if(!weakestHero || weakestHero->getFightingStrength() > existingHero->getFightingStrength())
{
weakestHero = existingHero;
}
}
if(!weakestHero)
if(!heroToDismiss)
continue;
heroToDismiss = weakestHero;
}
TGoalVec sequence;

View File

@ -592,7 +592,6 @@ int32_t getArmyCost(const CArmedInstance * army)
return value;
}
/// Gets aproximated reward in gold. Daily income is multiplied by 5
int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero) const
{
if(!target)
@ -713,9 +712,6 @@ public:
auto strategicalValue = evaluationContext.evaluator.getStrategicalValue(town);
if(evaluationContext.evaluator.ai->buildAnalyzer->getDevelopmentInfo().size() == 1)
vstd::amax(evaluationContext.strategicalValue, 10.0);
float multiplier = 1;
if(treat.turn < defendTown.getTurn())
@ -736,7 +732,12 @@ public:
evaluationContext.armyGrowth += armyGrowth * multiplier;
evaluationContext.goldReward += dailyIncome * 5 * multiplier;
evaluationContext.addNonCriticalStrategicalValue(1.7f * multiplier * strategicalValue);
if(evaluationContext.evaluator.ai->buildAnalyzer->getDevelopmentInfo().size() == 1)
vstd::amax(evaluationContext.strategicalValue, 2.5f * multiplier * strategicalValue);
else
evaluationContext.addNonCriticalStrategicalValue(1.7f * multiplier * strategicalValue);
vstd::amax(evaluationContext.danger, defendTown.getTreat().danger);
addTileDanger(evaluationContext, town->visitablePos(), defendTown.getTurn(), defendTown.getDefenceStrength());
}

View File

@ -171,6 +171,10 @@ RuleBlock: basic
rule: if heroRole is SCOUT and turn is NEXT and mainTurnDistance is LONG then Value is BAD
rule: if heroRole is SCOUT and turn is NOW and scoutTurnDistance is LONG then Value is BAD
rule: if heroRole is SCOUT and turn is NOW and scoutTurnDistance is MEDIUM then Value is BAD with 0.3
rule: if heroRole is SCOUT and fear is HIGH then Value is BAD with 0.8
rule: if heroRole is SCOUT and fear is MEDIUM then Value is BAD with 0.5
rule: if heroRole is MAIN and fear is HIGH then Value is BAD with 0.5
rule: if heroRole is MAIN and fear is MEDIUM then Value is BAD with 0.2
RuleBlock: strategicalValue
enabled: true
conjunction: AlgebraicProduct
@ -205,7 +209,8 @@ RuleBlock: strategicalValue
rule: if heroRole is SCOUT and strategicalValue is LOW and danger is NONE and scoutTurnDistance is MEDIUM and fear is not HIGH then Value is SMALL
rule: if armyLoss is HIGH and strategicalValue is LOW then Value is BAD
rule: if armyLoss is HIGH and strategicalValue is MEDIUM then Value is BAD with 0.7
rule: if strategicalValue is CRITICAL then Value is CRITICAL
rule: if strategicalValue is CRITICAL and heroRole is MAIN then Value is CRITICAL
rule: if strategicalValue is CRITICAL and heroRole is SCOUT then Value is CRITICAL with 0.7
RuleBlock: armyReward
enabled: true
conjunction: AlgebraicProduct
@ -220,6 +225,14 @@ RuleBlock: armyReward
rule: if heroRole is MAIN and armyReward is MEDIUM and mainTurnDistance is LONG and fear is not HIGH then Value is MEDIUM
rule: if heroRole is MAIN and armyReward is LOW and mainTurnDistance is LOW and fear is not HIGH then Value is MEDIUM
rule: if heroRole is MAIN and armyReward is LOW and mainTurnDistance is MEDIUM and fear is not HIGH then Value is SMALL
rule: if heroRole is SCOUT and armyReward is HIGH and danger is NONE and scoutTurnDistance is LOW and fear is not HIGH then Value is HIGH
rule: if heroRole is SCOUT and armyReward is HIGH and danger is NONE and scoutTurnDistance is MEDIUM and fear is not HIGH then Value is HIGH with 0.7
rule: if heroRole is SCOUT and armyReward is HIGH and danger is NONE and scoutTurnDistance is LONG and fear is not HIGH then Value is BITHIGH
rule: if heroRole is SCOUT and armyReward is MEDIUM and danger is NONE and scoutTurnDistance is LOW then Value is HIGH with 0.7
rule: if heroRole is SCOUT and armyReward is MEDIUM and danger is NONE and scoutTurnDistance is MEDIUM and fear is not HIGH then Value is BITHIGH
rule: if heroRole is SCOUT and armyReward is MEDIUM and danger is NONE and scoutTurnDistance is LONG and fear is not HIGH then Value is MEDIUM
rule: if heroRole is SCOUT and armyReward is LOW and danger is NONE and scoutTurnDistance is LOW and fear is not HIGH then Value is MEDIUM
rule: if heroRole is SCOUT and armyReward is LOW and danger is NONE and scoutTurnDistance is MEDIUM and fear is not HIGH then Value is SMALL
RuleBlock: gold
enabled: true
conjunction: AlgebraicProduct