IT's not Rocket Science!

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

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

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

フィヨルドブートキャンプのプラクティスにボウリングのスコア計算プログラムがあります。 ボウリングスコアのプログラムというと、当初、全然コードが書けなくて、身内にボウリングのルールの話をしたら、「普通、人生でこんなにボウリングのこと考えないよ。面白いね。」と言われたことを思い出します。

ネタバレ防止のためコードはあまり書きません。

作りたいもの

  • ボウリングの旧ルールの計算プログラム
  • ターミナルで実行したら計算結果が出力される

条件

  • 入力は6,3,9,0,0,3,8,2,7,3,X,9,1,8,0,X,6,4,5このようにする。
  • ,投球の区切り
  • 0-10 : 何本倒したか
  • X: ストライク
  • ストライクの場合は2投目は表記しない。

おそらく、私たちがボウリング場で使っているのが旧ルールです。 ルールがわからない場合は、ググってください。

やりたいこと

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

ざっとこんな感じです。

フィヨルドブートキャンプでは新ルールの方のコードが書いてあるので、そこにあるものは載せます。

ターミナルでスコアを入力して、それを格納する

カレンダーの記事ではoptparseを使いましたが、今回は別の方法です。

ARGVを使います。

Object::ARGV (Ruby 3.3 リファレンスマニュアル)

Rubyスクリプトとはfile.rbのようなファイル名のことです。その引数を文字列の配列にします。

score = ARGV[0]
p score

これで、ruby bowl.rb 1,2,3で出力すると

"1,2,3"

で返ります。

一方で、ruby bowl.rb 1 2 3と空白で出力すると

"1"

が返ります。 1,2,3は1つの文字列として認識しているため、上の結果となります。

これで、ターミナルの引数を格納できました。

1つずつの要素にする

今は、引数がそのまま1つの要素になっているので、, を取り除いて配列にします。

[0]で文字列を出力しているので、String#splitのsplit(',')を使って,で区切って分割します。(ちなみに、 [0]がない場合、配列となるので、splitは使えません。)

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

split('')は、,も配列に含みます。

score = "1,2,3"
score.split('') # => ["1", ",", "2", ",", "3"]

Xのような表記を数値に変換する

ストライクを取った場合、Xを入力していますが自動的に10にはなりません。 新しい配列を作って、X100を出力するようにします。 先ほど出来上がった配列に繰り返し処理行って、Xが来たら、10と0を返す、式を考えます。

繰り返しeachと、来たらifで行います。

<<で配列に追加ができます。 Array#<< (Ruby 3.3 リファレンスマニュアル)

shots = []
scores.each do |s|
  if s == 'X'
    shots << 10
    shots << 0
  else
    shots << s.to_i
  end
end

配列になんらかの処理を加えて配列で返す場合、mapメソッドを使用して表すこともできます。

【Ruby】eachとmapの違い #Ruby - Qiita

私の完成作品は上記の記事でいう悪い例でした。笑

今回の場合は、配列の中に配列を返すので、それを1つの配列にする必要があります。

class Array (Ruby 3.3 リファレンスマニュアル)からそれができそうなメソッドを探します。

Enumerable#collect_concat (Ruby 3.3 リファレンスマニュアル)を使って、ifの中を配列にします。もっというと、このifも1行で表せます。 ?:でできますので、チャレンジしてみてください。

一番下にあります。

演算子式 (Ruby 3.3 リファレンスマニュアル)

一旦ここまで

思った以上に長くなってしまったので、今回はこの辺で。

ラクティスの時に取り組んでいた時と違い知識がついていて、嬉しかったです。 これからもマイペースにがんばります。