我正在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
失败的输入是:
答案 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
。
lastFrame
,lastlastFrame
和workingValue
等变量命名为混合案例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