1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +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; 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) SecondarySkillScoreMap::SecondarySkillScoreMap(std::map<SecondarySkill, float> scoreMap)
:scoreMap(scoreMap) :scoreMap(scoreMap)
{ {

View File

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

View File

@@ -121,7 +121,9 @@ bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoa
return true; return true;
} }
if(!town->visitingHero && cb->getHeroCount(ai->playerID, false) < GameConstants::MAX_HEROES_PER_PLAYER) if(!town->visitingHero)
{
if(cb->getHeroCount(ai->playerID, false) < GameConstants::MAX_HEROES_PER_PLAYER)
{ {
logAi->trace( logAi->trace(
"Extracting hero %s from garrison of town %s", "Extracting hero %s from garrison of town %s",
@@ -132,6 +134,19 @@ bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoa
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; return false;
} }
@@ -141,14 +156,6 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
logAi->trace("Evaluating defence for %s", town->getNameTranslated()); logAi->trace("Evaluating defence for %s", town->getNameTranslated());
auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town); 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); std::vector<HitMapInfo> treats = ai->nullkiller->dangerHitMap->getTownTreats(town);
treats.push_back(treatNode.fastestDanger); // no guarantee that fastest danger will be there treats.push_back(treatNode.fastestDanger); // no guarantee that fastest danger will be there
@@ -158,6 +165,13 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
return; 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); uint64_t reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
if(reinforcement) if(reinforcement)
@@ -224,11 +238,6 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
if(path.getHeroStrength() < townDefenseStrength) if(path.getHeroStrength() < townDefenseStrength)
continue; continue;
} }
else
{
if(town->visitingHero)
continue;
}
if(path.turn() <= treat.turn - 2) if(path.turn() <= treat.turn - 2)
{ {
@@ -440,27 +449,10 @@ void DefenceBehavior::evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitM
} }
else if(ai->nullkiller->heroManager->heroCapReached()) else if(ai->nullkiller->heroManager->heroCapReached())
{ {
const CGHeroInstance * weakestHero = nullptr; heroToDismiss = ai->nullkiller->heroManager->findWeakHeroToDismiss(hero->getArmyStrength());
for(auto existingHero : myHeroes) if(!heroToDismiss)
{
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; continue;
if(!weakestHero || weakestHero->getFightingStrength() > existingHero->getFightingStrength())
{
weakestHero = existingHero;
}
}
if(!weakestHero)
continue;
heroToDismiss = weakestHero;
} }
TGoalVec sequence; TGoalVec sequence;

View File

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