0

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?

Dipet
  • 323
  • 3
  • 14
  • 2
    You are over-thinking some points. For instance you probably don't need to check the input is valid for the koan - although in a real business situation the way you are thinking about checking values (usually called validation) would be really useful. I don't think a walked-through setup for this koan would be useful on Stack Overflow. It's too personal to your training needs. Very brief pointers: The if-then checks are not a bad starting point, but don't think of processing each item in the array - look at the contents of the array as a group - check documentation for `Array` for good methods – Neil Slater Jun 28 '15 at 20:25
  • 2
    Don't give up! I recall taking real analysis as an undergraduate. I just couldn't get it. I thought I'd have to give up my dream of becoming a mathematician. Then one day--literally one day--the clouds parted and all was clear, even obvious. I went on to take more advanced courses in analysis and found them easy (but fascinating). On the other hand, most people are just not cut out to be coders. (Postscript: I finally came to my senses and realized I just wasn't smart enough to become anything more than a run-of-the-mill mathematician, which didn't interest me.) – Cary Swoveland Jun 28 '15 at 21:57
  • 2
    This is where test driven development can help you. I wouldn't worry about choosing a framework. A basic approach is to take one requirement from the koan and make it work in a script or fail if it doesn't. Maybe start with the case where you have three ones and fail('1,1,1,2,2 should be 1,000) unless score([1,1,1,2,2]) == 1,000. Then add another requirement and make score work when there are three numbers that are not ones. Continue adding requirements until you are done! A great video of this technique can be found in "Jim Weirich - Roman Numerals Kata" - https://youtu.be/983zk0eqYLY – Jeff Price Jun 29 '15 at 02:24

1 Answers1

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; and
  • other_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])]
Community
  • 1
  • 1
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
  • Oh wow, I had given up on trying to figure it out but your post makes me want to tackle it again. I jotted everything down and I'm currently trying to breakdown the logic. This is a tremendous help! Thank you Cary! – Dipet Jun 29 '15 at 17:37