Added stats + calculation

dev_raz_wip
Laurent 7 years ago
parent cf70562735
commit 1ccd36edc2
  1. 118
      app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt
  2. 4
      app/src/main/java/net/pokeranalytics/android/calculus/Computable.kt
  3. 35
      app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt
  4. 2
      app/src/main/java/net/pokeranalytics/android/model/realm/Result.kt
  5. 73
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  6. 61
      app/src/main/java/net/pokeranalytics/android/model/realm/TimeFrame.kt
  7. 15
      app/src/main/java/net/pokeranalytics/android/model/realm/TimeFrameGroup.kt
  8. 43
      app/src/test/java/net/pokeranalytics/android/ExampleUnitTest.kt

@ -1,5 +1,6 @@
package net.pokeranalytics.android.calculus
import net.pokeranalytics.android.calculus.Stat.*
import net.pokeranalytics.android.model.realm.TimeFrameGroup
@ -25,6 +26,15 @@ class Calculator {
var evolutionValues: EvolutionValues = EvolutionValues.NONE
var displayedStats: List<Stat> = listOf()
fun shouldComputeStandardDeviation() : Boolean {
this.displayedStats.forEach { stat ->
when (stat) {
STANDARD_DEVIATION, STANDARD_DEVIATION_HOURLY, STANDARD_DEVIATION_BB_PER_100_HANDS -> return true
}
}
return false
}
// var aggregation: Aggregation? = null
}
@ -62,43 +72,113 @@ class Calculator {
fun compute(group: SessionGroup, options: Options) : ComputedResults {
val sessions: List<SessionInterface> = group.sessionGroup
val series: Set<TimeFrameGroup> = setOf() // @todo get unique list of serie
val sessionGroups: Set<TimeFrameGroup> = setOf() // @todo get unique list of serie
var results: ComputedResults = ComputedResults()
var sum: Double = 0.0
var duration: Double = 0.0
var totalHands: Double = 0.0
var bbSum: Double = 0.0
var bbSessionCount: Int = 0
var winningSessionCount: Int = 0
var totalBuyin: Double = 0.0
// @todo add all stats
// Compute for each session
sessions.forEach { item ->
sum += item.value
when (options.evolutionValues) {
Options.EvolutionValues.STANDARD -> {
results.addEvolutionValue(sum, Stat.NETRESULT)
}
Options.EvolutionValues.DATED -> {
results.addEvolutionValue(sum, duration, Stat.NETRESULT)
}
var index: Int = 0
sessions.forEach { s ->
index++;
bbSessionCount += s.bigBlindSessionCount
if (s.value >= 0) {
winningSessionCount++
}
totalBuyin += s.buyin
if (options.evolutionValues != Options.EvolutionValues.NONE) {
sum += s.value
totalHands += s.estimatedHands
bbSum += s.bbNetResult
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)
results.addEvolutionValue(Stat.returnOnInvestment(sum, totalBuyin), ROI)
}
}
// Compute for each serie
series.forEach { serie ->
duration += serie.duration
var duration: Double = 0.0
var hourlyRate: Double = 0.0; var hourlyRateBB: Double = 0.0
index = 0; sum = 0.0; totalHands = 0.0; bbSum = 0.0;
sessionGroups.forEach { group ->
index++
duration += group.duration
sum += group.netResult
totalHands += group.estimatedHands
bbSum += group.bbNetResult
hourlyRate = sum / duration * 3600.0
hourlyRateBB = bbSum / duration * 3600.0
if (options.evolutionValues != Options.EvolutionValues.NONE) {
results.addEvolutionValue(duration, Stat.DURATION)
results.addEvolutionValue(sum, duration, NETRESULT)
results.addEvolutionValue(sum / duration * 3600.0, duration, HOURLY_RATE)
results.addEvolutionValue(Stat.netBBPer100Hands(bbSum, totalHands), duration, NET_BB_PER_100_HANDS)
results.addEvolutionValue(hourlyRate, duration, HOURLY_RATE)
results.addEvolutionValue(index.toDouble(), duration, NUMBER_OF_GROUPS)
results.addEvolutionValue(group.duration, duration, DURATION)
results.addEvolutionValue(duration / index, duration, AVERAGE_DURATION)
results.addEvolutionValue(hourlyRateBB, duration, HOURLY_RATE_BB)
}
}
val average: Double = sum / sessions.size
// Create stats
results.addStats(setOf(
ComputedStat(Stat.NETRESULT, sum),
ComputedStat(Stat.AVERAGE,sum / sessions.size),
ComputedStat(Stat.DURATION, duration)
))
ComputedStat(NETRESULT, sum),
ComputedStat(HOURLY_RATE, hourlyRate),
ComputedStat(AVERAGE, average),
ComputedStat(DURATION, duration),
ComputedStat(NUMBER_OF_GROUPS, sessionGroups.size.toDouble()),
ComputedStat(NUMBER_OF_GAMES, sessions.size.toDouble()),
ComputedStat(AVERAGE_DURATION, (duration / 3600.0) / sessions.size),
ComputedStat(NET_BB_PER_100_HANDS, Stat.netBBPer100Hands(bbSum, totalHands)),
ComputedStat(HOURLY_RATE_BB, bbSum / duration * 3600.0),
ComputedStat(AVERAGE_NET_BB, bbSum / bbSessionCount),
ComputedStat(WIN_RATIO, (winningSessionCount / sessions.size).toDouble()),
ComputedStat(AVERAGE_BUYIN, totalBuyin / sessions.size),
ComputedStat(ROI, Stat.returnOnInvestment(sum, totalBuyin))
))
// Standard Deviation
if (options.shouldComputeStandardDeviation()) {
var stdSum: Double = 0.0
sessions.forEach { s ->
stdSum += Math.pow(s.value - average, 2.0)
}
val standardDeviation: Double = Math.sqrt(stdSum / sessions.size)
var hourlyStdSum: Double = 0.0
sessionGroups.forEach { sg ->
hourlyStdSum += Math.pow(sg.hourlyRate - hourlyRate, 2.0)
}
val hourlyStandardDeviation: Double = Math.sqrt(hourlyStdSum / sessionGroups.size)
results.addStats(setOf(
ComputedStat(STANDARD_DEVIATION, standardDeviation),
ComputedStat(Stat.STANDARD_DEVIATION_HOURLY, hourlyStandardDeviation)
))
}
return results
}

@ -10,6 +10,10 @@ interface Summable {
// An interface describing some class that can be computed
interface SessionInterface : Summable {
var serie: TimeFrameGroup
var estimatedHands: Double
var bbNetResult: Double
var bigBlindSessionCount: Int // 0 or 1
var buyin: Double
}
/**

@ -7,25 +7,48 @@ interface AnyStat {
enum class Stat : AnyStat {
NETRESULT,
HOURLYRATE,
DURATION,
HOURLY_RATE,
AVERAGE,
STANDARDDEVIATION,
HOURLYSTANDARDDEVIATION;
NUMBER_OF_GROUPS,
NUMBER_OF_GAMES,
DURATION,
AVERAGE_DURATION,
NET_BB_PER_100_HANDS,
HOURLY_RATE_BB,
AVERAGE_NET_BB,
WIN_RATIO,
AVERAGE_BUYIN,
ROI,
STANDARD_DEVIATION,
STANDARD_DEVIATION_HOURLY,
STANDARD_DEVIATION_BB_PER_100_HANDS;
fun label() : String = when (this) {
NETRESULT -> ""
HOURLYRATE -> ""
HOURLY_RATE -> ""
else -> throw IllegalArgumentException("Label not defined")
}
fun hasDistributionSorting() : Boolean {
when (this) {
STANDARDDEVIATION, HOURLYSTANDARDDEVIATION -> return true
STANDARD_DEVIATION, STANDARD_DEVIATION_HOURLY, STANDARD_DEVIATION_BB_PER_100_HANDS -> return true
else -> return false
}
}
companion object {
fun returnOnInvestment(netResult: Double, buyin: Double) : Double {
return netResult / buyin
}
fun netBBPer100Hands(netBB: Double, numberOfHands: Double) : Double {
return netBB / numberOfHands * 100
}
}
}
enum class CashSessionStat : AnyStat {

@ -36,7 +36,7 @@ open class Result : RealmObject() {
}
// The net (readonly)
var net: Double? = null
var net: Double = 0.0
// The transactions associated with the Result, impacting the result
var transactions: RealmList<Transaction> = RealmList()

@ -1,18 +1,13 @@
package net.pokeranalytics.android.model.realm
import io.realm.RealmList
import io.realm.Realm
import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.Sort
import io.realm.*
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.*
import net.pokeranalytics.android.ui.adapter.components.DynamicRowDelegate
import net.pokeranalytics.android.ui.adapter.components.DynamicRowInterface
import net.pokeranalytics.android.ui.adapter.components.SessionRow
import net.pokeranalytics.android.util.data.sessionDao
import java.util.*
import java.util.UUID.randomUUID
import kotlin.collections.ArrayList
@ -21,6 +16,9 @@ open class Session(comment: String = "") : RealmObject(), DynamicRowDelegate {
@PrimaryKey
var id = UUID.randomUUID().toString()
// The result of the main user
var result: Result? = null
// The time frame of the Session, i.e. the start & end date
var timeFrame: TimeFrame? = null
@ -30,30 +28,27 @@ open class Session(comment: String = "") : RealmObject(), DynamicRowDelegate {
// the date of creation of the app
var creationDate: Date = Date()
// The bankroll hosting the results
var bankroll: Bankroll? = null
// The limit type: NL, PL...
var limit: Int? = null
// The number of tables played at the same time
var numberOfTables: Int = 1
// The game played during the Session
var game: Game? = null
// The number of players at the table
var tableSize: Int? = null
// The game played during the Session
var game: Game? = null
// the location where the session is played
var location: Location? = null
// The bankroll hosting the results
var bankroll: Bankroll? = null
// The number of tables played at the same time
var numberOfTables: Int = 1
// The hands list associated with the Session
var hands: RealmList<HandHistory> = RealmList()
// the location where the session is played
var location: Location? = null
// The result of the main user
var result: Result? = null
// The list of opponents who participated to the session
var opponents: RealmList<Player> = RealmList()
@ -85,6 +80,46 @@ open class Session(comment: String = "") : RealmObject(), DynamicRowDelegate {
// The features of the tournament, like Knockout, Shootout, Turbo...
var tournamentFeatures: RealmList<TournamentFeature> = RealmList()
companion object {
fun newInstance() : Session {
var session: Session = Session()
session.result = Result()
session.timeFrame = TimeFrame()
return session
}
}
@Ignore
var estimatedHands: Double = 0.0
@Ignore
var bbNetResult: Double = 0.0
get() {
this.cgBigBlind?.let { bb ->
this.result?.let { result ->
return result.net / bb
}
}
return 0.0
}
@Ignore
var bigBlindSessionCount: Int = if (this.cgBigBlind != null) 1 else 0
// get() = if (this.cgBigBlind != null) 1 else 0
@Ignore
var buyin: Double = 0.0
get() {
this.result?.let {
it.buyin?.let {
return it
}
}
return 0.0
}
override fun adapterRows(): ArrayList<DynamicRowInterface> {
val rows = ArrayList<DynamicRowInterface>()

@ -1,24 +1,79 @@
package net.pokeranalytics.android.model.realm
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import io.realm.RealmResults
import io.realm.annotations.LinkingObjects
import java.util.*
open class TimeFrame : RealmObject() {
// A start date
var startDate: Date = Date()
set(value) {
field = value
this.computeDuration()
if (this.session != null) {
this.notifyDateChange()
}
}
// An end date
var endDate: Date? = null
set(value) {
field = value
this.computeDuration()
if (this.session != null) {
this.notifyDateChange()
}
}
// The break duration
var breakDuration: Double = 0.0
var breakDuration: Long = 0L
set(value) {
field = value
this.computeDuration()
}
// the total duration
var duration: Double = 0.0
var duration: Long = 0L
private set
// indicates a state of pause
var paused: Boolean = false
@LinkingObjects("timeFrame")
private val session: RealmResults<Session>? = null
@LinkingObjects("timeFrame")
private val group: RealmResults<TimeFrameGroup>? = null
private fun computeDuration() {
var endDate: Date
if (this.endDate != null) {
endDate = this.endDate!!
} else {
endDate = Date()
}
this.duration = endDate.time - startDate.time - this.breakDuration
}
private fun notifyDateChange() {
// val realm = Realm.getDefaultInstance()
//
// var query: RealmQuery<TimeFrame> = realm.where(TimeFrame::class.java)
// query.greaterThan("startDate", this.startDate.time)
//
// this.endDate?.let { endDate ->
// query.or()
// .greaterThan("startDate", endDate.time)
// .lessThan("endDate", endDate.time)
// }
//
//
// realm.close()
}
}

@ -3,8 +3,6 @@ package net.pokeranalytics.android.model.realm
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import java.util.*
open class TimeFrameGroup() : RealmObject() {
@ -13,10 +11,21 @@ open class TimeFrameGroup() : RealmObject() {
var timeFrame: TimeFrame? = null
// The list of Session played within the group, i.e. played within the same time frame
var timeFrames: RealmList<Session> = RealmList()
var sessions: RealmList<Session> = RealmList()
@Ignore // a duration shortcut
var duration: Double = 0.0
@Ignore // a netResult shortcut
var netResult: Double = 0.0
@Ignore // a duration shortcut
var hourlyRate: Double = 0.0
@Ignore
var estimatedHands: Double = 0.0
@Ignore
var bbNetResult: Double = 0.0
}

@ -1,17 +1,52 @@
package net.pokeranalytics.android
import net.pokeranalytics.android.calculus.*
import net.pokeranalytics.android.model.realm.TimeFrameGroup
import org.junit.Assert.fail
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
class Grade(someValue: Double) : SessionInterface {
// override var serie: Serie = Serie(TimeFrame())
override var value: Double = someValue
override var serie: TimeFrameGroup = TimeFrameGroup()
override var estimatedHands: Double = 0.0
override var bbNetResult: Double = 0.0
override var bigBlindSessionCount: Int = 0 // 0 or 1
override var buyin: Double = 0.0
}
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
fun testStats() {
val grades: List<Grade> = listOf(Grade(someValue = 10.0), Grade(someValue = 20.0))
val group = SessionGroup(name = "test", sessions = grades)
val results: ComputedResults = Calculator.compute(group, Calculator.Options())
val sum = results.computedStat(Stat.NETRESULT)
if (sum != null) {
assert(sum.value == 30.0)
} else {
fail("No Net result stat")
}
val average = results.computedStat(Stat.AVERAGE)
if (average != null) {
assert(average.value == 15.0)
} else {
fail("No AVERAGE stat")
}
}
}

Loading…
Cancel
Save