From 8c293ce156110f0b1a5ed58c55fddae3a57b1114 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 29 Nov 2024 15:57:23 +0000 Subject: [PATCH] First attempt to make per-difficulty configurations for AI --- AI/Nullkiller/Engine/PriorityEvaluator.cpp | 3 +- AI/Nullkiller/Engine/Settings.cpp | 1 + AI/Nullkiller/Engine/Settings.h | 2 + config/ai/nkai/nkai-settings.json | 92 +++++++++++++++------- 4 files changed, 70 insertions(+), 28 deletions(-) diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index 92faf4bd8..840f5052f 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -1349,7 +1349,8 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier) else { float score = 0; - float maxWillingToLose = ai->cb->getTownsInfo().empty() || (evaluationContext.isDefend && evaluationContext.threatTurns == 0) ? 1 : 0.25; + const bool amIInDanger = ai->cb->getTownsInfo().empty() || (evaluationContext.isDefend && evaluationContext.threatTurns == 0); + const float maxWillingToLose = amIInDanger ? 1 : ai->settings->getMaxArmyLossTarget(); bool arriveNextWeek = false; if (ai->cb->getDate(Date::DAY_OF_WEEK) + evaluationContext.turn > 7) diff --git a/AI/Nullkiller/Engine/Settings.cpp b/AI/Nullkiller/Engine/Settings.cpp index 0e01aaf09..6cc7a5266 100644 --- a/AI/Nullkiller/Engine/Settings.cpp +++ b/AI/Nullkiller/Engine/Settings.cpp @@ -54,6 +54,7 @@ namespace NKAI maxGoldPressure = node["maxGoldPressure"].Float(); retreatThresholdRelative = node["retreatThresholdRelative"].Float(); retreatThresholdAbsolute = node["retreatThresholdAbsolute"].Float(); + maxArmyLossTarget = node["maxArmyLossTarget"].Float(); safeAttackRatio = node["safeAttackRatio"].Float(); allowObjectGraph = node["allowObjectGraph"].Bool(); updateHitmapOnTileReveal = node["updateHitmapOnTileReveal"].Bool(); diff --git a/AI/Nullkiller/Engine/Settings.h b/AI/Nullkiller/Engine/Settings.h index 7e8f9ffbf..ac8b718ec 100644 --- a/AI/Nullkiller/Engine/Settings.h +++ b/AI/Nullkiller/Engine/Settings.h @@ -31,6 +31,7 @@ namespace NKAI float retreatThresholdRelative; float retreatThresholdAbsolute; float safeAttackRatio; + float maxArmyLossTarget; bool allowObjectGraph; bool useTroopsFromGarrisons; bool updateHitmapOnTileReveal; @@ -45,6 +46,7 @@ namespace NKAI float getRetreatThresholdRelative() const { return retreatThresholdRelative; } float getRetreatThresholdAbsolute() const { return retreatThresholdAbsolute; } float getSafeAttackRatio() const { return safeAttackRatio; } + float getMaxArmyLossTarget() const { return maxArmyLossTarget; } int getMaxRoamingHeroes() const { return maxRoamingHeroes; } int getMainHeroTurnDistanceLimit() const { return mainHeroTurnDistanceLimit; } int getScoutHeroTurnDistanceLimit() const { return scoutHeroTurnDistanceLimit; } diff --git a/config/ai/nkai/nkai-settings.json b/config/ai/nkai/nkai-settings.json index b62a62910..cb395e88f 100644 --- a/config/ai/nkai/nkai-settings.json +++ b/config/ai/nkai/nkai-settings.json @@ -1,60 +1,77 @@ { + // "maxRoamingHeroes" - AI will never recruit new heroes above this value. + // Note that AI might end up with more heroes - due to prisons or if he has large number of heroes on start + // + // "maxpass" - ??? + // + // "mainHeroTurnDistanceLimit" - AI will only run pathfinding for specified number of turns for his main hero. + // "scoutHeroTurnDistanceLimit" - AI will only run pathfinding for specified number of turns for his secondary (scout) heroes + // Limiting this will make AI faster, but may result in AI being unable to discover objects outside of this range + // + // "maxGoldPressure" - ??? + // + // "useTroopsFromGarrisons" - AI can take troops from garrisons on map. + // Note that at the moment AI will not deliberately seek out such garrisons, he can only take troops from them when passing through. + // This option is always disabled on H3 RoE campaign maps to be in line with H3 AI + // + // "openMap" - AI will use map reveal cheat if cheats are enabled and AI is not allied with human player + // This improves AI decision making, but may lead AI to deliberately targeting targets that he should not be able to see at the moment + // + // "allowObjectGraph" - if used, AI will build "cache" for pathfinder on first turn, which should make AI faster. Requires openMap. + // + // "pathfinderBucketsCount" - ??? + // "pathfinderBucketSize" - ??? + // + // "retreatThresholdRelative" - AI will consider retreating from battle only if his troops are less than specified ratio compated to enemy + // "retreatThresholdAbsolute" - AI will consider retreating from battle only if total fight value of his troops are less than specified value + // + // "maxArmyLossTarget" - AI will try keep army loss below specified target + // + // "safeAttackRatio" - TODO: figure out how exactly it affects AI decision making + // + // "useFuzzy" - allow using of fuzzy logic. TODO: better description + + "pawn" : { - "maxRoamingHeroes" : 8, + "maxRoamingHeroes" : 3, "maxpass" : 30, "mainHeroTurnDistanceLimit" : 10, "scoutHeroTurnDistanceLimit" : 5, "maxGoldPressure" : 0.3, "updateHitmapOnTileReveal" : false, "useTroopsFromGarrisons" : true, - "openMap": false, + "openMap": true, "allowObjectGraph": false, "pathfinderBucketsCount" : 1, // old value: 3, "pathfinderBucketSize" : 32, // old value: 7, - "retreatThresholdRelative" : 0.3, - "retreatThresholdAbsolute" : 10000, + "retreatThresholdRelative" : 0, + "retreatThresholdAbsolute" : 0, "safeAttackRatio" : 1.1, + "maxArmyLossTarget" : 0.5, "useFuzzy" : false }, "knight" : { - "maxRoamingHeroes" : 8, + "maxRoamingHeroes" : 3, "maxpass" : 30, "mainHeroTurnDistanceLimit" : 10, "scoutHeroTurnDistanceLimit" : 5, "maxGoldPressure" : 0.3, "updateHitmapOnTileReveal" : false, "useTroopsFromGarrisons" : true, - "openMap": false, + "openMap": true, "allowObjectGraph": false, "pathfinderBucketsCount" : 1, // old value: 3, "pathfinderBucketSize" : 32, // old value: 7, - "retreatThresholdRelative" : 0.3, - "retreatThresholdAbsolute" : 10000, + "retreatThresholdRelative" : 0.1, + "retreatThresholdAbsolute" : 5000, "safeAttackRatio" : 1.1, + "maxArmyLossTarget" : 0.35, "useFuzzy" : false }, "rook" : { - "maxRoamingHeroes" : 8, - "maxpass" : 30, - "mainHeroTurnDistanceLimit" : 10, - "scoutHeroTurnDistanceLimit" : 5, - "maxGoldPressure" : 0.3, - "updateHitmapOnTileReveal" : false, - "useTroopsFromGarrisons" : true, - "openMap": false, - "allowObjectGraph": false, - "pathfinderBucketsCount" : 1, // old value: 3, - "pathfinderBucketSize" : 32, // old value: 7, - "retreatThresholdRelative" : 0.3, - "retreatThresholdAbsolute" : 10000, - "safeAttackRatio" : 1.1, - "useFuzzy" : false - }, - - "queen" : { - "maxRoamingHeroes" : 8, + "maxRoamingHeroes" : 4, "maxpass" : 30, "mainHeroTurnDistanceLimit" : 10, "scoutHeroTurnDistanceLimit" : 5, @@ -68,6 +85,26 @@ "retreatThresholdRelative" : 0.3, "retreatThresholdAbsolute" : 10000, "safeAttackRatio" : 1.1, + "maxArmyLossTarget" : 0.25, + "useFuzzy" : false + }, + + "queen" : { + "maxRoamingHeroes" : 6, + "maxpass" : 30, + "mainHeroTurnDistanceLimit" : 10, + "scoutHeroTurnDistanceLimit" : 5, + "maxGoldPressure" : 0.3, + "updateHitmapOnTileReveal" : false, + "useTroopsFromGarrisons" : true, + "openMap": true, + "allowObjectGraph": false, + "pathfinderBucketsCount" : 1, // old value: 3, + "pathfinderBucketSize" : 32, // old value: 7, + "retreatThresholdRelative" : 0.3, + "retreatThresholdAbsolute" : 10000, + "safeAttackRatio" : 1.1, + "maxArmyLossTarget" : 0.25, "useFuzzy" : false }, @@ -86,6 +123,7 @@ "retreatThresholdRelative" : 0.3, "retreatThresholdAbsolute" : 10000, "safeAttackRatio" : 1.1, + "maxArmyLossTarget" : 0.25, "useFuzzy" : false } } \ No newline at end of file