【超初心者用】RubyでBINGOカードを作る手順を丁寧すぎるぐらいに書いてみた（１）

#はじめに

```
 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に「ビンゴカード作成問題」を出題しました。みなさんの挑戦をお待ちしてます！](https://blog.jnito.com/entry/2015/02/09/101018)を参考にRubyでBINGOカードを作るを作ってみようと思いました。

しかしですね、インプットしかしてなかった人にはBINGOカードと言えど、これをアウトプットするのは多少なりとも苦戦するのではないでしょうか？

今回は、自分なりに順を追ってどのように作成していったかを記載していきます。
本当に、超初心者用にめちゃくちゃ丁寧に手順を記しています。

『そんな基本的な事・・・』と思わずみていただけると幸いです（笑）

そして何よりも、**手順も考え方も無茶苦茶ではあると思います。**

試行錯誤ある中で学習していきます。

作成手順にもっと効率の良い方法などありましたら、是非意見をお待ちしております。

#何から手を付ければ良いのか・・・
**①いきなりコードを書き始める前に何を作っていくかの全体の流れを考えてみる**
プログラムを作る時、いきなりコードを書き始めるよりも全体の流れを把握しなければいけない事を教わったためその通りに考えてみる。

とりあえず、今回作るべきメソッドなど書き記してみました。

**●＜今回使うであろうメソッド＞**
・BINGOの文字列を作るメソッド
・それぞれの文字列ごとに排出する数字をランダムで選択するメソッド

**●＜ポイント＞**
・BINGOカードの真ん中の空欄
・カードのような見た目の並びの作り方

#作ってみよう！
##＜①とりあえず文字列から＞
一番作りやすそうなものから作っていきます。

```bingo.rb
def word
  ['B', 'I', 'N', 'G', 'O'].join(' | ')
end

puts word #=>B | I | N | G | O
```
**joinメソッド**で文字列を連結していきます。これで一つの文字列の出来上がりです。
引数が文字同士の区切り文字になります。

##＜②次はランダムな数字の作成＞

```bingo.rb
  B = [1..15]
  I = [16..30]
  N = [31..45]
  G = [46..60]
  O = [61..75]
```

とりあえず、範囲オブジェクトで配列を作成してみました。
これをどうにか順番に出力してカードを作っていきます。

sampleメソッドで範囲オブジェクトの中からランダムに選んでもらおう!

しかし、

##詰まってしまった。

```bingo.rb
  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メソッド```で値が連続する配列を作成していきます。

```bingo.rb
 B = (1..15).to_a

def number
  B.sample
end

puts number => 14 #(1~15の中のランダムな数字)
```

ランダムに出力できました！
ちなみに、

```
(1..15).to_a 
```
は

```bingo.rb
(1..15).to_a #=>[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
```
の意味です。

##この調子で、ランダムな数字続けて作成！？

```bingo.rb
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```のような期待する通りの結果が得られると思っていました・・・
が、しかし、出たのは

```bingo.rb
#=> 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]```という配列にして出力しますと

```bingo.rb
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
```

この形をとりあえず目指して作っていきます。
ここはなんとなく想像はつきます。


**カラの文字列をで調節**していくだけ・・・？？

```bingo.rb
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メソッド```でやってみます。

```bingo.rb
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つ全て同じになります。

**・重複しない、異なるランダムな数字**

が出力されないといけませんね

では、どうしようか。

**ーーーここから結構てこずりましたーーー**

##そもそもが違う？

ランダムな数値の変数として

```bingo.rb
B = (1..15).to_a.sample
#以下省略
```

がありましたが、これでは```B```に格納されている数字は```1~15```の中から選ばれた1つのランダムな数値です。

例えば、```B = 11```で定義されてしまっているならば、その後何度出力しようが、ランダムな数値にはならずに```11```が出力されるだけです。

めちゃ基本的な事ですが、自分でコードを書いてみないと気づかなかった事ですね。

それを踏まえた上でコードを変えていきます。


```bingo.rb
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.ビンゴカードの中心は空白にしなければいけない 

##一度、長ったらしいコードをリファクタリングしよ

```bingo.rb
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メソッドには**引数を使う事で複数のランダムな数値を引き出す**こともできました。

てことなので

```bingo.rb
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メソッド```を使って出力すると

```bingo.rb
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メソッド```や。

```bingo.rb
[B.sample(5),I.sample(5),N.sample(5),G.sample(5),O.sample(5)]
```

この部分ですね。見た目がどうみても悪いです。変えましょう。

```bingo.rb
[B, I, N, G, O].map { |bingo| bingo.sample(5) }
```

よし！変わりました。

##5 * 5のカード形式にもう一度

次はまた見た目ですね。
現時点では、**出力順はできたものの、```5*5```のカード形式**にはなっていません。
ここからはどうするか？

どうやら```\n```のコードで改行ができると。

では実装。

使うメソッドは？

文字列を```|```で区切る際に使った、```joinメソッド```を使うことに。

```bingo.rb
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```を変数に格納してみます。

```bingo.rb
bingos = [B, I, N, G, O].map { |bingo| bingo.sample(5) }.transpose
```

これを、またmapメソッドで順番に吐いていってあげれば```join```を使えるのではないでしょうか？
やってみる。

```bingo.rb
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```の列の```|```の位置を揃える**
**・全力のリファクタリング**

です。

#まとめ
今回実装して思ったことは、

**インプットでメソッドを色々勉強していたけれど全く頭に入っていなかったし、実際に使わないと使っているメソッドのありがたみや、感動がわからないから覚えられない**

ってことでした。

自分で使ってみて初めて分かるものが多かった印象です。

続きはこちら→[【超初心者用】RubyでBINGOカードを作る手順を丁寧すぎるぐらいに書いてみた（2）](https://qiita.com/baby-0105/items/74f43b03d4567f72d71f)