This commit is contained in:
Yair Morgenstern 2021-05-10 22:34:50 +03:00
parent 278962c37d
commit 2f47fb77bc
9 changed files with 51 additions and 39 deletions

View File

@ -161,7 +161,7 @@ object GameSaver {
return getSaves().filter { it.name().startsWith("Autosave") }
}
while (getAutosaves().count() > 10) {
val saveToDelete = getAutosaves().minBy { it.lastModified() }!!
val saveToDelete = getAutosaves().minByOrNull { it: FileHandle -> it.lastModified() }!!
deleteSave(saveToDelete.name())
}
}

View File

@ -9,6 +9,7 @@ import com.unciv.logic.map.mapgenerator.MapGenerator
import com.unciv.models.metadata.GameParameters
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.ui.newgamescreen.GameSetupInfo
import java.util.*
import kotlin.collections.ArrayList
@ -155,7 +156,7 @@ object GameStarter {
&& it.unitType.isLandUnit()
&& !it.unitType.isCivilian()
}
return availableMilitaryUnits.maxBy { max(it.strength, it.rangedStrength) }?.name
return availableMilitaryUnits.maxByOrNull { max(it.strength, it.rangedStrength) }?.name
}
// no starting units for Barbarians and Spectators
for (civ in gameInfo.civilizations.filter { !it.isBarbarian() && !it.isSpectator() }) {

View File

@ -136,14 +136,17 @@ object BattleHelper {
var enemyTileToAttack: AttackableTile? = null
val capturableCity = cityTilesToAttack.firstOrNull { it.tileToAttack.getCity()!!.health == 1 }
val cityWithHealthLeft = cityTilesToAttack.filter { it.tileToAttack.getCity()!!.health != 1 } // don't want ranged units to attack defeated cities
.minBy { it.tileToAttack.getCity()!!.health }
val cityWithHealthLeft =
cityTilesToAttack.filter { it.tileToAttack.getCity()!!.health != 1 } // don't want ranged units to attack defeated cities
.minByOrNull { it.tileToAttack.getCity()!!.health }
if (unit.type.isMelee() && capturableCity != null)
enemyTileToAttack = capturableCity // enter it quickly, top priority!
else if (nonCityTilesToAttack.isNotEmpty()) // second priority, units
enemyTileToAttack = nonCityTilesToAttack.minBy { Battle.getMapCombatantOfTile(it.tileToAttack)!!.getHealth() }
enemyTileToAttack = nonCityTilesToAttack.minByOrNull {
Battle.getMapCombatantOfTile(it.tileToAttack)!!.getHealth()
}
else if (cityWithHealthLeft != null) enemyTileToAttack = cityWithHealthLeft // third priority, city
return enemyTileToAttack

View File

@ -10,6 +10,7 @@ import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.map.BFS
import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.VictoryType
import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.stats.Stat
import kotlin.math.min
import kotlin.math.sqrt
@ -80,11 +81,11 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
else theChosenOne = "Nothing"
} else if (relativeCostEffectiveness.any { it.remainingWork < production * 30 }) {
relativeCostEffectiveness.removeAll { it.remainingWork >= production * 30 }
theChosenOne = relativeCostEffectiveness.minBy { it.remainingWork / it.choiceModifier }!!.choice
theChosenOne = relativeCostEffectiveness.minByOrNull { it.remainingWork / it.choiceModifier }!!.choice
}
// it's possible that this is a new city and EVERYTHING is way expensive - ignore modifiers, go for the cheapest.
// Nobody can plan 30 turns ahead, I don't care how cost-efficient you are.
else theChosenOne = relativeCostEffectiveness.minBy { it.remainingWork }!!.choice
else theChosenOne = relativeCostEffectiveness.minByOrNull { it.remainingWork }!!.choice
civInfo.addNotification("Work has started on [$theChosenOne]", CityAction(cityInfo.location), NotificationIcon.Construction)
cityConstructions.currentConstructionFromQueue = theChosenOne
@ -132,7 +133,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
}
if (tilesThatNeedWorkboat.none { bfs.hasReachedTile(it) }) return
addChoice(relativeCostEffectiveness, buildableWorkboatUnits.minBy { it.cost }!!.name, 0.6f)
addChoice(relativeCostEffectiveness, buildableWorkboatUnits.minByOrNull { it.cost }!!.name, 0.6f)
}
private fun addWorkerChoice() {
@ -146,13 +147,13 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
if (workers < citiesCountedTowardsWorkers * 0.6f && civUnits.none { it.hasUnique(Constants.workerUnique) && it.isIdle() }) {
var modifier = citiesCountedTowardsWorkers / (workers + 0.1f)
if (!cityIsOverAverageProduction) modifier /= 5 // higher production cities will deal with this
addChoice(relativeCostEffectiveness, workerEquivalents.minBy { it.cost }!!.name, modifier)
addChoice(relativeCostEffectiveness, workerEquivalents.minByOrNull { it.cost }!!.name, modifier)
}
}
private fun addCultureBuildingChoice() {
val cultureBuilding = buildableNotWonders
.filter { it.isStatRelated(Stat.Culture) }.minBy { it.cost }
.filter { it.isStatRelated(Stat.Culture) }.minByOrNull { it.cost }
if (cultureBuilding != null) {
var modifier = 0.5f
if(cityInfo.cityStats.currentCityStats.culture==0f) // It won't grow if we don't help it
@ -174,7 +175,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
}
private fun addOtherBuildingChoice() {
val otherBuilding = buildableNotWonders.minBy { it.cost }
val otherBuilding = buildableNotWonders.minByOrNull { it.cost }
if (otherBuilding != null) {
val modifier = 0.6f
addChoice(relativeCostEffectiveness, otherBuilding.name, modifier)
@ -216,7 +217,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
private fun addUnitTrainingBuildingChoice() {
val unitTrainingBuilding = buildableNotWonders.asSequence()
.filter { it.xpForNewUnits > 0 }.minBy { it.cost }
.filter { it.xpForNewUnits > 0 }.minByOrNull { it.cost }
if (unitTrainingBuilding != null && (preferredVictoryType != VictoryType.Cultural || isAtWar)) {
var modifier = if (cityIsOverAverageProduction) 0.5f else 0.1f // You shouldn't be cranking out units anytime soon
if (isAtWar) modifier *= 2
@ -228,7 +229,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
private fun addDefenceBuildingChoice() {
val defensiveBuilding = buildableNotWonders.asSequence()
.filter { it.cityStrength > 0 }.minBy { it.cost }
.filter { it.cityStrength > 0 }.minByOrNull { it.cost }
if (defensiveBuilding != null && (isAtWar || preferredVictoryType != VictoryType.Cultural)) {
var modifier = 0.2f
if (isAtWar) modifier = 0.5f
@ -246,7 +247,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
val happinessBuilding = buildableNotWonders.asSequence()
.filter { it.isStatRelated(Stat.Happiness)
|| it.uniques.contains("Remove extra unhappiness from annexed cities") }
.minBy { it.cost }
.minByOrNull { it.cost }
if (happinessBuilding != null) {
var modifier = 1f
val civHappiness = civInfo.getHappiness()
@ -259,8 +260,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
private fun addScienceBuildingChoice() {
if (allTechsAreResearched) return
val scienceBuilding = buildableNotWonders.asSequence()
.filter { it.isStatRelated(Stat.Science) || it.name == "Library" } // only stat related in unique
.minBy { it.cost }
.filter { it.isStatRelated(Stat.Science) || it.name == "Library" } // only stat related in unique
.minByOrNull { it.cost }
if (scienceBuilding != null) {
var modifier = 1.1f
if (preferredVictoryType == VictoryType.Scientific)
@ -271,7 +272,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
private fun addGoldBuildingChoice() {
val goldBuilding = buildableNotWonders.asSequence().filter { it.isStatRelated(Stat.Gold) }
.minBy { it.cost }
.minByOrNull { it.cost }
if (goldBuilding != null) {
val modifier = if (civInfo.statsForNextTurn.gold < 0) 3f else 1.2f
addChoice(relativeCostEffectiveness, goldBuilding.name, modifier)
@ -282,11 +283,11 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
val hasWaterResource = cityInfo.tilesInRange
.any { it.isWater && it.resource!=null && it.position in cityInfo.tiles }
val productionBuilding = buildableNotWonders.asSequence()
.filter { it.isStatRelated(Stat.Production)
|| (hasWaterResource && (it.uniques.contains("+1 production and gold from all sea resources worked by the city")
|| it.uniques.contains("+1 production from all sea resources worked by the city")) )
}
.minBy { it.cost }
.filter { it.isStatRelated(Stat.Production)
|| (hasWaterResource && (it.uniques.contains("+1 production and gold from all sea resources worked by the city")
|| it.uniques.contains("+1 production from all sea resources worked by the city")) )
}
.minByOrNull { it.cost }
if (productionBuilding != null) {
addChoice(relativeCostEffectiveness, productionBuilding.name, 1.5f)
}
@ -295,7 +296,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
private fun addFoodBuildingChoice() {
val foodBuilding = buildableNotWonders.asSequence().filter { it.isStatRelated(Stat.Food)
|| it.getBaseBuilding(civInfo.gameInfo.ruleSet).name == "Aqueduct" || it.getBaseBuilding(civInfo.gameInfo.ruleSet).name == "Medical Lab"} // only stat related in unique
.minBy { it.cost }
.minByOrNull { it.cost }
if (foodBuilding != null) {
var modifier = 1f
if (cityInfo.population.population < 5) modifier = 1.3f

View File

@ -6,6 +6,8 @@ import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.TileInfo
import com.unciv.logic.map.UnitMovementAlgorithms
import com.unciv.models.AttackableTile
import com.unciv.models.ruleset.unit.UnitType
import com.unciv.ui.worldscreen.unit.UnitActions
@ -28,8 +30,8 @@ object UnitAutomation {
unit.movement.getDistanceToTiles().keys.filter { isGoodTileToExplore(unit, it) }
if (explorableTilesThisTurn.any()) {
val bestTile = explorableTilesThisTurn
.sortedByDescending { it.getHeight() } // secondary sort is by 'how far can you see'
.maxBy { it.aerialDistanceTo(unit.currentTile) }!! // primary sort is by 'how far can you go'
.sortedByDescending { it.getHeight() } // secondary sort is by 'how far can you see'
.maxByOrNull { it: TileInfo -> it.aerialDistanceTo(unit.currentTile) }!! // primary sort is by 'how far can you go'
unit.movement.headTowards(bestTile)
return true
}
@ -207,8 +209,8 @@ object UnitAutomation {
return true
}
val bestTilesForHealing = tilesByHealingRate.maxBy { it.key }!!.value
val bestTileForHealing = bestTilesForHealing.maxBy { it.getDefensiveBonus() }!!
val bestTilesForHealing = tilesByHealingRate.maxByOrNull { it.key }!!.value
val bestTileForHealing = bestTilesForHealing.maxByOrNull { it.getDefensiveBonus() }!!
val bestTileForHealingRank = unit.rankTileForHealing(bestTileForHealing)
if (currentUnitTile != bestTileForHealing
@ -227,7 +229,7 @@ object UnitAutomation {
.filter { unit.movement.canMoveTo(it) && UnitActions.canPillage(unit, it) }
if (tilesThatCanWalkToAndThenPillage.isEmpty()) return false
val tileToPillage = tilesThatCanWalkToAndThenPillage.maxBy { it.getDefensiveBonus() }!!
val tileToPillage = tilesThatCanWalkToAndThenPillage.maxByOrNull { it: TileInfo -> it.getDefensiveBonus() }!!
if (unit.getTile() != tileToPillage)
unit.movement.moveToTile(tileToPillage)
@ -263,7 +265,7 @@ object UnitAutomation {
if (unit.type.isRanged())
closeEnemies = closeEnemies.filterNot { it.tileToAttack.isCityCenter() && it.tileToAttack.getCity()!!.health == 1 }
val closestEnemy = closeEnemies.minBy { it.tileToAttack.aerialDistanceTo(unit.getTile()) }
val closestEnemy = closeEnemies.minByOrNull { it.tileToAttack.aerialDistanceTo(unit.getTile()) }
if (closestEnemy != null) {
unit.movement.headTowards(closestEnemy.tileToAttackFrom)
@ -321,7 +323,8 @@ object UnitAutomation {
.asSequence().map { it.getCenterTile() }
.sortedBy { cityCenterTile ->
// sort enemy cities by closeness to our cities, and only then choose the first reachable - checking canReach is comparatively very time-intensive!
unit.civInfo.cities.asSequence().map { cityCenterTile.aerialDistanceTo(it.getCenterTile()) }.min()!!
unit.civInfo.cities.asSequence()
.map { cityCenterTile.aerialDistanceTo(it.getCenterTile()) }.minOrNull()!!
}
.firstOrNull { unit.movement.canReach(it) }
@ -343,7 +346,7 @@ object UnitAutomation {
it.key.aerialDistanceTo(closestReachableEnemyCity) <=
unitRange && it.key !in tilesInBombardRange
}
.minBy { it.value.totalDistance }?.key
.minByOrNull { it.value.totalDistance }?.key
// move into position far away enough that the bombard doesn't hurt
if (tileToMoveTo != null) {
@ -399,7 +402,7 @@ object UnitAutomation {
.filter { it.getUnitType().isRanged() }
if (rangedUnits.any()) targets = rangedUnits
}
return targets.minBy { it.getHealth() }
return targets.minByOrNull { it: ICombatant -> it.getHealth() }
}
private fun tryTakeBackCapturedCity(unit: MapUnit): Boolean {
@ -485,7 +488,7 @@ object UnitAutomation {
return
}
val tileFurthestFromEnemy = reachableTiles.keys.filter { unit.movement.canMoveTo(it) }
.maxBy { countDistanceToClosestEnemy(unit, it) }
.maxByOrNull { countDistanceToClosestEnemy(unit, it) }
if (tileFurthestFromEnemy == null) return // can't move anywhere!
unit.movement.moveToTile(tileFurthestFromEnemy)
}

View File

@ -3,6 +3,7 @@ package com.unciv.logic.automation
import com.badlogic.gdx.graphics.Color
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.map.BFS
import com.unciv.logic.map.MapUnit
@ -289,11 +290,11 @@ class WorkerAutomation(val unit: MapUnit) {
enemyCivsIsCloseEnough.forEach { enemyCities.addAll(it.cities.map { city -> city.getCenterTile() }) }
// find closest enemy city
val closestEnemyCity = enemyCities.minBy { it.aerialDistanceTo(tile) }!!
val closestEnemyCity = enemyCities.minByOrNull { it.aerialDistanceTo(tile) }!!
val distanceToEnemy = tile.aerialDistanceTo(closestEnemyCity)
// find closest our city to defend from this enemy city
val closestOurCity = civInfo.cities.minBy { it.getCenterTile().aerialDistanceTo(tile) }!!.getCenterTile()
val closestOurCity = civInfo.cities.minByOrNull { it.getCenterTile().aerialDistanceTo(tile) }!!.getCenterTile()
val distanceToOurCity = tile.aerialDistanceTo(closestOurCity)
val distanceBetweenCities = closestEnemyCity.aerialDistanceTo(closestOurCity)

View File

@ -191,8 +191,8 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
return when (currentTile) {
in destinationNeighbors -> currentTile // We're right nearby anyway, no need to move
else -> destinationNeighbors.asSequence()
.filter { distanceToTiles.containsKey(it) && canMoveTo(it) }
.minBy { distanceToTiles.getValue(it).totalDistance } // we can get a little closer
.filter { distanceToTiles.containsKey(it) && canMoveTo(it) }
.minByOrNull { distanceToTiles.getValue(it).totalDistance } // we can get a little closer
?: currentTile // We can't get closer...
}
}

View File

@ -169,7 +169,7 @@ class MapGenerator(val ruleset: Ruleset) {
.filter { it.terrainsCanBeFoundOn.contains(tile.getLastTerrain().name) }
.map { it.name }
if (possibleResources.isEmpty()) continue
val resourceWithLeastAssignments = possibleResources.minBy { resourceToNumber[it]!! }!!
val resourceWithLeastAssignments = possibleResources.minByOrNull { resourceToNumber[it]!! }!!
resourceToNumber.add(resourceWithLeastAssignments, 1)
tile.resource = resourceWithLeastAssignments
}

View File

@ -55,7 +55,10 @@ class RiverGenerator(val randomness: MapGenerationRandomness) {
.filter { map.contains(it.position) }
if (possibleCoordinates.none()) return // end of the line
val newCoordinate = possibleCoordinates
.groupBy { getAdjacentTiles(it, map).map { it.aerialDistanceTo(endPosition) }.min()!! }
.groupBy {
getAdjacentTiles(it, map).map { it.aerialDistanceTo(endPosition) }
.minOrNull()!!
}
.minBy { it.key }!!
.component2().random(randomness.RNG)