From d6ccea472e2a0e7f156330dae22e6a564f016727 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 30 Mar 2020 18:00:35 +0200 Subject: [PATCH] Fixes hand evaluator issues --- .../migrations/PokerAnalyticsMigration.kt | 1 + .../android/model/realm/handhistory/Card.kt | 8 +-- .../model/realm/handhistory/HandHistory.kt | 72 +++++++++++++++++++ .../modules/handhistory/HandHistoryAdapter.kt | 1 - .../handhistory/evaluator/EvaluatorBridge.kt | 29 ++++---- .../modules/handhistory/model/ActionList.kt | 2 + .../handhistory/model/HandHistoryViewModel.kt | 1 + .../views/RowHandHistoryViewHolder.kt | 10 ++- .../android/ui/view/HandHistoryRowView.kt | 40 ----------- 9 files changed, 104 insertions(+), 60 deletions(-) diff --git a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt index 51cc8a59..e37b87bc 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt @@ -192,6 +192,7 @@ class PokerAnalyticsMigration : RealmMigration { hhSchema.addField("month", Integer::class.java) hhSchema.addField("year", Integer::class.java) hhSchema.addField("dayOfMonth", Integer::class.java) + hhSchema.addRealmListField("winningPositions", Integer::class.java) val cardSchema = schema.create("Card") cardSchema.addField("value", Int::class.java) diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt index 056d9e77..a1b94e2e 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt @@ -124,7 +124,7 @@ open class Card : RealmObject() { override val viewType: Int = RowViewType.TITLE_GRID.ordinal - val evualuatorSuit: Int? + val evaluatorSuit: Int? get() { return when (this) { SPADES -> net.pokeranalytics.android.ui.modules.handhistory.evaluator.Card.SPADES @@ -134,8 +134,6 @@ open class Card : RealmObject() { else -> null } } - - } /*** @@ -223,9 +221,9 @@ open class Card : RealmObject() { fun toEvaluatorCard():net.pokeranalytics.android.ui.modules.handhistory.evaluator.Card? { val rank = this.value - val suit = this.suit?.evualuatorSuit + val suit = this.suit?.evaluatorSuit return if (rank != null && suit != null) { - net.pokeranalytics.android.ui.modules.handhistory.evaluator.Card(rank, suit) + net.pokeranalytics.android.ui.modules.handhistory.evaluator.Card(rank - 2, suit) } else { null } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt index bf4ef27e..a312b1e8 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt @@ -20,6 +20,7 @@ import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.interfaces.TimeFilterable import net.pokeranalytics.android.model.realm.Session +import net.pokeranalytics.android.ui.modules.handhistory.evaluator.EvaluatorBridge import net.pokeranalytics.android.ui.modules.handhistory.model.ActionReadRow import net.pokeranalytics.android.ui.modules.handhistory.model.CardHolder import net.pokeranalytics.android.ui.view.RowRepresentable @@ -115,6 +116,11 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable, */ var heroIndex: Int? = null + /*** + * Indicates if the hero wins the hand + */ + var winningPositions: RealmList = RealmList() + /*** * The board */ @@ -403,6 +409,30 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable, return actionItems.joinToString(" ") } + /*** + * Returns if the hero has won the hand, or part of the pot + */ + val heroWins: Boolean? + get() { + return this.heroIndex?.let { + this.winningPositions.contains(it) + } ?: run { + null + } + } + +// private fun definesIfHeroWins() { +// this.heroIndex?.let { heroIndex -> +// +// val heroCards = this.playerSetupForPosition(heroIndex)?.cards +// val heroNumberOfCards = heroCards?.size ?: 0 +// if (this.board.size == 5 && heroNumberOfCards > 1) { +// +// } +// +// } +// } + /*** * Creates a list of cards for the hand history to give a representation of the hand * We will try to add a minimum of 5 cards using by priority: @@ -455,4 +485,46 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable, return views } + fun compareHands(activePositions: List): List { + + // Evaluate all hands + val results = activePositions.map { + this.playerSetupForPosition(it)?.cards?.let { hand -> + EvaluatorBridge.playerHand(hand, this.board) + } ?: run { + Int.MAX_VALUE + } + } + + // Check who has best score (EvaluatorBridge gives a lowest score for a better hand) + return results.min()?.let { best -> + val winners = mutableListOf() + results.forEachIndexed { index, i -> + if (i == best && i != Int.MAX_VALUE) { + winners.add(activePositions[index]) + } + } + winners + } ?: run { + listOf() // type needed for build + } + + } + + fun defineWinnerPositions() { + + val folds = this.sortedActions.filter { it.type == Action.Type.FOLD }.map { it.position } + val activePositions = (0 until this.numberOfPlayers).toMutableList() + activePositions.removeAll(folds) + + val winningPositions = 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 + } + + this.winningPositions.clear() + this.winningPositions.addAll(winningPositions) + } + } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryAdapter.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryAdapter.kt index dba34f7c..249f77de 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryAdapter.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryAdapter.kt @@ -39,7 +39,6 @@ import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.holder.RowViewHolder import net.pokeranalytics.android.ui.view.rowrepresentable.ViewIdentifier import net.pokeranalytics.android.util.extensions.formatted -import java.util.* enum class HandRowType(var layoutRes: Int) : ViewIdentifier, RowRepresentable { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/evaluator/EvaluatorBridge.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/evaluator/EvaluatorBridge.kt index ebb8c1ce..33561f10 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/evaluator/EvaluatorBridge.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/evaluator/EvaluatorBridge.kt @@ -11,8 +11,8 @@ class EvaluatorBridge { arr: Array, len: Int, startPosition: Int, - result: Array, - callback: (Array) -> (Unit) + result: Array, + callback: (Array) -> (Unit) ) { if (len == 0) { callback.invoke(result) @@ -30,27 +30,30 @@ class EvaluatorBridge { fun playerHand(hand: List, board: List): Int { - val handCards = hand.mapNotNull { it.toEvaluatorCard() }.toTypedArray() - val boardCards = board.mapNotNull { it.toEvaluatorCard() }.toTypedArray() + val handCards = hand.mapNotNull { it.toEvaluatorCard() } + val boardCards = board.mapNotNull { it.toEvaluatorCard() } val handSize = 5 var result = Int.MAX_VALUE - when (hand.size) { - 2 -> { // Hold'em - val allCards = arrayOf() - allCards.plus(handCards) - allCards.plus(boardCards) + when (handCards.size) { + in 0..2 -> { // Hold'em + val allCards = mutableListOf() + allCards.addAll(handCards) + allCards.addAll(boardCards) if (allCards.size >= handSize) { - Combinator.combinations(allCards, handSize, 0, arrayOf()) { + Combinator.combinations(allCards.toTypedArray(), handSize, 0, arrayOfNulls(handSize)) { result = min(result, Hand.evaluate(it)) } } } else -> { // Omahas - Combinator.combinations(boardCards, 3, 0, arrayOf()) { bc -> - Combinator.combinations(handCards, 2, 0, arrayOf()) { hc -> - val fcc = arrayOf() + val bcArray = boardCards.toTypedArray() + val hcArray = handCards.toTypedArray() + + Combinator.combinations(bcArray, 3, 0, arrayOfNulls(3)) { bc -> + Combinator.combinations(hcArray, 2, 0, arrayOfNulls(2)) { hc -> + val fcc = arrayOf() fcc.plus(bc) // Five Card Combination fcc.plus(hc) result = min(result, Hand.evaluate(fcc)) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ActionList.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ActionList.kt index 952a9ea4..001e5efb 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ActionList.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ActionList.kt @@ -238,6 +238,7 @@ class ActionList(var listener: ActionListListener) : ArrayList() * Returns the list of position still in play before the given [index] */ fun activePositions(index: Int, showDown: Boolean = false): MutableList { + val oustedPositions = this.take(index + 1) .filter { if (showDown) { @@ -251,6 +252,7 @@ class ActionList(var listener: ActionListListener) : ArrayList() val allPositions = this.positions.clone() as LinkedHashSet allPositions.removeAll(oustedPositions) return allPositions.toMutableList() + } /*** diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/HandHistoryViewModel.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/HandHistoryViewModel.kt index d01764fc..cbcb9c3f 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/HandHistoryViewModel.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/HandHistoryViewModel.kt @@ -595,6 +595,7 @@ class HandHistoryViewModel : ViewModel(), RowRepresentableDataSource, CardCentra this.handHistory.actions.clear() val actions = this.sortedActions.map { it.action } this.handHistory.actions.addAll(actions) + this.handHistory.defineWinnerPositions() if (!this.handHistory.isManaged) { realm.copyToRealmOrUpdate(this.handHistory) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/views/RowHandHistoryViewHolder.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/views/RowHandHistoryViewHolder.kt index d5515605..b8e94670 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/views/RowHandHistoryViewHolder.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/views/RowHandHistoryViewHolder.kt @@ -3,6 +3,7 @@ package net.pokeranalytics.android.ui.modules.handhistory.views import android.view.View import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.row_hand_history_view.view.* +import net.pokeranalytics.android.R import net.pokeranalytics.android.model.handhistory.Street import net.pokeranalytics.android.model.realm.handhistory.HandHistory import net.pokeranalytics.android.ui.adapter.BindableHolder @@ -30,7 +31,14 @@ class RowHandHistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemVie itemView.amount.text = handHistory.potSizeForStreet(Street.SUMMARY).formatted() -// itemView.handHistoryRow.setData(handHistory) + val colorId = when(handHistory.heroWins) { + true -> R.color.green + false -> R.color.red + else -> R.color.kaki_light + } + + itemView.amount.setTextColor(itemView.context.getColor(colorId)) + val listener = View.OnClickListener { delegate?.onRowSelected(position, row) } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/HandHistoryRowView.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/HandHistoryRowView.kt index da07a48b..7220d2a2 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/HandHistoryRowView.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/HandHistoryRowView.kt @@ -56,46 +56,6 @@ class HandHistoryRowView : FrameLayout { rowHandHistory.amount.text = handHistory.potSizeForStreet(Street.SUMMARY).formatted() -// val realm = Realm.getDefaultInstance() -// realm.executeTransaction { -// -// val list = listOf(Card.newInstance(it, null, Card.Suit.CLOVER), -// Card.newInstance(Realm.getDefaultInstance(), 12, Card.Suit.HEART) -// ) -// -// list.forEach { card -> -// val view = card.view(context, LayoutInflater.from(context), rowHandHistory.cardsLayout) -// rowHandHistory.cardsLayout.addView(view) -// } -// -// LayoutInflater.from(context).inflate(R.layout.view_card_separator, rowHandHistory.cardsLayout) -// -// val list2 = listOf(Card.newInstance(it, null, Card.Suit.CLOVER), -// Card.newInstance(Realm.getDefaultInstance(), 12, Card.Suit.HEART) -// ) -// -// list2.forEach { card -> -// val view = card.view(context, LayoutInflater.from(context), rowHandHistory.cardsLayout) -// rowHandHistory.cardsLayout.addView(view) -// } -// -// } - - // Date -// rowHandHistory.transactionDateDay.text = handHistory.date.getShortDayName() -// rowHandHistory.transactionDateNumber.text = handHistory.date.getDayNumber() - - // Title / Game type -// var title = handHistory.type?.name ?: "" + " " + handHistory.comment -// var subtitle = handHistory.bankroll?.name -// -// rowHandHistory.transactionTitle.text = title -// rowHandHistory.transactionSubtitle.text = subtitle -// -// Amount -// val formattedStat = ComputedStat(Stat.NET_RESULT, handHistory.amount, currency = handHistory.bankroll?.utilCurrency).format() -// rowHandHistory.transactionAmount.setTextFormat(formattedStat, context) - } } \ No newline at end of file