From 7a0e4a254976767aac9c9a83ac8784378ff34b91 Mon Sep 17 00:00:00 2001 From: itanasi <44038014+itanasi@users.noreply.github.com> Date: Wed, 6 Nov 2024 04:40:28 -0800 Subject: [PATCH] Avoid Growth and Food Ranking Improvements (#12356) * Code changes * Clean up code for clarity and suggestions * More commenting to clarify * Remove redundant -1 * Put back weights into Stats Add in small floating values for tie breaker preferences --- .../com/unciv/logic/automation/Automation.kt | 52 +++++++++++++------ core/src/com/unciv/logic/city/CityFocus.kt | 2 +- core/src/com/unciv/models/stats/Stats.kt | 4 +- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/core/src/com/unciv/logic/automation/Automation.kt b/core/src/com/unciv/logic/automation/Automation.kt index 89ec69260b..f945dcda05 100644 --- a/core/src/com/unciv/logic/automation/Automation.kt +++ b/core/src/com/unciv/logic/automation/Automation.kt @@ -21,6 +21,8 @@ import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.stats.Stat import com.unciv.models.stats.Stats import com.unciv.ui.screens.victoryscreen.RankingType +import kotlin.math.min + object Automation { @@ -64,33 +66,51 @@ object Automation { } val surplusFood = city.cityStats.currentCityStats[Stat.Food] + val starving = surplusFood < 0 // If current Production converts Food into Production, then calculate increased Production Yield if (cityStatsObj.canConvertFoodToProduction(surplusFood, city.cityConstructions.getCurrentConstruction())) { // calculate delta increase of food->prod. This isn't linear yieldStats.production += cityStatsObj.getProductionFromExcessiveFood(surplusFood+yieldStats.food) - cityStatsObj.getProductionFromExcessiveFood(surplusFood) yieldStats.food = 0f // all food goes to 0 } + + // Split Food Yield into feedFood, amount needed to not Starve + // and growthFood, any amount above that + var feedFood = 0f + if (starving) + feedFood = min(yieldStats.food, -surplusFood).coerceAtLeast(0f) + var growthFood = yieldStats.food - feedFood // how much extra Food we yield + // Avoid Growth, only count Food that gets you not-starving, but no more + if (city.avoidGrowth) { + growthFood = 0f + } + yieldStats.food = 1f + // Apply base weights yieldStats.applyRankingWeights() - if (surplusFood > 0 && city.avoidGrowth) { - yieldStats.food = 0f // don't need more food! - } else if (cityAIFocus in CityFocus.zeroFoodFocuses) { + val foodBaseWeight = yieldStats.food + + // If starving, need Food, so feedFood > 0 + // scale feedFood by 14(base weight)*8(super important) + // By only scaling what we need to reach Not Starving by x8, we can pick a tile that gives + // exactly as much Food as we need to Not Starve that also has other good yields instead of + // always picking the Highest Food tile until Not Starving + yieldStats.food = feedFood * (foodBaseWeight * 8) + // growthFood is any additional food not required to meet Starvation + if (cityAIFocus in CityFocus.zeroFoodFocuses) { // Focus on non-food/growth - if (surplusFood < 0) - yieldStats.food *= 8 // Starving, need Food, get to 0 - else if (city.civ.getHappiness() < 1) - yieldStats.food /= 4 - } else if (!city.avoidGrowth) { + // Reduce excess food focus to prevent Happiness spiral + if (city.civ.getHappiness() < 1) + yieldStats.food += growthFood * (foodBaseWeight / 4) + } else { // NoFocus or Food/Growth Focus. - if (surplusFood < 0) - yieldStats.food *= 8 // Starving, need Food, get to 0 - else if (city.civ.getHappiness() > -1) - yieldStats.food *= 2 //1.5f is preferred, but 2 provides more protection against badly configured personalities - else if (city.civ.getHappiness() < 0) { - // 75% of excess food is wasted when in negative happiness - yieldStats.food /= 4 - } + // When Happy, EmperorPenguin has run sims comparing weights + // 1.5f is preferred, + // but 2 provides more protection against badly configured personalities + // If unhappy, see above + val growthFoodScaling = if (city.civ.getHappiness() >= 0) foodBaseWeight * 2 else foodBaseWeight / 4 + yieldStats.food += growthFood * growthFoodScaling } if (city.population.population < 10) { diff --git a/core/src/com/unciv/logic/city/CityFocus.kt b/core/src/com/unciv/logic/city/CityFocus.kt index 9108394e17..110c81254b 100644 --- a/core/src/com/unciv/logic/city/CityFocus.kt +++ b/core/src/com/unciv/logic/city/CityFocus.kt @@ -62,7 +62,7 @@ enum class CityFocus( KeyboardBinding.None open fun getStatMultiplier(stat: Stat) = when (this.stat) { - stat -> 3f + stat -> 3.05f // on ties, prefer the Focus else -> 1f } diff --git a/core/src/com/unciv/models/stats/Stats.kt b/core/src/com/unciv/models/stats/Stats.kt index 2e122edd3d..3d24ddfe41 100644 --- a/core/src/com/unciv/models/stats/Stats.kt +++ b/core/src/com/unciv/models/stats/Stats.kt @@ -147,9 +147,9 @@ open class Stats( * Apply weighting for Production Ranking */ fun applyRankingWeights() { food *= 14 - production *= 12 + production *= 12.01f // tie break Production vs gold gold *= 6 // 2 gold worth about 1 production - science *= 9 + science *= 9.01f // 4 Science better than 3 Production culture *= 8 happiness *= 10 // base faith *= 7