How the Unciv AutoPlay System Automates Gameplay: A Technical Deep Dive

The Unciv AutoPlay system automates gameplay by executing unit movements, city management, and turn transitions on background threads through the AutoPlay class, which coordinates with WorldScreen to run multi-turn sequences or single-use automation actions without player input.

The AutoPlay system in Unciv—an open-source Civilization V clone—allows players to delegate repetitive turn-based decisions to AI algorithms. Built around the AutoPlay class located in core/src/com/unciv/ui/screens/worldscreen/unit/AutoPlay.kt, this system manages automation state, turn counters, and thread safety while integrating tightly with the UI layer. Understanding how the AutoPlay system automates Unciv gameplay reveals a carefully designed architecture that balances background processing with game state integrity.

Core Architecture of the AutoPlay System

The AutoPlay implementation separates concerns across three architectural layers: the core automation controller, the UI integration point, and the thread management system.

The AutoPlay Class and Settings

The AutoPlay class serves as the central coordination hub, instantiated with GameSettings.GameSettingsAutoPlay configuration. It maintains the turnsToAutoPlay counter to track remaining automated turns and references autoPlayMaxTurns and autoPlayUntilEnd flags to determine stopping conditions.

WorldScreen Integration

WorldScreen owns the primary AutoPlay instance and monitors automation status through autoPlay.isAutoPlaying() checks during UI update cycles. When victory or defeat conditions trigger, the screen invokes autoPlay.stopAutoPlay() to immediately halt further automation and return control to the player.

User Interface Components

The AutoPlayStatusButton in core/src/com/unciv/ui/screens/worldscreen/status/AutoPlayStatusButton.kt provides the primary interaction surface. A right-click or the KeyboardBinding.AutoPlay shortcut triggers startMultiturnAutoPlay() directly, while left-clicking opens the AutoPlayMenu for specific single-use actions. Configuration options for maximum turns and automation modes reside in AutomationTab within the Options screen.

How AutoPlay Automates Gameplay Execution

The system differentiates between continuous multi-turn automation and discrete single-use actions, each following distinct execution paths.

Starting Multi-Turn Automation

When initiated via startMultiturnAutoPlay(), the system initializes Timers.singleton for performance tracking and sets turnsToAutoPlay to the configured autoPlayMaxTurns value. This mode continues decrementing the counter each turn until reaching zero or until the autoPlayUntilEnd flag triggers completion at game end.

fun startMultiturnAutoPlay() {
    Timers.singleton.startTiming()
    autoPlayTurnInProgress = false
    turnsToAutoPlay = autoPlaySettings.autoPlayMaxTurns
}

Executing Single-Use Automation Jobs

Specific actions like "Military Once" or "Civilian Once" utilize runAutoPlayJobInNewThread(), which accepts a lambda containing the automation logic. The method signature ensures proper thread isolation:

fun runAutoPlayJobInNewThread(
    jobName: String,
    worldScreen: WorldScreen,
    setPlayerTurnAfterEnd: Boolean = true,
    job: () -> Unit
) {
    if (autoPlayTurnInProgress) throw IllegalStateException("AutoPlay already in progress")
    autoPlayTurnInProgress = true
    worldScreen.isPlayersTurn = false
    autoPlayJob = Concurrency.runOnNonDaemonThreadPool(jobName) {
        job()
        autoPlayTurnInProgress = false
        if (setPlayerTurnAfterEnd) worldScreen.isPlayersTurn = true
    }
}

The Automation Logic Pipeline

Different automation modes trigger distinct AI behaviors implemented in AutoPlayMenu.kt:

  • Military Once: Filters military units using isMilitary(), sorts them by NextTurnAutomation.getUnitPriority(), and invokes UnitAutomation.automateUnitMoves() for each unit. It also calls UnitAutomation.tryBombardEnemy() for cities.
  • Civilian Once: Similar logic but filters for civilian units only, automating workers and settlers.
  • Economy Once: Executes NextTurnAutomation.automateCities() to handle production queues, tile assignments, and city growth.
  • End-Turn: Runs the complete AI turn via TurnManager.automateTurn() followed by worldScreen.nextTurn() to advance the game state.

Turn Counting and Stopping Conditions

After each automated turn completes, endTurnMultiturnAutoPlay() manages the countdown:

fun endTurnMultiturnAutoPlay() {
    if (!autoPlaySettings.autoPlayUntilEnd && turnsToAutoPlay > 0)
        turnsToAutoPlay--
}

The UI checks isAutoPlaying() each frame, ensuring immediate cessation when turns exhaust or when WorldScreen detects victory/defeat conditions.

Thread Safety and State Management

The AutoPlay system prevents race conditions through the autoPlayTurnInProgress boolean flag. Before spawning background threads, runAutoPlayJobInNewThread() validates this flag is false, throwing IllegalStateException if a job is already active. The system disables player input via worldScreen.isPlayersTurn = false during execution and restores control only after the lambda completes successfully and setPlayerTurnAfterEnd evaluates to true.

Practical Code Examples

Initiating Multi-Turn AutoPlay Programmatically

val worldScreen = UncivGame.Current.worldScreen
worldScreen.autoPlay.startMultiturnAutoPlay()

Implementing Custom Military Automation

val worldScreen = UncivGame.Current.worldScreen
val autoPlay = worldScreen.autoPlay

val militaryJob = {
    val civ = worldScreen.viewingCiv
    val isAtWar = civ.isAtWar()
    
    val units = civ.units.getCivUnits()
        .filter { it.isMilitary() }
        .sortedBy { unit -> 
            NextTurnAutomation.getUnitPriority(unit, isAtWar) 
        }
    
    for (unit in units) {
        UnitAutomation.automateUnitMoves(unit)
    }
    for (city in civ.cities) {
        UnitAutomation.tryBombardEnemy(city)
    }
    
    worldScreen.shouldUpdate = true
}

autoPlay.runAutoPlayJobInNewThread(
    "AutoPlayMilitary", 
    worldScreen, 
    true, 
    militaryJob
)

Integrating Keyboard Shortcuts

The AutoPlayStatusButton binds shortcuts using:

val directAutoPlay = {
    if (!worldScreen.gameInfo.gameParameters.isOnlineMultiplayer &&
        worldScreen.viewingCiv == worldScreen.gameInfo.currentPlayerCiv) {
        worldScreen.autoPlay.startMultiturnAutoPlay()
        nextTurnButton.update()
    }
}

keyShortcuts.add(KeyboardBinding.AutoPlay, action = directAutoPlay)
onRightClick(action = directAutoPlay)

Summary

  • The AutoPlay class in core/src/com/unciv/ui/screens/worldscreen/unit/AutoPlay.kt centralizes automation logic, turn counters, and thread management.
  • Multi-turn automation executes via startMultiturnAutoPlay(), while single-use actions run through runAutoPlayJobInNewThread() with specific lambdas.
  • Background execution uses Concurrency.runOnNonDaemonThreadPool with autoPlayTurnInProgress locking to prevent concurrent automation jobs.
  • Automation delegates to UnitAutomation and NextTurnAutomation for military movements, civilian actions, and economic management.
  • UI components in AutoPlayStatusButton.kt and AutoPlayMenu.kt provide interaction points, while AutomationTab.kt exposes configuration options.

Frequently Asked Questions

What is the difference between multi-turn and single-use AutoPlay in Unciv?

Multi-turn AutoPlay runs continuously for a specified number of turns or until game end, managed by the turnsToAutoPlay counter in the AutoPlay class. Single-use AutoPlay executes specific actions—such as moving only military units or managing cities once—then immediately returns control to the player through the AutoPlayMenu options.

How does the Unciv AutoPlay system prevent game state corruption?

The system uses the autoPlayTurnInProgress atomic flag to block concurrent automation jobs. Before executing any background task, runAutoPlayJobInNewThread() verifies no job is active, disables player input via isPlayersTurn = false, and only restores control after the lambda completes successfully, ensuring the game state remains consistent during automation.

Can AutoPlay be used in Unciv multiplayer games?

No. The AutoPlayStatusButton explicitly checks !worldScreen.gameInfo.gameParameters.isOnlineMultiplayer before starting automation. This restriction prevents AutoPlay activation in multiplayer sessions to maintain fairness and synchronization between human players.

Which source files should developers modify to extend AutoPlay functionality?

Extend automation logic in core/src/com/unciv/ui/screens/worldscreen/status/AutoPlayMenu.kt for new action types like automated exploration or diplomacy. Modify core/src/com/unciv/ui/screens/worldscreen/unit/AutoPlay.kt for core state management changes. Update core/src/com/unciv/ui/popups/options/AutomationTab.kt to expose new settings in the Options screen, and adjust WorldScreen.kt for integration with the main game loop.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →