Package-level declarations

Persisted state and the pure reducers that update it: records, streaks, medals, unlocks, heatmaps and the LAN board.

Types

Link copied to clipboard

Bonus games unlocked by playing well.

Link copied to clipboard
@Serializable
data class BlocksStats(val wins: Int = 0, val losses: Int = 0, val streak: Int = 0, val bestStreak: Int = 0, val bestScore: Int = 0, val totalLines: Int = 0)
Link copied to clipboard
Link copied to clipboard
@Serializable
data class HeadToHead(val aId: String, val bId: String, val aName: String, val bName: String, val aWins: Int = 0, val bWins: Int = 0, val draws: Int = 0)

Head-to-head tally between two players; aId<bId so each pair has one entry.

Link copied to clipboard
@Serializable
data class HeatData(val games: Int = 0, val winCounts: List<Int> = emptyList(), val playCounts: List<Int> = emptyList())

Per-board-size tallies used to render the heatmap of where games are won and played.

Link copied to clipboard
@Serializable
data class HiveStats(val level: Int = 1, val points: Int = 0, val bestLevel: Int = 0, val wordsFound: Int = 0)
Link copied to clipboard
object LanBoard

LAN-only standings, rebuilt from match history so every member's streaks are tracked.

Link copied to clipboard
data class LanMember(val id: String, val name: String, val alias: String, val record: Record, val lastPlayed: Long, val isRemote: Boolean)

Someone who has played LAN matches on this device: a remote alias, or one of your profiles.

Link copied to clipboard

Ludo rewards: winning the game pays most; every game and every duel won pays a little.

Link copied to clipboard
@Serializable
data class LudoStats(val played: Int = 0, val wins: Int = 0, val duelsWon: Int = 0)
Link copied to clipboard
Link copied to clipboard
@Serializable
data class MatchRecord(val timestamp: Long, val mode: GameMode, val size: Int, val xName: String, val oName: String, val xColor: Long, val oColor: Long, val result: String, val board: String, val winCells: List<Int> = emptyList(), val difficulty: Difficulty? = null, val xId: String = "", val oId: String = "")
Link copied to clipboard
data class MatchResult(val mode: GameMode, val board: Board, val win: WinLine?, val x: PlayerRef, val o: PlayerRef, val humanMark: Mark? = null, val difficulty: Difficulty? = null, val timestamp: Long = System.currentTimeMillis())

Everything needed to record one finished round.

Link copied to clipboard
enum Medal : Enum<Medal>

Medals are handed out every Medals.GAP games of a streak. Win streaks climb five tiers (Diamond repeats past the top), losing streaks earn Grit badges, and snapping a losing streak of at least one gap earns a Comeback.

Link copied to clipboard
@Serializable
data class MedalAward(val medal: Medal, val ownerId: String, val ownerName: String, val streak: Int, val timestamp: Long)
Link copied to clipboard
Link copied to clipboard
object Medals
Link copied to clipboard
Link copied to clipboard
data class PlayerRef(val id: String, val name: String, val color: Long)
Link copied to clipboard
@Serializable
data class Profile(val id: String, val name: String, val color: Long)
Link copied to clipboard
@Serializable
data class Record(val wins: Int = 0, val losses: Int = 0, val draws: Int = 0, val streak: Int = 0, val bestStreak: Int = 0, val lossStreak: Int = 0, val worstLossStreak: Int = 0)
Link copied to clipboard
@Serializable
sealed interface SavedSession

A game in progress, saved when the app goes to the background so it can be resumed later.

Link copied to clipboard
class SessionStore(context: Context, scope: CoroutineScope)

Persists at most one SavedSession — the last unfinished game — in app-private storage. Written atomically, like the stats file, so a kill mid-save can't corrupt it.

Link copied to clipboard
data class Settings(val theme: ThemeMode = ThemeMode.SYSTEM, val markStyle: MarkStyle = MarkStyle.NEON, val xColor: Long = 4294917503, val oColor: Long = 4281590271, val strokeWidth: Float = 0.1f, val animSpeed: Float = 1.0f, val boardSize: Int = 3, val difficulty: Difficulty = Difficulty.MEDIUM, val sound: Boolean = true, val haptics: Boolean = true, val threatHeat: Boolean = true, val heatmapOverlay: Boolean = false, val soloProfileId: String? = null, val lanProfileId: String? = null, val lastXProfileId: String? = null, val lastOProfileId: String? = null, val lanAlias: String? = null)
Link copied to clipboard
class SettingsRepository(context: Context)
Link copied to clipboard
@Serializable
data class StatsData(val profiles: List<Profile> = emptyList(), val solo: Map<String, Record> = emptyMap(), val players: Map<String, Record> = emptyMap(), val playerNames: Map<String, String> = emptyMap(), val headToHead: Map<String, HeadToHead> = emptyMap(), val history: List<MatchRecord> = emptyList(), val heat: Map<String, HeatData> = emptyMap(), val soloOverall: Record = Record(), val medals: Map<String, Map<String, Int>> = emptyMap(), val awards: List<MedalAward> = emptyList(), val unlocked: List<ArcadeGame> = emptyList(), val demosUsed: List<ArcadeGame> = emptyList(), val blocks: BlocksStats = BlocksStats(), val hive: HiveStats = HiveStats(), val coins: Int = 1000, val gems: Int = 10, val ludo: LudoStats = LudoStats())
Link copied to clipboard

Pure state transitions for StatsData — kept separate from I/O so they are unit-testable.

Link copied to clipboard
class StatsRepository(context: Context, scope: CoroutineScope)

Scoreboard, profiles and match history, stored as one JSON file in app-private storage. Writes go to a temp file first and are renamed into place so a crash can't corrupt it.

Link copied to clipboard
Link copied to clipboard
object Unlocks