问题
I'm quickly coming to the conclusion that I can't be a programmer. Despite taking ample notes and practicing everything in Ruby Koans up to this point on top of going through the Ruby course on Codecademy three times and currently making my way through Chris Pine's book averaging 6hrs a day studying...this current Koan is an exercise in frustration and the realization that not everyone can be a programmer.
The exercise is as follows
# Greed is a dice game where you roll up to five dice to accumulate
# points. The following "score" function will be used to calculate the
# score of a single roll of the dice.
#
# A greed roll is scored as follows:
#
# * A set of three ones is 1000 points
#
# * A set of three numbers (other than ones) is worth 100 times the
# number. (e.g. three fives is 500 points).
#
# * A one (that is not part of a set of three) is worth 100 points.
#
# * A five (that is not part of a set of three) is worth 50 points.
#
# * Everything else is worth 0 points.
#
#
# Examples:
#
# score([1,1,1,5,1]) => 1150 points
# score([2,3,4,6,2]) => 0 points
# score([3,4,5,3,3]) => 350 points
# score([1,5,1,2,4]) => 250 points
#
# More scoring examples are given in the tests below:
#
# Your goal is to write the score method.
def score(dice)
end
So looking at everything on first glance, I'm assuming the test will provide the random num functionality via dice
? Or do I need to write that myself? Now I have to limit the range the dice rolls? Then all I have to do is get the numbers and put them into an array but only up to 5 positions (so like while array has 5 or less entries?
I then need to define how the point system works. So something like score[0] = 100, score[2]= 200
? But now I've hardcoded the scores, what if the first position isn't a 1 but is a 5? Then the number would be 50. So how about an if/else statement? something like
if score[0] == 1
point = 100
elsif score[0] == 5
point = 50
else point = 0
Then I repeat this for positions [1]-[4]?
Despite wanting to very badly, I don't want to simply google the answer, I'd rather get some kind of direction for lack of a better word.
Hopefully this isn't too general of a question but how do you even approach something like this? Perhaps I should go through Pine's book from beginning to end first before tackling this?
回答1:
You could write your method:
def score(d1,d2,d3,d4,d5)
...
end
The first thing you need to do is determine if three of five values are the same. If three are the same you will need to know if they are all 1
's or some other value, and you will need to know what the other two values are. If at most two values are the same, you need to know that as well. That's really the crux of the problem. So lets write a method:
def partition(d1,d2,d3,d4,d5)
...
end
which is called from score
like this:
three_value, other_values = partition(d1,d2,d3,d4,d5)
where:
three_value
equals the common value of the group of three equal values (1-6
) or (say)nil
if there is no group of three equal values; andother_values
is an array of the two values from[d1,d2,d3,d4,d5]
that exclude the three equal values (if there is a group of three equal values) or[d1,d2,d3,d4,d5]
(if there is no group of three equal values).
For example,
three_value, other_values = partition(1,3,4,3,6)
#=> [nil, [1, 3, 4, 3, 6]]
three_value, other_values = partition(1,3,4,3,3)
#=> [3, [1, 4]]
three_value, other_values = partition(1,3,3,3,3)
#=> [3, [1, 3]]
three_value, other_values = partition(3,3,3,3,3)
#=> [3, [3, 3]]
Once we have the method partition
, the method score
if pretty straightforward:
def score(d1,d2,d3,d4,d5)
three_value, other_values = partition(d1,d2,d3,d4,d5)
total = case three_value
when 1 then 1000
when nil then 0
else three_value * 1000
end
other_values.each do |i|
total += case i
when ...
...
when ...
...
end
end
end
Before we get on to the method partition
, we can simplify score
as follows:
def score(*d)
three_value, other_values = partition(*d)
total = case three_value
when 1 then 1000
when nil then 0
else three_value * 1000
end
other_values.each do |i|
total += case i
when ...
...
when ...
...
end
end
end
Using the "splat" operator, *
, is convenient, because we don't care about the order of the rolls of the die. In calling the method score
or partition
, if d = [1,3,4,3,3]
, score(*d)
is identical to:
score(1,3,4,3,3)
(Do to see why *
is called "splat"?)
In the method score
above, d
(not *d
) is an array of five values.
Now let's look at the method partition
. We need to count the number of times each outcome (1-6
) occurs. That's a good job for a hash:
def partition(*d)
counts = {}
d.each do |i|
if counts.key?(i)
counts[i] += 1
else
counts[i] = 1
end
end
...
end
Suppose d = [1,3,4,3,3]
. Then
counts #=> {1=>1, 3=>3, 4=>1}
We now need to find the key with the largest value. For that we could use Enumerable#max_by:
k, v = counts.max_by { |k,v| v }
#=> [3, 3]
k #=> 3
v #=> 3
We can then calculate other_values
like so:
other_values = case v
when 3
d - [k]
when 4
(d - [k]) << k
when 5
(d - [k]) << k << k
else
d
end
Note Array#- is the array difference method.
Lastly,
three_value = (v >= 3) ? k : nil
and partition
will return:
[three_value, other_values]
Can you put all this together?
[You may not want to read the following until you have working code.]
Once you gain experience with Ruby you will be able to write partition
as follows:
def partition(*d)
k,v = d.each_with_object(Hash.new(0)) { |i,h| h[i]+=1 }.max_by(&:last)
(v < 3)) ? [nil, d] : [k, d - [k] + [k]*(v-3)]
end
Aside: I would like to see a core method Array#difference
, which I define and elaborate here. This would allow the last line of the body of partition
to be expressed:
(v < 3)) ? [nil, d] : [k, d.difference([k,k,k])]
来源:https://stackoverflow.com/questions/31104241/ruby-koan-182-greed-dice