Match blood types in C I have written a solution to the blood-type matching problem, as described at https://projectlovelace.net/problems/blood-types/.  The problem is to determine whether a given recipient (in this case, argv[1]) will find a match for a blood transfusion in an array of available donors (argv + 2).


Input blood type: B+
Input list of available blood types: A- B+ AB+ O+ B+ B-
Output: match


#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;err.h&gt;

typedef struct {
    enum { O, A, B, AB } abo;
    enum { P, M } rh; 
} Blood;

const int abo[4][4] = {
    { O, O, O, O }, // O
    { O, A, O, A }, // A // *
    { O, B, O, B }, // B // *
    { O, A, B, AB }, // AB
};

const int rh[2][2] = {
    { P, M }, // P
    { M, M }, // M // *
};

Blood
parse(char *s){
    char rh0 = s[strlen(s)-1];
    char *abo0 = strdup(s);
    abo0[strlen(s)-1] = '\0';
    Blood b = {
        !strncmp(abo0, "O", 1) ? O
            : !strncmp(abo0, "A", 1) ? A
            : !strncmp(abo0, "B", 1) ? B
            : !strncmp(abo0, "AB", 2) ? AB
            : -1,
        rh0 == '+' ? P
            : rh0 == '-' ? M
            : -1,
    };
    return b;
}

int
main(int argc, char *argv[]) {
    if(argc &lt; 3)
        err(1, "no arguments given\n");
    int n = argc - 2;
    char *a0 = argv[1];
    char **as = argv + 2;

    Blood b0 = parse(a0);
    Blood *bs = malloc(sizeof(Blood) * n);
    for(int i = 0; i &lt; n; i++)
        bs[i] = parse(as[i]);

    for(int i = 0; i &lt; 4; i++) {
        for(int j = 0; j &lt; 2; j++) {
            for(int k = 0; k &lt; n; k++) {
                if(abo[b0.abo][i] == bs[k].abo &amp;&amp; rh[b0.rh][j] == bs[k].rh) {
                    printf("match\n");
                    return 0;
                }
            }
        }
    }
    printf("no match\n");
    return 1;
}


which works correctly in my tests, but uses some hacks.  Specifically, lines marked with // * have repeated values of enums, since I couldn't find a way to make loop sizes conditional on blood types.

What would be a better way, without forcing the blood type enums to be the same size?  And more generally, is there a simpler version of the blood-type matching algorithm?
Each blood factor can be present or not present.  The blood types can be bit-encoded using one bit for the A factor, one bit for the B factor, and one bit for the Rh factor.

enum { A=1, B=2, Rh=4 };


You would parse the blood type "AB+" as A + B + Rh == 7, and "O-" as 0 because it does not contain any A factor, B factor, or Rh factor.

Survival requires the donor's blood not contain any factors missing in the recipient's blood type.  Compliment the recipient's blood type to get the bad factors, and use a bit-wise and operation to test that all of the factors are not present in the donor's blood.

bool donor_matches = (donor &amp; ~recipient) == 0;




If you want to leave you enum's as encoding the 4 blood types, and the Rh factor separately, you can get to a similar coding.

Note that:

enum { O, A, B, AB } abo;


will define O=0, A=1, B=2, AB=3, so you have a similar bit assignment as above.  The Rh factor on the other hand would be better to reverse the order:

enum { M, P } rh;


so that M=0, P=1.  Now you could express the match condition:

bool donor_match = (donor_abo &amp; ~recipient_abo) == 0 &amp;&amp; (donor_rh &amp; ~recipient_rh) == 0;




Finally, if you want to leave the rh enum as you have originally defined it:

enum { P, M } rh;


then note that donor_rh &gt;= recipient_rh would express that the rh factors match, or the donor has a less-restrictive (numerically higher) rh factor.