AI worker improvements (#12153)

* AI behaviour changes

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update ConstructionAutomation.kt

* Update Automation.kt

* Reverting some changes

* Changes

* revert changes

* revert changes

* revert changes

* revert changes

* Update CityLocationTileRanker.kt

* Citizen assignment for stat conversion

* Update CityLocationTileRanker.kt

* Reduce AI settling

* Avoid AI building units when in negative Supply

* Update CityLocationTileRanker.kt

* Update CityLocationTileRanker.kt

* Update CityLocationTileRanker.kt

* Update ConstructionAutomation.kt

* Update build.gradle.kts

* Update gradle-wrapper.properties

* Update CityLocationTileRanker.kt

* Update CityLocationTileRanker.kt

* Update ConstructionAutomation.kt

* Update CityLocationTileRanker.kt

* AI changes for humans

* Fix puppet focus

* Update Automation.kt

* Puppet focus

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Stats.kt

* Update CityTurnManager.kt

* Remove specialist science modifier

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update CivilianUnitAutomation.kt

* Update ReligionAutomation.kt

* Worker prioritization

Workers are valuable in expand cities.

* Update ConstructionAutomation.kt

Food always important, it's rarely good to skip e.g. granary if we're on 6 pop.

* Update ConstructionAutomation.kt

Should achieve about the same with less lines of code.

* Update Automation.kt

* Update ConstructionAutomation.kt

* Update Policies.json

* Update Policies.json

* Update Policies.json

* Update ConstructionAutomation.kt

* Update Policies.json

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Rename Crop Yield to Growth

* Update worker usage
This commit is contained in:
EmperorPinguin 2024-08-24 21:04:24 +02:00 committed by GitHub
parent e8565b0b6f
commit 1009669126
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 33 additions and 36 deletions

View File

@ -470,16 +470,16 @@ object Automation {
fun rankStatsValue(stats: Stats, civInfo: Civilization): Float {
var rank = 0.0f
rank += if (stats.food <= 2)
(stats.food * 1.2f) //food get more value to keep city growing
else
(2.4f + (stats.food - 2) / 2) // 1.2 point for each food up to 2, from there on half a point
rank += stats.food * 1.2f //food get more value to keep city growing
rank += if (civInfo.gold < 0 && civInfo.stats.statsForNextTurn.gold <= 0)
stats.gold
stats.gold //build more gold infrastructure if in serious gold problems
// This could lead to oscilliatory behaviour however: gold problem -> build trade post -> no gold problem -> replace trade posts -> gold problem
else
stats.gold / 3 // 3 gold is much worse than 2 production
stats.gold / 3 // Gold is valued less than is the case for citizen assignment,
//otherwise the AI would replace tiles with trade posts upon entering a golden age,
//and replace the trade post again when the golden age ends.
// We need a way to take golden age gold into account before the GA actually takes place
rank += stats.happiness
rank += stats.production
rank += stats.science

View File

@ -165,6 +165,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
private fun addMilitaryUnitChoice() {
if (!isAtWar && !cityIsOverAverageProduction) return // don't make any military units here. Infrastructure first!
// There is a risk however, that these cities run out of things to build, and start to construct nothing
if (civInfo.stats.getUnitSupplyDeficit() > 0) return // we don't want more units if it's already hurting our empire
// todo: add worker disbandment and consumption of great persons if under attack & short on unit supply
if (!isAtWar && (civInfo.stats.statsForNextTurn.gold < 0 || militaryUnits > max(7, cities * 5))) return
@ -244,8 +245,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
}.filterBuildable()
if (workerEquivalents.none()) return // for mods with no worker units
// Dedicate a worker for the first 5 cities, from then on only build another worker for every 2 cities.
val numberOfWorkersWeWant = if (cities <= 5) cities else 5 + (cities - 5 / 2)
// Dedicate 1.5 workers for the first 5 cities, from then on only build one worker for every city.
val numberOfWorkersWeWant = if (cities <= 5) (cities * 1.5f) else 7.5f + ((cities - 5))
if (workers < numberOfWorkersWeWant) {
val modifier = numberOfWorkersWeWant / (workers + 0.4f) // The worse our worker to city ratio is, the more desperate we are
@ -266,7 +267,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
val localUniqueCache = LocalUniqueCache()
for (building in buildings.filterBuildable()) {
if (building.isWonder && city.isPuppet) continue
// We shouldn't try to build wonders in undeveloped empires
// We shouldn't try to build wonders in undeveloped cities and empires
if (building.isWonder && !cityIsOverAverageProduction)
if (building.isWonder && civInfo.cities.size < 3) continue
addChoice(relativeCostEffectiveness, building.name, getValueOfBuilding(building, localUniqueCache))
}

View File

@ -528,9 +528,6 @@ object NextTurnAutomation {
if (civInfo.isCityState) return
if (civInfo.isOneCityChallenger()) return
if (civInfo.isAtWar()) return // don't train settlers when you could be training troops.
if (civInfo.wantsToFocusOn(Victory.Focus.Culture) && civInfo.cities.size > 3 &&
civInfo.getPersonality().isNeutralPersonality)
return
if (civInfo.cities.none()) return
if (civInfo.getHappiness() <= civInfo.cities.size) return

View File

@ -102,9 +102,8 @@ object CivilianUnitAutomation {
return
}
// Great engineer -> Try to speed up wonder construction if late game
if (isLateGame &&
(unit.hasUnique(UniqueType.CanSpeedupConstruction)
// Great engineer -> Try to speed up wonder construction
if ((unit.hasUnique(UniqueType.CanSpeedupConstruction)
|| unit.hasUnique(UniqueType.CanSpeedupWonderConstruction))) {
val wonderCanBeSpedUpEventually = SpecificUnitAutomation.speedupWonderConstruction(unit)
if (wonderCanBeSpedUpEventually)

View File

@ -18,6 +18,7 @@ import com.unciv.models.ruleset.tile.Terrain
import com.unciv.models.ruleset.tile.TileImprovement
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.stats.Stat
import com.unciv.models.stats.Stats
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActions
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsFromUniques
@ -74,8 +75,8 @@ class WorkerAutomation(
val currentTile = unit.getTile()
// Must be called before any getPriority checks to guarantee the local road cache is processed
val citiesToConnect = roadBetweenCitiesAutomation.getNearbyCitiesToConnect(unit)
// Shortcut, we are working a good tile (like resource) and don't need to check for other tiles to work
if (!dangerousTiles.contains(currentTile) && getFullPriority(unit.getTile(), unit, localUniqueCache) >= 10
// Shortcut, we are working a suitable tile, and we're better off minimizing worker-turns by finishing everything on this tile
if (!dangerousTiles.contains(currentTile) && getFullPriority(unit.getTile(), unit, localUniqueCache) >= 2
&& currentTile.improvementInProgress != null) {
return
}
@ -160,7 +161,7 @@ class WorkerAutomation(
}
// Nothing to do, try again to connect cities
if (civInfo.stats.statsForNextTurn.gold > 10 && roadBetweenCitiesAutomation.tryConnectingCities(unit, citiesToConnect)) return
if (roadBetweenCitiesAutomation.tryConnectingCities(unit, citiesToConnect)) return
debug("WorkerAutomation: %s -> nothing to do", unit.toString())
@ -231,24 +232,22 @@ class WorkerAutomation(
if (tile.providesYield()) priority += 2
if (tile.isPillaged()) priority += 1
if (tile.hasFalloutEquivalent()) priority += 1
if (tile.terrainFeatures.isNotEmpty() && tile.lastTerrain.hasUnique("Provides a one-time Production bonus to the closest city when cut down")) priority += 1 // removing our forests is good for tempo
if (tile.terrainHasUnique(UniqueType.FreshWater)) priority += 1 // we want our farms up when unlocking Civil Service
}
// give a minor priority to tiles that we could expand onto
else if (tile.getOwner() == null && tile.neighbors.any { it.getOwner() == civInfo })
priority += 1
if (priority <= 0 && tile.hasViewableResource(civInfo)) {
if (tile.hasViewableResource(civInfo)) {
priority += 1
// New Resources are great!
if (tile.tileResource.resourceType != ResourceType.Bonus
&& !civInfo.hasResource(tile.resource!!))
priority += 2
if (tile.tileResource.resourceType == ResourceType.Luxury) priority += 3
//luxuries are more important than other types of resources
}
if (tile in roadBetweenCitiesAutomation.tilesOfRoadsMap) priority += when {
civInfo.stats.statsForNextTurn.gold <= 5 -> 0
civInfo.stats.statsForNextTurn.gold <= 10 -> 1
civInfo.stats.statsForNextTurn.gold <= 30 -> 2
else -> 3
}
if (tile in roadBetweenCitiesAutomation.tilesOfRoadsMap)
priority += 3
tileRankings[tile] = TileImprovementRank(priority)
return priority + unitSpecificPriority
}
@ -357,9 +356,10 @@ class WorkerAutomation(
// After gathering all the data, we conduct the hierarchy in one place
val improvementString = when {
bestBuildableImprovement != null && bestBuildableImprovement.isRoad() -> bestBuildableImprovement.name
improvementStringForResource != null -> if (improvementStringForResource==tile.improvement) null else improvementStringForResource
// For bonus resources we just want the highest-yield improvement, not necessarily the resource-yielding improvement
improvementStringForResource != null && tile.tileResource.resourceType != ResourceType.Bonus -> if (improvementStringForResource==tile.improvement) null else improvementStringForResource
// If this is a resource that HAS an improvement that we can see, but this unit can't build it, don't waste your time
tile.resource != null && tile.hasViewableResource(civInfo) && tile.tileResource.getImprovements().any() -> return null
tile.resource != null && tile.hasViewableResource(civInfo) && tile.tileResource.resourceType != ResourceType.Bonus && tile.tileResource.getImprovements().any() -> return null
bestBuildableImprovement == null -> null
tile.improvement != null &&
@ -386,10 +386,7 @@ class WorkerAutomation(
if (improvement.isRoad() && roadBetweenCitiesAutomation.bestRoadAvailable.improvement(ruleSet) == improvement
&& tile in roadBetweenCitiesAutomation.tilesOfRoadsMap) {
val roadPlan = roadBetweenCitiesAutomation.tilesOfRoadsMap[tile]!!
var value = roadPlan.priority
if (civInfo.stats.statsForNextTurn.gold >= 20)
// Higher priority if we are closer to connecting the city
value += (5 - roadPlan.numberOfRoadsToBuild).coerceAtLeast(0)
val value = (roadPlan.priority - 5) // We want some forest chopping and farm building first if the road doesn't have high priority
return value
}
@ -424,6 +421,8 @@ class WorkerAutomation(
stats.add(statDiff)
// Take into account that the resource might be improved by the *final* improvement
isResourceImprovedByNewImprovement = newTile.resource != null && newTile.tileResource.isImprovedBy(wantedFinalImprovement.name)
if (tile.terrainFeatures.isNotEmpty() && tile.lastTerrain.hasUnique("Provides a one-time Production bonus to the closest city when cut down"))
stats.add(Stat.Production, 0.5f) //We're gaining tempo by chopping the forest, adding an imaginary yield per turn is a way to correct for this
}
}