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))); 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; continue;
ClusterMap::accessor cluster; ClusterMap::accessor cluster;
@ -490,7 +492,9 @@ void ObjectClusterizer::clusterizeObject(
float priority = priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj))); 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; continue;
bool interestingObject = path.turn() <= 2 || priority > 0.5f; bool interestingObject = path.turn() <= 2 || priority > 0.5f;

View File

@ -1113,36 +1113,71 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
double result = 0; double result = 0;
try bool useFuzzy = ai->settings->isUseFuzzy();
if (task->hero)
{ {
armyLossPersentageVariable->setValue(evaluationContext.armyLossPersentage); if (task->hero->getOwner().getNum() > 1)
heroRoleVariable->setValue(evaluationContext.heroRole); useFuzzy = true;
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)
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 #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(), task->toString(),
evaluationContext.armyLossPersentage, evaluationContext.armyLossPersentage,
(int)evaluationContext.turn, (int)evaluationContext.turn,
@ -1151,6 +1186,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
goldRewardPerTurn, goldRewardPerTurn,
evaluationContext.goldCost, evaluationContext.goldCost,
evaluationContext.armyReward, evaluationContext.armyReward,
evaluationContext.skillReward,
evaluationContext.danger, evaluationContext.danger,
evaluationContext.heroRole == HeroRole::MAIN ? "main" : "scout", evaluationContext.heroRole == HeroRole::MAIN ? "main" : "scout",
evaluationContext.strategicalValue, evaluationContext.strategicalValue,

View File

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

View File

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

View File

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