Friday, May 26, 2006

Enumerating Poker Hands

Being able to easily and quickly enumerate through available poker hands is essential in doing any poker analysis. C# makes enumeration easy by providing the ablity to build methods that are used with the foreach language construct.

Iterating through hands simply requires toggling all of the possible 7 bit combinations for 7 card hands or 5 bit combinations for 5 card hands. To assist in this task, C# iterators can be used.

The following method can be used to iterate through all possible hands containing the number of cards specifed.

public static IEnumerable<ulong> Hands(int numberOfCards)

This next method is a little more elaborate. It allows a mask containing shared cards to be passed, along with a mask containing dead cards, and finally the number of cards to be returned.

Shared cards are cards that must be included in the returned mask, dead cards are cards that must not be included in the return mask

public static IEnumerable<ulong> Hands(ulong shared, ulong dead, int numberOfCards)

These methods provide and easy way to count up win/tie/lose stats to figure out what the odds are for a specific player/opponent/board match up. The following example shows how you can calculate such a matchup.

using System;
using System.Collections.Generic;
using System.Text;
using HoldemHand;

namespace ConsoleApplication1
class Program
static void Main(string[] args)
// Keep stats
double win = 0.0, lose = 0.0, tie = 0.0;
// Parse cards into hand masks
ulong pocketcards = Hand.ParseHand("as ks");
ulong opponentcards = Hand.ParseHand("5h tc");
ulong boardcards = Hand.ParseHand("qs ts 5c");

// Loop through all remaining 5 card boards.
// All 5 card boardmask must contain the starting
// board cards. That's why boardcards is passed in the first argument.
// Neither player nor opponents cards should be allowed in the boardmask
// which is why they are ORed together and passed to Hand.Hands()
// second argument.
foreach (ulong boardmask in
Hand.Hands(boardcards, pocketcards | opponentcards, 5))
// Get Hand Values
uint playerhandval = Hand.Evaluate(pocketcards | boardmask);
uint opphandval = Hand.Evaluate(opponentcards | boardmask);

// Compare and update the stats
if (playerhandval > opphandval)
win += 1.0;
else if (playerhandval == opphandval)
tie += 1.0;
lose += 1.0;
// Print results which is 42.63%
Console.WriteLine("Odds of beating opponent {0:0.00}%",
(win + tie / 2.0) / (win + tie + lose) * 100.0);

I have the following code to test an implementation of several of the topics you discussed. However, when running this code the freezes on this statement:

Dim oppmask As ULong = Hand.RandomHand(opplist1, pocketmask Or boardmask, 2)

For example my cards and the board cards are the following:

My Card: As Qh
Board Cards: Qs 8h 4d Qc 3c

It is unclear whether this is incorrect code by me or if there is problem with the RandomHands call. The only time this appears to happen if there is only one Query item for the opponent. In this case either AA or QQ will cause the freeze. If there is another item in the query ("QQ|JJ") then the call does not freeze. The goal is to send over a hand range for each player if known and get the win odds from that. Is this just a mistake on my end or is their something else I am missing to get this to work correctly?

Public Sub Test()
Dim win As Double = 0
Dim lose As Double = 0
Dim tie As Double = 0
Dim count As Double = 0
Dim list As ULong() = PocketHands.Query("As Qh")
For i As Integer = 0 To list.Length - 1
Debug.Print("Hand Query: {0}", Hand.MaskToString(list(i)))
Dim opplist1() As ULong = PocketHands.Query("(QQ)")
For i As Integer = 0 To opplist1.Length - 1
Debug.Print("Opp List Hand Query: {0}", Hand.MaskToString(opplist1(i)))

Dim board As String = "3c 4d 8h"
Dim boardmask As ULong
Dim trials As Integer = 10000
For i As Integer = 0 To trials
Dim pocketmask As ULong = Hand.RandomHand(list, 0, 2)
'Dim oppmask As ULong = Hand.RandomHand(opplist1, pocketmask, 2)
Debug.Print("My Card: {0}", Hand.MaskToString(pocketmask))
For Each boardmask In Hand.RandomHands(Hand.ParseHand(board), pocketmask, 5, 1)
'Dim oppmask As Long = Hand.RandomHand(boardmask Or pocketmask, 2)
Debug.Print("Board Cards: {0}", Hand.MaskToString(boardmask))

Dim oppmask As ULong = Hand.RandomHand(opplist1, pocketmask Or boardmask, 2)
Debug.Print("opp Card: {0}", Hand.MaskToString(oppmask))

Dim pockethandval As Integer = Hand.Evaluate(pocketmask Or boardmask, 7)
Dim opphandval As Integer = Hand.Evaluate(oppmask Or boardmask, 7)
Debug.Print("pockvet vs opp: {0} vs {1}", Hand.MaskToString(pocketmask), Hand.MaskToString(oppmask))
If (pockethandval > opphandval) Then
win += 1
ElseIf (pockethandval = opphandval) Then
tie += 1
lose += 1
End If
count += 1

End Sub
This problem is that your dead cards include all of the cards in your opplist1. That means the code can not find a solution and loops forever.

I'm reluctant to change the underlying code to detect this case (it would slow down finding random hands). But I'm open to suggestion here.
The language you used was very simple and easy to understand. Even a layman would be able to grasp it with out any difficulty. Enumerating Poker Hands is the one of my favorite game and I know it very well. While playing this game I enjoyed a lot and also I trained some of my friends.
Gary Bristow
Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?