Shuffle a deck of cards and draw 2 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 &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;time.h&gt;
    
#define SWAP_INT(a,b) {int tmp=a; a=b; b=tmp;}
    
const char const* suits[4] = {&quot;Diamonds&quot;,&quot;Clubs&quot;,&quot;Hearts&quot;,&quot;Spades&quot;};
const char const* ranks[13] = {&quot;Ace&quot;,&quot;2&quot;,&quot;3&quot;,&quot;4&quot;,&quot;5&quot;,&quot;6&quot;,&quot;7&quot;,&quot;8&quot;,&quot;9&quot;,&quot;10&quot;,&quot;Jack&quot;,&quot;Queen&quot;,&quot;King&quot;};
    
// from min inclusive to max inclusive
int rand_ranged_inclusive(int min, int max)
{
    if (min &gt; 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 &lt; biased_interval); 

    return x%range + min;
}

// Fisher-Yates (Durstenfield)
int shuffle(int arr[], int len)
{
    for (int i = 0; i &lt; len; ++i) {
        int j = rand_ranged_inclusive(i, len-1);
        SWAP_INT(arr[i], arr[j]);
    }
}

void print_card(int c)
{
    if (c &lt; 0 || c &gt; 52)
        printf(&quot;Joker\n&quot;);

    const char const* suit = suits[c/13];
    const char const* rank = ranks[c%13];

    printf(&quot;%s of %s\n&quot;, rank, suit);
}

int main(int argc, char const *argv[])
{
    srand(time(NULL));

    int cards[52] = {0};

    for (int i = 0; i &lt; 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.
rand_ranged_inclusive(a, b) is a surprisingly long function name, for how little information it communicates to the reader. I have frequently seen versions of this function named randint(a, b) or randint0(b) — but where the possible outputs are between 0 inclusive and b exclusive!
Using &quot;half-open ranges&quot; like this has huge advantages in C, because C uses half-open ranges for everything else. To pick a random element from an array, you want randint0(NELEM(a)). To pick a random character from a string, you want randint0(strlen(s)). And so on.
if (min &gt; max)
    SWAP_INT(min, max);

Here and throughout, I strongly recommend curly-bracing all your control flow, including if and for bodies which are currently only one line long. These braces may one day save you from a goto fail bug.
Also, I think it's un-idiomatic that you are going out of your way to produce well-defined behavior for rand_ranged_inclusive(6, 1). I claim that that kind of input clearly indicates a bug in the caller, and you should be using assert(min &lt;= max) here. (Or, if you switch to half-open ranges, assert(min &lt; max).)
Your choice to handle rand_ranged_inclusive(6, 1) becomes even weirder when you consider that you do not handle seemingly valid parameters such as rand_ranged_inclusive(-10, INT_MAX). (The caller who tries that gets integer overflow and undefined behavior.)
int range = max-min+1;
int biased_interval = RAND_MAX%(range-1);

The use of RAND_MAX % ... instead of (RAND_MAX+1) % ... is an immediate red flag for me, after our discussion of half-open ranges. (RAND_MAX is an endpoint-which-is-included, similar to INT_MAX.) All these +1 and -1 would go away if you switched to half-open ranges. I bet they're hiding some off-by-one errors. Let's trace through a sample input.
Suppose min=1, max=6, RAND_MAX=10. (This is legitimately the first input I traced.) Then range=6, biased_interval=0... so the do-while loop runs only once before dropping out... but you end up choosing 1-4 twice as often as 5-6.
Did you test this code at all?  How did you test it?
I think the algorithm you want here is more like
int range = max - min + 1;
int discard_top_k = ((RAND_MAX % range) + 1) % range;
int real_max = RAND_MAX - discard_top_k;
do {
    x = rand();
} while (x &gt; real_max);


int j = rand_ranged_inclusive(i, len-1);

Your Fisher-Yates shuffle looks correct; but notice again that you're having to add a -1 to fix up the lack of half-open ranges in your random-number code.

if (c &lt; 0 || c &gt; 52)
    printf(&quot;Joker\n&quot;);

Is this situation possible? If it's supposed to be impossible, you should assert(0 &lt;= c &amp;&amp; c &lt; 52) (hey look! half-open ranges!)
Also notice the off-by-one error in your code because of (say it with me) the lack of half-open ranges.

char const *argv[]

I agree with Linus on this one. Prefer
const char **argv