ペアプロで出されたボーリングプログラム Ruby編をまともにしてみた

はじめに

先日、ペアプロワークショップで出されたボーリングプログラムのRubyバージョンを書いてみたのですが、どう考えてもRubyらしくない。

なにがRubyらしいのかというのはいろんな人の思いがあると思うのですが、個人的には、

ループ処理でインデックスを使わなくて書くこと

Rubyらしいかなと思っています。

いろいろ考えてみた

で、インデックスを使わないように考えてみたのですが、そこはへっぽこプログラマ。結構考えたのですが、今回はGoogle先生に頼ることにしました。^^;

Ruby」「Bowling」で検索すると引っかかったこのページにそのままズバリのコードが書いてありました。

Bowling Game Kata in Ruby

正確には、このページの後ろの方にあるコードが正しいプログラム。このページにはちゃんとテストコードも書かれているという素晴らしさ。いやぁ、こういうコードを書けるようになりたいなぁ。

考え方を見てみる

これだけだとあれなので、ちょっとソースに書かれている考え方を勝手に解説してみよう。

まず、ボーリングのスコアを格納しているのが、インスタンス変数の@rolls。データの格納はrecordメソッドでやってんだけど、面白いのは、

    def record value
        @rolls << value
        @rolls << 0 if first_throw_is_strike?
    end

のようにストライクの時には0を追加でいれているところ。これは今回のペアプロで出された例と同じ形。これで、ストライクだろうが、それ以外だろうが、1フレームで投げる回数が2回と一般化できる。

おっと、忘れてた。第10フレームがある。第10フレームはストライクやスペアをとると1フレーム3回投げられるのだ。これをどうやっているかというと、should_allow_roll?にそのヒントがある。

    def should_allow_roll?
        max = 20
        max = 21 if is_spare_at? 10
        max = 22 if is_strike_at? 10
        max = 23 if is_strike_at? 11
        @rolls.length < max
    end

パッとみたところ何しているのか分からなかったんだけど、

  • 第10フレームでも2投しか投げないことを前提とする。(max=20のところ)
  • 第10フレームでスペアが発生した場合は、投げれる回数を増やす。(max=21のところ)
  • 第10フレームでストライクが発生した場合は、投げれる回数を増やす。(max=22のところ)
  • 第10フレームでも2投しか投げないことになっているので、ストライクやスペアが発生したときには、回数が増えてしまう。それをif文でこねくり回すわけではなく、11フレームとして取り扱う。(max = 23のところ)

ということをしている。

頭の中で「ボーリングは10フレームしかない」と思い込んでいて、それに引きづられて10フレームを前提としたデータ設計を考えていたんだけど、11フレームというのを導入したら話は簡単。これでスコアの計算も

    def score
        (1..10).inject(0) { |score, frame| score += score_for frame }
    end

というふうに綺麗にかける。す、素晴らしい・・・。

まとめ

今回の結論として、

  • 一般化することが重要。出来る限り一般化。
  • 現実世界の制約とプログラムの制約とは異なる。
  • 考えぬかれたプログラムは綺麗で素晴らしい。

という結果を得られました。いやぁ、奥が深いなぁ。