mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-28 06:16:37 -04:00
Silently log ranking stats for each major civ every turn (#8964)
* Record each stat each round for each civilization. * Implement custom serialization and encapsulate logic in separate CivRankingHistory.kt * Address comments * Address comments and add RankingTypeTest.kt
This commit is contained in:
parent
4ba6574419
commit
11be6e2804
@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx
|
|||||||
import com.badlogic.gdx.files.FileHandle
|
import com.badlogic.gdx.files.FileHandle
|
||||||
import com.badlogic.gdx.utils.Json
|
import com.badlogic.gdx.utils.Json
|
||||||
import com.badlogic.gdx.utils.SerializationException
|
import com.badlogic.gdx.utils.SerializationException
|
||||||
|
import com.unciv.logic.civilization.CivRankingHistory
|
||||||
import com.unciv.logic.map.tile.TileHistory
|
import com.unciv.logic.map.tile.TileHistory
|
||||||
import com.unciv.ui.components.KeyCharAndCode
|
import com.unciv.ui.components.KeyCharAndCode
|
||||||
import com.unciv.ui.components.KeyboardBindings
|
import com.unciv.ui.components.KeyboardBindings
|
||||||
@ -27,6 +28,7 @@ fun json() = Json().apply {
|
|||||||
setSerializer(KeyCharAndCode::class.java, KeyCharAndCode.Serializer())
|
setSerializer(KeyCharAndCode::class.java, KeyCharAndCode.Serializer())
|
||||||
setSerializer(KeyboardBindings::class.java, KeyboardBindings.Serializer())
|
setSerializer(KeyboardBindings::class.java, KeyboardBindings.Serializer())
|
||||||
setSerializer(TileHistory::class.java, TileHistory.Serializer())
|
setSerializer(TileHistory::class.java, TileHistory.Serializer())
|
||||||
|
setSerializer(CivRankingHistory::class.java, CivRankingHistory.Serializer())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
57
core/src/com/unciv/logic/civilization/CivRankingHistory.kt
Normal file
57
core/src/com/unciv/logic/civilization/CivRankingHistory.kt
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package com.unciv.logic.civilization
|
||||||
|
|
||||||
|
import com.badlogic.gdx.utils.Json
|
||||||
|
import com.badlogic.gdx.utils.JsonValue
|
||||||
|
import com.unciv.logic.IsPartOfGameInfoSerialization
|
||||||
|
import com.unciv.ui.screens.victoryscreen.RankingType
|
||||||
|
|
||||||
|
/** Records for each turn (key of outer map) what the score (value of inner map) was for each RankingType. */
|
||||||
|
open class CivRankingHistory : HashMap<Int, Map<RankingType, Int>>(),
|
||||||
|
IsPartOfGameInfoSerialization {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a shallow copy of this [CivRankingHistory] instance.
|
||||||
|
* The inner [Map] instances are not cloned, only their references are copied.
|
||||||
|
*/
|
||||||
|
override fun clone(): CivRankingHistory {
|
||||||
|
val toReturn = CivRankingHistory()
|
||||||
|
toReturn.putAll(this)
|
||||||
|
return toReturn
|
||||||
|
}
|
||||||
|
|
||||||
|
fun recordRankingStats(civilization: Civilization) {
|
||||||
|
this[civilization.gameInfo.turns] =
|
||||||
|
RankingType.values().associateWith { civilization.getStatForRanking(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Custom Json formatter for a [CivRankingHistory].
|
||||||
|
* Output looks like this: `statsHistory:{0:{S:50,G:120,...},1:{S:55,G:80,...}}`
|
||||||
|
*/
|
||||||
|
class Serializer : Json.Serializer<CivRankingHistory> {
|
||||||
|
override fun write(json: Json, `object`: CivRankingHistory, knownType: Class<*>?) {
|
||||||
|
json.writeObjectStart()
|
||||||
|
for ((turn, rankings) in `object`) {
|
||||||
|
json.writeObjectStart(turn.toString())
|
||||||
|
for ((rankingType, score) in rankings) {
|
||||||
|
json.writeValue(rankingType.idForSerialization, score)
|
||||||
|
}
|
||||||
|
json.writeObjectEnd()
|
||||||
|
}
|
||||||
|
json.writeObjectEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(json: Json, jsonData: JsonValue, type: Class<*>?) =
|
||||||
|
CivRankingHistory().apply {
|
||||||
|
for (entry in jsonData) {
|
||||||
|
val turn = entry.name.toInt()
|
||||||
|
val rankings = mutableMapOf<RankingType, Int>()
|
||||||
|
for (rankingEntry in entry) {
|
||||||
|
val rankingType = RankingType.fromIdForSerialization(rankingEntry.name)
|
||||||
|
?: continue // Silently drop unknown ranking types.
|
||||||
|
rankings[rankingType] = rankingEntry.asInt()
|
||||||
|
}
|
||||||
|
this[turn] = rankings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -237,6 +237,8 @@ class Civilization : IsPartOfGameInfoSerialization {
|
|||||||
@Transient
|
@Transient
|
||||||
var hasLongCountDisplayUnique = false
|
var hasLongCountDisplayUnique = false
|
||||||
|
|
||||||
|
var statsHistory = CivRankingHistory()
|
||||||
|
|
||||||
constructor()
|
constructor()
|
||||||
|
|
||||||
constructor(civName: String) {
|
constructor(civName: String) {
|
||||||
@ -285,6 +287,7 @@ class Civilization : IsPartOfGameInfoSerialization {
|
|||||||
toReturn.totalFaithForContests = totalFaithForContests
|
toReturn.totalFaithForContests = totalFaithForContests
|
||||||
toReturn.attacksSinceTurnStart = attacksSinceTurnStart.copy()
|
toReturn.attacksSinceTurnStart = attacksSinceTurnStart.copy()
|
||||||
toReturn.hasMovedAutomatedUnits = hasMovedAutomatedUnits
|
toReturn.hasMovedAutomatedUnits = hasMovedAutomatedUnits
|
||||||
|
toReturn.statsHistory = statsHistory.clone()
|
||||||
return toReturn
|
return toReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,10 @@ class TurnManager(val civInfo: Civilization) {
|
|||||||
|
|
||||||
|
|
||||||
fun startTurn() {
|
fun startTurn() {
|
||||||
|
if (civInfo.isMajorCiv() && civInfo.isAlive()) {
|
||||||
|
civInfo.statsHistory.recordRankingStats(civInfo)
|
||||||
|
}
|
||||||
|
|
||||||
civInfo.civConstructions.startTurn()
|
civInfo.civConstructions.startTurn()
|
||||||
civInfo.attacksSinceTurnStart.clear()
|
civInfo.attacksSinceTurnStart.clear()
|
||||||
civInfo.updateStatsForNextTurn() // for things that change when turn passes e.g. golden age, city state influence
|
civInfo.updateStatsForNextTurn() // for things that change when turn passes e.g. golden age, city state influence
|
||||||
|
@ -4,16 +4,24 @@ import com.badlogic.gdx.graphics.Color
|
|||||||
import com.badlogic.gdx.scenes.scene2d.ui.Image
|
import com.badlogic.gdx.scenes.scene2d.ui.Image
|
||||||
import com.unciv.ui.images.ImageGetter
|
import com.unciv.ui.images.ImageGetter
|
||||||
|
|
||||||
enum class RankingType(val getImage: ()->Image?) {
|
enum class RankingType(val getImage: () -> Image?, val idForSerialization: String) {
|
||||||
// production, gold, happiness, and culture already have icons added when the line is `tr()`anslated
|
// production, gold, happiness, and culture already have icons added when the line is `tr()`anslated
|
||||||
Score({ ImageGetter.getImage("CityStateIcons/Cultured").apply { color = Color.FIREBRICK } }),
|
Score(
|
||||||
Population({ ImageGetter.getStatIcon("Population") }),
|
{ ImageGetter.getImage("CityStateIcons/Cultured").apply { color = Color.FIREBRICK } },
|
||||||
Crop_Yield({ ImageGetter.getStatIcon("Food") }),
|
"S"
|
||||||
Production({ null }),
|
),
|
||||||
Gold({ null }),
|
Population({ ImageGetter.getStatIcon("Population") }, "N"),
|
||||||
Territory({ ImageGetter.getImage("OtherIcons/Hexagon") }),
|
Crop_Yield({ ImageGetter.getStatIcon("Food") }, "C"),
|
||||||
Force({ ImageGetter.getImage("OtherIcons/Shield") }),
|
Production({ null }, "P"),
|
||||||
Happiness({ null }),
|
Gold({ null }, "G"),
|
||||||
Technologies({ ImageGetter.getStatIcon("Science") }),
|
Territory({ ImageGetter.getImage("OtherIcons/Hexagon") }, "T"),
|
||||||
Culture({ null })
|
Force({ ImageGetter.getImage("OtherIcons/Shield") }, "F"),
|
||||||
|
Happiness({ null }, "H"),
|
||||||
|
Technologies({ ImageGetter.getStatIcon("Science") }, "W"),
|
||||||
|
Culture({ null }, "A");
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromIdForSerialization(s: String): RankingType? =
|
||||||
|
values().firstOrNull { it.idForSerialization == s }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.unciv.ui.screens.victoryscreen
|
||||||
|
|
||||||
|
import com.unciv.testing.GdxTestRunner
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(GdxTestRunner::class)
|
||||||
|
class RankingTypeTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun checkIdForSerializationUniqueness() {
|
||||||
|
val uniqueIds = HashSet<String>()
|
||||||
|
for (rankingType in RankingType.values()) {
|
||||||
|
val id = rankingType.idForSerialization
|
||||||
|
Assert.assertTrue(
|
||||||
|
"Id $id for RankingType $rankingType is not unique",
|
||||||
|
uniqueIds.add(id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user