Card game and simulation library and tools. Part of the OneJoker project.

Project maintained by Lee Daniel Crocker
Lee's blog is etceterology.

onejoker
CC-0: To the extent possible under law, I, Lee Daniel Crocker waive all copyright and related or neighboring rights to all creative works original to me.

Using the library in your C code

Combiner functions

The oj_combiner object represents the set of combination of k cards from a larger set of n cards. For example, if n = 52 and the larger set is the standard 52-card deck, and k = 5, then the object represents all 2,598,960 possible poker hands. Note that this includes all combinations but not permutations; that is, if two subset contain identical cards but differ only in order, they are considered the same combination. Once created, the object can be used to get information about the set of combinations, to iterate over combinations, or to produce random combinations.

int64_t ojc_binomial(int n, int k)

This function just returns the number of combinations of k items from a set of n. It is used extensively in the library code, and so is included here for convenience. Since this is optimized for use in card simulations, it requires n and k to be 54 or less.

int ojc_new(oj_combiner *cp, oj_cardlist *deck, oj_cardlist *hand, int k, int64_t count)

Creates a new combiner object. One must first create and initialize two oj_cardlist objects. One deck object which is the larger set from which the combinations are taken, and a hand object into which the combinations will be written.Argument k is the number of cards in each combination. The hand object must be writable and have room for at least k cards. The final argument count is used to limit the number of combinations produced. This is mostly for random combinations in Monte Carlo simulations. If the value 0 is given, the combiner will produce every combination exactly once.

Once the object is created, you will generally iterate over combinations in one of two ways: either you want every combination in order, or you want a certain number of random combinations. The two next functions below do this. They are “generator” functions; that is, each time they are called they produce the next result.

int ojc_next(oj_combiner *cp)

Writes into cp->hand the next combination in colexicographical order. Returns 0 if we have reached the last combination, otherwise returns 1. If the combiner object was created with a count of 0, this will enable you to step through every combination exactly once with a loop such as this:

ojc_new(&cmb, &deck, &hand, k, 0ll);
while (ojc_next(&cmb)) {
    . . .
}

int ojc_next_random(oj_combiner *cp)

Writes into cp->hand a random combination. Returns 0 if we have reached the last combination based on the count passed into ojc_new(), otherwise returns 1. This will enable you to run a Monte Carlo simulation with a loop like this:

ojc_new(&cmb, &deck, &hand, k, 1000000ll);
while (ojc_next_random(&cmb)) {
    . . .
}

int64_t ojc_colex_rank(oj_combiner *cp, oj_cardlist *hp)

Given a pointer hp to a hand of the appropriate size, returns its rank in the sequence of combinations in colexicographical order. That is, it returns the number of iterations at which this hand would appear if you stepped through all combinations as described above. This function does not affect the hand passed to it, so you can pass it cp->hand. You can do this, for example, to discover the rank of hands produced by ojc_next_random().

int ojc_colex_hand_at(oj_combiner *cp, int64_t rank, oj_cardlist *hp)

This is the reverse of ojc_colex_rank(). That is, given a rank value, it will write into the given cardlist the hand that would appear at that point in the sequence if you iterated over all combinations as described above.

Next: Poker functions