はじめに
B | I | N | G | O
8 | 16 | 36 | 57 | 67
10 | 24 | 33 | 58 | 72
2 | 17 | | 51 | 69
7 | 18 | 45 | 59 | 62
4 | 21 | 37 | 53 | 71
今回は(2)で前回の記事に続き、CodeIQに「ビンゴカード作成問題」を出題しました。みなさんの挑戦をお待ちしてます!を参考にRubyでBINGOカードを作るを作ってみようと思いました。
しかしですね、インプットしかしてなかった人にはBINGOカードと言えど、これをアウトプットするのは多少なりとも苦戦するのではないでしょうか?
今回は、自分なりに順を追ってどのように作成していったかを記載していきます。
本当に、超初心者用にめちゃくちゃ丁寧に手順を記しています。
『そんな基本的な事・・・』と思わずみていただけると幸いです(笑)
そして何よりも、手順も考え方も無茶苦茶ではあると思います。
試行錯誤ある中で学習していきます。
ちなみに(1)の【超初心者用】RubyでBINGOカードを作る手順を丁寧すぎるぐらいに書いてみた(1)が最初です。
作成手順にもっと効率の良い方法などありましたら、是非意見をお待ちしております。
前回の時点
B | I | N | G | O
14 | 30 | 35 | 55 | 64
6 | 23 | 41 | 50 | 71
9 | 19 | 45 | 59 | 74
4 | 24 | 37 | 52 | 70
8 | 22 | 31 | 48 | 68
ランダムな数字をカード形式に作り上げるところまでいきました。
まだ、していない事は
・カード真ん中の空欄を開ける
・B
の列の|
の位置を揃える
・全力のリファクタリング
です。
それでは、実装していきます。
カード真ん中の空欄を開ける
ここでは、string[]
を使います。
[]内で文字の場所を指定します。
今回は、列、行、共に3番目の位置。つまり、インデックス番号でいう2
が真ん中なので。
bingos[2][2] = " "
とします。
def numbers
bingos = [B, I, N, G, O].map { |bingo| bingo.sample(5) }.transpose
bingos[2][2] = " "
bingos.map { |bin|
bin.join(" | ")
}.join("\n")
end
puts numbers
B | I | N | G | O
2 | 26 | 38 | 58 | 63
5 | 20 | 35 | 59 | 64
15 | 19 | | 56 | 71
1 | 23 | 43 | 52 | 62
11 | 30 | 34 | 50 | 70
何とか真ん中を開けることは出来ました。
後は、|
の位置を揃えてあげるだけですね。
列の|
の位置を揃える
ポイントは、BINGO
という文字列とランダムな数字を右詰めにする事です。
しかし、しかし無理やり" "
で空白を作ろうにも1桁の数字と2桁の数字が混在しているので上手くいきません。
右詰めにするメソッドがあれば・・・
と思っていたらありました。(笑)
**文字列.rjust
**を使ってあげます。
これは、右詰めにした文字列を返すメソッドです。
これで完成まで出来そうですね。
と思ったのですが、、、
def numbers
bingos = [B, I, N, G, O].map { |bingo| bingo.sample(5) }.transpose
bingos[2][2] = " "
bingos.map { |bin|
bin.join(" | ").rjust(2) #.rjust(2)追加
}.join("\n")
end
puts numbers
B | I | N | G | O
11 | 28 | 33 | 56 | 66
7 | 27 | 32 | 55 | 61
4 | 25 | | 59 | 63
6 | 24 | 37 | 51 | 71
15 | 22 | 39 | 58 | 72
変わりません。
原因を考えてみます。
@scivolaさんの指摘でわかりました。bin.join(" | ")
では11 | 28 | 33 | 56 | 66
のような文字列が出来てしまっています。
つまり、rjust
で長さが2の文字列を右ずめにしようとしましたが、11 | 28 | 33 | 56 | 66
は2どころか10以上の1つの文字列が出来てしまっているため、効きませんでした。
それを踏まえた上で修正します。
def numbers
bingos = [B, I, N, G, O].map { |bingo| bingo.sample(5) }.transpose
bingos[2][2] = " "
bingos.map { |bin|
bin.map { |b| b.to_s.rjust(2) }.join(" | ")
}.join("\n")
end
B | I | N | G | O
8 | 16 | 36 | 57 | 67
10 | 24 | 33 | 58 | 72
2 | 17 | | 51 | 69
7 | 18 | 45 | 59 | 62
4 | 21 | 37 | 53 | 71
bingos.mapの中にもう一つmapメソッド
を足してjoin
とto_s.rjust(2)
を分けてみました。
何とか、これでやっと完成ですね!!!
B = (1..15).to_a
I = (16..30).to_a
N = (31..45).to_a
G = (46..60).to_a
O = (61..75).to_a
def word
[' B', ' I', ' N', ' G', ' O'].join(' | ')
end
def numbers
bingos = [B, I, N, G, O].map { |bingo| bingo.sample(5) }.transpose
bingos[2][2] = " "
bingos.map { |bin|
bin.map { |b| b.to_s.rjust(2) }.join(" | ")
}.join("\n")
end
puts word
puts numbers
これが現時点でのソースコードですが、
まだまだ、リファクタリングのしがいがありそうです。
リファクタリングしていく
まずは、
B = (1..15).to_a
I = (16..30).to_a
N = (31..45).to_a
G = (46..60).to_a
O = (61..75).to_a
の部分ですね。
まとめていきます。
この変数たちは、『15個の数字の配列』を示しているわけですから。
何か良い方法でまとめられそうです。
each_sliceメソッド
を使う
each_sliceメソッドは配列を分割するのに使うメソッドです。
これを上手く使っていきます。
B = (1..15).to_a
I = (16..30).to_a
N = (31..45).to_a
G = (46..60).to_a
O = (61..75).to_a
bingos = [B, I, N, G, O].map { |bingo| bingo.sample(5) }.transpose
こちらが現時点のコード。これをeach_sliceメソッドを使って変更していく。
これが何とも見事。
bingos = (1..75).each_slice(15).map { |bingo| bingo.sample(5) }.transpose
6行から1行へとこんなにも短くなりました!
空白+文字列(' '+文字列)で作った見た目の悪い配列をどうにかする
def word
[' B', ' I', ' N', ' G', ' O'].join(' | ')
end
これですね。' B'
では、空白+文字列で無理やり文字を作りました。
どうにかしていきます。
charsメソッド
を使います(%w()
でも出来ます)
これは、文字列の文字を一文字ずつ配列へと返すメソッドです。
"banana".chars #=> ["b", "a", "n", "a", "n", "a"]
これを使います。
["BINGO".chars] #[[' B', ' I', ' N', ' G', ' O']]を変更
# または
[%w(B I N G O)] ##[[' B', ' I', ' N', ' G', ' O']]
(この後、mapメソッドが2つあって、2つ展開するからここでは『配列の配列』を作っている?)
ですが、これでは空白がなくなってしまいましたね。
このまま出力すると
B | I | N | G | O
11 | 24 | 40 | 53 | 73
5 | 21 | 33 | 52 | 70
10 | 28 | | 46 | 75
12 | 20 | 38 | 48 | 71
1 | 26 | 42 | 56 | 63
空白をどうにかしましょう。
ここでもう一度、ここまでで使ったメソッドを思い出してみます。
文字数を調節するメソッドがありました。右詰めにするヤツ。
そうです。
・rjustメソッド
です
ちなみに現在のコードは
def word
["BINGO".chars].join(' | ')
end
def numbers
bingos = (1..75).each_slice(15).map { |bingo| bingo.sample(5) }.transpose
bingos[2][2] = " "
bingos.map { |bin|
bin.map { |b| b.to_s.rjust(2) }.join(" | ")
}.join("\n")
end
puts word
puts numbers
rjustメソッドは一度使っているのでその位置にまとめることが出来そうですね。
やってみましょう。
仕上げにかかります
まずは、def word
とdef numbers
をまとめます。
そして、変数の名前も分かりやすいように変更していきます。
出来たものがこちら。
def card
bingo = ["BINGO".chars]
numbers = (1..75).each_slice(15).map { |number| number.sample(5) }.transpose
numbers[2][2] = " "
rows = bingo + numbers
rows.map { |row|
row.map { |r| r.to_s.rjust(2) }.join(" | ")
}.join("\n")
end
puts card
B | I | N | G | O
3 | 19 | 39 | 46 | 69
8 | 21 | 35 | 50 | 61
13 | 22 | | 58 | 68
10 | 16 | 44 | 55 | 74
1 | 28 | 40 | 56 | 66
完成です。お疲れ様でした。