あまブログ

ドキドキ......ドキドキ2択クイ〜〜〜〜〜〜〜ズ!!

【Ruby3.1】ボウリングのスコア計算プログラムをオブジェクト指向で作る

OOP版はこちら↓

ama-tech.hatenablog.com

1. 実行環境

2. ソースコード

bowling.rb

#!/usr/bin/env ruby
# frozen_string_literal: true

require_relative 'game'

shots = ARGV[0].split(',')

game = Game.new(shots)
puts game.score

game.rb

# frozen_string_literal: true

require_relative 'frame'

class Game
  def initialize(shots)
    @shots = shots
  end

  def score
    @frames = build_frames
    game_score = 0
    (0..9).each do |idx|
      frame = Frame.new(@frames[idx])
      game_score += frame.score
      @frames[idx + 1] ||= []
      @frames[idx + 2] ||= []
      game_score += calc_bonus_point(idx, frame)
    end
    game_score
  end

  def build_frames
    frame = []
    frames = []
    @shots.each do |s|
      frame << s
      if frames.length < 10
        if frame.length >= 2 || s == 'X'
          frames << frame.dup
          frame.clear
        end
      else
        frames.last << s
      end
    end
    frames
  end

  def calc_bonus_point(idx, frame)
    if frame.strike?
      next_two_shots = (@frames[idx + 1] + @frames[idx + 2]).slice(0, 2)
      bonus_point = next_two_shots.sum { |s| Shot.new(s).score }
    elsif frame.spare?
      next_shot = @frames[idx + 1][0]
      bonus_point = Shot.new(next_shot).score
    else
      bonus_point = 0
    end
  end
end

frame.rb

# frozen_string_literal: true

require_relative 'shot'

class Frame
  def initialize(frame)
    @first_shot = Shot.new(frame[0])
    @second_shot = Shot.new(frame[1])
    @third_shot = Shot.new(frame[2])
  end

  def score
    [
      @first_shot.score,
      @second_shot.score,
      @third_shot.score
    ].sum
  end

  def strike?
    @first_shot.score == 10
  end

  def spare?
    score == 10
  end
end

shot.rb

# frozen_string_literal: true

class Shot
  def initialize(mark)
    @mark = mark
  end

  def score
    @mark == 'X' ? 10 : @mark.to_i
  end
end