OOP版はこちら↓
1. 実行環境
2. ボウリングのスコア計算プログラムの要件
- 1ゲーム = 10フレーム
- 1フレーム = 2投
- スペアのフレームの得点は次の1投の点を加算する。
- ストライクのフレームの得点は次の2投の点を加算する。
- 10フレーム目は1投目がストライクもしくは2投目がスペアだった場合、3投目が投げられる。
- ありえない投球数やありえない数字・記号がこない前提。
- 入力例:
6,3,9,0,0,3,8,2,7,3,X,9,1,8,0,X,6,4,5
3. ソースコード
- ver1:自作→レビュー反映
- ver2:ver1→他の人のコードを反映
3-1. ver1
#!/usr/bin/env ruby # frozen_string_literal: true score = ARGV[0] scores = score.split(',') shots = [] scores.each do |s| if s == 'X' shots << 10 shots << 0 else shots << s.to_i end end frames = [] shots.each_slice(2) do |s| frames << s end frames_point = [] frames.each_with_index do |frame, i| frames_point[i] = frame.sum if i < 9 if frame[0] == 10 frames_point[i] += frames[i + 1][0] frames_point[i] += if frames[i + 1][0] == 10 frames[i + 2][0] else frames[i + 1][1] end elsif frame.sum == 10 frames_point[i] += frames[i + 1][0] end end end puts frames_point.sum
3-2. ver2
#!/usr/bin/env ruby # frozen_string_literal: true shots = ARGV[0].split(',').map { |s| s == 'X' ? 10 : s.to_i } frame = [] frames = [] shots.each do |s| frame << s if frames.length < 10 if frame.length >= 2 || s == 10 frames << frame.dup frame.clear end else frames.last << s end end point = 0 (0..9).each do |n| point += frames[n].sum frames[n + 1] ||= [] frames[n + 2] ||= [] if frames[n][0] == 10 point += (frames[n + 1] + frames[n + 2]).slice(0, 2).sum elsif frames[n].sum == 10 point += frames[n + 1][0] end end puts point
4. ソースコード(ver2)の解説
4-1. 主な使用メソッド
4-2. 解説
4行目
shots = ARGV[0].split(',').map { |s| s == 'X' ? 10 : s.to_i }
ARGV[0].split(',')
で入力された文字列を,
で分割して文字列の配列にする"6,3,9,0,0,3,8,2,7,3,X,9,1,8,0,X,6,4,5"
が["6", "3", "9", "0", "0", "3", "8", "2", "7", "3", "X", "9", "1", "8", "0", "X", "6", "4", "5"]
になる
.map { |s| s == 'X' ? 10 : s.to_i }
でXを数値の10に、それ以外を数値に変換する- 結果、shotsには
[6, 3, 9, 0, 0, 3, 8, 2, 7, 3, 10, 9, 1, 8, 0, 10, 6, 4, 5]
が入る
6~18行目
frame = [] frames = [] shots.each do |s| frame << s if frames.length < 10 if frame.length >= 2 || s == 10 # frameに2投またはストライクの1投が入ったら frames << frame.dup frame.clear end else # 10フレーム目 frames.last << s end end
shots[6, 3, 9, 0, 0, 3, 8, 2, 7, 3, 10, 9, 1, 8, 0, 10, 6, 4, 5]
から、各フレーム毎に分割したframes[[6, 3], [9, 0], [0, 3], [8, 2], [7, 3], [10], [9, 1], [8, 0], [10], [6, 4, 5]]
を作りたい
frames << frame.dup
についてframes << frame
だとframeの参照が格納されるだけでオブジェクトそのものは格納されない- frame.dupを使うことでframeのオブジェクトそのもののコピーが作成され、framesに格納される
21~30行目
(0..9).each do |n| point += frames[n].sum # 倒したピンの本数を加算 frames[n + 1] ||= [] frames[n + 2] ||= [] if frames[n][0] == 10 # ストライクの場合 point += (frames[n + 1] + frames[n + 2]).slice(0, 2).sum # 次の2投を加算 elsif frames[n].sum == 10 # スペアの場合 point += frames[n + 1][0] # 次の1投を加算 end end
1~10フレーム目までの点数を加算
点数は倒したピンの本数 + スペアまたはストライクのボーナス
(frames[n + 1] + frames[n + 2]).slice(0, 2).sum
についてframes[n + 1] + frames[n + 2]
で次の2フレームを合わせる- ストライクの次の2フレームが[6, 3]と[0, 4]なら、[6, 3, 0, 4]になる
- ストライクの次の2フレームが[10]と[10]なら、[10, 10]になる
.slice(0, 2)
で先頭から2つ→次の2投を加算[6, 3, 0, 4].slice(0, 2)
なら[6, 3][10, 10].slice(0, 2)
なら[10, 10]
- 連続ストライクのための施策
- スペアはシンプルに次の1投だけなのでこのような施策はいらない
frames[n + 1] ||= []
とframes[n + 2] ||= []
について
【参考】