mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-16 02:40:41 +07:00
Compare improvements by tile yield calculation (#9873)
* Compare improvements by tile yield calculation * Docstring * Explicit documentation for which functions in Stats are and are not mutating functions
This commit is contained in:
@ -388,16 +388,6 @@ object Automation {
|
|||||||
if (distance > 3) score += 100
|
if (distance > 3) score += 100
|
||||||
}
|
}
|
||||||
|
|
||||||
// Improvements are good: less points
|
|
||||||
if (tile.improvement != null &&
|
|
||||||
tile.stats.getImprovementStats(
|
|
||||||
tile.getTileImprovement()!!,
|
|
||||||
city.civ,
|
|
||||||
city,
|
|
||||||
localUniqueCache
|
|
||||||
).values.sum() > 0f
|
|
||||||
) score -= 5
|
|
||||||
|
|
||||||
if (tile.naturalWonder != null) score -= 105
|
if (tile.naturalWonder != null) score -= 105
|
||||||
|
|
||||||
// Straight up take the sum of all yields
|
// Straight up take the sum of all yields
|
||||||
|
@ -391,18 +391,18 @@ class WorkerAutomation(
|
|||||||
if (potentialTileImprovements.isEmpty()) return null
|
if (potentialTileImprovements.isEmpty()) return null
|
||||||
|
|
||||||
val cityUniqueCaches = HashMap<City, LocalUniqueCache>()
|
val cityUniqueCaches = HashMap<City, LocalUniqueCache>()
|
||||||
fun getRankingWithImprovement(improvementName: String): Float {
|
fun getImprovementRanking(improvementName: String): Float {
|
||||||
val improvement = ruleSet.tileImprovements[improvementName]!!
|
val improvement = ruleSet.tileImprovements[improvementName]!!
|
||||||
val city = tile.getCity()
|
val city = tile.getCity()
|
||||||
val cache =
|
val cache =
|
||||||
if (city == null) LocalUniqueCache(false)
|
if (city == null) LocalUniqueCache(false)
|
||||||
else cityUniqueCaches.getOrPut(city) { LocalUniqueCache() }
|
else cityUniqueCaches.getOrPut(city) { LocalUniqueCache() }
|
||||||
val stats = tile.stats.getImprovementStats(improvement, civInfo, tile.getCity(), cache)
|
val stats = tile.stats.getStatDiffForImprovement(improvement, civInfo, tile.getCity(), cache)
|
||||||
return Automation.rankStatsValue(stats, unit.civ)
|
return Automation.rankStatsValue(stats, unit.civ)
|
||||||
}
|
}
|
||||||
|
|
||||||
val bestBuildableImprovement = potentialTileImprovements.values.asSequence()
|
val bestBuildableImprovement = potentialTileImprovements.values.asSequence()
|
||||||
.map { Pair(it, getRankingWithImprovement(it.name)) }
|
.map { Pair(it, getImprovementRanking(it.name)) }
|
||||||
.filter { it.second > 0f }
|
.filter { it.second > 0f }
|
||||||
.maxByOrNull { it.second }?.first
|
.maxByOrNull { it.second }?.first
|
||||||
|
|
||||||
@ -418,7 +418,7 @@ class WorkerAutomation(
|
|||||||
&& !tile.providesResources(civInfo)
|
&& !tile.providesResources(civInfo)
|
||||||
&& !isResourceImprovementAllowedOnFeature(tile, potentialTileImprovements) -> Constants.remove + lastTerrain.name
|
&& !isResourceImprovementAllowedOnFeature(tile, potentialTileImprovements) -> Constants.remove + lastTerrain.name
|
||||||
else -> tile.tileResource.getImprovements().filter { it in potentialTileImprovements || it==tile.improvement }
|
else -> tile.tileResource.getImprovements().filter { it in potentialTileImprovements || it==tile.improvement }
|
||||||
.maxByOrNull { getRankingWithImprovement(it) }
|
.maxByOrNull { getImprovementRanking(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// After gathering all the data, we conduct the hierarchy in one place
|
// After gathering all the data, we conduct the hierarchy in one place
|
||||||
@ -430,7 +430,7 @@ class WorkerAutomation(
|
|||||||
bestBuildableImprovement == null -> null
|
bestBuildableImprovement == null -> null
|
||||||
|
|
||||||
tile.improvement != null &&
|
tile.improvement != null &&
|
||||||
getRankingWithImprovement(tile.improvement!!) > getRankingWithImprovement(bestBuildableImprovement.name)
|
getImprovementRanking(tile.improvement!!) > getImprovementRanking(bestBuildableImprovement.name)
|
||||||
-> null // What we have is better, even if it's pillaged we should repair it
|
-> null // What we have is better, even if it's pillaged we should repair it
|
||||||
|
|
||||||
lastTerrain.let {
|
lastTerrain.let {
|
||||||
|
@ -193,22 +193,34 @@ class TileStatFunctions(val tile: Tile) {
|
|||||||
food + production + gold
|
food + production + gold
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the extra stats that we would get if we switched to this improvement
|
||||||
|
* Can be negative if we're switching to a worse improvement */
|
||||||
|
fun getStatDiffForImprovement(
|
||||||
|
improvement: TileImprovement,
|
||||||
|
observingCiv: Civilization,
|
||||||
|
city: City?,
|
||||||
|
cityUniqueCache: LocalUniqueCache = LocalUniqueCache(false)): Stats {
|
||||||
|
|
||||||
|
val currentStats = getTileStats(city, observingCiv, cityUniqueCache)
|
||||||
|
|
||||||
|
val tileClone = tile.clone()
|
||||||
|
tileClone.setTransients()
|
||||||
|
|
||||||
|
if (improvement.name.startsWith(Constants.remove))
|
||||||
|
tileClone.removeTerrainFeature(improvement.name.removePrefix(Constants.remove))
|
||||||
|
else tileClone.changeImprovement(improvement.name)
|
||||||
|
val futureStats = tileClone.stats.getTileStats(city, observingCiv, cityUniqueCache)
|
||||||
|
|
||||||
|
return futureStats.minus(currentStats)
|
||||||
|
}
|
||||||
|
|
||||||
// Also multiplies the stats by the percentage bonus for improvements (but not for tiles)
|
// Also multiplies the stats by the percentage bonus for improvements (but not for tiles)
|
||||||
fun getImprovementStats(
|
private fun getImprovementStats(
|
||||||
improvement: TileImprovement,
|
improvement: TileImprovement,
|
||||||
observingCiv: Civilization,
|
observingCiv: Civilization,
|
||||||
city: City?,
|
city: City?,
|
||||||
cityUniqueCache: LocalUniqueCache = LocalUniqueCache(false)
|
cityUniqueCache: LocalUniqueCache = LocalUniqueCache(false)
|
||||||
): Stats {
|
): Stats {
|
||||||
if (improvement.name.startsWith(Constants.remove)){
|
|
||||||
val currentTileStats = getTileStats(city, observingCiv, cityUniqueCache)
|
|
||||||
val tileClone = tile.clone()
|
|
||||||
tileClone.removeTerrainFeature(improvement.name.removePrefix(Constants.remove))
|
|
||||||
val tileStatsAfterRemoval = tileClone.stats.getTileStats(city, observingCiv, cityUniqueCache)
|
|
||||||
return tileStatsAfterRemoval.minus(currentTileStats)
|
|
||||||
}
|
|
||||||
|
|
||||||
val stats = improvement.cloneStats()
|
val stats = improvement.cloneStats()
|
||||||
if (tile.hasViewableResource(observingCiv) && tile.tileResource.isImprovedBy(improvement.name)
|
if (tile.hasViewableResource(observingCiv) && tile.tileResource.isImprovedBy(improvement.name)
|
||||||
&& tile.tileResource.improvementStats != null
|
&& tile.tileResource.improvementStats != null
|
||||||
|
@ -56,7 +56,8 @@ open class Stats(
|
|||||||
&& faith == otherStats.faith
|
&& faith == otherStats.faith
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return a new instance containing the same values as `this` */
|
/** **Non-Mutating function**
|
||||||
|
* @return a new instance containing the same values as `this` */
|
||||||
fun clone() = Stats(production, food, gold, science, culture, happiness, faith)
|
fun clone() = Stats(production, food, gold, science, culture, happiness, faith)
|
||||||
|
|
||||||
/** @return `true` if all values are zero */
|
/** @return `true` if all values are zero */
|
||||||
@ -80,7 +81,9 @@ open class Stats(
|
|||||||
faith = 0f
|
faith = 0f
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds each value of another [Stats] instance to this one in place */
|
/** **Mutating function**
|
||||||
|
* Adds each value of another [Stats] instance to this one in place
|
||||||
|
* @return this for chaining */
|
||||||
fun add(other: Stats): Stats {
|
fun add(other: Stats): Stats {
|
||||||
production += other.production
|
production += other.production
|
||||||
food += other.food
|
food += other.food
|
||||||
@ -92,20 +95,28 @@ open class Stats(
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return a new [Stats] instance containing the sum of its operands value by value */
|
/** **Non-mutating function**
|
||||||
|
* @return a new [Stats] instance */
|
||||||
operator fun plus(stats: Stats) = clone().apply { add(stats) }
|
operator fun plus(stats: Stats) = clone().apply { add(stats) }
|
||||||
|
|
||||||
|
/** **Non-mutating function**
|
||||||
|
* @return a new [Stats] instance */
|
||||||
operator fun minus(stats: Stats) = clone().apply { add(stats.times(-1)) }
|
operator fun minus(stats: Stats) = clone().apply { add(stats.times(-1)) }
|
||||||
|
|
||||||
/** Adds the [value] parameter to the instance value specified by [stat] in place
|
/** **Mutating function**
|
||||||
|
* Adds the [value] parameter to the instance value specified by [stat] in place
|
||||||
* @return `this` to allow chaining */
|
* @return `this` to allow chaining */
|
||||||
fun add(stat: Stat, value: Float): Stats {
|
fun add(stat: Stat, value: Float): Stats {
|
||||||
set(stat, value + get(stat))
|
set(stat, value + get(stat))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return The result of multiplying each value of this instance by [number] as a new instance */
|
/** **Non-Mutating function**
|
||||||
|
* @return a new [Stats] instance with the result of multiplying each value of this instance by [number] as a new instance */
|
||||||
operator fun times(number: Int) = times(number.toFloat())
|
operator fun times(number: Int) = times(number.toFloat())
|
||||||
/** @return The result of multiplying each value of this instance by [number] as a new instance */
|
|
||||||
|
/** **Non-Mutating function**
|
||||||
|
* @return a new [Stats] instance with the result of multiplying each value of this instance by [number] as a new instance */
|
||||||
operator fun times(number: Float) = Stats(
|
operator fun times(number: Float) = Stats(
|
||||||
production * number,
|
production * number,
|
||||||
food * number,
|
food * number,
|
||||||
@ -116,7 +127,8 @@ open class Stats(
|
|||||||
faith * number
|
faith * number
|
||||||
)
|
)
|
||||||
|
|
||||||
/** Multiplies each value of this instance by [number] in place */
|
/** **Mutating function**
|
||||||
|
* Multiplies each value of this instance by [number] in place */
|
||||||
fun timesInPlace(number: Float) {
|
fun timesInPlace(number: Float) {
|
||||||
production *= number
|
production *= number
|
||||||
food *= number
|
food *= number
|
||||||
@ -127,9 +139,12 @@ open class Stats(
|
|||||||
faith *= number
|
faith *= number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** **Non-Mutating function**
|
||||||
|
* @return a new [Stats] instance */
|
||||||
operator fun div(number: Float) = times(1/number)
|
operator fun div(number: Float) = times(1/number)
|
||||||
|
|
||||||
/** Apply weighting for Production Ranking */
|
/** **Mutating function**
|
||||||
|
* Apply weighting for Production Ranking */
|
||||||
fun applyRankingWeights(){
|
fun applyRankingWeights(){
|
||||||
food *= 14
|
food *= 14
|
||||||
production *= 12
|
production *= 12
|
||||||
|
@ -209,19 +209,16 @@ class CityScreen(
|
|||||||
fun isExistingImprovementValuable(tile: Tile, improvementToPlace: TileImprovement): Boolean {
|
fun isExistingImprovementValuable(tile: Tile, improvementToPlace: TileImprovement): Boolean {
|
||||||
if (tile.improvement == null) return false
|
if (tile.improvement == null) return false
|
||||||
val civInfo = city.civ
|
val civInfo = city.civ
|
||||||
val existingStats = tile.stats.getImprovementStats(
|
|
||||||
|
val statDiffForNewImprovement = tile.stats.getStatDiffForImprovement(
|
||||||
tile.getTileImprovement()!!,
|
tile.getTileImprovement()!!,
|
||||||
civInfo,
|
civInfo,
|
||||||
city,
|
city,
|
||||||
cityUniqueCache
|
cityUniqueCache
|
||||||
)
|
)
|
||||||
val replacingStats = tile.stats.getImprovementStats(
|
|
||||||
improvementToPlace,
|
// If stat diff for new improvement is negative/zero utility, current improvement is valuable
|
||||||
civInfo,
|
return Automation.rankStatsValue(statDiffForNewImprovement, civInfo) <= 0
|
||||||
city,
|
|
||||||
cityUniqueCache
|
|
||||||
)
|
|
||||||
return Automation.rankStatsValue(existingStats, civInfo) > Automation.rankStatsValue(replacingStats, civInfo)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPickImprovementColor(tile: Tile): Pair<Color, Float> {
|
fun getPickImprovementColor(tile: Tile): Pair<Color, Float> {
|
||||||
|
@ -157,24 +157,12 @@ class ImprovementPickerScreen(
|
|||||||
val statIcons = getStatIconsTable(provideResource, removeImprovement)
|
val statIcons = getStatIconsTable(provideResource, removeImprovement)
|
||||||
|
|
||||||
// get benefits of the new improvement
|
// get benefits of the new improvement
|
||||||
val stats = tile.stats.getImprovementStats(
|
val stats = tile.stats.getStatDiffForImprovement(
|
||||||
improvement,
|
improvement,
|
||||||
currentPlayerCiv,
|
currentPlayerCiv,
|
||||||
tile.getCity(),
|
tile.getCity(),
|
||||||
cityUniqueCache
|
cityUniqueCache
|
||||||
)
|
)
|
||||||
// subtract the benefits of the replaced improvement, if any
|
|
||||||
val existingImprovement = tile.getTileImprovement()
|
|
||||||
if (existingImprovement != null && removeImprovement) {
|
|
||||||
val existingStats = tile.stats.getImprovementStats(
|
|
||||||
existingImprovement,
|
|
||||||
currentPlayerCiv,
|
|
||||||
tile.getCity(),
|
|
||||||
cityUniqueCache
|
|
||||||
)
|
|
||||||
stats.add(existingStats.times(-1.0f))
|
|
||||||
}
|
|
||||||
|
|
||||||
val statsTable = getStatsTable(stats)
|
val statsTable = getStatsTable(stats)
|
||||||
statIcons.add(statsTable).padLeft(13f)
|
statIcons.add(statsTable).padLeft(13f)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user