From cb6160c4a8f3a3a5c2755ff6e1294a55f3ee2a2c Mon Sep 17 00:00:00 2001 From: OptimizedForDensity <105244635+OptimizedForDensity@users.noreply.github.com> Date: Thu, 14 Jul 2022 09:49:39 -0400 Subject: [PATCH] Significantly reduce AI turn time (#7441) * Significantly speed up AI pathfinding * Undo caching and improve AI * Undo comment change * Improvement * Reorder and comment --- .../com/unciv/logic/map/UnitMovementAlgorithms.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index 04e0ce5bf8..09c09666f0 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -137,7 +137,7 @@ class UnitMovementAlgorithms(val unit: MapUnit) { * Does not consider if tiles can actually be entered, use canMoveTo for that. * If a tile can be reached within the turn, but it cannot be passed through, the total distance to it is set to unitMovement */ - fun getDistanceToTilesWithinTurn(origin: Vector2, unitMovement: Float, considerZoneOfControl: Boolean = true): PathsToTilesWithinTurn { + fun getDistanceToTilesWithinTurn(origin: Vector2, unitMovement: Float, considerZoneOfControl: Boolean = true, tilesToIgnore: HashSet? = null): PathsToTilesWithinTurn { val distanceToTiles = PathsToTilesWithinTurn() if (unitMovement == 0f) return distanceToTiles @@ -151,6 +151,7 @@ class UnitMovementAlgorithms(val unit: MapUnit) { val updatedTiles = ArrayList() for (tileToCheck in tilesToCheck) for (neighbor in tileToCheck.neighbors) { + if (tilesToIgnore?.contains(neighbor) == true) continue // ignore this tile var totalDistanceToTile: Float = when { !unit.civInfo.exploredTiles.contains(neighbor.position) -> distanceToTiles[tileToCheck]!!.totalDistance + 1f // If we don't know then we just guess it to be 1. @@ -205,6 +206,7 @@ class UnitMovementAlgorithms(val unit: MapUnit) { val newTilesToCheck = ArrayList() val distanceToDestination = HashMap() var considerZoneOfControl = true // only for first distance! + val visitedTiles: HashSet = hashSetOf(currentTile) while (true) { if (distance == 2) { // only set this once after distance > 1 movementThisTurn = unit.getMaxMovement().toFloat() @@ -213,14 +215,15 @@ class UnitMovementAlgorithms(val unit: MapUnit) { newTilesToCheck.clear() distanceToDestination.clear() for (tileToCheck in tilesToCheck) { - val distanceToTilesThisTurn = getDistanceToTilesWithinTurn(tileToCheck.position, movementThisTurn, considerZoneOfControl) + val distanceToTilesThisTurn = getDistanceToTilesWithinTurn(tileToCheck.position, movementThisTurn, considerZoneOfControl, visitedTiles) for (reachableTile in distanceToTilesThisTurn.keys) { // Avoid damaging terrain on first pass if (avoidDamagingTerrain && unit.getDamageFromTerrain(reachableTile) > 0) continue - if (reachableTile == destination) + if (reachableTile == destination) { distanceToDestination[tileToCheck] = distanceToTilesThisTurn[reachableTile]!!.totalDistance - else { + break + } else { if (movementTreeParents.containsKey(reachableTile)) continue // We cannot be faster than anything existing... if (!isUnknownTileWeShouldAssumeToBePassable(reachableTile) && !canMoveTo(reachableTile)) continue // This is a tile that we can't actually enter - either an intermediary tile containing our unit, or an enemy unit/city @@ -245,6 +248,8 @@ class UnitMovementAlgorithms(val unit: MapUnit) { if (newTilesToCheck.isEmpty()) return emptyList() // there is NO PATH (eg blocked by enemy units) + // add newTilesToCheck to visitedTiles so we do not path over these tiles in a later iteration + visitedTiles.addAll(newTilesToCheck) // no need to check tiles that are surrounded by reachable tiles, only need to check the edgemost tiles. // Because anything we can reach from intermediate tiles, can be more easily reached by the edgemost tiles, // since we'll have to pass through an edgemost tile in order to reach the destination anyway