From ae19a7bd0ac0a3c10a5b7cd9ffd98ba79b98a073 Mon Sep 17 00:00:00 2001 From: SomeTroglodyte <63000004+SomeTroglodyte@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:46:55 +0200 Subject: [PATCH] Readability helpers for Map of Sets: add, contains (#10116) --- .../src/com/unciv/logic/city/CityConstructions.kt | 6 +++--- .../unciv/logic/civilization/CivConstructions.kt | 13 +++++++------ core/src/com/unciv/logic/map/TileMap.kt | 14 +++++++------- .../components/extensions/CollectionExtensions.kt | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index 49e21c5eb7..d033c8d648 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -31,6 +31,7 @@ import com.unciv.models.stats.Stat import com.unciv.models.stats.Stats import com.unciv.models.translations.tr import com.unciv.ui.components.Fonts +import com.unciv.ui.components.extensions.addToMapOfSets import com.unciv.ui.components.extensions.withItem import com.unciv.ui.components.extensions.withoutItem import com.unciv.ui.screens.civilopediascreen.CivilopediaCategories @@ -39,7 +40,6 @@ import kotlin.math.ceil import kotlin.math.min import kotlin.math.roundToInt - /** * City constructions manager. * @@ -604,7 +604,7 @@ class CityConstructions : IsPartOfGameInfoSerialization { for (city in citiesThatApply) { if (city.cityConstructions.containsBuildingOrEquivalent(freeBuilding.name)) continue city.cityConstructions.addBuilding(freeBuilding) - freeBuildingsProvidedFromThisCity.getOrPut(city.id) { hashSetOf() }.add(freeBuilding.name) + freeBuildingsProvidedFromThisCity.addToMapOfSets(city.id, freeBuilding.name) } } @@ -612,7 +612,7 @@ class CityConstructions : IsPartOfGameInfoSerialization { for (unique in city.civ.getMatchingUniques(UniqueType.GainFreeBuildings, stateForConditionals = StateForConditionals(city.civ, city))) { val freeBuilding = city.civ.getEquivalentBuilding(unique.params[0]) if (city.matchesFilter(unique.params[1])) { - freeBuildingsProvidedFromThisCity.getOrPut(city.id) { hashSetOf() }.add(freeBuilding.name) + freeBuildingsProvidedFromThisCity.addToMapOfSets(city.id, freeBuilding.name) if (!isBuilt(freeBuilding.name)) addBuilding(freeBuilding) } diff --git a/core/src/com/unciv/logic/civilization/CivConstructions.kt b/core/src/com/unciv/logic/civilization/CivConstructions.kt index c86c40ddd0..7b7db0675f 100644 --- a/core/src/com/unciv/logic/civilization/CivConstructions.kt +++ b/core/src/com/unciv/logic/civilization/CivConstructions.kt @@ -8,6 +8,8 @@ import com.unciv.models.ruleset.INonPerpetualConstruction import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.stats.Stat +import com.unciv.ui.components.extensions.addToMapOfSets +import com.unciv.ui.components.extensions.contains import com.unciv.ui.components.extensions.yieldAllNotNull class CivConstructions : IsPartOfGameInfoSerialization { @@ -88,8 +90,7 @@ class CivConstructions : IsPartOfGameInfoSerialization { getFreeBuildingNamesSequence(cityId).contains(buildingName) private fun addFreeBuilding(cityId: String, building: String) { - freeBuildings.getOrPut(cityId) { hashSetOf() } - .add(civInfo.getEquivalentBuilding(building).name) + freeBuildings.addToMapOfSets(cityId, civInfo.getEquivalentBuilding(building).name) } private fun addFreeStatsBuildings() { @@ -105,12 +106,12 @@ class CivConstructions : IsPartOfGameInfoSerialization { private fun addFreeStatBuildings(stat: Stat, amount: Int) { for (city in civInfo.cities.take(amount)) { - if (freeStatBuildingsProvided[stat.name]?.contains(city.id) == true) continue + if (freeStatBuildingsProvided.contains(stat.name, city.id)) continue if (!city.cityConstructions.hasBuildableStatBuildings(stat)) continue val builtBuilding = city.cityConstructions.addCheapestBuildableStatBuilding(stat) if (builtBuilding != null) { - freeStatBuildingsProvided.getOrPut(stat.name) { hashSetOf() }.add(city.id) + freeStatBuildingsProvided.addToMapOfSets(stat.name, city.id) addFreeBuilding(city.id, builtBuilding) } } @@ -130,12 +131,12 @@ class CivConstructions : IsPartOfGameInfoSerialization { private fun addFreeBuildings(building: Building, amount: Int) { for (city in civInfo.cities.take(amount)) { - if (freeSpecificBuildingsProvided[building.name]?.contains(city.id) == true + if (freeSpecificBuildingsProvided.contains(building.name, city.id) || city.cityConstructions.containsBuildingOrEquivalent(building.name)) continue building.postBuildEvent(city.cityConstructions) - freeSpecificBuildingsProvided.getOrPut(building.name) { hashSetOf() }.add(city.id) + freeSpecificBuildingsProvided.addToMapOfSets(building.name, city.id) addFreeBuilding(city.id, building.name) } } diff --git a/core/src/com/unciv/logic/map/TileMap.kt b/core/src/com/unciv/logic/map/TileMap.kt index 32779c0a95..ad4f65f242 100644 --- a/core/src/com/unciv/logic/map/TileMap.kt +++ b/core/src/com/unciv/logic/map/TileMap.kt @@ -15,6 +15,8 @@ import com.unciv.models.ruleset.tile.TerrainType import com.unciv.models.ruleset.unique.UniqueMap import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unit.BaseUnit +import com.unciv.ui.components.extensions.addToMapOfSets +import com.unciv.ui.components.extensions.contains import java.lang.Integer.max import java.util.concurrent.ConcurrentHashMap import kotlin.math.abs @@ -92,7 +94,7 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization { get() = tileList @Transient - val startingLocationsByNation = HashMap>() + val startingLocationsByNation = HashMap>() @Transient /** Continent ID to Continent size */ @@ -646,8 +648,7 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization { return translateStartingLocationsFromMap() startingLocationsByNation.clear() for ((position, nationName) in startingLocations) { - val nationSet = startingLocationsByNation[nationName] ?: hashSetOf().also { startingLocationsByNation[nationName] = it } - nationSet.add(get(position)) + startingLocationsByNation.addToMapOfSets(nationName, get(position)) } } @@ -670,16 +671,15 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization { /** Adds a starting position, maintaining the transients * @return true if the starting position was not already stored as per [Collection]'s add */ fun addStartingLocation(nationName: String, tile: Tile): Boolean { - if (startingLocationsByNation[nationName]?.contains(tile) == true) return false + if (startingLocationsByNation.contains(nationName, tile)) return false startingLocations.add(StartingLocation(tile.position, nationName)) - val nationSet = startingLocationsByNation[nationName] ?: hashSetOf().also { startingLocationsByNation[nationName] = it } - return nationSet.add(tile) + return startingLocationsByNation.addToMapOfSets(nationName, tile) } /** Removes a starting position, maintaining the transients * @return true if the starting position was removed as per [Collection]'s remove */ fun removeStartingLocation(nationName: String, tile: Tile): Boolean { - if (startingLocationsByNation[nationName]?.contains(tile) != true) return false + if (startingLocationsByNation.contains(nationName, tile)) return false startingLocations.remove(StartingLocation(tile.position, nationName)) return startingLocationsByNation[nationName]!!.remove(tile) // we do not clean up an empty startingLocationsByNation[nationName] set - not worth it diff --git a/core/src/com/unciv/ui/components/extensions/CollectionExtensions.kt b/core/src/com/unciv/ui/components/extensions/CollectionExtensions.kt index 611852749b..7b81eac3ca 100644 --- a/core/src/com/unciv/ui/components/extensions/CollectionExtensions.kt +++ b/core/src/com/unciv/ui/components/extensions/CollectionExtensions.kt @@ -83,3 +83,18 @@ suspend fun SequenceScope.yieldAllNotNull(elements: Iterable?) { if (elements == null) return for (element in elements) yieldIfNotNull(element) } + +/** + * Simplifies adding to a map of sets where the map entry where the new element belongs is not + * guaranteed to be already present in the map (sparse map). + * + * @param key The key identifying the Set to add [element] to + * @param element The new element to be added to the Set for [key] + * @return `false` if the element was already present, `true` if it was new (same as `Set.add()`) + */ +fun HashMap>.addToMapOfSets(key: KT, element: ET) = + getOrPut(key) { hashSetOf() }.add(element) + +/** Simplifies testing whether in a sparse map of sets the [element] exists for [key]. */ +fun HashMap>.contains(key: KT, element: ET) = + get(key)?.contains(element) == true