Description

Represent a deck of cards.

My solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define VALUE_ACE 14
#define VALUE_KING 13
#define VALUE_QUEEN 12
#define VALUE_JACK 11


typedef enum {
  SPADES,
  HEARTS,
  DIAMONDS,
  CLUBS,
  NUM_SUITS
} suit_t;


struct card_tag {
  unsigned value;
  suit_t suit;
};


typedef struct card_tag card_t;


typedef enum {
  STRAIGHT_FLUSH,
  FOUR_OF_A_KIND,
  FULL_HOUSE,
  FLUSH,
  STRAIGHT,
  THREE_OF_A_KIND,
  TWO_PAIR,
  PAIR,
  NOTHING
} hand_ranking_t;


card_t card_from_num(unsigned c);
void assert_card_valid(card_t c);
const char * ranking_to_string(hand_ranking_t r) ;
char value_letter(card_t c);
char suit_letter(card_t c) ;
void print_card(card_t c);
card_t card_from_letters(char value_let, char suit_let);

Functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Given a card, abort if the card's value is not between 2 and
// VALUE_ACE, inclusive, and its suit is not between SPADES and CLUBS,
// inclusive.
void assert_card_valid(card_t c) {
  assert(c.value >= 2 && c.value <= VALUE_ACE);
  assert(c.suit >= 0 && c.suit < NUM_SUITS);
}


// Given a hand ranking value, return a descriptive string.
const char * ranking_to_string(hand_ranking_t r) {
  char * suit_strings[10] = {
    "STRAIGHT_FLUSH",
    "FOUR_OF_A_KIND",
    "FULL_HOUSE",
    "FLUSH",
    "STRAIGHT",
    "THREE_OF_A_KIND",
    "TWO_PAIR",
    "PAIR",
    "NOTHING"
  };
  return suit_strings[r];
}


// Given a card, return a character representing its value as follows:
// 2 through 9 => '2' through '9'
// 10          => '0'
// VALUE_JACK through VALUE_ACE => 'J' through 'A'
char value_letter(card_t c) {
  if (c.value >= 2 && c.value <= 9) return 48 + c.value;
  if (c.value == 10) return '0';
  char jqka_s[4] = {'J', 'Q', 'K', 'A'};
  int i = c.value - VALUE_JACK;
  return jqka_s[i];
}


// Given a card, return a character representing its suit.
char suit_letter(card_t c) {
  char letters[4] = { 's', 'h', 'd', 'c' };
  int i = c.suit - SPADES;
  return letters[i];
}


// Given a card, print its string representation as follows:
// "As" for ace of spades
// "Kc" for king of clubs
// "0d" for 10 of diamonds
// etc.
void print_card(card_t c) {
  char value = value_letter(c);
  char suit = suit_letter(c);
  printf("%c%c", value, suit);
}


// Given character representations of a value and a suit, return a
// card.
card_t card_from_letters(char value_let, char suit_let) {
  unsigned value;
  suit_t suit;
  if (value_let >= '2' && value_let <= '9') value = value_let - 48;
  switch (value_let) {
    case '0':
      value = 10; break;
    case 'J':
      value = VALUE_JACK; break;
    case 'Q':
      value = VALUE_QUEEN; break;
    case 'K':
      value = VALUE_KING; break;
    case 'A':
      value = VALUE_ACE;
  }
  switch (suit_let) {
    case 's':
      suit = SPADES; break;
    case 'h':
      suit = HEARTS; break;
    case 'd':
      suit = DIAMONDS; break;
    case 'c':
      suit = CLUBS; break;
  }
  card_t ret = { .value = value, .suit = suit };
  assert_card_valid(ret);
  return ret;
}


// Given an integer between 0 and 51 inclusive, return the
// corresponding card.
card_t card_from_num(unsigned c) {
  card_t ret = { .value = 0, .suit = NUM_SUITS }; // invalid card
  int deck_card = 0;
  for (int suit = SPADES; suit < NUM_SUITS; suit++) {
    for (int val = 2; val <= VALUE_ACE; val++) {
      if (deck_card == c) {
        ret.value = val;
        ret.suit = suit;
        return ret;
      } else {
        deck_card++;
      }
    }
  }
  assert_card_valid(ret);
  return ret;
}

Tests

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
void test_assert_card_valid() {
  // card_t c1 = { .value = 1, .suit = -1 };  // invalid low value, invalid low suit
  // assert_card_valid(c1); // should fail

  // card_t c2 = { .value = 15, .suit = 4 }; // invalid high value, invalid high suit
  // assert_card_valid(c2); // should fail

  // card_t c3 = { .value = 2, .suit = -1 }; // valid value, invalid suit
  // assert_card_valid(c3); // should fail

  // card_t c4 = { .value = 15, .suit = 0 }; // invalid value, valid suit
  // assert_card_valid(c4); // should fail

  card_t c5 = { .value = 2, .suit = 0} ; // valid value, valid suit
  assert_card_valid(c5);

  // All remaining valid suits
  for (int i = 3; i <= VALUE_ACE; i++) {
    c5.value = i;
    assert_card_valid(c5);
  }
  for (int i = 1; i < NUM_SUITS; i++) {
    c5.suit = i;
    assert_card_valid(c5);
  }
}

void test_ranking_to_string() {
  // assert(strcmp(ranking_to_string(1), "STRAIGHT_FLUSH") == 0); // should fail

  char * suit_strings[10] = {
    "STRAIGHT_FLUSH",
    "FOUR_OF_A_KIND",
    "FULL_HOUSE",
    "FLUSH",
    "STRAIGHT",
    "THREE_OF_A_KIND",
    "TWO_PAIR",
    "PAIR",
    "NOTHING"
  };

  for (int i = STRAIGHT_FLUSH; i <= NOTHING; i++) {
    assert(strcmp(ranking_to_string(i), suit_strings[i]) == 0);
  }
}

void test_value_letter() {
  card_t c = { .value = 2, .suit = 0 };
  // assert(value_letter(c) == 1); // should fail

  for (int i = 2; i <= 9; i++) {
    c.value = i;
    assert(value_letter(c) == 48 + c.value);
  }

  c.value = 10;
  assert(value_letter(c) == '0');

  char jqka_s[4] = {'J', 'Q', 'K', 'A'};
  int i;
  for (int j = VALUE_JACK; j <= VALUE_ACE; j++) {
    c.value = j;
    i = j - VALUE_JACK;
    // printf("c.value=%d, i=%d, jqka_s[i]=%c, value_letter(c)=%c\n", 
    //        c.value, i, jqka_s[i], value_letter(c));
    assert(value_letter(c) == jqka_s[i]);
  }
}

void test_suit_letter() {
  char letters[4] = { 's', 'h', 'd', 'c' };
  card_t c = { .value = 2, .suit = SPADES };
  assert(suit_letter(c) == 's');

  int i;
  for (int j = HEARTS; j < NUM_SUITS; j++) {
    c.suit = j;
    i = j - SPADES;
    assert(suit_letter(c) == letters[i]);
  }
}

void test_card_from_letters() {
  // card_t c_fail; 
  // c_fail = card_from_letters('1', 's'); // should fail

  card_t c1 = { .value = 2, .suit = SPADES };
  card_t c2 = card_from_letters('2', 's');
  assert(c1.value == c2.value);
  assert(c1.suit == c2.suit);
}

void test_card_from_num() {
  // card_t c;
  // c = card_from_num(52); // should fail

  card_t c = card_from_num(0);
  assert(c.value == 2);
  assert(c.suit == 0);

  c = card_from_num(51);
  assert(c.value = VALUE_ACE);
  assert(c.suit = CLUBS);
}

int main(void) {
  printf("Running tests...\n");

  test_assert_card_valid();
  printf("test_assert_card_valid() succeeded\n");

  test_ranking_to_string();
  printf("test_ranking_to_string() succeeded\n");

  test_value_letter();
  printf("test_value_letter() succeeded\n");

  test_suit_letter();
  printf("test_suit_letter() succeeded\n");

  printf("\n");
  printf("Testing print_card()...\n");
  card_t c = { .value = VALUE_ACE, .suit = SPADES };
  printf("You should see 'As' here -> ");
  print_card(c);
  printf("\n");
  c.value = 10;
  c.suit = DIAMONDS;
  printf("You should see '0d' here -> ");
  print_card(c);
  printf("\n");
  c.value = VALUE_KING;
  c.suit = CLUBS;
  printf("You should see 'Kc' here -> ");
  print_card(c);
  printf("\n");
  printf("\n");

  test_card_from_letters();
  printf("test_card_from_letters() succeeded\n");

  test_card_from_num();
  printf("test_card_from_num() succeeded\n");
}