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

#はじめに

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

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

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

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

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

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

ちなみに(1)の[【超初心者用】RubyでBINGOカードを作る手順を丁寧すぎるぐらいに書いてみた（１）](https://qiita.com/baby-0105/items/fd717a05e435c656ccc3)が最初です。

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

#前回の時点

```
 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```が真ん中なので。

```bingo.rb
bingos[2][2] = "  " 
```

とします。

```bingo.rb
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```**を使ってあげます。
これは、右詰めにした文字列を返すメソッドです。

これで完成まで出来そうですね。

と思ったのですが、、、

```bingo.rb
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```で長さが２の文字列を右ずめにしようとしましたが、```11 | 28 | 33 | 56 | 66```は2どころか10以上の1つの文字列が出来てしまっているため、効きませんでした。

それを踏まえた上で修正します。

```bingo.rb
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)```を分けてみました。

何とか、これでやっと完成ですね！！！

```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 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
```

これが現時点でのソースコードですが、

まだまだ、リファクタリングのしがいがありそうです。

#リファクタリングしていく

まずは、

```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
```

の部分ですね。
まとめていきます。

この変数たちは、『15個の数字の配列』を示しているわけですから。
何か良い方法でまとめられそうです。

##```each_sliceメソッド```を使う
each_sliceメソッドは**配列を分割するのに使うメソッド**です。

これを上手く使っていきます。

```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

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

こちらが現時点のコード。これをeach_sliceメソッドを使って変更していく。

これが何とも見事。

```bingo.rb
bingos = (1..75).each_slice(15).map { |bingo| bingo.sample(5) }.transpose
```

6行から1行へとこんなにも短くなりました！

##空白+文字列（' '+文字列）で作った見た目の悪い配列をどうにかする

```bingo.rb
def word
  [' B', ' I', ' N', ' G', ' O'].join(' | ')
end
```
これですね。```' B'```では、空白+文字列で無理やり文字を作りました。
どうにかしていきます。

###```charsメソッド```を使います（```%w()```でも出来ます）

これは、**文字列の文字を一文字ずつ配列へと返すメソッド**です。

```sample.rb
"banana".chars #=> ["b", "a", "n", "a", "n", "a"]
```

これを使います。

```bingo.rb
["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メソッド```です

ちなみに現在のコードは

```bingo.rb
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```をまとめます。

そして、変数の名前も分かりやすいように変更していきます。

出来たものがこちら。

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

完成です。お疲れ様でした。