diff --git a/core/src/com/unciv/logic/GameSaver.kt b/core/src/com/unciv/logic/GameSaver.kt index be41fee635..78377c3c96 100644 --- a/core/src/com/unciv/logic/GameSaver.kt +++ b/core/src/com/unciv/logic/GameSaver.kt @@ -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()) } } diff --git a/core/src/com/unciv/logic/GameStarter.kt b/core/src/com/unciv/logic/GameStarter.kt index da705d6dbd..e8abb4dff0 100644 --- a/core/src/com/unciv/logic/GameStarter.kt +++ b/core/src/com/unciv/logic/GameStarter.kt @@ -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() }) { diff --git a/core/src/com/unciv/logic/automation/BattleHelper.kt b/core/src/com/unciv/logic/automation/BattleHelper.kt index 1a470092bf..22b3d0db19 100644 --- a/core/src/com/unciv/logic/automation/BattleHelper.kt +++ b/core/src/com/unciv/logic/automation/BattleHelper.kt @@ -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 diff --git a/core/src/com/unciv/logic/automation/ConstructionAutomation.kt b/core/src/com/unciv/logic/automation/ConstructionAutomation.kt index 3032faf690..ef822c9b13 100644 --- a/core/src/com/unciv/logic/automation/ConstructionAutomation.kt +++ b/core/src/com/unciv/logic/automation/ConstructionAutomation.kt @@ -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 diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index 265b82b9a9..8a6d2eecc7 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -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) } diff --git a/core/src/com/unciv/logic/automation/WorkerAutomation.kt b/core/src/com/unciv/logic/automation/WorkerAutomation.kt index 8ba3e6306f..0212ff6920 100644 --- a/core/src/com/unciv/logic/automation/WorkerAutomation.kt +++ b/core/src/com/unciv/logic/automation/WorkerAutomation.kt @@ -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) diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index 43ac0740da..a2773dbfda 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -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... } } diff --git a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt index 7f703d6b5e..771d5bc7ef 100644 --- a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt +++ b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt @@ -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 } diff --git a/core/src/com/unciv/logic/map/mapgenerator/RiverGenerator.kt b/core/src/com/unciv/logic/map/mapgenerator/RiverGenerator.kt index 3a9746ff72..24348dd1e4 100644 --- a/core/src/com/unciv/logic/map/mapgenerator/RiverGenerator.kt +++ b/core/src/com/unciv/logic/map/mapgenerator/RiverGenerator.kt @@ -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)