1 package cribbage
2
3 import (
4 "testing"
5
6 . "code.rocketnine.space/tslocum/joker"
7 )
8
9 var (
10 testHandA = Cards{
11 Card{Face2, SuitSpades},
12 Card{Face3, SuitSpades},
13 Card{Face4, SuitSpades},
14 Card{Face5, SuitSpades}}
15 testHandB = Cards{
16 Card{FaceJack, SuitHearts},
17 Card{Face5, SuitDiamonds},
18 Card{Face5, SuitClubs},
19 Card{Face5, SuitSpades}}
20 testHandC = Cards{
21 Card{Face3, SuitHearts},
22 Card{Face3, SuitDiamonds},
23 Card{Face3, SuitClubs},
24 Card{Face3, SuitSpades}}
25 testHandD = Cards{
26 Card{Face5, SuitHearts},
27 Card{Face5, SuitDiamonds},
28 Card{Face5, SuitClubs},
29 Card{Face5, SuitSpades}}
30 testHandE = Cards{
31 Card{Face7, SuitHearts},
32 Card{Face8, SuitHearts},
33 Card{Face9, SuitHearts},
34 Card{Face10, SuitHearts}}
35 testHandF = Cards{
36 Card{Face3, SuitHearts},
37 Card{Face6, SuitHearts},
38 Card{Face5, SuitHearts},
39 Card{Face4, SuitHearts}}
40 testHandG = Cards{
41 Card{Face6, SuitHearts},
42 Card{Face5, SuitHearts},
43 Card{Face4, SuitHearts}}
44 testHandH = Cards{
45 Card{FaceAce, SuitHearts},
46 Card{Face3, SuitHearts},
47 Card{Face2, SuitHearts},
48 Card{Face4, SuitHearts}}
49 testHandI = Cards{
50 Card{FaceAce, SuitHearts},
51 Card{Face3, SuitHearts},
52 Card{FaceAce, SuitClubs},
53 Card{Face2, SuitHearts},
54 Card{Face4, SuitHearts},
55 Card{Face7, SuitHearts}}
56 testHandJ = Cards{
57 Card{Face7, SuitHearts},
58 Card{Face3, SuitHearts},
59 Card{Face2, SuitHearts},
60 Card{Face4, SuitHearts}}
61 testHandK = Cards{
62 Card{Face5, SuitHearts},
63 Card{Face5, SuitDiamonds},
64 Card{Face5, SuitClubs},
65 Card{FaceJack, SuitSpades}}
66 testHandL = Cards{
67 Card{Face2, SuitHearts},
68 Card{Face2, SuitDiamonds},
69 Card{Face7, SuitClubs},
70 Card{Face7, SuitSpades}}
71 testHandM = Cards{
72 Card{Face5, SuitHearts},
73 Card{Face5, SuitDiamonds},
74 Card{Face5, SuitClubs},
75 Card{FaceKing, SuitSpades}}
76 )
77
78 type expectedPegScore struct {
79 Hand Cards
80
81 Result []ScoreResult
82 }
83
84 var expectedPegScores = []expectedPegScore{
85 {testHandA, []ScoreResult{
86 {Type: ScoreRun, Points: 4, Cards: Cards{
87 Card{Face2, SuitSpades},
88 Card{Face3, SuitSpades},
89 Card{Face4, SuitSpades},
90 Card{Face5, SuitSpades}}}}},
91 {testHandB, []ScoreResult{
92 {Type: ScorePair, Points: 6, Cards: Cards{
93 Card{Face5, SuitDiamonds},
94 Card{Face5, SuitClubs},
95 Card{Face5, SuitSpades}}}}},
96 {testHandC, []ScoreResult{
97 {Type: ScorePair, Points: 12, Cards: Cards{
98 Card{Face3, SuitHearts},
99 Card{Face3, SuitDiamonds},
100 Card{Face3, SuitClubs},
101 Card{Face3, SuitSpades}}}}},
102 {testHandF, []ScoreResult{
103 {Type: ScoreRun, Points: 4, Cards: Cards{
104 Card{Face3, SuitHearts},
105 Card{Face4, SuitHearts},
106 Card{Face5, SuitHearts},
107 Card{Face6, SuitHearts}}}}},
108 {testHandG, []ScoreResult{
109 {Type: Score15, Points: 2, Cards: Cards{
110 Card{Face4, SuitHearts},
111 Card{Face5, SuitHearts},
112 Card{Face6, SuitHearts}}},
113 {Type: ScoreRun, Points: 3, Cards: Cards{
114 Card{Face4, SuitHearts},
115 Card{Face5, SuitHearts},
116 Card{Face6, SuitHearts}}}}},
117 {testHandH, []ScoreResult{
118 {Type: ScoreRun, Points: 4, Cards: Cards{
119 Card{FaceAce, SuitHearts},
120 Card{Face2, SuitHearts},
121 Card{Face3, SuitHearts},
122 Card{Face4, SuitHearts}}}}},
123 {testHandI, []ScoreResult{}},
124 {testHandJ, []ScoreResult{
125 {Type: ScoreRun, Points: 3}}},
126 {testHandK, []ScoreResult{}},
127 {testHandL, []ScoreResult{
128 {Type: ScorePair, Points: 2, Cards: Cards{
129 Card{Face7, SuitClubs},
130 Card{Face7, SuitSpades}}}}},
131 {testHandM, []ScoreResult{}},
132 }
133
134 type expectedShowScore struct {
135 Starter Card
136 Hand Cards
137
138 HandResult []ScoreResult
139 CribResult []ScoreResult
140 }
141
142 var expectedShowScores = []expectedShowScore{
143 {Card{FaceAce, SuitSpades},
144 testHandA, []ScoreResult{
145 {Type: Score15, Points: 2, Cards: Cards{
146 Card{FaceAce, SuitSpades},
147 Card{Face2, SuitSpades},
148 Card{Face3, SuitSpades},
149 Card{Face4, SuitSpades},
150 Card{Face5, SuitSpades}}},
151 {Type: ScoreRun, Points: 5, Cards: Cards{
152 Card{FaceAce, SuitSpades},
153 Card{Face2, SuitSpades},
154 Card{Face3, SuitSpades},
155 Card{Face4, SuitSpades},
156 Card{Face5, SuitSpades}}},
157 {Type: ScoreFlush, Points: 5, Cards: Cards{
158 Card{FaceAce, SuitSpades},
159 Card{Face2, SuitSpades},
160 Card{Face3, SuitSpades},
161 Card{Face4, SuitSpades},
162 Card{Face5, SuitSpades}}},
163 }, []ScoreResult{
164 {Type: Score15, Points: 2, Cards: Cards{
165 Card{FaceAce, SuitSpades},
166 Card{Face2, SuitSpades},
167 Card{Face3, SuitSpades},
168 Card{Face4, SuitSpades},
169 Card{Face5, SuitSpades}}},
170 {Type: ScoreRun, Points: 5, Cards: Cards{
171 Card{FaceAce, SuitSpades},
172 Card{Face2, SuitSpades},
173 Card{Face3, SuitSpades},
174 Card{Face4, SuitSpades},
175 Card{Face5, SuitSpades}}},
176 {Type: ScoreFlush, Points: 5, Cards: Cards{
177 Card{FaceAce, SuitSpades},
178 Card{Face2, SuitSpades},
179 Card{Face3, SuitSpades},
180 Card{Face4, SuitSpades},
181 Card{Face5, SuitSpades}}},
182 }},
183
184 {Card{FaceKing, SuitClubs},
185 testHandA, []ScoreResult{
186 {Type: Score15, Points: 2, Cards: Cards{
187 Card{Face5, SuitSpades},
188 Card{FaceKing, SuitClubs}}},
189 {Type: Score15, Points: 2, Cards: Cards{
190 Card{Face2, SuitSpades},
191 Card{Face3, SuitSpades},
192 Card{FaceKing, SuitClubs}}},
193 {Type: ScoreRun, Points: 4, Cards: Cards{
194 Card{Face2, SuitSpades},
195 Card{Face3, SuitSpades},
196 Card{Face4, SuitSpades},
197 Card{Face5, SuitSpades}}},
198 {Type: ScoreFlush, Points: 4, Cards: Cards{
199 Card{Face2, SuitSpades},
200 Card{Face3, SuitSpades},
201 Card{Face4, SuitSpades},
202 Card{Face5, SuitSpades}}},
203 }, []ScoreResult{
204 {Type: Score15, Points: 2, Cards: Cards{
205 Card{Face5, SuitSpades},
206 Card{FaceKing, SuitClubs}}},
207 {Type: Score15, Points: 2, Cards: Cards{
208 Card{Face2, SuitSpades},
209 Card{Face3, SuitSpades},
210 Card{FaceKing, SuitClubs}}},
211 {Type: ScoreRun, Points: 4, Cards: Cards{
212 Card{Face2, SuitSpades},
213 Card{Face3, SuitSpades},
214 Card{Face4, SuitSpades},
215 Card{Face5, SuitSpades}}},
216 }},
217
218 {Card{Face8, SuitClubs},
219 testHandE, []ScoreResult{
220 {Type: Score15, Points: 2, Cards: Cards{
221 Card{Face7, SuitHearts},
222 Card{Face8, SuitHearts}}},
223 {Type: Score15, Points: 2, Cards: Cards{
224 Card{Face7, SuitHearts},
225 Card{Face8, SuitClubs}}},
226 {Type: ScorePair, Points: 2, Cards: Cards{
227 Card{Face8, SuitHearts},
228 Card{Face8, SuitClubs}}},
229 {Type: ScoreRun, Points: 4, Cards: Cards{
230 Card{Face7, SuitHearts},
231 Card{Face8, SuitHearts},
232 Card{Face9, SuitHearts},
233 Card{Face10, SuitHearts}}},
234 {Type: ScoreRun, Points: 4, Cards: Cards{
235 Card{Face7, SuitHearts},
236 Card{Face8, SuitClubs},
237 Card{Face9, SuitHearts},
238 Card{Face10, SuitHearts}}},
239 {Type: ScoreFlush, Points: 4, Cards: Cards{
240 Card{Face7, SuitHearts},
241 Card{Face8, SuitHearts},
242 Card{Face9, SuitHearts},
243 Card{Face10, SuitHearts}}},
244 }, []ScoreResult{
245 {Type: Score15, Points: 2, Cards: Cards{
246 Card{Face7, SuitHearts},
247 Card{Face8, SuitHearts}}},
248 {Type: Score15, Points: 2, Cards: Cards{
249 Card{Face7, SuitHearts},
250 Card{Face8, SuitClubs}}},
251 {Type: ScorePair, Points: 2, Cards: Cards{
252 Card{Face8, SuitHearts},
253 Card{Face8, SuitClubs}}},
254 {Type: ScoreRun, Points: 4, Cards: Cards{
255 Card{Face7, SuitHearts},
256 Card{Face8, SuitHearts},
257 Card{Face9, SuitHearts},
258 Card{Face10, SuitHearts}}},
259 {Type: ScoreRun, Points: 4, Cards: Cards{
260 Card{Face7, SuitHearts},
261 Card{Face8, SuitClubs},
262 Card{Face9, SuitHearts},
263 Card{Face10, SuitHearts}}},
264 }},
265
266 {Card{Face5, SuitSpades},
267 testHandK, []ScoreResult{
268 {Type: Score15, Points: 2, Cards: Cards{
269 Card{Face5, SuitHearts},
270 Card{FaceJack, SuitSpades}}},
271 {Type: Score15, Points: 2, Cards: Cards{
272 Card{Face5, SuitDiamonds},
273 Card{FaceJack, SuitSpades}}},
274 {Type: Score15, Points: 2, Cards: Cards{
275 Card{Face5, SuitClubs},
276 Card{FaceJack, SuitSpades}}},
277 {Type: Score15, Points: 2, Cards: Cards{
278 Card{Face5, SuitSpades},
279 Card{FaceJack, SuitSpades}}},
280 {Type: Score15, Points: 2, Cards: Cards{
281 Card{Face5, SuitHearts},
282 Card{Face5, SuitDiamonds},
283 Card{Face5, SuitClubs}}},
284 {Type: Score15, Points: 2, Cards: Cards{
285 Card{Face5, SuitHearts},
286 Card{Face5, SuitDiamonds},
287 Card{Face5, SuitSpades}}},
288 {Type: Score15, Points: 2, Cards: Cards{
289 Card{Face5, SuitHearts},
290 Card{Face5, SuitClubs},
291 Card{Face5, SuitSpades}}},
292 {Type: Score15, Points: 2, Cards: Cards{
293 Card{Face5, SuitDiamonds},
294 Card{Face5, SuitClubs},
295 Card{Face5, SuitSpades}}},
296 {Type: ScorePair, Points: 12, Cards: Cards{
297 Card{Face5, SuitHearts},
298 Card{Face5, SuitDiamonds},
299 Card{Face5, SuitClubs},
300 Card{Face5, SuitSpades}}},
301 {Type: ScoreNobs, Points: 1, Cards: Cards{
302 Card{FaceJack, SuitSpades}}},
303 }, []ScoreResult{
304 {Type: Score15, Points: 2, Cards: Cards{
305 Card{Face5, SuitHearts},
306 Card{FaceJack, SuitSpades}}},
307 {Type: Score15, Points: 2, Cards: Cards{
308 Card{Face5, SuitDiamonds},
309 Card{FaceJack, SuitSpades}}},
310 {Type: Score15, Points: 2, Cards: Cards{
311 Card{Face5, SuitClubs},
312 Card{FaceJack, SuitSpades}}},
313 {Type: Score15, Points: 2, Cards: Cards{
314 Card{Face5, SuitSpades},
315 Card{FaceJack, SuitSpades}}},
316 {Type: Score15, Points: 2, Cards: Cards{
317 Card{Face5, SuitHearts},
318 Card{Face5, SuitDiamonds},
319 Card{Face5, SuitClubs}}},
320 {Type: Score15, Points: 2, Cards: Cards{
321 Card{Face5, SuitHearts},
322 Card{Face5, SuitDiamonds},
323 Card{Face5, SuitSpades}}},
324 {Type: Score15, Points: 2, Cards: Cards{
325 Card{Face5, SuitHearts},
326 Card{Face5, SuitClubs},
327 Card{Face5, SuitSpades}}},
328 {Type: Score15, Points: 2, Cards: Cards{
329 Card{Face5, SuitDiamonds},
330 Card{Face5, SuitClubs},
331 Card{Face5, SuitSpades}}},
332 {Type: ScorePair, Points: 12, Cards: Cards{
333 Card{Face5, SuitHearts},
334 Card{Face5, SuitDiamonds},
335 Card{Face5, SuitClubs},
336 Card{Face5, SuitSpades}}},
337 {Type: ScoreNobs, Points: 1, Cards: Cards{
338 Card{FaceJack, SuitSpades}}},
339 }},
340
341 {Card{Face5, SuitSpades},
342 testHandM, []ScoreResult{
343 {Type: Score15, Points: 2, Cards: Cards{
344 Card{Face5, SuitHearts},
345 Card{FaceKing, SuitSpades}}},
346 {Type: Score15, Points: 2, Cards: Cards{
347 Card{Face5, SuitDiamonds},
348 Card{FaceKing, SuitSpades}}},
349 {Type: Score15, Points: 2, Cards: Cards{
350 Card{Face5, SuitClubs},
351 Card{FaceKing, SuitSpades}}},
352 {Type: Score15, Points: 2, Cards: Cards{
353 Card{Face5, SuitSpades},
354 Card{FaceKing, SuitSpades}}},
355 {Type: Score15, Points: 2, Cards: Cards{
356 Card{Face5, SuitHearts},
357 Card{Face5, SuitDiamonds},
358 Card{Face5, SuitClubs}}},
359 {Type: Score15, Points: 2, Cards: Cards{
360 Card{Face5, SuitHearts},
361 Card{Face5, SuitDiamonds},
362 Card{Face5, SuitSpades}}},
363 {Type: Score15, Points: 2, Cards: Cards{
364 Card{Face5, SuitHearts},
365 Card{Face5, SuitClubs},
366 Card{Face5, SuitSpades}}},
367 {Type: Score15, Points: 2, Cards: Cards{
368 Card{Face5, SuitDiamonds},
369 Card{Face5, SuitClubs},
370 Card{Face5, SuitSpades}}},
371 {Type: ScorePair, Points: 12, Cards: Cards{
372 Card{Face5, SuitHearts},
373 Card{Face5, SuitDiamonds},
374 Card{Face5, SuitClubs},
375 Card{Face5, SuitSpades}}},
376 }, []ScoreResult{
377 {Type: Score15, Points: 2, Cards: Cards{
378 Card{Face5, SuitHearts},
379 Card{FaceKing, SuitSpades}}},
380 {Type: Score15, Points: 2, Cards: Cards{
381 Card{Face5, SuitDiamonds},
382 Card{FaceKing, SuitSpades}}},
383 {Type: Score15, Points: 2, Cards: Cards{
384 Card{Face5, SuitClubs},
385 Card{FaceKing, SuitSpades}}},
386 {Type: Score15, Points: 2, Cards: Cards{
387 Card{Face5, SuitSpades},
388 Card{FaceKing, SuitSpades}}},
389 {Type: Score15, Points: 2, Cards: Cards{
390 Card{Face5, SuitHearts},
391 Card{Face5, SuitDiamonds},
392 Card{Face5, SuitClubs}}},
393 {Type: Score15, Points: 2, Cards: Cards{
394 Card{Face5, SuitHearts},
395 Card{Face5, SuitDiamonds},
396 Card{Face5, SuitSpades}}},
397 {Type: Score15, Points: 2, Cards: Cards{
398 Card{Face5, SuitHearts},
399 Card{Face5, SuitClubs},
400 Card{Face5, SuitSpades}}},
401 {Type: Score15, Points: 2, Cards: Cards{
402 Card{Face5, SuitDiamonds},
403 Card{Face5, SuitClubs},
404 Card{Face5, SuitSpades}}},
405 {Type: ScorePair, Points: 12, Cards: Cards{
406 Card{Face5, SuitHearts},
407 Card{Face5, SuitDiamonds},
408 Card{Face5, SuitClubs},
409 Card{Face5, SuitSpades}}},
410 }},
411 }
412
413 func TestScorePeg(t *testing.T) {
414 t.Parallel()
415
416 for i, expected := range expectedPegScores {
417 if Sum(expected.Hand) > 31 {
418 t.Errorf("case %d: invalid peg hand sum: got %d, want <=31: %s", i, Sum(expected.Hand), expected.Hand)
419 }
420
421 pegPoints, pegResult := Score(Peg, expected.Hand, Card{})
422
423 if !resultsEqual(pegResult, expected.Result) {
424 t.Fatalf("case %d: incorrect peg result: got %s, want %s: %s", i, pegResult, expected.Result, expected.Hand)
425 }
426
427 var expectedPoints int
428 for _, r := range expected.Result {
429 expectedPoints += r.Points
430 }
431 if pegPoints != expectedPoints {
432 t.Fatalf("case %d: incorrect peg score: got %d, want %d: %s", i, pegPoints, expectedPoints, expected.Hand)
433 }
434 }
435 }
436
437 func TestScoreShowHand(t *testing.T) {
438 t.Parallel()
439
440 var expectedPoints int
441 for i, expected := range expectedShowScores {
442 handPoints, handResult := Score(ShowHand, expected.Hand, expected.Starter)
443
444 if !resultsEqual(handResult, expected.HandResult) {
445 t.Fatalf("case %d: incorrect hand result: got %s, want %s: %s - %s", i, handResult, expected.HandResult, expected.Starter, expected.Hand)
446 }
447
448 expectedPoints = 0
449 for _, r := range expected.HandResult {
450 expectedPoints += r.Points
451 }
452 if handPoints != expectedPoints {
453 t.Fatalf("case %d: incorrect hand score: got %d, want %d: %s - %s", i, handPoints, expectedPoints, expected.Starter, expected.Hand)
454 }
455 }
456 }
457
458 func TestScoreShowCrib(t *testing.T) {
459 t.Parallel()
460
461 var expectedPoints int
462 for i, expected := range expectedShowScores {
463 cribPoints, cribResult := Score(ShowCrib, expected.Hand, expected.Starter)
464
465 if !resultsEqual(cribResult, expected.CribResult) {
466 t.Fatalf("case %d: incorrect crib result: got %s, want %s: %s - %s", i, cribResult, expected.CribResult, expected.Starter, expected.Hand)
467 }
468
469 expectedPoints = 0
470 for _, r := range expected.CribResult {
471 expectedPoints += r.Points
472 }
473 if cribPoints != expectedPoints {
474 t.Fatalf("case %d: incorrect crib score: got %d, want %d: %s - %s", i, cribPoints, expectedPoints, expected.Starter, expected.Hand)
475 }
476 }
477 }
478
479 func BenchmarkScorePeg(b *testing.B) {
480 var (
481 score int
482 results ScoreResults
483 )
484 b.ResetTimer()
485 for i := 0; i < b.N; i++ {
486 for _, expected := range expectedPegScores {
487 score, results = Score(Peg, expected.Hand, Card{})
488 }
489 }
490 _, _ = score, results
491 }
492
493 func BenchmarkScoreShowHand(b *testing.B) {
494 var (
495 score int
496 results ScoreResults
497 )
498 b.ResetTimer()
499 for i := 0; i < b.N; i++ {
500 for _, expected := range expectedShowScores {
501 score, results = Score(ShowHand, expected.Hand, expected.Starter)
502 }
503 }
504 _, _ = score, results
505 }
506
507 func BenchmarkScoreShowCrib(b *testing.B) {
508 var (
509 score int
510 results ScoreResults
511 )
512 b.ResetTimer()
513 for i := 0; i < b.N; i++ {
514 for _, expected := range expectedShowScores {
515 score, results = Score(ShowCrib, expected.Hand, expected.Starter)
516 }
517 }
518 _, _ = score, results
519 }
520
521 func resultsEqual(a []ScoreResult, b []ScoreResult) bool {
522 if len(a) != len(b) {
523 return false
524 }
525
526 for i := range a {
527 if a[i].Type != b[i].Type {
528 return false
529 } else if a[i].Points != b[i].Points {
530 return false
531 } else if len(a[i].Cards) != len(b[i].Cards) {
532 return false
533 }
534
535 for j := range a[i].Cards {
536 if !a[i].Cards[j].Equal(b[i].Cards[j]) {
537 return false
538 }
539 }
540 }
541
542 return true
543 }
544
View as plain text