Removed ALL hardcoded decisions in worker AI - we're now fully moddable! (#6003)

I tested this out with RekMOD, 150 simulated turns, to see if this was working.
Not only did I discover that workers were squatting on Antiquity Sites since they had resource improvements that they couldn't build, I also discovered something much worse.
Even after fixing that, most cities were woefully underimproved.
Turns out, the construction automation would limit worker construction to a measly *3 workers* even for a *10 city* civ!
After removing this limitation and making civs aim for a 1:1 ratio between cities and workers, everything started looking much, much better.
I'm not sure what the exact effect on the AI will be but I'm _sure_ that this leads to a major improvement. More improved tiles means more stats means more everything.
This commit is contained in:
Yair Morgenstern 2022-01-22 18:47:39 +02:00 committed by GitHub
parent feb9b19d11
commit 080fc245d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 26 additions and 25 deletions

View File

@ -168,12 +168,9 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
&& it.isBuildable(cityConstructions)
&& Automation.allowSpendingResource(civInfo, it) }
if (workerEquivalents.isEmpty()) return // for mods with no worker units
if (civInfo.getIdleUnits().any { it.isAutomated() && it.hasUniqueToBuildImprovements })
return // If we have automated workers who have no work to do then it's silly to construct new workers.
val citiesCountedTowardsWorkers = min(5, cities) // above 5 cities, extra cities won't make us want more workers
if (workers < citiesCountedTowardsWorkers * 0.6f && civUnits.none { it.hasUniqueToBuildImprovements && it.isIdle() }) {
var modifier = citiesCountedTowardsWorkers / (workers + 0.1f)
if (workers < cities) {
var modifier = cities / (workers + 0.1f) // The worse our worker to city ratio is, the more desperate we are
if (!cityIsOverAverageProduction) modifier /= 5 // higher production cities will deal with this
addChoice(relativeCostEffectiveness, workerEquivalents.minByOrNull { it.cost }!!.name, modifier)
}

View File

@ -333,15 +333,21 @@ class WorkerAutomation(
private fun chooseImprovement(unit: MapUnit, tile: TileInfo): TileImprovement? {
// turnsToBuild is what defines them as buildable
val tileImprovements = ruleSet.tileImprovements.filter {
it.value.turnsToBuild != 0 && tile.canImprovementBeBuiltHere(it.value, tile.hasViewableResource(civInfo)) }
val uniqueImprovement = tileImprovements.values
.firstOrNull { it.uniqueTo == civInfo.civName }
val potentialTileImprovements = ruleSet.tileImprovements.filter {
unit.canBuildImprovement(it.value, tile)
&& tile.canImprovementBeBuiltHere(it.value, tile.hasViewableResource(civInfo))
&& (it.value.uniqueTo == null || it.value.uniqueTo == unit.civInfo.civName)
}
if (potentialTileImprovements.isEmpty()) return null
val currentlyBuildableImprovements = tileImprovements.values.filter { tile.canBuildImprovement(it, civInfo) }
val bestBuildableImprovement = currentlyBuildableImprovements.map { Pair(it, Automation.rankStatsValue(it, civInfo)) }
.filter { it.second > 0f }
.maxByOrNull { it.second }?.first
val uniqueImprovement = potentialTileImprovements.values.asSequence()
.filter { it.uniqueTo == civInfo.civName }
.maxByOrNull { Automation.rankStatsValue(it, unit.civInfo) }
val bestBuildableImprovement = potentialTileImprovements.values.asSequence()
.map { Pair(it, Automation.rankStatsValue(it, civInfo)) }
.filter { it.second > 0f }
.maxByOrNull { it.second }?.first
val lastTerrain = tile.getLastTerrain()
@ -358,7 +364,12 @@ class WorkerAutomation(
val improvementString = when {
tile.improvementInProgress != null -> tile.improvementInProgress!!
improvementStringForResource != null && tileImprovements.containsKey(improvementStringForResource) -> improvementStringForResource
improvementStringForResource != null -> {
if (potentialTileImprovements.containsKey(improvementStringForResource))
improvementStringForResource
// if this is a resource that HAS an improvement, but this unit can't build it, don't waste your time
else return null
}
tile.containsGreatImprovement() -> return null
tile.containsUnfinishedGreatImprovement() -> return null
@ -367,20 +378,12 @@ class WorkerAutomation(
!civInfo.isPlayerCivilization() && evaluateFortPlacement(tile, civInfo,false) -> Constants.fort
// I think we can assume that the unique improvement is better
uniqueImprovement != null && tile.canBuildImprovement(uniqueImprovement, civInfo)
&& unit.canBuildImprovement(uniqueImprovement, tile) ->
uniqueImprovement.name
-> uniqueImprovement.name
lastTerrain.let {
isUnbuildableAndRemovable(it) &&
(Automation.rankStatsValue(it, civInfo) < 0 || it.hasUnique(UniqueType.NullifyYields) )
} -> Constants.remove + lastTerrain.name
tile.terrainFeatures.contains(Constants.jungle) -> Constants.tradingPost
tile.terrainFeatures.contains("Oasis") -> return null
tile.terrainFeatures.contains(Constants.forest) && tileImprovements.containsKey("Lumber mill") -> "Lumber mill"
tile.isHill() && tileImprovements.containsKey("Mine") -> "Mine"
tile.baseTerrain in listOf(Constants.grassland, Constants.desert, Constants.plains)
&& tileImprovements.containsKey("Farm") -> "Farm"
tile.isAdjacentToFreshwater && tileImprovements.containsKey("Farm") -> "Farm"
bestBuildableImprovement != null -> bestBuildableImprovement.name
else -> return null

View File

@ -1115,8 +1115,9 @@ class MapUnit {
&& improvement.name != Constants.cancelImprovementOrder
&& tile.improvementInProgress != improvement.name
) return false
val matchingUniques = getMatchingUniques(UniqueType.BuildImprovements)
return matchingUniques.any { improvement.matchesFilter(it.params[0]) || tile.matchesTerrainFilter(it.params[0]) }
return getMatchingUniques(UniqueType.BuildImprovements)
.any { improvement.matchesFilter(it.params[0]) || tile.matchesTerrainFilter(it.params[0]) }
}
fun getReligionDisplayName(): String? {