I have updated my reference application with code that evaluates Texas Hold ‘Em poker hands.

I had written the poker logic a few months ago. Recently, I moved it into a class library and service in order to validate my ASP.NET Core MVC + SOA Architecture. This project is a long way from a functioning poker application that enables users to play Texas Hold ‘Em against artificial intelligence “bots”- my ultimate goal. But it’s a fun start. Evaluating poker hands certainly is more entertaining than confirming a “Hello World” message sent through an SOA stack appears at the other end of the pipe- on a web page in the user’s browser.

Evaluating Poker Hands

The MVC page located at /poker/holdem/cash/dealtoriver/10 (not publicly accessible) displays the results of dealing a full table of 10 players:

Player Cards Hand Kickers Winner
Community J946Q
Player 10 T8 Straight Queen High Winner
Player 9 44 Three Fours QhJd
Player 3 JT Pair of Jacks QhTs9s
Player 7 6A Pair of Sixes AcQhJd
Player 5 K6 Pair of Sixes KsQhJd
Player 6 76 Pair of Sixes QhJd9s
Player 8 55 Pair of Fives QhJd9s
Player 4 KA Ace High KcQhJd9s
Player 1 7A Ace High QhJd9s7c
Player 2 52 Queen High Jd9s6c5c

The MVC page located at /poker/holdem/cash/dealtowinninghand/10?handname=straightflush (not publicly accessible) displays the results of dealing a full table of 10 players until a specific winning hand appears:

StraightFlush encountered on the 2,972nd deal.

Dealt, scored, and sorted 68,656 hands per second.

Player Cards Hand Kickers Winner
Community TJA98
Player 7 5Q Straight Flush Queen High Winner
Player 3 QK Flush King High JdTd9d8d
Player 6 7T Flush Jack High Td9d8d7d
Player 10 T4 Flush Jack High Td9d8d4d
Player 2 37 Straight Jack High
Player 5 A6 Pair of Aces JdTd9d
Player 1 J2 Pair of Jacks AcTd9d
Player 8 QT Pair of Tens AcQsJd
Player 9 83 Pair of Eights AcJdTd
Player 4 7K Ace High KsJdTd
9
d

Performance

The card shuffling and poker hand evaluation code is sufficiently fast. I know I can make it faster using bitwise tricks I’ve picked up over the years (there’s 64 bits in a ulong, only 52 cards in a full deck), but I’ll leave that as a future optimization after I write robust unit tests.

The code performs these tasks…

  1. Website controller calls Web API service method via Refit proxy.
  2. Refit proxy in website controller serializes entities as JSON via Newtonsoft Json.NET.
  3. Web API method deserializes JSON to entity classes.
  4. Web API method instantiates domain class to create table of 10 players.
  5. Shuffles 52 card deck.
  6. Deals two private cards to each of 10 players.
  7. Deals flop (3), turn (1), and river (1) community cards.
  8. Scores and sorts player hands, finding the best five card hand from the seven cards available.
  9. Web API calls .ToEntity() extension method to convert domain class to entity class.
  10. ASP.NET Core runtime serializes entity to JSON.
  11. Refit proxy in website controller deserializes JSON to entity class.
  12. Website controller passes entity to Razor view.
  13. Razor view iterates over messages and cards, writing HTML to response stream sent to user’s web browser.

… in five milliseconds. Not bad for code executing across two processes (website and service).

The code runs in five milliseconds. Not bad for code executing across two processes.

Testing

Speaking of unit tests, I have written a few. One of the most important is a test that validates the deck shuffling method produces an even distribution of cards. Meaning, over the course of many deals, each card has an equal chance of appearing in each of the 52 positions in a deck. When writing the Deck class, I referenced Microsoft’s RNGCryptoServiceProvider, then wrote the following code which implements the Fischer-Yates algorithm to randomly shuffle cards. Actually, I wrote the Shuffle method with generics so I may use it in other projects (not only for the Deck class).

I wrote the following code to validate Deck.Shuffle() is evenly distributed.

The distribution test passes. I also wrote two performance tests:

Shuffle and deal rate = 153,708 full decks per second.
Hand scoring rate = 681,244 per second.

Data Tier Architecture

In addition, I worked on data tier architecture. I wrote a class library with code that…

  • Maps domain classes to and from entity DTO classes.
  • Implements CRUD operations using a repository pattern.

So far I’m liking how the data tier code is separated from the domain and entity code. This design keeps the domain class library focused on business logic and the Contract library focused on defining service interfaces and entity DTOs. But I have more testing to do. I’ll write about my data tier architecture in a future blog post.

Next Steps

Next steps are to move the web service call from the website controller to AngularJS code running in the user’s web browser. Then perhaps write more poker logic. The code I’m most concerned about (other than artificial intelligence bot code) is how to correctly separate bets into multiple pots when players call all-in bets with short stacks. An entire table of players calling all-in with various stack sizes is a nightmare scenario. What is the correct algorithm and how do I write unit tests that exercise all the edge cases?

Leave a Reply

Your email address will not be published.