# Detecting poker hands

This topic is 4297 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi there, I'm writing a Poker game for fun but I've stumped myself with the problem of detecting what hands the player has. I don't know how to go about looking at a list of seemingly random cards and determining if they follow the rules for different hand rankings. Anybody have any ideas? Thanks.

##### Share on other sites
I would break it up into a smaller problem. How do you tell if the player has a pair? How do you tell if the player has two pair? How about three of a kind?

It's not too bad to tell if a player has a specific type of hand so maybe you can write functions like has_pair(), has_two_pair(), has_full_house() (which itself can be written in terms of has_pair() and has_three_of_kind(), etc. and then combine those into get_player_hands()

##### Share on other sites
I would probably do something simple like this. It obviously isn't the fastest way becuase there's a lot of duplicate checking. eg. when checking for 4 of a kind, you may find a pair, but you need to recheck for this later in the IsPair function. Like I said it's simple, slow, but should work. :)

Starting checking from the highest hand down to the lowest and return the appropriate rank/score.

int GetRankOfHand(CardList Cards)
{
if IsRoyalFlush(Cards) then
return TheRank
else if IsStraightFlush(Cards) then
return TheRank
else if .......
.....
......
else
Return RankOfHighestCard
}

IsRoyalFlush etc. check soley for the combination of cards that make up this hand. RankOfHighestCard returns the value of the highest card. Using this method you GetRankOfHand should always return the highest rank.

After checking all the hands, you can just compare the values returned to see who is the winner. Or let your AI use this value to decide how to proceed.

There's a good page here that describes the various hands you need to check for.. http://www.pagat.com/vying/pokerrank.html

Hope it's of some value to you.

fig

##### Share on other sites
Damn, I took 15 minutes to write that? Must be a timezone thing. :)

##### Share on other sites
Quote:
 Original post by figAfter checking all the hands, you can just compare the values returned to see who is the winner. Or let your AI use this value to decide how to proceed.

You'd need to return more than just the rank of the highest card, imagine this situation.
Player A has: AK752
Player B has: AJ843

Returning just the rank of would indicate that both players have an ace high, but wouldn't show that Player A beats B because he has an ace with a king kicker vs. B's ace with a jack kicker

##### Share on other sites
If the ranks come out the same for 2 or more hands you can always write a function that resolves ties whch just checks off the highest cards in order for each of the players.

##### Share on other sites
I've written a few card games in the past. And it does boil down to breaking them down into smaller test functons.

Make sure the cards are represented internally in a way that allows you to work out thier face value and suit (remember you have to account for flushes), test the values of the cards for patterns, and also the suit of the cards for flushes. keep and work on a copy of the hand, that you refresh fo each test, you may need alter values while testing to discount them

Then sort the card values in order(Ascending or decending, up to you). Test for the possible types of hand, starting with Royal flush, Str Flush, 4 of a kind etc.

Its easier to test the numerical values 1st then the suit values. If you test for a straight, just sort the numbers in order, and ensue they follow in sequence, 1,2,3,4,5 if the test succeeds, its a straight, then test the suit and if all the same upgrade the hand value to a stright flush.

If you find a set of cards meet 1 of those test conditions, assign a value for that type of hand (10 for RF, 9 for SF etc) and then compare hands, if the hands match, check for the kickers and return a value for that, maybe even a 2nd,3rd and 4th kicker value in the case of a high card hand.

Watch out for a couple of oddities, don't confuse 3 of a kind with a full house, and in 2 pair make sure the highest pair count, then the lower pair, but also there's a slim chance the kicker might count in a rated hand. If you work from a possible high hand down this should not happen.

You also need to be careful you check for a tie, its rare but it happens and you can find yourself iterating out of the hand if you don't check for it.

There's a load of rules you can impliment to speed the test up if running on a low speed system, for example if you have a flush,or straight, you can't have a full house or trio or pair, but you might as well just run through each test till you've done and take the highest value you find.

If you follow a simple process of checking for each type of hand, you can't go too far wrong.

##### Share on other sites

Hey, thanks a lot guys. I think I got it figured out now.

For the player I'm detecting number of possible outs and their probabilities of occuring by the river. Incorporating that into detecting the player's hand was really wracking my brains. Thanks again.

##### Share on other sites
Pokersource is a good starting point. :)

Its strength AND weakness is the fact that it will evaluate a poker hand given an arbitrary number of cards (ie, 5 cards, 6 cards, or 7 cards)

Due to this "feature" it is not well optimised. Specifically, the LAST evaluation is for "high card" hands which unfortunately is THE most common hand to hold given an even distribution of legal inputs. These hands can be detected and handled much sooner in the logic chain via a single table lookup if you know how many cards you are dealing with priori.

Still, its very good as is.

##### Share on other sites
I developed PokerVitals (pokervitals.com), and it needed a very high speed evaluator (had to evaluate thousands of hands a second), here are some things to keep in mind.

Remember that the best way to do this is evaluate the hand to a number. The larger the number , the better the hand, bearing this in mind you now want a way to ensure that the better hand always evaluates to a higher number.

So first, as people mentioned you want to figure out what the "best hand" out of the 5 cards are. I used several semi-propiatary (because I developed this under contract I cannot share it), ways to detect which hand is which. I will though, indicate that an optimized sorting alogorithm for straights is important, as well as detecting if a flush is present (this is important). When you have more then 5 cards to evaluate, I usualy did this by checking every possiable 5 card hand combination, and choosing the highest number out of the bunch.

Secondly, bear in mind how numbers are stored. I used the highest order byte or so to store a toggle partaining to what the hand was. For instance, if the highest bit was a 1, it is a straight flush, next bit was a 4 of a kind, then a full house, flush, straight, trips, 2pair, and then the pair bit. Because of how this works internaly, this guarentees that the proper hand always beats a lesser type. The rest of the numbers should then mearly store the strength of that particular hand. So now once you know this, you are going to want to figure out a way to ORDER whats left of the hand together. That is for full houses the trip cards come before the pair, on a flush the highest card comes first, same for the straight , etc. That should go into your second byte or so, and the result should be an integer that correctly vouches for the strength of the hand.

##### Share on other sites
I have done this before, and I agree with only part of what PaulCesar said. The part I don't agree with is testing each set of 5 cards and picking the best. It can be done faster. The function I wrote only works for 5, 6 or 7 cards of which any five might be the best (you'll need something different for games like Omaha, where only certain sets of five cards can be used). Representing hand strength as an integer is a very good idea. Because most computers these days have at least 32-bit integers, there is plenty of space to encode the hand's strength in there. I use the top six bits to represent the "class" of the hand (straight flush, 4 of a kind, full house, etc.) Then I have two fields of 13 bits each to untie. I use those two fields a little differently depending on the class. For instance, in a single pair, I use the first field to mark which pair it is (setting the corresponding bit to 1) and the second field to represent the three kickers, one bit for each.

You can use all sorts of bit tricks to make your function fast. For instance, you can start by building four 13-bit integers (one for each suit) indicating which cards you have. If you do a bitwise OR operation on all four, you can use the result as an index into a lookup table (8192 entries) that tells you whether you have a straight, and what's the best straight that you have. Actually, you can even store the full score in the table, and just return it, as long as you know you don't have a straight flush.

There are other bit representations you can use, like using four more bits to represent the four suits, so a card would be represented by a 17-bit integer with two bits set (one for the suit and one for the kind). As you read in the cards, you keep three integers that contain bits 0, 1 and 2 of the sum of each bit, now if bit 2 is set for any of the kinds, you have 4 of a kind. If bit 2 is set for a suit and bit 0 or bit 1 are also set for that suit, you have a flush. You can OR all three accumulators and take the lower 13 bits to see if you have a straight (the trick that I explained in the paragraph above). Actually, I seem to remember that this method was faster.

I hope that wasn't too too cryptic.

Oh! Actually you can evaluate a few *million* hands a second with these methods.

##### Share on other sites
I just wrote this for you. I can't guarantee 100% that it works. Let me know if you find a case where it doesn't, and I'll fix it. The code deals with sets of 5, 6 or 7 cards of which the 5 best play.

#include <iostream>enum Suit {Hearts=0x02000, Diamonds=0x4000, Spades=0x8000, Clubs=0x10000};enum Kind {Two=0x1,Three=0x2,Four=0x4,Five=0x8,Six=0x10,Seven=0x20,Eight=0x40,Nine=0x80,Ten=0x100,Jack=0x200,Quenn=0x400,King=0x800,Ace=0x1000};enum Hand {HighCard=0<<26, Pair=1<<26, TwoPair=2<<26, ThreeOfAKind=3<<26, Straight=4<<26, Flush=5<<26, FullHouse=6<<26, FourOfAKind=7<<26, StraightFlush=8<<26};unsigned straight_table[8192]={0};unsigned top_five_bits[8192];unsigned top_four_bits[8192];unsigned top_three_bits[8192];unsigned top_two_bits[8192];unsigned top_bit[8192];void init_tables(){  unsigned bit_count[8192];  bit_count[0]=0;  for(int i=1;i<8192;++i)    bit_count[i]=(i&1)+bit_count[i>>1];  for(int i=0;i<8192;++i){    int j=i;    while(bit_count[j]>5)      j&=j-1;    top_five_bits[i]=j;    if(bit_count[j]>4)      j&=j-1;    top_four_bits[i]=j;    if(bit_count[j]>3)      j&=j-1;    top_three_bits[i]=j;    if(bit_count[j]>2)      j&=j-1;    top_two_bits[i]=j;    if(bit_count[j]>1)      j&=j-1;    top_bit[i]=j;  }  for(int i=0;i<8192;++i){    int j=i<<1;    j|=j>>13;    straight_table[i]=top_bit[j&(j>>1)&(j>>2)&(j>>3)&(j>>4)];  }}void print_binary_13(unsigned v){  for(unsigned t=1u<<12;t;t>>=1)    std::cout << "01"[(t&v)!=0];}unsigned evaluate_hand(unsigned const *card, unsigned n_cards){  // Compute the three bits that represent the counter of each bit position  unsigned b0,b1,b2;  b0=card[0];  b1=b0&card[1];  b0^=card[1];  b1|=b0&card[2];  b0^=card[2];  b2=b1&b0&card[3];  b1=(b1|(b0&card[3]))&~b2;  b0^=card[3];  for(int i=4;i<n_cards;++i){    b2|=b1&b0&card[i];    b1=(b1|(b0&card[i]))&~(b1&b0&card[i]);    b0^=card[i];  }  // Deal with flushes and straight flushes  unsigned flush=b2&(b1|b0);  if(flush){    unsigned flush_set=0;    for(int i=0;i<n_cards;++i){      if(card[i]&flush)        flush_set|=card[i];    }    flush_set&=0x1fff;    if(straight_table[flush_set])      return StraightFlush|straight_table[flush_set];    return Flush|top_five_bits[flush_set];  }  // Flushes are taken care of, so forget about suits  b2&=0x1fff;  if(b2)    return FourOfAKind|b2;  b1&=0x1fff;  b0&=0x1fff;  unsigned straight=b2|b1|b0;  if(straight_table[straight]){    return Straight|straight_table[straight];  }  unsigned two_of_a_kind=b1;  if(!two_of_a_kind)    return HighCard|top_five_bits[b0];  unsigned three_of_a_kind=two_of_a_kind&b0;  if(three_of_a_kind){    three_of_a_kind=top_bit[three_of_a_kind];    two_of_a_kind&=~three_of_a_kind;    if(two_of_a_kind)      return FullHouse|(three_of_a_kind<<13)|top_bit[two_of_a_kind];    return ThreeOfAKind|(three_of_a_kind<<13)|top_two_bits[b0&~three_of_a_kind];  }  unsigned top_pair=top_bit[b1];  if(b1==top_pair)    return Pair|(b1<<13)|top_four_bits[b0];  unsigned two_pair=top_two_bits[b1];  return TwoPair|(two_pair<<13)|top_three_bits[(b1|b0)&~two_pair];}void print_value(unsigned v){  char *name[9]={"High card", "Pair", "Two pair", "Three of a kind", "Straight", "Flush", "Full house", "Four of a kind", "Straight flush"};  std::cout << name[v>>26] << ' ';  print_binary_13(v>>13);  std::cout << ' ';  print_binary_13(v);  std::cout << std::endl;}void print_card(unsigned c){  int s;  for(s=0;s<4;++s)    if((1u<<(s+13))&c)      break;  int k;  for(k=0;k<13;++k)    if((1u<<k)&c)      break;  std::cout << "HDSC"[s] << "23456789TJQKA"[k];}void print(unsigned const *card, unsigned n_cards){  for(unsigned i=0;i<n_cards;++i){    print_card(card[i]);    std::cout << ' ';  }  print_value(evaluate_hand(card,n_cards));}int main(){  init_tables();  unsigned k[9]={0};  unsigned d[52];  for(int i=0;i<4;++i)    for(int j=0;j<13;++j)      d[i*13+j]=(1u<<(i+13))|(1u<<j);  unsigned c[7];  for(int i0=0;i0<52;++i0){    c[0]=d[i0];    for(int i1=i0+1;i1<52;++i1){      c[1]=d[i1];      for(int i2=i1+1;i2<52;++i2){        c[2]=d[i2];        for(int i3=i2+1;i3<52;++i3){          c[3]=d[i3];          for(int i4=i3+1;i4<52;++i4){            c[4]=d[i4];            for(int i5=i4+1;i5<52;++i5){              c[5]=d[i5];              for(int i6=i5+1;i6<52;++i6){                c[6]=d[i6];                k[evaluate_hand(c,7)>>26]++;              }            }          }        }      }    }  }  unsigned sum=0;  for(int i=0;i<9;++i){    sum+=k[i];    std::cout << k[i] << std::endl;  }  std::cout << "Total: " << sum << std::endl;}

[EDIT: Removed a few lines that printed debugging information.]
[EDIT: Fixed a few bugs and changed main() to go through all 7-card combinations. Now I am confident that at least the basic classification is correct, since the total count agrees with the one I found on Wikipedia. I got over 19 million hand evaluations per second on my laptop. Sorry for posting buggy code, though.]
[EDIT: Yet another bug fix.]

[Edited by - alvaro on April 10, 2006 8:25:14 PM]

##### Share on other sites
Quote:
 Original post by Rockoon1Pokersource is a good starting point. :)... Specifically, the LAST evaluation is for "high card" hands which unfortunately is THE most common hand to hold given an even distribution of legal inputs. These hands can be detected and handled much sooner in the logic chain via a single table lookup if you know how many cards you are dealing with priori.

How would you index such a look-up table. It seems like it would require 252 entries.

Quote:
 Original post by PaulCesar... an optimized sorting alogorithm for straights is important, as well as detecting if a flush is present (this is important). When you have more then 5 cards to evaluate, I usualy did this by checking every possiable 5 card hand combination, and choosing the highest number out of the bunch.

That is really slow. There is no need to sort, or to check every possible 5 card hand. There are much better ways.

##### Share on other sites
Quote:
Original post by JohnBolton
Quote:
 Original post by Rockoon1Pokersource is a good starting point. :)... Specifically, the LAST evaluation is for "high card" hands which unfortunately is THE most common hand to hold given an even distribution of legal inputs. These hands can be detected and handled much sooner in the logic chain via a single table lookup if you know how many cards you are dealing with priori.

How would you index such a look-up table. It seems like it would require 252 entries.

No table should have more than 2^13 entries. One bit for each rank.

##### Share on other sites
Quote:
Original post by Rockoon1
Quote:
Original post by JohnBolton
Quote:
 Original post by Rockoon1... Specifically, the LAST evaluation is for "high card" hands which unfortunately is THE most common hand to hold given an even distribution of legal inputs. These hands can be detected and handled much sooner in the logic chain via a single table lookup if you know how many cards you are dealing with priori.

How would you index such a look-up table. It seems like it would require 252 entries.

No table should have more than 2^13 entries. One bit for each rank.

Then how would you rule out pairs and three-of-kind, etc. (with a single look-up)?

##### Share on other sites
Quote:
Original post by JohnBolton
Quote:
 Original post by Rockoon1No table should have more than 2^13 entries. One bit for each rank.

Then how would you rule out pairs and three-of-kind, etc. (with a single look-up)?

You can bitwise-OR together the kinds of the cards (represented as a 13-bit integer with a single bit set) and use a single look-up to determine if any bits are "missing" (indicating at least a pair), and detecting straights at the same time. You still need to check for flushes.

##### Share on other sites
I figured out one way to detect flushes at the same time. Here's some code for detecting if a hand of 5 to 7 cards is high card only:
#include <iostream>enum Suit {Hearts=0x10000000, Diamonds=0x01000000, Spades=0x00100000, Clubs=0x00010000};enum Kind {Two=0x1,Three=0x2,Four=0x4,Five=0x8,Six=0x10,Seven=0x20,Eight=0x40,Nine=0x80,Ten=0x100,Jack=0x200,Quenn=0x400,King=0x800,Ace=0x1000};bool not_high_card[3][8192];void init_tables(){  unsigned bit_count[8192];  bit_count[0]=0;  for(unsigned i=1;i<8192;++i)    bit_count[i]=(i&1)+bit_count[i>>1];  for(unsigned i=0;i<8192;++i){    unsigned j=i<<1;    j|=j>>13;    for(unsigned k=5;k<=7;++k)      not_high_card[k-5][i]=((j&(j>>1)&(j>>2)&(j>>3)&(j>>4))!=0)||(bit_count[i]!=k);  }}// 5 <= n_cards <= 7bool is_high_card(unsigned *card, unsigned n_cards){  unsigned sum=0x33330000;  for(unsigned i=0;i<n_cards;++i)    sum+=card[i];  return !((sum&0x88880000u) || not_high_card[n_cards-5][sum&0x00001fff]);}int main(){  init_tables();  unsigned k[2]={0,0};  unsigned d[52];  for(int i=0;i<4;++i)    for(int j=0;j<13;++j)      d[i*13+j]=(1u<<(i*4+16))|(1u<<j);  unsigned c[7];  for(int i0=0;i0<52;++i0){    c[0]=d[i0];    for(int i1=i0+1;i1<52;++i1){      c[1]=d[i1];      for(int i2=i1+1;i2<52;++i2){        c[2]=d[i2];        for(int i3=i2+1;i3<52;++i3){          c[3]=d[i3];          for(int i4=i3+1;i4<52;++i4){            c[4]=d[i4];            for(int i5=i4+1;i5<52;++i5){              c[5]=d[i5];              for(int i6=i5+1;i6<52;++i6){                c[6]=d[i6];                k[is_high_card(c,7)]++;              }            }          }        }      }    }  }  std::cout << "High card only: " << k[1] << std::endl;  std::cout << "At least a pair: " << k[0] << std::endl;  std::cout << "Total: " << (k[0]+k[1]) << std::endl;}

It checks around 115 million hands per second on an Opteron 250 (that's around 20 clocks per function call).

##### Share on other sites
Quote:
Original post by alvaro
Quote:
Original post by JohnBolton
Quote:
 Original post by Rockoon1No table should have more than 2^13 entries. One bit for each rank.

Then how would you rule out pairs and three-of-kind, etc. (with a single look-up)?

You can bitwise-OR together the kinds of the cards (represented as a 13-bit integer with a single bit set) and use a single look-up to determine if any bits are "missing" (indicating at least a pair), and detecting straights at the same time. You still need to check for flushes.

Yeah, your're right, (but it is not one lookup). The test would look something this I guess (roughly):
    int hearts = hand.GetHearts(); // 13 bits, one for each rank    int spades = hand.GetSpades(); // 13 bits, one for each rank    int clubs = hand.GetClubs(); // 13 bits, one for each rank    int diamonds = hand.GetDiamonds(); // 13 bits, one for each rank    int ranks = hearts | spades | clubs | diamonds;    int pairs = hearts & spades & clubs & diamonds;    if ( pairs == 0 &&         !straight[rank] &&         !flush[hearts] &&         !flush[spades] &&         !flush[clubs] &&         !flush[diamonds] )    {        hand = high_card;    }
I'm going to check if this speeds up the pokersource algorithm.

##### Share on other sites

I have the system down for detecting the hand the player has,

but I'm having a hardtime with determining outs.

does anyone know anything about detecting outs in poker?

##### Share on other sites
Quote:
Original post by JohnBolton
Quote:
Original post by alvaro
Quote:
Original post by JohnBolton
Quote:
 Original post by Rockoon1No table should have more than 2^13 entries. One bit for each rank.

Then how would you rule out pairs and three-of-kind, etc. (with a single look-up)?

You can bitwise-OR together the kinds of the cards (represented as a 13-bit integer with a single bit set) and use a single look-up to determine if any bits are "missing" (indicating at least a pair), and detecting straights at the same time. You still need to check for flushes.

Yeah, your're right, (but it is not one lookup). The test would look something this I guess
[...]

You didn't read my code, did you? The relevant function is this:
// 5 <= n_cards <= 7bool is_high_card(unsigned *card, unsigned n_cards){  unsigned sum=0x33330000;  for(unsigned i=0;i<n_cards;++i)    sum+=card[i];  return !((sum&0x88880000u) || not_high_card[n_cards-5][sum&0x00001fff]);}

##### Share on other sites
Quote:
 Original post by alvaroYou didn't read my code, did you? The relevant function is this:// 5 <= n_cards <= 7bool is_high_card(unsigned *card, unsigned n_cards){ unsigned sum=0x33330000; for(unsigned i=0;i

no offense.. but yuk ;-)

##### Share on other sites
Quote:
Original post by Rockoon1
Quote:
 Original post by alvaroYou didn't read my code, did you? The relevant function is this:// 5 <= n_cards <= 7bool is_high_card(unsigned *card, unsigned n_cards){ unsigned sum=0x33330000; for(unsigned i=0;i

no offense.. but yuk ;-)

Yeah, it's not very readable, but it's quite clever. The trick to detect flushes was an idea of a friend of mine. Did you figure out how it works?

##### Share on other sites
Quote:
Original post by alvaro
Quote:
Original post by Rockoon1
Quote:
 Original post by alvaroYou didn't read my code, did you? The relevant function is this:`// 5 <= n_cards <= 7bool is_high_card(unsigned *card, unsigned n_cards){ unsigned sum=0x33330000; for(unsigned i=0;i

no offense.. but yuk ;-)

Yeah, it's not very readable, but it's quite clever. The trick to detect flushes was an idea of a friend of mine. Did you figure out how it works?

I would say that your card representation is holding you back.

This problem isnt card-centric, its hand-centric!

Ie, a 'hand' (a collection of 'cards') should be encoded for easy evaluation. A 'card' is just a special-case of a 'hand' that happens to have only 1 card in it.

I believe 'Pokersource' encodes a HAND thusly:

13 bits representing the clubs in the hand
13 bits representing the diamonds in the hand
13 bits representing the hearts in the hand
13 bits representing the spades in the hand

In each case, a 1 bit represents a card included in the hand, and a 0 bit represents a card excluded in the hand.

There is no doubt in my mind that this representation (or one very similar to it) is optimal for the purposes of poker software (other card games could easily have different particularities)

Some things of note 'in the grand scheme' of things:

A 'hand' under this representation always consumes a precise structure regardless of how many cards it has.

Additionally there are no sorting issues at all, which often plague card-centric encodings.

Still further, it is clear that two 'hands' can by bitwise OR'd together to combine them. Card-centric encodings must concatenate strings of cards together, which is an expensive operation.

Cards can be added or removed from a hand without any iterating or memory reogranization.

And finally, this hand encoding makes rank-based hashing extremely efficient. Eliminating any need to iterate over the cards in a hand for that purpose. The hashes needed to detect flushes and straight flushes are right there already, and you are only a few OR's a way from a hash for straights and highcards.

##### Share on other sites

This topic is 4297 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.