Simulating Texas Hold'em in Elixir
A few weeks ago, Todd Sharp wrote an article called “Simulating Texas Hold’em in Groovy” where, no surprise, he simulated a Texas Hold’em hand in Groovy. At the end of the article, he welcomed others to do the same in different languages. I’ve started looking into Elixir recently and thought it would be a great language in which to replicate the experiment as best I could.
For those unfamiliar with Texas Hold’em, I’ll quote Todd’s article as he includes a helpful explanation.
If you’re not familiar with Texas Hold’em the game is pretty straightforward: 2 to 10 players each receive two down (“hole”) cards and then five community cards are dealt face up in three stages: three at first (called the “flop”), then two rounds of one card (called the “turn” and the “river” respectively). A card is “burned” or discarded prior to each round of community cards. The players make their best five card poker hand using the seven cards - their two hole cards and the five community cards. They can use any combination - use of the hole cards is not necessary to make their “best” hand. That’s pretty much all there is to it - I won’t get into betting or strategy here as that’s beyond the scope of the discussion for these purposes.
With our poker knowledge now fully intact, let’s dive in. While I could have kept all the logic in a single Elixir script file (.exs
), I decided to create a whole project.
Next, I created a separate module called TexasHoldem.Deck
that would manage the deck lifecycle, from shuffling all the way through to the river.
The first thing I added to the module were module attributes to hold all the possible suits and ranks for a card deck.
I also created some types so I could correctly annotate the functions in the module. I created one to represent a card
that was of type String.t()
. I then created a deck
type that was a list of card
s. Finally, I created a hands
type, a list of list of cards, to represent the cards dealt to each player to start the game.
Now I had the values and types I needed to create and shuffle a deck. I decided to put these two concerns (creating and shuffling) in the same function since we’d always need our deck shuffled to begin with. I did, however, break the deck creation out into it’s own private function for clarity. To create the deck, I mapped (actually, flat mapped; more on that in a second) over each suit. In the map function, I mapped again over each rank and concatenated each rank with the current suit. However, I now had a list of lists. I wanted a list of each card without any nesting, so I utilized Enum.flat_map
for the first map. The last step was to call Enum.shuffle()
on the resulting deck. Voila! A shuffled deck of cards.
I could now create a shuffled deck of cards. The next step was to deal hands out to each player. Before we look at the logic to deal out hands, let’s take a look at the logic to deal out N number of cards as that will be used both by the initial deal as well as for the flop, turn, and river.
I wanted to create a deal/2
function that would handle dealing an arbitrary number of cards. This means it would have to pull N number of cards from the top of the deck (read: head of the list) and return both the list of dealt cards as well as the remaining deck. To do this, I wrote a recursive deal/3
function that takes the deck, the number of times to iterate (number of cards to deal), and the current list of dealt cards. The first deal/3
has a guard to handle when we’ve dealt all the cards we need. The second deal/3
pops the top card off the deck and then calls itself again, decrementing timesRemaining
and adding the new card to the list of already dealt cards. The final return value of this function is a tuple of dealt cards and the remaining deck.
OK, I now had a function deal/2
that can deal an arbitrary number of cards for us. The next step was to actually deal out the initial hands to each player. This is honestly where I got tripped up the most. It took me a few tries to come up with the correct logic to handle this correctly. I’ll walk you through what I tried as well as what I eventually ended with. Here’s where I started:
Given a shuffled deck of cards and a number of players, I wanted to return the hands for each player (a list of lists) and the remaining deck of cards. In Todd’s code, he used multiple loops to deal out the cards. I decided to start there. Spoiler alert: it didn’t work.
The elixir masters out there are probably having a good laugh at that code. On the surface, it doesn’t seem like a terrible solution. Map twice (0..1
) for the number of cards in each player’s hand then map again for each player (0..(player_count - 1)
), grabbing a card off the top of the deck and zipping those cards up into hands. Spot the problem though? deck
isn’t mutable. The above code was always pulling the same card off the top of the deck. Assuming the top card on the deck was K❤️
, the players hands looked like this: [["K❤️", "K❤️"], ["K❤️", "K❤️"]]
. List.zip()
wasn’t doing what I wanted either, but since the loops were inherently flawed, I didn’t bother finishing the data transformation at the end.
I googled something along the lines of “iteration in Elixir” and found the section on the Elixir website (which has amazing documentation, by the way 😍) about generators. It seemed like they might get me closer, so I tried using some generators, but ran into the same problem I had with Enum.map
. The code is similar to the first iteration, except I’m using for
instead of Enum.map
and I added a little data transformation at the end, but deck
still isn’t mutable and the same card was always being pulled off.
Eventually, I realized I could go about dealing the hands in a more algorithmic way. To accomplish this, I could:
- deal out twice as many cards as players,
- split those cards into two lists,
- zip those lists (where the first card from each list is placed in a tuple, the second cards are placed in a tuple, etc.), and
- convert the tuples to lists to get what I needed.
Below is where I landed with comments showing the return value for each function in the pipeline.
Here’s the full function code again without the comments:
I was pretty pleased with how this function turned out, considering how much time it took me to figure out how to iterate correctly (meaning not using iteration at all). I’m sure there’s a better way to get from 10 cards to a zipped list of lists of those cards, but I don’t know it…yet.
Now I could handle the initial deal using initial_deal/2
as well as subsequent deals of the flop, turn, and river with deal/2
. Last thing to do was to add a function for burning a card (removing one card from the top of the pile). That function turned out to be very straightforward. We simply call tl/1
on the deck to get the list minus the head.
With that, the TexasHoldem.Deck
module was done. I had all the functions I needed to handle a Texas Hold’em game. It was time to call all these functions in TexasHoldem.main/0
and play out the hand.
I wanted to be able to see the results of the functions as I went, so I added a print_result/2
function to my TexasHoldem
module that would take the result of a previous operation (a deck
alone or a tuple of the previous operation result and deck
), log it out with a message and pass the remaining deck
of cards along.
I also added alias TexasHoldem.Deck
to my module. With that, main/0
was ready to be written. First, I called Deck.shuffle/0
to create a shuffled deck, piped the deck to Deck.initial_deal/2
to deal hands for players (in this case, 5 players), and called Deck.burn_card/1
and Deck.deal/2
together three times to deal the flop, turn, and river.
Here is the resulting console output from calling TexasHoldem.main/0
in iex
:
That’s it. A Texas Hold’em hand in Elixir. I’m still in the early days of learning Elixir, so there’s bound to be parts of this script that can be improved, but overall I’m pleased with where I landed. The repo with this code is up on GitHub. Check it out.
Are you learning Elixir too? Or maybe you’re an Elixir veteran? Chat with me about it on Twitter!