The simplest algorithm for poker hand evaluation

后端 未结 12 1021
独厮守ぢ
独厮守ぢ 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:15

    Here's a naive approach to five-card hand comparison that I'm using to help initially populate a lookup table:

    • https://gist.github.com/gdejohn/8293321#file-hand-java

    Instead of being as terse as possible, I prioritized type safety and clear, self-documenting code. If you're not familiar with the Guava types I'm using, you can browse their documentation.

    And I'll include the code here (minus static imports for the enum constants at the bottom), although it's really too long to comfortably view in an answer.

    import static com.google.common.base.Preconditions.checkArgument;
    import static com.google.common.collect.Ordering.from;
    import static com.google.common.collect.Ordering.natural;
    import static java.util.Comparator.comparing;
    import static java.util.Comparator.comparingInt;
    
    import java.util.Comparator;
    import java.util.EnumSet;
    import java.util.LinkedList;
    import java.util.Set;
    import java.util.function.Function;
    
    import com.google.common.collect.EnumMultiset;
    import com.google.common.collect.Multiset;
    import com.google.common.collect.Multiset.Entry;
    import com.google.common.collect.Ordering;
    
    public class Hand implements Comparable {
        public final Category category;
    
        private final LinkedList distinctRanks = new LinkedList<>();
    
        public Hand(Set cards) {
            checkArgument(cards.size() == 5);
            Set suits = EnumSet.noneOf(Suit.class);
            Multiset ranks = EnumMultiset.create(Rank.class);
            for (Card card : cards) {
                suits.add(card.suit);
                ranks.add(card.rank);
            }
            Set> entries = ranks.entrySet();
            for (Entry entry : byCountThenRank.immutableSortedCopy(entries)) {
                distinctRanks.addFirst(entry.getElement());
            }
            Rank first = distinctRanks.getFirst();
            int distinctCount = distinctRanks.size();
            if (distinctCount == 5) {
                boolean flush = suits.size() == 1;
                if (first.ordinal() - distinctRanks.getLast().ordinal() == 4) {
                    category = flush ? STRAIGHT_FLUSH : STRAIGHT;
                }
                else if (first == ACE && distinctRanks.get(1) == FIVE) {
                    category = flush ? STRAIGHT_FLUSH : STRAIGHT;
                    // ace plays low, move to end
                    distinctRanks.addLast(distinctRanks.removeFirst());
                }
                else {
                    category = flush ? FLUSH : HIGH_CARD;
                }
            }
            else if (distinctCount == 4) {
                category = ONE_PAIR;
            }
            else if (distinctCount == 3) {
                category = ranks.count(first) == 2 ? TWO_PAIR : THREE_OF_A_KIND;
            }
            else {
                category = ranks.count(first) == 3 ? FULL_HOUSE : FOUR_OF_A_KIND;
            }
        }
    
        @Override
        public final int compareTo(Hand that) {
            return byCategoryThenRanks.compare(this, that);
        }
    
        private static final Ordering> byCountThenRank;
    
        private static final Comparator byCategoryThenRanks;
    
        static {
            Comparator> byCount = comparingInt(Entry::getCount);
            Comparator> byRank = comparing(Entry::getElement);
            byCountThenRank = from(byCount.thenComparing(byRank));
            Comparator byCategory = comparing((Hand hand) -> hand.category);
            Function> getRanks =
                    (Hand hand) -> hand.distinctRanks;
            Comparator byRanks =
                    comparing(getRanks, natural().lexicographical());
            byCategoryThenRanks = byCategory.thenComparing(byRanks);
        }
    
        public enum Category {
            HIGH_CARD,
            ONE_PAIR,
            TWO_PAIR,
            THREE_OF_A_KIND,
            STRAIGHT,
            FLUSH,
            FULL_HOUSE,
            FOUR_OF_A_KIND,
            STRAIGHT_FLUSH;
        }
    
        public enum Rank {
            TWO,
            THREE,
            FOUR,
            FIVE,
            SIX,
            SEVEN,
            EIGHT,
            NINE,
            TEN,
            JACK,
            QUEEN,
            KING,
            ACE;
        }
    
        public enum Suit {
            DIAMONDS,
            CLUBS,
            HEARTS,
            SPADES;
        }
    
        public enum Card {
            TWO_DIAMONDS(TWO, DIAMONDS),
            THREE_DIAMONDS(THREE, DIAMONDS),
            FOUR_DIAMONDS(FOUR, DIAMONDS),
            FIVE_DIAMONDS(FIVE, DIAMONDS),
            SIX_DIAMONDS(SIX, DIAMONDS),
            SEVEN_DIAMONDS(SEVEN, DIAMONDS),
            EIGHT_DIAMONDS(EIGHT, DIAMONDS),
            NINE_DIAMONDS(NINE, DIAMONDS),
            TEN_DIAMONDS(TEN, DIAMONDS),
            JACK_DIAMONDS(JACK, DIAMONDS),
            QUEEN_DIAMONDS(QUEEN, DIAMONDS),
            KING_DIAMONDS(KING, DIAMONDS),
            ACE_DIAMONDS(ACE, DIAMONDS),
    
            TWO_CLUBS(TWO, CLUBS),
            THREE_CLUBS(THREE, CLUBS),
            FOUR_CLUBS(FOUR, CLUBS),
            FIVE_CLUBS(FIVE, CLUBS),
            SIX_CLUBS(SIX, CLUBS),
            SEVEN_CLUBS(SEVEN, CLUBS),
            EIGHT_CLUBS(EIGHT, CLUBS),
            NINE_CLUBS(NINE, CLUBS),
            TEN_CLUBS(TEN, CLUBS),
            JACK_CLUBS(JACK, CLUBS),
            QUEEN_CLUBS(QUEEN, CLUBS),
            KING_CLUBS(KING, CLUBS),
            ACE_CLUBS(ACE, CLUBS),
    
            TWO_HEARTS(TWO, HEARTS),
            THREE_HEARTS(THREE, HEARTS),
            FOUR_HEARTS(FOUR, HEARTS),
            FIVE_HEARTS(FIVE, HEARTS),
            SIX_HEARTS(SIX, HEARTS),
            SEVEN_HEARTS(SEVEN, HEARTS),
            EIGHT_HEARTS(EIGHT, HEARTS),
            NINE_HEARTS(NINE, HEARTS),
            TEN_HEARTS(TEN, HEARTS),
            JACK_HEARTS(JACK, HEARTS),
            QUEEN_HEARTS(QUEEN, HEARTS),
            KING_HEARTS(KING, HEARTS),
            ACE_HEARTS(ACE, HEARTS),
    
            TWO_SPADES(TWO, SPADES),
            THREE_SPADES(THREE, SPADES),
            FOUR_SPADES(FOUR, SPADES),
            FIVE_SPADES(FIVE, SPADES),
            SIX_SPADES(SIX, SPADES),
            SEVEN_SPADES(SEVEN, SPADES),
            EIGHT_SPADES(EIGHT, SPADES),
            NINE_SPADES(NINE, SPADES),
            TEN_SPADES(TEN, SPADES),
            JACK_SPADES(JACK, SPADES),
            QUEEN_SPADES(QUEEN, SPADES),
            KING_SPADES(KING, SPADES),
            ACE_SPADES(ACE, SPADES);
    
            public final Rank rank;
    
            public final Suit suit;
    
            Card(Rank rank, Suit suit) {
                this.rank = rank;
                this.suit = suit;
            }
        }
    }
    

提交回复
热议问题