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
class BowlingGame
attr_reader :name, :frames
def initialize(name)
@name = name
@frames = Array.new(10) { |i| Frame.new(i+1) }
@working = Array.new
end
def score_roll(pins)
frame = @frames.find {|f| !f.finished? }
if frame.nil? && @working.empty?
raise "Too many rolls are being scored in this game."
end
@working.delete_if {|f| f.bonus(pins); !f.working? }
return if !frame
frame.score_roll(pins)
if frame.spare? || frame.strike?
@working << frame
end
end
def score
@frames.inject(0) {|score, f| score += f.score }
end
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