mirror of
https://github.com/yairm210/Unciv.git
synced 2025-03-10 04:43:29 +07:00
Automated (and UI) workers now build roads between cities if they're big enough!
This was the major cause for the AI's economic failure!
This commit is contained in:
parent
c49a282a74
commit
71f4c04948
@ -2,6 +2,8 @@ package com.unciv.logic
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
|
||||
class HexMath {
|
||||
|
||||
@ -72,13 +74,8 @@ class HexMath {
|
||||
return hexesToReturn
|
||||
}
|
||||
|
||||
fun GetDistance(origin: Vector2, destination: Vector2): Int { // Yes, this is a dumb implementation. But I can't be arsed to think of a better one right now, other stuff to do.
|
||||
var distance = 0
|
||||
|
||||
while (true) {
|
||||
if (GetVectorsAtDistance(origin, distance).contains(destination)) return distance
|
||||
distance++
|
||||
}
|
||||
fun getDistance(origin: Vector2, destination: Vector2): Int { // Yes, this is a dumb implementation. But I can't be arsed to think of a better one right now, other stuff to do.
|
||||
return max(abs(origin.x-destination.x),abs(origin.y-destination.y) ).toInt()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package com.unciv.logic.automation
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.unciv.logic.HexMath
|
||||
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.RoadStatus
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
import com.unciv.models.gamebasics.unit.Unit
|
||||
@ -42,6 +44,15 @@ class Automation {
|
||||
civInfo.policies.adopt(policyToAdopt)
|
||||
}
|
||||
|
||||
// Order roads between cities if you can
|
||||
// for(city in civInfo.cities.filter { it.population.population>3 && !it.isCapital()
|
||||
// && !it.cityStats.isConnectedToCapital(RoadStatus.Road) }){
|
||||
// val closestConnectedCity = civInfo.cities.filter { it.isCapital() || it.cityStats.isConnectedToCapital(RoadStatus.Road) }
|
||||
// .minBy { HexMath().getDistance(city.location,it.location) }!!
|
||||
// val pathToClosestCity = civInfo.gameInfo.tileMap.getShortestPathBetweenTwoTiles(city.getCenterTile(),closestConnectedCity.getCenterTile())
|
||||
// }
|
||||
|
||||
|
||||
val rangedUnits = mutableListOf<MapUnit>()
|
||||
val meleeUnits = mutableListOf<MapUnit>()
|
||||
val civilianUnits = mutableListOf<MapUnit>()
|
||||
|
@ -72,7 +72,7 @@ class UnitAutomation{
|
||||
}
|
||||
|
||||
if (unit.name == "Worker") {
|
||||
WorkerAutomation().automateWorkerAction(unit)
|
||||
WorkerAutomation(unit).automateWorkerAction()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,21 +1,28 @@
|
||||
package com.unciv.logic.automation
|
||||
|
||||
import com.unciv.logic.HexMath
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.RoadStatus
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
import com.unciv.models.gamebasics.tile.TileImprovement
|
||||
|
||||
class WorkerAutomation {
|
||||
class WorkerAutomation(val unit: MapUnit) {
|
||||
|
||||
fun automateWorkerAction(unit: MapUnit) {
|
||||
fun automateWorkerAction() {
|
||||
val enemyUnitsInWalkingDistance = unit.getDistanceToTiles().keys
|
||||
.filter { it.militaryUnit!=null && it.militaryUnit!!.civInfo!=unit.civInfo }
|
||||
|
||||
if(enemyUnitsInWalkingDistance.isNotEmpty()) return // Don't you dare move.
|
||||
|
||||
val tile = unit.getTile()
|
||||
val tileToWork = findTileToWork(unit)
|
||||
val tileToWork = findTileToWork()
|
||||
|
||||
if(getPriority(tileToWork,unit.civInfo) < 3){ // building roads is more important
|
||||
if(tryConnectingCities()) return
|
||||
}
|
||||
|
||||
if (tileToWork != tile) {
|
||||
unit.movementAlgs().headTowards(tileToWork)
|
||||
unit.doPreTurnAction()
|
||||
@ -23,34 +30,68 @@ class WorkerAutomation {
|
||||
}
|
||||
if (tile.improvementInProgress == null) {
|
||||
val improvement = chooseImprovement(tile)
|
||||
if (tile.canBuildImprovement(improvement, unit.civInfo))
|
||||
// What if we're stuck on this tile but can't build there?
|
||||
if (tile.canBuildImprovement(improvement, unit.civInfo)) {
|
||||
// What if we're stuck on this tile but can't build there?
|
||||
tile.startWorkingOnImprovement(improvement, unit.civInfo)
|
||||
return
|
||||
}
|
||||
}
|
||||
if(tile.improvementInProgress!=null) return // we're working!
|
||||
}
|
||||
|
||||
private fun findTileToWork(worker:MapUnit): TileInfo {
|
||||
val currentTile=worker.getTile()
|
||||
fun tryConnectingCities():Boolean{ // returns whether we actually did anything
|
||||
val cityThatNeedsConnecting = unit.civInfo.cities.filter { it.population.population>3 && !it.isCapital()
|
||||
&& !it.cityStats.isConnectedToCapital(RoadStatus.Road) }
|
||||
.minBy { HexMath().getDistance(it.location, unit.getTile().position) }
|
||||
if(cityThatNeedsConnecting==null) return false// do nothing.
|
||||
|
||||
val closestConnectedCity = unit.civInfo.cities.filter { it.isCapital() || it.cityStats.isConnectedToCapital(RoadStatus.Road) }
|
||||
.minBy { HexMath().getDistance(cityThatNeedsConnecting.location,it.location) }!!
|
||||
|
||||
val pathToClosestCity = unit.civInfo.gameInfo.tileMap
|
||||
.getShortestPathBetweenTwoTiles(cityThatNeedsConnecting.getCenterTile(),
|
||||
closestConnectedCity.getCenterTile())
|
||||
.filter { it.roadStatus==RoadStatus.None}
|
||||
|
||||
val unitTile = unit.getTile()
|
||||
if(unitTile in pathToClosestCity){
|
||||
if(unitTile.improvementInProgress==null)
|
||||
unitTile.startWorkingOnImprovement(GameBasics.TileImprovements["Road"]!!,unit.civInfo)
|
||||
return true
|
||||
}
|
||||
|
||||
val closestTileInPathWithNoRoad = pathToClosestCity.filter { unit.canMoveTo(it)}
|
||||
.minBy { HexMath().getDistance(unit.getTile().position, it.position) }
|
||||
|
||||
if(closestTileInPathWithNoRoad==null) return false
|
||||
unit.movementAlgs().headTowards(closestTileInPathWithNoRoad)
|
||||
if(unit.currentMovement>0 && unit.getTile()==closestTileInPathWithNoRoad)
|
||||
closestTileInPathWithNoRoad.startWorkingOnImprovement(GameBasics.TileImprovements["Road"]!!,unit.civInfo)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun findTileToWork(): TileInfo {
|
||||
val currentTile=unit.getTile()
|
||||
val workableTiles = currentTile.getTilesInDistance(4)
|
||||
.filter {
|
||||
(it.civilianUnit== null || it == currentTile)
|
||||
&& it.improvement == null
|
||||
&& it.canBuildImprovement(chooseImprovement(it), worker.civInfo)
|
||||
&& {val city=it.getCity(); city==null || it.getCity()?.civInfo == worker.civInfo}() // don't work tiles belonging to another civ
|
||||
}.sortedByDescending { getPriority(it, worker.civInfo) }.toMutableList()
|
||||
&& it.canBuildImprovement(chooseImprovement(it), unit.civInfo)
|
||||
&& {val city=it.getCity(); city==null || it.getCity()?.civInfo == unit.civInfo}() // don't work tiles belonging to another civ
|
||||
}.sortedByDescending { getPriority(it, unit.civInfo) }.toMutableList()
|
||||
|
||||
// the tile needs to be actually reachable - more difficult than it seems,
|
||||
// which is why we DON'T calculate this for every possible tile in the radius,
|
||||
// but only for the tile that's about to be chosen.
|
||||
val selectedTile = workableTiles.firstOrNull{
|
||||
worker.movementAlgs()
|
||||
unit.movementAlgs()
|
||||
.getShortestPath(workableTiles.first())
|
||||
.isNotEmpty()}
|
||||
|
||||
if (selectedTile != null
|
||||
&& getPriority(selectedTile, worker.civInfo)>1
|
||||
&& getPriority(selectedTile, unit.civInfo)>1
|
||||
&& (!workableTiles.contains(currentTile)
|
||||
|| getPriority(selectedTile, worker.civInfo) > getPriority(currentTile,worker.civInfo)))
|
||||
|| getPriority(selectedTile, unit.civInfo) > getPriority(currentTile, unit.civInfo)))
|
||||
return selectedTile
|
||||
else return currentTile
|
||||
}
|
||||
@ -58,10 +99,12 @@ class WorkerAutomation {
|
||||
|
||||
private fun getPriority(tileInfo: TileInfo, civInfo: CivilizationInfo): Int {
|
||||
var priority = 0
|
||||
if (tileInfo.isWorked()) priority += 3
|
||||
if (tileInfo.getOwner() == civInfo) priority += 2
|
||||
if (tileInfo.hasViewableResource(civInfo)) priority += 1
|
||||
if (tileInfo.getOwner() == civInfo){
|
||||
priority += 2
|
||||
if (tileInfo.isWorked()) priority += 3
|
||||
}
|
||||
else if (tileInfo.neighbors.any { it.getOwner() != null }) priority += 1
|
||||
if (tileInfo.hasViewableResource(civInfo)) priority += 1
|
||||
return priority
|
||||
}
|
||||
|
||||
@ -80,4 +123,25 @@ class WorkerAutomation {
|
||||
return GameBasics.TileImprovements[improvementString]!!
|
||||
}
|
||||
|
||||
|
||||
fun constructRoadTo(destination:TileInfo) {
|
||||
val currentTile = unit.getTile()
|
||||
if (currentTile.roadStatus == RoadStatus.None) {
|
||||
currentTile.startWorkingOnImprovement(GameBasics.TileImprovements["Road"]!!, unit.civInfo)
|
||||
return
|
||||
}
|
||||
val pathToDestination = unit.movementAlgs().getShortestPath(destination)
|
||||
val destinationThisTurn = pathToDestination.first()
|
||||
val fullPathToCurrentDestination = unit.movementAlgs().getFullPathToCloseTile(destinationThisTurn)
|
||||
val firstTileWithoutRoad = fullPathToCurrentDestination.firstOrNull { it.roadStatus == RoadStatus.None && unit.canMoveTo(it) }
|
||||
if (firstTileWithoutRoad == null) {
|
||||
unit.moveToTile(destinationThisTurn)
|
||||
return
|
||||
}
|
||||
unit.moveToTile(firstTileWithoutRoad)
|
||||
if (unit.currentMovement > 0)
|
||||
firstTileWithoutRoad.startWorkingOnImprovement(GameBasics.TileImprovements["Road"]!!, unit.civInfo)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -60,7 +60,7 @@ class MapUnit {
|
||||
return
|
||||
}
|
||||
|
||||
if (action == "automation") WorkerAutomation().automateWorkerAction(this)
|
||||
if (action == "automation") WorkerAutomation(this).automateWorkerAction()
|
||||
}
|
||||
|
||||
private fun doPostTurnAction() {
|
||||
|
@ -5,6 +5,7 @@ import com.unciv.logic.GameInfo
|
||||
import com.unciv.logic.HexMath
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
import kotlin.math.abs
|
||||
|
||||
class TileMap {
|
||||
|
||||
@ -74,4 +75,15 @@ class TileMap {
|
||||
}
|
||||
}
|
||||
|
||||
fun getShortestPathBetweenTwoTiles(from:TileInfo, to:TileInfo): ArrayList<TileInfo> {
|
||||
val path = ArrayList<TileInfo>()
|
||||
var currentTile = from
|
||||
while(currentTile!=to){
|
||||
path += currentTile
|
||||
currentTile = currentTile.neighbors.minBy { abs(it.position.x-to.position.x)+abs(it.position.y-to.position.y) }!!
|
||||
}
|
||||
path+=to
|
||||
return path
|
||||
}
|
||||
|
||||
}
|
@ -34,10 +34,9 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
val updatedTiles = ArrayList<TileInfo>()
|
||||
for (tileToCheck in tilesToCheck)
|
||||
for (neighbor in tileToCheck.neighbors) {
|
||||
|
||||
var totalDistanceToTile:Float
|
||||
if ((neighbor.getOwner() != unit.civInfo && neighbor.isCityCenter())// Enemy city,
|
||||
|| neighbor.getUnits().isNotEmpty() && neighbor.getUnits().first().civInfo!=unit.civInfo) // Enemy unit
|
||||
|| neighbor.getUnits().isNotEmpty() && neighbor.getUnits().first().civInfo!=unit.civInfo) // Enemy unit
|
||||
totalDistanceToTile = unitMovement // can't move through it - we'll be "stuck" there
|
||||
|
||||
else {
|
||||
@ -149,4 +148,23 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
return getShortestPath(destination).isNotEmpty()
|
||||
}
|
||||
|
||||
fun getFullPathToCloseTile(destination: TileInfo): List<TileInfo> {
|
||||
val currentUnitTile = unit.getTile()
|
||||
val distanceToTiles = unit.getDistanceToTiles()
|
||||
val reversedList = ArrayList<TileInfo>()
|
||||
var currentTile = destination
|
||||
while(currentTile != currentUnitTile){
|
||||
reversedList.add(currentTile)
|
||||
val distanceToCurrentTile = distanceToTiles[currentTile]!!
|
||||
if(currentUnitTile in currentTile.neighbors
|
||||
&& getMovementCostBetweenAdjacentTiles(currentUnitTile,currentTile) == distanceToCurrentTile)
|
||||
return reversedList.reversed()
|
||||
|
||||
for(tile in currentTile.neighbors)
|
||||
currentTile = currentTile.neighbors.first{it in distanceToTiles
|
||||
&& getMovementCostBetweenAdjacentTiles(it,currentTile) == distanceToCurrentTile - distanceToTiles[it]!!}
|
||||
}
|
||||
throw Exception("We couldn't get the path between the two tiles")
|
||||
}
|
||||
|
||||
}
|
@ -110,7 +110,7 @@ class UnitActions {
|
||||
actionList += UnitAction("Automate",
|
||||
{
|
||||
unit.action = "automation"
|
||||
WorkerAutomation().automateWorkerAction(unit)
|
||||
WorkerAutomation(unit).automateWorkerAction()
|
||||
},unit.currentMovement != 0f
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user