Performance: Removed map lookup for getLastTerrain

This was used in isImpassible which was used in canPassThrough, which means it was called A LOT
This commit is contained in:
Yair Morgenstern 2023-04-03 10:55:52 +03:00
parent f0ee25dcac
commit 9c121086ea
8 changed files with 39 additions and 34 deletions

View File

@ -387,7 +387,7 @@ class WorkerAutomation(
.filter { it.second > 0f }
.maxByOrNull { it.second }?.first
val lastTerrain = tile.getLastTerrain()
val lastTerrain = tile.lastTerrain
fun isUnbuildableAndRemovable(terrain: Terrain): Boolean = terrain.unbuildable
&& ruleSet.tileImprovements.containsKey(Constants.remove + terrain.name)

View File

@ -220,7 +220,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc
.firstOrNull { tile.isAdjacentTo(it.params[1]) }
?: continue
val terrain = ruleset.terrains[conversionUnique.params[0]] ?: continue
if (!terrain.occursOn.contains(tile.getLastTerrain().name)) continue
if (!terrain.occursOn.contains(tile.lastTerrain.name)) continue
if (terrain.type == TerrainType.TerrainFeature)
tile.addTerrainFeature(terrain.name)
@ -316,7 +316,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc
val suitableTiles = candidateTiles
.filterNot { it.baseTerrain == Constants.snow && it.isHill() }
.filter { it.resource == null
&& resource.terrainsCanBeFoundOn.contains(it.getLastTerrain().name) }
&& resource.terrainsCanBeFoundOn.contains(it.lastTerrain.name) }
val locations = randomness.chooseSpreadOutLocations(resourcesPerType, suitableTiles, mapRadius)
@ -335,7 +335,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc
val suitableTiles = tileMap.values
.filterNot { it.baseTerrain == Constants.snow && it.isHill() }
.filter { it.resource == null && it.neighbors.none { neighbor -> neighbor.isNaturalWonder() }
&& resourcesOfType.any { r -> r.terrainsCanBeFoundOn.contains(it.getLastTerrain().name) } }
&& resourcesOfType.any { r -> r.terrainsCanBeFoundOn.contains(it.lastTerrain.name) } }
val numberOfResources = tileMap.values.count { it.isLand && !it.isImpassible() } *
tileMap.mapParameters.resourceRichness
val locations = randomness.chooseSpreadOutLocations(numberOfResources.toInt(), suitableTiles, mapRadius)
@ -344,7 +344,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc
for (tile in locations) {
val possibleResources = resourcesOfType
.filter { it.terrainsCanBeFoundOn.contains(tile.getLastTerrain().name) }
.filter { it.terrainsCanBeFoundOn.contains(tile.lastTerrain.name) }
if (possibleResources.isEmpty()) continue
val resourceWithLeastAssignments = possibleResources.minByOrNull { resourceToNumber[it.name]!! }!!
resourceToNumber.add(resourceWithLeastAssignments.name, 1)
@ -635,13 +635,13 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc
val candidateTerrains = vegetationTerrains.flatMap{ it.occursOn }
// Checking it.baseTerrain in candidateTerrains to make sure forest does not spawn on desert hill
for (tile in tileMap.values.asSequence().filter { it.baseTerrain in candidateTerrains
&& it.getLastTerrain().name in candidateTerrains }) {
&& it.lastTerrain.name in candidateTerrains }) {
val vegetation = (randomness.getPerlinNoise(tile, vegetationSeed, scale = 3.0, nOctaves = 1) + 1.0) / 2.0
if (vegetation <= tileMap.mapParameters.vegetationRichness) {
val possibleVegetation = vegetationTerrains.filter { vegetationTerrain ->
vegetationTerrain.occursOn.contains(tile.getLastTerrain().name)
vegetationTerrain.occursOn.contains(tile.lastTerrain.name)
&& vegetationTerrain.getMatchingUniques(UniqueType.TileGenerationConditions).none {
tile.temperature!! < it.params[0].toDouble() || tile.temperature!! > it.params[1].toDouble()
|| tile.humidity!! < it.params[2].toDouble() || tile.humidity!! > it.params[3].toDouble()
@ -714,7 +714,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc
val candidates = iceEquivalents
.filter {
it.matches(temperature, 1.0) &&
tile.getLastTerrain().name in it.terrain.occursOn
tile.lastTerrain.name in it.terrain.occursOn
}.map { it.terrain.name }
when (candidates.size) {
1 -> tile.addTerrainFeature(candidates.first())

View File

@ -646,9 +646,9 @@ class MapRegions (val ruleset: Ruleset){
val validBonuses = ruleset.tileResources.values.filter {
it.resourceType == ResourceType.Bonus &&
it.food >= 1 &&
plot.getLastTerrain().name in it.terrainsCanBeFoundOn
plot.lastTerrain.name in it.terrainsCanBeFoundOn
}
val goodPlotForOasis = canPlaceOasis && plot.getLastTerrain().name in oasisEquivalent!!.occursOn
val goodPlotForOasis = canPlaceOasis && plot.lastTerrain.name in oasisEquivalent!!.occursOn
if (validBonuses.isNotEmpty() || goodPlotForOasis) {
if (goodPlotForOasis) {
@ -699,7 +699,7 @@ class MapRegions (val ruleset: Ruleset){
if (plot.resource != null) continue
val bonusToPlace = stoneTypeBonuses.filter { plot.getLastTerrain().name in it.terrainsCanBeFoundOn }.randomOrNull()
val bonusToPlace = stoneTypeBonuses.filter { plot.lastTerrain.name in it.terrainsCanBeFoundOn }.randomOrNull()
if (bonusToPlace != null) {
plot.resource = bonusToPlace.name
stoneNeeded--
@ -715,7 +715,7 @@ class MapRegions (val ruleset: Ruleset){
val bestImprovementYield = tile.tileMap.ruleset!!.tileImprovements.values
.filter { !it.hasUnique(UniqueType.GreatImprovement) &&
it.uniqueTo == null &&
tile.getLastTerrain().name in it.terrainsCanBeBuiltOn }
tile.lastTerrain.name in it.terrainsCanBeBuiltOn }
.maxOfOrNull { it[stat] }
return baseYield + (bestImprovementYield ?: 0f)
}
@ -1334,7 +1334,7 @@ class MapRegions (val ruleset: Ruleset){
continue
val weightings = strategicResources.map {
if (fallbackStrategic) {
if (tile.getLastTerrain().name in it.terrainsCanBeFoundOn) 1f else 0f
if (tile.lastTerrain.name in it.terrainsCanBeFoundOn) 1f else 0f
} else {
val uniques = it.getMatchingUniques(UniqueType.MinorDepositWeighting, conditionalTerrain).toList()
uniques.sumOf { unique -> unique.params[0].toInt() }.toFloat()
@ -1391,7 +1391,7 @@ class MapRegions (val ruleset: Ruleset){
if(fallbackBonuses && resource.resourceType == ResourceType.Bonus) {
// Since we haven't been able to generate any rule-based lists, just generate new ones on the fly
// Increase impact to avoid clustering since there is no terrain type stratification.
val fallbackList = tileMap.values.filter { it.getLastTerrain().name in resource.terrainsCanBeFoundOn }.shuffled()
val fallbackList = tileMap.values.filter { it.lastTerrain.name in resource.terrainsCanBeFoundOn }.shuffled()
placeResourcesInTiles((20 * bonusMultiplier).toInt(), fallbackList, listOf(resource), 2 + extraImpact, 2 + extraImpact, false)
}
}
@ -1440,7 +1440,7 @@ class MapRegions (val ruleset: Ruleset){
for (tile in tiles) {
val conditionalTerrain = StateForConditionals(attackedTile = tile)
if (tile.resource == null &&
tile.getLastTerrain().name in resource.terrainsCanBeFoundOn &&
tile.lastTerrain.name in resource.terrainsCanBeFoundOn &&
!tile.getBaseTerrain().hasUnique(UniqueType.BlocksResources, conditionalTerrain) &&
!resource.hasUnique(UniqueType.NoNaturalGeneration, conditionalTerrain) &&
resource.getMatchingUniques(UniqueType.TileGenerationConditions).none {
@ -1515,7 +1515,7 @@ class MapRegions (val ruleset: Ruleset){
for (tile in tileList) {
if (tile.resource != null ||
(testTerrains &&
(tile.getLastTerrain().name !in resourceOptions.first().terrainsCanBeFoundOn ||
(tile.lastTerrain.name !in resourceOptions.first().terrainsCanBeFoundOn ||
resourceOptions.first().hasUnique(UniqueType.NoNaturalGeneration, conditionalTerrain)) ) ||
tile.getBaseTerrain().hasUnique(UniqueType.BlocksResources, conditionalTerrain))
continue // Can't place here, can't be a fallback tile

View File

@ -99,7 +99,7 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
val suitableLocations = tileMap.values.filter { tile->
tile.resource == null &&
naturalWonder.occursOn.contains(tile.getLastTerrain().name) &&
naturalWonder.occursOn.contains(tile.lastTerrain.name) &&
naturalWonder.uniqueObjects.all { unique ->
when (unique.type) {
UniqueType.NaturalWonderNeighborCount -> {
@ -215,7 +215,7 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
"Land" -> isLand
Constants.hill -> isHill()
naturalWonder -> true
in allTerrainFeatures -> getLastTerrain().name == filter
in allTerrainFeatures -> lastTerrain.name == filter
else -> baseTerrain == filter
}

View File

@ -68,7 +68,7 @@ class UnitMovement(val unit: MapUnit) {
if (unit.cache.ignoresTerrainCost) return 1f + extraCost
if (areConnectedByRiver) return 100f // Rivers take the entire turn to cross
val terrainCost = to.getLastTerrain().movementCost.toFloat()
val terrainCost = to.lastTerrain.movementCost.toFloat()
if (unit.cache.noTerrainMovementUniques)
return terrainCost + extraCost
@ -704,7 +704,7 @@ class UnitMovement(val unit: MapUnit) {
// helicopters can pass through impassable tiles like mountains
if (!unit.cache.canPassThroughImpassableTiles && !(unit.cache.canEnterIceTiles && tile.terrainFeatures.contains(Constants.ice))
// carthage-like uniques sometimes allow passage through impassible tiles
&& !(unit.civ.passThroughImpassableUnlocked && unit.civ.passableImpassables.contains(tile.getLastTerrain().name)))
&& !(unit.civ.passThroughImpassableUnlocked && unit.civ.passableImpassables.contains(tile.lastTerrain.name)))
return false
}
if (tile.isLand

View File

@ -96,6 +96,11 @@ open class Tile : IsPartOfGameInfoSerialization {
var allTerrains: Sequence<Terrain> = sequenceOf()
private set
@Transient
/** Saves a sequence of a list */
lateinit var lastTerrain: Terrain
private set
@Transient
var terrainUniqueMap = UniqueMap()
private set
@ -205,13 +210,6 @@ open class Tile : IsPartOfGameInfoSerialization {
fun getCity(): City? = owningCity
fun getLastTerrain(): Terrain = when {
terrainFeatures.isNotEmpty() -> ruleset.terrains[terrainFeatures.last()]
?: getBaseTerrain() // defense against rare edge cases involving baseTerrain Hill deprecation
naturalWonder != null -> getNaturalWonder()
else -> getBaseTerrain()
}
@Transient
private var tileResourceCache: TileResource? = null
val tileResource: TileResource
@ -257,7 +255,7 @@ open class Tile : IsPartOfGameInfoSerialization {
fun isCityCenter(): Boolean = isCityCenterInternal
fun isNaturalWonder(): Boolean = naturalWonder != null
fun isImpassible() = getLastTerrain().impassable
fun isImpassible() = lastTerrain.impassable
fun getTileImprovement(): TileImprovement? = if (improvement == null) null else ruleset.tileImprovements[improvement!!]
fun getUnpillagedTileImprovement(): TileImprovement? = if (getUnpillagedImprovement() == null) null else ruleset.tileImprovements[improvement!!]
@ -826,6 +824,13 @@ open class Tile : IsPartOfGameInfoSerialization {
val newUniqueMap = UniqueMap()
for (terrain in allTerrains)
newUniqueMap.addUniques(terrain.uniqueObjects)
lastTerrain = when {
terrainFeatures.isNotEmpty() -> ruleset.terrains[terrainFeatures.last()]
?: getBaseTerrain() // defense against rare edge cases involving baseTerrain Hill deprecation
naturalWonder != null -> getNaturalWonder()
else -> getBaseTerrain()
}
terrainUniqueMap = newUniqueMap
}

View File

@ -77,7 +77,7 @@ class TileInfoImprovementFunctions(val tile: Tile) {
fun TileImprovement.canBeBuildOnThisUnbuildableTerrain(
knownFeatureRemovals: List<TileImprovement>? = null,
): Boolean {
val topTerrain = tile.getLastTerrain()
val topTerrain = tile.lastTerrain
// We can build if we are specifically allowed to build on this terrain
if (isAllowedOnFeature(topTerrain.name)) return true
@ -115,7 +115,7 @@ class TileInfoImprovementFunctions(val tile: Tile) {
tile.improvement != null && tile.getTileImprovement()!!.hasUnique(UniqueType.Irremovable, stateForConditionals) -> false
// Can't build if this terrain is unbuildable, except when we are specifically allowed to
tile.getLastTerrain().unbuildable && !improvement.canBeBuildOnThisUnbuildableTerrain(knownFeatureRemovals) -> false
tile.lastTerrain.unbuildable && !improvement.canBeBuildOnThisUnbuildableTerrain(knownFeatureRemovals) -> false
// Can't build if any terrain specifically prevents building this improvement
tile.getTerrainMatchingUniques(UniqueType.RestrictedBuildableImprovements, stateForConditionals).any {
@ -146,7 +146,7 @@ class TileInfoImprovementFunctions(val tile: Tile) {
// At this point we know this is a normal improvement and that there is no reason not to allow it to be built.
// Lastly we check if the improvement may be built on this terrain or resource
improvement.canBeBuiltOn(tile.getLastTerrain().name) -> true
improvement.canBeBuiltOn(tile.lastTerrain.name) -> true
tile.isLand && improvement.canBeBuiltOn("Land") -> true
tile.isWater && improvement.canBeBuiltOn("Water") -> true
// DO NOT reverse this &&. isAdjacentToFreshwater() is a lazy which calls a function, and reversing it breaks the tests.

View File

@ -79,8 +79,8 @@ class ImprovementPickerScreen(
// clone tileInfo without "top" feature if it could be removed
// Keep this copy around for speed
val tileWithoutLastTerrain: Tile = tile.clone()
if (Constants.remove + tileWithoutLastTerrain.getLastTerrain().name in ruleSet.tileImprovements) {
tileWithoutLastTerrain.removeTerrainFeature(tileWithoutLastTerrain.getLastTerrain().name)
if (Constants.remove + tileWithoutLastTerrain.lastTerrain.name in ruleSet.tileImprovements) {
tileWithoutLastTerrain.removeTerrainFeature(tileWithoutLastTerrain.lastTerrain.name)
}
val cityUniqueCache = LocalUniqueCache()
@ -133,7 +133,7 @@ class ImprovementPickerScreen(
val proposedSolutions = mutableListOf<String>()
if (suggestRemoval)
proposedSolutions.add("${Constants.remove}[${tile.getLastTerrain().name}] first")
proposedSolutions.add("${Constants.remove}[${tile.lastTerrain.name}] first")
if (ImprovementBuildingProblem.MissingTech in unbuildableBecause)
proposedSolutions.add("Research [${improvement.techRequired}] first")
if (ImprovementBuildingProblem.NotJustOutsideBorders in unbuildableBecause)