mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-14 01:39:40 +07:00
Improved AI roadbuilding - faster and can overcome obstacles
This commit is contained in:
@ -17,7 +17,7 @@ class UnCivGame : Game() {
|
||||
* This exists so that when debugging we can see the entire map.
|
||||
* Remember to turn this to false before commit and upload!
|
||||
*/
|
||||
val viewEntireMapForDebug = false
|
||||
val viewEntireMapForDebug = true
|
||||
|
||||
lateinit var worldScreen: WorldScreen
|
||||
|
||||
@ -27,11 +27,11 @@ class UnCivGame : Game() {
|
||||
GameBasics.run { } // just to initialize the GameBasics
|
||||
settings = GameSaver().getGeneralSettings()
|
||||
if (GameSaver().getSave("Autosave").exists()) {
|
||||
// try {
|
||||
try {
|
||||
loadGame("Autosave")
|
||||
// } catch (ex: Exception) { // silent fail if we can't read the autosave
|
||||
// startNewGame()
|
||||
// }
|
||||
} catch (ex: Exception) { // silent fail if we can't read the autosave
|
||||
startNewGame()
|
||||
}
|
||||
}
|
||||
else startNewGame() // screen=LanguagePickerScreen() disabled because of people's negative reviews =(
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.unciv.logic.automation
|
||||
|
||||
import com.unciv.logic.HexMath
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.map.BFS
|
||||
import com.unciv.logic.map.MapUnit
|
||||
import com.unciv.logic.map.RoadStatus
|
||||
import com.unciv.logic.map.TileInfo
|
||||
@ -40,37 +40,48 @@ class WorkerAutomation(val unit: MapUnit) {
|
||||
if(tile.improvementInProgress!=null) return // we're working!
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun tryConnectingCities():Boolean{ // returns whether we actually did anything
|
||||
val cityThatNeedsConnecting = unit.civInfo.cities.filter { it.population.population>3 && !it.isCapital()
|
||||
val citiesThatNeedConnecting = 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.
|
||||
if(citiesThatNeedConnecting.isEmpty()) 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 citiesThatNeedConnectingBfs = citiesThatNeedConnecting
|
||||
.map { city -> BFS(city.getCenterTile()){it.isLand() && unit.canPassThrough(it)} }
|
||||
.toMutableList()
|
||||
|
||||
val pathToClosestCity = unit.civInfo.gameInfo.tileMap
|
||||
.getShortestPathBetweenTwoTiles(cityThatNeedsConnecting.getCenterTile(),
|
||||
closestConnectedCity.getCenterTile())
|
||||
.filter { it.roadStatus==RoadStatus.None}
|
||||
val connectedCities = unit.civInfo.cities.filter { it.isCapital() || it.cityStats.isConnectedToCapital(RoadStatus.Road) }
|
||||
.map { it.getCenterTile() }
|
||||
|
||||
val unitTile = unit.getTile()
|
||||
if(unitTile in pathToClosestCity){
|
||||
if(unitTile.improvementInProgress==null)
|
||||
unitTile.startWorkingOnImprovement(GameBasics.TileImprovements["Road"]!!,unit.civInfo)
|
||||
return true
|
||||
|
||||
while(citiesThatNeedConnectingBfs.any()){
|
||||
for(bfs in citiesThatNeedConnectingBfs.toList()){
|
||||
bfs.nextStep()
|
||||
if(bfs.tilesToCheck.isEmpty()){ // can't get to any connected city from here
|
||||
citiesThatNeedConnectingBfs.remove(bfs)
|
||||
continue
|
||||
}
|
||||
for(city in connectedCities)
|
||||
if(bfs.tilesToCheck.contains(city)) { // we have a winner!
|
||||
val pathToCity = bfs.getPathTo(city)
|
||||
val roadableTiles = pathToCity.filter { it.roadStatus==RoadStatus.None }
|
||||
val tileToConstructRoadOn :TileInfo
|
||||
if(unit.currentTile in roadableTiles) tileToConstructRoadOn = unit.currentTile
|
||||
else{
|
||||
val reachableTiles = roadableTiles.filter { unit.canMoveTo(it)&& unit.movementAlgs().canReach(it)}
|
||||
if(!reachableTiles.any()) continue
|
||||
tileToConstructRoadOn = roadableTiles.minBy { unit.movementAlgs().getShortestPath(it).size }!!
|
||||
}
|
||||
unit.movementAlgs().headTowards(tileToConstructRoadOn)
|
||||
if(unit.currentMovement>0 && unit.currentTile==tileToConstructRoadOn
|
||||
&& unit.currentTile.improvementInProgress!="Road")
|
||||
tileToConstructRoadOn.startWorkingOnImprovement(GameBasics.TileImprovements["Road"]!!,unit.civInfo)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val closestTileInPathWithNoRoad = pathToClosestCity
|
||||
.filter { unit.canMoveTo(it)}
|
||||
.sortedByDescending { HexMath().getDistance(unit.getTile().position, it.position) }
|
||||
.firstOrNull { unit.movementAlgs().canReach(it) }
|
||||
|
||||
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
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,11 +1,10 @@
|
||||
package com.unciv.logic.city
|
||||
|
||||
import com.unciv.logic.map.BFS
|
||||
import com.unciv.logic.map.RoadStatus
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.gamebasics.Building
|
||||
import com.unciv.models.gamebasics.GameBasics
|
||||
import com.unciv.models.gamebasics.unit.BaseUnit
|
||||
import com.unciv.models.gamebasics.unit.UnitType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
|
||||
@ -224,24 +223,33 @@ class CityStats {
|
||||
|
||||
fun isConnectedToCapital(roadType: RoadStatus): Boolean {
|
||||
if (cityInfo.civInfo.cities.count() < 2) return false// first city!
|
||||
val capitalTile = cityInfo.civInfo.getCapital().getCenterTile()
|
||||
val tilesReached = HashSet<TileInfo>()
|
||||
var tilesToCheck: List<TileInfo> = listOf(cityInfo.getCenterTile())
|
||||
while (tilesToCheck.isNotEmpty()) {
|
||||
val newTiles = tilesToCheck
|
||||
.flatMap { it.neighbors }.distinct()
|
||||
.filter {
|
||||
!tilesReached.contains(it)
|
||||
&& !tilesToCheck.contains(it)
|
||||
&& (roadType !== RoadStatus.Road || it.roadStatus !== RoadStatus.None)
|
||||
&& (roadType !== RoadStatus.Railroad || it.roadStatus === roadType)
|
||||
}
|
||||
|
||||
if (newTiles.contains(capitalTile)) return true
|
||||
tilesReached.addAll(tilesToCheck)
|
||||
tilesToCheck = newTiles
|
||||
}
|
||||
return false
|
||||
val capitalTile = cityInfo.civInfo.getCapital().getCenterTile()
|
||||
val BFS =
|
||||
if(roadType==RoadStatus.Road) BFS(capitalTile){it.roadStatus!=RoadStatus.None}
|
||||
else BFS(capitalTile){it.roadStatus == roadType}
|
||||
|
||||
val cityTile = cityInfo.getCenterTile()
|
||||
BFS.stepUntilDestination(cityTile)
|
||||
return BFS.tilesReached.containsKey(cityTile)
|
||||
|
||||
// val tilesReached = HashSet<TileInfo>()
|
||||
// var tilesToCheck: List<TileInfo> = listOf(cityInfo.getCenterTile())
|
||||
// while (tilesToCheck.isNotEmpty()) {
|
||||
// val newTiles = tilesToCheck
|
||||
// .flatMap { it.neighbors }.distinct()
|
||||
// .filter {
|
||||
// !tilesReached.contains(it)
|
||||
// && !tilesToCheck.contains(it)
|
||||
// && (roadType !== RoadStatus.Road || it.roadStatus !== RoadStatus.None)
|
||||
// && (roadType !== RoadStatus.Railroad || it.roadStatus === roadType)
|
||||
// }
|
||||
//
|
||||
// if (newTiles.contains(capitalTile)) return true
|
||||
// tilesReached.addAll(tilesToCheck)
|
||||
// tilesToCheck = newTiles
|
||||
// }
|
||||
// return false
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
42
core/src/com/unciv/logic/map/BFS.kt
Normal file
42
core/src/com/unciv/logic/map/BFS.kt
Normal file
@ -0,0 +1,42 @@
|
||||
package com.unciv.logic.map
|
||||
|
||||
/**
|
||||
* Defines intermediate steps of a breadth-first search, for use in either get shortest path or get onnected tiles.
|
||||
*/
|
||||
class BFS(val startingPoint: TileInfo, val predicate : (TileInfo) -> Boolean){
|
||||
var tilesToCheck = ArrayList<TileInfo>()
|
||||
val tilesReached = HashMap<TileInfo, TileInfo>() // each tile reached points to its parent tile, where we got to it from
|
||||
|
||||
init{
|
||||
tilesToCheck.add(startingPoint)
|
||||
tilesReached.put(startingPoint,startingPoint)
|
||||
}
|
||||
|
||||
fun stepUntilDestination(destination: TileInfo){
|
||||
while(!tilesReached.containsKey(destination) && tilesToCheck.isNotEmpty())
|
||||
nextStep()
|
||||
}
|
||||
|
||||
fun nextStep(){
|
||||
val newTilesToCheck = ArrayList<TileInfo>()
|
||||
for(tileInfo in tilesToCheck){
|
||||
val fitNeighbors = tileInfo.neighbors.asSequence()
|
||||
.filter(predicate)
|
||||
.filter{!tilesReached.containsKey(it)}.toList()
|
||||
fitNeighbors.forEach { tilesReached[it] = tileInfo }
|
||||
newTilesToCheck.addAll(fitNeighbors)
|
||||
}
|
||||
tilesToCheck = newTilesToCheck
|
||||
}
|
||||
|
||||
fun getPathTo(destination: TileInfo): ArrayList<TileInfo> {
|
||||
val path = ArrayList<TileInfo>()
|
||||
path.add(destination)
|
||||
var currentNode = destination
|
||||
while(currentNode != startingPoint){
|
||||
currentNode = tilesReached[currentNode]!!
|
||||
path.add(currentNode)
|
||||
}
|
||||
return path
|
||||
}
|
||||
}
|
@ -130,5 +130,4 @@ class TileMap {
|
||||
return path
|
||||
}
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user