Yahtzee in ruby in cyber-dojo

I've been doing the Yahtzee kata in cyber-dojo to improve my Ruby. During the 247 increments I learned about...
  • any?
  • send
  • [lo...hi]
  • reduce
  • :+
  • module names start with a capital
It's also on pastie if you like a black background. You can use Cyber-Dojo to step through the diffs of every single increment!
module Yahtzee

  def self.score dice, category, n=nil
    return -1 if invalid(dice, category)
    if category == :value
      value(dice, n)
    else
      send(category, dice)
    end
  end

  def self.chance dice
    dice.reduce(:+)
  end

  def self.yahtzee dice
    dice.uniq.length == 1 ? 50 : 0
  end

  def self.value dice, n
    dice.count{|die| die == n } * n
  end

  def self.one_pair dice
    die(tally(dice).find{|n| pair?n }) * 2
  end

  def self.two_pair dice
    score_tally dice, [2,2]
  end

  def self.three_of_a_kind dice
    score_tally dice, [3]
  end

  def self.four_of_a_kind dice
    score_tally dice, [4]
  end

  def self.full_house dice
    score_tally dice, [3,2]
  end
  
  def self.small_straight dice
    dice.sort == [1,2,3,4,5] ? 15 : 0
  end

  def self.large_straight dice
    dice.sort == [2,3,4,5,6] ? 20 : 0
  end

  #- - - - - - - - - - - - - - - - - - - - - - - 

  def self.categories
    [:value, :chance, :yahtzee, :one_pair, :two_pair,
     :three_of_a_kind, :four_of_a_kind, :full_house,
     :small_straight, :large_straight
    ]
  end

  def self.invalid dice, category
    dice == nil \
      or dice.length != 5 \
        or dice.any?{|die| die < 1 or die > 6 } \
          or !categories.include? category
  end

  def self.score_tally dice, pattern
    starts?(pattern, tallies(dice)) \
      ? sum(dice, pattern.length) \
      : 0
  end

  def self.starts? short, long
    long[0...short.length] == short
  end

  def self.tallies dice
    tally(dice).map{|n| count(n) }
  end

  def self.rolls dice
    tally(dice).map{|n| die(n) }
  end

  def self.tally dice
    dice.uniq.map {|die| [dice.count(die),die] }.sort.reverse 
  end

  def self.sum dice, n
    sum2(rolls(dice)[0...n], dice)
  end

  def self.sum2 scorers, dice
    dice.select {|die| scorers.include?die }.reduce(:+)
  end

  def self.pair? n
    count(n) == 2
  end

  def self.count n
    n[0]
  end

  def self.die n
    n ? n[1] : 0
  end

end
I challenged myself to write the code in a functional style with no local variables - it's a bit 'dense' in places as a result.

No comments:

Post a Comment