Improved AI roadbuilding - faster and can overcome obstacles

This commit is contained in:
Yair Morgenstern
2018-10-30 22:37:30 +02:00
parent 10bfdfc8bc
commit 9fe24e2d3d
5 changed files with 111 additions and 51 deletions

View File

@ -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 =(
}

View File

@ -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
}
/**

View File

@ -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

View 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
}
}

View File

@ -130,5 +130,4 @@ class TileMap {
return path
}
}