functional-yahtzee 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,48 @@
1
+ require 'yahtzee'
2
+ require 'score_card'
3
+ require 'scoring/upper_card'
4
+ require 'scoring/lower_card'
5
+
6
+ # Yahtzee::Scoring is our mechnism for scoring dice
7
+ # to placement spots on the score card. If the method
8
+ # is located here, it will include a callback you can
9
+ # define. Right now it is used for writing the value
10
+ # to the object, but you could print the value instead.
11
+ #
12
+ # Notice there is no direct coupling of type between this
13
+ # and the possible caller, just a contract on the params
14
+ module Yahtzee
15
+ module Scoring
16
+
17
+ extend UpperCard
18
+ extend LowerCard
19
+
20
+ module_function
21
+ def score(dice, placement, &updater)
22
+ value = send("score_#{placement.to_s}", dice)
23
+ updater.call(placement, value)
24
+ end
25
+
26
+ def score_subtotal(scores, placement, &updater)
27
+ value = reduce_values_from_hash(scores)
28
+ updater.call(placement, value)
29
+ end
30
+
31
+ def score_upper_total(scores, &updater)
32
+ subtotal = reduce_values_from_hash(scores)
33
+ bonus = subtotal >= 63 ? 35 : 0
34
+ updater.call(:upper_total, bonus+subtotal)
35
+ end
36
+
37
+ def score_game_total(scores, &updater)
38
+ value = reduce_values_from_hash(scores)
39
+ updater.call(:game_total, value)
40
+ end
41
+
42
+ def reduce_values_from_hash(hash)
43
+ hash.values.compact.reduce(:+)
44
+ end
45
+
46
+ end
47
+ end
48
+
@@ -0,0 +1,62 @@
1
+ require 'yahtzee'
2
+
3
+ module Yahtzee::Scoring
4
+ module LowerCard
5
+ module_function
6
+
7
+ def score_chance(dice)
8
+ dice.reduce(:+)
9
+ end
10
+
11
+ def score_full_house(dice)
12
+ sorted = dice.sort
13
+ (sorted.count(sorted.first) +
14
+ sorted.count(sorted.last) == 5) ? 25 : 0
15
+ end
16
+
17
+ def score_small_straight(dice)
18
+ dice.each_cons(4).any? do |a,b,c,d|
19
+ a+1==b && b+1==c && c+1==d
20
+ end ? 30 : 0
21
+ end
22
+
23
+ def score_large_straight(dice)
24
+ dice.each_cons(5).any? do |a,b,c,d,e|
25
+ a+1==b && b+1==c && c+1==d && d+1==e
26
+ end ? 40 : 0
27
+ end
28
+
29
+ def score_three_of_a_kind(dice)
30
+ score_min_of_a_kind(dice, 3)
31
+ end
32
+
33
+ def score_four_of_a_kind(dice)
34
+ score_min_of_a_kind(dice, 4)
35
+ end
36
+
37
+ def score_yahtzee(dice)
38
+ dice.uniq.count == 1 ? 50 : 0
39
+ end
40
+
41
+ def score_bonus_yahtzee(dice)
42
+ dice.uniq.count == 1 ? 100 : 0
43
+ end
44
+ alias_method :score_bonus_yahtzee_1, :score_bonus_yahtzee
45
+ alias_method :score_bonus_yahtzee_2, :score_bonus_yahtzee
46
+ alias_method :score_bonus_yahtzee_3, :score_bonus_yahtzee
47
+
48
+ def score_min_of_a_kind(dice, min)
49
+ value = max_dupe_count_as_hash(dice)
50
+ value[1] >= min ? value.reduce(:*) : 0
51
+ end
52
+
53
+ def dup_counts_as_hash(dice)
54
+ dice.each_with_object(Hash.new(0)) {|o, h| h[o] += 1 }
55
+ end
56
+
57
+ def max_dupe_count_as_hash(dice)
58
+ dup_counts_as_hash(dice).max_by {|_,v| v }
59
+ end
60
+ end
61
+ end
62
+
@@ -0,0 +1,34 @@
1
+ module Yahtzee::Scoring
2
+ module UpperCard
3
+ module_function
4
+
5
+ def score_aces(dice)
6
+ score_sums(dice, 1)
7
+ end
8
+
9
+ def score_twos(dice)
10
+ score_sums(dice, 2)
11
+ end
12
+
13
+ def score_threes(dice)
14
+ score_sums(dice, 3)
15
+ end
16
+
17
+ def score_fours(dice)
18
+ score_sums(dice, 4)
19
+ end
20
+
21
+ def score_fives(dice)
22
+ score_sums(dice, 5)
23
+ end
24
+
25
+ def score_sixes(dice)
26
+ score_sums(dice, 6)
27
+ end
28
+
29
+ def score_sums(dice, scoring_die)
30
+ dice.select {|die| die == scoring_die}.reduce(:+)
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,13 @@
1
+ $:.unshift(File.expand_path("../../lib", __FILE__))
2
+
3
+ # Yahtzee is the namespace for the whole project
4
+ # it is therefore used in every file. Every file
5
+ # also need access to our error defs, so that is
6
+ # here as well.
7
+ #
8
+ module Yahtzee
9
+ VERSION = "0.0.3"
10
+ end
11
+
12
+ require 'errors'
13
+
@@ -0,0 +1,20 @@
1
+ require 'test_helper'
2
+
3
+ require 'dice'
4
+
5
+ describe "A turn of dice rolling" do
6
+ subject { Yahtzee::Dice }
7
+
8
+ it "will return 5 new dice values" do
9
+ subject.roll(5).count.must_equal 5
10
+ end
11
+
12
+ it "will play a turn to a large straight" do
13
+ roll_1 = [1,2,3,6,6]
14
+
15
+ roll_2 = subject.reroll(roll_1[0..2])
16
+ roll_3 = roll_2[0..2] + [4,5]
17
+
18
+ roll_3.must_equal (1..5).to_a
19
+ end
20
+ end
@@ -0,0 +1,431 @@
1
+ require 'test_helper'
2
+ require 'game'
3
+ include Yahtzee
4
+ include Yahtzee::Game
5
+
6
+ describe Yahtzee::Game do
7
+ it "must play a full 1 person game" do
8
+ # Inore Dice stubs. They are only here to 'cheat'
9
+ # the outcomewhile testing the 'dsl'
10
+
11
+ score_card = ScoreCard.new
12
+ writer = ScoreCard.persist(score_card)
13
+
14
+ # Round 1: Yahtzee
15
+ Round.new do
16
+ Dice.stub :roll, [2,2,4,6,6] do
17
+ first_roll.must_equal [2,2,4,6,6]
18
+ end
19
+
20
+ Dice.stub :roll, [2,2,6] do
21
+ second_roll([2,2]).must_equal [2,2,2,2,6]
22
+ end
23
+
24
+ Dice.stub :roll, [2] do
25
+ third_roll = third_roll([2,2,2,2])
26
+ third_roll.must_equal [2,2,2,2,2]
27
+
28
+ score_card = Scoring.score(third_roll,
29
+ :yahtzee, &writer)
30
+
31
+ score_card.yahtzee.must_equal 50
32
+ end
33
+ end
34
+
35
+ # Round 2: Small Straight
36
+ Round.new do
37
+ Dice.stub :roll, [1,2,3,6,6] do
38
+ first_roll.must_equal [1,2,3,6,6]
39
+ end
40
+
41
+ Dice.stub :roll, [4,4,4] do
42
+ second_roll([1,2,3]).must_equal [1,2,3,4,4]
43
+ end
44
+
45
+ Dice.stub :roll, [2] do
46
+ third_roll = third_roll([1,2,3,4])
47
+ third_roll.must_equal [1,2,3,4,2]
48
+
49
+ score_card = Scoring.score(third_roll,
50
+ :small_straight, &writer)
51
+
52
+ score_card.small_straight.must_equal 30
53
+ score_card.yahtzee.must_equal 50
54
+ end
55
+ end
56
+
57
+ # Round 3: Aces
58
+ Round.new do
59
+ Dice.stub :roll, [1,1,1,3,6] do
60
+ first_roll.must_equal [1,1,1,3,6]
61
+ end
62
+
63
+ Dice.stub :roll, [1,4] do
64
+ second_roll([1,1,1]).must_equal [1,1,1,1,4]
65
+ end
66
+
67
+ Dice.stub :roll, [2] do
68
+ third_roll = third_roll([1,1,1,1])
69
+ third_roll.must_equal [1,1,1,1,2]
70
+
71
+ score_card = Scoring.score(third_roll,
72
+ :aces, &writer)
73
+
74
+ score_card.aces.must_equal 4
75
+ score_card.small_straight.must_equal 30
76
+ score_card.yahtzee.must_equal 50
77
+ end
78
+ end
79
+
80
+ # Round 4: Full House
81
+ Round.new do
82
+ Dice.stub :roll, [1,1,2,3,6] do
83
+ first_roll.must_equal [1,1,2,3,6]
84
+ end
85
+
86
+ Dice.stub :roll, [1,4,5] do
87
+ second_roll([1,1]).must_equal [1,1,1,4,5]
88
+ end
89
+
90
+ Dice.stub :roll, [4,4] do
91
+ third_roll = third_roll([1,1,1])
92
+ third_roll.must_equal [1,1,1,4,4]
93
+
94
+ score_card = Scoring.score(third_roll,
95
+ :full_house, &writer)
96
+
97
+ score_card.full_house.must_equal 25
98
+ score_card.aces.must_equal 4
99
+ score_card.small_straight.must_equal 30
100
+ score_card.yahtzee.must_equal 50
101
+ end
102
+ end
103
+
104
+ # Round 5: Fives
105
+ Round.new do
106
+ Dice.stub :roll, [1,5,2,5,5] do
107
+ first_roll.must_equal [1,5,2,5,5]
108
+ end
109
+
110
+ Dice.stub :roll, [4,5] do
111
+ second_roll([5,5,5]).must_equal [5,5,5,4,5]
112
+ end
113
+
114
+ Dice.stub :roll, [4] do
115
+ third_roll = third_roll([5,5,5,5])
116
+ third_roll.must_equal [5,5,5,5,4]
117
+
118
+ score_card = Scoring.score(third_roll,
119
+ :fives, &writer)
120
+
121
+ score_card.fives.must_equal 20
122
+ score_card.full_house.must_equal 25
123
+ score_card.aces.must_equal 4
124
+ score_card.small_straight.must_equal 30
125
+ score_card.yahtzee.must_equal 50
126
+ end
127
+ end
128
+
129
+ # Round 6: Four of a Kind
130
+ Round.new do
131
+ Dice.stub :roll, [4,4,4,5,5] do
132
+ first_roll.must_equal [4,4,4,5,5]
133
+ end
134
+
135
+ Dice.stub :roll, [4,5] do
136
+ second_roll([4,4,4]).must_equal [4,4,4,4,5]
137
+ end
138
+
139
+ Dice.stub :roll, [4,3] do
140
+ third_roll = third_roll([4,4,4])
141
+ third_roll.must_equal [4,4,4,4,3]
142
+
143
+ score_card = Scoring.score(third_roll,
144
+ :four_of_a_kind, &writer)
145
+
146
+ score_card.four_of_a_kind.must_equal 16
147
+ score_card.fives.must_equal 20
148
+ score_card.full_house.must_equal 25
149
+ score_card.aces.must_equal 4
150
+ score_card.small_straight.must_equal 30
151
+ score_card.yahtzee.must_equal 50
152
+ end
153
+ end
154
+
155
+ # Round 7: Bonus Yahtzee 1
156
+ Round.new do
157
+ Dice.stub :roll, [4,4,4,6,5] do
158
+ first_roll.must_equal [4,4,4,6,5]
159
+ end
160
+
161
+ Dice.stub :roll, [4,2] do
162
+ second_roll([4,4,4]).must_equal [4,4,4,4,2]
163
+ end
164
+
165
+ Dice.stub :roll, [4,4] do
166
+ third_roll = third_roll([4,4,4])
167
+ third_roll.must_equal [4,4,4,4,4]
168
+
169
+ score_card = Scoring.score(third_roll,
170
+ :bonus_yahtzee_1, &writer)
171
+
172
+ score_card.bonus_yahtzee_1.must_equal 100
173
+ score_card.four_of_a_kind.must_equal 16
174
+ score_card.fives.must_equal 20
175
+ score_card.full_house.must_equal 25
176
+ score_card.aces.must_equal 4
177
+ score_card.small_straight.must_equal 30
178
+ score_card.yahtzee.must_equal 50
179
+ end
180
+ end
181
+
182
+ # Round 8: sixes
183
+ Round.new do
184
+ Dice.stub :roll, [6,6,6,6,5] do
185
+ first_roll.must_equal [6,6,6,6,5]
186
+ end
187
+
188
+ Dice.stub :roll, [2] do
189
+ second_roll([6,6,6,6]).must_equal [6,6,6,6,2]
190
+ end
191
+
192
+ Dice.stub :roll, [4] do
193
+ third_roll = third_roll([6,6,6,6])
194
+ third_roll.must_equal [6,6,6,6,4]
195
+
196
+ score_card = Scoring.score(third_roll,
197
+ :sixes, &writer)
198
+
199
+ score_card.sixes.must_equal 24
200
+ score_card.bonus_yahtzee_1.must_equal 100
201
+ score_card.four_of_a_kind.must_equal 16
202
+ score_card.fives.must_equal 20
203
+ score_card.full_house.must_equal 25
204
+ score_card.aces.must_equal 4
205
+ score_card.small_straight.must_equal 30
206
+ score_card.yahtzee.must_equal 50
207
+ end
208
+ end
209
+
210
+ # Round 9: threes
211
+ Round.new do
212
+ Dice.stub :roll, [3,3,2,2,1] do
213
+ first_roll.must_equal [3,3,2,2,1]
214
+ end
215
+
216
+ Dice.stub :roll, [3,2,2] do
217
+ second_roll([3,3]).must_equal [3,3,3,2,2]
218
+ end
219
+
220
+ Dice.stub :roll, [3,1] do
221
+ third_roll = third_roll([3,3,3])
222
+ third_roll.must_equal [3,3,3,3,1]
223
+
224
+ score_card = Scoring.score(third_roll,
225
+ :threes, &writer)
226
+
227
+ score_card.threes.must_equal 12
228
+ score_card.sixes.must_equal 24
229
+ score_card.bonus_yahtzee_1.must_equal 100
230
+ score_card.four_of_a_kind.must_equal 16
231
+ score_card.fives.must_equal 20
232
+ score_card.full_house.must_equal 25
233
+ score_card.aces.must_equal 4
234
+ score_card.small_straight.must_equal 30
235
+ score_card.yahtzee.must_equal 50
236
+ end
237
+ end
238
+
239
+ # Round 10: large straight
240
+ Round.new do
241
+ Dice.stub :roll, [2,3,4,5,4] do
242
+ first_roll.must_equal [2,3,4,5,4]
243
+ end
244
+
245
+ Dice.stub :roll, [3] do
246
+ second_roll([2,3,4,5]).must_equal [2,3,4,5,3]
247
+ end
248
+
249
+ Dice.stub :roll, [6] do
250
+ third_roll = third_roll([2,3,4,5])
251
+ third_roll.must_equal [2,3,4,5,6]
252
+
253
+ score_card = Scoring.score(third_roll,
254
+ :large_straight, &writer)
255
+
256
+ score_card.large_straight.must_equal 40
257
+ score_card.threes.must_equal 12
258
+ score_card.sixes.must_equal 24
259
+ score_card.bonus_yahtzee_1.must_equal 100
260
+ score_card.four_of_a_kind.must_equal 16
261
+ score_card.fives.must_equal 20
262
+ score_card.full_house.must_equal 25
263
+ score_card.aces.must_equal 4
264
+ score_card.small_straight.must_equal 30
265
+ score_card.yahtzee.must_equal 50
266
+ end
267
+ end
268
+
269
+ # Round 11: twos
270
+ Round.new do
271
+ Dice.stub :roll, [2,2,2,5,4] do
272
+ first_roll.must_equal [2,2,2,5,4]
273
+ end
274
+
275
+ Dice.stub :roll, [2,1] do
276
+ second_roll([2,2,2]).must_equal [2,2,2,2,1]
277
+ end
278
+
279
+ Dice.stub :roll, [4] do
280
+ third_roll = third_roll([2,2,2,2])
281
+ third_roll.must_equal [2,2,2,2,4]
282
+
283
+ score_card = Scoring.score(third_roll,
284
+ :twos, &writer)
285
+
286
+ score_card.twos.must_equal 8
287
+ score_card.large_straight.must_equal 40
288
+ score_card.threes.must_equal 12
289
+ score_card.sixes.must_equal 24
290
+ score_card.bonus_yahtzee_1.must_equal 100
291
+ score_card.four_of_a_kind.must_equal 16
292
+ score_card.fives.must_equal 20
293
+ score_card.full_house.must_equal 25
294
+ score_card.aces.must_equal 4
295
+ score_card.small_straight.must_equal 30
296
+ score_card.yahtzee.must_equal 50
297
+ end
298
+ end
299
+
300
+ # Round 12: three of a kind
301
+ Round.new do
302
+ Dice.stub :roll, [2,2,2,5,4] do
303
+ first_roll.must_equal [2,2,2,5,4]
304
+ end
305
+
306
+ Dice.stub :roll, [1,1] do
307
+ second_roll([2,2,2]).must_equal [2,2,2,1,1]
308
+ end
309
+
310
+ Dice.stub :roll, [3,4] do
311
+ third_roll = third_roll([2,2,2])
312
+ third_roll.must_equal [2,2,2,3,4]
313
+
314
+ score_card = Scoring.score(third_roll,
315
+ :three_of_a_kind, &writer)
316
+
317
+ score_card.three_of_a_kind.must_equal 6
318
+ score_card.twos.must_equal 8
319
+ score_card.large_straight.must_equal 40
320
+ score_card.threes.must_equal 12
321
+ score_card.sixes.must_equal 24
322
+ score_card.bonus_yahtzee_1.must_equal 100
323
+ score_card.four_of_a_kind.must_equal 16
324
+ score_card.fives.must_equal 20
325
+ score_card.full_house.must_equal 25
326
+ score_card.aces.must_equal 4
327
+ score_card.small_straight.must_equal 30
328
+ score_card.yahtzee.must_equal 50
329
+ end
330
+ end
331
+
332
+ # Round 13: fours
333
+ Round.new do
334
+ Dice.stub :roll, [4,4,4,5,4] do
335
+ first_roll.must_equal [4,4,4,5,4]
336
+ end
337
+
338
+ Dice.stub :roll, [1] do
339
+ second_roll([4,4,4,4]).must_equal [4,4,4,4,1]
340
+ end
341
+
342
+ Dice.stub :roll, [3] do
343
+ third_roll = third_roll([4,4,4,4])
344
+ third_roll.must_equal [4,4,4,4,3]
345
+
346
+ score_card = Scoring.score(third_roll,
347
+ :fours, &writer)
348
+
349
+ score_card.fours.must_equal 16
350
+ score_card.three_of_a_kind.must_equal 6
351
+ score_card.twos.must_equal 8
352
+ score_card.large_straight.must_equal 40
353
+ score_card.threes.must_equal 12
354
+ score_card.sixes.must_equal 24
355
+ score_card.bonus_yahtzee_1.must_equal 100
356
+ score_card.four_of_a_kind.must_equal 16
357
+ score_card.fives.must_equal 20
358
+ score_card.full_house.must_equal 25
359
+ score_card.aces.must_equal 4
360
+ score_card.small_straight.must_equal 30
361
+ score_card.yahtzee.must_equal 50
362
+ end
363
+ end
364
+
365
+ # Round 14: chance
366
+ Round.new do
367
+ Dice.stub :roll, [4,4,4,5,4] do
368
+ first_roll.must_equal [4,4,4,5,4]
369
+ end
370
+
371
+ Dice.stub :roll, [1] do
372
+ second_roll([4,4,4,4]).must_equal [4,4,4,4,1]
373
+ end
374
+
375
+ Dice.stub :roll, [3] do
376
+ third_roll = third_roll([4,4,4,4])
377
+ third_roll.must_equal [4,4,4,4,3]
378
+
379
+ score_card = Scoring.score(third_roll,
380
+ :chance, &writer)
381
+
382
+ score_card.chance.must_equal 19
383
+ score_card.fours.must_equal 16
384
+ score_card.three_of_a_kind.must_equal 6
385
+ score_card.twos.must_equal 8
386
+ score_card.large_straight.must_equal 40
387
+ score_card.threes.must_equal 12
388
+ score_card.sixes.must_equal 24
389
+ score_card.bonus_yahtzee_1.must_equal 100
390
+ score_card.four_of_a_kind.must_equal 16
391
+ score_card.fives.must_equal 20
392
+ score_card.full_house.must_equal 25
393
+ score_card.aces.must_equal 4
394
+ score_card.small_straight.must_equal 30
395
+ score_card.yahtzee.must_equal 50
396
+
397
+ assert (score_card.to_hash.
398
+ select {|_,v| v.nil? }.
399
+ keys - ScoreCard.meta_keys).
400
+ none?, "all required fields must be filled"
401
+ end
402
+
403
+ # Collect the score from the 'upper' section
404
+ upper_scores = score_card.to_hash.select do |k,_|
405
+ ScoreCard.upper_keys.include? k
406
+ end
407
+ # Score and persist upper total
408
+ score_card = Scoring.score_upper_total(upper_scores,
409
+ &writer)
410
+
411
+ score_card.upper_total.must_equal 119
412
+
413
+ # Collect the score from the 'lower' section
414
+ lower_scores = score_card.to_hash.select do |k,_|
415
+ ScoreCard.lower_keys.include? k
416
+ end
417
+
418
+ # Merge the upper total with the lower scores
419
+ game_scores = lower_scores.merge(
420
+ upper_total: score_card.upper_total)
421
+
422
+ # Score and persist game total
423
+ score_card = Scoring.score_game_total(game_scores,
424
+ &writer)
425
+
426
+ score_card.game_total.must_equal 405
427
+ end
428
+
429
+ end
430
+ end
431
+