Saturday, April 6, 2019

Dave Computes Evaluating Poker Hands

Here's a simple algorithm for categorizing and sorting a 5-card poker hand, for the purpose of determining whether one hand is stronger than another.


Step 1:  Determine frequencies of each rank

Ranks range from 2 to 14, where Jack is 11, Queen is 12, King is 13, and Ace is 14.

Make an array rankFreqs of 15 integers, indexed from 0 to 14, so that rankFreqs[6] indicates the number of 6s in a particular hand. Positions 0 and 1 are ignored. The values in the array are first initialized to 0.

int[] rankFreqs = new int[15];

For each card in the hand, increment the value in rankFreqs corresponding to that card's rank.

for (Card card : hand)
  rankFreqs[card.getRank()]++;

For the following hand

Ace of Hearts Jack of Diamonds Jack of Clubs Seven of Clubs Queen of Hearts 

rankFreqs contains [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 1, 0, 1].


Step 2:  Sort the hand, first by rank frequency, and then by rank

In the course of sorting the hand, card x should precede card y if x's rank occurs more often than y's. If both ranks occur the same number of times, then x should precede y if x's rank is greater than y's.

For the hand above, J precedes A (because there are more jacks than aces) and Q precedes 7 (because there are the same number of Qs and 7s, and Q is higher than 7). Here's an implementation using selection sort.

for (int i = 0; i < hand.length; i++)
{
  for (int j = i + 1; j < hand.length; j++)
  {
    int ranki = hand[i].getRank();
    int rankj = hand[j].getRank();
    int freqi = rankFreqs[ranki];
    int freqj = rankFreqs[rankj];
    if (freqj > freqi || (freqj == freqi && rankj > ranki))
      swap(hand, i, j);
  }
}

After sorting, the hand contains:

Jack of Diamonds Jack of Clubs Ace of Hearts Queen of Hearts Seven of Clubs 


Step 3:  Test for a flush

Simply determine that all 5 cards in the hand have the same suit. No loop required.

boolean flush =
  hand[0].getSuit() == hand[1].getSuit() &&
  hand[0].getSuit() == hand[2].getSuit() &&
  hand[0].getSuit() == hand[3].getSuit() &&
  hand[0].getSuit() == hand[4].getSuit();


Step 4:  Test for a straight

Straights are just as easy, except that we need to test for 5-high straights. These are currently mis-sorted as A5432 and must be re-sorted as 5432A. For any other straight, we just need to test that each card's rank is one higher than the next.

boolean straight;
if (hand[0].getRank() == 14 && hand[1].getRank() == 5 &&
    hand[2].getRank() == 4  && hand[3].getRank() == 3 &&
    hand[4].getRank() == 2)
{
  straight = true;  //5-high straight
  Card ace = hand[0];
  hand[0] = hand[1];
  hand[1] = hand[2];
  hand[2] = hand[3];
  hand[3] = hand[4];
  hand[4] = ace;
}
else
{
  straight =
    hand[0].getRank() == hand[1].getRank() + 1 &&
    hand[1].getRank() == hand[2].getRank() + 1 &&
    hand[2].getRank() == hand[3].getRank() + 1 &&
    hand[3].getRank() == hand[4].getRank() + 1;
}


Step 5:  Categorize the hand, testing for the strongest categories first

Now that we've done the hard work, categorizing the hand is easy.  Assume we have integer constants defined for categories, such that STRAIGHT_FLUSH > FOUR_OF_A_KIND > FULL_HOUSE > FLUSH > STRAIGHT > THREE_OF_A_KIND > TWO_PAIR > PAIR > HIGH_CARD. We test for the strongest categories first.

if (straight && flush) return STRAIGHT_FLUSH;
if (hand[0].getRank() == hand[3].getRank()) return FOUR_OF_A_KIND;
if (hand[3].getRank() == hand[4].getRank()) return FULL_HOUSE;
if (flush) return FLUSH;
if (straight) return STRAIGHT;
if (hand[0].getRank() == hand[2].getRank()) return THREE_OF_A_KIND;
if (hand[2].getRank() == hand[3].getRank()) return TWO_PAIR;
if (hand[0].getRank() == hand[1].getRank()) return PAIR;
return HIGH_CARD;


Comparing Hands

Having sorted hands and identified their categories, it's easy to determine whether one hand beats another. If hand x's category is greater than y's, then x is the better hand. If they have the same category, then we compare the sorted hands, card by card. If x's card ever outranks y's, then x is the better hand. If all corresponding cards have the same ranks, then the hands are equal, presumably resulting in a split pot.

if (category1 > category2) return 1;  //hand 1 is stronger
if (category2 > category1) return 2;  //hand 2 is stronger
for (int i = 0; i < 5; i++)
{
  int rank1 = hand1[i].getRank();
  int rank2 = hand2[i].getRank();
  if (rank1 > rank2) return 1;  //hand 1 is stronger
  if (rank2 > rank1) return 2;  //hand 2 is stronger
}
return 0; //hands are equally strong




No comments:

Post a Comment