Pete On Software

RSS Feed

The Greed Ruby Koan

This post is an extension of my last post on the Ruby Koans. Today, I tackled the next Ruby Koan on the block, which was to score dice throws on a simplified version of the game Greed. The first thing I noticed was that the Koans that I got from EdgeCase had a typo in it. It suggested that the following was true:

That first assert should actually total 250, as the 3 2’s are worth 200 and the 5 is worth 50. I searched the web to make sure that I wasn’t crazy and the version of this file checked in to Google Code by Tim Wingfield agreed with me, so I forged on.

The hardest part of this one for me was figuring out the best way to even work this out in English. I play the Farkle game on Facebook quite a bit, so I was really comfortable with scoring the dice, but I just couldn’t figure out a good step-by-step way to do this. I eventually decided on a Hash to tally up the number of each dice present. This choice was made even easier when I found that Hash had an each_pair method that enumerated over the collection.

After that, I tried my best to implement the logic (this code passes the tests, I don’t promise it is bug-free) and have now put this up for criticism. Last time, I learned some great lessons from Nathan Kelley who shared his code in the comments of my last post. If you have any questions, comments, criticisms, or rebukes of this code, let me know and I’d love to hear them.

I definitely am guilty of writing C# in Ruby and had to undo a lot of code where I started to go down that path, so I welcome comments from Ruby enthusiasts on my code.

9 Comments

Matt Nowack  on October 2nd, 2009

I found the same error, here is how I went about solving the scoring exercise. These koans are nice for learning Ruby.

I had the same idea of using a hash to count them up


def score(dice)
result = 0
counter = {}
(1..6).each { |x| counter[x] = 0 }
dice.each { |d| counter[d] += 1 }
if counter[1] >= 3
result += 1000
counter[1] -= 3
end

(2..6).each do |x|
if counter[x] >= 3
result += (100 * x)
counter[x] -= 3
end
end

result += (counter[1] * 100)
result += (counter[5] * 50)
end

Jon Kruger  on May 13th, 2010

I implemented it in C#, but here is my solution.
http://github.com/downloads/JonKruger/solid-principles/SWE101%20Greed%20-%20Completed.zip

I took a different approach when writing the implementation code. I tried to make the code readable so that it would be really easy to figure out what it’s doing. For example, what happens when I get a change to the rules and I (or someone else) has to change my code? How easy is it to figure out what to change?

I tried to avoid complicated math formulas for this reason. Sure, you can make it work that way in less lines of code, but at the expense of readability.

I still have to try it in Ruby someday and see what I come up with! I’ll probably end up writing C# in Ruby like you said. 🙂

Mike Murray  on July 19th, 2010

Whoa, delete the last comment, horrible formatting…

Instead of doing a hash with counts, I sorted the dice array and then took length-of-3 subarrays to see if I could find a three-some. On each subarray, I used the array.uniq trick that was suggested in the comments of your last post about the Triangles koan.

Here’s my code: http://pastie.org/1051380

Michael Conigliaro  on December 22nd, 2010

FYI, you can replace line 42 with this:


dice_sort = Hash.new(0)

This will give each item in the hash a default value of 0, so that lines 46-48 will work properly.

Scott Coil  on January 22nd, 2011

Here was my solution.

def score(dice)
# You need to write this method
points = 0
dice.sort!
while dice.size > 0 do
if dice[0] == dice[2]
if dice[0] == 1
points += 1000
dice.shift(3)
next
end
points += dice[0] * 100
dice.shift(3)
end
die = dice.shift
points += 50 if die == 5
points += 100 if die == 1
end
return points
end

Dale  on February 2nd, 2011

This is my solution, curiously in some parts similar to some of the others give. I think it is quite clean, but I’m only learning so I’m open to criticisms and/or other suggestions.

http://pastebin.com/Aa9NbhDS

wildrhombus  on February 28th, 2011

This is how I did it. I’m new at ruby so I welcome any comments

if( dice.size > 5 )
return 0
end

result = 0;
10.times { |i|
count = dice.count(i)
if( count >= 3 )
if( i == 1 )
result += 1000
else
result += i*100
end
count -= 3
end

case i
when 1
result += count*100
when 5
result += count*50
end
}
return result

d2  on June 9th, 2011

Several examples here won’t scale beyond 5 dice, but the scoring rules imply that 6 dice wake up the chance for 2000 points for 6 x 1’s, etc.

Modulo and integer division can fix the scoring easily in routines where they’re coded for bins/counts.

Doing this stretch further into the koan is a nice extra stretch. I’d actually come here to look for sandwich code or other prior-Koan lessons put to work, since I just resorted to old lame count-the-ones, count-the-twos flat code. Mine’s easily read, but has that ‘lots of similar lines’ smell to it, and wouldn’t scale well for a ‘greed’ game played with N-sided dice. … which’d be another good ‘extra’ stretch beyond the Koan.

Lukasz  on July 27th, 2011

This is my solution:

def score(dice)
result = 0
dice.uniq.each { |element|
result += element * 100 if dice.count(element) >= 3 and element != 1 and element != 5
(1..dice.count(element)).to_a.each{ |elem| result += elem % 3 == 0 ? 800:100} if element == 1
(1..dice.count(element)).to_a.each{ |elem| result += elem % 3 == 0 ? 400:50} if element == 5
}
result
end

Leave a Comment