Search code examples
iterationlookup-tablespoker

Omaha Hi Hand Evaluator


Currently I'm trying to port Keith Rule's Texas Holdem Hand Evaluator to Omaha Hi:

After thinking more about the algorithm, I found a solution which gives me the right percentages for the hands and everything is fine..

But it's really really slow. How can I speed things up?

As the only thing I do right now is to look-up a normal five card hands, a LUT might be right for me. Anyone integrated one before?

static void Main(string[] args)
    {
        long count = 0;
        double player1win = 0.0, player2win=0.0;
        ulong player1 = Hand.ParseHand("Ad Kd As Ks");
        ulong player2 = Hand.ParseHand("Th 5c 2c 7d");
        foreach (ulong board in Hand.Hands(0, player1 | player2, 5))
        {
            uint maxplayer1value = 0, maxplayer2value = 0;
            foreach (ulong boardcards in Hand.Hands(0, ulong.MaxValue ^ board, 3))
            {
                foreach (ulong player1hand in Hand.Hands(0Ul, ulong.MaxValue ^ player1, 2))
                {
                    uint player1value = Hand.Evaluate(player1hand | boardcards, 5);
                    if (player1value > maxplayer1value) maxplayer1value = player1value;

                }
            }
            foreach (ulong boardcards in Hand.Hands(0, ulong.MaxValue ^ board, 3))
            {
                foreach (ulong player2hand in Hand.Hands(0UL, ulong.MaxValue ^ player2, 2))
                {
                    uint player2value = Hand.Evaluate(player2hand | boardcards, 5);
                    if (player2value > maxplayer2value) maxplayer2value = player2value;

                }
            }

            if (maxplayer1value > maxplayer2value)
            {
                player1win += 1.0;
            }
            else if (maxplayer2value > maxplayer1value)
            {
                player2win += 1.0;
            }
            else
            {
                player1win += 0.5;
                player2win += 0.5;
            }
            count++;
        }
        Console.WriteLine("Player1: {0:0.0000} Player2: {1:0.0000} Count: {2}", player1win / count * 100, player2win / count * 100, count);
        Console.ReadLine();       
    }

Solution

  • Looks like you're trying to create equity calculator. I've done this as well, but not for Omaha (Texas Hold'em instead). With then players to evaluate, I've got about ~200K hands per second, which gives accurate result enough in no time. If there only two players to evaluate, I can get up to 4 million evaluations per second.

    I used bitmasks for hands. One 64-bit integer to represent card, hand or entire board. You only need actually 52 of it, obviously. By using bitwise-operators, things get going rather quickly. Here's a quick sample from my project (in C++ tho). It's using 2 + 2 evaluator for fast look-ups:

    
            while (trial < trials) {
                    /** I use here a linked list over the hand-distributions (players).
                      * This is kind of natural as well, as circle is the basic
                      * shape of poker.
                      */
                    pDist = pFirstDist;
    
                    unsigned __int64 usedCards = _deadCards;
                    bool collision;
    
                    /** Here, we choose random distributions for the comparison.
                      * There is a chance, that two separate distributions has
                      * the same card being picked-up. In that case, we have a collision,
                      * so do the choosing again.
                      */
                    do {
                            pDist->Choose(usedCards, collision);
    
                            /** If there is only one hand in the distribution (unary),
                              * there is no need to check over collision, since it's been
                              * already done in the phase building them (distributions).
                              */
                            if (pDist->_isUnary)
                                    collision = false;
    
                            pDist = pDist->_pNext;
                    } while (pDist != pFirstDist && !collision);
    
                    if (collision) {
                            /** Oops! Collision occurred! Take the next player (hand-
                              * distribution and do this all over again.
                              *
                              */
                            pFirstDist = pDist->_pNext;
    
                            continue;
                    }
    
                    unsigned __int64 board = 0;
    
                    /** Pick a board from the hashed ones, until it's unique compared to
                      * the distributions.
                      *
                      */
                    do {
                            if (count == 1) {
                                    board = boards[0];
                                    collision = false;
                            } else {
                                    board = boards[Random()];
                                    collision = (board & usedCards) != 0;
                            }
                    } while (collision);
    
                    board |= _boardCards;
    
                    int best = 0, s = 1;
    
                    do {
                            pDist->_currentHand |= board;
    
                            unsigned long i, l = static_cast<unsigned long>(pDist->_currentHand >> 32);
                            int p;
                            bool f = false;
    
                            /** My solution to find out the set bits.
                              * Since I'm working on a 32-bit environment, the "64-bit"
                              * variable needs to be split in to parts.
                              */
                            if (_BitScanForward(&i, l)) {
                                    p = _evaluator->_handRanks[53 + i + 32]; // Initial entry to the 2 + 2 evaluator hash.
                                    l &= ~(static_cast<unsigned long>(1) << i);
                                    f = true;
                            }
    
                            if (f)
                                    while (_BitScanForward(&i, l)) {
                                            l &= ~(static_cast<unsigned long>(1) << i);
                                            p = _evaluator->_handRanks[p + i + 32];
                                    }
    
                            l = static_cast<unsigned long>(pDist->_currentHand & 0xffffffff);
    
                            if (!f) {
                                    _BitScanForward(&i, l);
    
                                    p = _evaluator->_handRanks[53 + i];
                                    l &= ~(static_cast<unsigned long>(1) << i);
                            }
    
                            while (_BitScanForward(&i, l)) {
                                    l &= ~(static_cast<unsigned long>(1) <<_handRanks[p + i];
                            }
    
                            pDist->_rank = p;
    
                            /** Keep the statistics up. Please do remember, that
                              * equity consist of ties as well, so it's not a percentual
                              * chance of winning.
                              */
                            if (p > best) {
                                    pWinner = pDist;
                                    s = 1;
                                    best = p;
                            } else if (p == best)
                                    ++s;
    
                            pDist = pDist->_pNext;
                    } while (pDist != pFirstDist);
    
                    if (s > 1) {
                            for (unsigned int i = 0; i _rank == best) {
                                            _handDistributions[i]->_ties += 1.0f / s;
                                            _handDistributions[i]->_equity += 1.0f / s;
                                    }
                    } else {
                            ++pWinner->_wins;
                            ++pWinner->_equity;
                    }
    
                    ++trial;
    
                    pFirstDist = pDist->_pNext;
            }
    

    Please refer to the 2 + 2 evaluator, which is quite easy to adapt in your own needs.