1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-14 10:12:59 +02:00

AI-variant without fuzzy-logic

It is now possible to switch to an AI-variant that uses hand-written heuristics for decision-making rather than the FuzzyLite-engine. This is configurable in nkai-settings.json via the new parameter "useFuzzy".
This commit is contained in:
Xilmi 2024-07-07 22:44:52 +02:00
parent aa891cb8b1
commit 7b407b6432
5 changed files with 76 additions and 28 deletions

View File

@ -469,7 +469,9 @@ void ObjectClusterizer::clusterizeObject(
float priority = priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
if(priority < MIN_PRIORITY)
if(ai->settings->isUseFuzzy() && priority < MIN_PRIORITY)
continue;
else if (priority <= 0)
continue;
ClusterMap::accessor cluster;
@ -490,7 +492,9 @@ void ObjectClusterizer::clusterizeObject(
float priority = priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
if(priority < MIN_PRIORITY)
if (ai->settings->isUseFuzzy() && priority < MIN_PRIORITY)
continue;
else if (priority <= 0)
continue;
bool interestingObject = path.turn() <= 2 || priority > 0.5f;

View File

@ -1113,36 +1113,71 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
double result = 0;
try
bool useFuzzy = ai->settings->isUseFuzzy();
if (task->hero)
{
armyLossPersentageVariable->setValue(evaluationContext.armyLossPersentage);
heroRoleVariable->setValue(evaluationContext.heroRole);
mainTurnDistanceVariable->setValue(evaluationContext.movementCostByRole[HeroRole::MAIN]);
scoutTurnDistanceVariable->setValue(evaluationContext.movementCostByRole[HeroRole::SCOUT]);
goldRewardVariable->setValue(goldRewardPerTurn);
armyRewardVariable->setValue(evaluationContext.armyReward);
armyGrowthVariable->setValue(evaluationContext.armyGrowth);
skillRewardVariable->setValue(evaluationContext.skillReward);
dangerVariable->setValue(evaluationContext.danger);
rewardTypeVariable->setValue(rewardType);
closestHeroRatioVariable->setValue(evaluationContext.closestWayRatio);
strategicalValueVariable->setValue(evaluationContext.strategicalValue);
goldPressureVariable->setValue(ai->buildAnalyzer->getGoldPressure());
goldCostVariable->setValue(evaluationContext.goldCost / ((float)ai->getFreeResources()[EGameResID::GOLD] + (float)ai->buildAnalyzer->getDailyIncome()[EGameResID::GOLD] + 1.0f));
turnVariable->setValue(evaluationContext.turn);
fearVariable->setValue(evaluationContext.enemyHeroDangerRatio);
engine->process();
result = value->getValue();
if (task->hero->getOwner().getNum() > 1)
useFuzzy = true;
}
catch(fl::Exception & fe)
if (useFuzzy)
{
logAi->error("evaluate VisitTile: %s", fe.getWhat());
try
{
armyLossPersentageVariable->setValue(evaluationContext.armyLossPersentage);
heroRoleVariable->setValue(evaluationContext.heroRole);
mainTurnDistanceVariable->setValue(evaluationContext.movementCostByRole[HeroRole::MAIN]);
scoutTurnDistanceVariable->setValue(evaluationContext.movementCostByRole[HeroRole::SCOUT]);
goldRewardVariable->setValue(goldRewardPerTurn);
armyRewardVariable->setValue(evaluationContext.armyReward);
armyGrowthVariable->setValue(evaluationContext.armyGrowth);
skillRewardVariable->setValue(evaluationContext.skillReward);
dangerVariable->setValue(evaluationContext.danger);
rewardTypeVariable->setValue(rewardType);
closestHeroRatioVariable->setValue(evaluationContext.closestWayRatio);
strategicalValueVariable->setValue(evaluationContext.strategicalValue);
goldPressureVariable->setValue(ai->buildAnalyzer->getGoldPressure());
goldCostVariable->setValue(evaluationContext.goldCost / ((float)ai->getFreeResources()[EGameResID::GOLD] + (float)ai->buildAnalyzer->getDailyIncome()[EGameResID::GOLD] + 1.0f));
turnVariable->setValue(evaluationContext.turn);
fearVariable->setValue(evaluationContext.enemyHeroDangerRatio);
engine->process();
result = value->getValue();
}
catch (fl::Exception& fe)
{
logAi->error("evaluate VisitTile: %s", fe.getWhat());
}
}
else
{
float score = evaluationContext.armyReward + evaluationContext.skillReward * 2000 + std::max((float)evaluationContext.goldReward, std::max((float)evaluationContext.armyGrowth, evaluationContext.strategicalValue * 1000));
if (task->hero)
{
score -= evaluationContext.armyLossPersentage * task->hero->getArmyCost();
if (evaluationContext.enemyHeroDangerRatio > 1)
score /= evaluationContext.enemyHeroDangerRatio;
}
if (score > 0)
{
result = score * evaluationContext.closestWayRatio / evaluationContext.movementCost;
if (task->hero)
{
if (task->hero->getArmyCost() > score
&& evaluationContext.strategicalValue == 0)
result /= task->hero->getArmyCost() / score;
//logAi->trace("Score %s: %f Armyreward: %f skillReward: %f GoldReward: %f Strategical: %f Armygrowth: %f", task->toString(), score, evaluationContext.armyReward, evaluationContext.skillReward, evaluationContext.goldReward, evaluationContext.strategicalValue, evaluationContext.armyGrowth);
logAi->trace("Score %s: %f Cost: %f Dist: %f Armygrowth: %f Prio: %f", task->toString(), score, task->hero->getArmyCost(), evaluationContext.movementCost, evaluationContext.armyGrowth, result);
}
}
}
#if NKAI_TRACE_LEVEL >= 2
logAi->trace("Evaluated %s, loss: %f, turn: %d, turns main: %f, scout: %f, gold: %f, cost: %d, army gain: %f, danger: %d, role: %s, strategical value: %f, cwr: %f, fear: %f, result %f",
logAi->trace("Evaluated %s, loss: %f, turn: %d, turns main: %f, scout: %f, gold: %f, cost: %d, army gain: %f, skill: %f danger: %d, role: %s, strategical value: %f, cwr: %f, fear: %f, result %f",
task->toString(),
evaluationContext.armyLossPersentage,
(int)evaluationContext.turn,
@ -1151,6 +1186,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
goldRewardPerTurn,
evaluationContext.goldCost,
evaluationContext.armyReward,
evaluationContext.skillReward,
evaluationContext.danger,
evaluationContext.heroRole == HeroRole::MAIN ? "main" : "scout",
evaluationContext.strategicalValue,

View File

@ -30,7 +30,8 @@ namespace NKAI
maxpass(10),
allowObjectGraph(true),
useTroopsFromGarrisons(false),
openMap(true)
openMap(true),
useFuzzy(false)
{
JsonNode node = JsonUtils::assembleFromFiles("config/ai/nkai/nkai-settings");
@ -69,6 +70,11 @@ namespace NKAI
openMap = node.Struct()["openMap"].Bool();
}
if (!node.Struct()["useFuzzy"].isNull())
{
useFuzzy = node.Struct()["useFuzzy"].Bool();
}
if(!node.Struct()["useTroopsFromGarrisons"].isNull())
{
useTroopsFromGarrisons = node.Struct()["useTroopsFromGarrisons"].Bool();

View File

@ -29,6 +29,7 @@ namespace NKAI
bool allowObjectGraph;
bool useTroopsFromGarrisons;
bool openMap;
bool useFuzzy;
public:
Settings();
@ -41,5 +42,6 @@ namespace NKAI
bool isObjectGraphAllowed() const { return allowObjectGraph; }
bool isGarrisonTroopsUsageAllowed() const { return useTroopsFromGarrisons; }
bool isOpenMap() const { return openMap; }
bool isUseFuzzy() const { return useFuzzy; }
};
}

View File

@ -6,5 +6,5 @@
"maxGoldPressure" : 0.3,
"useTroopsFromGarrisons" : true,
"openMap": true,
"allowObjectGraph": true
"allowObjectGraph": false
}