# Estimating the outcome of a Texas hold’em game using Monte Carlo simulation

--

## Introduction

Card games were always fascinating for both players and mathematicians due to the factor of uncertainty or, in less formal words, luck. For many years, they were also the moving force of relatively modern areas of mathematics such as probability theory and statistics.

## What is a Monte Carlo simulation?

In layman’s terms, Monte Carlo simulation is a technique for estimating a variable which depends on one or more random factors. This is achieved by repeated random sampling from a source of data (i.e a known distribution, a set of possible poker hands or from multiple tosses of a coin etc).

As you can easily understand, Monte Carlo, as probably the name suggest (no pun intended), is the perfect tool for estimating the outcome of various games that contain a factor of uncertainty.

## Texas Hold’em: Let’s remember the game

Texas hold ’em is one of the most popular variants of poker and it is played according to the following rules.

At the start of a round, players receive two down cards as their personal hand (holecards), after which there is a round of betting. Then, three board cards are turned in the middle of the table simultaneously (called the flop) and then another round of betting occurs. The next two board cards are turned one at a time (the first one is called “The Turn” and the second one is called “The River”), with a round of betting after each card.

The board cards are community cards (common within all players), and a player can use any five-card combination from among the board and personal cards. A player can even use all of the board cards and no personal cards to form a hand (“play the board”).

Finally, using the best five-card combination of their hole cards and the community cards, the players show their hands, with the bettor or last raiser showing first. The player with the five-card combination of highest strength wins the pot.

Below, you can see a chart showing the strength ranking (from highest to lowest) of each valid Texas Hold’em poker combination.

## And now a bit of math: Calculating the “Hand strength”

In order to express the strength of a hand (a set of 5 cards) with a real value, we should ask ourselves “how many distinct sets of 5 cards each exist” and then “how many of them have distinct strength”?

The first question is simple enough to be answered with some basic combinatorics. This number is the result of the expression “52 choose 5” (5 cards on each set that have been chosen out of 52 unique cards) which is approximately 2.6 millions.

Interestingly, it can be proven that all these hands share only 7642 different values of strength. This is because many combinations have the same strength as in most of them, the suits on the cards of the combinations do not contribute to the strength of the hand. Thus, we can count the strength of any hand on a scale from 0 (no combination in the set of 5 cards) to 7642 (Royal Flush).

## The simulation

Now that we have a way to quantify the strength of a hand, we can perform a simulation of a large number of different rounds of Texas Hold’em game given a specific personal hand and the first 3 board cards (flop round) which will remain the same across all iterations. In this way, we will estimate the probabilities the personal hand to win the round, to lose the round, or split (in case there is another or more players in the table that have a hand of equal strength with the strength of our personal hand).

## The code

We will implement the simulation in Python and we will use this library to evaluate the hand strength. Keep in mind that this library for technical reasons works on an inverse way based on what we explained on the previous paragraph. Thus, the hands that it evaluates with a higher evaluation, are less strong than the ones ranked with lower evaluation. So for example the strongest hand (Royal Flush) gets a score of 0 and the weakest gets a score of 7642 by this library.

Note that each card is represented by a string of this form `{card_Rank}{card_suit}` . Ranks take values from the set `{A,2,3,4,5,6,7,8,9,T,J,Q,K}` (again, for technical reasons we denote “10” as ”T”) and suits take values from the set `{c,d,h,s}` (c for “clubs”, d for “diamonds”, h for “hearts” and s for “spades”). So for example “Queen of Hearts” is represented by the string `Qh`.

So we import 2 packages that we will need later in the project (`random` and `phevaluator`, the second one needs to be installed using `pip install phevaluator`) and then we initialize a list that contains all the cards of a standard deck.

Now it is time to proceed to the “heart” of the estimation process. The simulation function.

As you observe, the function takes 3 arguments, `hand`, `table` and `players`. The first parameter ( `hand` ) contains a list of the 2 cards that the player we are interested in holds. The second parameter ( `table`) contains the 3 cards that the dealer has opened on the table up to this point of the game (which is the flop round). Finally, the third parameter ( `players` ) is the number of the rest of the players in the game.

The first thing this function does, is to store new hard copies of the `hand` and `table` cards, initialize an empty list called `hands` where the hands of the other players will be stored when cards will be given to them and of course to shuffle the deck.

Then, cards are dealt to the rest of the players (lines 11–17) and stored in the `hands` list. Next, the dealer deals the next 2 table cards (turn and river, lines 20–23).

Now it is time to evaluate the strength of our hand. In this point, let’s remember that our hand is considered as the combination of both our personal hand (2 cards dealt to us at the start of the game) and the current table cards. (line 24)

Finally, we evaluate the strengths of the hands of all other players in the table and return an integer result (0 for win, 1 for lose and 2 in case of split) based on the strength of our hand and the strength of opponents hands (lines 26–35).

Now, that the core of the simulation is ready, we have to perform this simulation many times in order to estimate the probability of each possible outcome for a given hand.

By default we run the simulation for 10000 samples and for 2 more players (so 3 in total). We count the number of times that each different outcome (win, lose or split) occurs and then to derive the estimated probability, we divide this number by the number of the samples we used for the simulation.

Finally, it is time to call the `monte_carlo()` function and see the results for a given hand and the first 3 table cards.

So we called the function with the hand we want to simulate for being `['Ah','Ad']` (Ace of hearts and Ace of diamonds) and the current table cards being `['Ac','2d','9s']` (Ace of clubs, 2 of diamonds and 9 of spades).

For 2 more players and after 10.000 simulations, I got the following results which van be interpreted as an expected probability of 97.6% to win the game, 2.4% probability to lose the game and 0% probability for a tie (split).

The full code of the project is available on this Github repository.

## Conclusion

Despite this demonstration is quite simple and thus it cannot fully explain the power of this method, I think it provides enough evidence of the enormous capabilities of Monte Carlo simulations. With the same basic principles and similar setup, the Monte Carlo method is able to provide accurate estimations for a variety of problems in different scientific fields.

--

--

💻Code-blooded, 🌏 Traveler, . Lifelong learner 📚. Currently studying Data Science and AI at TU/e, Eindhoven, NL. https://petrosdemetrakopoulos.github.io