mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-27 05:46:43 -04:00
Remove Tactical AI files - untouched for 2 years and unused, causes confusion to new developers
This commit is contained in:
parent
7b075bcf7c
commit
04ec4f9cdb
@ -1,57 +0,0 @@
|
|||||||
package com.unciv.logic.automation.ai
|
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
|
||||||
import com.unciv.GUI
|
|
||||||
import com.unciv.logic.IsPartOfGameInfoSerialization
|
|
||||||
import com.unciv.logic.civilization.Civilization
|
|
||||||
import com.unciv.logic.map.tile.Tile
|
|
||||||
import com.unciv.ui.components.extensions.toGroup
|
|
||||||
import com.unciv.ui.images.ImageGetter
|
|
||||||
import com.unciv.utils.Log
|
|
||||||
|
|
||||||
class TacticalAI : IsPartOfGameInfoSerialization {
|
|
||||||
|
|
||||||
private val debug: Boolean = false
|
|
||||||
|
|
||||||
@Transient private val tacticalAnalysisMap = TacticalAnalysisMap()
|
|
||||||
@Transient private var player: Civilization? = null
|
|
||||||
|
|
||||||
fun init(player: Civilization) {
|
|
||||||
this.player = player
|
|
||||||
tacticalAnalysisMap.reset(player)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun showZonesDebug(tile: Tile) {
|
|
||||||
|
|
||||||
if (!debug)
|
|
||||||
return
|
|
||||||
|
|
||||||
val zone = tacticalAnalysisMap.getZoneByTile(tile)
|
|
||||||
val zoneId = zone?.id
|
|
||||||
|
|
||||||
Log.debug("MYTAG Zone $zoneId City: ${zone?.city} Area: ${zone?.area} Area size: ${
|
|
||||||
tile.tileMap.continentSizes[tile.getContinent()]} Zone size: ${zone?.tileCount}")
|
|
||||||
|
|
||||||
val mapHolder = GUI.getMap()
|
|
||||||
|
|
||||||
for (otherTile in mapHolder.tileMap.values.asSequence()) {
|
|
||||||
|
|
||||||
val otherZoneId = tacticalAnalysisMap.plotPositionToZoneId[otherTile.position]
|
|
||||||
if (otherZoneId == zoneId) {
|
|
||||||
mapHolder.tileGroups[otherTile]?.let {
|
|
||||||
mapHolder.addOverlayOnTileGroup(it, ImageGetter.getCircle(
|
|
||||||
color = when (zone?.territoryType) {
|
|
||||||
TacticalTerritoryType.FRIENDLY -> Color.GREEN
|
|
||||||
TacticalTerritoryType.ENEMY -> Color.RED
|
|
||||||
else -> Color.WHITE
|
|
||||||
}
|
|
||||||
).toGroup(20f)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zone?.neighboringZones?.contains(otherZoneId) == true) {
|
|
||||||
mapHolder.tileGroups[otherTile]?.let {
|
|
||||||
mapHolder.addOverlayOnTileGroup(it, ImageGetter.getCircle(color = Color.GRAY).toGroup(20f)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,327 +0,0 @@
|
|||||||
package com.unciv.logic.automation.ai
|
|
||||||
|
|
||||||
import com.badlogic.gdx.math.Vector2
|
|
||||||
import com.unciv.Constants
|
|
||||||
import com.unciv.logic.GameInfo
|
|
||||||
import com.unciv.logic.city.City
|
|
||||||
import com.unciv.logic.civilization.Civilization
|
|
||||||
import com.unciv.logic.map.tile.Tile
|
|
||||||
import com.unciv.logic.map.TileMap
|
|
||||||
import com.unciv.utils.Log
|
|
||||||
import java.util.UUID
|
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
import kotlin.collections.HashMap
|
|
||||||
|
|
||||||
enum class TacticalTerritoryType {
|
|
||||||
NONE,
|
|
||||||
FRIENDLY,
|
|
||||||
ENEMY,
|
|
||||||
NEUTRAL
|
|
||||||
}
|
|
||||||
|
|
||||||
class TacticalDominanceZone {
|
|
||||||
var id = "UNKNOWN"
|
|
||||||
var territoryType = TacticalTerritoryType.NONE
|
|
||||||
var owner: Civilization? = null
|
|
||||||
var city: City? = null
|
|
||||||
var area: Int = -1
|
|
||||||
var tileCount: Int = 0
|
|
||||||
|
|
||||||
var neighboringZones: HashSet<String> = HashSet()
|
|
||||||
|
|
||||||
var friendlyMeleeStrength = 0
|
|
||||||
var friendlyRangeStrength = 0
|
|
||||||
var friendlyNavalMeleeStrength = 0
|
|
||||||
var friendlyNavalRangeStrength = 0
|
|
||||||
var friendlyUnitCount = 0
|
|
||||||
var friendlyNavalUnitCount = 0
|
|
||||||
|
|
||||||
var enemyMeleeStrength = 0
|
|
||||||
var enemyRangeStrength = 0
|
|
||||||
var enemyNavalMeleeStrength = 0
|
|
||||||
var enemyNavalRangeStrength = 0
|
|
||||||
var enemyUnitCount = 0
|
|
||||||
var enemyNavalUnitCount = 0
|
|
||||||
|
|
||||||
val neutralUnitStrength = 0
|
|
||||||
val neutralUnitCount = 0
|
|
||||||
|
|
||||||
var zoneValue = 0
|
|
||||||
|
|
||||||
fun getOverallFriendlyStrength(): Int {
|
|
||||||
return friendlyMeleeStrength*4/3 + friendlyNavalMeleeStrength + friendlyRangeStrength + friendlyNavalRangeStrength
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getOverallEnemyStrength(): Int {
|
|
||||||
return enemyMeleeStrength*4/3 + enemyNavalMeleeStrength + enemyRangeStrength + enemyNavalRangeStrength
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isWater(): Boolean {
|
|
||||||
return id.startsWith('-')
|
|
||||||
}
|
|
||||||
|
|
||||||
fun extend() {
|
|
||||||
tileCount += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TacticalAnalysisMap {
|
|
||||||
|
|
||||||
lateinit var game: GameInfo // Current game
|
|
||||||
lateinit var player: Civilization // Current player
|
|
||||||
|
|
||||||
var lastUpdate: Int = -1
|
|
||||||
|
|
||||||
val zones: ArrayList<TacticalDominanceZone> = ArrayList()
|
|
||||||
val zoneIdToZoneIndex: HashMap<String, Int> = HashMap()
|
|
||||||
val plotPositionToZoneId: HashMap<Vector2, String> = HashMap()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val maxRange = 4
|
|
||||||
const val maxZoneSize = 30
|
|
||||||
}
|
|
||||||
|
|
||||||
fun reset(player: Civilization) {
|
|
||||||
this.player = player
|
|
||||||
this.game = player.gameInfo
|
|
||||||
this.lastUpdate = -1
|
|
||||||
|
|
||||||
this.zones.clear()
|
|
||||||
this.zoneIdToZoneIndex.clear()
|
|
||||||
this.plotPositionToZoneId.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isUpToDate() : Boolean {
|
|
||||||
if (lastUpdate == -1)
|
|
||||||
return false
|
|
||||||
if (player != game.currentPlayerCiv)
|
|
||||||
return true
|
|
||||||
return lastUpdate == game.turns
|
|
||||||
}
|
|
||||||
|
|
||||||
fun invalidate() {
|
|
||||||
lastUpdate = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun refreshIfOutdated() {
|
|
||||||
|
|
||||||
if (isUpToDate())
|
|
||||||
return
|
|
||||||
|
|
||||||
Log.debug("Refreshing Tactical Analysis Map...")
|
|
||||||
|
|
||||||
// This is where creation and separation of zones occur
|
|
||||||
createDominanceZones()
|
|
||||||
establishZoneNeighborhood()
|
|
||||||
|
|
||||||
// This is workaround for absent "Area" mechanics.
|
|
||||||
// We glue small leftovers to bigger zones
|
|
||||||
glueSmallZonesToBig()
|
|
||||||
|
|
||||||
// TODO: calculateMilitaryStrength
|
|
||||||
// TODO: prioritizeZones
|
|
||||||
// TODO: updatePostures
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getZoneByTile(tile: Tile): TacticalDominanceZone? {
|
|
||||||
refreshIfOutdated()
|
|
||||||
val zoneId = plotPositionToZoneId[tile.position]?: return null
|
|
||||||
return getZoneById(zoneId)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getZoneById(id: String): TacticalDominanceZone? {
|
|
||||||
refreshIfOutdated()
|
|
||||||
val index = zoneIdToZoneIndex[id]
|
|
||||||
if (index != null)
|
|
||||||
return getZoneByIndex(index)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getZoneByIndex(index: Int): TacticalDominanceZone? {
|
|
||||||
refreshIfOutdated()
|
|
||||||
if (index < 0 || index >= zones.size)
|
|
||||||
return null
|
|
||||||
return zones[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createDominanceZones() {
|
|
||||||
|
|
||||||
lastUpdate = game.turns
|
|
||||||
zones.clear()
|
|
||||||
zoneIdToZoneIndex.clear()
|
|
||||||
plotPositionToZoneId.clear()
|
|
||||||
|
|
||||||
val unknownZone = TacticalDominanceZone()
|
|
||||||
|
|
||||||
zones.add(unknownZone)
|
|
||||||
zoneIdToZoneIndex[unknownZone.id] = 0
|
|
||||||
|
|
||||||
val nonCityTiles = ArrayList<Tile>()
|
|
||||||
|
|
||||||
var zone: TacticalDominanceZone? = null
|
|
||||||
|
|
||||||
for (tile in game.tileMap.values.asSequence()) {
|
|
||||||
|
|
||||||
// Unexplored plot go into their own zone
|
|
||||||
if (!player.hasExplored(tile)) {
|
|
||||||
plotPositionToZoneId[tile.position] = unknownZone.id
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is this plot close to a city?
|
|
||||||
val cityDistance = game.cityDistances.getClosestCityDistance(tile, null, false)
|
|
||||||
if (cityDistance == null) {
|
|
||||||
// Non-city tiles processed separately
|
|
||||||
nonCityTiles.add(tile)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val city: City? = when {
|
|
||||||
cityDistance.distance < 3 -> cityDistance.city
|
|
||||||
else -> tile.getCity()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (city == null) {
|
|
||||||
nonCityTiles.add(tile)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val zoneId = if (tile.isWater) "-${city.id}" else city.id
|
|
||||||
|
|
||||||
// Chances are it's the same zone as before
|
|
||||||
if (zone == null || zone.id != zoneId) {
|
|
||||||
zone = getZoneById(zoneId)
|
|
||||||
// Still not found? Create new
|
|
||||||
if (zone == null) {
|
|
||||||
|
|
||||||
val newZone = TacticalDominanceZone()
|
|
||||||
newZone.id = zoneId
|
|
||||||
newZone.city = city
|
|
||||||
newZone.owner = city.civ
|
|
||||||
newZone.area = tile.getContinent()
|
|
||||||
|
|
||||||
if (newZone.owner == player)
|
|
||||||
newZone.territoryType = TacticalTerritoryType.FRIENDLY
|
|
||||||
else if (newZone.owner?.isAtWarWith(player) == true)
|
|
||||||
newZone.territoryType = TacticalTerritoryType.ENEMY
|
|
||||||
else
|
|
||||||
newZone.territoryType = TacticalTerritoryType.NEUTRAL
|
|
||||||
|
|
||||||
zoneIdToZoneIndex[zoneId] = zones.size
|
|
||||||
zones.add(newZone)
|
|
||||||
|
|
||||||
zone = zones.last()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
plotPositionToZoneId[tile.position] = zoneId
|
|
||||||
zone.extend()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that continents sizes are calculated
|
|
||||||
game.tileMap.assignContinents(TileMap.AssignContinentsMode.Ensure)
|
|
||||||
|
|
||||||
groupRemainingNonCityTiles(nonCityTiles)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun groupRemainingNonCityTiles(nonCityTiles: ArrayList<Tile>) {
|
|
||||||
while (nonCityTiles.isNotEmpty()) {
|
|
||||||
|
|
||||||
var count = maxZoneSize
|
|
||||||
val stack: ArrayList<Tile> = ArrayList()
|
|
||||||
stack.add(nonCityTiles.removeFirst())
|
|
||||||
|
|
||||||
val randomId = UUID.randomUUID().toString()
|
|
||||||
val newId = if (stack.last().isWater) "-$randomId" else randomId
|
|
||||||
|
|
||||||
val newZone = TacticalDominanceZone()
|
|
||||||
newZone.id = newId
|
|
||||||
newZone.city = null
|
|
||||||
newZone.area = stack.last().getContinent()
|
|
||||||
newZone.territoryType = TacticalTerritoryType.NEUTRAL
|
|
||||||
|
|
||||||
while (stack.isNotEmpty() || count > 0) {
|
|
||||||
val tile = stack.removeLastOrNull() ?: break
|
|
||||||
val tileContinentSize = tile.tileMap.continentSizes[tile.getContinent()] ?: Int.MAX_VALUE
|
|
||||||
plotPositionToZoneId[tile.position] = newId
|
|
||||||
newZone.extend()
|
|
||||||
|
|
||||||
for (neighbor in tile.neighbors) {
|
|
||||||
|
|
||||||
// We don't want lakes and mountains to be separate zones - should attach them too
|
|
||||||
val isLake = neighbor.matchesTerrainFilter(Constants.lakes)
|
|
||||||
val isMountain = neighbor.matchesTerrainFilter(Constants.mountain)
|
|
||||||
val neighborContinentSize = neighbor.tileMap.continentSizes[neighbor.getContinent()] ?: Int.MAX_VALUE
|
|
||||||
|
|
||||||
val isSameZone = neighbor.getContinent() == tile.getContinent()
|
|
||||||
|| isLake || (isMountain && neighbor.isLand)
|
|
||||||
|| neighborContinentSize < 4 || tileContinentSize < 4
|
|
||||||
|
|
||||||
if (isSameZone && nonCityTiles.contains(neighbor) && count > 0) {
|
|
||||||
nonCityTiles.remove(neighbor)
|
|
||||||
stack.add(neighbor)
|
|
||||||
count -= 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
zoneIdToZoneIndex[newZone.id] = zones.size
|
|
||||||
zones.add(newZone)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun glueSmallZonesToBig() {
|
|
||||||
|
|
||||||
val toRemove = HashSet<TacticalDominanceZone>()
|
|
||||||
|
|
||||||
for (zone in zones) {
|
|
||||||
if (zone.tileCount < 5 && zone.city == null) {
|
|
||||||
val biggerZone = zones.asSequence()
|
|
||||||
.filter { it.isWater() == zone.isWater() && it.neighboringZones.contains(zone.id) }
|
|
||||||
.firstOrNull()
|
|
||||||
if (biggerZone != null) {
|
|
||||||
plotPositionToZoneId.asSequence()
|
|
||||||
.filter { it.value == zone.id }
|
|
||||||
.forEach { plotPositionToZoneId[it.key] = biggerZone.id }
|
|
||||||
toRemove.add(zone)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zones.removeAll(toRemove)
|
|
||||||
zoneIdToZoneIndex.clear()
|
|
||||||
|
|
||||||
for (i in 0 until zones.size) {
|
|
||||||
val zoneId = zones[i].id
|
|
||||||
zoneIdToZoneIndex[zoneId] = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun establishZoneNeighborhood() {
|
|
||||||
|
|
||||||
for (zone in zones)
|
|
||||||
zone.neighboringZones.clear()
|
|
||||||
|
|
||||||
val tileMap = game.tileMap
|
|
||||||
val directionsToCheck = setOf(12, 4, 8) // each tile only needs to check 3 directions for every possible neighboringZone to be identified
|
|
||||||
|
|
||||||
for (tile in tileMap.values) {
|
|
||||||
val zoneA = getZoneByTile(tile)
|
|
||||||
val zoneAId = zoneA!!.id
|
|
||||||
for (neighbor in directionsToCheck.mapNotNull { tileMap.getClockPositionNeighborTile(tile, it) }) {
|
|
||||||
val zoneB = getZoneByTile(neighbor)!!
|
|
||||||
val zoneBId = zoneB.id
|
|
||||||
if (zoneAId != zoneBId) {
|
|
||||||
zoneA.neighboringZones.add(zoneB.id)
|
|
||||||
zoneB.neighboringZones.add(zoneA.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun debugOutput() {
|
|
||||||
Log.debug("MYTAG: Total tactical zones: ${zones.size}")
|
|
||||||
for (zone in zones) {
|
|
||||||
Log.debug("MYTAG: Zone: ${zone.id} City: ${zone.city} Territory: ${zone.territoryType} Neighbors: ${zone.neighboringZones}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -8,7 +8,6 @@ import com.unciv.logic.GameInfo
|
|||||||
import com.unciv.logic.IsPartOfGameInfoSerialization
|
import com.unciv.logic.IsPartOfGameInfoSerialization
|
||||||
import com.unciv.logic.MultiFilter
|
import com.unciv.logic.MultiFilter
|
||||||
import com.unciv.logic.UncivShowableException
|
import com.unciv.logic.UncivShowableException
|
||||||
import com.unciv.logic.automation.ai.TacticalAI
|
|
||||||
import com.unciv.logic.automation.unit.WorkerAutomation
|
import com.unciv.logic.automation.unit.WorkerAutomation
|
||||||
import com.unciv.logic.city.City
|
import com.unciv.logic.city.City
|
||||||
import com.unciv.logic.city.managers.CityFounder
|
import com.unciv.logic.city.managers.CityFounder
|
||||||
@ -169,9 +168,6 @@ class Civilization : IsPartOfGameInfoSerialization {
|
|||||||
private var allyCivName: String? = null
|
private var allyCivName: String? = null
|
||||||
var naturalWonders = ArrayList<String>()
|
var naturalWonders = ArrayList<String>()
|
||||||
|
|
||||||
/* AI section */
|
|
||||||
val tacticalAI = TacticalAI()
|
|
||||||
|
|
||||||
var notifications = ArrayList<Notification>()
|
var notifications = ArrayList<Notification>()
|
||||||
|
|
||||||
var notificationsLog = ArrayList<NotificationsLog>()
|
var notificationsLog = ArrayList<NotificationsLog>()
|
||||||
@ -785,8 +781,6 @@ class Civilization : IsPartOfGameInfoSerialization {
|
|||||||
|
|
||||||
hasLongCountDisplayUnique = hasUnique(UniqueType.MayanCalendarDisplay)
|
hasLongCountDisplayUnique = hasUnique(UniqueType.MayanCalendarDisplay)
|
||||||
|
|
||||||
tacticalAI.init(this)
|
|
||||||
|
|
||||||
cache.setTransients()
|
cache.setTransients()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +197,6 @@ class WorldMapHolder(
|
|||||||
unitTable.citySelected(previousSelectedCity)
|
unitTable.citySelected(previousSelectedCity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
worldScreen.viewingCiv.tacticalAI.showZonesDebug(tile)
|
|
||||||
worldScreen.shouldUpdate = true
|
worldScreen.shouldUpdate = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user