Moved tile ranking and construction picking to Automation
This commit is contained in:
parent
5eec4deb84
commit
c6136acbec
5 changed files with 98 additions and 45 deletions
|
@ -3,11 +3,13 @@ package com.unciv.logic
|
|||
import com.unciv.UnCivGame
|
||||
import com.unciv.logic.battle.Battle
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
import com.unciv.logic.city.CityConstructions
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.UnitType
|
||||
import com.unciv.models.gamebasics.Building
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
import com.unciv.models.gamebasics.TileImprovement
|
||||
import com.unciv.ui.utils.getRandom
|
||||
|
@ -20,6 +22,7 @@ class Automation {
|
|||
.filter {
|
||||
(it.unit == null || it == currentTile)
|
||||
&& it.improvement == null
|
||||
&& (it.getCity()==null || it.getCity()!!.civInfo == civInfo) // don't work tiles belonging to another civ
|
||||
&& it.canBuildImprovement(chooseImprovement(it), civInfo)
|
||||
}
|
||||
.maxBy { getPriority(it, civInfo) }
|
||||
|
@ -27,6 +30,21 @@ class Automation {
|
|||
else return currentTile
|
||||
}
|
||||
|
||||
|
||||
internal fun rankTile(tile: TileInfo, civInfo: CivilizationInfo): Float {
|
||||
val stats = tile.getTileStats(null, civInfo)
|
||||
var rank = 0.0f
|
||||
if (stats.food <= 2) rank += stats.food
|
||||
else rank += (2 + (stats.food - 2) / 2f) // 1 point for each food up to 2, from there on half a point
|
||||
rank += (stats.gold / 2)
|
||||
rank += stats.production
|
||||
rank += stats.science
|
||||
rank += stats.culture
|
||||
if (tile.improvement == null) rank += 0.5f // improvement potential!
|
||||
if (tile.resource != null) rank += 1.0f
|
||||
return rank
|
||||
}
|
||||
|
||||
private fun getPriority(tileInfo: TileInfo, civInfo: CivilizationInfo): Int {
|
||||
var priority = 0
|
||||
if (tileInfo.isWorked()) priority += 3
|
||||
|
@ -67,6 +85,15 @@ class Automation {
|
|||
}
|
||||
}
|
||||
|
||||
fun rankTileAsCityCenter(tileInfo: TileInfo, civInfo: CivilizationInfo): Float {
|
||||
val bestTilesFromOuterLayer = tileInfo.tileMap.getTilesAtDistance(tileInfo.position,2)
|
||||
.sortedByDescending { rankTile(it,civInfo) }.take(2)
|
||||
val top5Tiles = tileInfo.neighbors.union(bestTilesFromOuterLayer)
|
||||
.sortedByDescending { rankTile(it,civInfo) }
|
||||
.take(5)
|
||||
return top5Tiles.map { rankTile(it,civInfo) }.sum()
|
||||
}
|
||||
|
||||
fun automateCivMoves(civInfo: CivilizationInfo) {
|
||||
if (civInfo.tech.techsToResearch.isEmpty()) {
|
||||
val researchableTechs = GameBasics.Technologies.values.filter { civInfo.tech.canBeResearched(it.name) }
|
||||
|
@ -74,13 +101,25 @@ class Automation {
|
|||
civInfo.tech.techsResearched.add(techToResearch!!.name)
|
||||
}
|
||||
|
||||
for (unit in civInfo.getCivUnits()) {
|
||||
automateUnitMoves(unit)
|
||||
}
|
||||
|
||||
// train settler?
|
||||
if (civInfo.cities.any()
|
||||
&& civInfo.happiness > 2*civInfo.cities.size +5
|
||||
&& civInfo.getCivUnits().none { it.name == "Settler" }
|
||||
&& civInfo.cities.none { it.cityConstructions.currentConstruction == "Settler" }) {
|
||||
|
||||
val bestCity = civInfo.cities.maxBy { it.cityStats.currentCityStats.production }!!
|
||||
if(bestCity.cityConstructions.builtBuildings.size > 1) // 2 buildings or more, otherwisse focus on self first
|
||||
bestCity.cityConstructions.currentConstruction = "Settler"
|
||||
}
|
||||
|
||||
for (city in civInfo.cities) {
|
||||
if (city.health < city.getMaxHealth()) trainCombatUnit(city)
|
||||
}
|
||||
|
||||
for (unit in civInfo.getCivUnits()) {
|
||||
automateUnitMoves(unit)
|
||||
}
|
||||
}
|
||||
|
||||
private fun trainCombatUnit(city: CityInfo) {
|
||||
|
@ -90,7 +129,24 @@ class Automation {
|
|||
fun automateUnitMoves(unit: MapUnit) {
|
||||
|
||||
if (unit.name == "Settler") {
|
||||
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen!!).first { it.name == "Found city" }.action()
|
||||
|
||||
val tileMap = unit.civInfo.gameInfo.tileMap
|
||||
|
||||
// find best city location within 5 tiles
|
||||
val bestCityLocation = tileMap.getTilesInDistance(unit.getTile().position, 5)
|
||||
.filterNot { it.isCityCenter || it.neighbors.any { it.isCityCenter } }
|
||||
.sortedByDescending { rankTileAsCityCenter(it, unit.civInfo) }
|
||||
.first()
|
||||
|
||||
if(unit.getTile() == bestCityLocation)
|
||||
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen!!).first { it.name == "Found city" }.action()
|
||||
|
||||
else {
|
||||
unit.headTowards(bestCityLocation.position)
|
||||
if(unit.currentMovement>0 && unit.getTile()==bestCityLocation)
|
||||
UnitActions().getUnitActions(unit, UnCivGame.Current.worldScreen!!).first { it.name == "Found city" }.action()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -178,4 +234,24 @@ class Automation {
|
|||
// else, go to a random space
|
||||
unit.moveToTile(distanceToTiles.keys.filter { it.unit == null }.toList().getRandom())
|
||||
}
|
||||
|
||||
|
||||
fun chooseNextConstruction(cityConstructions: CityConstructions) {
|
||||
cityConstructions.run {
|
||||
val buildableNotWonders = getBuildableBuildings().filterNot { (getConstruction(it) as Building).isWonder }
|
||||
if (!buildableNotWonders.isEmpty()) {
|
||||
currentConstruction = buildableNotWonders.first()
|
||||
} else {
|
||||
val militaryUnits = cityInfo.civInfo.getCivUnits().filter { it.getBaseUnit().unitType != UnitType.Civilian }.size
|
||||
if (cityInfo.civInfo.getCivUnits().none { it.name == CityConstructions.Worker } || militaryUnits > cityInfo.civInfo.cities.size * 2) {
|
||||
currentConstruction = CityConstructions.Worker
|
||||
} else {
|
||||
trainCombatUnit(cityInfo)
|
||||
}
|
||||
}
|
||||
if (cityInfo.civInfo == cityInfo.civInfo.gameInfo.getPlayerCivilization())
|
||||
cityInfo.civInfo.addNotification("Work has started on $currentConstruction", cityInfo.location)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package com.unciv.logic.city
|
||||
|
||||
import com.unciv.logic.map.UnitType
|
||||
import com.unciv.logic.Automation
|
||||
import com.unciv.models.gamebasics.Building
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
import com.unciv.models.stats.Stats
|
||||
|
@ -16,7 +16,7 @@ class CityConstructions {
|
|||
var currentConstruction: String = "Monument" // default starting building!
|
||||
|
||||
|
||||
private fun getBuildableBuildings(): List<String> = GameBasics.Buildings.values
|
||||
internal fun getBuildableBuildings(): List<String> = GameBasics.Buildings.values
|
||||
.filter { it.isBuildable(this) }.map { it.name }
|
||||
|
||||
// Library and public school unique (not actualy unique, though...hmm)
|
||||
|
@ -62,7 +62,7 @@ class CityConstructions {
|
|||
|
||||
fun isBuilding(buildingName: String): Boolean = currentConstruction == buildingName
|
||||
|
||||
private fun getConstruction(constructionName: String): IConstruction {
|
||||
internal fun getConstruction(constructionName: String): IConstruction {
|
||||
if (GameBasics.Buildings.containsKey(constructionName))
|
||||
return GameBasics.Buildings[constructionName]!!
|
||||
else if (GameBasics.Units.containsKey(constructionName))
|
||||
|
@ -87,7 +87,7 @@ class CityConstructions {
|
|||
if (!construction.isBuildable(this)) {
|
||||
// We can't build this building anymore! (Wonder has been built / resource is gone / etc.)
|
||||
cityInfo.civInfo.addNotification("Cannot continue work on $saveCurrentConstruction", cityInfo.location)
|
||||
chooseNextConstruction()
|
||||
Automation().chooseNextConstruction(this)
|
||||
construction = getConstruction(currentConstruction)
|
||||
} else
|
||||
currentConstruction = saveCurrentConstruction
|
||||
|
@ -99,28 +99,11 @@ class CityConstructions {
|
|||
inProgressConstructions.remove(currentConstruction)
|
||||
cityInfo.civInfo.addNotification(currentConstruction + " has been built in " + cityInfo.name, cityInfo.location)
|
||||
|
||||
chooseNextConstruction()
|
||||
Automation().chooseNextConstruction(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun chooseNextConstruction() {
|
||||
val buildableNotWonders = getBuildableBuildings().filterNot{(getConstruction(it) as Building).isWonder}
|
||||
if(!buildableNotWonders.isEmpty()){
|
||||
currentConstruction = buildableNotWonders.first()
|
||||
}
|
||||
else {
|
||||
val militaryUnits = cityInfo.civInfo.getCivUnits().filter { it.getBaseUnit().unitType != UnitType.Civilian }.size
|
||||
if (cityInfo.civInfo.getCivUnits().none { it.name== Worker } || militaryUnits > cityInfo.civInfo.cities.size*2) {
|
||||
currentConstruction = Worker
|
||||
} else {
|
||||
currentConstruction = "Archer"
|
||||
}
|
||||
}
|
||||
if(cityInfo.civInfo == cityInfo.civInfo.gameInfo.getPlayerCivilization())
|
||||
cityInfo.civInfo.addNotification("Work has started on $currentConstruction", cityInfo.location)
|
||||
}
|
||||
|
||||
private fun workDone(constructionName: String): Int {
|
||||
if (inProgressConstructions.containsKey(constructionName)) return inProgressConstructions[constructionName]!!
|
||||
else return 0
|
||||
|
@ -141,7 +124,7 @@ class CityConstructions {
|
|||
fun purchaseBuilding(buildingName: String) {
|
||||
cityInfo.civInfo.gold -= getConstruction(buildingName).getGoldCost(cityInfo.civInfo.policies.adoptedPolicies)
|
||||
getConstruction(buildingName).postBuildEvent(this)
|
||||
if (currentConstruction == buildingName) chooseNextConstruction()
|
||||
if (currentConstruction == buildingName) Automation().chooseNextConstruction(this)
|
||||
cityInfo.cityStats.update()
|
||||
}
|
||||
|
||||
|
@ -150,11 +133,15 @@ class CityConstructions {
|
|||
if (cultureBuildingToBuild == null) return
|
||||
builtBuildings.add(cultureBuildingToBuild)
|
||||
if (currentConstruction == cultureBuildingToBuild)
|
||||
chooseNextConstruction()
|
||||
Automation().chooseNextConstruction(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal const val Worker = "Worker"
|
||||
internal const val Settler = "Settler"
|
||||
}
|
||||
|
||||
fun chooseNextConstruction() {
|
||||
Automation().chooseNextConstruction(this)
|
||||
}
|
||||
} // for json parsing, we need to have a default constructor
|
|
@ -1,5 +1,7 @@
|
|||
package com.unciv.logic.city
|
||||
|
||||
import com.unciv.logic.Automation
|
||||
|
||||
class CityExpansionManager {
|
||||
|
||||
@Transient
|
||||
|
@ -16,11 +18,11 @@ class CityExpansionManager {
|
|||
// https://www.reddit.com/r/civ/comments/58rxkk/how_in_gods_name_do_borders_expand_in_civ_vi/ has it
|
||||
// (per game XML files) at 6*(t+0.4813)^1.3
|
||||
// The second seems to be more based, so I'll go with that
|
||||
//Speciality of Angkor Wat
|
||||
|
||||
fun getCultureToNextTile(): Int {
|
||||
val numTilesClaimed = cityInfo.tiles.size - 7
|
||||
var cultureToNextTile = 6 * Math.pow(numTilesClaimed + 1.4813, 1.3)
|
||||
if (cityInfo.civInfo.buildingUniques.contains("NewTileCostReduction")) cultureToNextTile *= 0.75
|
||||
if (cityInfo.civInfo.buildingUniques.contains("NewTileCostReduction")) cultureToNextTile *= 0.75 //Speciality of Angkor Wat
|
||||
if (cityInfo.civInfo.policies.isAdopted("Tradition")) cultureToNextTile *= 0.75
|
||||
return Math.round(cultureToNextTile).toInt()
|
||||
}
|
||||
|
@ -31,7 +33,7 @@ class CityExpansionManager {
|
|||
for (i in 2..3) {
|
||||
val tiles = cityInfo.civInfo.gameInfo.tileMap.getTilesInDistance(cityInfo.location, i).filter { it.getOwner() == null }
|
||||
if (tiles.isEmpty()) continue
|
||||
val chosenTile = tiles.maxBy { cityInfo.rankTile(it) }
|
||||
val chosenTile = tiles.maxBy { Automation().rankTile(it,cityInfo.civInfo) }
|
||||
cityInfo.tiles.add(chosenTile!!.position)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -127,19 +127,6 @@ class CityInfo {
|
|||
health = min(health+maxHealth/10, maxHealth)
|
||||
}
|
||||
|
||||
internal fun rankTile(tile: TileInfo): Float {
|
||||
val stats = tile.getTileStats(this, civInfo)
|
||||
var rank = 0.0f
|
||||
if (stats.food <= 2) rank += stats.food
|
||||
else rank += (2 + (stats.food - 2) / 2f) // 1 point for each food up to 2, from there on half a point
|
||||
rank += (stats.gold / 2)
|
||||
rank += stats.production
|
||||
rank += stats.science
|
||||
rank += stats.culture
|
||||
if (tile.improvement == null) rank += 0.5f // improvement potential!
|
||||
if (tile.resource != null) rank += 1.0f
|
||||
return rank
|
||||
}
|
||||
|
||||
internal fun getMaxHealth(): Int {
|
||||
return 200 // add more later when walls citadl etc.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.unciv.logic.city
|
||||
|
||||
import com.unciv.logic.Automation
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.stats.Stats
|
||||
|
||||
|
@ -61,7 +62,7 @@ class PopulationManager {
|
|||
internal fun autoAssignWorker() {
|
||||
val toWork: TileInfo? = cityInfo.getTiles()
|
||||
.filterNot { cityInfo.workedTiles.contains(it.position) || cityInfo.location==it.position}
|
||||
.maxBy { cityInfo.rankTile(it) }
|
||||
.maxBy { Automation().rankTile(it,cityInfo.civInfo) }
|
||||
if (toWork != null) // This is when we've run out of tiles!
|
||||
cityInfo.workedTiles.add(toWork.position)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue