IT's not Rocket Science!

プログラミング難しいけど頑張ろうね!

Rubyでボウリングのスコア計算プログラムを作ったメモ。後編

こんにちは、ひろきです。

前回は、スコアの入力をしたら、それを数値に変換して配列に格納するところまでやりました。

↑書いてて、投球数が奇数になってました。次で直ります。

やりたいこと

  • ターミナルでスコアを入力して、それを格納する
  • Xのような表記を数値に変換する
  • その数値をフレームとして分ける
  • ストライクやスペアの計算を実装する

その数値をフレームとして分ける

ボウリングでは、1ターンに最大2ボールを投げることができます。 例外として、10レーンでは2投までに10本倒すと3投目に挑戦できるボーナスがつきます。

なので、このフレームを配列で作っていきます。

Enumerable#each_slice (Ruby 3.3 リファレンスマニュアル)

frames = []

から初めて、前編で使ったコードを使用してもいいですし、 または

frames = #フレームを作るコード

でもいいと思います。

その場合、each_slice(2)は配列を返さないので、配列に変換しないといけません。

これで、ようやく計算までのベースを作ることができました。

ストライクやスペアの計算を実装する

よく考えたら、いきなりこの計算をするのではなくて、まずは全てを足し算できるようにしないとダメですね。

足し算の式を作る

まず、点数を表示する変数が欲しいので、

point = 0

を定義します。 point+=を使用して足し算をしていきます。

point += frames[0].sum

で1フレームの合計をポイントに入れられます。

しかし、これだと毎回フレーム数を指定しないといけないのでめちゃめちゃ面倒くさいです。

そこで、繰り返しです。 1フレームの合計を足す、2フレームの合計を足したいです。 eachで1つずつ足し算はできます。 とりあえず、eachで進めてみましょう。

次にストライクとスペアを順に実装します。

ストライクとスペア

ストライクは[10,0]でスペアは[2,8]のような2投目が10になることです。 ストライクを取ると、次のフレームの合計を足します。さらに2回連続ストライクだったら、3回目の投球の1投目まで足されます。

例: [10,0],[10,0],[3,7]→10+10+3+ 23

となると、足し算にストライクの次のフレームを足したいので、「次のフレーム」を指定する必要があります。 先ほどのeachだと、それはできなそうので(できたとしても、私にはできない)、繰り返しのリファレンスから配列の番号を指定できるメソッドがないか探します。

module Enumerable (Ruby 3.3 リファレンスマニュアル)

このメソッドを使います。

要素とそのインデックスをブロックに渡して繰り返します。 Enumerable#each_with_index (Ruby 3.3 リファレンスマニュアル)

さらに

あるまとまったデータから個々のものを取り出すのに便利なeachの働きに、さらに加えて同時に取り出した順序の番号を取り出せるのがeach_with_indexです。より便利なeachとして覚えておくとよいでしょう。

Rubyのeachでindexを取得する:each_with_index | UX MILK

これで、フレーム数の指定ができるようになります。

ストライクだったら10点、次のフレームの合計を足す。 ストライク、ストライクだったら、さらに次のフレームの1投目を足す。

ストライク([10,0])だったら、
10点と(+)次のフレームの合計(frame.sum)を足す(+=)。
ストライク、ストライク(frames[index + 1] == [10, 0])だったら(if)、
さらに次のフレーム(frames[index + 2])の1投目(frames[index + 2][0])を足す(+=)。

こんな感じです。

配列を[0][1]のように指定すれば、どの投球か指定できます。

同様に、スペアでも同じことをします。 省略します。

そして、最後に9本以下の合計を足すと、ポイント計算ができるようになります。

当時、私がわからず悩んだところが、each_with_index do |frame, index|frameです。 これはfのような文字でも大丈夫で、frameを使った結果、なぜframesframeを使い分けるのか悩みました。 冷静に考えれば、framesは配列全体を表し、frameは取り出した配列の1つを表しているとわかります。 なので、コードを書いてエラーが起きたときは、それに気をつけてください。

さて、もう少しで完成です。

10フレーム目

厄介ポイントです。

10フレーム目は最大3投投げられます。

やれそうなこととして

  • 3投の配列を作る
  • 12フレームまで作ってそれを使う

が考えられます。ただ、ストライクをすると、自動的に[10,0]となるため、3投配列を作るのは現実的ではありません。 後者でやります。

条件分岐は上から評価していくため、可能性の低いものを上に持ってきた方が良いです。 上記でやったようにifを足して、10フレーム目の条件を満たしてみてください。

繰り返しの終了

最後に計算を終わらせるべき時が来ます。 入力した際に、必要以上のフレーム数があっても繰り返してしまうので、とめる必要があります。 ループを抜け出すものをどこかに挟んで、不要な計算を止めましょう。

制御構造 (Ruby 3.3 リファレンスマニュアル)

最後はputs pointで終了です。

最後に

最後の方をはしょってしまったのは、コードを書いてしまう気がしたからです。 当時、ブロッックの部分が理解できていなかったり、pを使ったりして、何がどうできていないか確認するのにたくさん使ったを思い出しました。

後半上手く書けた気がしないので、気分転換でリライトできたらと思います。