Simulation: More Robust Stats (#12675)

* More robust Simulation Stats

* Some code cleanup

* Enum
This commit is contained in:
itanasi 2024-12-25 10:45:29 -08:00 committed by GitHub
parent c7fb6fb525
commit b948770c51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 70 additions and 21 deletions

View File

@ -29,7 +29,7 @@ class Simulation(
private var startTime: Long = 0
var steps = ArrayList<SimulationStep>()
var numWins = mutableMapOf<String, MutableInt>()
var sumStat = mutableMapOf<String, MutableMap<Int, MutableInt>>()
private var summaryStats = HashMap<String, HashMap<Int, HashMap<Stat, MutableInt>>>() // [civ][turn][stat]=value
private var winRateByVictory = HashMap<String, MutableMap<String, MutableInt>>()
private var winTurnByVictory = HashMap<String, MutableMap<String, MutableInt>>()
private var avgSpeed = 0f
@ -37,13 +37,22 @@ class Simulation(
private var totalTurns = 0
private var totalDuration: Duration = Duration.ZERO
private var stepCounter: Int = 0
enum class Stat {
SUM,
NUM
}
init{
for (civ in civilizations) {
this.numWins[civ] = MutableInt(0)
for (turn in statTurns)
this.sumStat.getOrPut(civ) { mutableMapOf() }[turn] = MutableInt(0)
for (turn in statTurns) {
this.summaryStats.getOrPut(civ) { hashMapOf() }.getOrPut(turn){hashMapOf()}[Stat.SUM] = MutableInt(0)
this.summaryStats.getOrPut(civ) { hashMapOf() }.getOrPut(turn){hashMapOf()}[Stat.NUM] = MutableInt(0)
}
val turn = -1 // end of game
this.summaryStats.getOrPut(civ) { hashMapOf() }.getOrPut(turn){hashMapOf()}[Stat.SUM] = MutableInt(0)
this.summaryStats.getOrPut(civ) { hashMapOf() }.getOrPut(turn){hashMapOf()}[Stat.NUM] = MutableInt(0)
winRateByVictory[civ] = mutableMapOf()
for (victory in UncivGame.Current.gameInfo!!.ruleset.victories.keys)
winRateByVictory[civ]!![victory] = MutableInt(0)
@ -63,26 +72,28 @@ class Simulation(
for (threadId in 1..threadsNumber) {
jobs.add(launch(CoroutineName("simulation-${threadId}")) {
repeat(simulationsPerThread) {
val step = SimulationStep(newGameInfo, statTurns)
val gameInfo = GameStarter.startNewGame(GameSetupInfo(newGameInfo))
gameInfo.simulateUntilWin = true
for (turn in statTurns) {
gameInfo.simulateMaxTurns = turn
gameInfo.nextTurn()
val step = SimulationStep(gameInfo)
step.update(gameInfo)
if (step.victoryType != null)
break
saveStat(gameInfo)
step.saveTurnStats(gameInfo)
}
// check if Victory
var step = SimulationStep(gameInfo)
step.update(gameInfo)
if (step.victoryType == null) {
gameInfo.simulateMaxTurns = maxTurns
gameInfo.nextTurn()
}
step = SimulationStep(gameInfo) // final game state
step.update(gameInfo) // final game state
if (step.victoryType != null) {
step.saveTurnStats(gameInfo)
step.winner = step.currentPlayer
println("${step.winner} won ${step.victoryType} victory on turn ${step.turns}")
}
@ -109,16 +120,6 @@ class Simulation(
println("Simulation step ($stepCounter/$maxSimulations)")
}
@Synchronized
fun saveStat(gameInfo: GameInfo) {
val turn = gameInfo.turns
for (civ in gameInfo.civilizations.filter { it.civName != Constants.spectator }) {
val popsum = civ.cities.sumOf { it.population.population }
//println("$civ $popsum")
sumStat[civ.civName]!![turn]!!.add(popsum)
}
}
@Synchronized
fun print(){
getStats()
@ -130,12 +131,27 @@ class Simulation(
numWins.values.forEach { it.value = 0 }
winRateByVictory.flatMap { it.value.values }.forEach { it.value = 0 }
winTurnByVictory.flatMap { it.value.values }.forEach { it.value = 0 }
summaryStats.flatMap { it.value.values }.forEach {
it.values.forEach { it.value = 0 }
}
steps.forEach {
if (it.winner != null) {
numWins[it.winner!!]!!.inc()
winRateByVictory[it.winner!!]!![it.victoryType]!!.inc()
winTurnByVictory[it.winner!!]!![it.victoryType]!!.add(it.turns)
}
for (civ in civilizations) {
for (turn in statTurns) {
if (it.turnStats[civ]!![turn]!!.value != -1) {
summaryStats[civ]!![turn]!![Stat.SUM]!!.add(it.turnStats[civ]!![turn]!!.value)
summaryStats[civ]!![turn]!![Stat.NUM]!!.inc()
//println("civ ${civ} @ ${turn} value ${it.turnStats[civ]!![turn]!!.value} avg ${summaryStats[civ]!![turn]!!["avg"]!!.value} numAvg ${summaryStats[civ]!![turn]!!["numAvg"]!!.value}")
}
}
val turn = -1 // end of game
summaryStats[civ]!![turn]!![Stat.SUM]!!.add(it.turnStats[civ]!![turn]!!.value)
summaryStats[civ]!![turn]!![Stat.NUM]!!.inc()
}
}
totalTurns = steps.sumOf { it.turns }
totalDuration = (System.currentTimeMillis() - startTime).milliseconds
@ -176,8 +192,13 @@ class Simulation(
outString += "$victory: $winsTurns "
}
outString += "avg turns\n"
for (turn in statTurns)
outString += "avgStat (@$turn): ${sumStat[civ]!![turn]!!.value.toFloat()/numSteps}\n"
for (turn in statTurns) {
val turnStats = summaryStats[civ]!![turn]!!
outString += "@$turn: popsum avg=${turnStats[Stat.SUM]!!.value.toFloat() / turnStats[Stat.NUM]!!.value.toFloat()} cnt=${turnStats[Stat.NUM]!!.value}\n"
}
val turn = -1 // end of match
val turnStats = summaryStats[civ]!![turn]!!
outString += "@END: popsum avg=${turnStats[Stat.SUM]!!.value.toFloat()/turnStats[Stat.NUM]!!.value.toFloat()} cnt=${turnStats[Stat.NUM]!!.value}\n"
}
outString += "\nAverage speed: %.1f turns/s \n".format(avgSpeed)
outString += "Average game duration: $avgDuration\n"

View File

@ -1,12 +1,40 @@
package com.unciv.logic.simulation
import com.unciv.Constants
import com.unciv.logic.GameInfo
class SimulationStep (gameInfo: GameInfo) {
class SimulationStep (gameInfo: GameInfo, statTurns: List<Int> = listOf()) {
val civilizations = gameInfo.civilizations.filter { it.civName != Constants.spectator }.map { it.civName }
var turns = gameInfo.turns
var victoryType = gameInfo.getCurrentPlayerCivilization().victoryManager.getVictoryTypeAchieved()
var winner: String? = null
val currentPlayer = gameInfo.currentPlayer
var currentPlayer = gameInfo.currentPlayer
val turnStats = mutableMapOf<String, MutableMap<Int, MutableInt>>() // [civ][turn][stat]
init {
for (civ in civilizations) {
for (turn in statTurns)
this.turnStats.getOrPut(civ) { mutableMapOf() }[turn] = MutableInt(-1)
this.turnStats.getOrPut(civ) { mutableMapOf() }[-1] = MutableInt(-1) // end of game
}
}
fun saveTurnStats(gameInfo: GameInfo) {
victoryType = gameInfo.getCurrentPlayerCivilization().victoryManager.getVictoryTypeAchieved()
val turn = if (victoryType != null) -1 else gameInfo.turns
for (civ in gameInfo.civilizations.filter { it.civName != Constants.spectator }) {
val popsum = civ.cities.sumOf { it.population.population }
//println("$civ $popsum")
turnStats[civ.civName]!![turn]!!.set(popsum)
}
}
fun update(gameInfo: GameInfo) {
turns = gameInfo.turns
victoryType = gameInfo.getCurrentPlayerCivilization().victoryManager.getVictoryTypeAchieved()
currentPlayer = gameInfo.currentPlayer
}
}