#はじめに
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
今回、CodeIQに「ビンゴカード作成問題」を出題しました。みなさんの挑戦をお待ちしてます!を参考にRubyでBINGOカードを作るを作ってみようと思いました。
しかしですね、インプットしかしてなかった人にはBINGOカードと言えど、これをアウトプットするのは多少なりとも苦戦するのではないでしょうか?
今回は、自分なりに順を追ってどのように作成していったかを記載していきます。
本当に、超初心者用にめちゃくちゃ丁寧に手順を記しています。
『そんな基本的な事・・・』と思わずみていただけると幸いです(笑)
そして何よりも、手順も考え方も無茶苦茶ではあると思います。
試行錯誤ある中で学習していきます。
作成手順にもっと効率の良い方法などありましたら、是非意見をお待ちしております。
#何から手を付ければ良いのか・・・
①いきなりコードを書き始める前に何を作っていくかの全体の流れを考えてみる
プログラムを作る時、いきなりコードを書き始めるよりも全体の流れを把握しなければいけない事を教わったためその通りに考えてみる。
とりあえず、今回作るべきメソッドなど書き記してみました。
●<今回使うであろうメソッド>
・BINGOの文字列を作るメソッド
・それぞれの文字列ごとに排出する数字をランダムで選択するメソッド
●<ポイント>
・BINGOカードの真ん中の空欄
・カードのような見た目の並びの作り方
#作ってみよう!
##<①とりあえず文字列から>
一番作りやすそうなものから作っていきます。
def word
['B', 'I', 'N', 'G', 'O'].join(' | ')
end
puts word #=>B | I | N | G | O
joinメソッドで文字列を連結していきます。これで一つの文字列の出来上がりです。
引数が文字同士の区切り文字になります。
##<②次はランダムな数字の作成>
B = [1..15]
I = [16..30]
N = [31..45]
G = [46..60]
O = [61..75]
とりあえず、範囲オブジェクトで配列を作成してみました。
これをどうにか順番に出力してカードを作っていきます。
sampleメソッドで範囲オブジェクトの中からランダムに選んでもらおう!
しかし、
##詰まってしまった。
B = [1..15]
I = [16..30]
N = [31..45]
G = [46..60]
O = [61..75]
def number
B.sample
end
puts number #=>1..15
ん?
sampleメソッドは、配列の要素1つをランダムに取得するもの
配列.sample
エラーは出ていないので、配列としては受け取っていますがそれぞれの数字が出力されません。
あ、なるほど!
** 1..15 自体でオブジェクトの一つとして認識されてしまっているために、それぞれの数字ではなく、配列内は 1..15 **という1つのオブジェクトがそのまま出力されてしまったのですね。
それでは、範囲オブジェクトに対してto_aメソッド
で値が連続する配列を作成していきます。
B = (1..15).to_a
def number
B.sample
end
puts number => 14 #(1~15の中のランダムな数字)
ランダムに出力できました!
ちなみに、
(1..15).to_a
は
(1..15).to_a #=>[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
の意味です。
##この調子で、ランダムな数字続けて作成!?
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 number
[B,I,N,G,O].sample
end
puts number #=>61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75
これで、8,26,33,50,67
のような期待する通りの結果が得られると思っていました・・・
が、しかし、出たのは
#=> 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75
配列[B,I,N,G,O]
の中のランダムオブジェクトであるO
の配列が出力されてしまっています。
これはあかん。
そこで、このように変えてみました。
それぞれの変数に『範囲オブジェクトの数字をランダムに選んだもの』を定義します。
その数字を、[B,I,N,G,O]
という配列にして出力しますと
B = (1..15).to_a.sample
I = (16..30).to_a.sample
N = (31..45).to_a.sample
G = (46..60).to_a.sample
O = (61..75).to_a.sample
def number
[B,I,N,G,O]
end
puts number #=>8,26,33,50,67
期待通りですね!
##続いて、見た目を作っていく
B | I | N | G | O
10 | 16 | 36 | 57 | 72
この形をとりあえず目指して作っていきます。
ここはなんとなく想像はつきます。
カラの文字列をで調節していくだけ・・・??
B = (1..15).to_a.sample
I = (16..30).to_a.sample
N = (31..45).to_a.sample
G = (46..60).to_a.sample
O = (61..75).to_a.sample
def word
['B', 'I', 'N', 'G', 'O'].join(' | ')
end
def number
[B,I,N,G,O].join(' | ')
end
puts word
puts number
これが、カラの文字列を足した後のコードです。
これが現在のbingo.rb
のコードの全貌ですねです。
しかし、これではまだカードではないのでビンゴカードを作っていきましょう
##カードまで作るで!
カードの真ん中の空白はとりあえず無視してしまって、現在、ランダムな数字が1行なのでそこをランダムな数値が5行になるまで持っていきます。
ここで重要なのは、
・指定した変数の数字の範囲内で、重複しない異なる数字が5つ出力される事です。
それでは、コーディングしていきます。
今回、思いついたtimesメソッド
でやってみます。
def number
5.times do
puts [B,I,N,G,O].join(' | ')
end
end
number
これで、とりあえず5行出力されるのではないでしょうか。
いざ出力!
B | I | N | G | O
13 | 22 | 40 | 50 | 71
13 | 22 | 40 | 50 | 71
13 | 22 | 40 | 50 | 71
13 | 22 | 40 | 50 | 71
13 | 22 | 40 | 50 | 71
確かに形ができてきましたね。
しかし、全ての行で同じものが出力されてしまっています。
オブジェクト.time do
//繰り返したい処理
end
これでは、" " + [B,I,N,G,O].join(' | ')
を5回繰り返すだけです。
出力されるものの中身は5つ全て同じになります。
・重複しない、異なるランダムな数字
が出力されないといけませんね
では、どうしようか。
ーーーここから結構てこずりましたーーー
##そもそもが違う?
ランダムな数値の変数として
B = (1..15).to_a.sample
#以下省略
がありましたが、これではB
に格納されている数字は1~15
の中から選ばれた1つのランダムな数値です。
例えば、B = 11
で定義されてしまっているならば、その後何度出力しようが、ランダムな数値にはならずに11
が出力されるだけです。
めちゃ基本的な事ですが、自分でコードを書いてみないと気づかなかった事ですね。
それを踏まえた上でコードを変えていきます。
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 number1
[B.sample,I.sample,N.sample,G.sample,O.sample].join(' | ')
end
def number2
[B.sample,I.sample,N.sample,G.sample,O.sample].join(' | ')
end
def number3
[B.sample,I.sample,N.sample,G.sample,O.sample].join(' | ')
end
def number4
[B.sample,I.sample,N.sample,G.sample,O.sample].join(' | ')
end
def number5
[B.sample,I.sample,N.sample,G.sample,O.sample].join(' | ')
end
puts word
puts number1
puts number2
puts number3
puts number4
puts number5
完成形をみるためにコードを長くしてしまいました見えてきましたね(最後にリファクタリングします。)
それでは、出力します。
B | I | N | G | O
12 | 27 | 38 | 52 | 66
12 | 21 | 43 | 48 | 70
7 | 21 | 34 | 48 | 65
7 | 27 | 35 | 49 | 67
12 | 30 | 32 | 58 | 72
見えてきました😀
しかし、まだまだ修正すべき点が見えてきましたね。
#####1. 重複する数字も出てきている
#####2. 'B'は1桁の数字もあるため、仕切り『|』がどうしてもずれてしまう
#####3.ビンゴカードの中心は空白にしなければいけない
##一度、長ったらしいコードをリファクタリングしよ
def number1
[B.sample,I.sample,N.sample,G.sample,O.sample].join(' | ')
end
#省略
def number5
[B.sample,I.sample,N.sample,G.sample,O.sample].join(' | ')
end
この部分、どうにかしましょう。
実はsampleメソッドには引数を使う事で複数のランダムな数値を引き出すこともできました。
てことなので
def number
[B.sample(5),I.sample(5),N.sample(5),G.sample(5),O.sample(5)].join(' | ')
end
puts number
B | I | N | G | O
13 | 14 | 6 | 11 | 12 | 29 | 24 | 23 | 20 | 17 | 38 | 36 | 33 | 39 | 40 | 58 | 50 | 48 | 55 | 46 | 68 | 65 | 62 | 70 | 66
んー、これをどうにか並べ替えることができれば。。。
そこで、色々ググっていると出てきました!!しかもめちゃめちゃスマートなものが!
それが、transposeメソッド
です。
詳しいことは、ググってみてください。
簡単に言いますと、
**『配列を行列に見立てて行と列の入れ替え』**を行います。
位置が変わった配列が出来上がります。
そして、transposeメソッド
を使って出力すると
def number
[B.sample(5),I.sample(5),N.sample(5),G.sample(5),O.sample(5)].transpose.join(' | ')
end
puts number
B | I | N | G | O
4 | 28 | 43 | 57 | 70 | 1 | 27 | 36 | 59 | 64 | 13 | 21 | 41 | 60 | 67 | 9 | 30 | 34 | 50 | 69 | 12 | 23 | 38 | 54 | 75
素晴らしい。
でも、まだリファクタリングできるはず。
やっと思い出した、mapメソッド
や。
[B.sample(5),I.sample(5),N.sample(5),G.sample(5),O.sample(5)]
この部分ですね。見た目がどうみても悪いです。変えましょう。
[B, I, N, G, O].map { |bingo| bingo.sample(5) }
よし!変わりました。
##5 * 5のカード形式にもう一度
次はまた見た目ですね。
現時点では、出力順はできたものの、5*5
のカード形式にはなっていません。
ここからはどうするか?
どうやら\n
のコードで改行ができると。
では実装。
使うメソッドは?
文字列を|
で区切る際に使った、joinメソッド
を使うことに。
def number
[B, I, N, G, O].map { |bingo| bingo.sample(5) }.transpose.join(' | ')
end
しかし、ここでは既にjoinメソッドを使ってしまっている。
どないしよう?
では、[B, I, N, G, O].map { |bingo| bingo.sample(5) }.transpose
を変数に格納してみます。
bingos = [B, I, N, G, O].map { |bingo| bingo.sample(5) }.transpose
これを、またmapメソッドで順番に吐いていってあげればjoin
を使えるのではないでしょうか?
やってみる。
def numbers
bingos = [B, I, N, G, O].map { |bingo| bingo.sample(5) }.transpose
bingos.map { |bin|
bin.join(" | ")
}.join("\n")
end
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
の列の|
の位置を揃える
・全力のリファクタリング
です。
#まとめ
今回実装して思ったことは、
インプットでメソッドを色々勉強していたけれど全く頭に入っていなかったし、実際に使わないと使っているメソッドのありがたみや、感動がわからないから覚えられない
ってことでした。
自分で使ってみて初めて分かるものが多かった印象です。