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:
parent
0271fdead2
commit
4bcae5f664
19 changed files with 316 additions and 41 deletions
|
@ -52,8 +52,8 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
|
|||
* Does not update World View changes until finished.
|
||||
* Set false to disable.
|
||||
*/
|
||||
val simulateMaxTurns: Int = 2000
|
||||
val simulateUntilWin = false
|
||||
var simulateMaxTurns: Int = 1500
|
||||
var simulateUntilWin = false
|
||||
|
||||
/** Console log battles
|
||||
*/
|
||||
|
|
|
@ -102,9 +102,7 @@ class GameInfo {
|
|||
if (thisPlayer.victoryManager.hasWon() && simulateUntilWin) {
|
||||
// stop simulation
|
||||
simulateUntilWin = false
|
||||
println("Simulation stopped on turn $turns")
|
||||
val victoryType = thisPlayer.victoryManager.hasWonVictoryType()
|
||||
println("$thisPlayer won $victoryType victory")
|
||||
break
|
||||
}
|
||||
}
|
||||
switchTurn()
|
||||
|
@ -113,8 +111,6 @@ class GameInfo {
|
|||
currentPlayer = thisPlayer.civName
|
||||
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.
|
||||
notifyOfCloseEnemyUnits(thisPlayer)
|
||||
|
@ -379,6 +375,4 @@ class GameInfo {
|
|||
cityConstructions.inProgressConstructions.remove(oldBuildingName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -57,7 +57,11 @@ object GameSaver {
|
|||
}
|
||||
|
||||
fun getGeneralSettingsFile(): FileHandle {
|
||||
return Gdx.files.local(settingsFileName)
|
||||
try {
|
||||
return Gdx.files.local(settingsFileName)
|
||||
} catch (ex: NullPointerException) {
|
||||
return FileHandle(settingsFileName)
|
||||
}
|
||||
}
|
||||
|
||||
fun getGeneralSettings(): GameSettings {
|
||||
|
|
|
@ -372,7 +372,8 @@ class CityInfo {
|
|||
isPuppet = false
|
||||
cityConstructions.inProgressConstructions.clear() // undo all progress of the previous civ on units etc.
|
||||
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 */
|
||||
|
|
|
@ -51,9 +51,9 @@ class MapGenerator(val ruleset: Ruleset) {
|
|||
return map
|
||||
}
|
||||
|
||||
private fun seedRNG(seed: Long) {
|
||||
private fun seedRNG(seed: Long, verbose: Boolean = false) {
|
||||
randomness.RNG = Random(seed)
|
||||
println("RNG seeded with $seed")
|
||||
if (verbose) println("RNG seeded with $seed")
|
||||
}
|
||||
|
||||
private fun spawnLakesAndCoasts(map: TileMap) {
|
||||
|
|
|
@ -39,13 +39,13 @@ class GameSettings {
|
|||
|
||||
init {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
GameSaver.setGeneralSettings(this)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.unciv.models.ruleset
|
||||
|
||||
import com.badlogic.gdx.Files
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.files.FileHandle
|
||||
import com.unciv.Constants
|
||||
|
@ -181,9 +182,20 @@ class Ruleset {
|
|||
object RulesetCache :HashMap<String,Ruleset>() {
|
||||
val vanillaRuleset = "Civ V - Vanilla"
|
||||
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
|
||||
try {
|
||||
val modRuleset = Ruleset()
|
||||
|
|
125
core/src/com/unciv/models/simulation/Simulation.kt
Normal file
125
core/src/com/unciv/models/simulation/Simulation.kt
Normal 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
|
||||
}
|
||||
}
|
||||
|
15
core/src/com/unciv/models/simulation/SimulationStep.kt
Normal file
15
core/src/com/unciv/models/simulation/SimulationStep.kt
Normal 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))
|
||||
}
|
||||
|
||||
|
30
core/src/com/unciv/models/simulation/Utils.kt
Normal file
30
core/src/com/unciv/models/simulation/Utils.kt
Normal 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")
|
||||
}
|
|
@ -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
|
||||
// and the units aren't assigned to any "real" CivInfo
|
||||
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.update()
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.unciv.ui.mapeditor
|
|||
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.map.Scenario
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.ui.newgamescreen.GameOptionsTable
|
||||
import com.unciv.ui.newgamescreen.GameSetupInfo
|
||||
import com.unciv.ui.newgamescreen.PlayerPickerTable
|
||||
|
@ -16,10 +17,11 @@ import com.unciv.ui.utils.*
|
|||
* @param [mapEditorScreen] previous screen from map editor.
|
||||
*/
|
||||
class GameParametersScreen(var mapEditorScreen: MapEditorScreen): IPreviousScreen, PickerScreen() {
|
||||
|
||||
override var gameSetupInfo: GameSetupInfo = mapEditorScreen.gameSetupInfo
|
||||
override var ruleset: Ruleset = mapEditorScreen.ruleset
|
||||
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 {
|
||||
setDefaultCloseAction(mapEditorScreen)
|
||||
|
@ -32,6 +34,7 @@ class GameParametersScreen(var mapEditorScreen: MapEditorScreen): IPreviousScree
|
|||
rightSideButton.onClick {
|
||||
mapEditorScreen.gameSetupInfo = gameSetupInfo
|
||||
mapEditorScreen.scenario = Scenario(mapEditorScreen.tileMap, mapEditorScreen.gameSetupInfo.gameParameters)
|
||||
mapEditorScreen.ruleset = ruleset //TODO: figure out whether it is necessary
|
||||
mapEditorScreen.tileEditorOptions.update()
|
||||
mapEditorScreen.mapHolder.updateTileGroups()
|
||||
UncivGame.Current.setScreen(mapEditorScreen)
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.unciv.logic.MapSaver
|
|||
import com.unciv.logic.map.Scenario
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.newgamescreen.GameSetupInfo
|
||||
import com.unciv.ui.newgamescreen.IPreviousScreen
|
||||
|
@ -25,6 +26,7 @@ class MapEditorScreen(): IPreviousScreen, CameraStageBaseScreen() {
|
|||
var tileMap = TileMap()
|
||||
var scenarioName = "" // when loading map: mapName is taken as default for scenarioName
|
||||
var scenario: Scenario? = null // main indicator whether scenario information is present
|
||||
override var ruleset = RulesetCache.getBaseRuleset()
|
||||
|
||||
override var gameSetupInfo = GameSetupInfo()
|
||||
lateinit var mapHolder: EditorMapHolder
|
||||
|
@ -66,7 +68,7 @@ class MapEditorScreen(): IPreviousScreen, CameraStageBaseScreen() {
|
|||
}
|
||||
|
||||
fun initialize() {
|
||||
tileMap.setTransients(gameSetupInfo.ruleset,false)
|
||||
tileMap.setTransients(ruleset,false)
|
||||
|
||||
mapHolder = EditorMapHolder(this, tileMap)
|
||||
mapHolder.addTiles(stage.width)
|
||||
|
|
|
@ -34,7 +34,7 @@ class TileEditorOptionsTable(val mapEditorScreen: MapEditorScreen): Table(Camera
|
|||
var brushSize = 1
|
||||
private var currentHex: Actor = Group()
|
||||
|
||||
private val ruleset = mapEditorScreen.gameSetupInfo.ruleset
|
||||
private val ruleset = mapEditorScreen.ruleset
|
||||
private val gameParameters = mapEditorScreen.gameSetupInfo.gameParameters
|
||||
|
||||
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
|
||||
val tileInfo = TileInfo()
|
||||
tileInfo.ruleset = mapEditorScreen.gameSetupInfo.ruleset
|
||||
tileInfo.ruleset = mapEditorScreen.ruleset
|
||||
val terrain = resource.terrainsCanBeFoundOn.first()
|
||||
val terrainObject = ruleset.terrains[terrain]!!
|
||||
if (terrainObject.type == TerrainType.TerrainFeature) {
|
||||
|
@ -446,7 +446,7 @@ class TileEditorOptionsTable(val mapEditorScreen: MapEditorScreen): Table(Camera
|
|||
}
|
||||
|
||||
private fun makeTileGroup(tileInfo: TileInfo): TileGroup {
|
||||
tileInfo.ruleset = mapEditorScreen.gameSetupInfo.ruleset
|
||||
tileInfo.ruleset = mapEditorScreen.ruleset
|
||||
tileInfo.setTerrainTransients()
|
||||
val group = TileGroup(tileInfo, TileSetStrings())
|
||||
group.showEntireMap = true
|
||||
|
|
|
@ -10,10 +10,10 @@ import com.unciv.models.ruleset.VictoryType
|
|||
import com.unciv.models.translations.tr
|
||||
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) {
|
||||
var gameParameters = gameSetupInfo.gameParameters
|
||||
val ruleset = gameSetupInfo.ruleset
|
||||
var gameParameters = previousScreen.gameSetupInfo.gameParameters
|
||||
val ruleset = previousScreen.ruleset
|
||||
var locked = false
|
||||
|
||||
init {
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.unciv.ui.utils.CameraStageBaseScreen
|
|||
interface IPreviousScreen {
|
||||
val gameSetupInfo: GameSetupInfo
|
||||
var stage: Stage
|
||||
val ruleset: Ruleset
|
||||
|
||||
/**
|
||||
* Method added for compatibility with [PlayerPickerTable] which addresses
|
||||
|
|
|
@ -20,7 +20,7 @@ import kotlin.concurrent.thread
|
|||
|
||||
|
||||
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(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() {
|
||||
override val gameSetupInfo = _gameSetupInfo ?: GameSetupInfo()
|
||||
override val ruleset = RulesetCache.getComplexRuleset(gameSetupInfo.gameParameters.mods)
|
||||
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)
|
||||
|
||||
|
||||
|
|
|
@ -15,10 +15,12 @@ import com.unciv.logic.civilization.PlayerType
|
|||
import com.unciv.models.metadata.GameParameters
|
||||
import com.unciv.models.metadata.Player
|
||||
import com.unciv.models.ruleset.Nation
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.mapeditor.GameParametersScreen
|
||||
import com.unciv.ui.utils.*
|
||||
import java.util.*
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
/**
|
||||
* 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 = "") {
|
||||
playerListTable.clear()
|
||||
val ruleset = previousScreen.gameSetupInfo.ruleset // the mod picking changes this ruleset
|
||||
val gameBasics = previousScreen.ruleset // the mod picking changes this ruleset
|
||||
|
||||
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)
|
||||
gameParameters.players = ArrayList(gameParameters.players.subList(0, newRulesetPlayableCivs))
|
||||
if (desiredCiv.isNotEmpty()) assignDesiredCiv(desiredCiv)
|
||||
|
@ -66,7 +68,7 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
|||
for (player in gameParameters.players) {
|
||||
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) {
|
||||
playerListTable.add("+".toLabel(Color.BLACK, 30).apply { this.setAlignment(Align.center) }
|
||||
.surroundWithCircle(50f).onClick {
|
||||
|
@ -85,7 +87,7 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
|||
*/
|
||||
private fun reassignRemovedModReferences() {
|
||||
for (player in gameParameters.players) {
|
||||
if (!previousScreen.gameSetupInfo.ruleset.nations.containsKey(player.chosenCiv))
|
||||
if (!previousScreen.ruleset.nations.containsKey(player.chosenCiv))
|
||||
player.chosenCiv = "Random"
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +120,6 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
|||
|
||||
val playerTypeTextbutton = player.playerType.name.toTextButton()
|
||||
playerTypeTextbutton.onClick {
|
||||
// if (locked) return@onClick
|
||||
if (player.playerType == PlayerType.AI)
|
||||
player.playerType = PlayerType.Human
|
||||
else player.playerType = PlayerType.AI
|
||||
|
@ -184,7 +185,7 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
|||
.apply { this.setAlignment(Align.center) }
|
||||
.surroundWithCircle(36f).apply { circle.color = Color.BLACK }
|
||||
.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(player.chosenCiv.toLabel()).pad(5f)
|
||||
nationTable.touchable = Touchable.enabled
|
||||
|
@ -225,10 +226,9 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
|||
if (player.chosenCiv == nation.name)
|
||||
continue
|
||||
|
||||
nationListTable.add(NationTable(nation, nationsPopupWidth, previousScreen.gameSetupInfo.ruleset).onClick {
|
||||
if (previousScreen is GameParametersScreen) {
|
||||
nationListTable.add(NationTable(nation, nationsPopupWidth, previousScreen.ruleset).onClick {
|
||||
if (previousScreen is GameParametersScreen)
|
||||
previousScreen.mapEditorScreen.tileMap.switchPlayersNation(player, nation)
|
||||
}
|
||||
player.chosenCiv = nation.name
|
||||
nationsPopup.close()
|
||||
update()
|
||||
|
@ -259,7 +259,7 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
|||
*/
|
||||
private fun getAvailablePlayerCivs(): ArrayList<Nation> {
|
||||
var nations = ArrayList<Nation>()
|
||||
for (nation in previousScreen.gameSetupInfo.ruleset.nations.values
|
||||
for (nation in previousScreen.ruleset.nations.values
|
||||
.filter { it.isMajorCiv() }) {
|
||||
if (gameParameters.players.any { it.chosenCiv == nation.name })
|
||||
continue
|
||||
|
@ -267,5 +267,4 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
|||
}
|
||||
return nations
|
||||
}
|
||||
|
||||
}
|
88
desktop/src/com/unciv/app/desktop/ConsoleLauncher.kt
Normal file
88
desktop/src/com/unciv/app/desktop/ConsoleLauncher.kt
Normal 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"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue