Readability helpers for Map of Sets: add, contains (#10116)

This commit is contained in:
SomeTroglodyte
2023-09-18 08:46:55 +02:00
committed by GitHub
parent 29e32303ec
commit ae19a7bd0a
4 changed files with 32 additions and 16 deletions

View File

@ -31,6 +31,7 @@ import com.unciv.models.stats.Stat
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.components.Fonts 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.withItem
import com.unciv.ui.components.extensions.withoutItem import com.unciv.ui.components.extensions.withoutItem
import com.unciv.ui.screens.civilopediascreen.CivilopediaCategories import com.unciv.ui.screens.civilopediascreen.CivilopediaCategories
@ -39,7 +40,6 @@ import kotlin.math.ceil
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt import kotlin.math.roundToInt
/** /**
* City constructions manager. * City constructions manager.
* *
@ -604,7 +604,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
for (city in citiesThatApply) { for (city in citiesThatApply) {
if (city.cityConstructions.containsBuildingOrEquivalent(freeBuilding.name)) continue if (city.cityConstructions.containsBuildingOrEquivalent(freeBuilding.name)) continue
city.cityConstructions.addBuilding(freeBuilding) 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))) { for (unique in city.civ.getMatchingUniques(UniqueType.GainFreeBuildings, stateForConditionals = StateForConditionals(city.civ, city))) {
val freeBuilding = city.civ.getEquivalentBuilding(unique.params[0]) val freeBuilding = city.civ.getEquivalentBuilding(unique.params[0])
if (city.matchesFilter(unique.params[1])) { if (city.matchesFilter(unique.params[1])) {
freeBuildingsProvidedFromThisCity.getOrPut(city.id) { hashSetOf() }.add(freeBuilding.name) freeBuildingsProvidedFromThisCity.addToMapOfSets(city.id, freeBuilding.name)
if (!isBuilt(freeBuilding.name)) if (!isBuilt(freeBuilding.name))
addBuilding(freeBuilding) addBuilding(freeBuilding)
} }

View File

@ -8,6 +8,8 @@ import com.unciv.models.ruleset.INonPerpetualConstruction
import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.stats.Stat 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 import com.unciv.ui.components.extensions.yieldAllNotNull
class CivConstructions : IsPartOfGameInfoSerialization { class CivConstructions : IsPartOfGameInfoSerialization {
@ -88,8 +90,7 @@ class CivConstructions : IsPartOfGameInfoSerialization {
getFreeBuildingNamesSequence(cityId).contains(buildingName) getFreeBuildingNamesSequence(cityId).contains(buildingName)
private fun addFreeBuilding(cityId: String, building: String) { private fun addFreeBuilding(cityId: String, building: String) {
freeBuildings.getOrPut(cityId) { hashSetOf() } freeBuildings.addToMapOfSets(cityId, civInfo.getEquivalentBuilding(building).name)
.add(civInfo.getEquivalentBuilding(building).name)
} }
private fun addFreeStatsBuildings() { private fun addFreeStatsBuildings() {
@ -105,12 +106,12 @@ class CivConstructions : IsPartOfGameInfoSerialization {
private fun addFreeStatBuildings(stat: Stat, amount: Int) { private fun addFreeStatBuildings(stat: Stat, amount: Int) {
for (city in civInfo.cities.take(amount)) { 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 if (!city.cityConstructions.hasBuildableStatBuildings(stat)) continue
val builtBuilding = city.cityConstructions.addCheapestBuildableStatBuilding(stat) val builtBuilding = city.cityConstructions.addCheapestBuildableStatBuilding(stat)
if (builtBuilding != null) { if (builtBuilding != null) {
freeStatBuildingsProvided.getOrPut(stat.name) { hashSetOf() }.add(city.id) freeStatBuildingsProvided.addToMapOfSets(stat.name, city.id)
addFreeBuilding(city.id, builtBuilding) addFreeBuilding(city.id, builtBuilding)
} }
} }
@ -130,12 +131,12 @@ class CivConstructions : IsPartOfGameInfoSerialization {
private fun addFreeBuildings(building: Building, amount: Int) { private fun addFreeBuildings(building: Building, amount: Int) {
for (city in civInfo.cities.take(amount)) { 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 || city.cityConstructions.containsBuildingOrEquivalent(building.name)) continue
building.postBuildEvent(city.cityConstructions) building.postBuildEvent(city.cityConstructions)
freeSpecificBuildingsProvided.getOrPut(building.name) { hashSetOf() }.add(city.id) freeSpecificBuildingsProvided.addToMapOfSets(building.name, city.id)
addFreeBuilding(city.id, building.name) addFreeBuilding(city.id, building.name)
} }
} }

View File

@ -15,6 +15,8 @@ import com.unciv.models.ruleset.tile.TerrainType
import com.unciv.models.ruleset.unique.UniqueMap import com.unciv.models.ruleset.unique.UniqueMap
import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.ruleset.unit.BaseUnit 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.lang.Integer.max
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import kotlin.math.abs import kotlin.math.abs
@ -92,7 +94,7 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization {
get() = tileList get() = tileList
@Transient @Transient
val startingLocationsByNation = HashMap<String,HashSet<Tile>>() val startingLocationsByNation = HashMap<String, HashSet<Tile>>()
@Transient @Transient
/** Continent ID to Continent size */ /** Continent ID to Continent size */
@ -646,8 +648,7 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization {
return translateStartingLocationsFromMap() return translateStartingLocationsFromMap()
startingLocationsByNation.clear() startingLocationsByNation.clear()
for ((position, nationName) in startingLocations) { for ((position, nationName) in startingLocations) {
val nationSet = startingLocationsByNation[nationName] ?: hashSetOf<Tile>().also { startingLocationsByNation[nationName] = it } startingLocationsByNation.addToMapOfSets(nationName, get(position))
nationSet.add(get(position))
} }
} }
@ -670,16 +671,15 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization {
/** Adds a starting position, maintaining the transients /** Adds a starting position, maintaining the transients
* @return true if the starting position was not already stored as per [Collection]'s add */ * @return true if the starting position was not already stored as per [Collection]'s add */
fun addStartingLocation(nationName: String, tile: Tile): Boolean { 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)) startingLocations.add(StartingLocation(tile.position, nationName))
val nationSet = startingLocationsByNation[nationName] ?: hashSetOf<Tile>().also { startingLocationsByNation[nationName] = it } return startingLocationsByNation.addToMapOfSets(nationName, tile)
return nationSet.add(tile)
} }
/** Removes a starting position, maintaining the transients /** Removes a starting position, maintaining the transients
* @return true if the starting position was removed as per [Collection]'s remove */ * @return true if the starting position was removed as per [Collection]'s remove */
fun removeStartingLocation(nationName: String, tile: Tile): Boolean { 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)) startingLocations.remove(StartingLocation(tile.position, nationName))
return startingLocationsByNation[nationName]!!.remove(tile) return startingLocationsByNation[nationName]!!.remove(tile)
// we do not clean up an empty startingLocationsByNation[nationName] set - not worth it // we do not clean up an empty startingLocationsByNation[nationName] set - not worth it

View File

@ -83,3 +83,18 @@ suspend fun <T> SequenceScope<T>.yieldAllNotNull(elements: Iterable<T?>?) {
if (elements == null) return if (elements == null) return
for (element in elements) yieldIfNotNull(element) 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 <KT, ET> HashMap<KT, HashSet<ET>>.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 <KT, ET> HashMap<KT, HashSet<ET>>.contains(key: KT, element: ET) =
get(key)?.contains(element) == true