Click here to Skip to main content
Click here to Skip to main content

More Texas Holdem Analysis in C#: Part 1

By , 20 May 2008
 

Screenshot - titleimage1.jpg

Introduction

In December 2005, I published "Fast, Texas Holdem Hand Evaluation and Analysis" which was my first Texas Holdem Analysis article. Since then, I've done a lot of enhancements and extensions to the code. I've also had several folks more skilled in poker analysis offer their advice. This update to the poker library offers many new and unique features that make analysis of Texas Holdem much easier. In this article, you will find the following topics covered:

  • Pocket Query Language - This offers a straightforward set of methods that allow you experiment with different pocket hand combinations without rewriting your code.
  • Outs and draw methods - Determining outs and draws is key to analyzing a situation. This section discusses techniques for doing just that.

Part 2 will cover these topics:

  • Monte Carlo analysis - Sometimes quick estimates are good enough. Monte Carlo Analysis makes it possible to make high quality estimates in a very short time.
  • Multi-player hand analysis - More often than not, you are playing against multiple players. Here's how to calculate win odds against multiple players in a reasonable amount of time.
  • Multiple core support - Using multiple cores to decrease calculation time. The speed ups using this technique are nearly linear when adding cores.

Pocket Query Language

If you are reading this article, I'm sure you've read a book or two on Holdem. One of the things I've noticed while reading poker books is that there is a de facto standard for describing pocket cards. Most books use some variant of the following to describe pocket hands.

Pocket Hand Description Language

Example

Description

Ac Kd

Specific cards are specified using letters and numbers for ranks such 'A'-ace, 'K'-king, 'Q'-queen, 'J'-jack, 'T'-10, '9'-nine and so on. Suits are described usually in lower case as 'c'-clubs, 's'-spades, 'h'-hearts and 'd'-diamonds.

AKs, 78s

Card combinations where only the card ranks are specified and the 's' indicates they are suited.

QJ, T8

Card combinations where the card ranks are specified and the cards suits are unsuited or offsuit.

Kx, Ax

This notation indicates a card rank and another unknown card which is less than 9 and doesn't form a pair.

Most poker software that parses string representations of pocket hands only supports the first item in the de facto standard. I've implemented a much richer query language. I support all of the common poker book syntax, plus some extensions.

The Extended Pocket Hand Description Language

Example

Description

Group1

Any starting hand in Sklansky group1. Group1 through Group8 are supported.

Suited

Any set of pocket cards that are of the same suit.

Offsuit

Any set of pocket cards that are of different suits.

Connected

Any pocket card set, such as AK, that are adjacent in rank.

Gap1

Any pocket card set, such as AQ which have a 1 card.

Gap2

Any pocket card set, such as AJ which have a 2 card.

Gap3

Any pocket card set, such as AT which have a 3 card.

I've also added some operators.

Pocket Hand Description Language Basic Operators

Example

Description

Group2 To Group5

Sklansky groups can be specified as a range. Ranges use the '-' character or the word 'to' or the word 'Through'

Group3 And Offset

The intersection operator can be used with any pair of expressions. The resulting set is the intersection of the two expressions. The keywords: 'And', '&' and 'Intersection' can all be used to represent this operation.

Group1 Or Group3

The union operator can be used with any pair of expressions. The resulting set is the union of the two expressions. The keywords 'Or', '|' and 'Union' can all represent this operation.

Not Offset

The 'Not' operator returns the 1326 set of possible pocket cards minus the items defined in the expression on the right.

(AK* | AA) & Offset

Parenthesis may be used to group operations.

Why a Pocket Query Language

One of the things I've done too many times has been to write code to analyze specific match-ups. For example, have you ever wondered what the advantage is to having a suited connector versus a non-suited connector? I'd guess that you've probably wondered, but weren't enough of a masochist to write the code. On the other hand, I am enough of a masochist to write the code for many, many match-ups. After awhile, I decided I'd had enough of that and wrote a query language so that I could write my match-up analysis once and just put in query strings. The following is an example of the result of using query strings rather than hard coded match-ups. Oh, and there is about a 5% advantage for suited connectors.

Screenshot - titleimage.jpg

Using Pocket Queries in code

I've attempted to make it trivial to write analysis code that utilized Pocket Queries. Here's an example.

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

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            // A Pocket Query Returns an array of all
            // hands that meet the criterion.
            ulong[] player1 = PocketHands.Query("Connected Suited");
            ulong[] player2 = PocketHands.Query("Connected Offsuit");

            // Holds stats            
            long player1Wins = 0, player2Wins = 0,
                ties = 0, count = 0;

            // Iterate through 10000 trials.            
            for (int trials = 0; trials < 10000; trials++)
            {
                // Pick a random pocket hand out of 
                // player1's query set                
                ulong player1Mask = Hand.RandomHand(player1, 0UL, 2);

                // Pick a random pocket hand for player2                
                ulong player2Mask = Hand.RandomHand(player2, player1Mask, 2);

                // Pick a random board                
                ulong boardMask 
                    = Hand.RandomHand(player1Mask | player2Mask, 5);

                // Create a hand value for each player
                uint player1HandValue = 
                    Hand.Evaluate(boardMask | player1Mask, 7);
                uint player2HandValue = 
                    Hand.Evaluate(boardMask | player2Mask, 7);

                // Calculate Winners                
                if (player1HandValue > player2HandValue)
                {
                    player1Wins++;
                }
                else if (player1HandValue < player2HandValue)
                {
                    player2Wins++;
                }
                else
                {
                    ties++;
                }
                count++;
            }
            // Print results
            Console.WriteLine("Player1: {0:0.0}%",
                (player1Wins + ties / 2.0) / ((double)count) * 100.0);
            Console.WriteLine("Player2: {0:0.0}%",
                (player2Wins + ties / 2.0) / ((double)count) * 100.0);
        }
    }
}

Notice that I've added a new class to the Holdem library. It's called PocketHands. Here are some simple examples of how to use this class:

The simplest way to use this class is to iterate through all possible pocket hands for the specified Pocket Query.

// This will iterate through all the possible "connected suited" pocket hands
foreach (ulong pocketmask in PocketHands.Query("Connected Suited"))
{
    // Insert calculation here.
}

Another way to use this class is to iterate through a Pocket Query given a specific hand match-up.

// Looks at an AKs match up (specifically As Ks) against all possible 
// opponents hands that are connected and suited.
ulong mask = Hand.Evaluate("As Ks"); // AKs
foreach (ulong oppmask in PocketHands.Query("Connected Suited", mask))
{
    // Insert calculation here.
}

This example loops exhaustively through two specific Pocket Query match-ups.

// Iterates through all possible "Connected Suited" versus
// "Connected Offsuit" match ups.
foreach (ulong playermask in PocketHands.Query("Connected Suited"))
{
    foreach (ulong oppmask in PocketHands.Query(
        "Connected Offsuit", playermask))
    {
        foreach (ulong board in Hand.Hands(0UL, playermask | oppmask, 5))
        {
            // Insert Calculation Here
        }
    }
}

It's also possible to use pocket queries while doing random samples of match-ups.

// Randomly selects 100000 possible hands when player starts with a
// suited connector
ulong[] masks = PocketHands.Query("Connected Suited");
for (int trials = 0; trials < 100000; trials++)
{
    // Select a random player hand from the list of possible 
    // Connected Suited hands.
    ulong randomPlayerHandMask = Hand.RandomHand(masks, 0UL, 2);
    // Get a random opponent hand
    ulong randomOpponentHandMask = Hand.RandomHand(randomPlayerHandMask, 2);
    // Get a random board
    ulong boardMask = 
        Hand.RandomHand(randomPlayerHandMask | randomOpponentHandMask, 5);
    // Insert evaluation here
}
    

Outs and draws

Most Hold'em players know what outs are. According to Wikipedia:

[A]n out is any unseen card that, if drawn, will improve a player's hand to one that is likely to win

The question for the programmer is, "Is this definition sufficient to write a function that returns the cards that are outs?" Let's look at the two key points made by Wikipedia. They are:

  1. The hand must improve.
  2. The new hand is likely to win.

Let's start by writing a function that meets the first criterion.

using System;
using HoldemHand;

// A first try at calculating outs
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {       
            string pocket = "As Ac";
            string board = "Kd 8h 9c";
            // Calcuate the outs
            ulong outsmask = 
                Outs(Hand.ParseHand(pocket), Hand.ParseHand(board));

            Console.WriteLine("[{0}] {1} : Outs Count {2}",
                    pocket, board, Hand.BitCount(outsmask));

            // List the cards
            foreach (string card in Hand.Cards(outsmask))
            {
                Console.Write("{0} ", card);
            }
            Console.WriteLine();
        }

        // Return a hand mask of the cards that improve our hand
        static ulong Outs(ulong pocket, ulong board)
        {
            ulong retval = 0UL;
            ulong hand = pocket  board;

            // Get original hand value
            uint playerOrigHandVal = Hand.Evaluate(hand);

            // Look ahead one card
            foreach (ulong card in Hand.Hands(0UL, hand, 1))
            {
                // Get new hand value
                uint playerNewHandVal = Hand.Evaluate(hand  card);

                // If the hand improved then we have an out
                if (playerNewHandVal > playerOrigHandVal)
                {
                    // Add card to outs mask
                    retval = card;
                }
            }

            // return outs as a hand mask
            return retval;
        }
    }
}

Passing this starting hand A♠ A♣, K♦ 8♥ 9♣ into our new method returns the following outs:

  1. K♠, 9♠, 9♥, 8♠, K♥, 9♦, 8♦, K♣, 8♣ - Two pair
  2. A♥, A♦ - Trips
  3. Q♠, J♠, T♠, Q♥, J♥, T♥, Q♦, J♦, T♦, Q♣, J♣, T♣ - Improves Kicker

I think most people would agree that super-sizing your kicker probably doesn't help much here. I think most people would also agree that improving the board doesn't help either. So, let's add two more rules:

  1. The hand must improve.
  2. The new hand improvement must be better than an improved kicker.
  3. The new combined hand must be stronger than the just the board.
  4. The new hand is likely to win.

The following example handles these new rules and allows opponent hands to be added.

using System;
using HoldemHand;

// A first try at calculating outs
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {       
            string pocket = "As Ac";
            string board = "Kd 8h 9c";
            // Calcuate the outs
            ulong outsmask = 
                Outs(Hand.ParseHand(pocket), Hand.ParseHand(board));

            Console.WriteLine("[{0}] {1} : Outs Count {2}",
                    pocket, board, Hand.BitCount(outsmask));

            // List the cards
            foreach (string card in Hand.Cards(outsmask))
            {
                Console.Write("{0} ", card);
            }
            Console.WriteLine();
        }

        // Return a hand mask of the cards that improve our hand
        static ulong Outs(
            ulong pocket, ulong board, params ulong [] opponents)
        {
            ulong retval = 0UL;

            // Get original hand value
            uint playerOrigHandVal = Hand.Evaluate(pocket  board);

            // Look ahead one card
            foreach (ulong card in Hand.Hands(0UL, board  pocket, 1))
            {
                // Get new hand value
                uint playerNewHandVal = Hand.Evaluate(pocket  board  card);
               
                // Get new board value
                uint boardHandVal = Hand.Evaluate(board  card);

                // Is the new hand better than the old one?
                bool handImproved = 
                    playerNewHandVal > playerOrigHandVal &&
                    Hand.HandType(playerNewHandVal) > Hand.HandType(
                    playerOrigHandVal);

                // This compare ensures we move up in hand type.
                bool handStrongerThanBoard = 
                    Hand.HandType(playerNewHandVal) > Hand.HandType(
                    boardHandVal);

                // Check against opponents cards
                bool handBeatAllOpponents = true;
                if (handImproved && handStrongerThanBoard && 
                    opponents != null && opponents.Length > 0)
                {
                    foreach (ulong opponent in opponents)
                    {
                        uint opponentHandVal = 
                            Hand.Evaluate(opponent  board  card);
                        if (opponentHandVal > playerNewHandVal)
                        {
                            handBeatAllOpponents = false;
                            break;
                        }
                    }
                }

                // If the hand improved then we have an out
                if (handImproved && handStrongerThanBoard && 
                    handBeatAllOpponents)
                {
                    // Add card to outs mask
                    retval = card;
                }
            }

            // return outs as a hand mask
            return retval;
        }
    }
}

The problem with outs calculation is that you often don't know the opponent cards you are up against. That makes this calculation a bit subjective. I've had many discussions with different folks about this. One of the more interesting discussions was with Matt Baker. He rewrote my outs function (shown above) to include a heuristic that tries to more accurately account for opponent cards. I won't go into that here, but you can use his code. He generously provided OutsDiscounted and OutsMaskDiscounted.

static int OutsDiscounted(ulong player, ulong board, params ulong[] opponents)

The OutsDiscounted function returns the number of outs for the specified player's pocket hand, the current board and optionally the opponent pocket hands.

ulong OutsMaskDiscounted(ulong player, ulong board, params ulong[] opponents)

The OutsMaskDiscounted function returns a card mask of all of the cards that are outs. You can turn this mask into a string by calling MaskToString().

Draw variation

I've had several folks contact me about other functions that focus on specific outs calculations. These are referred to as "draw" calculations. Wesley Tansey requested several variants of draw functions and offered to test them for me. The result is the following set of functions.

StraightDrawCount

The StraightDrawCount method returns the number of straight draws that are possible for the player, board and dead card configuration. It also filters the results so only player hand improvements are counted.

public static int StraightDrawCount(ulong player, ulong board, ulong dead)

IsOpenEndedStraightDraw

The IsOpenEndedStraightDraw function returns true if the combined mask is an open-ended straight draw. Only straight possibilities that improve the player's mask are considered in this method.

public static bool IsOpenEndedStraightDraw(ulong pocket, 
    ulong board, ulong dead)

IsGutShotStraightDraw

The method IsGutShotStraightDraw returns true if the combined cards contain a gut shot straight draw.

public static bool IsGutShotStraightDraw(ulong pocket, 
    ulong board, ulong dead)

IsStraightDraw

The method IsStraightDraw returns true if the combined cards contain a straight draw.

public static bool IsStraightDraw(ulong pocket, ulong board, ulong dead)

IsOpenEndedStraightDraw

The method IsOpenEndedStraightDraw returns true if the combined cards contain an open-ended straight draw.

public static bool IsOpenEndedStraightDraw(ulong pocket, 
    ulong board, ulong dead)

FlushDrawCount

This method counts the number of hands that are a flush with one more drawn card. However, only flush hands that improve the board are considered.

public static int FlushDrawCount(ulong player, ulong board, ulong dead)

IsFlushDraw

This method returns true if there are 4 cards of the same suit.

public static bool IsFlushDraw(ulong pocket, ulong board, ulong dead)

IsBackdoorFlushDraw

This method returns true if there are three cards of the same suit. The pocket cards must have at least one card in that suit.

public static bool IsBackdoorFlushDraw(ulong pocket, ulong board, ulong dead)

DrawCount

This method returns the number of draws that are possible for the specified HandType. It only returns the counts that improve the player's mask, rather than just the board.

public static int DrawCount(ulong player, ulong board, 
    ulong dead, Hand.HandTypes type)

Demo programs

I've included several demo programs and examples in the downloadable project. This is a quick summary of each of the demo applications:

MultiOddsApp

Screenshot - demo_multioppodds.png

This application graphs several interesting values to allow hand/board scenarios to be scenarios against 1 through 9 opponents. This demo application accepts Pocket Query descriptions for the Player Pocket field. These values include:

  • Win - These are the odds that the given specified hand will end up the winning hand.
  • Hand Strength - These are the odds that the given hand -- not including any future dealt cards -- is currently the best hand.
  • Positive Potential - These are the odds that a currently losing hand will improve to be a winner.
  • Negative Potential - These are the odds that a current winning hand will become a loser.

Hand Odds

Screenshot - demo_handodds.png

This application allows a player pocket hand and an opponent pocket hand definition -- using the Pocket Query Language -- to be entered along with a board definition. The resulting odds are returned for a single random opponent. Since this takes the Pocket Query Language as input, it makes it very easy to try all kinds of pocket hand scenarios that would otherwise require a fair amount of programming to determine the answer.

Acknowledgements

  • Wesley Tansey - For a lot of help with functions determining different variations of outs.
  • Matt Baker - For many discussions on outs and for providing the DiscountedOuts functions.
  • Scott Turner - For using and providing feedback on enhancements.
  • poker-eval - The core to my evaluation engine.
  • ZedGraph - I've used ZedGraph in several of the examples that graph results.
  • Malcolm Crowe - Author of the lexer generator/parser generator tools for C# that I used for the Pocket Query Language.

Disclaimer

I could easily have spent another 6 months editing and tweaking the code and examples. I decided that I had spent enough time on this and had a "critical mass" of features available. So, here is my current code, warts and all.

I don't claim to be a great poker player or even a good one, for that matter. To paraphrase: those who can do, those who can't code. I don't recommend taking any poker advice I may have given (or implied to have given) in this article without independent verification.

I'd also like to encourage feedback. Poker programmers appear to be a somewhat secretive lot, for good reason I'm sure. I've learned a lot from the feedback I've been given over the last year or so. Please keep it coming.

History

First released May 2007

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

About the Author

Keith Rule
Web Developer
United States United States
Member
I work at Tektronix in Beaverton OR. I've been programming for fun since 1975 (I started while in a Computer Explorer Scout group in Spokane WA). I've been programming in C since 1979 and I've been working professionally since 1983.
 
I really enjoy www.codeproject.com. It has saved me an incredible amount of time. I only hope my small contributions have given back some of what I've taken.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionMultiple decksmemberrfrenzob19 Jul '12 - 7:45 
Hello,
 
I'm having a good bit of fun experimenting with this code and was wondering how to extend it to run multiple decks as I rarely play in real life with a single deck.
 
Thanks,
--Brian
QuestionMissing debug targetmemberq-bertsuit10 Jul '12 - 3:18 
I'm getting an error when I try to run the source code, "Visual Studio cannot start debugging because the debug target "c:\program files\NUnit-Net 2.0 2.2.5\bin\nunit-gui.exe" is missing"
 
What am I doing wrong here?
 
Thanks!
AnswerRe: Missing debug targetmemberrfrenzob19 Jul '12 - 8:26 
HandEvaluator is the default startup project. Since this project does not compile to a .exe, there is nothing for the debugger to start. Change the default to the application you wish to run and try the debugger again.
AnswerGood jobmembertwinki8 Jun '12 - 7:02 
Man ,you are the best ,congratulation for this formula .

Questionvisual studio 2010memberivanf815 Jun '12 - 9:43 
Hi,
 
I downloaded the source which looks like it was made in VS 2005.
 
When VS 2010 converts the file everything seems ok except when trying to debug.
 
I always get the error message
 
Visual Studio cannot start debugging because the debug target 'C:Program Files\NUnit-Net-2.0.2.2.5\bin\nunit\nunit-gui.exe is missing.'
 
I checked the output path and build properties and there is no mention of nunit.
 
Has anybody else seen this problem ?
 
Any suggestions would be appreciated!
 
Thanks,
 
Ivan
AnswerRe: visual studio 2010 [modified]memberivanf815 Jun '12 - 11:13 
I managed to get past this problem.
 
In the properties of the solution I checked the "build" option for the new project I created.
 
I am making a standalone project which will depend on the other project primarily to explore the Outs functions described in the article.
 
These functions don't seem to be present in the source that I downloaded.

modified 5 Jun '12 - 18:10.

GeneralCompiled Help File (chm)memberAlmightyEdge17 Jan '12 - 5:54 
Download here:
http://www.mediafire.com/?d6ed3arus2aczq6[^]
GeneralBug on 3 suit boardmemberRiki7123 Sep '10 - 7:50 
Hello Keith and codeproject comunity,
 
first thank you a lot for this great codes.
 
I used this code below and find out that all boards with 3 suits in the first 3 boardcards
(and two player) analysed are not same to pokerstove...
but if there are not 3 suits in the first 3 boardcards the analyse is the same like pokerstove..
 
Can you help me ... in this case I dont know where I have to start...
 
Thank you a lot
 
Riki
 
and I tryed it with this 4 boards:
 
a) board = "9h 6h 4s" with result pokerstove for AsKs (28,3%) <> my code for AsKs(28,3%) o.k.
b) board = "9h 6h 4h" with result pokerstove for AsKs (24,7%) <> my code for AsKs(22,4%) !!!
c) board = "9h 6h 4h 5h" with result pokerstove for AsKs (19,3%) <> my code for AsKs(9,1%) !!!
c) board = "9h 6h 4s 5h" with result pokerstove for AsKs (13,6%) <> my code for AsKs(13,6%) o.k.
 
Here my used code
 

using System;
using HoldemHand;
 
// Iterate through all possible hands
 
// to see how these two hands compare
 
namespace HoldemHand
{
class Program2
{
static void Main(string[] args)
{
// Create hand masks
 
ulong player1Mask = Hand.ParseHand("as ks");
ulong player2Mask = Hand.ParseHand("jd jc");
ulong boardcards = Hand.ParseHand("9h 6h 4s 5h");
ulong deadcards = Hand.ParseHand("");
long player1Wins = 0, player2Wins = 0;
long count = 0;
 
// Iterate through all possible boards
 
foreach (ulong board in Hand.Hands(boardcards | 0UL,
deadcards | player1Mask | player2Mask, 5))
{
// Create a hand value for each player
 
uint player1HandValue = Hand.Evaluate(board | player1Mask, 7);
uint player2HandValue = Hand.Evaluate(board | player2Mask, 7);
 
// Calculate Winners
 
if (player1HandValue > player2HandValue)
{
player1Wins++;
}
else if (player1HandValue < player2HandValue)
{
player2Wins++;
}
count++;
}
 
// Print results
 
Console.WriteLine("Player1: {0Blush | :O .0}%",
((double)player1Wins) / ((double)count) * 100.0);
Console.WriteLine("Player2: {0Blush | :O .0}%",
((double)player2Wins) / ((double)count) * 100.0);
}
}
}
GeneralRe: Bug on 3 suit boardmemberRiki7127 Sep '10 - 9:37 
Srry my fault... of course Laugh | :laugh:
 
this code-part was wrong:
 
"foreach (ulong board in Hand.Hands(boardcards | 0UL, deadcards | player1Mask | player2Mask, 5))"
 
the right code ist:
 
"foreach (ulong board in Hand.Hands(boardcards, deadcards | player1Mask | player2Mask, 5))"
 
Bye Riki
QuestionRapid Question, How can I obtain the best player five cards?memberdino_asd451130 Aug '10 - 22:43 
Hi Keith, in the texas holdem hand evaluation how can obtain the best five card at showdown.
Example. board is "As 2d 3d Ah Kc" player has "4h 5c" -> the best five cards are :"As 2d 3d 4h 5c" ->Streight!
Thanks very much!
Alberto
AnswerRe: Rapid Question, How can I obtain the best player five cards?memberdino_asd451130 Aug '10 - 23:11 
I complete answer my question.
for all, to get the best five cards at showdown this is the code:
 
string GetBestFiveCard(string board, string winner_hand)
{
ulong totalmask = ParseHand(winner_hand+ " " + board);
string name= DescriptionFromMask(totalmask);
ulong bestfivemask= BestFiveCards(mask);
return Hand.MaskToString(bestfivemask);
}
 
private static ulong BestFiveCards(ulong hand)
{
#if DEBUG
if (Hand.BitCount(hand) < 5 || Hand.BitCount(hand) > 7) throw new ArgumentException();
#endif
uint hv = Hand.Evaluate(hand);
 
// Loop through possible 5 card hands
foreach (ulong hand5 in Hand.Hands(0UL, ~hand, 5))
{
if (Hand.Evaluate(hand5) == hv)
return hand5;
}
throw new Exception("Error");
}
GeneralHand Distance Discounted Functionmembermjb4 Dec '09 - 11:28 
There are issues with the current hand distance function in that it calculates the distance from the nuts without taking account of the distribution of the cards (if one player has a card then no other players can also have it). Eg: having the second nuts flushing hand: Ks, 2s with a board 7s, 3s, Qs gives a value of 17th best hand instead of 2nd. Having a hand of Ks, 9d with a board Kc, Kh, 9h gives a value of 2nd best hand instead of nuts.
 
I have been testing the following function for a while and seems to be correct for hands greater than two pair, once you get to hands lower than trips it is not easy to create an algorithm to take account of all possibilities without over counting / undercounting. Moreover, at this type of hand, depending on the board, there are many hands that can beat the hand held and the actual count is probably academic.
 
All feedback welcome. The code is given in the same spirit that keith has released the library.
 
/// <summary>
/// This method returns the mask distance from the best possible
/// mask given this board discounted to take account of the
/// distribution of cards (no draws are considered). The value 0 is the
/// best possible mask. The value 1 is the next best mask and so on.
///
/// Function provided by Matt Baker.
/// </summary>
/// <param name="pocket">The players pocket mask</param>
/// <param name="board">The board mask</param>
/// <returns>An integer representing the distance from the best possible mask</returns>
static public long HandDistanceDiscounted(ulong pocket, ulong board)
{
#if DEBUG
if (BitCount(pocket) != 2) throw new ArgumentException("player must have exactly 2 cards");
if (BitCount(board) != 3 && BitCount(board) != 4) throw new ArgumentException("board must contain 3 or 4 cards");
#endif
uint hv = 0U;
 
SortedDictionary<uint, string> dictHandVal = new SortedDictionary<uint, string>();
SortedDictionary<String, long> dictHandDesc = new SortedDictionary<String, long>();
 
// Current Hand (board and pocket)
uint pocketBoardVal = Hand.Evaluate(pocket | board);
uint pocketBoardType = Hand.HandType(pocketBoardVal);
 
// Board for special cases...
uint boardVal = Hand.Evaluate(board);
uint boardType = Hand.HandType(boardVal);
 
// Trips on the board (without using the pocket cards)...
bool tripsOnBoard = (boardType == (uint)HandTypes.Trips && pocketBoardType == (uint)HandTypes.Trips);
 
string desc = Hand.DescriptionFromMask(pocket | board);
 
// player has a flush, note the highest non-shared (board) card.
if (pocketBoardType == (uint)HandTypes.Flush)
{
uint topFlushCard = 0U;
 
// Seperate out by suit
uint sc = (uint)(((pocket | board) >> (CLUB_OFFSET)) & 0x1fffUL);
uint sd = (uint)(((pocket | board) >> (DIAMOND_OFFSET)) & 0x1fffUL);
uint sh = (uint)(((pocket | board) >> (HEART_OFFSET)) & 0x1fffUL);
uint ss = (uint)(((pocket | board) >> (SPADE_OFFSET)) & 0x1fffUL);
 
if (nBitsTable[sc] >= 5)
topFlushCard = topCardTable[(uint)((pocket >> (CLUB_OFFSET)) & 0x1fffUL)];
else if (nBitsTable[sd] >= 5)
topFlushCard = topCardTable[(uint)((pocket >> (DIAMOND_OFFSET)) & 0x1fffUL)];
else if (nBitsTable[sh] >= 5)
topFlushCard = topCardTable[(uint)((pocket >> (HEART_OFFSET)) & 0x1fffUL)];
else if (nBitsTable[ss] >= 5)
topFlushCard = topCardTable[(uint)((pocket >> (SPADE_OFFSET)) & 0x1fffUL)];
 
desc += " and top non board flush card " + (ranktbl[topFlushCard]);
}
// Special case trips on the board, we need to look at top pocket kicker.
else if (tripsOnBoard)
{
uint topKicker;
 
// Seperate out by suit
uint sc = (uint)((pocket >> (CLUB_OFFSET)) & 0x1fffUL);
uint sd = (uint)((pocket >> (DIAMOND_OFFSET)) & 0x1fffUL);
uint sh = (uint)((pocket >> (HEART_OFFSET)) & 0x1fffUL);
uint ss = (uint)((pocket >> (SPADE_OFFSET)) & 0x1fffUL);
 
uint ranks = sc | sd | sh | ss;
topKicker = topCardTable[ranks];
 
desc += " and " + (ranktbl[topKicker]) + " for a kicker";
}
 
// Always add the pocket hand to the sorted lists.
dictHandVal.Add(pocketBoardVal, desc);
dictHandDesc.Add(desc, 1);
 
// Build a List of unique hands, excluding the pocket cards. As the pocket cards may
// prevent some possible nut hands: Ks 9d - Kc Kh 9h - four of a kind impossible...
foreach (ulong p in Hand.Hands(0UL, board | pocket, 2))
{
hv = Hand.Evaluate(p | board);
 
if (!dictHandVal.ContainsKey(hv))
{
uint hvType = Hand.HandType(hv);
 
uint pv = Hand.Evaluate(p);
uint pvType = Hand.HandType(pv);
 
// Four of a kind but NOT four of a kind on the board (where best kicker counts).
bool hvFourOfAKind = (boardType != (uint)HandTypes.FourOfAKind && hvType == (uint)HandTypes.FourOfAKind);
 
// Trips using a pair on the board.
bool hvTrips = (hvType == (uint)HandTypes.Trips && pvType != (uint)HandTypes.Pair && boardType != (uint)HandTypes.Trips);
 
// Trips on the board.
bool hvTripsOnBoard = (boardType == (uint)HandTypes.Trips && hvType == (uint)HandTypes.Trips);
 
// Get a description of the hand, this will ensure best 5 cards are added only once.
desc = Hand.DescriptionFromMask(p | board);
 
if (hvFourOfAKind || hvType == (uint)HandTypes.Flush || (hvTrips && pocketBoardType != (uint)HandTypes.Trips) || hvTripsOnBoard)
{
// Flush on the board, note the highest non-shared (board) card and only add this once.
if (hvType == (uint)HandTypes.Flush)
{
uint topFlushCard = 0U;
 
// Seperate out by suit
uint sc = (uint)(((p | board) >> (CLUB_OFFSET)) & 0x1fffUL);
uint sd = (uint)(((p | board) >> (DIAMOND_OFFSET)) & 0x1fffUL);
uint sh = (uint)(((p | board) >> (HEART_OFFSET)) & 0x1fffUL);
uint ss = (uint)(((p | board) >> (SPADE_OFFSET)) & 0x1fffUL);
 
if (nBitsTable[sc] >= 5)
topFlushCard = topCardTable[(uint)((p >> (CLUB_OFFSET)) & 0x1fffUL)];
else if (nBitsTable[sd] >= 5)
topFlushCard = topCardTable[(uint)((p >> (DIAMOND_OFFSET)) & 0x1fffUL)];
else if (nBitsTable[sh] >= 5)
topFlushCard = topCardTable[(uint)((p >> (HEART_OFFSET)) & 0x1fffUL)];
else if (nBitsTable[ss] >= 5)
topFlushCard = topCardTable[(uint)((p >> (SPADE_OFFSET)) & 0x1fffUL)];
 
desc += " and top non board flush card " + (ranktbl[topFlushCard]);
}
// Special case trips on the board, we need to look at top non board kicker.
else if (hvTripsOnBoard)
{
uint topKicker;
 
// Seperate out by suit
uint sc = (uint)((p >> (CLUB_OFFSET)) & 0x1fffUL);
uint sd = (uint)((p >> (DIAMOND_OFFSET)) & 0x1fffUL);
uint sh = (uint)((p >> (HEART_OFFSET)) & 0x1fffUL);
uint ss = (uint)((p >> (SPADE_OFFSET)) & 0x1fffUL);
 
uint ranks = sc | sd | sh | ss;
topKicker = topCardTable[ranks];
 
desc += " and " + (ranktbl[topKicker]) + " for a kicker";
}
 
if (!dictHandDesc.ContainsKey(desc))
{
dictHandVal.Add(hv, desc);
dictHandDesc.Add(desc, 1);
}
}
// Add everything else...
else
{
dictHandVal.Add(hv, desc);
}
}
}
 
// Find current mask in the sorted list
long count = dictHandVal.Count - 1;
foreach (uint handval in dictHandVal.Keys)
{
if (handval == pocketBoardVal)
return count;
count--;
}
return -1;
}

GeneralHand Distance Discounted Function Test Casesmembermjb4 Dec '09 - 11:34 
Here are a the test cases that I have used to check the functinality, they are in VB:
 
Private Sub TestHandDistance()
'# These tests come from Lee Jones Preliminary Chapter Quiz
'# Question 8a
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Qs Jc"), Hand.ParseHand("Ad Kh 6s 4h Tc")) = 0, "8a nuts error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("As Ac"), Hand.ParseHand("Ad Kh 6s 4h Tc")) = 1, "8a second best hand error")
 
'# Question 8b
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Ts 8c"), Hand.ParseHand("7h 4s 3d 9h Js")) = 0, "8b nuts error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("5s 6c"), Hand.ParseHand("7h 4s 3d 9h Js")) = 1, "8b second best hand error")
 
'# Question 8c
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("3s 4c"), Hand.ParseHand("Ac 5s Th 2c 9s")) = 0, "8c nuts error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("As Ah"), Hand.ParseHand("Ac 5s Th 2c 9s")) = 1, "8c second best hand error")
 
'# Question 8d
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("3h 3s"), Hand.ParseHand("3c Jc Qh 6c 3d")) = 0, "8d nuts error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Qs Qd"), Hand.ParseHand("3c Jc Qh 6c 3d")) = 1, "8d second best hand error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Js Jd"), Hand.ParseHand("3c Jc Qh 6c 3d")) = 2, "8d third best hand error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("6s 6d"), Hand.ParseHand("3c Jc Qh 6c 3d")) = 3, "8d fourth best hand error")
 
'# Question 8e
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("8d 7d"), Hand.ParseHand("6d 9d 9h Kh 5d")) = 0, "8e nuts error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("9s 9c"), Hand.ParseHand("6d 9d 9h Kh 5d")) = 1, "8d second best hand error")
 
'# Question 8f
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Ad 7d"), Hand.ParseHand("Tc 5h Jd Qs Kc")) = 0, "8f nuts error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("9s 9c"), Hand.ParseHand("Tc 5h Jd Qs Kc")) = 1, "8f second best hand error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Ks Kd"), Hand.ParseHand("Tc 5h Jd Qs Kc")) = 2, "8f third best hand error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Qd Qc"), Hand.ParseHand("Tc 5h Jd Qs Kc")) = 3, "8f fourth best hand error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Js Jc"), Hand.ParseHand("Tc 5h Jd Qs Kc")) = 4, "8f fifth best hand error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Ts Td"), Hand.ParseHand("Tc 5h Jd Qs Kc")) = 5, "8f sixth best hand error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("5s 5d"), Hand.ParseHand("Tc 5h Jd Qs Kc")) = 6, "8f seventh best hand error")
 
'# Question 8g
'# Note 3 queens is the lowest possible hand that can be the nuts when 5 board cards are dealt.
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Qh Qc"), Hand.ParseHand("Js 2c Qd 4h 7c")) = 0, "8g nuts error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Jh Jc"), Hand.ParseHand("Js 2c Qd 4h 7c")) = 1, "8g second best hand error")
 
'# Question 8h
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Ah 2h"), Hand.ParseHand("4h 6s 8h Jd 9h")) = 0, "8h nuts error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Kh 2h"), Hand.ParseHand("4h 6s 8h Jd 9h")) = 1, "8h second best hand error")
 
'# These tests come from the Hendon Mobs Nut test and will test a full board
'http://www.thehendonmob.com/guide/index.php?p=2.3.1
'# Q1
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Ks Qs"), Hand.ParseHand("4d 8s 6s Jh 3s")) = 1, "1a hendon hand a error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("As 2s"), Hand.ParseHand("4d 8s 6s Jh 3s")) = 0, "1a hendon hand b error")
 
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Qs Ts"), Hand.ParseHand("As 2d Js Ac Ks")) = 0, "1b hendon hand a error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Ad Ah"), Hand.ParseHand("As 2d Js Ac Ks")) = 1, "1b hendon hand b error")
 
'# Q2
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Qd Qh"), Hand.ParseHand("Qc 6s 4c Jh 3h")) = 2, "2a hendon hand a error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("7c 5c"), Hand.ParseHand("Qc 6s 4c Jh 3h")) = 0, "2a hendon hand b error")
 
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Qd Jd"), Hand.ParseHand("9d 4c 8s Ts 7h")) = 0, "2b hendon hand a error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Jh Tc"), Hand.ParseHand("9d 4c 8s Ts 7h")) = 1, "2b hendon hand b error")
 
'# Q3
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Ad 2d"), Hand.ParseHand("4c 9s 5d 3s 8h")) = 2, "3a hendon hand a error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("7c 6c"), Hand.ParseHand("4c 9s 5d 3s 8h")) = 0, "3a hendon hand b error")
 

Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("8c Ad"), Hand.ParseHand("8s 2c 6d Ac 8d")) = 1, "3b hendon hand a error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("As Ah"), Hand.ParseHand("8s 2c 6d Ac 8d")) = 1, "3b hendon hand b error")
 

'# Special Cases
'# four of a kind on the board.
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Ah 2h"), Hand.ParseHand("5h 5s 5d 5c 9h")) = 0, "four of a kind with best kicker as pocket card error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("2c 2h"), Hand.ParseHand("5h 5s 5d 5c Ah")) = 0, "four of a kind with best kicker as board card error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Kh 2h"), Hand.ParseHand("5h 5s 5d 5c 9h")) = 1, "four of a kind with second best kicker as pocket card error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("2c 2h"), Hand.ParseHand("5h 5s 5d 5c 2s")) = 11, "four of a kind with worst (11th) kicker as pocket card error")
 
'# trips on the board.
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Ac Kc"), Hand.ParseHand("7h 7s 7d")) = 13, "trips on board best kickers (without a full house or 4 of a kind) error")
'# Note can't have a 2 high kicker.
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("2c 3c"), Hand.ParseHand("7h 7s 7d")) = 23, "trips on board worst kickers (without a full house or 4 of a kind) error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Kc Tc"), Hand.ParseHand("7h 7s 7d")) = 14, "trips on board kickers K T (without a full house or 4 of a kind) error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("8c 2c"), Hand.ParseHand("7h 7s 7d")) = 19, "trips on board kickers 8 2 (without a full house or 4 of a kind) error")
 
'# flush on the board.
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Ac 2c"), Hand.ParseHand("9c 8c 3c")) = 0, "nut flush error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Kd 2d"), Hand.ParseHand("9d 8d 3d")) = 1, "second best flush error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("9h 2h"), Hand.ParseHand("7h 8h 3h, 4h, Td")) = 5, "fifth best flush error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Ks 2s"), Hand.ParseHand("As 8s 3s")) = 0, "nut flush top card on board error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Qs Js"), Hand.ParseHand("As 8s 3s")) = 1, "second best flush top card on board error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("2h 3h"), Hand.ParseHand("Ah Kh 4h")) = 7, "seventh best flush top card on board error")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("8d 7d"), Hand.ParseHand("Qd 3d 2d")) = 5, "fifth best flush top card on board error")
 
'# 2 pair
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Ac Ks"), Hand.ParseHand("Ad Kc Ts")) = 4, "2 pair error 1")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Js Th"), Hand.ParseHand("Jc Td 2s")) = 3, "2 pair error 2")
Debug.Assert(Hand.HandDistanceDiscounted(Hand.ParseHand("Js 7c"), Hand.ParseHand("7s 3d 3h")) = 14, "2 pair error 2")
End Sub

GeneralMultiOddsApp by Keith RulememberTimo Michaelis22 Sep '09 - 6:51 
Hello CodeProject community.
 
Because I can't find out how to contact Keith Rule directly I now choose this way to either get in contact with him or maybe get some help by people who are willing to help me out.
 
I am interested in Keith Rules software tool called "MultiOddsApp".
I would like to practise my Pokergame with it.
In the first moment I thought, that Keith tools are Windows executable programs.
I am wrong, am I?
 
I need to run some sort of Coding program, maybe Visual Basic?
I already downloaded it and tried to open Keith Poker tool in it.
But I got a few error messages and I didn't bring it to work at all.
 
So maybe one here is willing to explain to me how I can run "MultiOddsApp" though I have never got contact with Visual Basic before?
 
I would be really happy about your help.
 
Thank you very much.
 
Sorry, english is not my original language.
 
Many greetings
GeneralRe: MultiOddsApp by Keith RulememberKeith Rule20 Oct '09 - 10:24 
Timo Michaelis wrote:
I would like to practise my Pokergame with it.
In the first moment I thought, that Keith tools are Windows executable programs.
I am wrong, am I?

 
I'm not a poker expert nor am I trying to produce a general tool for poker enthusiast. I'm a programmer that found Poker analysis to be an interesting programming challenge.
 
Codeproject is primarily a site for programmers. The download associated with this article is the source for the project rather than the binaries for that reason. The MultiOddsApp is intended to be an example of how to use the underlying library.
 
The easiest way to get a binary would be to download Visual C# 2008 Express Edition (http://www.microsoft.com/exPress/download/[^])
 
You should be able to load, compile and then execute the application your interested in.
 
Good Luck,
 
Keith Rule

Questionzedgraph control [modified]memberhamidhakimi11 Jul '09 - 0:47 
pleas help me....
I want fill color between two curves and between one curve to Xaxis in the chart on the zedgraph control.what I do? thanks ....
 
modified on Sunday, July 12, 2009 8:07 AM

GeneralMeaning of "outs"membersupercat913 Feb '09 - 13:27 
In some contexts, especially when an opponent's hand can be guessed at, it's possible for an improved kicker to be an "out" (yielding a tie on a hand which would otherwise be a loss). For example, player holds A2, opponent is believed to hold Ax (based upon pre-flop betting), and the flop comes AKK rainbow.
 
If the opponent's other card is a three through queen, you have a 2 as an out to win, and any card higher than the opponent's card as an out to tie (e.g. if the opponent holds A6, then after the flop your AAKK2 would lose to AAKK6, but if a seven comes up both you and the opponent would hold AAKK7; an ace would leave you both holding AAAKK, and a king would leave you both holding KKKAA).
 
Obviously one will seldom know perfectly what cards an opponent has (unless cards are being shown all-in, in which case you can't do anything with the information) but one can frequently make an educated guess. While one must be careful to consider implied as well as actual pot odds in situations where one is likely to at best tie, I would still consider it very useful to know what outs exist that would give the player a tie (in one on-line game, I held a crummy hand in the Big Blind, everything was checked through to the river, and the board came down with a four of a kind and an ace kicker; my hand was no better than the board, but I'd still say the board's s four-of-a-kind counted as an out).
 
Incidentally, it's possible for a board card to "improve" one's hand and yet make it far worse. For example, holding 22 with a flop of 233, having another 3 come up on the turn would likely make one's hand somewhat worse (it would lose to any other pocket pair); if a fourth 3 comes up on the river, the pocket deuces would go from very good (only beaten by three exact holdings) to completely worthless (losing to any other possible hand). The third or fourth 3 in that situation should not be considered an "out" for the person with the pocket deuces. Instead, they would transform the winning 22233 into a dubious 33322 or a losing 33332.
 
BTW, I haven't seen a lot of poker games, the ones I've played give bulky but incomplete textual descriptions of hands (e.g. "three of a kind sixes with a nine kicker", or "flush, jack high"), when it would be more concise and more informative to say "three of a kind 66694" or "flush J9742s". I wonder why they don't give something more like the latter?
GeneralWeightsmemberesepepe28 Jan '09 - 1:26 
Is there support for "weights" in your classes (as described by davidson master thesis)?
QuestionHand analysis and reportingmemberLeo Imrie3 Nov '08 - 10:29 
Hi Keith,
 
Thank you for posting these invaluable tools for all to use, it certainly looks like you have spent a large amount of time writing and perfecting these libraries.
 
I am an avid online player (a poor one unfortunately), and what you might diplomatically call an "enthusiastic" C# developer. The reason I came across your page on code project is I have recently started writing a poker-related program, albeit not in the traditional sense - i.e. neither an odds calculator nor a bot. What I have in mind involves the analysis of played hands, as my background is primarily in database development and business intelligence. I would be very interested to hear your feedback on some of my ideas, if you have the time of course. Is it possible to email you directly?
 
Many thanks,
 
Leo
QuestionBug in Query Language?memberHuguesD7 Oct '08 - 2:12 
Dear Keith,
 
your code is definitely brilliant and very useful. Many thanks for that.
 
I work with it quite a lot and I found an issue about the query language and the parser:
ulong[] List = PocketHands.Query("AK") returns a list of 12 hands, all of them being offsuit.
So, this line of code produces the same result as ulong[] List = PocketHands.Query("AKo").
 
It should return a list of 16 hands (offsuit and suited), no?
 
Since I have absolutely no knowledge of Lex and Yacc, I really have no idea to fix this issue.
 
Thank you in advance for your help
And again many many thanks for providing this code
AnswerRe: Bug in Query Language?memberJohn Trebor7 Aug '10 - 10:13 
You could just use (AKs | AKo) for the desired hands.
AnswerRe: Bug in Query Language?memberAlmightyEdge17 Jan '12 - 5:23 
This behavior is intended.
AK should only select AKo and not to AKo + AKs.
QuestionBug in RandomHand?membergubbabump3 Oct '08 - 0:01 
I've been doing some range calculations feeding ulong[]-arrays into RandomHand, and it seems that the last element of the array is never chosen. I think this is because rand.Next(int maxValue) uses an exclusive upper bound. Hmm, poor explanation, I'll show the code (from HandItorator.cs):
 
static public ulong RandomHand(Random rand, ulong[] list, ulong dead, int ncards)
        {
            ulong mask = 0UL;
            do
            {
                mask = list[rand.Next(list.Length - 1)];  //<-- shouldn't this be "mask = list[rand.Next(list.Length)];" ?
            } while ((mask & dead) != 0);
 
            return RandomHand(rand, mask, dead, ncards);
         }
If I replace "list.Length -1" with just "list.Length" it seems to work as intended.
AnswerRe: Bug in RandomHand?memberHuguesD7 Oct '08 - 23:31 
I did this change and performed a lot of tests & comparisons with other odds calculators and you are right.
Thank you for the fix
 
Any idea about my query issue?
GeneralRe: Bug in RandomHand?memberJohn Trebor7 Aug '10 - 10:10 
Anybody knows if this bug is fixed in the current version? If not: Is it only in RandomHand with the specified arguments or is it in all RandomHand-methods?

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 20 May 2008
Article Copyright 2007 by Keith Rule
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid