Perl

The Perl script below calculates the probability a specific pre-flop hand will win given a number of players in Texas Hold’em.

This is probably not the best application for Perl. Perl is the slowest performer at this task. Yet when it comes to manipulating data and admin tasks I’d be lost without it.

#!/usr/bin/perl
use strict;

use constant {
TRUE         => 1,
FALSE        => 0,
ACE          => 12,
DEUCE        => 0,
THREE        => 1,
FOUR         => 2,
FIVE         => 3,
SIX          => 4,
SEVEN        => 5,
EIGHT        => 6,
NINE         => 7,
TEN          => 8,
JACK         => 9,
QUEEN        => 10,
KING         => 11,
HANDTYPE     => 10000000000,
FIRSTKICKER  => 100000000,
SECONDKICKER => 1000000,
THIRDKICKER  => 10000,
FOURTHKICKER => 100,
FIFTHKICKER  => 1,
SFLUSH       => 9,
QUADS        => 8,
BOAT         => 7,
FLUSH        => 6,
STRAIGHT     => 5,
TRIPS        => 4,
TWOPAIR      => 3,
ONEPAIR      => 2,
HIGHCARD     => 1,
UNKNOWN      => 0
};

# There's really no need for a deck since I only care about suited and unsuited
# but if I had a deck it would look like this -
#my @deck = ("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");
# and suits that are also not needed would look like this -
#my \@suits = ("Clubs", "Diamonds", "Hearts", "Spades");

sub usage{
	print "dealHands requires 2 arguments - number of hands to play and number of players per hand.\n";
	exit;
}
if($#ARGV != 1){
	usage;
}

my @shuffDeck;
my $cardIndex;
my $dealtCardIndex;
my $noMatch;
my $randHold;
my @cardRank=("2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A");
my %suitedHandsDealt=();
my %suitedHandsWon=();
my %unsuitedHandsDealt=();
my %unsuitedHandsWon=();
my $handsToPlay=$ARGV[0];
my $handsPerGame=$ARGV[1];
my $i;
my $card1;
my $card2;
my @flop;
my $cardNo;
my $isSuited;
my @handValue;
my @handIsSuited;
my @handCards;
my @handKey;
my $playerIndex;
my $winningHand;
my $hold;
my @sortedCards;
my @cardCount;
my $highCard;
my $highPair;
my $lowPair;
my $quads;
my $highTrips;
my $pair;
my @suits;
my $flushSuit;
my $kicker1;
my $kicker2;
my $kicker3;
my $holdCard;
my $i2;
my $i3;
my $handIndex;
my $hand;
my $winPct;

sub getDeck{
	$shuffDeck[0]=int(rand(52)); # 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=int(rand(52));
			for($dealtCardIndex=0;$dealtCardIndex<$cardIndex;++$dealtCardIndex){
				if($shuffDeck[$dealtCardIndex]==$randHold){
					$noMatch=FALSE;
					last;
				}
			}
			if($noMatch==TRUE){
				$shuffDeck[$cardIndex]=$randHold;
				last;
			}
		}
	}
}

sub deal{
	getDeck;
	$cardNo=0;
	for($cardIndex=0;$cardIndex<2;++$cardIndex){
		for($playerIndex=0;$playerIndex<$handsPerGame;++$playerIndex){
			$handCards[$playerIndex][$cardIndex]=$shuffDeck[$cardNo++];
		}
	}
	#
	#for($i=0;$i<$handsPerGame;$i++){
	#	print "Player $i $deck[$handCards[$i][0]] $deck[$handCards[$i][1]]\n";
	#}
	#
	$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++];
	#
	#print "Flop $deck[$flop[0]] $deck[$flop[1]] $deck[$flop[2]] $deck[$flop[3]] $deck[$flop[4]]\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();
		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=int($card1/4);
		$card2=int($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]!\n";
			if($handIsSuited[$playerIndex]){
				$suitedHandsWon{$handKey[$playerIndex]}++;
			}
			else{
				$unsuitedHandsWon{$handKey[$playerIndex]}++;
			}
		}
	}
}

sub isThereQuads{
	loadCardCount();
	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]\n";
			return TRUE;
		}
	}
	return FALSE;
}
sub isThereABoat{
	loadCardCount();
	$highTrips=-1;
	$highPair=-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]>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]\n";
					return TRUE;
					last
				}
			}
		}
	}
	return FALSE;
}
sub isThereAFlush{
	@suits=(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;
			last;
		}
	}
	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]=int($handCards[$playerIndex][$cardIndex]/4);
				}
				else{
					$handCards[$playerIndex][$cardIndex]=-1;
					$sortedCards[$cardIndex]=-1;
				}
			}
			if(isThereAStraight()){
				# Wow a Straight Flush!
		                $handValue[$playerIndex]=(HANDTYPE*SFLUSH)+(FIRSTKICKER*$highCard); # $highCard comes from isThereAStraight
				#print "Player $playerIndex has a straight flush $handValue[$playerIndex]\n";
				return TRUE;
			}
			else{
				sortCards();
				$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]\n";
				return TRUE;
			}
	}
	return FALSE;
}

sub isThereAStraight{
	loadCardCount();
	$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]\n";
		return TRUE;
	}
	return FALSE;
}
sub isThereTrips{
	loadCardCount();
	$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]\n";
						return TRUE;
						last;
					}
				}
			}
		}
	}
	return FALSE;
}
sub isThereTwoPairs{
        loadCardCount();
        $highPair=-1;
        $lowPair=-1;
	$kicker1=-1;
	for($i=12;$i>-1;$i--){
		if($cardCount[$i]==2){
			$highPair=$i;
			last;
		}
	}
	if($highPair>-1){
		for($i=12;$i>-1;$i--){
			if($i != $highPair){
				if($cardCount[$i]==2){
					$lowPair=$i;
					last;
				}
			}
		}
	}
	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]\n";
					return TRUE;
					last;
				}
			}
		}
	}
	return FALSE;
}
sub isThereOnePair{
        loadCardCount();
        $pair=-1;
	$kicker1=-1;
	$kicker2=-1;
	$kicker3=-1;
	for($i=12;$i>-1;$i--){
		if($cardCount[$i]==2){
			$pair=$i;
			last;
		}
	}
	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]\n";
							return TRUE;
							last;
						}
					}
				}
			}
		}
	}
	return FALSE;
}
sub getHandValue{
	#@sortedCards=(-1,-1,-1,-1,-1,-1,-1);
	$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();
	if($handValue[$playerIndex]==UNKNOWN){
		if(!isThereQuads){
			if(!isThereABoat){
				if(!isThereAStraight){
					if(!isThereTrips){
						if(!isThereTwoPairs){
							if(!isThereOnePair){
								for($i=0;$i<7;$i++){
									$sortedCards[$i]=int($handCards[$playerIndex][$i]/4);
								}
								sortCards();
		                        			$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]\n";
							}
						}
					}
				}
			}
		}
	}
}
sub loadCardCount{
	@cardCount=(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[int($handCards[$playerIndex][$cardIndex]/4)]++;
		}
	}
}
sub sortCards{
	# 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
for($handIndex=0;$handIndex<$handsToPlay;++$handIndex){
    deal();
}
# print results for suited hands
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);
			print "suited $hand $suitedHandsDealt{$hand} $suitedHandsWon{$hand} $winPct\n";
		}
	}
}
# 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);
			print "unsuited $hand $unsuitedHandsDealt{$hand} $unsuitedHandsWon{$hand} $winPct\n";
		}
	}
}