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

Card list functions

The oj_cardlist structure manages an array-like list of cards. The actual contained array must be allocated separately and passed into the initialization function ojl_new().
These may be allocated in any way desired, but the array of cards must be validly accessible memory for the life of the structure, or else memory curruption is likely. It is recommended that all access to the list be done through the functions below, which do error checking and maintain data integrity for special-purpose functions.

Many of these functions return negative integer error codes as follows:

    OJE_NOTFOUND        // The item sought was not found in the list
    OJE_RDONLY          // Attempt was made to alter read-only list
    OJE_FULL            // Attempt was made to add to list already full
    OJE_DUPLICATE       // Attempt was made to add card already in list
    OJE_BADINDEX        // Access out of bounds

int ojl_new(oj_cardlist *p, oj_card *arr, int size)

Argument p is a pointer to the structure to initialized, arr is an array of oj_card, size is the count of elements of the array. The structure and the array must already be allocated. The size of the array sets a maximum for the number of cards that may be contained in the structure, and this maximum never changes throughout the life of the structure.

static oj_card dbuf[52];
static oj_cardlist deck;

int main(int argc, char *argv[]) {
    oj_card hbuf1[10], *hbuf2;
    oj_cardlist hand, *phand;

    ojl_new(&deck, dbuf, 52);
    ojl_new(&hand, hbuf, 10);
    phand = malloc(sizeof(oj_cardlist));
    hbuf2 = malloc(5 * sizeof(oj_card));
    ojl_new(phand, hbuf2, 5);
    . . .
}

int ojl_pflag(oj_cardlist *p, int mask)
int ojl_set_pflag(oj_cardlist *p, int mask)
int ojl_clear_pflag(oj_cardlist *p, int mask)
int ojl_eflag(oj_cardlist *p, int mask)
int ojl_set_eflag(oj_cardlist *p, int mask)
int ojl_clear_eflag(oj_cardlist *p, int mask)

The cardlist structure keeps two sets of flags: “ephemeral” flags used for short-term status indications that are cleared whenever the contents of the list change, and “persistent” flags for describing general attributes of the list. Pre-defined flag masks are:

    OJF_RDONLY = 0x01   // Persistent: list is read-only
    OJF_UNIQUE = 0x02   // Persistent: list disallows duplicates
    OJF_SORTED = 0x01   // Ephemeral: list is currently in sorted order

The most common use for these will likely be using ojl_set_pflag() to create read-only or unique lists. But programs are free to define other masks and use these flags for their onw needs.

Lists with the OJF_RDONLY mask set will return an error code whenever any attempt is made to alter them, with one exception: they will allow the OJF_RDONLY flag to be cleared. List with the OJF_UNIQUE flag set will return an error when an attempt is made to add a card already present in the list. These lists also behave differently in some of the functions below: for example, they hash and compare equal regardless of the order of cards in the list.

oj_card ojl_get(oj_cardlist *p, int index)

Basic indexed access to the list. Returns the card at the given index, or a negative error code OJE_BADINDEX if the given index is out of bounds.

oj_card ojl_set(oj_cardlist *p, int index, oj_card card)

Replace the card at index with card . Returns the card formerly at that index, or a negative error code. Possible errors are OJE_RDONLY (the list is flagged read only), OJE_DUPLICATE (the list is flagged OJF_UNIQUE and already contains the given card).

The ojl_get()/ojl_set() functions do full error checking (for example, bounds checking, honoring OJF_RDONLY, etc.) and maintain the integrity of the structure, so it is recommend that programs use these rather than accessing the underlying array directly. If you want to forgo this error checking for maximum performance in a critical section of code, you can use macro versions OJL_GET() and OJL_SET(). This makes it easy to code for reliability first, then only when necessary make a small change where needed.

int ojl_size(oj_cardlist *p)

Returns the number of cards currently contained in the list.

int ojl_clear(oj_cardlist *p)

Remove all cards from the list. Returns 0 on success, or negative error code. It should be noted that when cards are removed from a list, the underlying array is not zeroed, so code that accesses the underlying array directly may find valid card values at invalid indices.

int ojl_truncate(oj_cardlist *p, int size)

Reduce the list to its first size cards, discarding the rest. If the list is already the given size, it is not affected. If the list is already smaller than the given size, returns OJE_BADINDEX. Returns 0 on success or negative error code. The same warning for ojl_clear() above applies here as well.

int ojl_equal(oj_cardlist *p1, oj_cardlist *p2)

Returns true if the two given lists have identical contents and the same OJF_UNIQUE flag. If both lists have the OJF_UNIQUE flag set, they will compare as equal if they contain the same set of cards in any order. Two non-unique lists will compare as equal only if they contain the same cards in the same order. A unique and a non-unique list will compare as unequal regardless of their contents.

uint32_t ojl_hash(oj_cardlist *p)

Return a hash value for the list, based on its contents. Different algorithms are used for unique and non-unique lists. If two unique lists compare as equal, they will have the same hash value; if two non-unique lists compare equal, they will have the same hash value. However, if a unique list and a non-unique list happen to be otherwise equal, they will not hash equally. This should not be a problem for most uses, but might clash with some hashtable implementations if you mix the two in a single hashtable.

oj_card ojl_append(oj_cardlist *p, oj_card c)

Adds the given card to the end of the list, returning the card value or a negative error code. Possible errors are OJE_FULL (the list is already at capacity), OJE_RDONLY (the list is flagged read only), OJE_DUPLICATE (the list is flagged OJF_UNIQUE and already contains the given card). For maximum performance at the cost of this error checking, can be replaced with the macro OJL_APPEND(p,c).

oj_card ojl_pop(oj_cardlist *p)

Removes and returns the card at the end of the list, or a negative error code. Possible errors are OJE_RDONLY (the list is flagged read only), OJE_BADINDEX (the list is empty). For maximum performance at the cost of this error checking, can be replaced with the macro OJL_POP(p).

oj_card ojl_pop_random(oj_cardlist *p)

Removes and returns a card chosen randomly from the list, or a negative error code. Possible errors are OJE_RDONLY (the list is flagged read only), OJE_BADINDEX (the list is empty). Applications that deal random cards from a deck have two choices: use this function to deal them one at a time, or shuffle the deck and use ojl_pop(). Which is better depends on the application: if you typically deal most of the deck (or shoe), it will probably be faster to shuffle-and-pop. If you only deal a few cards from a large deck, pop-random may be faster. There is no macro form of this function.

int ojl_index(oj_cardlist *p, oj_card card)

Returns the index into the list at which the given card is found, or the negative value OJE_NOTFOUND.

int ojl_insert(oj_cardlist *p, int index, oj_card card)

Inserts the card at the given index in the list. Returns the new length of the list, or a negative error code. Possible errors are OJE_FULL (the list is already at capacity), OJE_RDONLY (the list is flagged read only), OJE_BADINDEX (you’ve given an index beyond the end of the list), OJE_DUPLICATE (the list is flagged OJF_UNIQUE and already contains the given card).

int ojl_delete(oj_cardlist *p, int index)

Removes and returns the card at the given index in the list, or a negative error code. Possible errors are OJE_RDONLY (the list is flagged read only), OJE_BADINDEX (you’ve given an index beyond the end of the list).

ojl_delete_card(oj_cardlist *p, oj_card card)

Removes and returns the given card from the list if it is present, or a negative error code. Possible errors are OJE_NOTFOUND (the given card is not in the list), OJE_RDONLY (the list is flagged read only).

int ojl_extend(oj_cardlist *destp, oj_cardlist *srcp, int count)

Add count cards from the start of srcp to the end of destp. Returns the count of cards added, or a negative error code. If count is 0, the entire source list is added. Possible errors are OJE_FULL (the destination list does not have sufficient room), OJE_RDONLY (the destination list is flagged read only), OJE_DUPLICATE (the destination list is flagged OJF_UNIQUE and already contains one or more of the cards to be added). On any error return, no cards will have been added to the destination list.

int ojl_extend_text(oj_cardlist *p, char *src, int count)

Add count cards interpreted from the string src to the end of list p. Returns the count of cards added, or a negative error code. If count is 0, all cards in thre string are added, up to available room in p . Possible errors are OJE_FULL (the destination list does not have sufficient room), OJE_RDONLY (the destination list is flagged read only), OJE_DUPLICATE (the destination list is flagged OJF_UNIQUE and already contains one or more of the cards to be added). On any error return, no cards will have been added to the destination list.

Note a subtle distinction here between ojl_extend() and ojl_extend_text() in the handling of count = 0: In ojl_extend(), source is an oj_cardlist, so it is expected that you will (or can) know its length. So if you pass a count of 0 to indicate that you want the whole list copied, and there is insufficient room, an OJE_FULL error will be returned. For ojl_extend_text(), the source is a string, possibly from an unknown input source, possibly with extra whitespace, and so on. So if you pass a count of 0, the function will append as many cards as will fit, even if that’s fewer than those encoded in the string. Only if a count is given explicitly will insufficient room result in an OJE_FULL error.

int ojl_copy(oj_cardlist *destp, oj_cardlist *srcp)

Copies the list srcp onto the list destp , overwriting its previous contents. Returns the length of the list or a negative error code. Possible errors are OJE_FULL (the destination list does not have sufficient room), OJE_RDONLY (the destination list is flagged read only).

int ojl_sort(oj_cardlist *p)

Sorts the list in ascending order. Returns 0 if successful, or the negative error code OJE_RDONLY if the list is flagged read-only.

int ojl_reverse(oj_cardlist *p)

Reverse the order of the cards in the list. Returns 0 if successful, or the negative error code OJE_RDONLY if the list is flagged read-only.

int ojl_fill(oj_cardlist *p, int count, oj_decktype t)

Fill the list with count cards of the standard deck type t. Overwrites any previous contents. If count is greater than the number of cards in the given standard deck, cards will be repeated (this can be used, for example, to fill a multi-deck blackjack shoe or for games like Pnochle and Canasta). Returns the length of the list or a negative error code. Possible errors are OJE_FULL (the destination list does not have sufficient room), OJE_RDONLY (the destination list is flagged read only), OJE_DUPLICATE (the list is flagged no duplicates allowed and count is greater than deck size).

int ojl_shuffle(oj_cardlist *)

Randomizes the list in such a way that every possible permutation is equally likely. Returns 0 if successful, or the negative error code OJE_RDONLY if the list is flagged read-only.

int ojl_fill_shuffled(oj_cardlist *p, oj_decktype t)

Fill the list with one shuffled copy of the standard deck. Cannot do repeats like ojl_fill(). This is a bit faster than doing a separate fill and shuffle. Returns the length of the list or a negative error code. Possible errors are OJE_FULL (the destination list does not have sufficient room), OJE_RDONLY (the destination list is flagged read only).

char *ojl_text(oj_cardlist *p, char *buf, int size)

Puts a string representation of the list into the given buffer, clipping if necessary. Returns the buffer, or NULL if it was too small for anything useful.

Next: Combiner functions