functional-yahtzee 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+