summaryrefslogtreecommitdiff
path: root/c3prj2_eval/README
blob: ce5530e93d57e03833a0637da2bc07bb5dd46f22 (plain)
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
Hand Evaluation
---------------
The other part of the project that you will do in this
course is write some of the code to evaluate and compare hands.
Remember that in Course 2 you already wrote test cases
for this code.   That means you have already thought
about various corner cases that might come up
and will have a nice suite of tests ready to go
when you finish your code.

Your ultimate goal in this step is to write a function,
which when passed two hands of cards, determines
which one won (or if they tied).   We'll use the
deck_t type that you worked with in the previous
part to represent a hand of cards as well (a hand
of cards is just a much smaller deck of cards---they
are both just sets of cards).

There are three major steps to determining who won:
  (1) Figuring out what ranking each hand has (straight, flush, etc)
      If you look in cards.h, you will see enum hand_ranking_t,
      which you worked with in Course 2.
  (2) Figuring out which 5 cards make up the hand (picking out
      the 5 cards that made the flush, or the two pairs and tiebreaker)
  (3) Comparing the rankings, and if they are the same, breaking
      ties by comparing the values in the hands.

At this point, you might be thinking that there is going to
be a lot of code to write with all the different possible
arrangements of cards and different possible hand rankings.
However, there are a few important things that will make
this managable:

(1) You will start by sorting the  cards into descending order
    by value.  This makes it much easier to find straights (cards
    in order), and you will have "N of a kind"s grouped together.
(2) The code to find "N of a kind" is basically the same
    for 4, 3, and 2 (so we can abstract it out into a function...)
(3) Full house and two pair are just three of a kind and a pair
    (so we already have that code...) with another pair
    (so we can just write a function to find a secondary pair)
(4) We are going to make two simplifying assumptions:
       - if there is a flush, it will occur in at most one suit.
         (i.e., you won't have As Ah Kh Qs 8s 7h 4s 3s 3h 2h,
	  which has two different flushes).
       - if there is an ace-high straight, there is not also
         an ace-low straight. 
     (These both hold for all major poker variants)

If you open up eval.c, you will find the following functions
that you will need to write:

 - int card_ptr_comp(const void * vp1, const void * vp2) 
    You want to sort the hand by value, so you need
    a comparison function to pass to quicksort.
    Quicksort sorts into ascending order, but you
    want descending order, so you will want to
    return
       something < 0  if card1 > card2
       0              if card1 == card2
       something > 0  if card1 < card2
    If two cards have the same value, compare them by
    suit in the same order as the enum suit_t:
       club < diamond < heart < spade
    Note that vp1 and vp2 are passed as const void *
    because that is the type that qsort demands.
    They will should each be assigned to variables
    of type
     const card_t * const * cp1
    before using them (this is much like sorting
    an array of strings from your readings).
    To learn more about using the C library function qsort,
    we suggest reviewing the course reading
    "Sorting Functions" in the "Function Pointers"
    lesson and consulting "man qsort"
    to read about the comparison function.

 - suit_t flush_suit(deck_t * hand);
   This function looks at the hand and determines
   if a flush (at least 5 cards of one suit) exists.
   If so, it returns the suit of the cards comprising
   the flush.  If not, it returns NUM_SUITS.
   For example:
    Given Ks Qs 0s 9h 8s 7s, it would return SPADES.  
    Given Kd Qd 0s 9h 8c 7c, it would return NUM_SUITS.

 - unsigned get_largest_element(unsigned * arr, size_t n);
   This function returns the largest element in an array
   of unsigned integers.  This should be familiar
   from the videos you watched.
   
   In course 4 (after you learn to dynamically allocate
   memory), you will write get_match_counts,
   which will construct an array with one element
   per card in the hand.  That array will
   tell you how many cards in the hand
   have the same value as the corresponding
   card.  You will then use get_largest_element
   to figure out which is the best "N of a kind".


 - size_t get_match_index(unsigned * match_counts, size_t n,unsigned n_of_akind);
   This function returns the index in the array (match_counts) whose
   value is n_of_akind.  The array has n elements.  The array match_counts
   may have multiple values equal to n_of_akind.  You should return
   the LOWEST index whose value is n_of_akind [which also guarantees
   it corresponds to the largest valued cards, since they will be sorted].
   (Once you figure out the best n_of_akind above,
    you will use this to locate that group of cards
    in the hand).
    Note that it is guaranteed that n_of_akind is in match_counts.
    If not, you should abort as this is evidence of an error.


 - ssize_t find_secondary_pair(deck_t * hand,
    			        unsigned * match_counts,
			        size_t match_idx) ;
   When you have a hand with 3 of a kind or a pair,
   you will want to look and see if there is another
   pair to make the hand into a full house or
   or two pairs.  This function takes in
   the hand, the match counts from before, and
   the index where the original match (3 of a kind
   or pair) was found. It should find
   the index of a card meeting the following conditions:
     - Its match count is > 1 [so there is at least a pair of them]
     - The card's value is not the same as the value of the
       card at match_idx (so it is not part of the original
       three of a kind/pair)
     - It is the lowest index meeting the first two conditions
       (which will be the start of that pair, and the highest
        value pair other than the original match).
   If no such index can be found, this function should
   return -1.

 - int is_straight_at(deck_t * hand, size_t index, suit_t fs)
   This function should determine if there is a straight
   starting at index (and only starting at index) in the
   given hand.  If fs is NUM_SUITS, then it should look
   for any straight.  If fs is some other value, then
   it should look for a straight flush in the specified suit.
    This function should return:
    -1 if an Ace-low straight was found at that index (and that index is the Ace)
     0  if no straight was found at that index
     1  if any other straight was found at that index

   When writing this function, you can assume
   that the hand is sorted by value: the
   values of cards will appear in descending order.
   (A K Q ... 4 3 2).
   
   There are two things that make this function
   tricky (probably the trickiest function in
   this assignment):
     (1) Ace low straights. An Ace low straight
         will appear in the hand with the Ace
	 first, then possibly some other cards,
	 then the 5 4 3 2.  For example, you
	 might have
	   As Ks Qc 5s 4c 3d 2c
     (2) You may have multiple cards with the
         same value, but still have a straight:
	   As Ac Ks Kc Qh Jh 0d
         has a straight even though A K Q
	 do not appear next to each other in
	 our sorted order.
  Hint: I made this easier on myself, by writing
  two helper functions:
  int is_n_length_straight_at(deck_t * hand, size_t index, suit_t fs, int n) ;
   and
  int is_ace_low_straight_at(deck_t * hand, size_t index, suit_t fs);

  The second of these lets me pull out the complexities of an ace
  low straight.  However, in doing so, I realized that there
  would be a lot of duplication of code between the ace low straight
  helper and the original function (for an ace low, you want to find
  a 5, then a straight of length 4: 5, 4, 3, 2).   This realization
  caused me to pull out much of the code into is_n_length_straight_at,
  so that I could call it with n=4 to search for the 5,4,3,2 part
  of an ace low straight.
  

 - hand_eval_t build_hand_from_match(deck_t * hand,
     				     unsigned n,
				     hand_ranking_t what,
				     size_t idx) ;
   Now you have written a bunch of functions that
   will figure out which ranking a hand has. It
   is time to construct a hand_eval_t (see eval.h) which
   has the ranking and the 5 cards used for it.
   This helper function will handle the
   "n of a kind" case.
   It should make hand_eval_t and set its
   ranking to the passed in "what" value.
   Then it should copy "n" cards from
   the hand, starting at "idx" into
   the first "n" elements of the hand_eval_t's 
   "cards" array.  The cards field in
   hand_eval_t is declared as:
         card_t * cards[5]
   This is an array of pointers, each to a card_t.
   Draw a picture to be sure you know how to name
   each card_t "box" before you start writing code.

   Your function should then fill the remainder
   of the "cards" array with the highest-value
   cards from the hand which were not in
   the "n of a kind".

   For example, given this hand:
     As Kc Kh Kd Qc 8s 5d
   The hand has 3 kings, and the As and Qc will break ties.
   Note that here  n = 3, what= THREE_OF_A_KIND, idx= 1.
   So the cards array in the hand_eval_t should have

     Kc Kh Kd As Qc

   Note that what may also be FULL_HOUSE or TWO_PAIR,
   since this function will get used for the first
   part of preparing those evaluations (then other code
   will later fix the rest of the hand with the other pair).


 - int compare_hands(deck_t * hand1, deck_t * hand2)

   This is the goal of the whole thing: given two hands,
   figure out which wins (or if it is a tie).
   Everything you wrote goes together to make this work!
 

   We're providing you with
     hand_eval_t evaluate_hand(deck_t * hand) ;
   since it involves some things you won't learn until
   Course 4.   It's also not super interesting:
   it mostly make a bunch of calls to the functions
   you wrote above, and has a lot of if-statements
   to handle the rules of poker.

   The important part of evaluate_hand is that
   (a) assumes the cards in the passed in hand are
   sorted and (b) it returns a hand_eval_t for the passed in hand.
   
   That means that to implement compare_hands, you should

 (a) sort each hand using qsort on the hand's cards
     and your card_ptr_comp [from earlier]
 (b) Call evaluate_hand on each hand, which gives you a hand_eval_t
     for each hand.
 (c) Check if the rankings in the hand_eval_t are the same
     or different.  If they are different, you can just use
     the ranking to determine the winner.
 (d) If they are the same, then you need to look
     at the values in the cards array of each hand_eval_t
     to break the tie. The way that we constructed
     the hand_eval_t's cards array means that
     the cards are already ordered from most significant (at index 0)
     to least significant (at index 4).  You can just
     do a lexicographic comparison on the values in the arrays.
     (Its like comparing strings, but you are comparing values
      of cards---if element 0 is the different, use that difference
      to determine your answer.  If element 0 is the same,
      look at element 1, and so on).
Note that compare hands should return a positive number
if hand 1 is better, 0 if the hands tie, and a negative number
if hand 2 is better.

You will also notice some functions at the bottom of eval.c that
we provided.  You don't need to do anything to these---we wrote
them for you to keep the amount of code manageable.

--------------
That sure was a lot of code!  You've been compiling and testing
along the way, right?  We sure hope so :)

However, to help you test things out even more, we've
provided some test infrastructure for you.

If you do

make 

You will compile the  test-eval program you are
familiar with from Course 2. This program behaves
exactly like it did in Course 2.  As a reminder,
it expects input where each line looks like:

hand1 ; hand2

where a hand looks like something that print_hand
would output.  So a valid input might be

Kc Ac Jh 8s 9c 2s ; Ah Kh 0s 7c 7h 3c

For each line in the input, the test program
will tell you:
 - The results of your functions that went into evaluating it
   (if there was a straight, if there was a flush, etc).
 - What hand_eval_t was returned by evaluate_hand for each hand
 - Which hand won (or if it was a tie) according to compare_hands

Good thing you have all those test cases from Course 2
to use with it!

Because you have an object file test-eval.o and not the source
test-eval.c, you may need to use the debugger differently than you're
used to. For this test program, we recommend running gdb in emacs,
then first, specifying the command line argument (in this case, a file
name with your tests)

set args tests.txt

Then you will want to set a breakpoint in the code you wrote (since
you can't see test-eval.c, where main is, to step through it). For
example, if you just wrote the function is_straight_at, and it doesn't
behave the way you expect, you can do

break is_straight_at

and gdb will pause execution any time the program calls that
function. Then you can use the command "run" instead of "start," since
you don't need to pause execution at the start of main. Also recall
the command "continue," which you can review from the debugging
lesson.


As usual, when you are finished, use the "grade" command.
When you pass, this, congratulations! You are done
with Course 3 and ready to move on to Course 4 :)