Friday, August 5, 2011

Ruby Koan for Dice Scoring (Greed) - Solution #1

Recently I've started teaching myself Ruby and as part of my quest I discovered the amazing Ruby Koan site by EdgeCase.  The concept of a koan dates back to the foundations of Zen Buddhism.  You might recognize the question "what is the sound of one hand clapping".  This is actually part of a Zen koan.  A koan is a question or story that serves as a fundamental teaching tool.  A traditional Zen koan cannot be understood via rational thought but rather through intuition leading to an "Aha!" moment.

Ruby Koans, as developed by EdgeCase, consist of 274 test cases and incomplete Ruby code.  Your task is to implement the code in order to make all the tests pass in classic TDD style.  For those unfamiliar with the term, TDD means Test Driven Development.  TDD follows the pattern of red, green, refactor.  When implementing a new section of code you start with its tests which will all fail (be 'red') initially.  Then you implement the code turning all of the tests green.  Finally, and over time, you go forth with confidence and refactor the code to higher and higher levels of quality (keeping it green).

Many of the Ruby Koan tests are fairly simple to make pass.  Some of the more esoteric Ruby features were more difficult to master (I thought Array.inject and blocks/yield would drive me to murder).  However, one of the most interesting koan related to scoring dice throws in a fictional game called Greed.  This koan is quite different than the others and is more like a Kata (a martial arts term referring to a repetitive exercise meant to hone skill -- I'll save Kata for another post).  Briefly, the koan asks you to implement a class that conforms to the following requirements:

# A greed roll is scored as follows [for a roll of up to 5 dice]:
#
# * 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

This particular koan consists of 13 (initially failing) tests.  Your implementation of the scoring method must make them all pass.  Ruby, by its nature, provides a multitude of ways (more than any other language I think) to solve this problem.  What follows is my first attempt.  It should be noted that error checking was excluded from the requirements nor did any of the tests include input error tests (like dice rolls outside 1..6, strings, etc.).

As a student of Ruby I am keen for feedback (particularly from the Ruby masters) as this solution seems a little... wordy.

def score(dice)
  score = 0
  sorted_dice = dice.sort # Sort the rolls

  while sorted_dice.length > 0
    val = sorted_dice.shift
    if sorted_dice.length >= 2 && val == sorted_dice[0] && val == sorted_dice[1]  # a triple!
      score += val == 1 ? 1000 : val * 100
      sorted_dice.shift(2)
    else
      score += 100 if val == 1
      score += 50  if val == 5
    end
  end

  return score
end

6 comments:

@rogueleaderr said...

Here's my solution, but I feel like there has to be a better way.

def score(dice)
total = 0
dice = dice.sort
(1..6).each do |die|
count = dice.count(die)
if count >= 3 then
total =+ (die == 1 ? 1000 : die * 100)
end
total += (count % 3) * 100 if die == 1
total += (count % 3) * 50 if die == 5

end

return total

end

Anonymous said...

Easybinarycash.com - Learn how to turn $500 into $5,000 in a month!

[url=http://www.easybinarycash.com/]Make Money Online[/url] - The Secret Reveled with Binary Option

Binary Options is the way to [url=http://www.easybinarycash.com/]make money[/url] securely online

Anonymous said...

def score(dice)
# You need to write this method
grouped_die = dice.sort_by{ |i| -dice.count(i)}.first.to_i
total = 100 * ( dice.count(1) % 3 ) + 50 * ( dice.count(5) % 3 )
total += (grouped_die == 1 ? 1000 : grouped_die * 100) if dice.count( grouped_die ) >= 3
total
end

Graphic Maker said...

A nice article for all web designers with a lot of great tips and tricks!
i recommend this to all my friends and all my members.
May i recommend a Graphic maker tool every web designer should have?
It;s better, faster and easier then any other tool!

Danillo Magno said...
This comment has been removed by the author.
Danillo Magno said...
This comment has been removed by the author.