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() }
private val buildableWonders = buildableBuildings
.filter { it.isAnyWonder() }
val buildableUnits = cityConstructions.getConstructableUnits()
val civUnits = civInfo.getCivUnits()
val militaryUnits = civUnits.count { it.baseUnit.isMilitary() }
@ -53,8 +55,9 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
fun chooseNextConstruction() {
if (!UncivGame.Current.settings.autoAssignCityProduction
&& civInfo.playerType == PlayerType.Human && !cityInfo.isPuppet
)
) {
return
}
if (cityConstructions.getCurrentConstruction() !is PerpetualConstruction) return // don't want to be stuck on these forever
addFoodBuildingChoice()
@ -65,15 +68,15 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
addDefenceBuildingChoice()
addUnitTrainingBuildingChoice()
addCultureBuildingChoice()
addSpaceshipPartChoice()
addOtherBuildingChoice()
addReligiousUnit()
if (!cityInfo.isPuppet) {
addSpaceshipPartChoice()
addWondersChoice()
addWorkerChoice()
addWorkBoatChoice()
addMilitaryUnitChoice()
addReligiousUnit()
}
val production = cityInfo.cityStats.currentCityStats.production
@ -138,15 +141,16 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
}
private fun addWorkBoatChoice() {
val buildableWorkboatUnits = cityInfo.cityConstructions.getConstructableUnits()
val buildableWorkboatUnits = buildableUnits
.filter {
it.hasUnique(UniqueType.CreateWaterImprovements)
&& Automation.allowSpendingResource(civInfo, it)
&& Automation.allowSpendingResource(civInfo, it)
}
val canBuildWorkboat = buildableWorkboatUnits.any()
&& !cityInfo.getTiles()
.any { it.civilianUnit?.hasUnique(UniqueType.CreateWaterImprovements) == true }
if (!canBuildWorkboat) return
val alreadyHasWorkBoat = buildableWorkboatUnits.any()
&& !cityInfo.getTiles().any {
it.civilianUnit?.hasUnique(UniqueType.CreateWaterImprovements) == true
}
if (!alreadyHasWorkBoat) return
val bfs = BFS(cityInfo.getCenterTile()) {
@ -167,12 +171,12 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
}
private fun addWorkerChoice() {
val workerEquivalents = civInfo.gameInfo.ruleSet.units.values
val workerEquivalents = buildableUnits
.filter {
it.hasUnique(UniqueType.BuildImprovements)
&& it.isBuildable(cityConstructions)
&& Automation.allowSpendingResource(civInfo, it) }
if (workerEquivalents.isEmpty()) return // for mods with no worker units
&& Automation.allowSpendingResource(civInfo, it)
}
if (workerEquivalents.none()) return // for mods with no worker units
if (workers < cities) {
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() {
val spaceshipPart = buildableNotWonders.firstOrNull { it.hasUnique(UniqueType.SpaceshipPart) }
val spaceshipPart = (buildableNotWonders + buildableUnits).firstOrNull { it.hasUnique(UniqueType.SpaceshipPart) }
if (spaceshipPart != null) {
var modifier = 1.5f
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,
// 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"}
&& 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")
&& it.canBePurchasedWithStat(cityInfo, Stat.Faith) }
@ -374,7 +378,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
val citiesNotFollowingOurReligion = civInfo.cities.asSequence()
.filterNot { it.religion.getMajorityReligion()?.name == civInfo.religionManager.religion!!.name }
val buildInqusitor = citiesNotFollowingOurReligion
val buildInquisitor = citiesNotFollowingOurReligion
.filter { it.religion.getMajorityReligion()?.name == civInfo.religionManager.religion?.name }
.toList().size.toFloat() / 10 + modifier
@ -383,7 +387,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
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)
}

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) {
if (unit.religion != unit.civInfo.religionManager.religion?.name)
return unit.destroy()

View File

@ -153,6 +153,11 @@ object UnitAutomation {
if (unit.hasUnique(UniqueType.CreateWaterImprovements))
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%"))
return SpecificUnitAutomation.automateGreatGeneral(unit)

View File

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

View File

@ -407,11 +407,9 @@ object UnitActions {
}.takeIf { unit.currentMovement > 0 }
)
}
private fun addAddInCapitalAction(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) {
if (!unit.hasUnique(UniqueType.AddInCapital)) return
actionList += UnitAction(UnitActionType.AddInCapital,
fun getAddInCapitalAction(unit: MapUnit, tile: TileInfo): UnitAction {
return UnitAction(UnitActionType.AddInCapital,
title = "Add to [${unit.getMatchingUniques(UniqueType.AddInCapital).first().params[0]}]",
action = {
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) {