Console mode for multiple game automation (#2777)

* Remove ruleset from GameSetupInfo class

* Remove dependency from Gdx for file IO:
- load Ruleset
- save/init in GameSettings
- get settings in GameSaver

* Remove simulation logging from GameInfo class

* MapGenerator: add switch for RNG seed verbose

* PlayerPickerTable small refactor

* Basic console mode

* Add multithreading to console mode and refactoring.

* Merge branch 'master' into console

* Small refactor
This commit is contained in:
Alexander Korolyov 2020-07-04 20:47:52 +02:00 committed by GitHub
parent 0271fdead2
commit 4bcae5f664
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 316 additions and 41 deletions

View File

@ -52,8 +52,8 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
* Does not update World View changes until finished. * Does not update World View changes until finished.
* Set false to disable. * Set false to disable.
*/ */
val simulateMaxTurns: Int = 2000 var simulateMaxTurns: Int = 1500
val simulateUntilWin = false var simulateUntilWin = false
/** Console log battles /** Console log battles
*/ */

View File

@ -102,9 +102,7 @@ class GameInfo {
if (thisPlayer.victoryManager.hasWon() && simulateUntilWin) { if (thisPlayer.victoryManager.hasWon() && simulateUntilWin) {
// stop simulation // stop simulation
simulateUntilWin = false simulateUntilWin = false
println("Simulation stopped on turn $turns") break
val victoryType = thisPlayer.victoryManager.hasWonVictoryType()
println("$thisPlayer won $victoryType victory")
} }
} }
switchTurn() switchTurn()
@ -113,8 +111,6 @@ class GameInfo {
currentPlayer = thisPlayer.civName currentPlayer = thisPlayer.civName
currentPlayerCiv = getCivilization(currentPlayer) currentPlayerCiv = getCivilization(currentPlayer)
if (turns == UncivGame.Current.simulateMaxTurns && UncivGame.Current.simulateUntilWin)
println ("Max simulation turns reached $turns: Draw")
// Start our turn immediately before the player can made decisions - affects whether our units can commit automated actions and then be attacked immediately etc. // Start our turn immediately before the player can made decisions - affects whether our units can commit automated actions and then be attacked immediately etc.
notifyOfCloseEnemyUnits(thisPlayer) notifyOfCloseEnemyUnits(thisPlayer)
@ -379,6 +375,4 @@ class GameInfo {
cityConstructions.inProgressConstructions.remove(oldBuildingName) cityConstructions.inProgressConstructions.remove(oldBuildingName)
} }
} }
} }

View File

@ -57,7 +57,11 @@ object GameSaver {
} }
fun getGeneralSettingsFile(): FileHandle { fun getGeneralSettingsFile(): FileHandle {
return Gdx.files.local(settingsFileName) try {
return Gdx.files.local(settingsFileName)
} catch (ex: NullPointerException) {
return FileHandle(settingsFileName)
}
} }
fun getGeneralSettings(): GameSettings { fun getGeneralSettings(): GameSettings {

View File

@ -372,7 +372,8 @@ class CityInfo {
isPuppet = false isPuppet = false
cityConstructions.inProgressConstructions.clear() // undo all progress of the previous civ on units etc. cityConstructions.inProgressConstructions.clear() // undo all progress of the previous civ on units etc.
cityStats.update() cityStats.update()
UncivGame.Current.worldScreen.shouldUpdate = true if (!UncivGame.Current.consoleMode)
UncivGame.Current.worldScreen.shouldUpdate = true
} }
/** This happens when we either puppet OR annex, basically whenever we conquer a city and don't liberate it */ /** This happens when we either puppet OR annex, basically whenever we conquer a city and don't liberate it */

View File

@ -51,9 +51,9 @@ class MapGenerator(val ruleset: Ruleset) {
return map return map
} }
private fun seedRNG(seed: Long) { private fun seedRNG(seed: Long, verbose: Boolean = false) {
randomness.RNG = Random(seed) randomness.RNG = Random(seed)
println("RNG seeded with $seed") if (verbose) println("RNG seeded with $seed")
} }
private fun spawnLakesAndCoasts(map: TileMap) { private fun spawnLakesAndCoasts(map: TileMap) {

View File

@ -39,13 +39,13 @@ class GameSettings {
init { init {
// 26 = Android Oreo. Versions below may display permanent icon in notification bar. // 26 = Android Oreo. Versions below may display permanent icon in notification bar.
if (Gdx.app.type == Application.ApplicationType.Android && Gdx.app.version < 26) { if (Gdx.app?.type == Application.ApplicationType.Android && Gdx.app.version < 26) {
multiplayerTurnCheckerPersistentNotificationEnabled = false multiplayerTurnCheckerPersistentNotificationEnabled = false
} }
} }
fun save(){ fun save(){
if (!isFreshlyCreated && Gdx.app.type == Application.ApplicationType.Desktop) { if (!isFreshlyCreated && Gdx.app?.type == Application.ApplicationType.Desktop) {
windowState = WindowState( Gdx.graphics.width, Gdx.graphics.height) windowState = WindowState( Gdx.graphics.width, Gdx.graphics.height)
} }
GameSaver.setGeneralSettings(this) GameSaver.setGeneralSettings(this)

View File

@ -1,5 +1,6 @@
package com.unciv.models.ruleset package com.unciv.models.ruleset
import com.badlogic.gdx.Files
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.files.FileHandle
import com.unciv.Constants import com.unciv.Constants
@ -181,9 +182,20 @@ class Ruleset {
object RulesetCache :HashMap<String,Ruleset>() { object RulesetCache :HashMap<String,Ruleset>() {
val vanillaRuleset = "Civ V - Vanilla" val vanillaRuleset = "Civ V - Vanilla"
fun loadRulesets() { fun loadRulesets() {
this[""] = Ruleset().apply { load(Gdx.files.internal("jsons/$vanillaRuleset")) } try {
this[""] = Ruleset().apply { load(Gdx.files.internal("jsons/$vanillaRuleset")) }
} catch (e: NullPointerException) {
this[""] = Ruleset().apply { load(FileHandle("jsons/$vanillaRuleset")) }
}
for (modFolder in Gdx.files.local("mods").list()) { var modsHandles: Array<FileHandle>
try {
modsHandles = Gdx.files.local("mods").list()
} catch (ex: NullPointerException) {
modsHandles = FileHandle("mods").list()
}
for (modFolder in modsHandles) {
if (modFolder.name().startsWith('.')) continue if (modFolder.name().startsWith('.')) continue
try { try {
val modRuleset = Ruleset() val modRuleset = Ruleset()

View File

@ -0,0 +1,125 @@
package com.unciv.models.simulation
import com.unciv.logic.GameInfo
import com.unciv.logic.GameStarter
import com.unciv.models.ruleset.VictoryType
import com.unciv.ui.newgamescreen.GameSetupInfo
import java.lang.Integer.max
import java.time.Duration
import kotlin.concurrent.thread
class Simulation(val newGameInfo: GameInfo,
val simulationsPerThread: Int = 5,
val threadsNumber: Int = 1
) {
val maxSimulations = threadsNumber * simulationsPerThread
val civilizations = newGameInfo.civilizations.filter { it.civName != "Spectator" }.map { it.civName }
private var startTime: Long = 0
private var endTime: Long = 0
var steps = ArrayList<SimulationStep>()
var winRate = mutableMapOf<String, MutableInt>()
var winRateByVictory = HashMap<String, MutableMap<VictoryType, MutableInt>>()
var avgSpeed = 0f
var avgDuration: Duration = Duration.ZERO
private var totalTurns = 0
private var totalDuration: Duration = Duration.ZERO
var stepCounter: Int = 0
init{
for (civ in civilizations) {
this.winRate[civ] = MutableInt(0)
winRateByVictory[civ] = mutableMapOf<VictoryType,MutableInt>()
for (victory in VictoryType.values())
winRateByVictory[civ]!![victory] = MutableInt(0)
}
}
fun start() {
startTime = System.currentTimeMillis()
val threads: ArrayList<Thread> = ArrayList()
for (threadId in 1..threadsNumber) {
threads.add(thread {
for (i in 1..simulationsPerThread) {
val gameInfo = GameStarter.startNewGame(GameSetupInfo(newGameInfo))
gameInfo.nextTurn()
var step = SimulationStep(gameInfo)
if (step.victoryType != null) {
step.winner = step.currentPlayer
printWinner(step)
}
else
printDraw(step)
updateCounter(threadId)
add(step)
}
})
}
// wait for all threads to finish
for (thread in threads) thread.join()
endTime = System.currentTimeMillis()
}
@Synchronized fun add(step: SimulationStep, threadId: Int = 1) {
// println("Thread $threadId: End simulation ($stepCounter/$maxSimulations)")
steps.add(step)
}
@Synchronized fun updateCounter(threadId: Int = 1) {
stepCounter++
// println("Thread $threadId: Start simulation ($stepCounter/$maxSimulations)")
println("Simulation step ($stepCounter/$maxSimulations)")
}
private fun printWinner(step: SimulationStep) {
println("%s won %s victory on %d turn".format(
step.winner,
step.victoryType,
step.turns
))
}
private fun printDraw(step: SimulationStep) {
println("Max simulation %d turns reached : Draw".format(
step.turns
))
}
fun getStats() {
// win Rate
steps.forEach {
if (it.winner != null) {
winRate[it.winner!!]!!.inc()
winRateByVictory[it.winner!!]!![it.victoryType]!!.inc()
}
}
totalTurns = steps.sumBy { it.turns }
totalDuration = Duration.ofMillis(endTime - startTime)
avgSpeed = totalTurns.toFloat() / totalDuration.seconds
avgDuration = totalDuration.dividedBy(steps.size.toLong())
}
override fun toString(): String {
var outString = ""
for (civ in civilizations) {
outString += "\n$civ:\n"
val wins = winRate[civ]!!.value!! * 100 / max(steps.size, 1)
outString += "$wins% total win rate \n"
for (victory in VictoryType.values()) {
val winsVictory = winRateByVictory[civ]!![victory]!!.value * 100 / max(winRate[civ]!!.value, 1)
outString += "$victory: $winsVictory% "
}
outString += "\n"
}
outString += "\nAverage speed: %.1f turns/s \n".format(avgSpeed)
outString += "Average game duration: " + formatDuration(avgDuration) + "\n"
outString += "Total time: " + formatDuration(totalDuration) + "\n"
return outString
}
}

View File

@ -0,0 +1,15 @@
package com.unciv.models.simulation
import com.unciv.logic.GameInfo
import com.unciv.models.ruleset.VictoryType
import java.time.Duration
class SimulationStep (gameInfo: GameInfo) {
var turns = gameInfo.turns
var victoryType = gameInfo.currentPlayerCiv.victoryManager.hasWonVictoryType()
var winner: String? = null
val currentPlayer = gameInfo.currentPlayer
// val durationString: String = formatDuration(Duration.ofMillis(System.currentTimeMillis() - startTime))
}

View File

@ -0,0 +1,30 @@
package com.unciv.models.simulation
import java.time.Duration
class MutableInt(var value: Int = 0) {
fun inc() { ++value }
fun get(): Int { return value }
fun set(newValue: Int) { value = newValue }
override fun toString(): String {
return value.toString()
}
}
fun formatDuration(d: Duration): String {
var d = d
val days = d.toDays()
d = d.minusDays(days)
val hours = d.toHours()
d = d.minusHours(hours)
val minutes = d.toMinutes()
d = d.minusMinutes(minutes)
val seconds = d.seconds
d = d.minusSeconds(seconds)
val millis = d.toMillis()
return (if (days == 0L) "" else "$days"+"d ") +
(if (hours == 0L) "" else "$hours"+"h ") +
(if (minutes == 0L) "" else "$minutes"+"m ") +
(if (seconds == 0L) "$millis"+"ms" else "$seconds"+"."+"$millis".take(2)+"s")
}

View File

@ -31,7 +31,7 @@ class EditorMapHolder(internal val mapEditorScreen: MapEditorScreen, internal va
// This is a hack to make the unit icons render correctly on the game, even though the map isn't part of a game // This is a hack to make the unit icons render correctly on the game, even though the map isn't part of a game
// and the units aren't assigned to any "real" CivInfo // and the units aren't assigned to any "real" CivInfo
tileGroup.tileInfo.getUnits().forEach { it.civInfo= CivilizationInfo() tileGroup.tileInfo.getUnits().forEach { it.civInfo= CivilizationInfo()
.apply { nation=mapEditorScreen.gameSetupInfo.ruleset.nations[it.owner]!! } } .apply { nation=mapEditorScreen.ruleset.nations[it.owner]!! } }
tileGroup.showEntireMap = true tileGroup.showEntireMap = true
tileGroup.update() tileGroup.update()

View File

@ -2,6 +2,7 @@ package com.unciv.ui.mapeditor
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.map.Scenario import com.unciv.logic.map.Scenario
import com.unciv.models.ruleset.Ruleset
import com.unciv.ui.newgamescreen.GameOptionsTable import com.unciv.ui.newgamescreen.GameOptionsTable
import com.unciv.ui.newgamescreen.GameSetupInfo import com.unciv.ui.newgamescreen.GameSetupInfo
import com.unciv.ui.newgamescreen.PlayerPickerTable import com.unciv.ui.newgamescreen.PlayerPickerTable
@ -16,10 +17,11 @@ import com.unciv.ui.utils.*
* @param [mapEditorScreen] previous screen from map editor. * @param [mapEditorScreen] previous screen from map editor.
*/ */
class GameParametersScreen(var mapEditorScreen: MapEditorScreen): IPreviousScreen, PickerScreen() { class GameParametersScreen(var mapEditorScreen: MapEditorScreen): IPreviousScreen, PickerScreen() {
override var gameSetupInfo: GameSetupInfo = mapEditorScreen.gameSetupInfo override var gameSetupInfo: GameSetupInfo = mapEditorScreen.gameSetupInfo
override var ruleset: Ruleset = mapEditorScreen.ruleset
var playerPickerTable = PlayerPickerTable(this, this.gameSetupInfo.gameParameters) var playerPickerTable = PlayerPickerTable(this, this.gameSetupInfo.gameParameters)
var gameOptionsTable = GameOptionsTable(mapEditorScreen.gameSetupInfo) { desiredCiv: String -> playerPickerTable.update(desiredCiv) } var gameOptionsTable = GameOptionsTable(mapEditorScreen) { desiredCiv: String -> playerPickerTable.update(desiredCiv) }
init { init {
setDefaultCloseAction(mapEditorScreen) setDefaultCloseAction(mapEditorScreen)
@ -32,6 +34,7 @@ class GameParametersScreen(var mapEditorScreen: MapEditorScreen): IPreviousScree
rightSideButton.onClick { rightSideButton.onClick {
mapEditorScreen.gameSetupInfo = gameSetupInfo mapEditorScreen.gameSetupInfo = gameSetupInfo
mapEditorScreen.scenario = Scenario(mapEditorScreen.tileMap, mapEditorScreen.gameSetupInfo.gameParameters) mapEditorScreen.scenario = Scenario(mapEditorScreen.tileMap, mapEditorScreen.gameSetupInfo.gameParameters)
mapEditorScreen.ruleset = ruleset //TODO: figure out whether it is necessary
mapEditorScreen.tileEditorOptions.update() mapEditorScreen.tileEditorOptions.update()
mapEditorScreen.mapHolder.updateTileGroups() mapEditorScreen.mapHolder.updateTileGroups()
UncivGame.Current.setScreen(mapEditorScreen) UncivGame.Current.setScreen(mapEditorScreen)

View File

@ -12,6 +12,7 @@ import com.unciv.logic.MapSaver
import com.unciv.logic.map.Scenario import com.unciv.logic.map.Scenario
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.logic.map.TileMap import com.unciv.logic.map.TileMap
import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.newgamescreen.GameSetupInfo import com.unciv.ui.newgamescreen.GameSetupInfo
import com.unciv.ui.newgamescreen.IPreviousScreen import com.unciv.ui.newgamescreen.IPreviousScreen
@ -25,6 +26,7 @@ class MapEditorScreen(): IPreviousScreen, CameraStageBaseScreen() {
var tileMap = TileMap() var tileMap = TileMap()
var scenarioName = "" // when loading map: mapName is taken as default for scenarioName var scenarioName = "" // when loading map: mapName is taken as default for scenarioName
var scenario: Scenario? = null // main indicator whether scenario information is present var scenario: Scenario? = null // main indicator whether scenario information is present
override var ruleset = RulesetCache.getBaseRuleset()
override var gameSetupInfo = GameSetupInfo() override var gameSetupInfo = GameSetupInfo()
lateinit var mapHolder: EditorMapHolder lateinit var mapHolder: EditorMapHolder
@ -66,7 +68,7 @@ class MapEditorScreen(): IPreviousScreen, CameraStageBaseScreen() {
} }
fun initialize() { fun initialize() {
tileMap.setTransients(gameSetupInfo.ruleset,false) tileMap.setTransients(ruleset,false)
mapHolder = EditorMapHolder(this, tileMap) mapHolder = EditorMapHolder(this, tileMap)
mapHolder.addTiles(stage.width) mapHolder.addTiles(stage.width)

View File

@ -34,7 +34,7 @@ class TileEditorOptionsTable(val mapEditorScreen: MapEditorScreen): Table(Camera
var brushSize = 1 var brushSize = 1
private var currentHex: Actor = Group() private var currentHex: Actor = Group()
private val ruleset = mapEditorScreen.gameSetupInfo.ruleset private val ruleset = mapEditorScreen.ruleset
private val gameParameters = mapEditorScreen.gameSetupInfo.gameParameters private val gameParameters = mapEditorScreen.gameSetupInfo.gameParameters
private val scrollPanelHeight = mapEditorScreen.stage.height*0.7f - 100f // -100 reserved for currentHex table private val scrollPanelHeight = mapEditorScreen.stage.height*0.7f - 100f // -100 reserved for currentHex table
@ -353,7 +353,7 @@ class TileEditorOptionsTable(val mapEditorScreen: MapEditorScreen): Table(Camera
// for the tile image // for the tile image
val tileInfo = TileInfo() val tileInfo = TileInfo()
tileInfo.ruleset = mapEditorScreen.gameSetupInfo.ruleset tileInfo.ruleset = mapEditorScreen.ruleset
val terrain = resource.terrainsCanBeFoundOn.first() val terrain = resource.terrainsCanBeFoundOn.first()
val terrainObject = ruleset.terrains[terrain]!! val terrainObject = ruleset.terrains[terrain]!!
if (terrainObject.type == TerrainType.TerrainFeature) { if (terrainObject.type == TerrainType.TerrainFeature) {
@ -446,7 +446,7 @@ class TileEditorOptionsTable(val mapEditorScreen: MapEditorScreen): Table(Camera
} }
private fun makeTileGroup(tileInfo: TileInfo): TileGroup { private fun makeTileGroup(tileInfo: TileInfo): TileGroup {
tileInfo.ruleset = mapEditorScreen.gameSetupInfo.ruleset tileInfo.ruleset = mapEditorScreen.ruleset
tileInfo.setTerrainTransients() tileInfo.setTerrainTransients()
val group = TileGroup(tileInfo, TileSetStrings()) val group = TileGroup(tileInfo, TileSetStrings())
group.showEntireMap = true group.showEntireMap = true

View File

@ -10,10 +10,10 @@ import com.unciv.models.ruleset.VictoryType
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.utils.* import com.unciv.ui.utils.*
class GameOptionsTable(gameSetupInfo: GameSetupInfo, val updatePlayerPickerTable:(desiredCiv:String)->Unit) class GameOptionsTable(previousScreen: IPreviousScreen, val updatePlayerPickerTable:(desiredCiv:String)->Unit)
: Table(CameraStageBaseScreen.skin) { : Table(CameraStageBaseScreen.skin) {
var gameParameters = gameSetupInfo.gameParameters var gameParameters = previousScreen.gameSetupInfo.gameParameters
val ruleset = gameSetupInfo.ruleset val ruleset = previousScreen.ruleset
var locked = false var locked = false
init { init {

View File

@ -12,6 +12,7 @@ import com.unciv.ui.utils.CameraStageBaseScreen
interface IPreviousScreen { interface IPreviousScreen {
val gameSetupInfo: GameSetupInfo val gameSetupInfo: GameSetupInfo
var stage: Stage var stage: Stage
val ruleset: Ruleset
/** /**
* Method added for compatibility with [PlayerPickerTable] which addresses * Method added for compatibility with [PlayerPickerTable] which addresses

View File

@ -20,7 +20,7 @@ import kotlin.concurrent.thread
class GameSetupInfo(var gameId:String, var gameParameters: GameParameters, var mapParameters: MapParameters) { class GameSetupInfo(var gameId:String, var gameParameters: GameParameters, var mapParameters: MapParameters) {
var ruleset = RulesetCache.getComplexRuleset(gameParameters.mods) // var ruleset = RulesetCache.getComplexRuleset(gameParameters.mods)
constructor() : this("", GameParameters(), MapParameters()) constructor() : this("", GameParameters(), MapParameters())
constructor(gameInfo: GameInfo) : this("", gameInfo.gameParameters.clone(), gameInfo.tileMap.mapParameters) constructor(gameInfo: GameInfo) : this("", gameInfo.gameParameters.clone(), gameInfo.tileMap.mapParameters)
@ -29,8 +29,9 @@ class GameSetupInfo(var gameId:String, var gameParameters: GameParameters, var m
class NewGameScreen(previousScreen:CameraStageBaseScreen, _gameSetupInfo: GameSetupInfo?=null): IPreviousScreen, PickerScreen() { class NewGameScreen(previousScreen:CameraStageBaseScreen, _gameSetupInfo: GameSetupInfo?=null): IPreviousScreen, PickerScreen() {
override val gameSetupInfo = _gameSetupInfo ?: GameSetupInfo() override val gameSetupInfo = _gameSetupInfo ?: GameSetupInfo()
override val ruleset = RulesetCache.getComplexRuleset(gameSetupInfo.gameParameters.mods)
var playerPickerTable = PlayerPickerTable(this, gameSetupInfo.gameParameters) var playerPickerTable = PlayerPickerTable(this, gameSetupInfo.gameParameters)
var newGameOptionsTable = GameOptionsTable(gameSetupInfo) { desiredCiv: String -> playerPickerTable.update(desiredCiv) } var newGameOptionsTable = GameOptionsTable(this) { desiredCiv: String -> playerPickerTable.update(desiredCiv) }
var mapOptionsTable = MapOptionsTable(this) var mapOptionsTable = MapOptionsTable(this)

View File

@ -15,10 +15,12 @@ import com.unciv.logic.civilization.PlayerType
import com.unciv.models.metadata.GameParameters import com.unciv.models.metadata.GameParameters
import com.unciv.models.metadata.Player import com.unciv.models.metadata.Player
import com.unciv.models.ruleset.Nation import com.unciv.models.ruleset.Nation
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.mapeditor.GameParametersScreen import com.unciv.ui.mapeditor.GameParametersScreen
import com.unciv.ui.utils.* import com.unciv.ui.utils.*
import java.util.* import java.util.*
import kotlin.reflect.typeOf
/** /**
* This [Table] is used to pick or edit players information for new game/scenario creation. * This [Table] is used to pick or edit players information for new game/scenario creation.
@ -55,10 +57,10 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
*/ */
fun update(desiredCiv: String = "") { fun update(desiredCiv: String = "") {
playerListTable.clear() playerListTable.clear()
val ruleset = previousScreen.gameSetupInfo.ruleset // the mod picking changes this ruleset val gameBasics = previousScreen.ruleset // the mod picking changes this ruleset
reassignRemovedModReferences() reassignRemovedModReferences()
val newRulesetPlayableCivs = previousScreen.gameSetupInfo.ruleset.nations.count { it.key != Constants.barbarians } val newRulesetPlayableCivs = previousScreen.ruleset.nations.count { it.key != Constants.barbarians }
if (gameParameters.players.size > newRulesetPlayableCivs) if (gameParameters.players.size > newRulesetPlayableCivs)
gameParameters.players = ArrayList(gameParameters.players.subList(0, newRulesetPlayableCivs)) gameParameters.players = ArrayList(gameParameters.players.subList(0, newRulesetPlayableCivs))
if (desiredCiv.isNotEmpty()) assignDesiredCiv(desiredCiv) if (desiredCiv.isNotEmpty()) assignDesiredCiv(desiredCiv)
@ -66,7 +68,7 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
for (player in gameParameters.players) { for (player in gameParameters.players) {
playerListTable.add(getPlayerTable(player)).width(civBlocksWidth).padBottom(20f).row() playerListTable.add(getPlayerTable(player)).width(civBlocksWidth).padBottom(20f).row()
} }
if (gameParameters.players.count() < ruleset.nations.values.count { it.isMajorCiv() } if (gameParameters.players.count() < gameBasics.nations.values.count { it.isMajorCiv() }
&& !locked) { && !locked) {
playerListTable.add("+".toLabel(Color.BLACK, 30).apply { this.setAlignment(Align.center) } playerListTable.add("+".toLabel(Color.BLACK, 30).apply { this.setAlignment(Align.center) }
.surroundWithCircle(50f).onClick { .surroundWithCircle(50f).onClick {
@ -85,7 +87,7 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
*/ */
private fun reassignRemovedModReferences() { private fun reassignRemovedModReferences() {
for (player in gameParameters.players) { for (player in gameParameters.players) {
if (!previousScreen.gameSetupInfo.ruleset.nations.containsKey(player.chosenCiv)) if (!previousScreen.ruleset.nations.containsKey(player.chosenCiv))
player.chosenCiv = "Random" player.chosenCiv = "Random"
} }
} }
@ -118,7 +120,6 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
val playerTypeTextbutton = player.playerType.name.toTextButton() val playerTypeTextbutton = player.playerType.name.toTextButton()
playerTypeTextbutton.onClick { playerTypeTextbutton.onClick {
// if (locked) return@onClick
if (player.playerType == PlayerType.AI) if (player.playerType == PlayerType.AI)
player.playerType = PlayerType.Human player.playerType = PlayerType.Human
else player.playerType = PlayerType.AI else player.playerType = PlayerType.AI
@ -184,7 +185,7 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
.apply { this.setAlignment(Align.center) } .apply { this.setAlignment(Align.center) }
.surroundWithCircle(36f).apply { circle.color = Color.BLACK } .surroundWithCircle(36f).apply { circle.color = Color.BLACK }
.surroundWithCircle(40f, false).apply { circle.color = Color.WHITE } .surroundWithCircle(40f, false).apply { circle.color = Color.WHITE }
else ImageGetter.getNationIndicator(previousScreen.gameSetupInfo.ruleset.nations[player.chosenCiv]!!, 40f) else ImageGetter.getNationIndicator(previousScreen.ruleset.nations[player.chosenCiv]!!, 40f)
nationTable.add(nationImage).pad(5f) nationTable.add(nationImage).pad(5f)
nationTable.add(player.chosenCiv.toLabel()).pad(5f) nationTable.add(player.chosenCiv.toLabel()).pad(5f)
nationTable.touchable = Touchable.enabled nationTable.touchable = Touchable.enabled
@ -225,10 +226,9 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
if (player.chosenCiv == nation.name) if (player.chosenCiv == nation.name)
continue continue
nationListTable.add(NationTable(nation, nationsPopupWidth, previousScreen.gameSetupInfo.ruleset).onClick { nationListTable.add(NationTable(nation, nationsPopupWidth, previousScreen.ruleset).onClick {
if (previousScreen is GameParametersScreen) { if (previousScreen is GameParametersScreen)
previousScreen.mapEditorScreen.tileMap.switchPlayersNation(player, nation) previousScreen.mapEditorScreen.tileMap.switchPlayersNation(player, nation)
}
player.chosenCiv = nation.name player.chosenCiv = nation.name
nationsPopup.close() nationsPopup.close()
update() update()
@ -259,7 +259,7 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
*/ */
private fun getAvailablePlayerCivs(): ArrayList<Nation> { private fun getAvailablePlayerCivs(): ArrayList<Nation> {
var nations = ArrayList<Nation>() var nations = ArrayList<Nation>()
for (nation in previousScreen.gameSetupInfo.ruleset.nations.values for (nation in previousScreen.ruleset.nations.values
.filter { it.isMajorCiv() }) { .filter { it.isMajorCiv() }) {
if (gameParameters.players.any { it.chosenCiv == nation.name }) if (gameParameters.players.any { it.chosenCiv == nation.name })
continue continue
@ -267,5 +267,4 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
} }
return nations return nations
} }
}
}

View File

@ -0,0 +1,88 @@
package com.unciv.app.desktop
import com.unciv.UncivGame
import com.unciv.UncivGameParameters
import com.unciv.logic.GameStarter
import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.map.MapParameters
import com.unciv.logic.map.MapSize
import com.unciv.models.metadata.GameParameters
import com.unciv.models.metadata.GameSettings
import com.unciv.models.metadata.GameSpeed
import com.unciv.models.metadata.Player
import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.simulation.Simulation
import com.unciv.models.simulation.SimulationStep
import com.unciv.models.simulation.formatDuration
import com.unciv.ui.newgamescreen.GameSetupInfo
import java.time.Duration
import kotlin.concurrent.thread
import kotlin.system.exitProcess
internal object ConsoleLauncher {
@JvmStatic
fun main(arg: Array<String>) {
val version = "0.1"
val consoleParameters = UncivGameParameters(
version,
null,
{ exitProcess(0) },
null,
null,
true
)
val game = UncivGame(consoleParameters)
UncivGame.Current = game
UncivGame.Current.settings = GameSettings().apply { showTutorials = false }
UncivGame.Current.simulateMaxTurns = 1000
UncivGame.Current.simulateUntilWin = true
RulesetCache.loadRulesets()
val gameParameters = getGameParameters("China", "Greece")
val mapParameters = getMapParameters()
val gameSetupInfo = GameSetupInfo(gameParameters, mapParameters)
val newGame = GameStarter.startNewGame(gameSetupInfo)
UncivGame.Current.gameInfo = newGame
var simulation = Simulation(newGame,25,4)
simulation.start()
simulation.getStats()
println(simulation)
}
private fun getMapParameters(): MapParameters {
return MapParameters().apply {
size = MapSize.Tiny
noRuins = true
noNaturalWonders = true
}
}
private fun getGameParameters(civilization1: String, civilization2: String): GameParameters {
return GameParameters().apply {
difficulty = "Chieftain"
gameSpeed = GameSpeed.Quick
noBarbarians = true
players = ArrayList<Player>().apply {
add(Player().apply {
playerType = PlayerType.AI
chosenCiv = civilization1
})
add(Player().apply {
playerType = PlayerType.AI
chosenCiv = civilization2
})
add(Player().apply {
playerType = PlayerType.Human
chosenCiv = "Spectator"
})
}
}
}
}