Added an AI for building & using spaceship parts (#6374)

This commit is contained in:
Xander Lenstra
2022-03-21 20:05:43 +01:00
committed by GitHub
parent f6a989f1fc
commit dcd8e6c845
5 changed files with 46 additions and 23 deletions

View File

@ -26,6 +26,8 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
.filterNot { it.isAnyWonder() } .filterNot { it.isAnyWonder() }
private val buildableWonders = buildableBuildings private val buildableWonders = buildableBuildings
.filter { it.isAnyWonder() } .filter { it.isAnyWonder() }
val buildableUnits = cityConstructions.getConstructableUnits()
val civUnits = civInfo.getCivUnits() val civUnits = civInfo.getCivUnits()
val militaryUnits = civUnits.count { it.baseUnit.isMilitary() } val militaryUnits = civUnits.count { it.baseUnit.isMilitary() }
@ -53,8 +55,9 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
fun chooseNextConstruction() { fun chooseNextConstruction() {
if (!UncivGame.Current.settings.autoAssignCityProduction if (!UncivGame.Current.settings.autoAssignCityProduction
&& civInfo.playerType == PlayerType.Human && !cityInfo.isPuppet && civInfo.playerType == PlayerType.Human && !cityInfo.isPuppet
) ) {
return return
}
if (cityConstructions.getCurrentConstruction() !is PerpetualConstruction) return // don't want to be stuck on these forever if (cityConstructions.getCurrentConstruction() !is PerpetualConstruction) return // don't want to be stuck on these forever
addFoodBuildingChoice() addFoodBuildingChoice()
@ -65,15 +68,15 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
addDefenceBuildingChoice() addDefenceBuildingChoice()
addUnitTrainingBuildingChoice() addUnitTrainingBuildingChoice()
addCultureBuildingChoice() addCultureBuildingChoice()
addSpaceshipPartChoice()
addOtherBuildingChoice() addOtherBuildingChoice()
addReligiousUnit()
if (!cityInfo.isPuppet) { if (!cityInfo.isPuppet) {
addSpaceshipPartChoice()
addWondersChoice() addWondersChoice()
addWorkerChoice() addWorkerChoice()
addWorkBoatChoice() addWorkBoatChoice()
addMilitaryUnitChoice() addMilitaryUnitChoice()
addReligiousUnit()
} }
val production = cityInfo.cityStats.currentCityStats.production val production = cityInfo.cityStats.currentCityStats.production
@ -138,15 +141,16 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
} }
private fun addWorkBoatChoice() { private fun addWorkBoatChoice() {
val buildableWorkboatUnits = cityInfo.cityConstructions.getConstructableUnits() val buildableWorkboatUnits = buildableUnits
.filter { .filter {
it.hasUnique(UniqueType.CreateWaterImprovements) it.hasUnique(UniqueType.CreateWaterImprovements)
&& Automation.allowSpendingResource(civInfo, it) && Automation.allowSpendingResource(civInfo, it)
} }
val canBuildWorkboat = buildableWorkboatUnits.any() val alreadyHasWorkBoat = buildableWorkboatUnits.any()
&& !cityInfo.getTiles() && !cityInfo.getTiles().any {
.any { it.civilianUnit?.hasUnique(UniqueType.CreateWaterImprovements) == true } it.civilianUnit?.hasUnique(UniqueType.CreateWaterImprovements) == true
if (!canBuildWorkboat) return }
if (!alreadyHasWorkBoat) return
val bfs = BFS(cityInfo.getCenterTile()) { val bfs = BFS(cityInfo.getCenterTile()) {
@ -167,12 +171,12 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
} }
private fun addWorkerChoice() { private fun addWorkerChoice() {
val workerEquivalents = civInfo.gameInfo.ruleSet.units.values val workerEquivalents = buildableUnits
.filter { .filter {
it.hasUnique(UniqueType.BuildImprovements) it.hasUnique(UniqueType.BuildImprovements)
&& it.isBuildable(cityConstructions) && Automation.allowSpendingResource(civInfo, it)
&& Automation.allowSpendingResource(civInfo, it) } }
if (workerEquivalents.isEmpty()) return // for mods with no worker units if (workerEquivalents.none()) return // for mods with no worker units
if (workers < cities) { if (workers < cities) {
var modifier = cities / (workers + 0.1f) // The worse our worker to city ratio is, the more desperate we are var modifier = cities / (workers + 0.1f) // The worse our worker to city ratio is, the more desperate we are
@ -195,7 +199,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
} }
private fun addSpaceshipPartChoice() { private fun addSpaceshipPartChoice() {
val spaceshipPart = buildableNotWonders.firstOrNull { it.hasUnique(UniqueType.SpaceshipPart) } val spaceshipPart = (buildableNotWonders + buildableUnits).firstOrNull { it.hasUnique(UniqueType.SpaceshipPart) }
if (spaceshipPart != null) { if (spaceshipPart != null) {
var modifier = 1.5f var modifier = 1.5f
if (preferredVictoryType == VictoryType.Scientific) modifier = 2f if (preferredVictoryType == VictoryType.Scientific) modifier = 2f
@ -358,12 +362,12 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
// The performance of the regular getMatchingUniques is better, since it only tries to find one unique, // The performance of the regular getMatchingUniques is better, since it only tries to find one unique,
// while the canBePurchasedWithStat tries (at time of writing) *6* different uniques. // while the canBePurchasedWithStat tries (at time of writing) *6* different uniques.
val missionary = cityInfo.getRuleset().units.values val missionary = buildableUnits
.firstOrNull { it -> it.getMatchingUniques("Can [] [] times").any { it.params[0] == "Spread Religion"} .firstOrNull { it -> it.getMatchingUniques("Can [] [] times").any { it.params[0] == "Spread Religion"}
&& it.canBePurchasedWithStat(cityInfo, Stat.Faith) } && it.canBePurchasedWithStat(cityInfo, Stat.Faith) }
val inquisitor = cityInfo.getRuleset().units.values val inquisitor = buildableUnits
.firstOrNull { it.hasUnique("Prevents spreading of religion to the city it is next to") .firstOrNull { it.hasUnique("Prevents spreading of religion to the city it is next to")
&& it.canBePurchasedWithStat(cityInfo, Stat.Faith) } && it.canBePurchasedWithStat(cityInfo, Stat.Faith) }
@ -374,7 +378,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
val citiesNotFollowingOurReligion = civInfo.cities.asSequence() val citiesNotFollowingOurReligion = civInfo.cities.asSequence()
.filterNot { it.religion.getMajorityReligion()?.name == civInfo.religionManager.religion!!.name } .filterNot { it.religion.getMajorityReligion()?.name == civInfo.religionManager.religion!!.name }
val buildInqusitor = citiesNotFollowingOurReligion val buildInquisitor = citiesNotFollowingOurReligion
.filter { it.religion.getMajorityReligion()?.name == civInfo.religionManager.religion?.name } .filter { it.religion.getMajorityReligion()?.name == civInfo.religionManager.religion?.name }
.toList().size.toFloat() / 10 + modifier .toList().size.toFloat() / 10 + modifier
@ -383,7 +387,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
val buildMissionary = possibleSpreadReligionTargets.toList().size.toFloat() / 15 + modifier val buildMissionary = possibleSpreadReligionTargets.toList().size.toFloat() / 15 + modifier
if (buildMissionary > buildInqusitor && missionary != null) faithConstruction.add(missionary) if (buildMissionary > buildInquisitor && missionary != null) faithConstruction.add(missionary)
else if(inquisitor != null) faithConstruction.add(inquisitor) else if(inquisitor != null) faithConstruction.add(inquisitor)
} }

View File

@ -282,6 +282,15 @@ object SpecificUnitAutomation {
} }
} }
fun automateAddInCapital(unit: MapUnit) {
val capitalTile = unit.civInfo.getCapital().getCenterTile()
val unitTile = unit.movement.headTowards(capitalTile)
if (unitTile == capitalTile) {
UnitActions.getAddInCapitalAction(unit, unitTile).action!!()
return
}
}
fun automateMissionary(unit: MapUnit) { fun automateMissionary(unit: MapUnit) {
if (unit.religion != unit.civInfo.religionManager.religion?.name) if (unit.religion != unit.civInfo.religionManager.religion?.name)
return unit.destroy() return unit.destroy()

View File

@ -153,6 +153,11 @@ object UnitAutomation {
if (unit.hasUnique(UniqueType.CreateWaterImprovements)) if (unit.hasUnique(UniqueType.CreateWaterImprovements))
return SpecificUnitAutomation.automateWorkBoats(unit) return SpecificUnitAutomation.automateWorkBoats(unit)
// We try to add any unit in the capital we can, though that might not always be desirable
// For now its a simple option to allow AI to win a science victory again
if (unit.hasUnique(UniqueType.AddInCapital))
return SpecificUnitAutomation.automateAddInCapital(unit)
if (unit.hasUnique("Bonus for units in 2 tile radius 15%")) if (unit.hasUnique("Bonus for units in 2 tile radius 15%"))
return SpecificUnitAutomation.automateGreatGeneral(unit) return SpecificUnitAutomation.automateGreatGeneral(unit)

View File

@ -71,6 +71,7 @@ class CityConstructions {
return toReturn return toReturn
} }
// Why is one of these called 'buildable' and the other 'constructable'?
internal fun getBuildableBuildings(): Sequence<Building> = cityInfo.getRuleset().buildings.values internal fun getBuildableBuildings(): Sequence<Building> = cityInfo.getRuleset().buildings.values
.asSequence().filter { it.isBuildable(this) } .asSequence().filter { it.isBuildable(this) }

View File

@ -407,11 +407,9 @@ object UnitActions {
}.takeIf { unit.currentMovement > 0 } }.takeIf { unit.currentMovement > 0 }
) )
} }
private fun addAddInCapitalAction(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) { fun getAddInCapitalAction(unit: MapUnit, tile: TileInfo): UnitAction {
if (!unit.hasUnique(UniqueType.AddInCapital)) return return UnitAction(UnitActionType.AddInCapital,
actionList += UnitAction(UnitActionType.AddInCapital,
title = "Add to [${unit.getMatchingUniques(UniqueType.AddInCapital).first().params[0]}]", title = "Add to [${unit.getMatchingUniques(UniqueType.AddInCapital).first().params[0]}]",
action = { action = {
unit.civInfo.victoryManager.currentsSpaceshipParts.add(unit.name, 1) unit.civInfo.victoryManager.currentsSpaceshipParts.add(unit.name, 1)
@ -420,6 +418,12 @@ object UnitActions {
) )
} }
private fun addAddInCapitalAction(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) {
if (!unit.hasUnique(UniqueType.AddInCapital)) return
actionList += getAddInCapitalAction(unit, tile)
}
private fun addGreatPersonActions(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) { private fun addGreatPersonActions(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) {