i don't know if you guys have seen celebrity poker, but i just can't get enough. i want to write a poker hand evaluation algorithm and was wondering if any of you folks had done already see one.

i was a good stats student but it's been like 16 years...

    There was one in my old GUIs book, but I don't know where that book is right now.

      alrighty...i guess i'll start piecing this together bit by bit. right now, i'm working on an algorithm to evaluate a hand in texas holdem. this page:

      http://hopper.unco.edu/course/CS102/week8.html

      suggests writing a function to check for each of the possible winning hands, checking for the highest hands first...

      royal flush
      straight flush
      four of a kind
      full house
      flush
      straight
      three of a kind
      two pair
      pair

      we would also incorporate code into these 9 functions to determine what the highest card is.

      anybody have any suggestions about how to do this?

        That's a decent way to do it, however I feel it would be more generic, though a tad more programming to do it like this.

        First define two arrays:

        <?php
        $cards = array('A','1','3','4','5','6','7','8','9','10','J','Q','K');
        $suits = array('C','D','H','S');
        ?>

        Then create a 13x4 matrix. The columns represent the cards you should give them names accordingly to make the code more readable. The rows represent the suits. This code does the trick

        <?php
        /*array*/ function buildEmptyDeck($cards,$suits) {
            $retval = array();
            foreach($cards as $card) {
                $retval[$card] = array();
                foreach($suits as $suit)
                    $retval[$card][$suit] = 0;
            } //end foreach
            return $retval;
        } //end buildEmptyDeck
        ?>

        Then you should generate some hands. This code does the trick, with no repeats

        <?php
        /*array*/ function buildHands($numHands,$numCards,$cards,$suits) {
            $i = 1;
            $cardLimit = count($cards)-1;
            $suitLimit = count($suits) - 1;
            $retval = array();
            $temp = array();
            mt_srand();
            for($i=0;$i<$numHands;$i++) {
                $j = 0;
                $retval[$i] = array();
                while($j < $numCards) {
                    $card = $cards[mt_rand(0,$cardLimit)] . ' ' . 
                        $suits[mt_rand(0,$suitLimit)];
                    if(array_search($card,$temp) === FALSE) {
                        $temp[] = $card;
                        $retval[$i][] = $card;
                        $j++;
                    } //end if
                } //end while
            } //end for
        
        return $retval;
        } //end buildHands
        ?>

        Now we need to "deal" these hands from the deck. We'll do this by placing markers into the deck array for each used card that signify whose hand the card is in. The code for this is pretty simple

        <?php
        /*void*/ function dealHands(&$deck,$hands) {
            $i=1;
            foreach($hands as $hand) {
                foreach($hand as $card) {
                    $acard = explode(' ',$card);
                    $deck[$acard[0]][$acard[1]] = $i;
                } //end foreach
                $i++;
            } //end foreach
        } //end dealHands
        ?>

        So let's recap what we have so far. In the first step we specified our available cards and suits. In the second step we created an empty deck and in the third step we created some hands. In the fourth step we "dealt" the hands out of the deck.

        To explain why I have done things this way to this point. First of all defining the cards and suits dynamically makes it possible for you to decide that you're not playing with a standard deck of cards, which means this portion of the program could be used to deal the initial hands for any card game including uno, phase 10, weed, etc.

        The next step will be writting the programming to pull the reward groups out of the deck array. I'll cover that in another post and this one is already getting very long and it's getting very late.

          With using the above listed method of dealing the cards here is a function that will get all the runs.

          <?php
          /*array*/ function getRuns($deck,$players) {
              $retval = array();
              foreach(array_keys($deck) as $card) {
                  for($i=1;$i<=$players;$i++) {
                      $run = 0;
                      foreach($deck[$card] as $key => $val) {
                          if($val == $i)
                              $run++;
                      } //end foreach
                      if($run > 1) {
                          $retval[] = array(
                              'player' => $i,
                              'card' => $card,
                              'length' => $run);
                      } //end if
                  } //end for
              } //end foreach
              return $retval;
          } //end getRuns
          ?>

          The rest of the program is left as an exercise to the reader.

            i can grok the whole card creator and the shuffle routine. my functions look like this (and forgive me, i haven been developing this in action script...there may be some crud still in here from that):

            // each card is array with 2 elements:
            // 0 is value (0-12)
            // 1 is suit (0-3)
            function CreateDeck() {
               global $numberOfDecks;
               global $arrDeck;
               // create a deck
               $arrDeck = Array();
               // for each suit...
               for ($i=0;$i<$numberOfDecks;$i++) {
                  for ($suit=0; $suit<4; $suit++) {
                     // for each card...
                     for ($tmpCardValue=0; $tmpCardValue<13; $tmpCardValue++) {
                        $tmpCard = Array();
                        $tmpCard[0] = $tmpCardValue;
                        $tmpCard[1] = $suit;
                        array_push($arrDeck, $tmpCard);
                     } // each card
                  }  // each suit
               } // each deck
            }
            
            
            function ShuffleDeck() {
               global $arrDeck;
               global $howManyShuffles;
            
               $numCards = sizeof($arrDeck);
            
               for ($shuffleTimes=0; $shuffleTimes<$howManyShuffles; $shuffleTimes++) {
                  $newShuffledDeck = Array();
                  for (i=0; i<numCards; i++) {
                     $cardsRemaining = sizeof($arrDeck);
                     if ($cardsRemaining > 1) {
                        // if there's more than one card
                        // pick one at random
                        $randomCard = getRandomInteger(0,($cardsRemaining-1));
                        // add it to the new deck
                        array_push($newShuffledDeck, $arrDeck[randomCard]);
                        // and delete it from the old one...
                        // NOT SURE I'VE USED THIS RIGHT...
                        array_splice($arrDeck, $randomCard,1);
                     } else {
                        // just take the last card
                        array_push(newShuffledDeck, $arrDeck[0]);
                     } // if last card
                  } // cards
                     $arrDeck = $newShuffledDeck;
               } //shuffleTimes
            } //ShuffleDeck()
            

            the beastie i'm really having trouble with is evaluating a poker hand...best 5 cards out of seven....kinda tricky. i'll put that in my next post...

              ok...this is my function to evaluate a texas hold'em poker hand (best 5 of 7). it seems really complicated to me but i think it's doing the trick. it does have a few problems:

              • highest card values are taken from table cards as well as hole (player's) cards.
              • it doesn't allow for a straight with aces low
              • anybody notice other problems??

              once again, i'm translating this from actionscript to php...forgive me if there are any bugs...

                
              // this function assumes that an arra of players objects // and an array of table cards has been defined // you call it with a player's number to // evaluate their hand function evaluateHand($intPlayer) { $arrHand = array_push($arrTableCards, $arrPlayers[intPlayer]); rsort($arrHand); $numCards = sizeof($arrHand); // check for flush $currentCardSeries = Array(); $boolFlush = false; $arrSuitCounts = Array(4); for ($i=0;$i<$numCards;$i++) { $intValue = $arrHand[i][0]; $intSuit = $arrHand[i][1]; if (!is_array($arrSuitCounts[$intSuit])) { $arrSuitCounts[$intSuit] = Array(); // this array stores two elements for each suit: // the number of cards belong to that suit and also // the value for highest value comparison // REMEMBER that the hand array has been sorted // by card valud in descending order so the first // value encountered will be the highest. $arrSuitCounts[$intSuit][0] = $intValue; // set the count for that suit to 1 $arrSuitCounts[$intSuit][1] = 1; } else { $arrSuitCounts[$intSuit][1]++ if ($arrSuitCounts[$intSuit][1] >= 5) { $boolFlush = true; $arrFlushCards = Array(); echo 'a flush! highest card is ' . $arrSuitCounts[$intSuit][0] . '<BR>'; $intHighestFlushCard = $arrSuitCounts[$intSuit][0]; // remember the cards of the hand for later... for($j=0;$j<$numCards;$j++) { if ($arrHand[$j][1] == $intSuit) { array_push($arrFlushCards, $arrHand[i]); } if (sizeof($arrFlushCards) >= 5) { break; } } // remember the cards of... break; } // if there are 5 card of any given suit } // if clause to create or increment $array element } // for each card // check for straight/straight flush... // this loop checks for a straight // straight could be: // cards 1 thru 5 // cards 2 thru 6 // cards 3 thru 7 // etc. $numCardsToCheckForStraight = ($numCards - 5) + 1; // each of these cards could be the highest card in a straight for ($i=0; $i < $numCardsToCheckForStraight; $i++) { $intValue = $arrHand[$i][0]; $intSuit = $arrHand[$i][1]; $currentCardSeries = Array(); array_push($currentCardSeries, $arrHand[$i]); // this will be true if the hand // has five consecutive card values $boolStraight = false; // assume these are true until proven false below $boolThisOneIsStraight = true; $boolStraightFlush = true; // we have one card...check the next 4 $howManyCardsToCheck = 4 // check the next 4 cards... for ($j=1;$j<=$howManyCardsToCheck;$j++) { if (($i+$j) > sizeof($arrHand)) { break; } if (($j != 1) && ($arrHand[$i+$j] == $arrHand[$i+$j-1])) { // if this card is not the first card // and has same value as the previous card // then our streak is not broken // but we must check one more card $howManyCardsToCheck++; } else if ($arrHand[$i+$j][0] != ($intValue-$j)) { // check if the straight is broken... $boolThisOneIsStraight=false; $boolStraightFlush = false; break; } else { // if our straight continues array_push($currentCardSeries, $arrHand[$i+$j]); // then check the suit if ($arrHand[$i+$j][1] != $intSuit) { $boolStraightFlush = false; } } } // check the next 4 cards if ($boolStraightFlush) { // we have a straight flush! // don't bother checking the others... // we can check out right here $intHighestCard = $intValue; if ($intHighestCard == 12) { // royal flush! $handValue = 9; } else { $handValue = 8; } echo "straight flush! high card is " + $intHighestCard; // store the hand info in the global players object $arrPlayers[$intPlayer].highestCard = $intHighestCard; $arrPlayers[$intPlayer].handValue = $handValue; $arrPlayers[$intPlayer].bestHand = $currentCardSeries; return $handValue; } else { // this particular series is not a straight flush... // be we need to keep checking...just in case // there IS a straight flush in here // if this one was a straight, let's remember it if ($boolThisOneIsStraight) { $boolStraight = true; if ($intValue > $intHighestStraightCard) { $intHighestStraightCard = $intValue; $arrStraightHand = Array(); $arrStraightHand = $currentCardSeries; echo 'straight! high card is ' + $intHighestCard; } else { echo 'another straight...but not the highest which is: ' + intHighestStraightCard; } } } // if straight flush... } // for each of the high cards // if we reach this point, our straight check // is completed. no straight flush exists // but a straight *might*. // we need to check for higher-ranked hands like full house, etc. // count how many of each value // we have... $arrValueCounts = Array(); for ($i=0;$i<$numCards;$i++) { intValue = $arrHand[$i][0]; intSuit = $arrHand[$i][1]; if (!$arrValueCounts[$intValue]) { $arrValueCounts[$intValue] = 1; } else { $arrValueCounts[$intValue]++; } } // check for 4 of a kind, full house // three of a kind, etc. in descending // card order from highest to lowest values $boolThreeOfAKind = false; $intPairs = 0; for ($i=12;$i>=0;$i--) { if ($arrValueCounts[$i] == 4) { // FOUR OF A KIND! we can stop looking $arrPlayers[$intPlayer].handValue = 7; $arrPlayers[$intPlayer].highestCard = $i; return 7; } else if ($arrValueCounts[$i]==3) { // if we have 3 of a kind // and we've already seen a pair // or another triple, then we have // FULL HOUSE! if ($boolThreeOfAKind) { $arrPlayers[$intPlayer].handValue = 6; $arrPlayers[$intPlayer].highestCard = $intHighestTriple; return 6; } else if ($intPairs > 0) { // FULL HOUSE! // we previous landed a pair // but our highest card is the // one for this triplet $arrPlayers[$intPlayer].handValue = 6; $arrPlayers[$intPlayer].highestCard = $i; return 6; } else { // we're not done checking yet // but we do have 3 of a kind. $boolThreeOfAKind = true; $intHighestTriple = $i; } // if 3 of a kind } else if ($arrValueCounts[$i]==2) { if ($boolThreeOfAKind) { // currently have a pair, have already seen a triple // FULL HOUSE! // highest card has already been set $arrPlayers[intPlayer].handValue = 6; $arrPlayers[intPlayer].highestCard = $intHighestTriple; return 6; } else { // if we get this far, we *might* // have already seen a pair // but there should be NO CHANCE // that the player has 3 of a kind if ($intPairs <= 0) { // no pairs yet seen, no 3 of a kind $intPairs++; $intHighestPair = $i; } else { $intPairs++; } } } // check for 4/3/2 of a kind } // for 12 to 0 // we have check for all higher hands...if user has // a flush, return that if ($boolFlush) { $arrPlayers[$intPlayer].handValue = 5; $arrPlayers[$intPlayer].highestCard = $intHighestFlushCard; return 5; } // otherwise, if we had a straight... if ($boolStraight) { $arrPlayers[$intPlayer].highestCard = $intHighestStraightCard; $arrPlayers[$intPlayer].handValue = 4; $arrPlayers[$intPlayer].bestHand = $arrStraightHand; echo 'straight selected as best possible hand'; return 4; } // 3 of a kind if ($boolThreeOfAKind) { $arrPlayers[$intPlayer].handValue = 3; $arrPlayers[$intPlayer].highestCard = intHighestTriple; return 3; } // 2 pair if ($intPairs == 2) { $arrPlayers[$intPlayer].handValue = 2; $arrPlayers[$intPlayer].highestCard = $intHighestPair; return 2; } // pair if ($intPairs == 1) { $arrPlayers[$intPlayer].handValue = 1; $arrPlayers[$intPlayer].highestCard = $intHighestPair; return 1; } // what a bad hand! $arrPlayers[$intPlayer].handValue = 0; $arrPlayers[$intPlayer].highestCard = $arrHand[0][0]; return 0; } // evaluateHand()

                i don't think it will be too difficult once i get the poker hand evaluation function finished. it's already a beast and it's not even finished yet.

                i'm still trying to nail down some particulars of the game. i have learned a couple of things:

                1) each players hand makes no distinction between pocket cards and cards on the board. your best possible hand wins...so if there was a royal flush sitting on the table, anybody who hasn't yet folded when that last card gets flipped will split the pot.

                2) in the event there is a tie, as many as 4 kicker cards must be considered! for example, if 2 players are tied with a pair of jacks, 3 kicker cards might possibly be considered!
                E.G.:
                player 1: JH, 10D
                player 2: JC, 9S

                board:
                JS, AD, KD, 2C, 3C

                each player has a pair of Jacks. A is first kicker, K is second kicker for both--then the 3rd kicker (if it is considered) would give the hand to player 1 whose 10 wins over the 9 of player 2. if nobody has any pairs or anything, you might have to consider 4 kicker cards!

                3) in cases where players are tied with 2 pairs, you have to consider both pairs--and then the kicker.

                4) tied full houses (however unlikely) means you put the triple first, then the pair.

                5) tied hands with two pairs each: compare higher pair, then lower pair, then kicker card.

                6) in a flush, you march right down through the flush, possibly comparing ALL of the cards.

                as you can imagine, all of this is complicating my function...i still need to go back and check for all these special cases.

                i'm still trying to find out some details about betting...like, if you only have 2 players left, is the button/dealer also the big blind?

                  I wouldn't write a single function for poker hand evaluation.

                  I would write a small function that decides if each type of hand exists in the cards dealt.

                  Then I would write a function that can break ties between the same hands.

                  Then I would call these functions with a control function.

                    Don't forget that if you're writing a Texas Holdum algo, it'll need to know how to determine the value of the partial hands at the beginning. I think you'll need to either calculate the math on the fly (slow) or make a lookup table of probability of completing given hands based on what's showing, how many other players there are, etc...

                      thanks guys!

                      drawmack:
                      it did occur to me to write a function to check for straight / four of a kind / flush, etc. but when i started to think about it, i got very confused. for instance, if you find a straight and you find a flush, how do you determine if it's a straight flush? or should i write a function to look specifically for straight flush? if i did write the straight flush alg., it seems like i would be dealing with a lot of redundant processing. not a big deal i guess because we're dealing with just a few hands and tiny arrays.

                      if you could help me think of a general approach to this, i would be grateful.

                      sxooter:
                      the artificial intelligence for bidding and the ability to create savvy computer opponents is next on my agenda, but i don't really even know where to begin! i've seen examples of the lookup table, and i think i can figure out the stats calculations based on a given hand, but i can't help but think this is going to be rather beastly as well. any chance you could offer some sagely advice?

                      also:
                      that evaluation routine above has a few issues. i've since written a better (but even more beastly) one.

                        write one function to check for each possible kind of hand.

                        You can build the functions up though.

                        For example
                        check for a straight with the straight function
                        check for a flush with the flush function
                        if they are both true execute the straight flush function

                          yep. in retrospect, this helps keep things conceptually simple. no gain really at all from a single function.

                          btw, if you are using multiple decks (which is not really done with texas holdem AFAIK), where does 5 of a kind fit in?

                          EDIT: Also complicating the situation is that all these checks for flushes/straight flushes/etc. must sometimes return not just true or false but also highest card and up to 4 kickers cards!

                            this page is also interesting...lots of probabilities for multiple decks 5 card / 7 card:

                            http://wizardofodds.com/games/pokerodd.html

                            PS: does anyone know how to comprensively test your functions?? i.e., programmatically feed a function called isRoyalFlush() with every possible royal flush combination with 7 cards to determine if it works??

                              Originally posted by sneakyimp
                              yep. in retrospect, this helps keep things conceptually simple. no gain really at all from a single function.

                              btw, if you are using multiple decks (which is not really done with texas holdem AFAIK), where does 5 of a kind fit in?

                              EDIT: Also complicating the situation is that all these checks for flushes/straight flushes/etc. must sometimes return not just true or false but also highest card and up to 4 kickers cards!

                              Didn't I say I'd write a tie breaking functions? That's where those things would be handeled. Though you could just have every function return an array.

                                Originally posted by sneakyimp
                                PS: does anyone know how to comprensively test your functions?? i.e., programmatically feed a function called isRoyalFlush() with every possible royal flush combination with 7 cards to determine if it works??

                                You wouldn't want to call it with every possible royal flush, you'd want to call it with evey possible card combination.

                                There are 52 cards in a deck, foreach hand you'll be using 7 cards - but 5 will be thrown away. You could do this one of two ways.

                                1) Nested loops

                                <?php
                                for($i=0;$i<52;$i++) {
                                    for($j=0;$j<52;$j++) {
                                        if($j != $i) {
                                            for($k=0;$k<52;$k++) {
                                                if($k != $i && $k != $j) {
                                                    ...
                                                    for($o=0;$o<52;$o++) {
                                                        if(!in_array($o,array($i,$j,$k,$l,$m,$n))) {
                                                            printHand(array($i,$j,$k,$l,$m,$n,$o));
                                                            if(callToYourFunction(array($i,$j,$k,$l,$m,$n,$o)))
                                                                echo 'This hand has a whatever.<br />';
                                                            else
                                                                echo 'This hand does not have a whatever.<br />';
                                                        } //end if
                                                    } //end for
                                                } //end if
                                             } //end for
                                ...
                                ?>

                                The second way is to do something similar but save all the possible combinations to a text file then on subsequent runs just use the already created text file.

                                  unfortunately, there are 133,784,560 different 7-card hands. i can't look at them all 😉

                                  i was hoping to write a function that would generate all the possible combinations of 7-cards royal flushes and then set the isRoyalFlush() function on it and if it didn't return TRUE every time, then i would know something was wrong and could figure it out. this is probably tantamount to writing a function that can recognize a royal flush, eh?