diff --git a/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt b/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt index 371f98b9..9119a8a3 100644 --- a/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt +++ b/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt @@ -90,7 +90,7 @@ class PokerAnalyticsApplication : Application() { val sessions = realm.where().findAll() if (sessions.size < 10) { - val numberOfSessions = 200 + val numberOfSessions = 100 Timber.d("*** Start creating ${numberOfSessions} fake sessions...") val s = Date() diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt b/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt index ce276d86..75b016c4 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt @@ -9,249 +9,284 @@ import timber.log.Timber */ class Calculator { - /** - * The options used for calculations or display - */ - class Options { - - /** - * The way the stats are going to be displayed - */ - enum class Display { - TABLE, - EVOLUTION, - COMPARISON, - MAP, - POLYNOMIAL - } - - /** - * The type of evolution values - */ - enum class EvolutionValues { - NONE, - STANDARD, - DATED - } - - var display: Display = Display.TABLE - var evolutionValues: EvolutionValues = EvolutionValues.NONE - var displayedStats: List = listOf() - - /** - * This function determines whether the standard deviation should be computed - */ - fun shouldComputeStandardDeviation() : Boolean { - this.displayedStats.forEach { stat -> - return when (stat) { - STANDARD_DEVIATION, STANDARD_DEVIATION_HOURLY, STANDARD_DEVIATION_BB_PER_100_HANDS -> true - else -> false - } - } - return true - } + /** + * The options used for calculations or display + */ + class Options { + + /** + * The way the stats are going to be displayed + */ + enum class Display { + TABLE, + EVOLUTION, + COMPARISON, + MAP, + POLYNOMIAL + } + + /** + * The type of evolution values + */ + enum class EvolutionValues { + NONE, + STANDARD, + DATED + } + + var display: Display = Display.TABLE + var evolutionValues: EvolutionValues = EvolutionValues.NONE + var displayedStats: List = listOf() + + /** + * This function determines whether the standard deviation should be computed + */ + fun shouldComputeStandardDeviation(): Boolean { + this.displayedStats.forEach { stat -> + return when (stat) { + STANDARD_DEVIATION, STANDARD_DEVIATION_HOURLY, STANDARD_DEVIATION_BB_PER_100_HANDS -> true + else -> false + } + } + return true + } // var aggregation: Aggregation? = null - } - - companion object { - - fun computePreAggregation(sets: List, options: Options): List { - Timber.d("sets = ${sets.size}") - return listOf() - } - - /** - * Computes all stats for list of Session sessionGroup - */ - fun computeGroups(groups: List, options: Options): List { - - val computedResults = mutableListOf() - groups.forEach { group -> - // Computes actual sessionGroup stats - val results: ComputedResults = Calculator.compute(group, options = options) - - // Computes the compared sessionGroup if existing - val comparedGroup = group.comparedSessions - if (comparedGroup != null) { - val comparedResults = Calculator.compute(comparedGroup, options = options) - group.comparedComputedResults = comparedResults - results.computeStatVariations(comparedResults) - } - - results.finalize(options) // later treatment, such as evolution values sorting - computedResults.add(results) - } - - return computedResults - } - - /** - * Computes stats for a SessionSet - */ - fun compute(sessionGroup: SessionGroup, options: Options) : ComputedResults { - - Timber.d(">>>> Start computing group ${sessionGroup.name}, ${sessionGroup.sessions.size} sessions") - - val sessions: List = sessionGroup.sessions - val sessionSets = sessionGroup.sessions.mapNotNull { it.sessionSet }.toHashSet() - - val results: ComputedResults = ComputedResults(sessionGroup) - - var sum: Double = 0.0 - var totalHands: Double = 0.0 - var bbSum: Double = 0.0 - var bbSessionCount: Int = 0 - var winningSessionCount: Int = 0 - var totalBuyin = 0.0 - - // Compute for each session - var index: Int = 0 - sessions.forEach { s -> - index++; - - sum += s.ratedNet - bbSum += s.bbNetResult - bbSessionCount += s.bigBlindSessionCount + } + + companion object { + + fun computePreAggregation(sets: List, options: Options): List { + Timber.d("sets = ${sets.size}") + return listOf() + } + + /** + * Computes all stats for list of Session sessionGroup + */ + fun computeGroups(groups: List, options: Options): List { + + val computedResults = mutableListOf() + groups.forEach { group -> + // Computes actual sessionGroup stats + val results: ComputedResults = Calculator.compute(group, options = options) + + // Computes the compared sessionGroup if existing + val comparedGroup = group.comparedSessions + if (comparedGroup != null) { + val comparedResults = Calculator.compute(comparedGroup, options = options) + group.comparedComputedResults = comparedResults + results.computeStatVariations(comparedResults) + } + + results.finalize(options) // later treatment, such as evolution values sorting + computedResults.add(results) + } + + return computedResults + } + +// fun compute(sessionGroup: SessionGroup, options: Options): ComputedResults { +// +// var sum: Double = 0.0 +// val sessions: List = sessionGroup.sessions +// val results: ComputedResults = ComputedResults(sessionGroup) +// +// sessions.forEach { s -> +// sum += s.ratedNet +// } +// +// results.addStats( +// setOf( +// ComputedStat(NETRESULT, sum) +// ) +// ) +// +// return results +// } + + + /** + * Computes stats for a SessionSet + */ + fun compute(sessionGroup: SessionGroup, options: Options): ComputedResults { + + Timber.d(">>>> Start computing group ${sessionGroup.name}, ${sessionGroup.sessions.size} sessions") + + val sessions: List = sessionGroup.sessions + val sessionSets = sessionGroup.sessions.mapNotNull { it.sessionSet }.toHashSet() + + val results: ComputedResults = ComputedResults(sessionGroup) + + var sum: Double = 0.0 + var totalHands: Double = 0.0 + var bbSum: Double = 0.0 + var bbSessionCount: Int = 0 + var winningSessionCount: Int = 0 // sessions.filter { it.value >= 0.0 }.size + var totalBuyin = 0.0 + + // Compute for each session + var index: Int = 0 + sessions.forEach { s -> + index++; + + sum += s.ratedNet + bbSum += s.bbNetResult + bbSessionCount += s.bigBlindSessionCount if (s.value >= 0) { winningSessionCount++ } - totalBuyin += s.buyin - totalHands += s.estimatedHands - - if (options.evolutionValues == Options.EvolutionValues.STANDARD) { - - results.addEvolutionValue(sum, NETRESULT) - results.addEvolutionValue(sum / index, AVERAGE) - results.addEvolutionValue(index.toDouble(), NUMBER_OF_GAMES) - results.addEvolutionValue(bbSum / bbSessionCount, AVERAGE_NET_BB) - results.addEvolutionValue((winningSessionCount / index).toDouble(), WIN_RATIO) - results.addEvolutionValue(totalBuyin / index, AVERAGE_BUYIN) - - val netBB100 = Stat.netBBPer100Hands(bbSum, totalHands) - if (netBB100 != null) { - results.addEvolutionValue(netBB100, NET_BB_PER_100_HANDS) - } - - val roi = Stat.returnOnInvestment(sum, totalBuyin) - if (roi != null) { - results.addEvolutionValue(roi, ROI) - } - - } - - } - - // Compute for each serie - var duration: Double = 0.0 - var hourlyRate: Double = 0.0 - var hourlyRateBB: Double = 0.0 - - var gIndex = 0; var gSum = 0.0; var gTotalHands = 0.0; var gBBSum = 0.0; - sessionSets.forEach { sessionSet -> - gIndex++ - duration += sessionSet.hourlyDuration - gSum += sessionSet.ratedNet - gTotalHands += sessionSet.estimatedHands - gBBSum += sessionSet.bbNetResult - - hourlyRate = gSum / duration - hourlyRateBB = gBBSum / duration - - if (options.evolutionValues == Options.EvolutionValues.DATED) { - results.addEvolutionValue(gSum, duration, NETRESULT) - results.addEvolutionValue(gSum / duration, duration, HOURLY_RATE) - results.addEvolutionValue(hourlyRate, duration, HOURLY_RATE) - results.addEvolutionValue(gIndex.toDouble(), duration, NUMBER_OF_SETS) - results.addEvolutionValue(sessionSet.netDuration.toDouble(), duration, DURATION) - results.addEvolutionValue(duration / gIndex, duration, AVERAGE_DURATION) - results.addEvolutionValue(hourlyRateBB, duration, HOURLY_RATE_BB) - } - - val netBB100 = Stat.netBBPer100Hands(gBBSum, gTotalHands) - if (netBB100 != null) { - results.addEvolutionValue(netBB100, duration, NET_BB_PER_100_HANDS) - } else { //@todo maybe not - results.addEvolutionValue(0.0, duration, NET_BB_PER_100_HANDS) - } - } - - var average = 0.0 - if (sessions.size > 0) { - average = sum / sessions.size.toDouble() - val winRatio = winningSessionCount.toDouble() / sessions.size.toDouble() - val avgBuyin = totalBuyin / sessions.size - - results.addStats(setOf( - ComputedStat(AVERAGE, average), - ComputedStat(WIN_RATIO, winRatio), - ComputedStat(AVERAGE_BUYIN, avgBuyin) - )) - } - - if (sessionSets.size > 0) { - val avgDuration = duration / sessionSets.size - results.addStats(setOf( - ComputedStat(HOURLY_RATE, hourlyRate), - ComputedStat(AVERAGE_DURATION, avgDuration) - )) - } - - // Create stats - results.addStats(setOf( - ComputedStat(NETRESULT, sum), - ComputedStat(DURATION, duration), - ComputedStat(NUMBER_OF_SETS, sessionSets.size.toDouble()), - ComputedStat(NUMBER_OF_GAMES, sessions.size.toDouble()), - ComputedStat(HOURLY_RATE_BB, bbSum / duration), - ComputedStat(AVERAGE_NET_BB, bbSum / bbSessionCount), - ComputedStat(HANDS_PLAYED, totalHands) - - )) - - val roi = Stat.returnOnInvestment(sum, totalBuyin) - val netBB100 = Stat.netBBPer100Hands(bbSum, totalHands) - - if (roi != null) { - results.addStats(setOf(ComputedStat(ROI, roi))) - } - if (netBB100 != null) { - results.addStats(setOf(ComputedStat(NET_BB_PER_100_HANDS, netBB100))) - } - - val bbPer100Hands = bbSum / totalHands * 100 - - // Standard Deviation - if (options.shouldComputeStandardDeviation()) { - - // Session - var stdSum: Double = 0.0 - var stdBBper100HandsSum: Double = 0.0 - sessions.forEach { session -> - stdSum += Math.pow(session.ratedNet - average, 2.0) - stdBBper100HandsSum += Math.pow(session.bbPer100Hands - bbPer100Hands, 2.0) - } - val standardDeviation: Double = Math.sqrt(stdSum / sessions.size) - val standardDeviationBBper100Hands: Double = Math.sqrt(stdBBper100HandsSum / sessions.size) - - // Session Set - var hourlyStdSum: Double = 0.0 - sessionSets.forEach { set -> - hourlyStdSum += Math.pow(set.hourlyRate - hourlyRate, 2.0) - } - val hourlyStandardDeviation: Double = Math.sqrt(hourlyStdSum / sessionSets.size) - - results.addStats(setOf( - ComputedStat(STANDARD_DEVIATION, standardDeviation), - ComputedStat(STANDARD_DEVIATION_HOURLY, hourlyStandardDeviation), - ComputedStat(STANDARD_DEVIATION_BB_PER_100_HANDS, standardDeviationBBper100Hands) - )) - } - - return results - } - - } + totalBuyin += s.buyin + totalHands += s.estimatedHands + + when (options.evolutionValues) { + Options.EvolutionValues.STANDARD -> { + + results.addEvolutionValue(sum, NETRESULT) + results.addEvolutionValue(sum / index, AVERAGE) + results.addEvolutionValue(index.toDouble(), NUMBER_OF_GAMES) + results.addEvolutionValue(bbSum / bbSessionCount, AVERAGE_NET_BB) + results.addEvolutionValue((winningSessionCount / index).toDouble(), WIN_RATIO) + results.addEvolutionValue(totalBuyin / index, AVERAGE_BUYIN) + + val netBB100 = Stat.netBBPer100Hands(bbSum, totalHands) + if (netBB100 != null) { + results.addEvolutionValue(netBB100, NET_BB_PER_100_HANDS) + } + + val roi = Stat.returnOnInvestment(sum, totalBuyin) + if (roi != null) { + results.addEvolutionValue(roi, ROI) + } + + } + } + } + + // Compute for each serie + var duration: Double = 0.0 + var hourlyRate: Double = 0.0 + var hourlyRateBB: Double = 0.0 + + var gIndex = 0; + var gSum = 0.0; + var gTotalHands = 0.0; + var gBBSum = 0.0; + sessionSets.forEach { sessionSet -> + gIndex++ + duration += sessionSet.hourlyDuration + gSum += sessionSet.ratedNet + gTotalHands += sessionSet.estimatedHands + gBBSum += sessionSet.bbNetResult + + hourlyRate = gSum / duration + hourlyRateBB = gBBSum / duration + + when (options.evolutionValues) { + Options.EvolutionValues.DATED -> { + results.addEvolutionValue(gSum, duration, NETRESULT) + results.addEvolutionValue(gSum / duration, duration, HOURLY_RATE) + results.addEvolutionValue(hourlyRate, duration, HOURLY_RATE) + results.addEvolutionValue(gIndex.toDouble(), duration, NUMBER_OF_SETS) + results.addEvolutionValue(sessionSet.netDuration.toDouble(), duration, DURATION) + results.addEvolutionValue(duration / gIndex, duration, AVERAGE_DURATION) + results.addEvolutionValue(hourlyRateBB, duration, HOURLY_RATE_BB) + + val netBB100 = Stat.netBBPer100Hands(gBBSum, gTotalHands) + if (netBB100 != null) { + results.addEvolutionValue(netBB100, duration, NET_BB_PER_100_HANDS) + } else { //@todo maybe not + results.addEvolutionValue(0.0, duration, NET_BB_PER_100_HANDS) + } + } + } + + } + + var average = 0.0 + if (sessions.size > 0) { + average = sum / sessions.size.toDouble() + val winRatio = winningSessionCount.toDouble() / sessions.size.toDouble() + val avgBuyin = totalBuyin / sessions.size + + results.addStats( + setOf( + ComputedStat(AVERAGE, average), + ComputedStat(WIN_RATIO, winRatio), + ComputedStat(AVERAGE_BUYIN, avgBuyin) + ) + ) + } + + if (sessionSets.size > 0) { + val avgDuration = duration / sessionSets.size + results.addStats( + setOf( + ComputedStat(HOURLY_RATE, hourlyRate), + ComputedStat(AVERAGE_DURATION, avgDuration) + ) + ) + } + + // Create stats + results.addStats( + setOf( + ComputedStat(NETRESULT, sum), + ComputedStat(DURATION, duration), + ComputedStat(NUMBER_OF_SETS, sessionSets.size.toDouble()), + ComputedStat(NUMBER_OF_GAMES, sessions.size.toDouble()), + ComputedStat(HOURLY_RATE_BB, bbSum / duration), + ComputedStat(AVERAGE_NET_BB, bbSum / bbSessionCount), + ComputedStat(HANDS_PLAYED, totalHands) + + ) + ) + + val roi = Stat.returnOnInvestment(sum, totalBuyin) + val netBB100 = Stat.netBBPer100Hands(bbSum, totalHands) + + if (roi != null) { + results.addStats(setOf(ComputedStat(ROI, roi))) + } + if (netBB100 != null) { + results.addStats(setOf(ComputedStat(NET_BB_PER_100_HANDS, netBB100))) + } + + val bbPer100Hands = bbSum / totalHands * 100 + + // Standard Deviation + if (options.shouldComputeStandardDeviation()) { + + // Session + var stdSum: Double = 0.0 + var stdBBper100HandsSum: Double = 0.0 + sessions.forEach { session -> + stdSum += Math.pow(session.ratedNet - average, 2.0) + stdBBper100HandsSum += Math.pow(session.bbPer100Hands - bbPer100Hands, 2.0) + } + val standardDeviation: Double = Math.sqrt(stdSum / sessions.size) + val standardDeviationBBper100Hands: Double = Math.sqrt(stdBBper100HandsSum / sessions.size) + + // Session Set + var hourlyStdSum: Double = 0.0 + sessionSets.forEach { set -> + hourlyStdSum += Math.pow(set.hourlyRate - hourlyRate, 2.0) + } + val hourlyStandardDeviation: Double = Math.sqrt(hourlyStdSum / sessionSets.size) + + results.addStats( + setOf( + ComputedStat(STANDARD_DEVIATION, standardDeviation), + ComputedStat(STANDARD_DEVIATION_HOURLY, hourlyStandardDeviation), + ComputedStat(STANDARD_DEVIATION_BB_PER_100_HANDS, standardDeviationBBper100Hands) + ) + ) + } + + return results + } + + } } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Result.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Result.kt index fba3dd3b..d16c0d5c 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Result.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Result.kt @@ -7,9 +7,6 @@ import io.realm.annotations.RealmClass @RealmClass open class Result : RealmObject() { - // the user associated to this session result - var player: Player? = null - // The buyin amount var buyin: Double? = null set(value) { diff --git a/app/src/test/java/net/pokeranalytics/android/RealmUnitTest.kt b/app/src/test/java/net/pokeranalytics/android/RealmUnitTest.kt index ffe2ffdd..91684013 100644 --- a/app/src/test/java/net/pokeranalytics/android/RealmUnitTest.kt +++ b/app/src/test/java/net/pokeranalytics/android/RealmUnitTest.kt @@ -1,7 +1,6 @@ package net.pokeranalytics.android import io.realm.Realm -import io.realm.RealmConfiguration import org.junit.After import org.junit.Before @@ -11,15 +10,15 @@ open class RealmUnitTest { @Before fun setup() { - val testConfig = RealmConfiguration.Builder().inMemory().name("test-realm").build() - Realm.setDefaultConfiguration(testConfig) - mockRealm = Realm.getDefaultInstance() +// val testConfig = RealmConfiguration.Builder().inMemory().name("test-realm").build() +// Realm.setDefaultConfiguration(testConfig) +// mockRealm = Realm.getDefaultInstance() } @After @Throws(Exception::class) public fun tearDown() { - mockRealm.close() +// mockRealm.close() } } \ No newline at end of file