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>
";
?>