mirror of
https://github.com/yairm210/Unciv.git
synced 2025-03-13 19:39:34 +07:00
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:
parent
e8565b0b6f
commit
1009669126
@ -470,16 +470,16 @@ object Automation {
|
|||||||
|
|
||||||
fun rankStatsValue(stats: Stats, civInfo: Civilization): Float {
|
fun rankStatsValue(stats: Stats, civInfo: Civilization): Float {
|
||||||
var rank = 0.0f
|
var rank = 0.0f
|
||||||
rank += if (stats.food <= 2)
|
rank += stats.food * 1.2f //food get more value to keep city growing
|
||||||
(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 += if (civInfo.gold < 0 && civInfo.stats.statsForNextTurn.gold <= 0)
|
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
|
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.happiness
|
||||||
rank += stats.production
|
rank += stats.production
|
||||||
rank += stats.science
|
rank += stats.science
|
||||||
|
@ -165,6 +165,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
|||||||
|
|
||||||
private fun addMilitaryUnitChoice() {
|
private fun addMilitaryUnitChoice() {
|
||||||
if (!isAtWar && !cityIsOverAverageProduction) return // don't make any military units here. Infrastructure first!
|
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
|
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
|
// 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
|
if (!isAtWar && (civInfo.stats.statsForNextTurn.gold < 0 || militaryUnits > max(7, cities * 5))) return
|
||||||
@ -244,8 +245,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
|||||||
}.filterBuildable()
|
}.filterBuildable()
|
||||||
if (workerEquivalents.none()) return // for mods with no worker units
|
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.
|
// 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 else 5 + (cities - 5 / 2)
|
val numberOfWorkersWeWant = if (cities <= 5) (cities * 1.5f) else 7.5f + ((cities - 5))
|
||||||
|
|
||||||
if (workers < numberOfWorkersWeWant) {
|
if (workers < numberOfWorkersWeWant) {
|
||||||
val modifier = numberOfWorkersWeWant / (workers + 0.4f) // The worse our worker to city ratio is, the more desperate we are
|
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()
|
val localUniqueCache = LocalUniqueCache()
|
||||||
for (building in buildings.filterBuildable()) {
|
for (building in buildings.filterBuildable()) {
|
||||||
if (building.isWonder && city.isPuppet) continue
|
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
|
if (building.isWonder && civInfo.cities.size < 3) continue
|
||||||
addChoice(relativeCostEffectiveness, building.name, getValueOfBuilding(building, localUniqueCache))
|
addChoice(relativeCostEffectiveness, building.name, getValueOfBuilding(building, localUniqueCache))
|
||||||
}
|
}
|
||||||
|
@ -528,9 +528,6 @@ object NextTurnAutomation {
|
|||||||
if (civInfo.isCityState) return
|
if (civInfo.isCityState) return
|
||||||
if (civInfo.isOneCityChallenger()) return
|
if (civInfo.isOneCityChallenger()) return
|
||||||
if (civInfo.isAtWar()) return // don't train settlers when you could be training troops.
|
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.cities.none()) return
|
||||||
if (civInfo.getHappiness() <= civInfo.cities.size) return
|
if (civInfo.getHappiness() <= civInfo.cities.size) return
|
||||||
|
|
||||||
|
@ -102,9 +102,8 @@ object CivilianUnitAutomation {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Great engineer -> Try to speed up wonder construction if late game
|
// Great engineer -> Try to speed up wonder construction
|
||||||
if (isLateGame &&
|
if ((unit.hasUnique(UniqueType.CanSpeedupConstruction)
|
||||||
(unit.hasUnique(UniqueType.CanSpeedupConstruction)
|
|
||||||
|| unit.hasUnique(UniqueType.CanSpeedupWonderConstruction))) {
|
|| unit.hasUnique(UniqueType.CanSpeedupWonderConstruction))) {
|
||||||
val wonderCanBeSpedUpEventually = SpecificUnitAutomation.speedupWonderConstruction(unit)
|
val wonderCanBeSpedUpEventually = SpecificUnitAutomation.speedupWonderConstruction(unit)
|
||||||
if (wonderCanBeSpedUpEventually)
|
if (wonderCanBeSpedUpEventually)
|
||||||
|
@ -18,6 +18,7 @@ import com.unciv.models.ruleset.tile.Terrain
|
|||||||
import com.unciv.models.ruleset.tile.TileImprovement
|
import com.unciv.models.ruleset.tile.TileImprovement
|
||||||
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
|
import com.unciv.models.stats.Stat
|
||||||
import com.unciv.models.stats.Stats
|
import com.unciv.models.stats.Stats
|
||||||
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActions
|
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActions
|
||||||
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsFromUniques
|
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsFromUniques
|
||||||
@ -74,8 +75,8 @@ class WorkerAutomation(
|
|||||||
val currentTile = unit.getTile()
|
val currentTile = unit.getTile()
|
||||||
// Must be called before any getPriority checks to guarantee the local road cache is processed
|
// Must be called before any getPriority checks to guarantee the local road cache is processed
|
||||||
val citiesToConnect = roadBetweenCitiesAutomation.getNearbyCitiesToConnect(unit)
|
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
|
// 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) >= 10
|
if (!dangerousTiles.contains(currentTile) && getFullPriority(unit.getTile(), unit, localUniqueCache) >= 2
|
||||||
&& currentTile.improvementInProgress != null) {
|
&& currentTile.improvementInProgress != null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -160,7 +161,7 @@ class WorkerAutomation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nothing to do, try again to connect cities
|
// 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())
|
debug("WorkerAutomation: %s -> nothing to do", unit.toString())
|
||||||
@ -231,24 +232,22 @@ class WorkerAutomation(
|
|||||||
if (tile.providesYield()) priority += 2
|
if (tile.providesYield()) priority += 2
|
||||||
if (tile.isPillaged()) priority += 1
|
if (tile.isPillaged()) priority += 1
|
||||||
if (tile.hasFalloutEquivalent()) 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
|
// give a minor priority to tiles that we could expand onto
|
||||||
else if (tile.getOwner() == null && tile.neighbors.any { it.getOwner() == civInfo })
|
else if (tile.getOwner() == null && tile.neighbors.any { it.getOwner() == civInfo })
|
||||||
priority += 1
|
priority += 1
|
||||||
|
|
||||||
if (priority <= 0 && tile.hasViewableResource(civInfo)) {
|
if (tile.hasViewableResource(civInfo)) {
|
||||||
priority += 1
|
priority += 1
|
||||||
// New Resources are great!
|
if (tile.tileResource.resourceType == ResourceType.Luxury) priority += 3
|
||||||
if (tile.tileResource.resourceType != ResourceType.Bonus
|
//luxuries are more important than other types of resources
|
||||||
&& !civInfo.hasResource(tile.resource!!))
|
|
||||||
priority += 2
|
|
||||||
}
|
}
|
||||||
if (tile in roadBetweenCitiesAutomation.tilesOfRoadsMap) priority += when {
|
|
||||||
civInfo.stats.statsForNextTurn.gold <= 5 -> 0
|
if (tile in roadBetweenCitiesAutomation.tilesOfRoadsMap)
|
||||||
civInfo.stats.statsForNextTurn.gold <= 10 -> 1
|
priority += 3
|
||||||
civInfo.stats.statsForNextTurn.gold <= 30 -> 2
|
|
||||||
else -> 3
|
|
||||||
}
|
|
||||||
tileRankings[tile] = TileImprovementRank(priority)
|
tileRankings[tile] = TileImprovementRank(priority)
|
||||||
return priority + unitSpecificPriority
|
return priority + unitSpecificPriority
|
||||||
}
|
}
|
||||||
@ -357,9 +356,10 @@ class WorkerAutomation(
|
|||||||
// After gathering all the data, we conduct the hierarchy in one place
|
// After gathering all the data, we conduct the hierarchy in one place
|
||||||
val improvementString = when {
|
val improvementString = when {
|
||||||
bestBuildableImprovement != null && bestBuildableImprovement.isRoad() -> bestBuildableImprovement.name
|
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
|
// 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
|
bestBuildableImprovement == null -> null
|
||||||
|
|
||||||
tile.improvement != null &&
|
tile.improvement != null &&
|
||||||
@ -386,10 +386,7 @@ class WorkerAutomation(
|
|||||||
if (improvement.isRoad() && roadBetweenCitiesAutomation.bestRoadAvailable.improvement(ruleSet) == improvement
|
if (improvement.isRoad() && roadBetweenCitiesAutomation.bestRoadAvailable.improvement(ruleSet) == improvement
|
||||||
&& tile in roadBetweenCitiesAutomation.tilesOfRoadsMap) {
|
&& tile in roadBetweenCitiesAutomation.tilesOfRoadsMap) {
|
||||||
val roadPlan = roadBetweenCitiesAutomation.tilesOfRoadsMap[tile]!!
|
val roadPlan = roadBetweenCitiesAutomation.tilesOfRoadsMap[tile]!!
|
||||||
var value = roadPlan.priority
|
val value = (roadPlan.priority - 5) // We want some forest chopping and farm building first if the road doesn't have high priority
|
||||||
if (civInfo.stats.statsForNextTurn.gold >= 20)
|
|
||||||
// Higher priority if we are closer to connecting the city
|
|
||||||
value += (5 - roadPlan.numberOfRoadsToBuild).coerceAtLeast(0)
|
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,6 +421,8 @@ class WorkerAutomation(
|
|||||||
stats.add(statDiff)
|
stats.add(statDiff)
|
||||||
// Take into account that the resource might be improved by the *final* improvement
|
// Take into account that the resource might be improved by the *final* improvement
|
||||||
isResourceImprovedByNewImprovement = newTile.resource != null && newTile.tileResource.isImprovedBy(wantedFinalImprovement.name)
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user