diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5664823..820ecc2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -32,6 +32,11 @@ + + () + private val baseGame = "NES Tetris" + + val sampleCount: Int + get() = samples.size + + fun addSample(sample: GameScoreSample) { + samples.add(sample) + } + + fun addSamples(newSamples: List) { + samples.addAll(newSamples) + } + + fun clearSamples() { + samples.clear() + } + + fun analyzeScoringCurves(): Map { + val groupedSamples = samples.groupBy { it.game } + val conversionFactors = mutableMapOf() + + groupedSamples.forEach { (game, scores) -> + if (game != baseGame) { + val lowScores = scores.filter { it.score < 100000 } + val midScores = scores.filter { it.score in 100000..500000 } + val highScores = scores.filter { it.score > 500000 } + + val lowFactor = calculateAverageFactor(lowScores, baseGame) + val midFactor = calculateAverageFactor(midScores, baseGame) + val highFactor = calculateAverageFactor(highScores, baseGame) + + conversionFactors[game] = RangeScalingFactor(lowFactor, midFactor, highFactor) + } + } + + return conversionFactors + } + + private fun calculateAverageFactor(samples: List, baseGame: String): Double { + if (samples.isEmpty()) return 1.0 + + val baseGameSamples = this.samples.filter { it.game == baseGame } + if (baseGameSamples.isEmpty()) return 1.0 + + // Find matching base game samples by skill level + val factors = samples.map { sample -> + val matchingBaseSamples = baseGameSamples.filter { + it.skillLevel == sample.skillLevel && + it.level == sample.level + } + + if (matchingBaseSamples.isNotEmpty()) { + matchingBaseSamples.map { it.score.toDouble() / sample.score } + } else { + // If no exact match, find closest level + val closestBaseSample = baseGameSamples.minByOrNull { + kotlin.math.abs(it.level - sample.level) + } + if (closestBaseSample != null) { + listOf(closestBaseSample.score.toDouble() / sample.score) + } else { + emptyList() + } + } + }.flatten() + + return if (factors.isNotEmpty()) { + factors.average() + } else { + 1.0 + } + } + + fun generateScalingFactorCode(): String { + val factors = analyzeScoringCurves() + val code = StringBuilder() + + code.appendLine("val FACTORS = mapOf(") + + // Add base game entries + val games = samples.map { it.game }.distinct() + games.forEach { game -> + code.appendLine(" \"$game\" to mapOf(") + + games.filter { it != game }.forEach { otherGame -> + val factor = factors[otherGame] ?: RangeScalingFactor(1.0, 1.0, 1.0) + code.appendLine(" \"$otherGame\" to RangeScalingFactor(${factor.low}, ${factor.mid}, ${factor.high}),") + } + + code.appendLine(" ),") + } + + code.appendLine(")") + return code.toString() + } + + fun validateConversion(fromGame: String, toGame: String, score: Int): Double { + val factor = analyzeScoringCurves()[toGame] ?: return 1.0 + return when { + score < 100000 -> factor.low + score < 500000 -> factor.mid + else -> factor.high + } + } + + fun printAnalysisReport() { + println("=== Scaling Factor Analysis Report ===") + println("Base Game: $baseGame") + println("Total Samples: ${samples.size}") + println("\nSamples per Game:") + samples.groupBy { it.game }.forEach { (game, samples) -> + println("$game: ${samples.size} samples") + println(" Skill Levels: ${samples.map { it.skillLevel }.distinct()}") + println(" Level Range: ${samples.minOf { it.level }} - ${samples.maxOf { it.level }}") + println(" Score Range: ${samples.minOf { it.score }} - ${samples.maxOf { it.score }}") + } + + println("\nCalculated Scaling Factors:") + analyzeScoringCurves().forEach { (game, factor) -> + println("$game:") + println(" Low (<100k): ${factor.low}") + println(" Mid (100k-500k): ${factor.mid}") + println(" High (>500k): ${factor.high}") + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_scaling_factor_test.xml b/app/src/main/res/layout/activity_scaling_factor_test.xml new file mode 100644 index 0000000..517160b --- /dev/null +++ b/app/src/main/res/layout/activity_scaling_factor_test.xml @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file