Merge branch 'master' of gitlab.com:stax-river/poker-analytics

dev_raz_wip
Razmig Sarkissian 7 years ago
commit 185890e669
  1. 9
      app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt
  2. 71
      app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt
  3. 24
      app/src/main/java/net/pokeranalytics/android/calculus/Computable.kt
  4. 3
      app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt
  5. 4
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  6. 14
      app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt
  7. 66
      app/src/main/java/net/pokeranalytics/android/model/realm/TimeFrame.kt
  8. 10
      app/src/test/java/net/pokeranalytics/android/ExampleUnitTest.kt

@ -9,6 +9,8 @@ import timber.log.Timber
class PokerAnalyticsApplication: Application() { class PokerAnalyticsApplication: Application() {
// var timeFrames: RealmResults<Session>? = null
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -20,6 +22,13 @@ class PokerAnalyticsApplication: Application() {
.build() .build()
Realm.setDefaultConfiguration(realmConfiguration) Realm.setDefaultConfiguration(realmConfiguration)
// val realm: Realm = Realm.getDefaultInstance()
// // Add observer on session time frame changes
// this.timeFrames = realm.where(Session::class.java).findAllAsync()
// this.timeFrames?.addChangeListener { t, changeSet -> // @todo check if main thread has running Looper, cf Realm doc
// changeSet.deletions
// }
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
// Logs // Logs
Timber.plant(PokerAnalyticsLogs()) Timber.plant(PokerAnalyticsLogs())

@ -1,7 +1,7 @@
package net.pokeranalytics.android.calculus package net.pokeranalytics.android.calculus
import net.pokeranalytics.android.calculus.Stat.* import net.pokeranalytics.android.calculus.Stat.*
import net.pokeranalytics.android.model.realm.SessionGroup import net.pokeranalytics.android.model.realm.SessionSet
class Calculator { class Calculator {
@ -40,20 +40,20 @@ class Calculator {
companion object { companion object {
fun computePreAggregation(groups: List<SessionGroup>, options: Options): List<ComputedGroup> { fun computePreAggregation(sets: List<SessionSet>, options: Options): List<ComputedGroup> {
return listOf() return listOf()
} }
// Computes all stats for list of Session group // Computes all stats for list of Session sessionGroup
fun computeGroups(groups: List<ComputableSessions>, options: Options): List<ComputedGroup> { fun computeGroups(groups: List<SessionGroup>, options: Options): List<ComputedGroup> {
var computedGroups: MutableList<ComputedGroup> = mutableListOf() var computedGroups: MutableList<ComputedGroup> = mutableListOf()
groups.forEach { group -> groups.forEach { group ->
// Computes actual group stats // Computes actual sessionGroup stats
val results: ComputedResults = Calculator.compute(group, options = options) val results: ComputedResults = Calculator.compute(group, options = options)
// Computes the compared group if existing // Computes the compared sessionGroup if existing
val comparedGroup = group.comparedGroup val comparedGroup = group.comparedSessions
if (comparedGroup != null) { if (comparedGroup != null) {
val comparedResults = Calculator.compute(comparedGroup, options = options) val comparedResults = Calculator.compute(comparedGroup, options = options)
group.comparedComputedGroup = ComputedGroup(comparedGroup, comparedResults) group.comparedComputedGroup = ComputedGroup(comparedGroup, comparedResults)
@ -68,11 +68,11 @@ class Calculator {
return computedGroups return computedGroups
} }
// Computes stats for a SessionGroup // Computes stats for a SessionSet
fun compute(group: ComputableSessions, options: Options) : ComputedResults { fun compute(sessionGroup: SessionGroup, options: Options) : ComputedResults {
val sessions: List<SessionInterface> = group.sessionGroup val sessions: List<SessionInterface> = sessionGroup.sessions
val sessionGroups: Set<SessionGroup> = setOf() // @todo get unique list of serie val sessionSets: Set<SessionSet> = setOf() // @todo get unique list of serie
var results: ComputedResults = ComputedResults() var results: ComputedResults = ComputedResults()
var sum: Double = 0.0 var sum: Double = 0.0
@ -82,28 +82,28 @@ class Calculator {
var winningSessionCount: Int = 0 var winningSessionCount: Int = 0
var totalBuyin: Double = 0.0 var totalBuyin: Double = 0.0
// @todo add all stats
// Compute for each session // Compute for each session
var index: Int = 0 var index: Int = 0
sessions.forEach { s -> sessions.forEach { s ->
index++; index++;
sum += s.value
bbSum += s.bbNetResult
bbSessionCount += s.bigBlindSessionCount bbSessionCount += s.bigBlindSessionCount
if (s.value >= 0) { if (s.value >= 0) {
winningSessionCount++ winningSessionCount++
} }
totalBuyin += s.buyin totalBuyin += s.buyin
totalHands += s.estimatedHands
if (options.evolutionValues != Options.EvolutionValues.NONE) { if (options.evolutionValues == Options.EvolutionValues.STANDARD) {
sum += s.value
totalHands += s.estimatedHands
bbSum += s.bbNetResult
results.addEvolutionValue(sum, NETRESULT)
results.addEvolutionValue(sum / index, AVERAGE) results.addEvolutionValue(sum / index, AVERAGE)
results.addEvolutionValue(index.toDouble(), NUMBER_OF_GAMES) results.addEvolutionValue(index.toDouble(), NUMBER_OF_GAMES)
results.addEvolutionValue(bbSum / bbSessionCount, AVERAGE_NET_BB) results.addEvolutionValue(bbSum / bbSessionCount, AVERAGE_NET_BB)
results.addEvolutionValue(Stat.netBBPer100Hands(bbSum, totalHands), NET_BB_PER_100_HANDS)
results.addEvolutionValue((winningSessionCount / index).toDouble(), WIN_RATIO) results.addEvolutionValue((winningSessionCount / index).toDouble(), WIN_RATIO)
results.addEvolutionValue(totalBuyin / index, AVERAGE_BUYIN) results.addEvolutionValue(totalBuyin / index, AVERAGE_BUYIN)
results.addEvolutionValue(Stat.returnOnInvestment(sum, totalBuyin), ROI) results.addEvolutionValue(Stat.returnOnInvestment(sum, totalBuyin), ROI)
@ -115,25 +115,25 @@ class Calculator {
var duration: Double = 0.0 var duration: Double = 0.0
var hourlyRate: Double = 0.0; var hourlyRateBB: Double = 0.0 var hourlyRate: Double = 0.0; var hourlyRateBB: Double = 0.0
index = 0; sum = 0.0; totalHands = 0.0; bbSum = 0.0; var gIndex = 0; var gSum = 0.0; var gTotalHands = 0.0; var gBBSum = 0.0;
sessionGroups.forEach { group -> sessionSets.forEach { group ->
index++ gIndex++
duration += group.duration duration += group.duration
sum += group.netResult gSum += group.netResult
totalHands += group.estimatedHands gTotalHands += group.estimatedHands
bbSum += group.bbNetResult gBBSum += group.bbNetResult
hourlyRate = sum / duration * 3600.0 hourlyRate = gSum / duration * 3600.0
hourlyRateBB = bbSum / duration * 3600.0 hourlyRateBB = gBBSum / duration * 3600.0
if (options.evolutionValues != Options.EvolutionValues.NONE) { if (options.evolutionValues == Options.EvolutionValues.DATED) {
results.addEvolutionValue(sum, duration, NETRESULT) results.addEvolutionValue(gSum, duration, NETRESULT)
results.addEvolutionValue(sum / duration * 3600.0, duration, HOURLY_RATE) results.addEvolutionValue(gSum / duration * 3600.0, duration, HOURLY_RATE)
results.addEvolutionValue(Stat.netBBPer100Hands(bbSum, totalHands), duration, NET_BB_PER_100_HANDS) results.addEvolutionValue(Stat.netBBPer100Hands(gBBSum, gTotalHands), duration, NET_BB_PER_100_HANDS)
results.addEvolutionValue(hourlyRate, duration, HOURLY_RATE) results.addEvolutionValue(hourlyRate, duration, HOURLY_RATE)
results.addEvolutionValue(index.toDouble(), duration, NUMBER_OF_GROUPS) results.addEvolutionValue(gIndex.toDouble(), duration, NUMBER_OF_GROUPS)
results.addEvolutionValue(group.duration, duration, DURATION) results.addEvolutionValue(group.duration, duration, DURATION)
results.addEvolutionValue(duration / index, duration, AVERAGE_DURATION) results.addEvolutionValue(duration / gIndex, duration, AVERAGE_DURATION)
results.addEvolutionValue(hourlyRateBB, duration, HOURLY_RATE_BB) results.addEvolutionValue(hourlyRateBB, duration, HOURLY_RATE_BB)
} }
} }
@ -146,7 +146,7 @@ class Calculator {
ComputedStat(HOURLY_RATE, hourlyRate), ComputedStat(HOURLY_RATE, hourlyRate),
ComputedStat(AVERAGE, average), ComputedStat(AVERAGE, average),
ComputedStat(DURATION, duration), ComputedStat(DURATION, duration),
ComputedStat(NUMBER_OF_GROUPS, sessionGroups.size.toDouble()), ComputedStat(NUMBER_OF_GROUPS, sessionSets.size.toDouble()),
ComputedStat(NUMBER_OF_GAMES, sessions.size.toDouble()), ComputedStat(NUMBER_OF_GAMES, sessions.size.toDouble()),
ComputedStat(AVERAGE_DURATION, (duration / 3600.0) / sessions.size), ComputedStat(AVERAGE_DURATION, (duration / 3600.0) / sessions.size),
ComputedStat(NET_BB_PER_100_HANDS, Stat.netBBPer100Hands(bbSum, totalHands)), ComputedStat(NET_BB_PER_100_HANDS, Stat.netBBPer100Hands(bbSum, totalHands)),
@ -154,7 +154,8 @@ class Calculator {
ComputedStat(AVERAGE_NET_BB, bbSum / bbSessionCount), ComputedStat(AVERAGE_NET_BB, bbSum / bbSessionCount),
ComputedStat(WIN_RATIO, (winningSessionCount / sessions.size).toDouble()), ComputedStat(WIN_RATIO, (winningSessionCount / sessions.size).toDouble()),
ComputedStat(AVERAGE_BUYIN, totalBuyin / sessions.size), ComputedStat(AVERAGE_BUYIN, totalBuyin / sessions.size),
ComputedStat(ROI, Stat.returnOnInvestment(sum, totalBuyin)) ComputedStat(ROI, Stat.returnOnInvestment(sum, totalBuyin)),
ComputedStat(HANDS_PLAYED, totalHands)
)) ))
@ -169,10 +170,10 @@ class Calculator {
val standardDeviation: Double = Math.sqrt(stdSum / sessions.size) val standardDeviation: Double = Math.sqrt(stdSum / sessions.size)
var hourlyStdSum: Double = 0.0 var hourlyStdSum: Double = 0.0
sessionGroups.forEach { sg -> sessionSets.forEach { sg ->
hourlyStdSum += Math.pow(sg.hourlyRate - hourlyRate, 2.0) hourlyStdSum += Math.pow(sg.hourlyRate - hourlyRate, 2.0)
} }
val hourlyStandardDeviation: Double = Math.sqrt(hourlyStdSum / sessionGroups.size) val hourlyStandardDeviation: Double = Math.sqrt(hourlyStdSum / sessionSets.size)
results.addStats(setOf( results.addStats(setOf(
ComputedStat(STANDARD_DEVIATION, standardDeviation), ComputedStat(STANDARD_DEVIATION, standardDeviation),

@ -1,6 +1,6 @@
package net.pokeranalytics.android.calculus package net.pokeranalytics.android.calculus
import net.pokeranalytics.android.model.realm.SessionGroup import net.pokeranalytics.android.model.realm.SessionSet
/** /**
* An interface to describe objects that can be summed * An interface to describe objects that can be summed
@ -13,7 +13,7 @@ interface Summable {
* An interface describing some class that can be computed * An interface describing some class that can be computed
*/ */
interface SessionInterface : Summable { interface SessionInterface : Summable {
var serie: SessionGroup var serie: SessionSet
var estimatedHands: Double var estimatedHands: Double
var bbNetResult: Double var bbNetResult: Double
var bigBlindSessionCount: Int // 0 or 1 var bigBlindSessionCount: Int // 0 or 1
@ -21,26 +21,26 @@ interface SessionInterface : Summable {
} }
/** /**
* A group of computable items identified by a name * A sessionGroup of computable items identified by a name
*/ */
class ComputableSessions(name: String, sessions: List<SessionInterface>) { class SessionGroup(name: String, sessions: List<SessionInterface>) {
var name: String = name var name: String = name
var sessionGroup: List<SessionInterface> = sessions var sessions: List<SessionInterface> = sessions
// A subgroup used to compute stat variation // A subgroup used to compute stat variation
var comparedGroup: ComputableSessions? = null var comparedSessions: SessionGroup? = null
// The computed stats of the comparable group // The computed stats of the comparable sessionGroup
var comparedComputedGroup: ComputedGroup? = null var comparedComputedGroup: ComputedGroup? = null
} }
class ComputedGroup(group: ComputableSessions, computedResults: ComputedResults) { class ComputedGroup(sessionGroup: SessionGroup, computedResults: ComputedResults) {
// A computable group // A computable sessionGroup
var group: ComputableSessions = group var sessionGroup: SessionGroup = sessionGroup
// The computed stats of the group // The computed stats of the sessionGroup
var computedResults: ComputedResults = computedResults var computedResults: ComputedResults = computedResults
fun statValue(stat: Stat) : Double? { fun statValue(stat: Stat) : Double? {
@ -51,7 +51,7 @@ class ComputedGroup(group: ComputableSessions, computedResults: ComputedResults)
class ComputedResults() { class ComputedResults() {
// The computed stats of the group // The computed stats of the sessionGroup
private var _computedStats: MutableMap<Stat, ComputedStat> = mutableMapOf() private var _computedStats: MutableMap<Stat, ComputedStat> = mutableMapOf()
// The map containing all evolution values for all stats // The map containing all evolution values for all stats

@ -21,7 +21,8 @@ enum class Stat : AnyStat {
ROI, ROI,
STANDARD_DEVIATION, STANDARD_DEVIATION,
STANDARD_DEVIATION_HOURLY, STANDARD_DEVIATION_HOURLY,
STANDARD_DEVIATION_BB_PER_100_HANDS; STANDARD_DEVIATION_BB_PER_100_HANDS,
HANDS_PLAYED;
fun label() : String = when (this) { fun label() : String = when (this) {
NETRESULT -> "" NETRESULT -> ""

@ -23,8 +23,8 @@ open class Session(comment: String = "") : RealmObject(), DynamicRowDelegate, Di
// The time frame of the Session, i.e. the start & end date // The time frame of the Session, i.e. the start & end date
var timeFrame: TimeFrame? = null var timeFrame: TimeFrame? = null
// The time frame group, which can contain multiple sessions // The time frame sessionGroup, which can contain multiple sessions
var sessionGroup: SessionGroup? = null var sessionSet: SessionSet? = null
// the date of creation of the app // the date of creation of the app
var creationDate: Date = Date() var creationDate: Date = Date()

@ -5,12 +5,12 @@ import io.realm.RealmObject
import io.realm.annotations.Ignore import io.realm.annotations.Ignore
open class SessionGroup() : RealmObject() { open class SessionSet() : RealmObject() {
// The timeframe of the group, i.e. its start & end date // The timeframe of the set, i.e. its start & end date
var timeFrame: TimeFrame? = null var timeFrame: TimeFrame? = null
// The list of Session played within the group, i.e. played within the same time frame // The list of Session played within the set, i.e. played within the same time frame
var sessions: RealmList<Session> = RealmList() var sessions: RealmList<Session> = RealmList()
@Ignore // a duration shortcut @Ignore // a duration shortcut
@ -30,10 +30,10 @@ open class SessionGroup() : RealmObject() {
companion object { companion object {
fun newInstance() : SessionGroup { fun newInstance() : SessionSet {
val sessionGroup: SessionGroup = SessionGroup() val sessionSet: SessionSet = SessionSet()
sessionGroup.timeFrame = TimeFrame() sessionSet.timeFrame = TimeFrame()
return sessionGroup return sessionSet
} }
} }

@ -44,11 +44,11 @@ open class TimeFrame : RealmObject() {
// Group // Group
@LinkingObjects("timeFrame") @LinkingObjects("timeFrame")
private val groups: RealmResults<SessionGroup>? = null // we should have only one group private val sets: RealmResults<SessionSet>? = null // we should have only one sessionGroup
@Ignore @Ignore
var group: SessionGroup? = null var set: SessionSet? = null
get() = this.groups?.first() get() = this.sets?.first()
fun setDate(startDate: Date, endDate: Date?) { fun setDate(startDate: Date, endDate: Date?) {
this.startDate = startDate this.startDate = startDate
@ -68,7 +68,7 @@ open class TimeFrame : RealmObject() {
private fun notifySessionDateChange() { private fun notifySessionDateChange() {
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
var query: RealmQuery<SessionGroup> = realm.where(SessionGroup::class.java) var query: RealmQuery<SessionSet> = realm.where(SessionSet::class.java)
query.isNotNull("timeFrame") query.isNotNull("timeFrame")
if (this.endDate == null) { if (this.endDate == null) {
@ -94,44 +94,44 @@ open class TimeFrame : RealmObject() {
} }
/** /**
* Update Time frames from groups * Update Time frames from sets
*/ */
private fun updateTimeFrames(sessionGroups: RealmResults<SessionGroup>) { private fun updateTimeFrames(sessionSets: RealmResults<SessionSet>) {
when (sessionGroups.size) { when (sessionSets.size) {
0 -> this.createSessionGroup() 0 -> this.createSessionGroup()
1 -> this.updateSingleSessionGroup(sessionGroups.first()!!) 1 -> this.updateSingleSessionGroup(sessionSets.first()!!)
else -> this.mergeSessionGroups(sessionGroups) else -> this.mergeSessionGroups(sessionSets)
} }
} }
/** /**
* Creates the session group when the session has none * Creates the session sessionGroup when the session has none
*/ */
private fun createSessionGroup() { private fun createSessionGroup() {
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
realm.beginTransaction() realm.beginTransaction()
val group: SessionGroup = SessionGroup.newInstance() val set: SessionSet = SessionSet.newInstance()
group.timeFrame?.let { set.timeFrame?.let {
it.startDate = this.startDate it.startDate = this.startDate
it.endDate = this.endDate it.endDate = this.endDate
} ?: run { } ?: run {
throw ModelException("TimeFrame should never be null here") throw ModelException("TimeFrame should never be null here")
} }
group.sessions.add(this.session) set.sessions.add(this.session)
realm.commitTransaction() realm.commitTransaction()
} }
/** /**
* Single session group update * Single session sessionGroup update
* Changes the group timeframe using the current timeframe dates * Changes the sessionGroup timeframe using the current timeframe dates
*/ */
private fun updateSingleSessionGroup(sessionGroup: SessionGroup) { private fun updateSingleSessionGroup(sessionSet: SessionSet) {
var groupTimeFrame: TimeFrame = sessionGroup.timeFrame!! // tested in the query var groupTimeFrame: TimeFrame = sessionSet.timeFrame!! // tested in the query
if (this.startDate.before(groupTimeFrame.startDate)) { if (this.startDate.before(groupTimeFrame.startDate)) {
groupTimeFrame.startDate = this.startDate groupTimeFrame.startDate = this.startDate
@ -146,8 +146,8 @@ open class TimeFrame : RealmObject() {
// Realm Update // Realm Update
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
realm.beginTransaction() realm.beginTransaction()
if (!sessionGroup.sessions.contains(this.session)) { if (!sessionSet.sessions.contains(this.session)) {
sessionGroup.sessions.add(this.session) sessionSet.sessions.add(this.session)
} }
realm.copyToRealmOrUpdate(groupTimeFrame) realm.copyToRealmOrUpdate(groupTimeFrame)
realm.commitTransaction() realm.commitTransaction()
@ -155,15 +155,15 @@ open class TimeFrame : RealmObject() {
} }
/** /**
* Multiple session groups update: * Multiple session sets update:
* Merges all groups into one (delete all then create a new one) * Merges all sets into one (delete all then create a new one)
*/ */
private fun mergeSessionGroups(sessionGroups: RealmResults<SessionGroup>) { private fun mergeSessionGroups(sessionSets: RealmResults<SessionSet>) {
var startDate: Date = this.startDate var startDate: Date = this.startDate
var endDate: Date? = this.endDate var endDate: Date? = this.endDate
val timeFrames = sessionGroups.mapNotNull { it.timeFrame } val timeFrames = sessionSets.mapNotNull { it.timeFrame }
timeFrames.forEach { tf -> timeFrames.forEach { tf ->
if (tf.startDate.before(startDate)) { if (tf.startDate.before(startDate)) {
startDate = tf.startDate startDate = tf.startDate
@ -181,34 +181,34 @@ open class TimeFrame : RealmObject() {
} }
// get all sessions from groups // get all sessions from sets
var sessions = sessionGroups.flatMap { it.sessions } var sessions = sessionSets.flatMap { it.sessions }
// Start Realm updates // Start Realm updates
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
realm.beginTransaction() realm.beginTransaction()
// delete all groups // delete all sets
sessionGroups.deleteAllFromRealm() sessionSets.deleteAllFromRealm()
// Create a new groups // Create a new sets
val group: SessionGroup = SessionGroup.newInstance() val set: SessionSet = SessionSet.newInstance()
group.timeFrame?.let { set.timeFrame?.let {
it.startDate = startDate it.startDate = startDate
it.endDate = endDate it.endDate = endDate
} ?: run { } ?: run {
throw ModelException("TimeFrame should never be null here") throw ModelException("TimeFrame should never be null here")
} }
// Add the session linked to this timeframe to the new group // Add the session linked to this timeframe to the new sessionGroup
this.sessions?.first()?.let { this.sessions?.first()?.let {
group.sessions.add(it) set.sessions.add(it)
} ?: run { } ?: run {
throw ModelException("TimeFrame should never be null here") throw ModelException("TimeFrame should never be null here")
} }
// Add all orphan sessions // Add all orphan sessions
group.sessions.addAll(sessions) set.sessions.addAll(sessions)
realm.commitTransaction() realm.commitTransaction()
} }

@ -1,7 +1,7 @@
package net.pokeranalytics.android package net.pokeranalytics.android
import net.pokeranalytics.android.calculus.* import net.pokeranalytics.android.calculus.*
import net.pokeranalytics.android.model.realm.SessionGroup import net.pokeranalytics.android.model.realm.SessionSet
import org.junit.Assert.fail import org.junit.Assert.fail
import org.junit.Test import org.junit.Test
@ -17,7 +17,7 @@ class ExampleUnitTest {
// override var serie: Serie = Serie(TimeFrame()) // override var serie: Serie = Serie(TimeFrame())
override var value: Double = someValue override var value: Double = someValue
override var serie: SessionGroup = SessionGroup() override var serie: SessionSet = SessionSet()
override var estimatedHands: Double = 0.0 override var estimatedHands: Double = 0.0
override var bbNetResult: Double = 0.0 override var bbNetResult: Double = 0.0
override var bigBlindSessionCount: Int = 0 // 0 or 1 override var bigBlindSessionCount: Int = 0 // 0 or 1
@ -28,21 +28,21 @@ class ExampleUnitTest {
@Test @Test
fun testStats() { fun testStats() {
val grades: List<Grade> = listOf(Grade(someValue = 10.0), Grade(someValue = 20.0)) val grades: List<Grade> = listOf(Grade(10.0), Grade(20.0))
val group = SessionGroup(name = "test", sessions = grades) val group = SessionGroup(name = "test", sessions = grades)
val results: ComputedResults = Calculator.compute(group, Calculator.Options()) val results: ComputedResults = Calculator.compute(group, Calculator.Options())
val sum = results.computedStat(Stat.NETRESULT) val sum = results.computedStat(Stat.NETRESULT)
if (sum != null) { if (sum != null) {
assert(sum.value == 30.0) assert(sum.value == 30.0) { "sum is ${sum.value}" }
} else { } else {
fail("No Net result stat") fail("No Net result stat")
} }
val average = results.computedStat(Stat.AVERAGE) val average = results.computedStat(Stat.AVERAGE)
if (average != null) { if (average != null) {
assert(average.value == 15.0) assert(average.value == 15.0) { "average is ${average.value}" }
} else { } else {
fail("No AVERAGE stat") fail("No AVERAGE stat")
} }

Loading…
Cancel
Save