poker hands

Started by
6 comments, last by nprz 18 years, 8 months ago
Does anyone know what a good system would be for finding out the relative strength of two or more poker hands? Currently I have the cards defined like this:


#define AceClubs 0
#define TwoClubs 1
#define ThreeClubs 2
#define FourClubs 3
#define FiveClubs 4
#define SixClubs 5
#define SevenClubs 6 
...

#define AceSpades 13
#define TwoSpades 14
#define ThreeSpades 15

...


I initially was just going to check from a royal flush down to high cards, but this seems like a slow method and i don't know how to accomdate for kickers.
Advertisement
There's a few libraries on sourceforge to deal with this. There's also a formal method, whose name eludes me at the moment, but google should remember.

Personally, I just checked for straight, 4oak, 3oak, pair, high, flush. Ranked by card, and repeated until no cards remained. The would provide a list of 'stuff' the hand had. Compare was a bit easier by simply comparing the best 'stuff' until one hand won [or ran out of stuff].
I coded a poker game once. I kept track of every card in array with 52 elements. Then to score each hand I wrote parsing functions to go through the players hand and figure out what they had. I had to code about 4 different functions to parse through all possible hands. Some hands can be handled by the same function like two of kind and 3 of kind.
The way I've done it years ago, was something like the following (may include pseudo code).

First of all, I used a struct to describe the cards, not just numbers:
struct CARD{  int rank; // 0 - 12  int suit; // 0 - 3 (enum clubs, hearts, diamonds, spades)}

and have an array for the deck, community cards and hands:
CARD hand[5];

The moment I had to determine the "score" I sorted them by rank or suit, whatever the best is to search for a pattern.
Let's say you want to search for one pair.
sort_by_rank(hand);for (int i = 1; i < 5; i++){  if (hand.rank == hand.rank)<br>    break; // we have found a pair !!!<br>}</pre><br>Or let's look for a straight.<br><pre>sort_by_rank(hand);<br><br>bool nFound = true;<br>for (int i = 1; i &lt; 5; i++)<br>{<br>  if (hand<span style="font-weight:bold;">.rank != hand.rank + 1)<br>  {<br>    nFound = false;<br>    break;<br>  }<br>}<br>if (bFound)<br>  // we have found a straight !!!</pre><br>These are not the best coding examples, but I'll guess you get the point.<br>The &#111;nly thing you have to do is come up with the algorithms that check for each winning pattern. Start with the "highest score" first, because &#111;nce you found a full house, there's no need to check for the existence of two pair for instance.<br><br>To come up with a single number to compare two hands, you could asign some score for each winning pattern + the total value of the cards:<br><br>Player 1: d9 h9 c5 c4 c3<br>Player 2: s9 c9 h8 c7 s2<br><br>Score Player 1: <br>  1000000 (one pair bonus) + <br>  10000 * 9 (card 1) +  <br>  1000 * 9 (card 2) +<br>  100 * 5 (card 3) + <br>  10 * 4 (card 4) + <br>  1 * 3 (card 5) = 1099543<br>Score Player 2: <br>  1000000 (one pair bonus) + <br>  10000 * 9 (card 1) +  <br>  1000 * 9 (card 2) +<br>  100 * 8 (card 3) + <br>  10 * 7 (card 4) + <br>  1 * 2 (card 4) = 1099872<br><br>Player 2 wins.<br><br>You just have to make sure the offsets of the pattern score (full house, straight etc.) and the individual cards are large enough to get the correct end score to compare.<br><br>Anyway, that's how I did it, it may give you some ideas.<br><br><!–EDIT–><span class=editedby><!–/EDIT–>[Edited by - WanMaster on July 30, 2005 10:44:05 PM]<!–EDIT–></span><!–/EDIT–>
Quote:Original post by WanMaster
struct CARD{  int rank; // 0 - 12  int suit; // 0 - 3 (enum clubs, hearts, diamonds, spades)}



Whoa.. You can pack a card into a byte... there's no need to pack 1 card into two ints! ...

for the card value, it'll be 2 - 14 .. so that means you still have the first four bits of the byte reserved, to where you can have something like
enum { SPADES=128, HEARTS=64, DIAMONDS=32, CLUBS=16 };


Anyway... I've been working on poker stuff for the past 6 months and I've got all of that down... best do it all with stl.
Another way to do it is to create an array with 13 elements. Then go through every card in your hand, add one to the corresponding position in the array. For instance, if there's a 5 in your hand, you'd add one to array[4], if you had a jack, add one to array[10], and so on. Then sort this array. If array[0] == 4, you have 4 of a kind. If array[0] == 3 && array[1] == 2, it's a full house. Flushes and straights have to be handled differently, though.
I like adamb's method; it's sort of like implicitly radix-sorting the cards, actually. Going off of that, we can do something along these lines (not tested and can probably be improved quite a bit):

// First I'll just create the card and hand concepts - the deck is up to you :)enum {ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, RANK_COUNT} Rank;enum {CLUB, DIAMOND, HEART, SPADE, SUIT_COUNT} Suit;const int CARD_COUNT = Rank::RANK_COUNT * Suit::SUIT_COUNT;class Card {  char id;  public:  Rank rank() { return Rank(id / SUIT_COUNT); }  Suit suit() { return Suit(id % SUIT_COUNT); }  Card(char id) : id(id) {    if (id < 0 || id >= CARD_COUNT) throw std::exception("invalid card");  }}// Trust me, this is much nicer than passing arrays around directlyconst int HAND_SIZE = 5;struct Hand { Card[HAND_SIZE] cards; }int evaluate(const Hand& hand) {  // First, detect flushes  bool isFlush = true;  Suit flushType = hand.cards[0].suit();  for (int i = 1; i < HAND_SIZE; ++i) {    if (hand.cards.suit() != flushType) isFlush = false;  }  // Now find matching cards. We will add n^2 to a "match total" for each group  // of n matching cards; this correctly ranks all hands without flushes/straights  // relative to each other. First, tally cards of each type...  int cardCounts[Rank::RANK_COUNT] = {0};  for(int i = 0; i < HAND_SIZE; ++i) {    ++cardCounts[hand.cards.rank()];  }  // Then look through the ranks and accumulate the match total. At the same  // time, we will note the lowest and highest card; this lets us detect  // straights.  int matchTotal = 0;  int minCard = Rank::RANK_COUNT;  int maxCard = -1;  for(int i = 0; i < Rank::RANK_COUNT; ++i) {    matchTotal += cardCounts * cardCounts;    if (cardCounts != 0) {      // update min and max      if (i < minCard) minCard = i;      if (i > maxCard) maxCard = i;    }  }  // A straight is a hand with no pairs, triples or quads, such that the  // match total will be 5 (1*1 for each card), and such that the min and max  // cards are 4 apart.  bool isStraight = (matchTotal == HAND_SIZE && (maxCard - minCard) == (HAND_SIZE - 1));  // Finally, flushes rank above straights, which both rank between three of a  // kind (match total 11) and full house (match total 13). Clearly we need to  // space the match totals out a little :) We'll multiply the match count by  // 2, and add appropriate values (13 and 14) for straights or flushes (which  // cannot contain any matches, so that their match counts are 5). As it  // happens, a straight flush will then have value 27 + 10 = 37, putting it  // (correctly) ahead of a four of a kind with 34 ((4*4 + 1*1) * 2).  return 2*matchTotal + (isStraight ? 13 : 0) + (isFlush ? 14 : 0);  // TODO: account for kickers. Probably better to do that in a hand vs. hand  // comparison function, which would compare the two evaluate() results first  // and then look at each card from highest to lowest in each hand.}


On the other hand, maybe it's better to explicitly sort the hand by rank as a part of the evaluation - it's nice for display purposes, and then it's already done when you need to check kickers.
My partner that helped me with my poker game, did a method like WanMaster. Since it is texas hold 'em, it wasn't just finding what was the best of a 5 card hand, but rather which 5 out of 7 cards were best and what hand it was. Then compare it with all the other players.

It is actually complex (540 lines), but it is bug free and until it becomes a bottleneck, I will keep it.

It is kind of difficult to think of everything, such as ace has the highest value, but it can be the lowest in a straight (A-2-3-4-5). So if you are sorting the cards by rank, you need to be careful of this.

This topic is closed to new replies.

Advertisement