Ruby Koan 182 - 贪婪骰子

时间:2015-06-28 20:09:07

标签: ruby tdd

我很快得出的结论是,我不能成为一名程序员。尽管在Ruby Koans上做了大量的笔记和实践,直到完成Codecademy的Ruby课程三次,目前正在通过Chris Pine的书每天平均学习6小时...这个当前的Koan是一个练习沮丧和认识到不是每个人都可以成为程序员。

练习如下

# 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

首先看一下所有内容,我假设测试会通过dice提供随机数功能?或者我需要自己写吗?现在我必须限制掷骰子的范围?然后,我所要做的就是得到数字并将它们放入一个数组中,但最多只有5个位置(所以就像while array has 5 or less entries?我需要定义点系统的工作方式。所以像score[0] = 100, score[2]= 200这样的东西?但是现在我已经对分数进行了硬编码,如果第一个位置不是1但是5是什么呢?那么数字是50个。那么if / else语句怎么样呢?

if score[0] == 1
  point = 100
elsif score[0] == 5
  point = 50
else point = 0

然后我重复这个位置[1] - [4]?

尽管想要非常糟糕,我不想简单地谷歌答案,我宁愿因缺乏更好的词而得到某种方向。

希望这不是一般问题,但你怎么处理这样的事情呢?也许在解决这个问题之前,我应该首先从头到尾阅读Pine的书?

1 个答案:

答案 0 :(得分:1)

你可以写下你的方法:

def score(d1,d2,d3,d4,d5)
  ...
end

您需要做的第一件事是确定五个值中的三个是否相同。如果三个相同,则需要知道它们是1还是其他值,并且您需要知道其他两个值是什么。如果最多两个值相同,那么您也需要知道它。这确实是问题的症结所在。所以让我们写一个方法:

def partition(d1,d2,d3,d4,d5)
  ...
end

score调用,如下所示:

three_value, other_values = partition(d1,d2,d3,d4,d5)

其中:

    如果没有三个相等值的组,则
  • three_value等于三个相等值组(1-6)或(例如)nil的公共值;和
  • other_values是来自[d1,d2,d3,d4,d5]的两个值的数组,它排除了三个相等的值(如果有一组三个相等的值)或[d1,d2,d3,d4,d5](如果没有组)三个相等的值)。

例如,

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]] 

方法partition之后,方法score非常简单:

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

在我们开始使用方法partition之前,我们可以简化score,如下所示:

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

使用" splat"操作员*很方便,因为我们不关心骰子卷的顺序。在调用方法scorepartition时,如果d = [1,3,4,3,3],则score(*d)与以下内容相同:

score(1,3,4,3,3)    

(看看为什么*被调用" splat"?)

在上面的方法score中,d(不是*d)是一个包含五个值的数组。

现在让我们看看方法partition。我们需要计算每个结果(1-6)出现的次数。哈希是一个很好的工作:

def partition(*d)
  counts = {}
  d.each do |i|
    if counts.key?(i)
      counts[i] += 1
    else
      counts[i] = 1
    end
  end
  ...
end

假设d = [1,3,4,3,3]。然后

counts #=> {1=>1, 3=>3, 4=>1}

我们现在需要找到具有最大值的密钥。为此,我们可以使用Enumerable#max_by

k, v = counts.max_by { |k,v| v }
  #=> [3, 3]
k #=> 3
v #=> 3

我们可以像这样计算other_values

other_values = case v
               when 3
                 d - [k]
               when 4
                 (d - [k]) << k
               when 5
                 (d - [k]) << k << k
               else
                 d
               end

注意Array#-是数组差异方法。

最后,

 three_value = (v >= 3) ? k : nil

partition将返回:

 [three_value, other_values]

你能把所有这些放在一起吗?

[在您使用代码之前,您可能不想阅读以下内容。]

获得Ruby经验后,您可以按如下方式编写partition

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                      

旁白:我希望看到核心方法Array#difference,我定义并详细阐述here。这将允许表达partition正文的最后一行:

(v < 3)) ? [nil, d] : [k, d.difference([k,k,k])]