Introduction
This code snippet will let you calculate poker probabilities the hard way, using C# and .NET. Along the way, you will learn:
Enum
s and how they can help eliminate clutter.
- How to use
IComparable
in a real world situation.
- How to score a poker hand.
- How to calculate poker probabilties with math.
Points of Interest
Enums: In the code, enumerators form the basis of the two main domain concepts that make up a card: the rank and the suit.
enum SUIT { None, Diamonds, Hearts, Clubs, Spades }
enum RANK { None, Ace, Two, Three, Four, Five, Six, Seven, Eight,
Nine, Ten, Jack, Queen, King }
By using enumerators, the initialization code that fills out the 52 cards in a standard deck is nothing more than two simple foreach
statements. The code is complicated only by the fact that you must allow for a card not to have a default rank when it is created.
Notice, the call to the static method Enum.GetValues
returns an Array
, which is suitable for a foreach
statement.
foreach(SUIT s in Enum.GetValues(typeof(SUIT)))
foreach(RANK r in Enum.GetValues(typeof(RANK)))
if (r!=RANK.None && s!=SUIT.None)
d[counter++] = new Card(r,s);
The only trick to this method is the typeof
call which is needed to get the type of the enumerator for the method call.
The code also uses an enumerator to define the different poker hands that you can get. The most interesting use of the POKERSCORE
enumerator comes in the Stats
class.
In the Append
method, a POKERSCORE
is cast into an int
that is used as an index into an array that keeps scoring information.
In the Report
method, the call to the static method Enum.GetName
gets the string value associated with each POKERSCORE
.
for(int i=0;i<counts.Length;++i)
{
Console.WriteLine("{0,-10}\t{1,10}\t{2,10:p4}",
Enum.GetName(typeof(POKERSCORE),i),
counts[i],
counts[i]/(double)_simCount);
}
This is another example of how C# lets you use words and phrases from the problem domain in your program.
IComparable: In order to make scoring a poker hand easier, we must sort the poker hand by rank. This will force pairs and other groups to appear that will let us take shortcuts in scoring the hand. In .NET, the Array.Sort
method will handle the sorting for you, as long as the elements in the Array
implement the IComparable
interface. In the code, we implement CompareTo
and use the _rank
variable to determine the sort position.
The only problem that the example code presents with this arrangement is that in poker, an Ace is scored differently depending on the poker hand. For instance, a straight that starts with an Ace looks like this: A2345; but a straight that ends with an Ace looks like this: TJQKA. In both cases, we sort an Ace lower in the array since we are using the enum
to determine the sort order. So, when we are scoring a straight, we must check for a special case.
Scoring a poker hand: In the code, the poker hand is scored in the static method score
found in the class PokerLogic
. The order of the calls and the fact that the scores are mutually exclusive is important.
The scoring happens in reverse order. A four of a kind is also a pair. In the domain of poker, a four of a kind is more desirable, and so should be the correct score.
Scoring a poker hand with math: You can look up all types of reference on Google on poker probability. Most of them do not list the Jacks or better case.
The total number of possible pairs is given by 52 choose 5 or 2,598,960 possible choices.
To get the total number of possible hands for Jacks or better, you must do the following:
- Choose 1 of the 4 possible choices for the pair (Ace, Jack, Queen, or King).
- Pick two suits for this pair.
- Pick 3 cards from the remaining 12 ranks.
- Choose one suit for each of the last three cards.
This will give you 337,920 possible hands with Jacks or better.
In our simulation, we should see around 13% (337,920/2,598,960) of the hands counted as Jacks or better.
By default, when you run the application, it will run 5000 poker hands and produce statistics to the screen. You may also run it with a single command line parameter that will be the number of hands dealt.
You must run the simulation for at least 100,000 hands to get a true picture of what the percentages will be.