I recently came across a coding challenge on some sort of technological basket weaving forum, where you had to pick 2 cards from a shuffled deck. I believe the challenge can be interpreted 2 ways:
- Generate random deck, pick first 2
- Generate sequential (or any) deck, pick 2 random cards
I went for option one. I believe I have used a good shuffling algorithm and accounted for modulo bias.
My main priorities:
- correctness
- code easy to handle and read
cards.c
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define SWAP_INT(a,b) {int tmp=a; a=b; b=tmp;}
const char const* suits[4] = {"Diamonds","Clubs","Hearts","Spades"};
const char const* ranks[13] = {"Ace","2","3","4","5","6","7","8","9","10","Jack","Queen","King"};
// from min inclusive to max inclusive
int rand_ranged_inclusive(int min, int max)
{
if (min > max)
SWAP_INT(min, max);
if (min == max)
return min;
int range = max-min+1;
int biased_interval = RAND_MAX%(range-1);
int x;
// prevent modulo bias by choosing a number outside biased interval
do {
x = rand();
} while (x < biased_interval);
return x%range + min;
}
// Fisher-Yates (Durstenfield)
int shuffle(int arr[], int len)
{
for (int i = 0; i < len; ++i) {
int j = rand_ranged_inclusive(i, len-1);
SWAP_INT(arr[i], arr[j]);
}
}
void print_card(int c)
{
if (c < 0 || c > 52)
printf("Joker\n");
const char const* suit = suits[c/13];
const char const* rank = ranks[c%13];
printf("%s of %s\n", rank, suit);
}
int main(int argc, char const *argv[])
{
srand(time(NULL));
int cards[52] = {0};
for (int i = 0; i < 52; ++i)
cards[i] = i;
shuffle(cards, 52);
print_card(cards[0]);
print_card(cards[1]);
return 0;
}
I feel like it can be optimized some more, especially picking random numbers, but reading from dev/urandom
seems overall slower, when the modulo bias is so small and easily overcome.
SWAP_INT
macro to remain untyped.#define SWAP(a,b) {a^=b; b^=a; a^=b;}
. Branchless check to see if not equal:#define SWAP(a,b) {a^=(b*(a!=b)); b^=(a*(a!=b)); a^=(b*(a!=b));}
\$\endgroup\$void shuffle()
\$\endgroup\$