|
|
|
|
@ -29,6 +29,7 @@ import net.pokeranalytics.android.ui.view.RowViewType |
|
|
|
|
import net.pokeranalytics.android.util.extensions.addLineReturn |
|
|
|
|
import net.pokeranalytics.android.util.extensions.formatted |
|
|
|
|
import net.pokeranalytics.android.util.extensions.fullDate |
|
|
|
|
import timber.log.Timber |
|
|
|
|
import java.util.* |
|
|
|
|
|
|
|
|
|
open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable, TimeFilterable, |
|
|
|
|
@ -120,7 +121,7 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable, |
|
|
|
|
/*** |
|
|
|
|
* Indicates if the hero wins the hand |
|
|
|
|
*/ |
|
|
|
|
var winningPositions: RealmList<Int> = RealmList() |
|
|
|
|
var winnerPots: RealmList<WonPot> = RealmList() |
|
|
|
|
|
|
|
|
|
/*** |
|
|
|
|
* The board |
|
|
|
|
@ -143,6 +144,12 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable, |
|
|
|
|
override var year: Int? = null |
|
|
|
|
override var dayOfMonth: Int? = null |
|
|
|
|
|
|
|
|
|
/*** |
|
|
|
|
* Returns the indexes of all players |
|
|
|
|
*/ |
|
|
|
|
val positionIndexes: IntRange |
|
|
|
|
get() { return (0 until this.numberOfPlayers) } |
|
|
|
|
|
|
|
|
|
// Deletable |
|
|
|
|
|
|
|
|
|
override fun isValidForDelete(realm: Realm): Boolean { |
|
|
|
|
@ -199,7 +206,7 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable, |
|
|
|
|
val totalActions = this.actions.size |
|
|
|
|
val startingPosition = lastStraddler?.let { it + 1 } ?: totalActions |
|
|
|
|
|
|
|
|
|
for (i in 0 until this.numberOfPlayers) { |
|
|
|
|
for (i in this.positionIndexes) { |
|
|
|
|
this.addAction((startingPosition + i) % this.numberOfPlayers) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -415,8 +422,8 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable, |
|
|
|
|
*/ |
|
|
|
|
val heroWins: Boolean? |
|
|
|
|
get() { |
|
|
|
|
return this.heroIndex?.let { |
|
|
|
|
this.winningPositions.contains(it) |
|
|
|
|
return this.heroIndex?.let { heroIndex -> |
|
|
|
|
this.winnerPots.any { it.position == heroIndex } |
|
|
|
|
} ?: run { |
|
|
|
|
null |
|
|
|
|
} |
|
|
|
|
@ -474,40 +481,114 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable, |
|
|
|
|
return views |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
data class Pot(var amount: Double, var positions: Set<Int>, var level: Double? = null) |
|
|
|
|
fun pots(): List<Pot> { |
|
|
|
|
|
|
|
|
|
var currentPot = 0.0 |
|
|
|
|
val positions = this.positionIndexes.toMutableSet() |
|
|
|
|
val pots = mutableListOf<Pot>() |
|
|
|
|
var allinAtStreet: Street? = null |
|
|
|
|
val allinActions = mutableListOf<Action>() |
|
|
|
|
this.sortedActions.forEach { |
|
|
|
|
|
|
|
|
|
if (allinAtStreet == null) { |
|
|
|
|
|
|
|
|
|
when { |
|
|
|
|
it.type == Action.Type.FOLD -> { |
|
|
|
|
positions.remove(it.position) |
|
|
|
|
} |
|
|
|
|
it.type?.isPullOut == false -> { |
|
|
|
|
currentPot += it.effectiveAmount |
|
|
|
|
} |
|
|
|
|
it.type == Action.Type.CALL_ALLIN -> { |
|
|
|
|
allinAtStreet = it.street |
|
|
|
|
// TODO create pot |
|
|
|
|
} |
|
|
|
|
it.type?.isAllin == true -> { |
|
|
|
|
currentPot += it.effectiveAmount |
|
|
|
|
allinAtStreet = it.street |
|
|
|
|
allinActions.add(it) |
|
|
|
|
} |
|
|
|
|
else -> { |
|
|
|
|
Timber.d("unmanaged action type: ${it.type}") |
|
|
|
|
// throw PAIllegalStateException("unmanaged action type: ${it.type}") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} else { // Allin situation |
|
|
|
|
|
|
|
|
|
if (it.street != allinAtStreet) { |
|
|
|
|
allinAtStreet = null |
|
|
|
|
allinActions.clear() |
|
|
|
|
} else { |
|
|
|
|
when { |
|
|
|
|
it.type == Action.Type.FOLD -> { |
|
|
|
|
positions.remove(it.position) |
|
|
|
|
} |
|
|
|
|
it.type == Action.Type.CALL -> { |
|
|
|
|
currentPot += it.effectiveAmount |
|
|
|
|
allinActions.add(it) |
|
|
|
|
} |
|
|
|
|
it.type == Action.Type.CALL_ALLIN -> { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (currentPot > 0.0) { |
|
|
|
|
pots.add(Pot(currentPot, positions)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return pots |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*** |
|
|
|
|
* Defines which positions win the hand |
|
|
|
|
*/ |
|
|
|
|
fun defineWinnerPositions() { |
|
|
|
|
|
|
|
|
|
val folds = this.sortedActions.filter { it.type == Action.Type.FOLD }.map { it.position } |
|
|
|
|
val activePositions = (0 until this.numberOfPlayers).toMutableList() |
|
|
|
|
val activePositions = this.positionIndexes.toMutableList() |
|
|
|
|
activePositions.removeAll(folds) |
|
|
|
|
|
|
|
|
|
val winningPositions = when (activePositions.size) { |
|
|
|
|
val wonPots = when (activePositions.size) { |
|
|
|
|
0 -> listOf() // no winner, everyone has fold. Should not happen |
|
|
|
|
1 -> activePositions // One player has not fold, typically BET / FOLD |
|
|
|
|
else -> this.compareHands(activePositions) // Several players remains, typically BET/FOLD or CHECKS |
|
|
|
|
1 -> { // One player has not fold, typically BET / FOLD |
|
|
|
|
val pot = WonPot() |
|
|
|
|
pot.position = activePositions.first() |
|
|
|
|
pot.amount = potSizeForStreet(Street.SUMMARY) |
|
|
|
|
listOf(pot) |
|
|
|
|
} |
|
|
|
|
else -> this.getWinningsByPosition(activePositions) // Several players remains, typically BET/FOLD or CHECKS |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.winningPositions.clear() |
|
|
|
|
this.winningPositions.addAll(winningPositions) |
|
|
|
|
this.winnerPots.clear() |
|
|
|
|
this.winnerPots.addAll(wonPots) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*** |
|
|
|
|
* Compares the hands of the players at the given [positions] |
|
|
|
|
* Returns the list of winning hands by position + chips won |
|
|
|
|
*/ |
|
|
|
|
private fun getWinningsByPosition(positions: List<Int>): Map<Int, Double> { |
|
|
|
|
private fun getWinningsByPosition(positions: List<Int>): Collection<WonPot> { |
|
|
|
|
|
|
|
|
|
// get the total committed amounts for each position, same order |
|
|
|
|
val committedAmounts = positions.map { position -> |
|
|
|
|
val committedAmounts = this.positionIndexes.map { position -> |
|
|
|
|
this.actions.filter { it.position == position }.sumByDouble { it.effectiveAmount } |
|
|
|
|
}.toMutableList() |
|
|
|
|
|
|
|
|
|
// get the various committed levels, ascendly sorted |
|
|
|
|
val sortedPotLevels = committedAmounts.toSet().toList().sorted() |
|
|
|
|
|
|
|
|
|
val wonAmounts = hashMapOf<Int, Double>() |
|
|
|
|
val wonPots = hashMapOf<Int, WonPot>() |
|
|
|
|
var previousPotLevel = 0.0 // previous pot level, to remove from the next level |
|
|
|
|
|
|
|
|
|
// Iterate on each pot |
|
|
|
|
@ -535,16 +616,19 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable, |
|
|
|
|
// Distributes the pot for each winners |
|
|
|
|
val share = pot / winningPositions.size |
|
|
|
|
winningPositions.forEach { p -> |
|
|
|
|
val winnings = wonAmounts[p] |
|
|
|
|
if (winnings == null) { |
|
|
|
|
wonAmounts[p] = share |
|
|
|
|
val wp = wonPots[p] |
|
|
|
|
if (wp == null) { |
|
|
|
|
val wonPot = WonPot() |
|
|
|
|
wonPot.position = p |
|
|
|
|
wonPot.amount = share |
|
|
|
|
wonPots[p] = wonPot |
|
|
|
|
} else { |
|
|
|
|
wonAmounts[p] = share + winnings |
|
|
|
|
wp.amount += share |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return wonAmounts |
|
|
|
|
return wonPots.values |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*** |
|
|
|
|
|