Fixes hand evaluator issues

hh
Laurent 6 years ago
parent 736bc0d543
commit d6ccea472e
  1. 1
      app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt
  2. 8
      app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/Card.kt
  3. 72
      app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt
  4. 1
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryAdapter.kt
  5. 29
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/evaluator/EvaluatorBridge.kt
  6. 2
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/ActionList.kt
  7. 1
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/HandHistoryViewModel.kt
  8. 10
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/views/RowHandHistoryViewHolder.kt
  9. 40
      app/src/main/java/net/pokeranalytics/android/ui/view/HandHistoryRowView.kt

@ -192,6 +192,7 @@ class PokerAnalyticsMigration : RealmMigration {
hhSchema.addField("month", Integer::class.java) hhSchema.addField("month", Integer::class.java)
hhSchema.addField("year", Integer::class.java) hhSchema.addField("year", Integer::class.java)
hhSchema.addField("dayOfMonth", Integer::class.java) hhSchema.addField("dayOfMonth", Integer::class.java)
hhSchema.addRealmListField("winningPositions", Integer::class.java)
val cardSchema = schema.create("Card") val cardSchema = schema.create("Card")
cardSchema.addField("value", Int::class.java) cardSchema.addField("value", Int::class.java)

@ -124,7 +124,7 @@ open class Card : RealmObject() {
override val viewType: Int = RowViewType.TITLE_GRID.ordinal override val viewType: Int = RowViewType.TITLE_GRID.ordinal
val evualuatorSuit: Int? val evaluatorSuit: Int?
get() { get() {
return when (this) { return when (this) {
SPADES -> net.pokeranalytics.android.ui.modules.handhistory.evaluator.Card.SPADES SPADES -> net.pokeranalytics.android.ui.modules.handhistory.evaluator.Card.SPADES
@ -134,8 +134,6 @@ open class Card : RealmObject() {
else -> null else -> null
} }
} }
} }
/*** /***
@ -223,9 +221,9 @@ open class Card : RealmObject() {
fun toEvaluatorCard():net.pokeranalytics.android.ui.modules.handhistory.evaluator.Card? { fun toEvaluatorCard():net.pokeranalytics.android.ui.modules.handhistory.evaluator.Card? {
val rank = this.value val rank = this.value
val suit = this.suit?.evualuatorSuit val suit = this.suit?.evaluatorSuit
return if (rank != null && suit != null) { 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 { } else {
null null
} }

@ -20,6 +20,7 @@ import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.interfaces.TimeFilterable import net.pokeranalytics.android.model.interfaces.TimeFilterable
import net.pokeranalytics.android.model.realm.Session 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.ActionReadRow
import net.pokeranalytics.android.ui.modules.handhistory.model.CardHolder import net.pokeranalytics.android.ui.modules.handhistory.model.CardHolder
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
@ -115,6 +116,11 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable,
*/ */
var heroIndex: Int? = null var heroIndex: Int? = null
/***
* Indicates if the hero wins the hand
*/
var winningPositions: RealmList<Int> = RealmList()
/*** /***
* The board * The board
*/ */
@ -403,6 +409,30 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable,
return actionItems.joinToString(" ") 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 * 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: * 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 return views
} }
fun compareHands(activePositions: List<Int>): List<Int> {
// 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<Int>()
results.forEachIndexed { index, i ->
if (i == best && i != Int.MAX_VALUE) {
winners.add(activePositions[index])
}
}
winners
} ?: run {
listOf<Int>() // 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)
}
} }

@ -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.holder.RowViewHolder
import net.pokeranalytics.android.ui.view.rowrepresentable.ViewIdentifier import net.pokeranalytics.android.ui.view.rowrepresentable.ViewIdentifier
import net.pokeranalytics.android.util.extensions.formatted import net.pokeranalytics.android.util.extensions.formatted
import java.util.*
enum class HandRowType(var layoutRes: Int) : ViewIdentifier, RowRepresentable { enum class HandRowType(var layoutRes: Int) : ViewIdentifier, RowRepresentable {

@ -11,8 +11,8 @@ class EvaluatorBridge {
arr: Array<T>, arr: Array<T>,
len: Int, len: Int,
startPosition: Int, startPosition: Int,
result: Array<T>, result: Array<T?>,
callback: (Array<T>) -> (Unit) callback: (Array<T?>) -> (Unit)
) { ) {
if (len == 0) { if (len == 0) {
callback.invoke(result) callback.invoke(result)
@ -30,27 +30,30 @@ class EvaluatorBridge {
fun playerHand(hand: List<Card>, board: List<Card>): Int { fun playerHand(hand: List<Card>, board: List<Card>): Int {
val handCards = hand.mapNotNull { it.toEvaluatorCard() }.toTypedArray() val handCards = hand.mapNotNull { it.toEvaluatorCard() }
val boardCards = board.mapNotNull { it.toEvaluatorCard() }.toTypedArray() val boardCards = board.mapNotNull { it.toEvaluatorCard() }
val handSize = 5 val handSize = 5
var result = Int.MAX_VALUE var result = Int.MAX_VALUE
when (hand.size) { when (handCards.size) {
2 -> { // Hold'em in 0..2 -> { // Hold'em
val allCards = arrayOf<net.pokeranalytics.android.ui.modules.handhistory.evaluator.Card>() val allCards = mutableListOf<net.pokeranalytics.android.ui.modules.handhistory.evaluator.Card>()
allCards.plus(handCards) allCards.addAll(handCards)
allCards.plus(boardCards) allCards.addAll(boardCards)
if (allCards.size >= handSize) { if (allCards.size >= handSize) {
Combinator.combinations(allCards, handSize, 0, arrayOf()) { Combinator.combinations(allCards.toTypedArray(), handSize, 0, arrayOfNulls(handSize)) {
result = min(result, Hand.evaluate(it)) result = min(result, Hand.evaluate(it))
} }
} }
} }
else -> { // Omahas else -> { // Omahas
Combinator.combinations(boardCards, 3, 0, arrayOf()) { bc -> val bcArray = boardCards.toTypedArray()
Combinator.combinations(handCards, 2, 0, arrayOf()) { hc -> val hcArray = handCards.toTypedArray()
val fcc = arrayOf<net.pokeranalytics.android.ui.modules.handhistory.evaluator.Card>()
Combinator.combinations(bcArray, 3, 0, arrayOfNulls(3)) { bc ->
Combinator.combinations(hcArray, 2, 0, arrayOfNulls(2)) { hc ->
val fcc = arrayOf<net.pokeranalytics.android.ui.modules.handhistory.evaluator.Card?>()
fcc.plus(bc) // Five Card Combination fcc.plus(bc) // Five Card Combination
fcc.plus(hc) fcc.plus(hc)
result = min(result, Hand.evaluate(fcc)) result = min(result, Hand.evaluate(fcc))

@ -238,6 +238,7 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
* Returns the list of position still in play before the given [index] * Returns the list of position still in play before the given [index]
*/ */
fun activePositions(index: Int, showDown: Boolean = false): MutableList<Position> { fun activePositions(index: Int, showDown: Boolean = false): MutableList<Position> {
val oustedPositions = this.take(index + 1) val oustedPositions = this.take(index + 1)
.filter { .filter {
if (showDown) { if (showDown) {
@ -251,6 +252,7 @@ class ActionList(var listener: ActionListListener) : ArrayList<ComputedAction>()
val allPositions = this.positions.clone() as LinkedHashSet<Position> val allPositions = this.positions.clone() as LinkedHashSet<Position>
allPositions.removeAll(oustedPositions) allPositions.removeAll(oustedPositions)
return allPositions.toMutableList() return allPositions.toMutableList()
} }
/*** /***

@ -595,6 +595,7 @@ class HandHistoryViewModel : ViewModel(), RowRepresentableDataSource, CardCentra
this.handHistory.actions.clear() this.handHistory.actions.clear()
val actions = this.sortedActions.map { it.action } val actions = this.sortedActions.map { it.action }
this.handHistory.actions.addAll(actions) this.handHistory.actions.addAll(actions)
this.handHistory.defineWinnerPositions()
if (!this.handHistory.isManaged) { if (!this.handHistory.isManaged) {
realm.copyToRealmOrUpdate(this.handHistory) realm.copyToRealmOrUpdate(this.handHistory)

@ -3,6 +3,7 @@ package net.pokeranalytics.android.ui.modules.handhistory.views
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.row_hand_history_view.view.* 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.handhistory.Street
import net.pokeranalytics.android.model.realm.handhistory.HandHistory import net.pokeranalytics.android.model.realm.handhistory.HandHistory
import net.pokeranalytics.android.ui.adapter.BindableHolder 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.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 { val listener = View.OnClickListener {
delegate?.onRowSelected(position, row) delegate?.onRowSelected(position, row)
} }

@ -56,46 +56,6 @@ class HandHistoryRowView : FrameLayout {
rowHandHistory.amount.text = handHistory.potSizeForStreet(Street.SUMMARY).formatted() 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)
} }
} }
Loading…
Cancel
Save