Allocation and runtime optimization with sequences

This commit is contained in:
Yair Morgenstern 2020-01-04 23:02:57 +02:00
parent 8f231cd6e1
commit 47e998269a
6 changed files with 32 additions and 28 deletions

View File

@ -101,7 +101,7 @@ class Automation {
chosenUnit = militaryUnits.filter { it.unitType== UnitType.Ranged }.maxBy { it.cost }!! chosenUnit = militaryUnits.filter { it.unitType== UnitType.Ranged }.maxBy { it.cost }!!
else{ // randomize type of unit and take the most expensive of its kind else{ // randomize type of unit and take the most expensive of its kind
val chosenUnitType = militaryUnits.map { it.unitType }.distinct().filterNot{it==UnitType.Scout}.random() val chosenUnitType = militaryUnits.map { it.unitType }.distinct().filterNot{it==UnitType.Scout}.toList().random()
chosenUnit = militaryUnits.filter { it.unitType==chosenUnitType }.maxBy { it.cost }!! chosenUnit = militaryUnits.filter { it.unitType==chosenUnitType }.maxBy { it.cost }!!
} }
return chosenUnit.name return chosenUnit.name

View File

@ -18,8 +18,10 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
val cityInfo = cityConstructions.cityInfo val cityInfo = cityConstructions.cityInfo
val civInfo = cityInfo.civInfo val civInfo = cityInfo.civInfo
val buildableNotWonders = cityConstructions.getBuildableBuildings().filterNot { it.isWonder || it.isNationalWonder } val buildableNotWonders = cityConstructions.getBuildableBuildings()
val buildableWonders = cityConstructions.getBuildableBuildings().filter { it.isWonder || it.isNationalWonder } .filterNot { it.isWonder || it.isNationalWonder }
val buildableWonders = cityConstructions.getBuildableBuildings()
.filter { it.isWonder || it.isNationalWonder }
val civUnits = civInfo.getCivUnits() val civUnits = civInfo.getCivUnits()
val militaryUnits = civUnits.filter { !it.type.isCivilian()}.size val militaryUnits = civUnits.filter { !it.type.isCivilian()}.size
@ -118,6 +120,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
private fun addWorkerChoice() { private fun addWorkerChoice() {
if(civInfo.getIdleUnits().any { it.name==Constants.worker && it.action== Constants.unitActionAutomation}) if(civInfo.getIdleUnits().any { it.name==Constants.worker && it.action== Constants.unitActionAutomation})
return // If we have automated workers who have no work to do then it's silly to construct new workers. return // If we have automated workers who have no work to do then it's silly to construct new workers.
val citiesCountedTowardsWorkers = min(5, cities) // above 5 cities, extra cities won't make us want more workers val citiesCountedTowardsWorkers = min(5, cities) // above 5 cities, extra cities won't make us want more workers
if (workers < citiesCountedTowardsWorkers * 0.6f && civUnits.none { it.name==Constants.worker && it.isIdle() }) { if (workers < citiesCountedTowardsWorkers * 0.6f && civUnits.none { it.name==Constants.worker && it.isIdle() }) {
var modifier = citiesCountedTowardsWorkers / (workers + 0.1f) var modifier = citiesCountedTowardsWorkers / (workers + 0.1f)
@ -127,7 +130,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
} }
private fun addCultureBuildingChoice() { private fun addCultureBuildingChoice() {
val cultureBuilding = buildableNotWonders.filter { it.isStatRelated(Stat.Culture) }.minBy { it.cost } val cultureBuilding = buildableNotWonders
.filter { it.isStatRelated(Stat.Culture) }.minBy { it.cost }
if (cultureBuilding != null) { if (cultureBuilding != null) {
var modifier = 0.5f var modifier = 0.5f
if(cityInfo.cityStats.currentCityStats.culture==0f) // It won't grow if we don't help it if(cityInfo.cityStats.currentCityStats.culture==0f) // It won't grow if we don't help it
@ -149,7 +153,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
} }
private fun addWondersChoice() { private fun addWondersChoice() {
if (buildableWonders.isNotEmpty()) { if (buildableWonders.any()) {
fun getWonderPriority(wonder: Building): Float { fun getWonderPriority(wonder: Building): Float {
if (preferredVictoryType == VictoryType.Cultural if (preferredVictoryType == VictoryType.Cultural
&& wonder.name in listOf("Sistine Chapel", "Eiffel Tower", "Cristo Redentor", "Neuschwanstein", "Sydney Opera House")) && wonder.name in listOf("Sistine Chapel", "Eiffel Tower", "Cristo Redentor", "Neuschwanstein", "Sydney Opera House"))
@ -167,21 +171,20 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
return 1f return 1f
} }
val wondersByPriority = buildableWonders val highestPriorityWonder = buildableWonders
.sortedByDescending { getWonderPriority(it) } .maxBy { getWonderPriority(it) }!!
val wonder = wondersByPriority.first()
val citiesBuildingWonders = civInfo.cities val citiesBuildingWonders = civInfo.cities
.count { it.cityConstructions.isBuildingWonder() } .count { it.cityConstructions.isBuildingWonder() }
var modifier = 2f * getWonderPriority(wonder) / (citiesBuildingWonders + 1) var modifier = 2f * getWonderPriority(highestPriorityWonder) / (citiesBuildingWonders + 1)
if (!cityIsOverAverageProduction) modifier /= 5 // higher production cities will deal with this if (!cityIsOverAverageProduction) modifier /= 5 // higher production cities will deal with this
addChoice(relativeCostEffectiveness, wonder.name, modifier) addChoice(relativeCostEffectiveness, highestPriorityWonder.name, modifier)
} }
} }
private fun addUnitTrainingBuildingChoice() { private fun addUnitTrainingBuildingChoice() {
val unitTrainingBuilding = buildableNotWonders.filter { it.xpForNewUnits > 0 } val unitTrainingBuilding = buildableNotWonders.asSequence()
.minBy { it.cost } .filter { it.xpForNewUnits > 0 }.minBy { it.cost }
if (unitTrainingBuilding != null && (preferredVictoryType != VictoryType.Cultural || isAtWar)) { 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 var modifier = if (cityIsOverAverageProduction) 0.5f else 0.1f // You shouldn't be cranking out units anytime soon
if (isAtWar) modifier *= 2 if (isAtWar) modifier *= 2
@ -192,8 +195,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
} }
private fun addDefenceBuildingChoice() { private fun addDefenceBuildingChoice() {
val defensiveBuilding = buildableNotWonders.filter { it.cityStrength > 0 } val defensiveBuilding = buildableNotWonders.asSequence()
.minBy { it.cost } .filter { it.cityStrength > 0 }.minBy { it.cost }
if (defensiveBuilding != null && (isAtWar || preferredVictoryType != VictoryType.Cultural)) { if (defensiveBuilding != null && (isAtWar || preferredVictoryType != VictoryType.Cultural)) {
var modifier = 0.2f var modifier = 0.2f
if (isAtWar) modifier = 0.5f if (isAtWar) modifier = 0.5f
@ -208,7 +211,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
} }
private fun addHappinessBuildingChoice() { private fun addHappinessBuildingChoice() {
val happinessBuilding = buildableNotWonders val happinessBuilding = buildableNotWonders.asSequence()
.filter { it.isStatRelated(Stat.Happiness) .filter { it.isStatRelated(Stat.Happiness)
|| it.uniques.contains("Remove extra unhappiness from annexed cities") } || it.uniques.contains("Remove extra unhappiness from annexed cities") }
.minBy { it.cost } .minBy { it.cost }
@ -222,7 +225,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
} }
private fun addScienceBuildingChoice() { private fun addScienceBuildingChoice() {
val scienceBuilding = buildableNotWonders.filter { it.isStatRelated(Stat.Science) || it.name=="Library" } // only stat related in unique val scienceBuilding = buildableNotWonders.asSequence()
.filter { it.isStatRelated(Stat.Science) || it.name=="Library" } // only stat related in unique
.minBy { it.cost } .minBy { it.cost }
if (scienceBuilding != null) { if (scienceBuilding != null) {
var modifier = 1.1f var modifier = 1.1f
@ -233,7 +237,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
} }
private fun addGoldBuildingChoice() { private fun addGoldBuildingChoice() {
val goldBuilding = buildableNotWonders.filter { it.isStatRelated(Stat.Gold) } val goldBuilding = buildableNotWonders.asSequence().filter { it.isStatRelated(Stat.Gold) }
.minBy { it.cost } .minBy { it.cost }
if (goldBuilding != null) { if (goldBuilding != null) {
val modifier = if (civInfo.statsForNextTurn.gold < 0) 3f else 1.2f val modifier = if (civInfo.statsForNextTurn.gold < 0) 3f else 1.2f
@ -244,7 +248,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
private fun addProductionBuildingChoice() { private fun addProductionBuildingChoice() {
val hasWaterResource = cityInfo.tilesInRange val hasWaterResource = cityInfo.tilesInRange
.any { it.isWater && it.resource!=null && it.position in cityInfo.tiles } .any { it.isWater && it.resource!=null && it.position in cityInfo.tiles }
val productionBuilding = buildableNotWonders val productionBuilding = buildableNotWonders.asSequence()
.filter { it.isStatRelated(Stat.Production) .filter { it.isStatRelated(Stat.Production)
|| (hasWaterResource && (it.uniques.contains("+1 production and gold from all sea resources worked by the city") || (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")) ) || it.uniques.contains("+1 production from all sea resources worked by the city")) )
@ -256,7 +260,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
} }
private fun addFoodBuildingChoice() { private fun addFoodBuildingChoice() {
val foodBuilding = buildableNotWonders.filter { it.isStatRelated(Stat.Food) 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 || it.getBaseBuilding(civInfo.gameInfo.ruleSet).name == "Aqueduct" || it.getBaseBuilding(civInfo.gameInfo.ruleSet).name == "Medical Lab"} // only stat related in unique
.minBy { it.cost } .minBy { it.cost }
if (foodBuilding != null) { if (foodBuilding != null) {

View File

@ -19,7 +19,7 @@ class SpecificUnitAutomation{
} }
fun automateWorkBoats(unit: MapUnit) { fun automateWorkBoats(unit: MapUnit) {
val seaResourcesInCities = unit.civInfo.cities.flatMap { city -> city.getWorkableTiles() } val seaResourcesInCities = unit.civInfo.cities.asSequence().flatMap { city -> city.getWorkableTiles() }
.filter { hasWorkableSeaResource(it, unit.civInfo) && (unit.movement.canMoveTo(it) || unit.currentTile == it) } .filter { hasWorkableSeaResource(it, unit.civInfo) && (unit.movement.canMoveTo(it) || unit.currentTile == it) }
val closestReachableResource = seaResourcesInCities.sortedBy { it.arialDistanceTo(unit.currentTile) } val closestReachableResource = seaResourcesInCities.sortedBy { it.arialDistanceTo(unit.currentTile) }
.firstOrNull { unit.movement.canReach(it) } .firstOrNull { unit.movement.canReach(it) }

View File

@ -6,8 +6,8 @@ import com.unciv.logic.automation.ConstructionAutomation
import com.unciv.logic.civilization.AlertType import com.unciv.logic.civilization.AlertType
import com.unciv.logic.civilization.PopupAlert import com.unciv.logic.civilization.PopupAlert
import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.Building
import com.unciv.models.translations.tr
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
import com.unciv.models.translations.tr
import com.unciv.ui.utils.withItem import com.unciv.ui.utils.withItem
import com.unciv.ui.utils.withoutItem import com.unciv.ui.utils.withoutItem
import java.util.* import java.util.*
@ -33,11 +33,11 @@ class CityConstructions {
return toReturn return toReturn
} }
internal fun getBuildableBuildings(): List<Building> = cityInfo.getRuleset().buildings.values internal fun getBuildableBuildings(): Sequence<Building> = cityInfo.getRuleset().buildings.values
.filter { it.isBuildable(this) } .asSequence().filter { it.isBuildable(this) }
fun getConstructableUnits() = cityInfo.getRuleset().units.values fun getConstructableUnits() = cityInfo.getRuleset().units.values
.filter { it.isBuildable(this) } .asSequence().filter { it.isBuildable(this) }
fun getStats(): Stats { fun getStats(): Stats {
val stats = Stats() val stats = Stats()
@ -109,7 +109,7 @@ class CityConstructions {
throw NotBuildingOrUnitException("$constructionName is not a building or a unit!") throw NotBuildingOrUnitException("$constructionName is not a building or a unit!")
} }
internal fun getBuiltBuildings(): List<Building> = builtBuildingObjects internal fun getBuiltBuildings(): Sequence<Building> = builtBuildingObjects.asSequence()
fun containsBuildingOrEquivalent(building: String): Boolean = fun containsBuildingOrEquivalent(building: String): Boolean =
isBuilt(building) || getBuiltBuildings().any{it.replaces==building} isBuilt(building) || getBuiltBuildings().any{it.replaces==building}

View File

@ -116,7 +116,7 @@ class CityInfo {
fun getCenterTile(): TileInfo = centerTileInfo fun getCenterTile(): TileInfo = centerTileInfo
fun getTiles(): List<TileInfo> = tiles.map { tileMap[it] } fun getTiles(): Sequence<TileInfo> = tiles.asSequence().map { tileMap[it] }
fun getWorkableTiles() = getTiles().filter { it in tilesInRange } fun getWorkableTiles() = getTiles().filter { it in tilesInRange }
fun isCapital() = cityConstructions.isBuilt("Palace") fun isCapital() = cityConstructions.isBuilt("Palace")
@ -192,7 +192,7 @@ class CityInfo {
return 0 return 0
} }
fun getBuildingUniques(): List<String> = cityConstructions.getBuiltBuildings().flatMap { it.uniques } fun getBuildingUniques(): Sequence<String> = cityConstructions.getBuiltBuildings().flatMap { it.uniques.asSequence() }
fun containsBuildingUnique(unique:String) = cityConstructions.getBuiltBuildings().any { it.uniques.contains(unique) } fun containsBuildingUnique(unique:String) = cityConstructions.getBuiltBuildings().any { it.uniques.contains(unique) }
fun getGreatPersonMap():HashMap<String,Stats>{ fun getGreatPersonMap():HashMap<String,Stats>{

View File

@ -492,7 +492,7 @@ class CivilizationInfo {
val city = NextTurnAutomation().getClosestCities(this, otherCiv).city1 val city = NextTurnAutomation().getClosestCities(this, otherCiv).city1
val militaryUnit = city.cityConstructions.getConstructableUnits() val militaryUnit = city.cityConstructions.getConstructableUnits()
.filter { !it.unitType.isCivilian() && it.unitType.isLandUnit() } .filter { !it.unitType.isCivilian() && it.unitType.isLandUnit() }
.random() .toList().random()
placeUnitNearTile(city.location, militaryUnit.name) placeUnitNearTile(city.location, militaryUnit.name)
addNotification("[${otherCiv.civName}] gave us a [${militaryUnit.name}] as gift near [${city.name}]!", null, Color.GREEN) addNotification("[${otherCiv.civName}] gave us a [${militaryUnit.name}] as gift near [${city.name}]!", null, Color.GREEN)
} }