Ruby Greed Koan -我该如何改进我的if/then soup呢?

时间:2021-01-18 03:59:26

I'm working my way through the Ruby Koans in order to try and learn Ruby, and so far, so good. I've gotten to the greed koan, which at the time of this writing is 183. I've got a working solution, but I feel like I've cobbled together just a bunch of if/then logic and that I'm not embracing Ruby patterns.

我通过Ruby Koans来学习Ruby,到目前为止,我学得很好。我讲到贪婪的koan,在我写这篇文章的时候是183。我有一个可行的解决方案,但我觉得我只是拼凑了一堆if/then逻辑,我不喜欢Ruby模式。

In the following code, are there ways you would point me to more fully embracing Ruby patterns? (My code is wrapped in "MY CODE [BEGINS|ENDS] HERE" comments.

在下面的代码中,有什么方法可以让我更全面地使用Ruby模式吗?(我的代码被包装在“我的代码[开始|结束]在这里”注释中。

# Greed is a dice game where you roll up to five dice to accumulate
# points.  The following "score" function will be used 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.

# MY CODE BEGINS HERE

def score(dice)

  # set up basic vars to handle total points and count of each number
  total = 0
  count = [0, 0, 0, 0, 0, 0]

  # for each die, make sure we've counted how many occurrencess there are
  dice.each do |die|
    count[ die - 1 ] += 1
  end

  # iterate over each, and handle points for singles and triples
  count.each_with_index do |count, index|
    if count == 3
      total = doTriples( index + 1, total )
    elsif count < 3
      total = doSingles( index + 1, count, total )
    elsif count > 3
      total = doTriples( index + 1, total )
      total = doSingles( index + 1, count % 3, total )
    end
  end

  # return the new point total
  total

end

def doTriples( number, total )
  if number == 1
    total += 1000
  else
    total += ( number ) * 100
  end
  total
end

def doSingles( number, count, total )
  if number == 1
    total += ( 100 * count )
  elsif number == 5
    total += ( 50 * count )
  end
  total
end

# MY CODE ENDS HERE

class AboutScoringProject < EdgeCase::Koan
  def test_score_of_an_empty_list_is_zero
    assert_equal 0, score([])
  end

  def test_score_of_a_single_roll_of_5_is_50
    assert_equal 50, score([5])
  end

  def test_score_of_a_single_roll_of_1_is_100
    assert_equal 100, score([1])
  end

  def test_score_of_multiple_1s_and_5s_is_the_sum_of_individual_scores
    assert_equal 300, score([1,5,5,1])
  end

  def test_score_of_single_2s_3s_4s_and_6s_are_zero
    assert_equal 0, score([2,3,4,6])
  end

  def test_score_of_a_triple_1_is_1000
    assert_equal 1000, score([1,1,1])
  end

  def test_score_of_other_triples_is_100x
    assert_equal 200, score([2,2,2])
    assert_equal 300, score([3,3,3])
    assert_equal 400, score([4,4,4])
    assert_equal 500, score([5,5,5])
    assert_equal 600, score([6,6,6])
  end

  def test_score_of_mixed_is_sum
    assert_equal 250, score([2,5,2,2,3])
    assert_equal 550, score([5,5,5,5])
  end

end

Thanks so much to any help you can give as I try to get my head around Ruby.

非常感谢你的帮助,因为我试着去了解Ruby。

37 个解决方案

#1


4  

Looks OK. I might have written some things slightly differently, say:

看起来好。我可能写了一些稍微不同的东西,比如:

def do_triples number, total
  total + (number == 1 ? 1000 : number * 100)
end

If you want to do something that few languages other than Ruby can do, I suppose the following might be justifiable under DIE and DRY, on alternate Tuesdays, but I don't think those Ruby maxims were really intended to apply to common subexpression elimination. Anyway:

如果您想要做一些除了Ruby之外的其他语言的事情,我想下面的例子可能是在DIE和DRY下进行的,但是我不认为这些Ruby maxims真的是用于普通的子表达式消除。无论如何:

def do_triples number, total
  total +
  if number == 1
    1000
  else
    number * 100
  end
end

def do_triples number, total
  if number == 1
    1000
  else
    number * 100
  end + total
end

#2


41  

Wow! There are a lot of really cool approaches being done here. I like everybody's creativity. However, I have a pedagogical problem with all of the answers presented here. ("Pedagogy is the study of … the process of teaching." -- Wikipedia)

哇!这里有很多很酷的方法。我喜欢每个人的创造力。然而,我有一个教育学问题,所有的答案都在这里。(“教育学是对……教学过程的研究。”——*)

It is obvious from the first several koans (back in about_asserts.rb) that the Path to Enlightenment does not require any prior/outside knowledge of Ruby. It also seems fairly clear that the Path doesn't even require prior programming experience. So from an educational standpoint, this koan must be answerable using only the methods, language constructs, and programming techniques taught in earlier koans. That means:

从最初的几个koans(回到about_asserts.rb)可以明显看出,通往启蒙的道路并不需要任何Ruby的先验知识。这条路径甚至不需要之前的编程经验,这一点似乎相当清楚。因此,从教育的角度来看,这个koan必须只使用早期koan教过的方法、语言结构和编程技术来回答。这意味着:

  • no Enumerable#each_with_index
  • 没有可列举的# each_with_index
  • no Enumerable#count
  • 没有可列举的#计数
  • no Enumerable#sort
  • 没有可列举的#
  • no Hash.new(0) specifying a default value
  • 没有指定默认值的Hash.new(0)
  • no Numeric#abs
  • 没有数字# abs
  • no Numeric#divmod
  • 没有数字# divmod
  • no recursion
  • 没有递归
  • no case when
  • 任何情况下,当
  • etc

Now, I'm not saying that you are not allowed to use these things in your implementation, but the koan mustn't require using them. There must be a solution that only uses constructs introduced by prior koans.

现在,我并不是说在实现中不允许使用这些东西,但是koan不需要使用它们。必须有一个只使用以前koans引入的结构的解决方案。

Also, since the template was just

而且,因为模板是公正的

def score(dice)
  # You need to write this method
end

it seemed implied that the solution should not define other methods or classes. That is, you should only replace the # You need to write this method line.

似乎暗示了解决方案不应该定义其他方法或类。也就是说,您应该只替换编写此方法行的#。

Here is a solution that fits my philosophical requirements:

这里有一个符合我哲学要求的解决方案:

def score (dice)
    sum = 0
    (1..6).each do |i|
        idice = dice.select { |d| d == i }
        count = idice.size

        if count >= 3
            sum += (i==1 ? 1000 : i*100)
        end
        sum += (count % 3) * 100   if i == 1
        sum += (count % 3) *  50   if i == 5
    end
    sum
end

The methods/constructs here are introduced in the following koan files:

这里的方法/构造在以下koan文件中介绍:

Enumerable#each    about_iteration.rb
Enumerable#select  about_iteration.rb
Array#size         about_arrays.rb
a ? b : c          about_control_statements.rb
%                  about_control_statements.rb

Related * Questions:

*相关问题:

#3


22  

A student asked Joshu, "How can I write an algorithm to calculate the scores for a dice game?"

一个学生问Joshu:“我怎样才能写出计算骰子游戏分数的算法呢?”

Joshu struck the student with his stick and said: "Use a calculator."

约书亚用棍子打了那个学生,说:“用计算器。”

def score(dice)
  score = [0, 100, 200, 1000, 1100, 1200][dice.count(1)]
  score += [0, 50, 100, 500, 550, 600][dice.count(5)]
  [2,3,4,6].each do |num|
      if dice.count(num) >= 3 then score += num * 100 end
  end
  score
end

#4


6  

I went through and passed each of the tests one at a time. Not sure this is a very "ruby" solution, but I do like that it's obvious what each section is doing and that there are no excess declarations of values

我一遍又一遍地通过了每一次考试。我不确定这是一个非常“ruby”的解决方案,但我很喜欢每个部分都在做什么,并且没有多余的值声明

def score(dice)
  ## score is set to 0 to start off so if no dice, no score
  score = 0
  ## setting the 1000 1,1,1 rule
  score += 1000 if (dice.count(1) / 3) == 1
  ## taking care of the single 5s and 1s here
  score += (dice.count(5) % 3) * 50
  score += (dice.count(1) % 3) * 100
  ## set the other triples here
  [2, 3, 4, 5, 6].each do |num|
    score += num * 100 if (dice.count(num) / 3 ) == 1
  end
  score
end

#5


4  

Here's what I did. Looks pretty similar to a few of the older replies. I would love to find some ingenious usage of inject for this one (mikeonbike's one is niiiice).

这是我所做的。看起来很像一些老的回复。我很乐意为这一款找到一些巧妙的用法(mikeonbike的那款是niiiice)。

def score(dice)
  total = 0

  # handle triples scores for all but '1'
  (2..6).each do |num|
    total += dice.count(num) / 3 * num * 100
  end

  # non-triple score for '5'
  total += dice.count(5) % 3 * 50

  # all scores for '1'
  total += dice.count(1) / 3 * 1000 + dice.count(1) % 3 * 100

  total
end

#6


3  

You can condense this down to fewer lines but the readability of the algorithm gets lost so I ended up with this:

你可以把它压缩成更少的行但是算法的可读性会丢失所以我最后得到了这个

def score(dice)
  result = 0;

  (1..6).each do |die|
    multiplier = die == 1 ? 1000 : 100
    number_of_triples = dice.count(die) / 3
    result += die * multiplier * number_of_triples
  end

  result += 100 * (dice.count(1) % 3)

  result += 50 * (dice.count(5) % 3)
end

And if you're using 1.8.6, you'll have to use backports or add the count method to Array yourself:

如果你用的是1。8.6,你必须使用backports或者添加count方法来自己排列:

class Array
  def count(item)
    self.select { |x| x == item }.size
  end
end

#7


3  

Here is the answer I went with after about four iterations and trying to take advantage of the Ruby constructs I'm learning doing the koans:

下面是我在大约四次迭代之后得出的答案,并试图利用我正在学习的Ruby构造来做koans:

def score(dice)
  total = 0
  (1..6).each { |roll| total += apply_bonus(dice, roll)}
  return total
end

def apply_bonus(dice, roll, bonus_count = 3)
  bonus = 0
  bonus = ((roll == 1 ? 1000 : 100) * roll) if (dice.count(roll) >= bonus_count)
  bonus += 50 * (dice.count(5) % bonus_count) if (roll == 5)
  bonus += 100 * (dice.count(1) % bonus_count)  if (roll == 1)
  return bonus
end

#8


2  

Yet another answer :)

另一个回答:)

def score(dice)
  score = 0
  for num in 1..6
    occurrences = dice.count {|dice_num| dice_num == num}
    score += 1000 if num == 1 and occurrences >= 3
    score += 100 * (occurrences % 3) if num == 1
    score += 100 * num if num != 1 and occurrences >= 3
    score += 50 * (occurrences % 3) if num == 5
  end
  score
end

#9


2  

This is the simplest and most readable solution that I came up with. This also accounts for a few situations not in the tests, such as a roll of six 5's or six 1's.

这是我想到的最简单、最易读的解决方案。这也解释了一些测试中没有的情况,比如6个5或6个1。

def score(dice)
  score = 0
  (1..6).each { |d|
    count = dice.find_all { |a| a == d }
    score = ( d == 1 ? 1000 : 100 ) * d if count.size >= 3
    score += (count.size - 3) * 50 if (count.size >= 4) && d == 5
    score += (count.size - 3) * 100 if (count.size >= 4) && d == 1  
    score += count.size * 50 if (count.size < 3) && d == 5
    score += count.size * 100 if (count.size < 3) && d == 1
  }
  score
end

I opted to use the size method instead of the count method as count isn't supported by all versions of Ruby and the koans had not tested count up to this test.

我选择使用size方法而不是count方法,因为count不受所有Ruby版本的支持,koan还没有对count进行测试。

#10


2  

def score(dice)
  total = 0
  sets = dice.group_by{|num| num }

  sets.each_pair do |num, values|
    number_of_sets, number_of_singles = values.length.divmod(3)
    number_of_sets.times { total += score_set(num) }
    number_of_singles.times { total += score_single(num) }
  end

  total
end

def score_set(num)
  return 1000 if num == 1
  num * 100
end

def score_single(num)
  return 100 if num == 1
  return 50 if num == 5
  0
end

#11


2  

This was my eventual solution after initially having a similar if/then/else mess on my first attempt.

这是我最初的解决方案,在我的第一次尝试中有了类似的if/then/else。

def score(dice)
  score = 0
  dice.uniq.each do |roll| 
    score += dice.count(roll) / 3 * (roll == 1 ? 1000 : 100*roll)
    score += dice.count(roll) % 3 * (roll == 1 ? 100 : (roll == 5 ? 50 : 0))
  end
  score
end

#12


1  

I'd say you have it looking very Ruby-like already. The only thing that doesn't look very Rubyish to me would be the use of camelCase method names instead of snake_case, but of course that's a personal convention and I haven't read the koans myself.

我得说你已经把它看得像红宝石了。在我看来,唯一不太粗鲁的是使用camelCase方法名而不是snake_case,当然这是我个人的习惯,我自己也没读过koans。

Other than that, your example wouldn't be improved much by using case/when or any other solution for that matter. Aim for anything less than 3 elseif operations, anything more than that and you'd probably want to hunt for a better solution.

除此之外,您的示例不会因为使用case/when或任何其他解决方案而得到很大的改进。目标是任何少于3个的操作,任何超过3个的操作,你可能想要寻找更好的解决方案。

#13


1  

You could shorten [0, 0, 0, 0, 0, 0] to [0] * 6 but aside from the camelCase @injekt mentioned it looks fine to me. I'd be quite happy to see this in a code review.

你可以把[0,0,0,0,0,0,0,0,0,0,0]缩短为[0]* 6,但除了camelCase @injekt提到的,我觉得它看起来还不错。我很高兴在代码评审中看到这一点。

Also I suppose your doTriples and doSingles don't really need their temporary variables.

而且我认为你的二元组和二元组并不需要它们的临时变量。

def doTriples( number, total )
  if number == 1
    total + 1000
  else
    total + ( number ) * 100 # be careful with precedence here
  end
end

#14


1  

You may want to change

你可能想要改变

  # for each die, make sure we've counted how many occurrencess there are
  dice.each do |die|
    count[ die - 1 ] += 1
  end

into a hash, such as

变成散列,例如。

count = Hash.new(0)
dice.each do |die|
  count[die] += 1
end

or even

甚至

count = {} # Or Hash.new(0)
grouped_by_dots = dice.group_by {|die| die}
1.upto(6) do |dots| # Or grouped_by_dots.each do |dots, dice_with_those_dots|
  dice_with_those_dots = grouped_by_dots.fetch(dots) {[]}
  count_of_that_dots = dice_with_those_dots.length
  count[dots] = count_of_that_dots
end

That way you don't have to have index + 1 littered throughout your code.

这样,您就不必在代码中到处乱放索引+ 1。

It'd be nice if Ruby had a count_by method built in.

如果Ruby内置了一个count_by方法,那就太好了。

#15


1  

My 2 cents. Having new methods for singles/doubles seems like a roundabout way of doing something very simple.

我的2美分。单打/双打的新方法似乎是做一些非常简单的事情的迂回方式。

def score(dice)

  #fill initial throws
  thrown = Hash.new(0)
  dice.each do |die|
    thrown[die]+=1
  end

  #calculate score
  score = 0
  faces.each do |face,amount|
    if amount >= 3
      amount -= 3
      score += (face == 1 ? 1000 : face * 100)
    end
    score += (100 * amount) if (face == 1)
    score += (50 * amount) if (face == 5)
  end

  score
end

#16


1  

Well,

好吧,

Here's my solution:

这是我的解决方案:

def score(dice)
    total = 0

    #Iterate through 1-6, and add triples to total if found 
    (1..6).each { |roll| total += (roll == 1 ? 1000 : 100 * roll) if dice.count(roll) > 2 }

    #Handle Excess 1's and 5's
    total += (dice.count(1) % 3) * 100 
    total += (dice.count(5) % 3) * 50
end

Once I found the "count" method for an array, this exercise was pretty easy.

一旦我找到了数组的“count”方法,这个练习就非常简单了。

#17


1  

Here is my answer. I do not know if it is good or not, but at least, it looks clear :)

这是我的回答。我不知道它是好还是坏,但至少看起来很清楚:

RULEHASH = { 
    1 => [1000, 100],
    2 => [100,0],
    3 => [100,0],
    4 => [100,0],
    5 => [100,50],
    6 => [100,0] 
}

def score(dice)
    score = 0
    RULEHASH.each_pair do |i, rule|
        mod = dice.count(i).divmod(3)
        score += mod[0] * rule[0] * i + mod[1] * rule[1]
    end
    score
end

#18


1  

My solution is not ruby-like style. Just for fun and shortest code. We can set rules through hash p.

我的解决方案不是像红宝石一样的风格。只是为了好玩和最短的代码。我们可以通过哈希p来设置规则。

def score(dice)
  p = Hash.new([100,0]).merge({1 => [1000,100], 5 => [100,50]})
  dice.uniq.inject(0) { |sum, n| sum + dice.count(n) / 3 * n * p[n][0] + dice.count(n) % 3 * p[n][1] }
end

#19


1  

My answer uses a "lookup table" approach...

我的回答使用了“查找表”方法……

def score(dice)
  tally = (1..6).inject(Array.new(7,0)){|a,i| a[i] = dice.count(i); a}
  rubric = {1 => [0,100,200,1000,1100,1200], 5 => [0,50,100,500,550,600]}
  score = rubric[1][tally[1]] + rubric[5][tally[5]]
  [2,3,4,6].each do |i| score += 100 * i if dice.count(i) >= 3 end
  score
end

#20


1  

Mine was similar to a couple of others posted here.

我的作品和这里的其他一些作品很相似。

score = 0
[1,2,3,4,5,6].each {|d| 
  rolls = dice.count(d)
  score = (d==1 ? 1000 : 100)*d if rolls >= 3
  score += 100*(rolls % 3) if d == 1 
  score += 50*(rolls % 3) if d == 5 
}
score

#21


1  

Me and my girlfriend were going through these rubykoans this weekend and I had quite a bit of fun golfing on this and trying many different solutions. Here is a reasonably short data-driven solution:

我和我的女朋友这周末都在玩rubykoans,我玩得很开心,还尝试了很多不同的解决方法。这里有一个相当短的数据驱动解决方案:

SCORES = [[1000, 100], [200, 0], [300, 0], [400, 0], [500, 50], [600, 0]]

def score(dice)
  counts = dice.group_by(&:to_i).map { |i, j| [i-1, j.length] }
  counts.inject(0) do |score, (i, count)|
    sets, singles = count.divmod 3

    score + sets * SCORES[i][0] + singles * SCORES[i][1]
  end
end

Here is my obligatory one-liner (and perhaps FP version):

以下是我必须使用的一行代码(可能还有FP版本):

SCORES = [[1000, 100], [200, 0], [300, 0], [400, 0], [500, 50], [600, 0]]

def score(dice)
  dice.group_by(&:to_i).inject(0) {|s,(i,j)| s + j.size / 3 * SCORES[i-1][0] + j.size % 3 * SCORES[i-1][1]}
end

I also went some weird routes as well:

我也走了一些奇怪的路线:

SCORES = [[1000, 100], [200, 0], [300, 0], [400, 0], [500, 50], [600, 0]]
def score(dice)
  dice.group_by(&:to_i).inject(0) do |s, (i,j)| 
    s + j.size.divmod(3).zip(SCORES[i-1]).map {|a,b| a*b }.reduce(:+)
  end
end

All programmers should be screwing around with little problems like this...It is like performing morning stretches :)

所有的程序员都应该处理像这样的小问题……这就像在晨练:

#22


1  

def score(dice)
    result = 0
    result += 1000 * (dice.find_all{|e| e == 1}).length.divmod(3)[0]
    result += 100 * (dice.find_all{|e| e == 1}).length.divmod(3)[1]
    result += 50 * (dice.find_all{|e| e == 5}).length.divmod(3)[1]
    (2..6).each {|value| result += value*100 * (dice.find_all{|e| e == value}).length.divmod(3)[0]}
    return result
end

#23


0  

Some nice answers up here, time for one more?

这里有一些不错的答案,还有时间吗?

I took the approach of using lookups to minimize conditional statements - so only one if. [And I think I've only used what's been introduced already in the koans.]

我采用了使用查找最小化条件语句的方法——所以只有一个if。(我想我只使用了《*》中已经介绍过的东西。)

def score(dice)

count = [0]*7
score = [0, 100, 0, 0, 0, 50, 0]
bonus = [0, 700, 200, 300, 400, 350, 600]

total = 0

dice.each do |roll|

    total += score[roll]

    count[roll] += 1    
    total += bonus[roll] if count[roll]==3

end 

total

end

(I know I could make the lookup arrays six elements but I think the better readability is worth a few bytes.)

(我知道我可以将查找数组设置为六个元素,但我认为更好的可读性值几个字节。)

#24


-1  

And what about this solution? Thanks for the feedback!

那么这个解呢?谢谢你的反馈!

def score(dice)
  count = Hash.new(0)
  dice.each do |die|
    count[die] += 1
  end
  total = 0
  count.each_pair { |die, set| total += set < 3 ? single_value(die,set) : triple_value(die,set)}
  total
end

def single_value(die,set)
  value = 0
  value += (set * 100) if die == 1
  value += (set * 50) if die == 5
  value
end

def triple_value(die,set)
  value = 0
  diff = set - 3
  value += single_value(die,diff)
  value += die == 1 ? 1000 : die * 100
  value
end

#25


-1  

I used a slightly different method to others here, and (naturally) it's one that I see as preferable. It's very DRY and uses ruby methods fairly extensively to avoid manual loops and branches as much as possible. Should be relatively obvious, but essentially what is happening is we loop through each unique dice roll, and use iterative erosion of the number of occurences of that roll to add the appropriate points to an aggregate total score.

我在这里使用了一种与其他方法稍有不同的方法,而且(自然)我认为这是一种更好的方法。它非常枯燥,并且广泛地使用ruby方法来避免手工循环和分支。应该是相对比较明显的,但实际上发生的是我们循环遍历每个独特的骰子滚动,并使用迭代的次数来减少这个滚动的次数,以将适当的点添加到总得分。

def score(dice)
  score = 0 # An initial score of 0.

  throw_scores = { 1 => 10, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6 }
    # A hash to store the scores for each dice throw

  dice.uniq.each { |throw| # for each unique dice value present in the "hand"

    throw_count = (dice.select { |item| item == throw }).count
      # use select to store the number of times this throw occurs

    while throw_count > 0 
      # iteratively erode the throw count, accumulating 
      # points as appropriate along the way.

      if throw_count >= 3
        score += throw_scores[throw] * 100
        throw_count -= 3
      elsif throw == 1 || throw == 5
        score += throw_scores[throw] * 10
        throw_count -= 1
      else
        throw_count -= 1
      end
    end
  }
  return score
end

#26


-1  

And another one, just for the fun of it:

还有一个,只是为了好玩:

def score(dice)
  result = 0
  dice.uniq.each { |k|
    result += ((dice.count(k) / 3) * 1000 + (dice.count(k) % 3) * 100) if k == 1
    result += ((dice.count(k) / 3) * 100 * k + (dice.count(k) % 3) * ( k == 5 ? 50 : 0 )) if k != 1
  }
  result
end

#27


-1  

Here's my opinion. All other solutions here try to be clever. There's a place for learning clever tricks, but it's even more important to learn to write clear and maintainable code. The main problem I see with all of these solutions is that it's very difficult to discern the scoring rules from the code. Can you read your solution and make sure that it's correct in your head? Then imagine someone asks you to add a new scoring rule, or remove one. Can you quickly point to the place where the rule must be added or removed?

这是我的意见。这里所有其他的解决方案都很聪明。这里有学习聪明技巧的地方,但更重要的是要学会编写清晰和可维护的代码。我在所有这些解决方案中看到的主要问题是很难从代码中区分评分规则。你能读懂你的答案并确保它在你的脑海中是正确的吗?然后想象有人让你添加一个新的评分规则,或者删除一个。您能快速地指向必须添加或删除规则的地方吗?

Here's my solution. I'm sure it can be improved, but look at the shape of the "score" function. This is the sort of code that I would not mind to maintain.

这是我的解决方案。我确信它可以改进,但是看看“分数”函数的形状。我不介意维护这种代码。

class Array
  def occurrences_of(match)
    self.select{ |number| match == number }.size
  end

  def delete_one(match)
    for i in (0..size)
      if match == self[i]
        self.delete_at(i)
        return
      end
    end
  end
end

def single_die_rule(match, score, dice)
  dice.occurrences_of(match) * score
end

def triple_rule(match, score, dice)
  return 0 if dice.occurrences_of(match) < 3
  3.times { dice.delete_one match }
  score
end

def score(dice)
  triple_rule(1, 1000, dice) +
  triple_rule(2, 200, dice) +
  triple_rule(3, 300, dice) +
  triple_rule(4, 400, dice) +
  triple_rule(5, 500, dice) +
  triple_rule(6, 600, dice) +
  single_die_rule(1, 100, dice) +
  single_die_rule(5, 50, dice)
end

#28


-1  

I'm gonna have to go with:

我得说

def score(dice)
    # some checks
    raise ArgumentError, "input not array" unless dice.is_a?(Array)
    raise ArgumentError, "invalid array size" unless dice.size <= 5
    raise ArgumentError, "invalid dice result" if dice.any? { |x| x<1 || x>6 }

    # setup (output var, throws as hash)
    out = 0
    freqs = dice.inject(Hash.new(0)) { |m,x| m[x] += 1; m }

    # 3-sets
    1.upto(6) { |i| out += freqs[i]/3 * (i == 1 ? 10 : i) * 100 }

    # one not part of 3-set
    out += (freqs[1] % 3) * 100

    # five not part of 3-set
    out += (freqs[5] % 3) * 50

    out
end

Because most of the solutions presented so far lack basic checks. And some of them are fairly unreadable (in my book) and not very idiomatic.

因为到目前为止提出的大多数解决方案缺乏基本的检查。其中一些相当难读(在我的书中),而且不是很地道。

Granted, the 3-set condition could be made more readable by splitting into two clauses:

当然,通过将3个条件分解为两个子句,可以使其更具可读性:

    # 3-sets of ones
    out += freqs[1]/3 * 1_000
    # 3-sets of others
    2.upto(6) { |i| out += freqs[i]/3 * i * 100 }

but that's IMO mostly about personal preference.

但这主要是出于个人喜好。

#29


-1  

Coming from Perl, my instinct is to use a hash:

来自Perl,我的直觉是使用散列:

def score(dice)
  # You need to write this method
  score = 0
  count = Hash.new(0)

  for die in dice
    count[die] += 1

    is_triple = (count[die] % 3 == 0)
    if die == 1 then
      score += is_triple ? 800 : 100
    elsif die == 5 then
      score += is_triple ? 400 : 50
    elsif is_triple
      score += 100 * die
    end
  end

  return score
end

This has the advantage that it makes a single pass over dice. I could probably have used an Array in place of the Hash.

这有一个优势,它使一个单一的通过骰子。我可以用一个数组来代替哈希。

#30


-1  

I grouped the dice by face, then looped over these groups, first scoring the threes, then individual dice. This is how I would score the game were I playing IRL

我将骰子按脸分组,然后在这些组上进行循环,首先得分3,然后是单独的骰子。这就是我打比赛的方式。

def score(dice)
    points = 0
    dice.group_by {|face| face}.each do |face,group|
        while group.size >= 3
            if face == 1
                # A set of three ones is 1000 points
                points += 1000
            else
                # A set of three numbers (other than ones) is worth 100 times the number.
                points += 100 * face
            end
            group.pop(3)
        end
        group.each do |x|
             # A one (that is not part of a set of three) is worth 100 points.
            points += 100 if x==1
            # A five (that is not part of a set of three) is worth 50 points.
            points += 50 if x==5 
        end
    end
    return points
end

That's how I roll

这就是我

#1


4  

Looks OK. I might have written some things slightly differently, say:

看起来好。我可能写了一些稍微不同的东西,比如:

def do_triples number, total
  total + (number == 1 ? 1000 : number * 100)
end

If you want to do something that few languages other than Ruby can do, I suppose the following might be justifiable under DIE and DRY, on alternate Tuesdays, but I don't think those Ruby maxims were really intended to apply to common subexpression elimination. Anyway:

如果您想要做一些除了Ruby之外的其他语言的事情,我想下面的例子可能是在DIE和DRY下进行的,但是我不认为这些Ruby maxims真的是用于普通的子表达式消除。无论如何:

def do_triples number, total
  total +
  if number == 1
    1000
  else
    number * 100
  end
end

def do_triples number, total
  if number == 1
    1000
  else
    number * 100
  end + total
end

#2


41  

Wow! There are a lot of really cool approaches being done here. I like everybody's creativity. However, I have a pedagogical problem with all of the answers presented here. ("Pedagogy is the study of … the process of teaching." -- Wikipedia)

哇!这里有很多很酷的方法。我喜欢每个人的创造力。然而,我有一个教育学问题,所有的答案都在这里。(“教育学是对……教学过程的研究。”——*)

It is obvious from the first several koans (back in about_asserts.rb) that the Path to Enlightenment does not require any prior/outside knowledge of Ruby. It also seems fairly clear that the Path doesn't even require prior programming experience. So from an educational standpoint, this koan must be answerable using only the methods, language constructs, and programming techniques taught in earlier koans. That means:

从最初的几个koans(回到about_asserts.rb)可以明显看出,通往启蒙的道路并不需要任何Ruby的先验知识。这条路径甚至不需要之前的编程经验,这一点似乎相当清楚。因此,从教育的角度来看,这个koan必须只使用早期koan教过的方法、语言结构和编程技术来回答。这意味着:

  • no Enumerable#each_with_index
  • 没有可列举的# each_with_index
  • no Enumerable#count
  • 没有可列举的#计数
  • no Enumerable#sort
  • 没有可列举的#
  • no Hash.new(0) specifying a default value
  • 没有指定默认值的Hash.new(0)
  • no Numeric#abs
  • 没有数字# abs
  • no Numeric#divmod
  • 没有数字# divmod
  • no recursion
  • 没有递归
  • no case when
  • 任何情况下,当
  • etc

Now, I'm not saying that you are not allowed to use these things in your implementation, but the koan mustn't require using them. There must be a solution that only uses constructs introduced by prior koans.

现在,我并不是说在实现中不允许使用这些东西,但是koan不需要使用它们。必须有一个只使用以前koans引入的结构的解决方案。

Also, since the template was just

而且,因为模板是公正的

def score(dice)
  # You need to write this method
end

it seemed implied that the solution should not define other methods or classes. That is, you should only replace the # You need to write this method line.

似乎暗示了解决方案不应该定义其他方法或类。也就是说,您应该只替换编写此方法行的#。

Here is a solution that fits my philosophical requirements:

这里有一个符合我哲学要求的解决方案:

def score (dice)
    sum = 0
    (1..6).each do |i|
        idice = dice.select { |d| d == i }
        count = idice.size

        if count >= 3
            sum += (i==1 ? 1000 : i*100)
        end
        sum += (count % 3) * 100   if i == 1
        sum += (count % 3) *  50   if i == 5
    end
    sum
end

The methods/constructs here are introduced in the following koan files:

这里的方法/构造在以下koan文件中介绍:

Enumerable#each    about_iteration.rb
Enumerable#select  about_iteration.rb
Array#size         about_arrays.rb
a ? b : c          about_control_statements.rb
%                  about_control_statements.rb

Related * Questions:

*相关问题:

#3


22  

A student asked Joshu, "How can I write an algorithm to calculate the scores for a dice game?"

一个学生问Joshu:“我怎样才能写出计算骰子游戏分数的算法呢?”

Joshu struck the student with his stick and said: "Use a calculator."

约书亚用棍子打了那个学生,说:“用计算器。”

def score(dice)
  score = [0, 100, 200, 1000, 1100, 1200][dice.count(1)]
  score += [0, 50, 100, 500, 550, 600][dice.count(5)]
  [2,3,4,6].each do |num|
      if dice.count(num) >= 3 then score += num * 100 end
  end
  score
end

#4


6  

I went through and passed each of the tests one at a time. Not sure this is a very "ruby" solution, but I do like that it's obvious what each section is doing and that there are no excess declarations of values

我一遍又一遍地通过了每一次考试。我不确定这是一个非常“ruby”的解决方案,但我很喜欢每个部分都在做什么,并且没有多余的值声明

def score(dice)
  ## score is set to 0 to start off so if no dice, no score
  score = 0
  ## setting the 1000 1,1,1 rule
  score += 1000 if (dice.count(1) / 3) == 1
  ## taking care of the single 5s and 1s here
  score += (dice.count(5) % 3) * 50
  score += (dice.count(1) % 3) * 100
  ## set the other triples here
  [2, 3, 4, 5, 6].each do |num|
    score += num * 100 if (dice.count(num) / 3 ) == 1
  end
  score
end

#5


4  

Here's what I did. Looks pretty similar to a few of the older replies. I would love to find some ingenious usage of inject for this one (mikeonbike's one is niiiice).

这是我所做的。看起来很像一些老的回复。我很乐意为这一款找到一些巧妙的用法(mikeonbike的那款是niiiice)。

def score(dice)
  total = 0

  # handle triples scores for all but '1'
  (2..6).each do |num|
    total += dice.count(num) / 3 * num * 100
  end

  # non-triple score for '5'
  total += dice.count(5) % 3 * 50

  # all scores for '1'
  total += dice.count(1) / 3 * 1000 + dice.count(1) % 3 * 100

  total
end

#6


3  

You can condense this down to fewer lines but the readability of the algorithm gets lost so I ended up with this:

你可以把它压缩成更少的行但是算法的可读性会丢失所以我最后得到了这个

def score(dice)
  result = 0;

  (1..6).each do |die|
    multiplier = die == 1 ? 1000 : 100
    number_of_triples = dice.count(die) / 3
    result += die * multiplier * number_of_triples
  end

  result += 100 * (dice.count(1) % 3)

  result += 50 * (dice.count(5) % 3)
end

And if you're using 1.8.6, you'll have to use backports or add the count method to Array yourself:

如果你用的是1。8.6,你必须使用backports或者添加count方法来自己排列:

class Array
  def count(item)
    self.select { |x| x == item }.size
  end
end

#7


3  

Here is the answer I went with after about four iterations and trying to take advantage of the Ruby constructs I'm learning doing the koans:

下面是我在大约四次迭代之后得出的答案,并试图利用我正在学习的Ruby构造来做koans:

def score(dice)
  total = 0
  (1..6).each { |roll| total += apply_bonus(dice, roll)}
  return total
end

def apply_bonus(dice, roll, bonus_count = 3)
  bonus = 0
  bonus = ((roll == 1 ? 1000 : 100) * roll) if (dice.count(roll) >= bonus_count)
  bonus += 50 * (dice.count(5) % bonus_count) if (roll == 5)
  bonus += 100 * (dice.count(1) % bonus_count)  if (roll == 1)
  return bonus
end

#8


2  

Yet another answer :)

另一个回答:)

def score(dice)
  score = 0
  for num in 1..6
    occurrences = dice.count {|dice_num| dice_num == num}
    score += 1000 if num == 1 and occurrences >= 3
    score += 100 * (occurrences % 3) if num == 1
    score += 100 * num if num != 1 and occurrences >= 3
    score += 50 * (occurrences % 3) if num == 5
  end
  score
end

#9


2  

This is the simplest and most readable solution that I came up with. This also accounts for a few situations not in the tests, such as a roll of six 5's or six 1's.

这是我想到的最简单、最易读的解决方案。这也解释了一些测试中没有的情况,比如6个5或6个1。

def score(dice)
  score = 0
  (1..6).each { |d|
    count = dice.find_all { |a| a == d }
    score = ( d == 1 ? 1000 : 100 ) * d if count.size >= 3
    score += (count.size - 3) * 50 if (count.size >= 4) && d == 5
    score += (count.size - 3) * 100 if (count.size >= 4) && d == 1  
    score += count.size * 50 if (count.size < 3) && d == 5
    score += count.size * 100 if (count.size < 3) && d == 1
  }
  score
end

I opted to use the size method instead of the count method as count isn't supported by all versions of Ruby and the koans had not tested count up to this test.

我选择使用size方法而不是count方法,因为count不受所有Ruby版本的支持,koan还没有对count进行测试。

#10


2  

def score(dice)
  total = 0
  sets = dice.group_by{|num| num }

  sets.each_pair do |num, values|
    number_of_sets, number_of_singles = values.length.divmod(3)
    number_of_sets.times { total += score_set(num) }
    number_of_singles.times { total += score_single(num) }
  end

  total
end

def score_set(num)
  return 1000 if num == 1
  num * 100
end

def score_single(num)
  return 100 if num == 1
  return 50 if num == 5
  0
end

#11


2  

This was my eventual solution after initially having a similar if/then/else mess on my first attempt.

这是我最初的解决方案,在我的第一次尝试中有了类似的if/then/else。

def score(dice)
  score = 0
  dice.uniq.each do |roll| 
    score += dice.count(roll) / 3 * (roll == 1 ? 1000 : 100*roll)
    score += dice.count(roll) % 3 * (roll == 1 ? 100 : (roll == 5 ? 50 : 0))
  end
  score
end

#12


1  

I'd say you have it looking very Ruby-like already. The only thing that doesn't look very Rubyish to me would be the use of camelCase method names instead of snake_case, but of course that's a personal convention and I haven't read the koans myself.

我得说你已经把它看得像红宝石了。在我看来,唯一不太粗鲁的是使用camelCase方法名而不是snake_case,当然这是我个人的习惯,我自己也没读过koans。

Other than that, your example wouldn't be improved much by using case/when or any other solution for that matter. Aim for anything less than 3 elseif operations, anything more than that and you'd probably want to hunt for a better solution.

除此之外,您的示例不会因为使用case/when或任何其他解决方案而得到很大的改进。目标是任何少于3个的操作,任何超过3个的操作,你可能想要寻找更好的解决方案。

#13


1  

You could shorten [0, 0, 0, 0, 0, 0] to [0] * 6 but aside from the camelCase @injekt mentioned it looks fine to me. I'd be quite happy to see this in a code review.

你可以把[0,0,0,0,0,0,0,0,0,0,0]缩短为[0]* 6,但除了camelCase @injekt提到的,我觉得它看起来还不错。我很高兴在代码评审中看到这一点。

Also I suppose your doTriples and doSingles don't really need their temporary variables.

而且我认为你的二元组和二元组并不需要它们的临时变量。

def doTriples( number, total )
  if number == 1
    total + 1000
  else
    total + ( number ) * 100 # be careful with precedence here
  end
end

#14


1  

You may want to change

你可能想要改变

  # for each die, make sure we've counted how many occurrencess there are
  dice.each do |die|
    count[ die - 1 ] += 1
  end

into a hash, such as

变成散列,例如。

count = Hash.new(0)
dice.each do |die|
  count[die] += 1
end

or even

甚至

count = {} # Or Hash.new(0)
grouped_by_dots = dice.group_by {|die| die}
1.upto(6) do |dots| # Or grouped_by_dots.each do |dots, dice_with_those_dots|
  dice_with_those_dots = grouped_by_dots.fetch(dots) {[]}
  count_of_that_dots = dice_with_those_dots.length
  count[dots] = count_of_that_dots
end

That way you don't have to have index + 1 littered throughout your code.

这样,您就不必在代码中到处乱放索引+ 1。

It'd be nice if Ruby had a count_by method built in.

如果Ruby内置了一个count_by方法,那就太好了。

#15


1  

My 2 cents. Having new methods for singles/doubles seems like a roundabout way of doing something very simple.

我的2美分。单打/双打的新方法似乎是做一些非常简单的事情的迂回方式。

def score(dice)

  #fill initial throws
  thrown = Hash.new(0)
  dice.each do |die|
    thrown[die]+=1
  end

  #calculate score
  score = 0
  faces.each do |face,amount|
    if amount >= 3
      amount -= 3
      score += (face == 1 ? 1000 : face * 100)
    end
    score += (100 * amount) if (face == 1)
    score += (50 * amount) if (face == 5)
  end

  score
end

#16


1  

Well,

好吧,

Here's my solution:

这是我的解决方案:

def score(dice)
    total = 0

    #Iterate through 1-6, and add triples to total if found 
    (1..6).each { |roll| total += (roll == 1 ? 1000 : 100 * roll) if dice.count(roll) > 2 }

    #Handle Excess 1's and 5's
    total += (dice.count(1) % 3) * 100 
    total += (dice.count(5) % 3) * 50
end

Once I found the "count" method for an array, this exercise was pretty easy.

一旦我找到了数组的“count”方法,这个练习就非常简单了。

#17


1  

Here is my answer. I do not know if it is good or not, but at least, it looks clear :)

这是我的回答。我不知道它是好还是坏,但至少看起来很清楚:

RULEHASH = { 
    1 => [1000, 100],
    2 => [100,0],
    3 => [100,0],
    4 => [100,0],
    5 => [100,50],
    6 => [100,0] 
}

def score(dice)
    score = 0
    RULEHASH.each_pair do |i, rule|
        mod = dice.count(i).divmod(3)
        score += mod[0] * rule[0] * i + mod[1] * rule[1]
    end
    score
end

#18


1  

My solution is not ruby-like style. Just for fun and shortest code. We can set rules through hash p.

我的解决方案不是像红宝石一样的风格。只是为了好玩和最短的代码。我们可以通过哈希p来设置规则。

def score(dice)
  p = Hash.new([100,0]).merge({1 => [1000,100], 5 => [100,50]})
  dice.uniq.inject(0) { |sum, n| sum + dice.count(n) / 3 * n * p[n][0] + dice.count(n) % 3 * p[n][1] }
end

#19


1  

My answer uses a "lookup table" approach...

我的回答使用了“查找表”方法……

def score(dice)
  tally = (1..6).inject(Array.new(7,0)){|a,i| a[i] = dice.count(i); a}
  rubric = {1 => [0,100,200,1000,1100,1200], 5 => [0,50,100,500,550,600]}
  score = rubric[1][tally[1]] + rubric[5][tally[5]]
  [2,3,4,6].each do |i| score += 100 * i if dice.count(i) >= 3 end
  score
end

#20


1  

Mine was similar to a couple of others posted here.

我的作品和这里的其他一些作品很相似。

score = 0
[1,2,3,4,5,6].each {|d| 
  rolls = dice.count(d)
  score = (d==1 ? 1000 : 100)*d if rolls >= 3
  score += 100*(rolls % 3) if d == 1 
  score += 50*(rolls % 3) if d == 5 
}
score

#21


1  

Me and my girlfriend were going through these rubykoans this weekend and I had quite a bit of fun golfing on this and trying many different solutions. Here is a reasonably short data-driven solution:

我和我的女朋友这周末都在玩rubykoans,我玩得很开心,还尝试了很多不同的解决方法。这里有一个相当短的数据驱动解决方案:

SCORES = [[1000, 100], [200, 0], [300, 0], [400, 0], [500, 50], [600, 0]]

def score(dice)
  counts = dice.group_by(&:to_i).map { |i, j| [i-1, j.length] }
  counts.inject(0) do |score, (i, count)|
    sets, singles = count.divmod 3

    score + sets * SCORES[i][0] + singles * SCORES[i][1]
  end
end

Here is my obligatory one-liner (and perhaps FP version):

以下是我必须使用的一行代码(可能还有FP版本):

SCORES = [[1000, 100], [200, 0], [300, 0], [400, 0], [500, 50], [600, 0]]

def score(dice)
  dice.group_by(&:to_i).inject(0) {|s,(i,j)| s + j.size / 3 * SCORES[i-1][0] + j.size % 3 * SCORES[i-1][1]}
end

I also went some weird routes as well:

我也走了一些奇怪的路线:

SCORES = [[1000, 100], [200, 0], [300, 0], [400, 0], [500, 50], [600, 0]]
def score(dice)
  dice.group_by(&:to_i).inject(0) do |s, (i,j)| 
    s + j.size.divmod(3).zip(SCORES[i-1]).map {|a,b| a*b }.reduce(:+)
  end
end

All programmers should be screwing around with little problems like this...It is like performing morning stretches :)

所有的程序员都应该处理像这样的小问题……这就像在晨练:

#22


1  

def score(dice)
    result = 0
    result += 1000 * (dice.find_all{|e| e == 1}).length.divmod(3)[0]
    result += 100 * (dice.find_all{|e| e == 1}).length.divmod(3)[1]
    result += 50 * (dice.find_all{|e| e == 5}).length.divmod(3)[1]
    (2..6).each {|value| result += value*100 * (dice.find_all{|e| e == value}).length.divmod(3)[0]}
    return result
end

#23


0  

Some nice answers up here, time for one more?

这里有一些不错的答案,还有时间吗?

I took the approach of using lookups to minimize conditional statements - so only one if. [And I think I've only used what's been introduced already in the koans.]

我采用了使用查找最小化条件语句的方法——所以只有一个if。(我想我只使用了《*》中已经介绍过的东西。)

def score(dice)

count = [0]*7
score = [0, 100, 0, 0, 0, 50, 0]
bonus = [0, 700, 200, 300, 400, 350, 600]

total = 0

dice.each do |roll|

    total += score[roll]

    count[roll] += 1    
    total += bonus[roll] if count[roll]==3

end 

total

end

(I know I could make the lookup arrays six elements but I think the better readability is worth a few bytes.)

(我知道我可以将查找数组设置为六个元素,但我认为更好的可读性值几个字节。)

#24


-1  

And what about this solution? Thanks for the feedback!

那么这个解呢?谢谢你的反馈!

def score(dice)
  count = Hash.new(0)
  dice.each do |die|
    count[die] += 1
  end
  total = 0
  count.each_pair { |die, set| total += set < 3 ? single_value(die,set) : triple_value(die,set)}
  total
end

def single_value(die,set)
  value = 0
  value += (set * 100) if die == 1
  value += (set * 50) if die == 5
  value
end

def triple_value(die,set)
  value = 0
  diff = set - 3
  value += single_value(die,diff)
  value += die == 1 ? 1000 : die * 100
  value
end

#25


-1  

I used a slightly different method to others here, and (naturally) it's one that I see as preferable. It's very DRY and uses ruby methods fairly extensively to avoid manual loops and branches as much as possible. Should be relatively obvious, but essentially what is happening is we loop through each unique dice roll, and use iterative erosion of the number of occurences of that roll to add the appropriate points to an aggregate total score.

我在这里使用了一种与其他方法稍有不同的方法,而且(自然)我认为这是一种更好的方法。它非常枯燥,并且广泛地使用ruby方法来避免手工循环和分支。应该是相对比较明显的,但实际上发生的是我们循环遍历每个独特的骰子滚动,并使用迭代的次数来减少这个滚动的次数,以将适当的点添加到总得分。

def score(dice)
  score = 0 # An initial score of 0.

  throw_scores = { 1 => 10, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6 }
    # A hash to store the scores for each dice throw

  dice.uniq.each { |throw| # for each unique dice value present in the "hand"

    throw_count = (dice.select { |item| item == throw }).count
      # use select to store the number of times this throw occurs

    while throw_count > 0 
      # iteratively erode the throw count, accumulating 
      # points as appropriate along the way.

      if throw_count >= 3
        score += throw_scores[throw] * 100
        throw_count -= 3
      elsif throw == 1 || throw == 5
        score += throw_scores[throw] * 10
        throw_count -= 1
      else
        throw_count -= 1
      end
    end
  }
  return score
end

#26


-1  

And another one, just for the fun of it:

还有一个,只是为了好玩:

def score(dice)
  result = 0
  dice.uniq.each { |k|
    result += ((dice.count(k) / 3) * 1000 + (dice.count(k) % 3) * 100) if k == 1
    result += ((dice.count(k) / 3) * 100 * k + (dice.count(k) % 3) * ( k == 5 ? 50 : 0 )) if k != 1
  }
  result
end

#27


-1  

Here's my opinion. All other solutions here try to be clever. There's a place for learning clever tricks, but it's even more important to learn to write clear and maintainable code. The main problem I see with all of these solutions is that it's very difficult to discern the scoring rules from the code. Can you read your solution and make sure that it's correct in your head? Then imagine someone asks you to add a new scoring rule, or remove one. Can you quickly point to the place where the rule must be added or removed?

这是我的意见。这里所有其他的解决方案都很聪明。这里有学习聪明技巧的地方,但更重要的是要学会编写清晰和可维护的代码。我在所有这些解决方案中看到的主要问题是很难从代码中区分评分规则。你能读懂你的答案并确保它在你的脑海中是正确的吗?然后想象有人让你添加一个新的评分规则,或者删除一个。您能快速地指向必须添加或删除规则的地方吗?

Here's my solution. I'm sure it can be improved, but look at the shape of the "score" function. This is the sort of code that I would not mind to maintain.

这是我的解决方案。我确信它可以改进,但是看看“分数”函数的形状。我不介意维护这种代码。

class Array
  def occurrences_of(match)
    self.select{ |number| match == number }.size
  end

  def delete_one(match)
    for i in (0..size)
      if match == self[i]
        self.delete_at(i)
        return
      end
    end
  end
end

def single_die_rule(match, score, dice)
  dice.occurrences_of(match) * score
end

def triple_rule(match, score, dice)
  return 0 if dice.occurrences_of(match) < 3
  3.times { dice.delete_one match }
  score
end

def score(dice)
  triple_rule(1, 1000, dice) +
  triple_rule(2, 200, dice) +
  triple_rule(3, 300, dice) +
  triple_rule(4, 400, dice) +
  triple_rule(5, 500, dice) +
  triple_rule(6, 600, dice) +
  single_die_rule(1, 100, dice) +
  single_die_rule(5, 50, dice)
end

#28


-1  

I'm gonna have to go with:

我得说

def score(dice)
    # some checks
    raise ArgumentError, "input not array" unless dice.is_a?(Array)
    raise ArgumentError, "invalid array size" unless dice.size <= 5
    raise ArgumentError, "invalid dice result" if dice.any? { |x| x<1 || x>6 }

    # setup (output var, throws as hash)
    out = 0
    freqs = dice.inject(Hash.new(0)) { |m,x| m[x] += 1; m }

    # 3-sets
    1.upto(6) { |i| out += freqs[i]/3 * (i == 1 ? 10 : i) * 100 }

    # one not part of 3-set
    out += (freqs[1] % 3) * 100

    # five not part of 3-set
    out += (freqs[5] % 3) * 50

    out
end

Because most of the solutions presented so far lack basic checks. And some of them are fairly unreadable (in my book) and not very idiomatic.

因为到目前为止提出的大多数解决方案缺乏基本的检查。其中一些相当难读(在我的书中),而且不是很地道。

Granted, the 3-set condition could be made more readable by splitting into two clauses:

当然,通过将3个条件分解为两个子句,可以使其更具可读性:

    # 3-sets of ones
    out += freqs[1]/3 * 1_000
    # 3-sets of others
    2.upto(6) { |i| out += freqs[i]/3 * i * 100 }

but that's IMO mostly about personal preference.

但这主要是出于个人喜好。

#29


-1  

Coming from Perl, my instinct is to use a hash:

来自Perl,我的直觉是使用散列:

def score(dice)
  # You need to write this method
  score = 0
  count = Hash.new(0)

  for die in dice
    count[die] += 1

    is_triple = (count[die] % 3 == 0)
    if die == 1 then
      score += is_triple ? 800 : 100
    elsif die == 5 then
      score += is_triple ? 400 : 50
    elsif is_triple
      score += 100 * die
    end
  end

  return score
end

This has the advantage that it makes a single pass over dice. I could probably have used an Array in place of the Hash.

这有一个优势,它使一个单一的通过骰子。我可以用一个数组来代替哈希。

#30


-1  

I grouped the dice by face, then looped over these groups, first scoring the threes, then individual dice. This is how I would score the game were I playing IRL

我将骰子按脸分组,然后在这些组上进行循环,首先得分3,然后是单独的骰子。这就是我打比赛的方式。

def score(dice)
    points = 0
    dice.group_by {|face| face}.each do |face,group|
        while group.size >= 3
            if face == 1
                # A set of three ones is 1000 points
                points += 1000
            else
                # A set of three numbers (other than ones) is worth 100 times the number.
                points += 100 * face
            end
            group.pop(3)
        end
        group.each do |x|
             # A one (that is not part of a set of three) is worth 100 points.
            points += 100 if x==1
            # A five (that is not part of a set of three) is worth 50 points.
            points += 50 if x==5 
        end
    end
    return points
end

That's how I roll

这就是我