-
Notifications
You must be signed in to change notification settings - Fork 0
/
dice_game.rb
128 lines (114 loc) · 3.21 KB
/
dice_game.rb
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
class Greed
def self.score(roll)
validate!(roll)
with_descriptive(roll)
# with_string(roll)
# with_array(roll)
# with_enumerable(roll)
# without_grace(roll)
# with_hack(roll)
# with_enumerablhack(roll)
end
private
# The Descriptive Way
POINTS = {
"1" => 100,
"2" => 0,
"3" => 0,
"4" => 0,
"5" => 50,
"6" => 0,
"111" => 1000,
"222" => 200,
"333" => 300,
"444" => 400,
"555" => 500,
"666" => 600,
}.freeze
def self.with_descriptive(roll)
roll.sort.join.scan(/((\d)\2\2)|(\d)/).sum do |num|
POINTS[num.compact.first]
end
end
# The String Way
def self.with_string(roll)
sorted = roll.sort.join
triple = sorted.match(/([1-6])\1\1/).to_s
aces = sorted.gsub(/#{triple}/, '').count("1")
fives = sorted.gsub(/#{triple}/, '').count("5")
result = triple == "111" ? 1000 : triple.to_i.div(100) * 100
result + aces * 100 + fives * 50
end
# The Array Way
def self.with_array(roll)
# scores[0] will store the value of a set of three
scores = roll.each_with_object(Array.new(7, 0)) do |val, count|
count[val] += 1
count[val], count[0] = 0, val if count[val] == 3
end
result = scores[0] == 1 ? 1000 : scores[0] * 100
result + scores[1] * 100 + scores[5] * 50
end
# The Enumerable Way
def self.with_enumerable(roll)
# (see shortened to 6 lines version below)
(1..6).map do |val|
[val, roll.count(val)]
end.inject(0) do |sum, (val, score)|
if score >= 3
score -= 3
sum += val == 1 ? 1000 : val * 100
end
sum + score * 50 * [nil,5,1].index(val).to_i # tortuous, I know
end
end
# The Disgraceful Way
def self.without_grace(roll)
# all possible outcomes: 5 - 4[,1] - 3[,2][,1] - 2[,1] - 1
(1..6).group_by do |value|
roll.count(value)
end.inject(0) do |sum, (score, values)|
if score >= 3
if values[0] == 1
sum += 1000 + (score - 3) * 100
else
sum += values[0] * 100
sum += (score - 3) * 50 if values[0] == 5
end
else
sum += values.sum do |val|
[1,5].include?(val) ? score * 50 * (val == 1 ? 2 : 1) : 0
end
end
sum
end
end
# The Hacky Way
def self.with_hack(roll)
roll.uniq.group_by do |value|
roll.count(value)
end.sum do |score, values|
values.map! { |v| v == 1 ? 10 : v }
if score >= 3
values[0]*100 + values[0]*10*(values[0] % 5 == 0 ? score-3 : 0)
else
values.sum { |v| v % 5 == 0 ? v*10*score : 0 }
end
end
end
# The Enumerablhack Way
def self.with_enumerablhack(roll)
(1..6).map do |val|
[val == 1 ? 10 : val, roll.count(val)]
end.inject(0) do |sum, (val, score)|
trio, score = val*100, score-3 if score >= 3 # love that
sum + trio.to_i + score*50*[0,5,10].index(val).to_i
end
end
def self.validate!(roll)
# most solutions will not work with more than 5 numbers
error = "Must be an array of at most 5 integers between 1 and 6"
valid = roll.is_a?(Array) && roll.size <= 5 && roll.all? { |n| (1..6).include? n }
raise ArgumentError, error unless valid
end
end