diff --git a/06.bowling_object/bowling.rb b/06.bowling_object/bowling.rb new file mode 100644 index 0000000000..951ad8b49f --- /dev/null +++ b/06.bowling_object/bowling.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require_relative 'game' +require_relative 'frame' +require_relative 'shot' + +game = Game.new(ARGV[0]) +puts game.total_score diff --git a/06.bowling_object/frame.rb b/06.bowling_object/frame.rb new file mode 100644 index 0000000000..e5b628cdb5 --- /dev/null +++ b/06.bowling_object/frame.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require_relative 'shot' + +class Frame + attr_reader :first_shot, :second_shot, :third_shot + + def initialize(first_score, second_score, third_score = nil) + @first_shot = Shot.new(first_score) + @second_shot = Shot.new(second_score) + @third_shot = third_score.nil? ? nil : Shot.new(third_score) + end + + def total_score(next_frame, after_the_next_frame) + return [@first_shot, @second_shot, @third_shot].map(&:score).sum unless @third_shot.nil? + + total_score = total_score_first_and_second + total_score += strike_bounus(next_frame, after_the_next_frame) if strike? + total_score += spare_bounus(next_frame) if spare? + total_score + end + + def total_score_first_and_second + [@first_shot, @second_shot].map(&:score).sum + end + + def strike_bounus(next_frame, after_the_next_frame) + if next_frame.strike? + # 9フレーム目の場合 + return next_frame.total_score_first_and_second if after_the_next_frame.nil? + + [next_frame.first_shot, after_the_next_frame.first_shot].map(&:score).sum + else + next_frame.total_score_first_and_second + end + end + + def spare_bounus(next_frame) + next_frame.first_shot.score + end + + def spare? + @first_shot.score + @second_shot.score == 10 && @first_shot.score != 10 + end + + def strike? + @first_shot.score == 10 + end +end diff --git a/06.bowling_object/game.rb b/06.bowling_object/game.rb new file mode 100644 index 0000000000..f4f5c9c942 --- /dev/null +++ b/06.bowling_object/game.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require_relative 'frame' + +class Game + ONE_TO_NICE_FRAMES_SHOTS_COUNT = 2 * 9 + def initialize(row_scores) + framed_scores = parse_scores(row_scores) + # Frameクラス内のfirst_shot, second_shotとは命名の法則が異なっており統一感は無いが、 + # ここでfirst_frame, second_frame・・・と宣言したら全体の記述が長くなり、 + # むしろ可読性が落ちると判断したので配列で宣言 + @frames = framed_scores.map do |score| + Frame.new(*score) + end + end + + def total_score + total_score = 0 + @frames.each_with_index do |frame, index| + next_frame, after_the_next_frame = @frames[index + 1, 2] + total_score += frame.total_score(next_frame, after_the_next_frame) + end + total_score + end + + private + + def parse_scores(row_scores) + scores = [] + row_scores.split(',').each do |score| + scores.length < ONE_TO_NICE_FRAMES_SHOTS_COUNT && score == 'X' ? scores.push('X', '0') : scores << score + end + tenth_frame_scores = scores[ONE_TO_NICE_FRAMES_SHOTS_COUNT...scores.count] + framed_scores = scores.take(ONE_TO_NICE_FRAMES_SHOTS_COUNT).each_slice(2).to_a + framed_scores << tenth_frame_scores + end +end diff --git a/06.bowling_object/shot.rb b/06.bowling_object/shot.rb new file mode 100644 index 0000000000..da9ee9ca12 --- /dev/null +++ b/06.bowling_object/shot.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class Shot + attr_reader :score + + def initialize(string_score) + @score = string_score == 'X' ? 10 : string_score.to_i + end +end diff --git a/06.bowling_object/test_bowling.rb b/06.bowling_object/test_bowling.rb new file mode 100644 index 0000000000..1d28be4ceb --- /dev/null +++ b/06.bowling_object/test_bowling.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require_relative 'game' + +def main + test_bowling_return_139 + test_bowling_return_164 + test_bowling_return_107 + test_bowling_return_134 + test_bowling_return_144 + test_bowling_return_300 + test_bowling_return_292 + test_bowling_return_50 +end + +def test_bowling_return_139 + template_test_bowling('6,3,9,0,0,3,8,2,7,3,X,9,1,8,0,X,6,4,5', 139) +end + +def test_bowling_return_164 + template_test_bowling('6,3,9,0,0,3,8,2,7,3,X,9,1,8,0,X,X,X,X', 164) +end + +def test_bowling_return_107 + template_test_bowling('0,10,1,5,0,0,0,0,X,X,X,5,1,8,1,0,4', 107) +end + +def test_bowling_return_134 + template_test_bowling('6,3,9,0,0,3,8,2,7,3,X,9,1,8,0,X,X,0,0', 134) +end + +def test_bowling_return_144 + template_test_bowling('6,3,9,0,0,3,8,2,7,3,X,9,1,8,0,X,X,1,8', 144) +end + +def test_bowling_return_300 + template_test_bowling('X,X,X,X,X,X,X,X,X,X,X,X', 300) +end + +def test_bowling_return_292 + template_test_bowling('X,X,X,X,X,X,X,X,X,X,X,2', 292) +end + +def test_bowling_return_50 + template_test_bowling('X,0,0,X,0,0,X,0,0,X,0,0,X,0,0', 50) +end + +def template_test_bowling(input, expected) + game = Game.new(input) + puts "入力したスコア:#{input}" + puts "expected: #{expected}" + + total_score = game.total_score + puts "actual: #{total_score}" + + if total_score == expected + puts '正常' + else + puts '異常' + end + puts '' +end + +main