Fixes issues with allins & player stack

hh
Laurent 6 years ago
parent c4022d9b15
commit d004781edb
  1. 7
      app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Action.kt
  2. 19
      app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt
  3. 133
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ActionList.kt
  4. 100
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ComputedAction.kt
  5. 2
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/HandHistoryViewModel.kt

@ -9,6 +9,7 @@ import net.pokeranalytics.android.ui.modules.handhistory.model.ActionReadRow
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.extensions.formatted
import timber.log.Timber
/***
@ -172,6 +173,10 @@ open class Action : RealmObject() {
* The amount linked for a bet, raise...
*/
var amount: Double? = null
set(value) {
field = value
Timber.d("/// set value = $value")
}
var effectiveAmount: Double = 0.0
@ -184,7 +189,7 @@ open class Action : RealmObject() {
val formattedAmount: String?
get() {
val amount = when (type) {
val amount = when (this.type) {
Type.CALL, Type.CALL_ALLIN -> this.effectiveAmount
else -> this.amount
}

@ -84,8 +84,10 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable,
var ante: Double = 0.0
set(value) {
field = value
if (this.bigBlindAnte) {
this.bigBlindAnte = false
}
}
/***
* Big blind ante
@ -93,8 +95,10 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable,
var bigBlindAnte: Boolean = false
set(value) {
field = value
if (this.ante > 0) {
this.ante = 0.0
}
}
/***
* Number of players in the hand
@ -153,6 +157,8 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable,
*/
fun configure(handSetup: HandSetup) {
this.playerSetups.removeAll(this.playerSetups)
handSetup.tableSize?.let { this.numberOfPlayers = it }
handSetup.smallBlind?.let { this.smallBlind = it }
handSetup.bigBlind?.let { this.bigBlind = it }
@ -201,6 +207,7 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable,
action.position = position
action.type = type
action.amount = amount
action.effectiveAmount = amount ?: 0.0
this.actions.add(action)
}
@ -350,6 +357,18 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable,
return string
}
fun anteForPosition(position: Position): Double {
return if (this.bigBlindAnte) {
if (position == Position.BB) {
this.bigBlind ?: 0.0
} else {
0.0
}
} else {
this.ante
}
}
/***
* Returns a string representation of the [playerSetup]
*/

@ -18,6 +18,9 @@ interface ActionManager {
fun blindsUpdated(type: Action.Type, amount: Double)
fun minimumBetAmount(index: Int): Double
fun totalPotSize(index: Int): Double
fun initialStack(position: Int): Double?
fun stackForNewAction(action: Action): Double?
fun stackAtStreetStart(index: Int): Double?
val heroIndex: Int?
}
@ -60,7 +63,6 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
this,
action,
totalPotSize,
action.positionRemainingStack,
position
)
computedActions.add(ca)
@ -88,15 +90,31 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
*/
override fun selectAction(index: Int, actionType: Action.Type) {
var type = actionType
val computedAction = this[index]
// Automatically sets action for the previous empty actions
val modifiedActions = mutableListOf<ComputedAction>()
getPreviousEmptyActions(index).forEach {
modifiedActions.add(it)
val lastSignificant = getStreetLastSignificantAction(computedAction.street, index - 1)
it.action.type = if (lastSignificant != null) {
Action.Type.FOLD
} else {
Action.Type.CHECK
}
}
var type = actionType
// define allin type
if (type == Action.Type.UNDEFINED_ALLIN) {
when (type) {
Action.Type.UNDEFINED_ALLIN -> {
val significant = getStreetLastSignificantAction(computedAction.street, index - 1)
type = if (significant != null) {
val betAmount = significant.action.amount
val remainingStack = computedAction.playerRemainingStack
val remainingStack = computedAction.stackBeforeActing
if (remainingStack != null && betAmount != null && remainingStack < betAmount) {
Action.Type.CALL_ALLIN
} else {
@ -105,25 +123,24 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
} else {
Action.Type.BET_ALLIN
}
}
Timber.d(">>> Sets $type at index: $index")
computedAction.setType(type) // false
// Automatically sets action for the previous empty actions
val modifiedActions = mutableListOf<ComputedAction>()
getPreviousEmptyActions(index).forEach {
modifiedActions.add(it)
val lastSignificant = getStreetLastSignificantAction(computedAction.street, index - 1)
it.action.type = if (lastSignificant != null) {
Action.Type.FOLD
} else {
Action.Type.CHECK
}
Action.Type.CALL -> {
getStreetLastSignificantAction(computedAction.street, index - 1)?.action?.amount?.let { betAmount ->
val remainingStack = computedAction.stackBeforeActing
if (remainingStack != null && remainingStack < betAmount) {
type = Action.Type.CALL_ALLIN
}
} ?: throw PAIllegalStateException("Can't happen")
}
else -> {}
}
Timber.d(">>> Sets $type at index: $index")
computedAction.setType(type)
this.updateFollowupActions(index)
this.updateRemainingStacksForPositions(listOf(computedAction.action.position))
fireListener()
}
@ -133,7 +150,8 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
*/
fun setAmount(index: Int, amount: Double) {
val computedAction = this[index]
computedAction.setAmount(amount)
computedAction.setBetAmount(amount)
this.updateRemainingStacksForPositions(listOf(computedAction.action.position))
}
/***
@ -180,7 +198,7 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
return if (lastSignificantAction == null) {
setOf(Action.Type.FOLD, Action.Type.CHECK, Action.Type.BET, Action.Type.POT, Action.Type.UNDEFINED_ALLIN)
} else {
val remainingStack = getLastPlayerAction(index)?.playerRemainingStack
val remainingStack = computedAction.stackBeforeActing
val actionAmount = lastSignificantAction.action.amount
when (lastSignificantAction.action.type) {
@ -273,15 +291,6 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
return this.take(index).filter { it.street == street && it.action.type == null }
}
/***
* Returns the last user action, if any, for the action at the provided [index]
*/
private fun getLastPlayerAction(index: Int): ComputedAction? {
val action = this[index].action
val previousActions = this.take(index)
return previousActions.lastOrNull { it.positionIndex == action.position }
}
/***
* Returns the last significant player action, if any, for the action at the provided [index]
*/
@ -319,7 +328,7 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
activePositions.indexOfFirst { it.ordinal > refIndexPosition.ordinal })
for (i in 0 until activePositions.size) {
val position = activePositions[(firstPositionAfterCurrent + i) % activePositions.size]
this.addNewEmptyAction(position, refAction.street, refAction.totalPotSize, lastRemainingStack(position, index))
this.addNewEmptyAction(position, refAction.street, refAction.totalPotSize)
}
if (activePositions.isNotEmpty()) {
@ -330,7 +339,7 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
/***
* Creates a new action at the end of the action stack, and in the HH representation
*/
private fun addNewEmptyAction(position: Position, street: Street, currentPotSize: Double, remainingStack: Double?) {
private fun addNewEmptyAction(position: Position, street: Street, currentPotSize: Double) {
if (street == Street.SUMMARY) {
throw PAIllegalStateException("Summary cannot have player actions")
@ -345,7 +354,6 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
this,
action,
currentPotSize,
remainingStack,
position
)
this.add(computedAction)
@ -353,13 +361,6 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
this.sizeChanged = true
}
/***
* Returns the last remaining stack of the player, if available
*/
private fun lastRemainingStack(position: Position, index: Int): Double? {
return this.take(index).lastOrNull { it.position == position }?.playerRemainingStack
}
/***
* Adds, if necessary, new ComputedAction for players that needs to act
* Also adds, if necessary, the Street separators and board selectors
@ -481,7 +482,8 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
created = true
}
playerSetup.stack = this.filter { it.positionIndex == positionIndex }.sumByDouble { it.action.effectiveAmount }
val stack = this.filter { it.positionIndex == positionIndex }.sumByDouble { it.action.effectiveAmount }
playerSetup.stack = stack
if (created) {
this.listener.playerSetupCreated()
@ -497,7 +499,7 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
Action.Type.POST_BB -> {
this.handHistory.bigBlind = amount
if (this.handHistory.bigBlindAnte) {
this.updateRemainingStacksForPositions(listOf())
this.updateBigBlindRemainingStack()
}
}
else -> throw PAIllegalStateException("Should never happen")
@ -586,7 +588,7 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
val lastActionIndex = lastComputedAction.action.index
val isShowDown = street == Street.SUMMARY
activePositions(lastActionIndex, isShowDown).sortedBy { it.ordinal }.forEach {
addNewEmptyAction(it, street, totalPotSize, lastRemainingStack(it, lastActionIndex))
addNewEmptyAction(it, street, totalPotSize)
}
}
@ -608,23 +610,26 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
}
/***
* Recomputes all remaining stacks for the given [positionIndexes]
* Recomputes all remaining stacks for the given [positionIndexes], after the given [index]
*/
fun updateRemainingStacksForPositions(positionIndexes: List<Int>) {
val ante = if (this.handHistory.bigBlindAnte) this.handHistory.bigBlind ?: 0.0 else this.handHistory.ante
positionIndexes.forEach { posIndex ->
val ante = this.handHistory.anteForPosition(this.positions.elementAt(posIndex))
this.handHistory.playerSetupForPosition(posIndex)?.stack?.let { initialStack ->
var remainingStack = initialStack - ante
val startStack = initialStack - ante
val playerActions = this.filter { it.positionIndex == posIndex }
var previousAction: ComputedAction? = null
playerActions.forEach {
remainingStack -= it.action.effectiveAmount
it.playerRemainingStack = remainingStack
// remainingStack -= it.action.effectiveAmount
it.stackBeforeActing = previousAction?.stackAfterActing ?: startStack
if (it.action.type?.isAllin == true) {
it.action.amount = remainingStack
it.action.amount = this.stackAtStreetStart(it.action.index)
}
previousAction = it
}
}
@ -632,30 +637,22 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
}
/***
* Updates all ComputedAction linked to the position with an updated value of the remaining stack
*/
fun stackUpdatedAtPosition(positionIndex: Int) {
this.handHistory.playerSetupForPosition(positionIndex)?.let { playerSetup ->
var remainingStack = playerSetup.stack
this.filter { it.action.position == positionIndex }.forEach { ca ->
remainingStack?.let { rs ->
if (ca.action.type?.isAllin == true) { // updates the action amount
ca.setType(ca.action.type!!)
} else { // updates the remaining stack
remainingStack = rs - ca.action.effectiveAmount
ca.playerRemainingStack = remainingStack
override fun initialStack(position: Int): Double? {
val ante = this.handHistory.anteForPosition(this.positions.elementAt(position))
return this.handHistory.playerSetupForPosition(position)?.stack?.let { it - ante } ?: run { null}
}
} ?: run {
ca.playerRemainingStack = null
override fun stackForNewAction(action: Action): Double? {
return this.lastOrNull { it.action.position == action.position }?.stackAfterActing
?: run {
this.initialStack(action.position)
}
}
} ?: throw PAIllegalStateException("Can't happen")
override fun stackAtStreetStart(index: Int): Double? {
val computedAction = this[index]
val firstStreetAction = this.firstOrNull { it.street == computedAction.street && it.action.position == computedAction.action.position }
return firstStreetAction?.stackBeforeActing
}
}

@ -8,7 +8,6 @@ import net.pokeranalytics.android.model.realm.handhistory.HandHistory
import net.pokeranalytics.android.model.realm.handhistory.toReadRow
import net.pokeranalytics.android.ui.modules.handhistory.HandRowType
import kotlin.math.max
import kotlin.math.min
/***
* An extension to transform a list of ComputedAction into
@ -33,10 +32,27 @@ fun List<ComputedAction>.compact(positions: LinkedHashSet<Position>, heroIndex:
class ComputedAction(var manager: ActionManager,
var action: Action,
var totalPotSize: Double = 0.0,
var playerRemainingStack: Double? = null,
var position: Position
) : HandHistoryRow, PositionalRow {
/***
* The remaining stack before acting
*/
var stackBeforeActing: Double? = null
init {
this.stackBeforeActing = this.manager.stackForNewAction(this.action)
}
val stackAfterActing: Double?
get() {
return this.stackBeforeActing?.let {
it - this.action.effectiveAmount
} ?: run {
null
}
}
enum class Tag {
PLAYER,
ACTION,
@ -56,7 +72,7 @@ class ComputedAction(var manager: ActionManager,
return when(this.action.type) {
Action.Type.POST_SB, Action.Type.POST_BB, Action.Type.STRADDLE,
Action.Type.BET, Action.Type.RAISE -> (this.action.amount == null)
Action.Type.BET_ALLIN, Action.Type.RAISE_ALLIN -> (this.playerRemainingStack == null && this.action.amount == null)
Action.Type.BET_ALLIN, Action.Type.RAISE_ALLIN -> (this.stackBeforeActing == null && this.action.amount == null)
else -> false
}
}
@ -71,6 +87,7 @@ class ComputedAction(var manager: ActionManager,
if (typeChange) {
this.action.type = type
this.action.amount = null
this.action.effectiveAmount = 0.0
}
// define action amounts if possible
@ -79,21 +96,22 @@ class ComputedAction(var manager: ActionManager,
updateEffectiveAmount()
}
Action.Type.CALL_ALLIN -> {
this.setEffectiveAmount(this.playerRemainingStack!!)
}
Action.Type.STRADDLE -> {
// TODO
this.action.effectiveAmount = this.stackBeforeActing!!
}
Action.Type.POT -> {
val lastSignificantAction = this.getStreetLastSignificantAction()
val lastSignificantAmount = lastSignificantAction?.action?.amount ?: 0.0
val potAmount = lastSignificantAmount * 2 + this.manager.totalPotSize(this.action.index)
this.setAmount(potAmount)
this.setBetAmount(potAmount)
}
Action.Type.BET_ALLIN, Action.Type.RAISE_ALLIN, Action.Type.UNDEFINED_ALLIN -> {
this.playerRemainingStack?.let {
this.setAmount(it)
this.manager.stackAtStreetStart(this.action.index)?.let { stack ->
this.setBetAmount(stack)
}
// this.stackBeforeActing?.let {
// this.setBetAmount(it)
// }
}
else -> {}
}
@ -107,7 +125,7 @@ class ComputedAction(var manager: ActionManager,
/***
* Sets the action amount
*/
fun setAmount(amount: Double) {
fun setBetAmount(amount: Double) {
if (amount <= 0) {
return
@ -120,17 +138,9 @@ class ComputedAction(var manager: ActionManager,
this.action.effectiveAmount = correctedAmount - committedAmount
val oldAmount = this.action.amount
val remainingStack = this.playerRemainingStack
val remainingStack = this.stackBeforeActing
if (oldAmount != null && remainingStack != null) {
// Updates the remaining stack if the amount is changed
val oldPlayerRemainingStack = remainingStack + oldAmount
val revisedAmount = min(correctedAmount, oldPlayerRemainingStack)
val revisedRemainingStack = remainingStack - revisedAmount + oldAmount
this.playerRemainingStack = revisedRemainingStack
this.action.toggleType(remainingStack)
} else {
this.action.amount = correctedAmount
}
@ -154,7 +164,7 @@ class ComputedAction(var manager: ActionManager,
this.getStreetLastSignificantAction()?.action?.amount?.let { significantActionAmount ->
val askedAmount = significantActionAmount - committedAmount
this.playerRemainingStack?.let { remainingStack ->
this.stackBeforeActing?.let { remainingStack ->
if (remainingStack < askedAmount) {
this.manager.selectAction(this.action.index, Action.Type.CALL_ALLIN)
}
@ -180,22 +190,22 @@ class ComputedAction(var manager: ActionManager,
* Sets the effective amount of the action
* Also calculates the player remaining stack if possible
*/
private fun setEffectiveAmount(amount: Double) {
val oldEffective = this.action.effectiveAmount
this.action.effectiveAmount = amount
this.playerRemainingStack?.let {
val oldPlayerRemainingStack = it + oldEffective
val revisedAmount = min(amount, oldPlayerRemainingStack)
val remainingStack = it - revisedAmount + oldEffective
this.playerRemainingStack = remainingStack
this.action.toggleType(remainingStack)
}
}
// private fun setEffectiveAmount(amount: Double) {
// this.action.effectiveAmount = amount
//
// val oldEffective = this.action.effectiveAmount
//
// this.stackBeforeActing?.let {
//
// val oldPlayerRemainingStack = it + oldEffective
// val revisedAmount = min(amount, oldPlayerRemainingStack)
// val remainingStack = it - revisedAmount + oldEffective
// this.stackBeforeActing = remainingStack
//
// this.action.toggleType(remainingStack)
//
// }
// }
override fun isFieldNeedsInput(tag: Int, handHistory: HandHistory): Boolean {
return when (tag) {
@ -236,19 +246,7 @@ class ComputedAction(var manager: ActionManager,
val significantAmount = significantAction.action.amount ?: 0.0
val committedAmount = getPreviouslyCommittedAmount()
var effectiveAmount = significantAmount - committedAmount
// Fixes effective if the remaining stack is lower
this.playerRemainingStack?.let { stack ->
if (stack < effectiveAmount) {
effectiveAmount = stack
if (this.action.type == Action.Type.CALL) {
this.action.type = Action.Type.CALL_ALLIN
}
}
}
this.setEffectiveAmount(effectiveAmount)
this.action.effectiveAmount = significantAmount - committedAmount
}
private fun getStreetLastSignificantAction(): ComputedAction? {
@ -287,7 +285,7 @@ class ComputedAction(var manager: ActionManager,
return when (this.action.type) {
Action.Type.POST_SB, Action.Type.POST_BB, Action.Type.STRADDLE,
Action.Type.BET, Action.Type.RAISE -> true
Action.Type.BET_ALLIN, Action.Type.RAISE_ALLIN -> (this.playerRemainingStack == null)
Action.Type.BET_ALLIN, Action.Type.RAISE_ALLIN -> true // (this.action.amount == null)
else -> false
}
}

@ -496,7 +496,7 @@ class HandHistoryViewModel : ViewModel(), RowRepresentableDataSource, CardCentra
}
is PlayerSetupRow -> {
row.setStack(amount)
this.sortedActions.stackUpdatedAtPosition(row.positionIndex)
this.sortedActions.updateRemainingStacksForPositions(listOf(row.positionIndex))
}
else -> {}
}

Loading…
Cancel
Save