I would personally avoid using a structure to describe each card; I'd prefer encoding the suit and the face into a single positive number. For example:
Code:
/* Suit is an integer 1 to 4, inclusive. */
#define SUIT_SPADES 1
#define SUIT_CLUBS 2
#define SUIT_DIAMONDS 3
#define SUIT_HEARTS 4
/* Face is an integer 1 to 13 or 2 to 14. */
#define FACE_2 2
#define FACE_3 3
#define FACE_4 4
#define FACE_5 5
#define FACE_6 6
#define FACE_7 7
#define FACE_8 8
#define FACE_9 9
#define FACE_10 10
#define FACE_JACK 11
#define FACE_QUEEN 12
#define FACE_KING 13
#define FACE_ACE 14 /* or 1, depending on game */
/* This macro combines a suit and a face to a single positive integer (larger than zero, less than 80) */
#define CARD(suit, face) (16*(suit) + (face))
/* This macro yields the suit of a card */
#define SUIT(card) ((card) / 16)
/* This macro yields the face of a card */
#define FACE(card) ((card) % 16)
You can then use e.g. unsigned char type to describe each card; a deck could be unsigned char deck[52];.
In numerical order, each suit is sorted consecutively, with spades first (lowest value) and hearts last (highest value). In other words, CARD(SUIT_HEARTS, FACE_QUEEN) > CARD(SUIT_HEARTS, FACE_JACK), but CARD(SUIT_HEARTS, FACE_2) > CARD(SUIT_SPADES, FACE_ACE).
Writing just one or two helper functions, you can trivially sort decks using qsort(). For example, with
Code:
int by_suit_and_face(const void *ptr1, const void *ptr2)
{
const unsigned char card1 = *(const unsigned char *)ptr1;
const unsigned char card2 = *(const unsigned char *)ptr2;
const int value1 = FACE(card1) - 16 * SUIT(card1));
const int value2 = FACE(card2) - 16 * SUIT(card2));
return value1 - value2;
}
int by_face_and_suit(const void *ptr1, const void *ptr2)
{
const unsigned char card1 = *(const unsigned char *)ptr1;
const unsigned char card2 = *(const unsigned char *)ptr2;
const int value1 = SUIT(card1) - 5 * FACE(card1);
const int value2 = SUIT(card2) - 5 * FACE(card2);
return value1 - value2;
}
you can sort the aforementioned deck so that within each suit, the cards are in their face order, but hearts first, then diamonds, then clubs, and spades last:
qsort(deck, sizeof deck, sizeof deck[0], by_suit_and_face);
Conversely, if you want a deck with all four faces (of different suits) consecutive, use
qsort(deck, sizeof deck, sizeof deck[0], by_face_and_suit);
You can even construct an array of strings to describe each face and suit (but remember that we skip zero index):
Code:
static const char *const suit_name[] = {
"", /* This corresponds to 0, which is no suit. */
"spades",
"clubs",
"diamonds",
"hearts",
};
static const char *const face_name[] = {
"", /* This corresponds to 0, which is no face */
"ace", /* For both 1 and 14, as you might use either one */
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"jack",
"queen",
"king",
"ace",
};
which lets you describe any card using
printf("%s of %s", face_name[FACE(card)], suit_name[SUIT(card)]);
which will print strings like ten of spades and ace of hears, for every possible card.
When you don't need the string representation nor the qsort() helper functions, but you need additional speed -- say, you're investigating card shuffling or cyclic stuff, and deal with billions of cards -- you might change the definitions into
Code:
#define SUIT_SPADES 0
#define SUIT_CLUBS 16
#define SUIT_DIAMONDS 32
#define SUIT_HEARTS 48
#define COLOR_BLACK 0
#define COLOR_RED 32
#define CARD(suit, face) ((suit) | (face))
#define SUIT(card) ((card) & 15)
#define FACE(card) ((card) & 48)
#define COLOR(card) ((card) & 32)
which lets you extract and combine card features with just one binary AND or OR operator (which are fastest possible on all computer and microcontroller architectures that use binary arithmetic -- all, except for a few expensive museum curiosities).