The maths of Texas Hold ’em with R

[This article was first published on The Beginner Programmer, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Please before continue reading, make sure to read the disclaimer at the bottom of this article.

Every time I watch on tv some game of Texas Hold’em I am always curious about the small percentages which appear in the bottom corners of the screen and tell us the chances of win for each player. There must be some kind of algorithm which implements and refreshes those numbers at each draw.

Today I am going to write about one of the first simulations I put down as code and wrapped my head around. Now that I am a little smarter at coding than I was when I coded this, I believe this whole simulation could be done much better and with A LOT less code… Anyway I guess that is a good sign since the fact I found “mistakes” in my code should mean I improved at least on the very things I made mistakes on.

The code I am about to show estimates the probabilities of drawing a pair, a three of a kind (tris) and a poker.

Let’s proceed, first of all: we need to define the deck and the drawing function.

# A deck of 52 cards (Jokers are excluded)
deck = c("A","A","A","A","K","K","K","K","Q","Q","Q","Q","J","J","J","J",10,10,10,10,9,9,9,9,8,8,8,8,7,7,7,7,6,6,6,6,5,5,5,5,4,4,4,4,3,3,3,3,2,2,2,2)
# This function simulates a hand at Texas Hold'em poker for 2 players and the dealer
hand <- function()
{
player1Hand <- sample(deck,2,F)
for(i in player1Hand)
{
# is.element(i,deck) checks if the element i is in the vector deck
# match gets the position of the i element in the vector deck and deck[-v] deletes the vth element of the vector deck
if(is.element(i,deck)
){
deck<-deck[-match(i,deck)]
}
}
player2Hand <- sample(deck,2,F)
for(i in player2Hand)
{
if(is.element(i,deck))
{
deck<-deck[-match(i,deck)]
}
}
# The flop
dealt_cards <- sample(deck,3,F)
for(i in dealt_cards)
{
if(is.element(i,deck))
{
deck<-deck[-match(i,deck)]
}
}
# The turn
second_dealt <- sample(deck,1,F)
for(i in second_dealt)
{
if(is.element(i,deck))
{
deck<-deck[-match(i,deck)]
dealt_cards = append(dealt_cards,second_dealt)
}
}
# The river
third_dealt <- sample(deck,1,F)
for(i in third_dealt)
{
if(is.element(i,deck))
{
deck<-deck[-match(i,deck)]
dealt_cards = append(dealt_cards,third_dealt)
}
}
# Eventually, a list of the results is returned by the function
results = list(player1Hand,player2Hand,dealt_cards)
return(results)
}

Let’s try our drawing function hand() and check the result:

im2



Not that good draw we were looking for huh? Anyway that’s a complete game of Texas Hold’em (provided neither of the two player folded).



Why don’t we create 100 of these games? Here they are:

# This code builds three arrays where it stores the results of 100 poker hands
player1Hands <- c(0,0)
player1 <- matrix(player1Hands,ncol=2)
player2Hands <- c(0,0)
player2 <- matrix(player2Hands,ncol=2)
dealerst <- c(0,0,0,0,0)
dealer <- matrix(dealerst,ncol=5)
for(i in 1:100)
{
first_round <- hand()
p1_h <- c(first_round[[1]][1],first_round[[1]][2])
player1 <- rbind(player1,p1_h)
p2_h <- c(first_round[[2]][1],first_round[[2]][2])
player2 <- rbind(player2,p2_h)
deal_h <- c(first_round[[3]][1],first_round[[3]][2],first_round[[3]][3],first_round[[3]][4],first_round[[3]][5])
dealer <- rbind(dealer,deal_h)
}
player1 <- player1[-1,]
player2 <- player2[-1,]
dealer <- dealer[-1,]
rm(p1_h,p2_h,deal_h,first_round,player1Hands,player2Hands,dealerst,i)

At this stage, if we run the code, R will generate three tables (or matrix) with the results of each one of the 100 simulate games. Something like this:



games



Now we need only to look for pairs, tris and pokers. We need to define 3 functions as follows:

# This function runs through all the 100 hands and searches for pairs
find_pairs <- function()
{
pair = 0
for(i in 1:100)
{
if((player1[i,1] == player1[i,2]) | (is.element(player1[i,1],dealer[i,])) | (is.element(player1[i,2],dealer[i,])))
{
pair = pair + 1
}
}
return(pair)
}
# This function runs through all the 100 hands and searches for tris
find_tris <- function()
{
tris = 0
for(i in 1:100)
{
test = 0
if((is.element(player1[i,1],dealer[i,]) | is.element(player1[i,2],dealer[i,])) && (player1[i,1] == player1[i,2]))
{
tris = tris + 1
}
for(b in 2 : 5)
{
if(dealer[i,b] == dealer[i,b-1])
{
dealer_pair = T
test = dealer[i,b]
if(is.element(test,player1[i,]) && dealer_pair)
{
tris = tris + 1
}
}
}
}
return(tris)
}
# This function runs through all the 100 hands and searches for pokers
find_poker <- function()
poker = 0
for(i in 1:100)
{
test = 0
dealer_pair = 0
test2 = 0
dealer_tris = 0
for(b in 2 : 5)
{
test = 0
if(dealer[i,b] == dealer[i,b-1])
{
dealer_pair = T
test = dealer[i,b]
}else
{
dealer_pair = F
}
if((player1[i,1] == player1[i,2]) && (dealer_pair) && (player1[i,1] == test))
{
poker = poker + 1
#-------------------
print(player1[i,])
print(dealer[i,])
}
}
for(b in 3 : 5)
{
test2 = 0
dealer_tris = 0
if((dealer[i,b] == dealer[i,b-1]) && (dealer[i,b-1] == dealer[i,b-2]))
{
dealer_tris = T
test2 = dealer[i,b]
}else
{
dealer_tris = F
}
if(dealer_tris && ((player1[i,1] == test2) | (player1[i,2] == test2)))
{
poker = poker + 1
#-------------------
print(player1[i,])
print(dealer[i,])
}
}
}
return(poker)
}
find_pairs()
find_tris()
find_poker()

The result should look like this:

im5



In 100 games, we have got 47 pairs, 2 three of a kind and no pokers! Interesting data! However, this might be a mere case! We need to run this a LOT of times to be sure the odds we obtained are at least near the real ones. Let’s build a function that can do this. For instance, the function below runs n times 100 games and then collect the results. Note that It outputs the probabilities as the mean of the probabilities occurred. Much of the code here is replicated from the functions above. I guess this could have been done a lot better! If you have any idea please let me know or leave a comment!

# The following function calculates the probabilities of drawing a pair or a tris in a Texas Hold'em game with 2 players and the dealer
pairs_and_tris <- function(n)
{
# This function searches for all the pairs in 100 hands and returns the number of pairs found
find_pairs <- function()
{
pair = 0
for(i in 1:100){
if((player1[i,1] == player1[i,2]) | (is.element(player1[i,1],dealer[i,])) | (is.element(player1[i,2],dealer[i,])))
{
pair = pair + 1
}
}
return(pair)
}
# This function searches for all the tris in 100 hands and returns the number of tris found
find_tris <- function()
{
tris = 0
for(i in 1:100)
{
test = 0
if((is.element(player1[i,1],dealer[i,]) | is.element(player1[i,2],dealer[i,])) && (player1[i,1] == player1[i,2]))
{
tris = tris + 1
}
for(b in 2 : 5){
if(dealer[i,b] == dealer[i,b-1])
{
dealer_pair = T
test = dealer[i,b]
if(is.element(test,player1[i,]) && dealer_pair)
{
tris = tris + 1
}
}
}
}
return(tris)
}
# This function searches for all the pokers in 100 hands and returns the number of pokers found
find_poker <- function()
{
poker = 0
for(i in 1:100)
{
test = 0
dealer_pair = 0
test2 = 0
dealer_tris = 0
for(b in 2 : 5)
{
test = 0
if(dealer[i,b] == dealer[i,b-1])
{
dealer_pair = T
test = dealer[i,b]
}else
{
dealer_pair = F
}
if((player1[i,1] == player1[i,2]) && (dealer_pair) && (player1[i,1] == test))
{
poker = poker + 1
#-------------------
print(player1[i,])
print(dealer[i,])
}
}
for(b in 3 : 5)
{
test2 = 0
dealer_tris = 0
if((dealer[i,b] == dealer[i,b-1]) && (dealer[i,b-1] == dealer[i,b-2]))
{
dealer_tris = T
test2 = dealer[i,b]
}else
{
dealer_tris = F
}
if(dealer_tris && ((player1[i,1] == test2) | (player1[i,2] == test2)))
{
poker = poker + 1
#-------------------
print(player1[i,])
print(dealer[i,])
}
}
}
return(poker)
}
# The code below calculates probabilties of pairs and tris for each of the n 100 texas holdem hands simulation
# The vector help_vector is used to build the matrix risultati_p1 where all the pairs and tris will be stored
help_vector <- c(0,0,0)
results_p1 <- matrix(help_vector,ncol=3)
colnames(results_p1) = c("Pairs per 100 hands","Tris per 100 hands","Poker per 100 hands")
for(i in 1:n)
{
player1Hands <- c(0,0)
player1 <- matrix(player1Hands,ncol=2)
player2Hands <- c(0,0)
player2 <- matrix(player2Hands,ncol=2)
dealerst <- c(0,0,0,0,0)
dealer <- matrix(dealerst,ncol=5)
# The code below simulates 100 texas hold'em hands
for(i in 1:100)
{
first_round <- hand()
p1_h <- c(first_round[[1]][1],first_round[[1]][2])
player1 <- rbind(player1,p1_h)
p2_h <- c(first_round[[2]][1],first_round[[2]][2])
player2 <- rbind(player2,p2_h)
deal_h <- c(first_round[[3]][1],first_round[[3]][2],first_round[[3]][3],first_round[[3]][4],first_round[[3]][5])
dealer <- rbind(dealer,deal_h)
}
player1 <- player1[-1,]
player2 <- player2[-1,]
dealer <- dealer[-1,]
pairs <- find_pairs()
tris <- find_tris()
#----------------
poker <- find_poker()
#----------------
results__p1 <- c(pairs,tris,poker)
results_p1 <- rbind(results_p1,results__p1)
}
results_p1 <- results_p1[-1,]
# Results for each 100 hand are shown
View(results_p1)
# Final result is printed
string_to_print <- paste("Given a Texas Hold'em game and two players, here are probabilities for p1 in",n*100,"hands:",sep=" ")
print(string_to_print)
# Please note that probabilites are calculated as average of pairs and tris found
mean_scores = c(mean(results_p1[,1]),mean(results_p1[,2]),mean(results_p1[,3]))
names(mean_scores) = c("Probability of a pair,","Probability of a tris","Probabilty of a poker")
return(mean_scores)
}

Let’s try for instance to run this 10.000 times, with n = 100. Here are the results:



Immagine 9



Immagine 7



For debugging purposes our function outputs each poker it finds. Since usually pokers are not that frequent it should be fine. 10.000 times seems not to be enough…
Let’s do 100.000 times!!!



Immagine 8



This looks better! By simulating in less than 2 minutes 100.000 games of Texas Hold’em with 2 players we concluded that drawing a poker two times in a row is a very unlikely event, drawing a pair is a relative common event while three of a kind is rare, but not as much as poker!



I should mention that, it looks like the probability of a poker is overestimated according to the formal calculation, I believe that is either because it is an “outlier” or because I did not run the simulation a big enough number of times. Anyway the other two probabilities seem fine (you can check for more information on http://en.wikipedia.org/wiki/Poker_probability)



Hope you enjoyed!








Disclaimer: This article is for educational purpose ONLY. Odds generated by this code are calculated by a random simulation. As such the odds will represent an approximation of the true odds. They might even be completely wrong or misleading. This code must NOT be used for anything other than educational purpose. The provider of this code does not guarantee the accuracy of the results and accepts no liability for any loss or damage that may occur as a result of the use of this code. Understanding and agreeing to the terms of this disclaimer is a condition of use of this code. By reading the article you confirm you have understood and will comply with this disclaimer.

To leave a comment for the author, please follow the link and comment on their blog: The Beginner Programmer.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)