The simplest algorithm for poker hand evaluation

后端 未结 12 1031
独厮守ぢ
独厮守ぢ 2020-12-04 12:39

I am thinking about poker hand (5 cards) evaluation in Java. Now I am looking for simplicity and clarity rather than performance and efficiency. I probably can

12条回答
  •  借酒劲吻你
    2020-12-04 13:14

    Here's a simple rule-based implementation in Kotlin:

    class PokerHand constructor(hand: String) : Comparable {
    
    companion object {
        const val WIN = 1
        const val TIE = 0
        const val LOSS = -1
    }
    
    val cards: List
    
    val isStraightFlush: Boolean
        get() = isStraight && isFlush
    
    val isFourOfAKind: Boolean
        get() = cards.groupBy { it.weight }.map { it.value }.any { it.size == 4 }
    
    val isFullHouse: Boolean
        get() = cards.groupBy { it.weight }.map { it.value }.size == 2
    
    val isFlush: Boolean
        get() = cards.groupBy { it.suit }.map { it.value }.size == 1
    
    val isStraight: Boolean
        get() = cards.map { it.weight.ordinal } == (cards[0].weight.ordinal..cards[0].weight.ordinal + 4).toList()
    
    val isThreeOfAKind: Boolean
        get() = cards.groupBy { it.weight }.map { it.value }.any { it.size == 3 }
    
    val isTwoPair: Boolean
        get() = cards.groupBy { it.weight }.map { it.value }.filter { it.size == 2 }.count() == 2
    
    val isPair: Boolean
        get() = cards.groupBy { it.weight }.map { it.value }.any { it.size == 2 }
    
    init {
        val cards = ArrayList()
        hand.split(" ").forEach {
            when (it.length != 2) {
                true -> throw RuntimeException("A card code must be two characters")
                else -> cards += Card(Weight.forCode(it[0]), Suit.forCode(it[1]))
            }
        }
        if (cards.size != 5) {
            throw RuntimeException("There must be five cards in a hand")
        }
        this.cards = cards.sortedBy { it.weight.ordinal }
    }
    
    override fun compareTo(other: PokerHand): Int = when {
        (this.isStraightFlush || other.isStraightFlush) ->
            if (this.isStraightFlush) if (other.isStraightFlush) compareByHighCard(other) else WIN else LOSS
        (this.isFourOfAKind || other.isFourOfAKind) ->
            if (this.isFourOfAKind) if (other.isFourOfAKind) compareByHighCard(other) else WIN else LOSS
        (this.isFullHouse || other.isFullHouse) ->
            if (this.isFullHouse) if (other.isFullHouse) compareByHighCard(other) else WIN else LOSS
        (this.isFlush || other.isFlush) ->
            if (this.isFlush) if (other.isFlush) compareByHighCard(other) else WIN else LOSS
        (this.isStraight || other.isStraight) ->
            if (this.isStraight) if (other.isStraight) compareByHighCard(other) else WIN else LOSS
        (this.isThreeOfAKind || other.isThreeOfAKind) ->
            if (this.isThreeOfAKind) if (other.isThreeOfAKind) compareByHighCard(other) else WIN else LOSS
        (this.isTwoPair || other.isTwoPair) ->
            if (this.isTwoPair) if (other.isTwoPair) compareByHighCard(other) else WIN else LOSS
        (this.isPair || other.isPair) ->
            if (this.isPair) if (other.isPair) compareByHighCard(other) else WIN else LOSS
        else -> compareByHighCard(other)
    }
    
    private fun compareByHighCard(other: PokerHand, index: Int = 4): Int = when {
        (index < 0) -> TIE
        cards[index].weight === other.cards[index].weight -> compareByHighCard(other, index - 1)
        cards[index].weight.ordinal > other.cards[index].weight.ordinal -> WIN
        else -> LOSS
    }
    

    }

    Implementation details:

    • Instantiate with a coded hand, eg 2H 3H 4H 5H 6H
    • Methods to evaluate whether the hand is a 'Straight Flush', 'Four of a Kind', 'Full House' etc. These are easy to express in Kotlin.
    • Implements Comparable to evaluate against another hand using a simple rules approach, eg a straight flush beats four of a kind, which beats a full house, and so forth.

    The sources are here.

提交回复
热议问题