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

View File

@ -646,9 +646,9 @@ class MapRegions (val ruleset: Ruleset){
val validBonuses = ruleset.tileResources.values.filter { val validBonuses = ruleset.tileResources.values.filter {
it.resourceType == ResourceType.Bonus && it.resourceType == ResourceType.Bonus &&
it.food >= 1 && 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 (validBonuses.isNotEmpty() || goodPlotForOasis) {
if (goodPlotForOasis) { if (goodPlotForOasis) {
@ -699,7 +699,7 @@ class MapRegions (val ruleset: Ruleset){
if (plot.resource != null) continue 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) { if (bonusToPlace != null) {
plot.resource = bonusToPlace.name plot.resource = bonusToPlace.name
stoneNeeded-- stoneNeeded--
@ -715,7 +715,7 @@ class MapRegions (val ruleset: Ruleset){
val bestImprovementYield = tile.tileMap.ruleset!!.tileImprovements.values val bestImprovementYield = tile.tileMap.ruleset!!.tileImprovements.values
.filter { !it.hasUnique(UniqueType.GreatImprovement) && .filter { !it.hasUnique(UniqueType.GreatImprovement) &&
it.uniqueTo == null && it.uniqueTo == null &&
tile.getLastTerrain().name in it.terrainsCanBeBuiltOn } tile.lastTerrain.name in it.terrainsCanBeBuiltOn }
.maxOfOrNull { it[stat] } .maxOfOrNull { it[stat] }
return baseYield + (bestImprovementYield ?: 0f) return baseYield + (bestImprovementYield ?: 0f)
} }
@ -1334,7 +1334,7 @@ class MapRegions (val ruleset: Ruleset){
continue continue
val weightings = strategicResources.map { val weightings = strategicResources.map {
if (fallbackStrategic) { if (fallbackStrategic) {
if (tile.getLastTerrain().name in it.terrainsCanBeFoundOn) 1f else 0f if (tile.lastTerrain.name in it.terrainsCanBeFoundOn) 1f else 0f
} else { } else {
val uniques = it.getMatchingUniques(UniqueType.MinorDepositWeighting, conditionalTerrain).toList() val uniques = it.getMatchingUniques(UniqueType.MinorDepositWeighting, conditionalTerrain).toList()
uniques.sumOf { unique -> unique.params[0].toInt() }.toFloat() uniques.sumOf { unique -> unique.params[0].toInt() }.toFloat()
@ -1391,7 +1391,7 @@ class MapRegions (val ruleset: Ruleset){
if(fallbackBonuses && resource.resourceType == ResourceType.Bonus) { 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 // 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. // 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) 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) { for (tile in tiles) {
val conditionalTerrain = StateForConditionals(attackedTile = tile) val conditionalTerrain = StateForConditionals(attackedTile = tile)
if (tile.resource == null && if (tile.resource == null &&
tile.getLastTerrain().name in resource.terrainsCanBeFoundOn && tile.lastTerrain.name in resource.terrainsCanBeFoundOn &&
!tile.getBaseTerrain().hasUnique(UniqueType.BlocksResources, conditionalTerrain) && !tile.getBaseTerrain().hasUnique(UniqueType.BlocksResources, conditionalTerrain) &&
!resource.hasUnique(UniqueType.NoNaturalGeneration, conditionalTerrain) && !resource.hasUnique(UniqueType.NoNaturalGeneration, conditionalTerrain) &&
resource.getMatchingUniques(UniqueType.TileGenerationConditions).none { resource.getMatchingUniques(UniqueType.TileGenerationConditions).none {
@ -1515,7 +1515,7 @@ class MapRegions (val ruleset: Ruleset){
for (tile in tileList) { for (tile in tileList) {
if (tile.resource != null || if (tile.resource != null ||
(testTerrains && (testTerrains &&
(tile.getLastTerrain().name !in resourceOptions.first().terrainsCanBeFoundOn || (tile.lastTerrain.name !in resourceOptions.first().terrainsCanBeFoundOn ||
resourceOptions.first().hasUnique(UniqueType.NoNaturalGeneration, conditionalTerrain)) ) || resourceOptions.first().hasUnique(UniqueType.NoNaturalGeneration, conditionalTerrain)) ) ||
tile.getBaseTerrain().hasUnique(UniqueType.BlocksResources, conditionalTerrain)) tile.getBaseTerrain().hasUnique(UniqueType.BlocksResources, conditionalTerrain))
continue // Can't place here, can't be a fallback tile 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-> val suitableLocations = tileMap.values.filter { tile->
tile.resource == null && tile.resource == null &&
naturalWonder.occursOn.contains(tile.getLastTerrain().name) && naturalWonder.occursOn.contains(tile.lastTerrain.name) &&
naturalWonder.uniqueObjects.all { unique -> naturalWonder.uniqueObjects.all { unique ->
when (unique.type) { when (unique.type) {
UniqueType.NaturalWonderNeighborCount -> { UniqueType.NaturalWonderNeighborCount -> {
@ -215,7 +215,7 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
"Land" -> isLand "Land" -> isLand
Constants.hill -> isHill() Constants.hill -> isHill()
naturalWonder -> true naturalWonder -> true
in allTerrainFeatures -> getLastTerrain().name == filter in allTerrainFeatures -> lastTerrain.name == filter
else -> baseTerrain == filter else -> baseTerrain == filter
} }

View File

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

View File

@ -96,6 +96,11 @@ open class Tile : IsPartOfGameInfoSerialization {
var allTerrains: Sequence<Terrain> = sequenceOf() var allTerrains: Sequence<Terrain> = sequenceOf()
private set private set
@Transient
/** Saves a sequence of a list */
lateinit var lastTerrain: Terrain
private set
@Transient @Transient
var terrainUniqueMap = UniqueMap() var terrainUniqueMap = UniqueMap()
private set private set
@ -205,13 +210,6 @@ open class Tile : IsPartOfGameInfoSerialization {
fun getCity(): City? = owningCity 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 @Transient
private var tileResourceCache: TileResource? = null private var tileResourceCache: TileResource? = null
val tileResource: TileResource val tileResource: TileResource
@ -257,7 +255,7 @@ open class Tile : IsPartOfGameInfoSerialization {
fun isCityCenter(): Boolean = isCityCenterInternal fun isCityCenter(): Boolean = isCityCenterInternal
fun isNaturalWonder(): Boolean = naturalWonder != null 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 getTileImprovement(): TileImprovement? = if (improvement == null) null else ruleset.tileImprovements[improvement!!]
fun getUnpillagedTileImprovement(): TileImprovement? = if (getUnpillagedImprovement() == 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() val newUniqueMap = UniqueMap()
for (terrain in allTerrains) for (terrain in allTerrains)
newUniqueMap.addUniques(terrain.uniqueObjects) 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 terrainUniqueMap = newUniqueMap
} }

View File

@ -77,7 +77,7 @@ class TileInfoImprovementFunctions(val tile: Tile) {
fun TileImprovement.canBeBuildOnThisUnbuildableTerrain( fun TileImprovement.canBeBuildOnThisUnbuildableTerrain(
knownFeatureRemovals: List<TileImprovement>? = null, knownFeatureRemovals: List<TileImprovement>? = null,
): Boolean { ): Boolean {
val topTerrain = tile.getLastTerrain() val topTerrain = tile.lastTerrain
// We can build if we are specifically allowed to build on this terrain // We can build if we are specifically allowed to build on this terrain
if (isAllowedOnFeature(topTerrain.name)) return true 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 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 // 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 // Can't build if any terrain specifically prevents building this improvement
tile.getTerrainMatchingUniques(UniqueType.RestrictedBuildableImprovements, stateForConditionals).any { 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. // 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 // 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.isLand && improvement.canBeBuiltOn("Land") -> true
tile.isWater && improvement.canBeBuiltOn("Water") -> 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. // 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 // clone tileInfo without "top" feature if it could be removed
// Keep this copy around for speed // Keep this copy around for speed
val tileWithoutLastTerrain: Tile = tile.clone() val tileWithoutLastTerrain: Tile = tile.clone()
if (Constants.remove + tileWithoutLastTerrain.getLastTerrain().name in ruleSet.tileImprovements) { if (Constants.remove + tileWithoutLastTerrain.lastTerrain.name in ruleSet.tileImprovements) {
tileWithoutLastTerrain.removeTerrainFeature(tileWithoutLastTerrain.getLastTerrain().name) tileWithoutLastTerrain.removeTerrainFeature(tileWithoutLastTerrain.lastTerrain.name)
} }
val cityUniqueCache = LocalUniqueCache() val cityUniqueCache = LocalUniqueCache()
@ -133,7 +133,7 @@ class ImprovementPickerScreen(
val proposedSolutions = mutableListOf<String>() val proposedSolutions = mutableListOf<String>()
if (suggestRemoval) if (suggestRemoval)
proposedSolutions.add("${Constants.remove}[${tile.getLastTerrain().name}] first") proposedSolutions.add("${Constants.remove}[${tile.lastTerrain.name}] first")
if (ImprovementBuildingProblem.MissingTech in unbuildableBecause) if (ImprovementBuildingProblem.MissingTech in unbuildableBecause)
proposedSolutions.add("Research [${improvement.techRequired}] first") proposedSolutions.add("Research [${improvement.techRequired}] first")
if (ImprovementBuildingProblem.NotJustOutsideBorders in unbuildableBecause) if (ImprovementBuildingProblem.NotJustOutsideBorders in unbuildableBecause)