class Frame
  attr_reader :score, :type

  def initialize(frame)
    @frame = frame
    @score = 0
    @state = nil
    @bonus = Array.new
    @first_pin = nil
  end

  def display_score
    ret = if @state.nil?
      ""
    elsif @state == :strike
      "X"
    elsif @state == :spare
      "#{display_pin(@first_pin)}/"
    else
      "#{display_pin(@first_pin)}#{display_pin(@score-@first_pin)}"
    end
    if @frame == 10
      @bonus.each {|b| ret << display_pin(b)}
    end
    return ret
  end

  def display_pin(pins)
    if pins == 0
      return '-'
    elsif pins == 10
      return 'X'
    else
      return pins.to_s
    end
  end

  def score_roll(pins)
    @first_pin ||= pins
    if @state.nil?
      @score += pins
      if @score == 10
        @state = :strike
      else
        @state = :incomplete
      end
    elsif @state == :incomplete
      @score += pins
      if @score > 10
        raise "Illegal roll in incomplete frame with score #{@score - pins}: #{pins}"
      end
      if @score == 10
        @state = :spare
      else
        @state = :open
      end
    end
  end

  def finished?
    [:open, :strike, :spare].include?(@state)
  end

  def strike?
    @state == :strike
  end

  def spare?
    @state == :spare
  end

  def working?
    (@bonus.size < 1 && @state == :spare) || (@bonus.size < 2 && @state == :strike)
  end

  def bonus(pins)
    @bonus << pins
    @score += pins
  end

end

# Represents a bowling game
class BowlingGame
  attr_reader :name, :frames

  # Create a bowling game for the given named player
  def initialize(name)
    @name = name
    @frames = Array.new(10) { |i| Frame.new(i+1) }
    @working = Array.new
  end

  # Score a roll of the given number of pins
  def score_roll(pins)
    # Find the current frame
    frame = @frames.find {|f| !f.finished? }

    # If we have no current frame and nothing is working, we are
    # scoring too many rolls
    if frame.nil? && @working.empty?
      raise "Too many rolls are being scored in this game."
    end

    # Score bonus pins for strikes and spares that are working
    @working.delete_if {|f| f.bonus(pins); !f.working? }

    # If we found no current frame, we are in bonus rolls of
    # the tenth frame
    return if !frame

    # Score this ball on the current frame and move it to
    # working if we rolled a spare or strike
    frame.score_roll(pins)
    if frame.spare? || frame.strike?
      @working << frame
    end
  end

  # Returns the current score of this game
  def score
    @frames.inject(0) {|score, f| score += f.score }
  end

  # Prints a ascii score sheet
  def print_score_sheet
    running_score = 0
    print "+---" * 11, "+\r"
    puts @name
    @frames.each do |f|
      print "|%3s" % f.display_score
    end
    print "|%3s|\n" % self.score
    @frames.each do |f|
      running_score += f.score
      print "|%3s" % (f.finished? && !f.working? ? running_score.to_s : '')
    end
    print "|   |\n"
    print "+---" * 11, "+\n"
  end

end

if __FILE__ == $0
  name, *pins = *ARGV
  game = BowlingGame.new(name)
  pins.each do |p|
    game.score_roll(p.to_i)
  end
  game.print_score_sheet
end