保龄球计算器

时间:2015-07-25 04:28:55

标签: ruby rspec

我正在Ruby中编写一个使用RSpec定义和测试的保龄球计分器。它目前正在运行,但只通过了8个输入测试中的5个。以下是我的实现代码:

    class ScoreKeeper
  def calculate(input)
    unless input.is_a? String
      raise argumentException, "Score Keeper will only except string types for score calculation."
    end

    # Thanksgiving Turkey Edge Case
    return 300 if input == "xxxxxxxxxxxx"

    # Calculate Score   
    throws = input.gsub(/-/, "0").split(//)

    score = 0

    throws.each_with_index do |ball, i|
        current_throw = i
        last_throw = throws[i - 1] || "0"
        lastlast_throw = throws[i - 2] || "0"
        next_throw = throws[i + 1] || "0"

        if current_throw == 0
            last_throw = 0
            lastlast_throw = 0
        end

        if current_throw == 1
            lastlast_throw = 0
        end

        working_value = 0

        # Add numbers directly (unless part of a spare frame)
        if ((1..9) === ball.to_i)
            working_value = ball.to_i
        end

        # Add strike as 10 points
        if ball == "x"
            working_value = 10
        end

        # Add spare as number of remaining pins from last throw
        if ball == "/"
            if last_throw == "/" || last_throw == "x"
                raise argumentException, "Invalid score string. A spare cannot immediately follow a strike or spare."
            end

            working_value = 10 - last_throw.to_i
        end

        # Strike / Spare Bonus
        if last_throw == "x" || last_throw == "/" || lastlast_throw == "x"
            score += working_value
        end

        # Add current throw value
        score += working_value      
    end

    if score > 300 || score < 0
        raise argumentExcpetion, "Invalid score string. Impossible score detected."
    end

    score   
  end
end

我无法分辨为什么我的代码在每个测试用例中都没有计算出正确的分数。

RSpec:

require "./score_keeper"

describe ScoreKeeper do
  describe "calculating score" do
    let(:score_keeper) { described_class.new }

    context "when rolls are valid" do
      {
        "xxxxxxxxxxxx"          => 300,
        "--------------------"  => 0,
        "9-9-9-9-9-9-9-9-9-9-"  => 90,
        "5/5/5/5/5/5/5/5/5/5/5" => 150,
        "14456/5/---17/6/--2/6" => 82,
        "9/3561368153258-7181"  => 86,
        "9-3/613/815/0/8-7/8-"  => 121,
        "x3/61xxx2/9-7/xxx"     => 193
      }.each do |bowling_stats, score|

        it "returns #{score} for #{bowling_stats}" do
          expect(score_keeper.calculate(bowling_stats)).to eq score
        end

      end
    end
  end
end

失败的输入是:

  • “5/5/5/5/5/5/5/5/5/5/5”(预期:150,得到:155),
  • “x3 / 61xxx2 / 9-7 / xxx”(预期:82,得到:88),
  • “14456/5 / --- 17/6 / - 2/6”(预期:193,得到:223)

2 个答案:

答案 0 :(得分:3)

我看到的第一件事是您使用gsub

input.gsub(/-/, "0")

您并未将gsub返回的字符串分配给任何内容,而是将其丢弃。

input = '#0#'
input.gsub('0', '-') # => "#-#"
input # => "#0#"

我怀疑你正在考虑改变gsub!,但我建议只将值传递给split

_frames = input.gsub(/-/, "0").split(//)

你的代码不是惯用的Ruby;您需要做的事情有很多:

  • 而不是if !input.is_a? String使用:

    unless input.is_a? String
      raise argumentException, "Score Keeper will only except string types for score calculation."
    end
    

    使用unless比使用否定测试更好。

  • 而不是

    if input == "xxxxxxxxxxxx"
        return 300
    end
    

    使用&#34;尾随if&#34;:

    return 300 if input == "xxxxxxxxxxxx"
    
  • 不要使用前导_命名变量。 _frames应为frames

  • 不要将lastFramelastlastFrameworkingValue等变量命名为混合案例AKA&#34; camelCase&#34;。我们使用&#34; snake_case&#34;用于Ruby变量和方法以及用于类和模块的camelCase。 It_is_a matterOfReadability。
  • 不要尾随;

    workingValue = 0;
    

    我们使用尾随分号的唯一时间是我们在一行上使用多个语句,这应该是非常罕见的。除非你知道为什么以及何时应该知道,否则不要这样做。

  • 考虑一下您在这里遇到的潜在问题:

    "12".include?('1') # => true
    "12".include?('2') # => true
    "12".include?('12') # => true
    

    虽然你的代码可能会忽略这个问题,但是不要编写这样的代码并考虑副作用。也许您想真正测试以查看该值是否为1到9之间的整数?

    ((1 .. 9) === '1'.to_i) # => true
    ((1 .. 9) === '2'.to_i) # => true
    ((1 .. 9) === '12'.to_i) # => false
    
  • 而不是使用

    return score
    

    你可以简单地使用

    score
    

    Ruby会返回看到的最后一个值;你不必明确地退回它。

  • 正确缩进代码。当你不得不重新调试代码来调试某些东西时,你的未来将会很感激。一致地使用两个空格缩进。
  • 大量使用空格将代码分成可读块。它不会影响代码的运行时速度,因此更容易阅读。再一次,你未来的自我会欣赏它。

虽然这可能看起来很挑剔,但是在开发人员团队编写代码时,这些小事情会有很长的路要走,而且在代码审查过程中,如果不做这些事情就会让你陷入尴尬境地。

答案 1 :(得分:1)

你的问题似乎是,你的前两帧你正在添加最后两帧。请考虑以下事项。

arr = [1,2,3,4,5,6,7,8,9]
arr.each_with_index do |num, i|
  puts "current number #{num}"
  puts arr[i-1]
  puts arr[i-2]
end

我认为你需要一个if语句来处理前两帧因为 - 如果你的索引是0,index会循环回到数组的末尾。

所以你需要像

这样的东西
arr = [1,2,3,4,5,6,7,8,9]

arr.each_with_index do |num, i|
  puts "current number #{num}"
  if i <= 1
    puts "no previous frame"
  elsif i == 1
    puts arr[i-1] + "can be added to frame 2"
  else
    puts arr[i-1] + "can be added to frame 1"
    puts arr[i-2] + "can be added to frame 2"
  end

end