From 7b407b6432570c4eb1a5f246d0c1e7095ba52483 Mon Sep 17 00:00:00 2001 From: Xilmi Date: Sun, 7 Jul 2024 22:44:52 +0200 Subject: [PATCH] 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". --- AI/Nullkiller/Analyzers/ObjectClusterizer.cpp | 8 +- AI/Nullkiller/Engine/PriorityEvaluator.cpp | 84 +++++++++++++------ AI/Nullkiller/Engine/Settings.cpp | 8 +- AI/Nullkiller/Engine/Settings.h | 2 + config/ai/nkai/nkai-settings.json | 2 +- 5 files changed, 76 insertions(+), 28 deletions(-) diff --git a/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp b/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp index 7b9607390..5c4bb4cea 100644 --- a/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp +++ b/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp @@ -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; diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index f29153c20..73d77623a 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -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, diff --git a/AI/Nullkiller/Engine/Settings.cpp b/AI/Nullkiller/Engine/Settings.cpp index db4e3f455..3631ce816 100644 --- a/AI/Nullkiller/Engine/Settings.cpp +++ b/AI/Nullkiller/Engine/Settings.cpp @@ -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(); diff --git a/AI/Nullkiller/Engine/Settings.h b/AI/Nullkiller/Engine/Settings.h index 775f7f399..b0ec08b0d 100644 --- a/AI/Nullkiller/Engine/Settings.h +++ b/AI/Nullkiller/Engine/Settings.h @@ -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; } }; } diff --git a/config/ai/nkai/nkai-settings.json b/config/ai/nkai/nkai-settings.json index f597be497..1e757dee3 100644 --- a/config/ai/nkai/nkai-settings.json +++ b/config/ai/nkai/nkai-settings.json @@ -6,5 +6,5 @@ "maxGoldPressure" : 0.3, "useTroopsFromGarrisons" : true, "openMap": true, - "allowObjectGraph": true + "allowObjectGraph": false } \ No newline at end of file