The HTML calling page and the PHP script below are a web application that calculates the probability a specific pre-flop hand will win given a number of players in Texas Hold’em.
Like with Perl this is not a great application to to build in PHP. Performance is almost as bad as Perl though the page that’s displayed looks good. For building web pages quickly PHP is a great tool. Intense processing is not PHP’s strong suit (get it suit).
index.html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Holdem Hand Value Calculator</title> </head> <BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#4F9D9D" VLINK="#4A0063" ALINK="#4F9D9D"> <form name="holdemForm" method="post" action="dealHands.php"> <CENTER> <TABLE> <TR> <TD>Select number of hands per game: <select id="handsPerGame" name="handsPerGame"> <option value=2>2</option> <option value=3>3</option> <option value=4>4</option> <option value=5>5</option> <option value=6>6</option> <option value=7>7</option> <option value=8>8</option> <option value=9>9</option> <option value=10 selected>10</option> </select> </td> </TR> <TR> <TD>Select number of hands to play: <select id="handsToPlay" name="handsToPlay"> <option value=1>1</option> <option value=10>10</option> <option value=100>100</option> <option value=1000>1000</option> <option value=10000>10000</option> <option value=100000>100000</option> <option value=1000000>1000000</option> </select> </td> </TR> <TR> <TD><BR> <INPUT TYPE="submit" VALUE="Submit"> <INPUT TYPE="reset" VALUE="Reset"><BR> </TD> </TR> </FORM> </TABLE> </CENTER> </BODY> </html> dealHands.php <?php $start=time(); echo "<style> table{ border-collapse:collapse; } table th{ color:#ffffff;background-color:#555555;border:1px solid #555555;padding:3px;vertical-align:top;text-align:center; } table td{ border:1px solid #d4d4d4;padding:5px;padding-top:7px;padding-bottom:7px;vertical-align:top; } .anumber {text-align: right} </style> "; define('TRUE', '1'); define('FALSE', '0'); define('ACE', '12'); define('DEUCE', '0'); define('THREE', '1'); define('FOUR', '2'); define('FIVE', '3'); define('SIX', '4'); define('SEVEN', '5'); define('EIGHT', '6'); define('NINE', '7'); define('TEN', '8'); define('JACK', '9'); define('QUEEN', '10'); define('KING', '11'); define('HANDTYPE', '10000000000'); define('FIRSTKICKER', '100000000'); define('SECONDKICKER', '1000000'); define('THIRDKICKER', '10000'); define('FOURTHKICKER', '100'); define('FIFTHKICKER', '1'); define('SFLUSH', '9'); define('QUADS', '8'); define('BOAT', '7'); define('FLUSH', '6'); define('STRAIGHT', '5'); define('TRIPS', '4'); define('TWOPAIR', '3'); define('ONEPAIR', '2'); define('HIGHCARD', '1'); define('UNKNOWN', '0'); #$deck = array("2c", "2d", "2h", "2s", #"3c", "3d", "3h", "3s", #"4c", "4d", "4h", "4s", #"5c", "5d", "5h", "5s", #"6c", "6d", "6h", "6s", #"7c", "7d", "7h", "7s", #"8c", "8d", "8h", "8s", #"9c", "9d", "9h", "9s", #"Tc", "Td", "Th", "Ts", #"Jc", "Jd", "Jh", "Js", #"Qc", "Qd", "Qh", "Qs", #"Kc", "Kd", "Kh", "Ks", #"Ac", "Ad", "Ah", "As"); $cardRank=array("2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A"); $handsToPlay=$_POST["handsToPlay"]; $handsPerGame=$_POST["handsPerGame"]; $highCard=0; function getDeck(&$shuffDeck){ $shuffDeck[0]=rand(0, 51); # first card is easy to deal just a random number # After the first card we need to check that the card has not been dealt already for($cardIndex=1;$cardIndex<52;++$cardIndex){ while(TRUE){ $noMatch=TRUE; $randHold=rand(0, 51); for($dealtCardIndex=0;$dealtCardIndex<$cardIndex;++$dealtCardIndex){ if($shuffDeck[$dealtCardIndex]==$randHold){ $noMatch=FALSE; break; } } if($noMatch==TRUE){ $shuffDeck[$cardIndex]=$randHold; break; } } } } function deal($handsPerGame, &$handCards, &$suitedHandsDealt, &$suitedHandsWon, &$unsuitedHandsDealt, &$unsuitedHandsWon){ global $cardRank; $handIsSuited=array(); $handValue=array(); $flop=array(); $handKey=array(); $shuffDeck=array(); getDeck($shuffDeck); $cardNo=0; for($cardIndex=0;$cardIndex<2;++$cardIndex){ for($playerIndex=0;$playerIndex<$handsPerGame;++$playerIndex){ $handCards[$playerIndex][$cardIndex]=$shuffDeck[$cardNo++]; } } #global $deck; #for($i=0;$i<$handsPerGame;$i++){ #$card1=$deck[$handCards[$i][0]]; #$card2=$deck[$handCards[$i][1]]; #echo "Player $i $card1 $card2<br>"; #} $cardNo++; #burn - the burns probably don't affect the outcome but it follows the way the game is played $flop[0]=$shuffDeck[$cardNo++]; $flop[1]=$shuffDeck[$cardNo++]; $flop[2]=$shuffDeck[$cardNo++]; $cardNo++; #burn $flop[3]=$shuffDeck[$cardNo++]; $cardNo++; #burn $flop[4]=$shuffDeck[$cardNo++]; $card1=$deck[$flop[0]]; $card2=$deck[$flop[1]]; $card3=$deck[$flop[2]]; $card4=$deck[$flop[3]]; $card5=$deck[$flop[4]]; #print "Flop $card1 $card2 $card3 $card4 $card5<br>\n"; for($playerIndex=0;$playerIndex<$handsPerGame;++$playerIndex){ for($cardIndex=2;$cardIndex<7;++$cardIndex){ # Add the board cards to each players hand to be used in valuing the hand $handCards[$playerIndex][$cardIndex]=$flop[$cardIndex-2]; } } $winningHand=0; for($playerIndex=0;$playerIndex<$handsPerGame;++$playerIndex){ $handIsSuited[$playerIndex]=FALSE; $card1=$handCards[$playerIndex][0]; $card2=$handCards[$playerIndex][1]; # getHandValue computes value for each hand this will be # used to compute the winning hand. # getHandValue may change the order of the cards to calculate # the proper value so I need to store the players cards # before we calculate value getHandValue($playerIndex, $handValue, $handCards); if(($card1 % 4) == ($card2 % 4)){ $handIsSuited[$playerIndex]=TRUE; } # cards are identified by their position in a sorted deck # by doing a modulus operation on the card number you get # the suit (we only care if the players cards are suited not # what suit they are) by dividing by 4 we get the value of the # card. For example at index 4 is the THREE of CLUBS, at index 5 # is the THREE of DIAMONDS ..... by doing an integer divide by # 4 gets us a result of 1 regardless of the suit. And 1 is the # index for a THREE. $card1=intval($card1/4); $card2=intval($card2/4); # Once we've removed suit from the card value we want the relativly # higher ranked card to always be the first card (does not matter # for pairs if($card1<$card2){ $hold=$card1; $card1=$card2; $card2=$hold; } $handKey[$playerIndex]=$cardRank[$card1].$cardRank[$card2]; if($handValue[$playerIndex]>$winningHand){ $winningHand=$handValue[$playerIndex]; } } for($playerIndex=0;$playerIndex<$handsPerGame;++$playerIndex){ if($handValue[$playerIndex] != 0){ if($handIsSuited[$playerIndex]){ $suitedHandsDealt[$handKey[$playerIndex]]++; } else{ $unsuitedHandsDealt[$handKey[$playerIndex]]++; } } if($handValue[$playerIndex] == $winningHand){ #print "Player $playerIndex wins $handValue[$playerIndex]!<br>\n"; if($handIsSuited[$playerIndex]){ $suitedHandsWon[$handKey[$playerIndex]]++; } else{ $unsuitedHandsWon[$handKey[$playerIndex]]++; } } } } function isThereQuads($playerIndex, &$handValue, &$handCards){ $cardCount=array(); loadCardCount($playerIndex, $cardCount, $handCards); for($i=0;$i<13;$i++){ if($cardCount[$i]==4){ #$areQuads=TRUE; # FIRSTKICKER is what you have 4 of # no need for a second kicker because 2 players can't # have the same quads $handValue[$playerIndex]=(HANDTYPE*QUADS)+(FIRSTKICKER*$i); #print "Player $playerIndex has quads $handValue[$playerIndex]<br>\n"; return TRUE; } } return FALSE; } function isThereABoat($playerIndex, &$handValue, &$handCards){ $cardCount=array(); loadCardCount($playerIndex, $cardCount, $handCards); $highTrips=-1; $highPair=-1; for($i=12;$i>-1;$i--){ if($cardCount[$i]==3){ $highTrips=$i; break; } } if($highTrips>-1){ for($i=12;$i>-1;$i--){ if($i != $highTrips){ if($cardCount[$i]>1){ # Could have 2 trips in which case second trips count as a pair $highPair=$i; $handValue[$playerIndex]=(HANDTYPE*BOAT)+(FIRSTKICKER*$highTrips)+(SECONDKICKER*$highPair); #print "Player $playerIndex has a boat $handValue[$playerIndex]<br>\n"; return TRUE; break; } } } } return FALSE; } function isThereAFlush($playerIndex, &$handValue, &$handCards){ $suits=array(0,0,0,0); $flushSuit=-1; for($cardIndex=0;$cardIndex<7;$cardIndex++){ $suits[$handCards[$playerIndex][$cardIndex]%4]++; } for($i=0;$i<4;$i++){ if($suits[$i]>4){ $flushSuit=$i; break; } } if($flushSuit!=-1){ # OK we have a flush is it a straight Flush? # first lets remove the unsuited cards so we check for a straight for($cardIndex=0;$cardIndex<7;$cardIndex++){ if(($handCards[$playerIndex][$cardIndex]%4)==$flushSuit){ $sortedCards[$cardIndex]=intval($handCards[$playerIndex][$cardIndex]/4); } else{ $handCards[$playerIndex][$cardIndex]=-1; $sortedCards[$cardIndex]=-1; } } global $highCard; if(isThereAStraight($playerIndex, $handValue, $handCards)){ # Wow a Straight Flush! $handValue[$playerIndex]=(HANDTYPE*SFLUSH)+(FIRSTKICKER*$highCard); # $highCard comes from isThereAStraight #print "Player $playerIndex has a straight flush $handValue[$playerIndex]<br>\n"; return TRUE; } else{ sortCards($sortedCards); $handValue[$playerIndex]=(HANDTYPE*FLUSH)+(FIRSTKICKER*$sortedCards[0])+(SECONDKICKER*$sortedCards[1])+ (THIRDKICKER*$sortedCards[2])+(FOURTHKICKER*$sortedCards[3])+(FIFTHKICKER*$sortedCards[4]); #print "Player $playerIndex has a flush $handValue[$playerIndex]<br>\n"; return TRUE; } } return FALSE; } function isThereAStraight($playerIndex, &$handValue, &$handCards){ global $highCard; $cardCount=array(); loadCardCount($playerIndex, $cardCount, $handCards); $highCard=FALSE; if($cardCount[FIVE] > 0 && $cardCount[FOUR] > 0 && $cardCount[THREE] > 0 && $cardCount[DEUCE] > 0 && $cardCount[ACE] > 0){ $highCard=FIVE; } if($cardCount[SIX] > 0 && $cardCount[FIVE] > 0 && $cardCount[FOUR] > 0 && $cardCount[THREE] > 0 && $cardCount[DEUCE] > 0){ $highCard=SIX; } if($cardCount[SEVEN] > 0 && $cardCount[SIX] > 0 && $cardCount[FIVE] > 0 && $cardCount[FOUR] > 0 && $cardCount[THREE] > 0){ $highCard=SEVEN; } if($cardCount[NINE] > 0 && $cardCount[EIGHT] > 0 && $cardCount[SEVEN] > 0 && $cardCount[SIX] > 0 && $cardCount[FIVE] > 0){ $highCard=NINE; } if($cardCount[TEN] > 0 && $cardCount[NINE] > 0 && $cardCount[EIGHT] > 0 && $cardCount[SEVEN] > 0 && $cardCount[SIX] > 0){ $highCard=TEN; } if($cardCount[JACK] > 0 && $cardCount[TEN] > 0 && $cardCount[NINE] > 0 && $cardCount[EIGHT] > 0 && $cardCount[SEVEN] > 0){ $highCard=JACK; } if($cardCount[QUEEN] > 0 && $cardCount[JACK] > 0 && $cardCount[TEN] > 0 && $cardCount[NINE] > 0 && $cardCount[EIGHT] > 0){ $highCard=QUEEN; } if($cardCount[KING] > 0 && $cardCount[QUEEN] > 0 && $cardCount[JACK] > 0 && $cardCount[TEN] > 0 && $cardCount[NINE] > 0){ $highCard=KING; } if($cardCount[ACE] > 0 && $cardCount[KING] > 0 && $cardCount[QUEEN] > 0 && $cardCount[JACK] > 0 && $cardCount[TEN] > 0){ $highCard=ACE; } if($highCard){ $handValue[$playerIndex]=(HANDTYPE*STRAIGHT)+(FIRSTKICKER*$highCard); #print "Player $playerIndex has a straight $handValue[$playerIndex]<br>\n"; return TRUE; } return FALSE; } function isThereTrips($playerIndex, &$handValue, &$handCards){ $cardCount=array(); loadCardCount($playerIndex, $cardCount, $handCards); $highTrips=-1; $kicker1=-1; $kicker2=-1; for($i=12;$i>-1;$i--){ if($cardCount[$i]==3){ $highTrips=$i; last; } } if($highTrips>-1){ for($i=12;$i>-1;$i--){ if($i!=$highTrips){ if($cardCount[$i]>0){ if($kicker1==-1){ $kicker1=$i; } else{ $kicker2=$i; $handValue[$playerIndex]=(HANDTYPE*TRIPS)+(FIRSTKICKER*$highTrips)+ (SECONDKICKER*$kicker1)+(THIRDKICKER*$kicker2); #print "Player $playerIndex has trips $handValue[$playerIndex]<br>\n"; return TRUE; break; } } } } } return FALSE; } function isThereTwoPairs($playerIndex, &$handValue, &$handCards){ $cardCount=array(); loadCardCount($playerIndex, $cardCount, $handCards); $highPair=-1; $lowPair=-1; $kicker1=-1; for($i=12;$i>-1;$i--){ if($cardCount[$i]==2){ $highPair=$i; break; } } if($highPair>-1){ for($i=12;$i>-1;$i--){ if($i != $highPair){ if($cardCount[$i]==2){ $lowPair=$i; break; } } } } if($highPair>-1 && $lowPair>-1){ for($i=12;$i>-1;$i--){ if($i != $highPair && $i != $lowPair){ if($cardCount[$i]>0){ $kicker1=$i; $handValue[$playerIndex]=(HANDTYPE*TWOPAIR)+(FIRSTKICKER*$highPair)+(SECONDKICKER*$lowPair)+(THIRDKICKER*$kicker1); #print "Player $playerIndex has 2 pair $handValue[$playerIndex]<br>\n"; return TRUE; break; } } } } return FALSE; } function isThereOnePair($playerIndex, &$handValue, &$handCards){ $cardCount=array(); loadCardCount($playerIndex, $cardCount, $handCards); $pair=-1; $kicker1=-1; $kicker2=-1; $kicker3=-1; for($i=12;$i>-1;$i--){ if($cardCount[$i]==2){ $pair=$i; break; } } if($pair>-1){ for($i=12;$i>-1;$i--){ if($i!=$pair){ if($cardCount[$i]>0){ if($kicker1==-1){ $kicker1=$i; } else{ if($kicker2==-1){ $kicker2=$i; } else{ $kicker3=$i; $handValue[$playerIndex]=(HANDTYPE*ONEPAIR)+(FIRSTKICKER*$pair)+(SECONDKICKER*$kicker1)+ (THIRDKICKER*$kicker2)+(FOURTHKICKER*$kicker3); #print "Player $playerIndex has 1 pair $handValue[$playerIndex]<br>\n"; return TRUE; break; } } } } } } return FALSE; } function getHandValue($playerIndex, &$handValue, &$handCards){ $sortedCards=array(); $handValue[$playerIndex]=UNKNOWN; # Makes sense to check for a flush first, if a flush exists within the seven cards # the only higher possible hand is a straight flush which is easy to check for # using the same code that will check for a straight isThereAFlush($playerIndex, $handValue, $handCards); if($handValue[$playerIndex]==UNKNOWN){ if(!isThereQuads($playerIndex, $handValue, $handCards)){ if(!isThereABoat($playerIndex, $handValue, $handCards)){ if(!isThereAStraight($playerIndex, $handValue, $handCards)){ if(!isThereTrips($playerIndex, $handValue, $handCards)){ if(!isThereTwoPairs($playerIndex, $handValue, $handCards)){ if(!isThereOnePair($playerIndex, $handValue, $handCards)){ for($i=0;$i<7;$i++){ $sortedCards[$i]=intval($handCards[$playerIndex][$i]/4); } sortCards($sortedCards); $handValue[$playerIndex]=(HANDTYPE*HIGHCARD)+(FIRSTKICKER*$sortedCards[0]) +(SECONDKICKER*$sortedCards[1]) +(THIRDKICKER*$sortedCards[2]) +(FOURTHKICKER*$sortedCards[3]) +(FIFTHKICKER*$sortedCards[4]); #print "Player $playerIndex has high card $handValue[$playerIndex]<br>\n"; } } } } } } } } function loadCardCount($playerIndex, &$cardCount,&$handCards){ $cardCount=array(0,0,0,0,0,0,0,0,0,0,0,0,0); for($cardIndex=0;$cardIndex<7;$cardIndex++){ if($handCards[$playerIndex][$cardIndex]!=-1){ $cardCount[intval($handCards[$playerIndex][$cardIndex]/4)]++; } } } function sortCards(&$sortedCards){ # simple bubble sort for($i=0;$i<6;$i++){ for($i2=0;$i2<6;$i2++){ $i3=$i2+1; if($sortedCards[$i2]<$sortedCards[$i3]){ $holdCard=$sortedCards[$i2]; $sortedCards[$i2]=$sortedCards[$i3]; $sortedCards[$i3]=$holdCard; } } } } # Main processing $suitedHandsDealt=array(); $suitedHandsWon=array(); $unsuitedHandsDealt=array(); $unsuitedHandsWon=array(); for($handIndex=0;$handIndex<$handsToPlay;++$handIndex){ $handCards=array(); deal($handsPerGame, $handCards, $suitedHandsDealt, $suitedHandsWon, $unsuitedHandsDealt, $unsuitedHandsWon); } # print results for suited hands $handsToPlay=number_format($handsToPlay); $end=time(); $diff = $end-$start; $diff=number_format($diff); echo "<table> <tr> <th colspan=5>Players per game are $handsPerGame, Number of hands palyed was $handsToPlay</th> </tr> <tr> <th colspan=5>Run time was $diff seconds</th> </tr> <tr> <th>Type</td> <th>Hand</td> <th>Times Dealt</td> <th>Times Won</td> <th>Winning %</td> </tr> "; for($i=0;$i<13;$i++){ for($i2=0;$i2<13;$i2++){ $hand=$cardRank[$i].$cardRank[$i2]; if($suitedHandsDealt[$hand]>0){ $winPct=100*($suitedHandsWon[$hand]/$suitedHandsDealt[$hand]); $winPct=sprintf("%.2f", $winPct); $suitedHandsDealt[$hand]=number_format($suitedHandsDealt[$hand]); $suitedHandsWon[$hand]=number_format($suitedHandsWon[$hand]); echo " <tr> <td>suited</td> <td>$hand</td> <td class='anumber'>$suitedHandsDealt[$hand]</td> <td class='anumber'>$suitedHandsWon[$hand]</td> <td class='anumber'>$winPct</td> </tr> "; } } } # print results for unsuited hands for($i=0;$i<13;$i++){ for($i2=0;$i2<13;$i2++){ $hand=$cardRank[$i].$cardRank[$i2]; if($unsuitedHandsDealt[$hand]>0){ $winPct=100*($unsuitedHandsWon[$hand]/$unsuitedHandsDealt[$hand]); $winPct=sprintf("%.2f", $winPct); $unsuitedHandsDealt[$hand]=number_format($unsuitedHandsDealt[$hand]); $unsuitedHandsWon[$hand]=number_format($unsuitedHandsWon[$hand]); echo " <tr> <td>unsuited</td> <td>$hand</td> <td class='anumber'>$unsuitedHandsDealt[$hand]</td> <td class='anumber'>$unsuitedHandsWon[$hand]</td> <td class='anumber'>$winPct</td> </tr> "; } } } echo "</table> "; ?>