11

Possible Duplicate:
How to iteratively generate k elements subsets from a set of size n in java?

I want to build my own poker hand evaluator but am having trouble with a particular part.

If two players get dealt a two card hand, then there will be 48 cards left in the pack. In Texas Hold'em a further 5 possible community cards are then dealt (this is called the board). I want to enumerate / loop through all the 48 choose 5 possible combinations of boards and count the times Player A wins and the times Player B wins and when they tie.

I'm not sure how I can systematically loop through every 5 card combination. Does anyone have any ideas? The cards are represented as an array of class Card, but I could also represent them as a bitset if this makes it faster.

I'm doing this in Java.

Many thanks

Community
  • 1
  • 1
Joe
  • 4,852
  • 10
  • 63
  • 82
  • 4
    *"Does anyone have any ideas?"* 1) Search for duplicates before asking a question 2) Ask a (specific) question. 3) Be good to your mum. – Andrew Thompson Dec 04 '11 at 13:21
  • 9
    @Andrew Thompson: except that his question *is* very specific. It is specifically about evaluating all C(48,5) possible matchups between two players that would be all-in (at least one all-in) preflop in a Texas Hold'em game. How much more specific than that could it be? He even goes on to explain how he has modelled the cards and that he has his own hand evaluator (apparently). So his question is about generating the C(48,5) boards. See my answer. – TacticalCoder Dec 04 '11 at 23:04
  • @user988052 A 'statement of need' (specific or not) does not equate to a question. The only question asked in that post is the sentence I quoted. – Andrew Thompson Dec 04 '11 at 23:33
  • 5
    @Andrew Thompson: well except that the context is very obvious from the very phrase right before the question, in the same paragraph: *"I'm not sure how I can systematically loop through every 5 card combinations. Does anyone have any ideas?"*. I did help by answering the (obvious) OP's question. What about also helping by using your rep to reformulate the question? – TacticalCoder Dec 04 '11 at 23:43

2 Answers2

17

(disclaimer: I've written a very fast poker hand evaluator)

I want to enumerate / loop through all the 48 choose 5 possible combinations of boards and count the times Player A wins and the times Player B wins and when they tie.

You do not want to evaluate C(48,5) (1 712 304) hands every time you have a matchup between two players preflop: most programs simply use a precomputed lookup table between all the possible matchups between two players preflop.

For example say you have "Ac Ad" vs "7c 6c", you simply go look in a lookup table that contains: 1 333 573, 371 831, 6900 (where 1 333 573 is the number of times "Ac Ad" wins, 371 831 is the number of times "7c 6c" wins and 6 900 is the number of ties (they sum to 1 712 304). To gain some room you can discard the 6 900, knowing that the number of ties shall always be C(48,5) - (wins 1 + wins 2).

(more on the lookup table at the end of this answer)

But to answer your question:

I'm not sure how I can systematically loop through every 5 card combination.

If you do really want to loop trough every combination, you have to know that poker hand evaluators are typically the kind of program that need to be very, very fast. These programs can typically evaluate hundreds of millions of hands per second (you read correctly: hundreds of millions).

When you need such high-performance "number crunching" you can forget about "design patterns" and "OO". What you want is raw speed.

For example the following will pass through the innermost loop C(48,5) times and it is quite fast:

    for ( int i = 0; i < n; i++ ) {
        for ( int j = i + 1; j < n; j++ ) {
            for ( int k = j + 1; k < n; k++ ) {
                for (int l = k + 1; l < n; l++) {
                    for (int m = l + 1; m < n; m++) {
                        ...
                    }
                }
            }
        }
    }

Once again for two players preflop it's probably a very bad idea: you're gonna be much faster by using a lookup table.

But for three players preflop (where it's impractical to use a preflop tables, there are too many matchups), you may want to loop like that, over C(46,5) hands, using the five nested loops (of course you need to use i,j,k,l,m to get the correct 5 cards out of the 46 cards that are left). Then, once you've got the 5 cards, you use a fast hand evaluator that gives you the best out of 7 (the 5 of the board + the two of each player).

Regarding the lookup table:

Most people use an approximated 169 vs 169 lookup table ("Ac Kd", "As Kh", "Ad Ks", etc. all become "AK offsuit" and the C(52,2) possible starting hands become grouped in 169 type of starting hands). The Wikipedia article explains how to get the 169 non-equivalent starting hands:

http://en.wikipedia.org/wiki/Texas_hold_%27em_starting_hands

They're non equivalent when you take one hand into account, but as soon as you do hand 1 vs hand 2 the "169 vs 169" is an approximation (a quite good one that said).

Of course you can get fancier. There are only C(52,2) (which gives 1326) real different Hold'em starting hands, which means it's very practical to build a perfect lookup table (no approximation at all) on modern computers (C(1326,2) ain't that big) if you really need perfect numbers. If you can live with approximation, go for the 169 vs 169 table (it would need C(169,2) or 14 196 entries).

TacticalCoder
  • 6,275
  • 3
  • 31
  • 39
  • 1
    Thanks for your fantastic answer! So helpful. In fact the reason I am trying to build this at all is that I got the 169 vs 169 look-up table from http://www.pokerstove.com/analysis/preflopeq.php and was trying to figure out how the figures were arrived at and how I could combine individual hand vs hand equity values to arrive at range vs range values. It seemed like if I could build my own implementation then that learning experience would clear up the maths. Cheers again for your answer. – Joe Dec 05 '11 at 13:28
  • @Joe: no issue, that is what StackOverflow is for : ) Regarding the *"individual hand vs range"* or *"range vs range"*, for two players: you can basically simply make several lookup in the 169 vs 169 table. For example you need "JJ vs a range of AA, KK and QQ" you simply do "JJ vs AA" + "JJ vs KK" + "JJ vs QQ" / 3. It's a bit more complicated than that but it's basically one way to go. And you can always use PokerStove to verify your results : ) – TacticalCoder Dec 05 '11 at 16:56
  • @TacticalCoder, However the code above is static. When the number of `for` is dynamic, this approach wouldn't work. – Pacerier Jan 17 '16 at 22:36
16
  1. Initialize an array (A) to the first 5 indices. (0,1,2,3,4)
  2. Move the last possible index in A to the next position. A[k]++
  3. Move the following indices to the following successive positions. (A[k+1] = A[k] + 1, ...). If some index would become too large, try with an earlier index in step 2.
  4. Yield the elements at the current indices in A.
  5. If possible, repeat from step 2.

Implemented as an iterator:

public class CombinationIterator<T>
        implements Iterable<List<T>>,
                   Iterator<List<T>> {
    private List<T> items;
    private int choose;
    private boolean started;
    private boolean finished;
    private int[] current;

    public CombinationIterator(List<T> items, int choose) {
        if (items == null || items.size() == 0) {
            throw new IllegalArgumentException("items");
        }
        if (choose <= 0 || choose > items.size()) {
            throw new IllegalArgumentException("choose");
        }
        this.items = items;
        this.choose = choose;
        this.finished = false;
    }

    public Iterator<List<T>> iterator() {
        return this;
    }

    public boolean hasNext() {
        return !finished;
    }

    public List<T> next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }

        if (current == null) {
            current = new int[choose];
            for (int i = 0; i < choose; i++) {
                current[i] = i;
            }
        }

        List<T> result = new ArrayList<T>(choose);
        for (int i = 0; i < choose; i++) {
            result.add(items.get(current[i]));
        }

        int n = items.size();
        finished = true;
        for (int i = choose - 1; i >= 0; i--) {
            if (current[i] < n - choose + i) {
                current[i]++;
                for (int j = i + 1; j < choose; j++) {
                    current[j] = current[i] - i + j;
                }
                finished = false;
                break;
            }
        }

        return result;
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }
}

Equivalent in C#, using an Iterator method:

public IEnumerable<IList<T>> Combinations<T>(IEnumerable<T> items, int choose)
{
    if (items == null) throw new ArgumentNullException("items");

    var itemsList = items.ToList();
    int n = itemsList.Count;

    if (n < 1) throw new ArgumentException("Must contain at least one item.", "items");
    if (choose <= 0 || choose >= n) throw new ArgumentOutOfRangeException("choose");

    var indices = Enumerable.Range(0, choose).ToArray();

    bool moreWork = true;
    while (moreWork)
    {
        yield return indices.Select(i => itemsList[i]).ToList();

        moreWork = false;
        for (int i = choose - 1; i >= 0; i--)
        {
            if (indices[i] < n - choose + i)
            {
                indices[i]++;
                for (int j = i + 1; j < choose; j++)
                {
                    indices[j] = indices[i] - i + j;
                }
                moreWork = true;
                break;
            }
        }
    }
}
Markus Jarderot
  • 86,735
  • 21
  • 136
  • 138
  • Thank you for a useful piece of code. – Erel Segal-Halevi Jan 25 '12 at 17:40
  • Amazingly useful. I ported the code to C# for use with "foreach". The method there is slightly different though, with `Boolean MoveNext()` to prepare the next result and return whether there is one, and the `List Current` property for fetching that result. This just meant the prepared result had to be stored, but besides that, very little changes. – Nyerguds Mar 20 '13 at 13:27
  • @Nyerguds I added a C# version, making better use of language conventions. – Markus Jarderot Mar 20 '13 at 16:18
  • Oh, pretty. I used this code for optimizing the folders of a project with 4 applications which include many of the same dlls. The installed end result would end up in one folder, but to be able to have an installer with component selection, they were built to separate folders. This code helped me a lot to optimize it and move all common files to different folders :) – Nyerguds Mar 21 '13 at 11:51