Ruby初心者問題を解いてみる（No.1~No.3）

## はじめに
RubyについてProgateで勉強したので、実際の問題を伊藤さんのサイトにある
[アウトプットのネタに困ったらこれ！？Ruby初心者向けのプログラミング問題を集めてみた（全10問）](https://blog.jnito.com/entry/2019/05/03/121235)
を解きました。各3問ずつ×3回で発信します。

備忘録のために、解答のソースだけでなく考え方も記入しています。
もしソースコードを確認して頂き改善点等あればコメントいただくと幸いです。

## 問題1 (カレンダー作成問題)
> [Dateクラス](https://docs.ruby-lang.org/ja/latest/class/Date.html)を使って、今月の１日と月末の日付と曜日を求め、次のような形式でカレンダーを表示する

>```
 　　 April 2013
 Su Mo Tu We Th Fr Sa
     1  2  3  4  5  6
  7  8  9 10 11 12 13
 14 15 16 17 18 19 20
 21 22 23 24 25 26 27
 28 29 30
```

この問題は、以下のように考えて解いていきました。

### 1. Dateクラスを利用する
・既存のクラスを利用する場合は ```require "クラス名"``` 

### 2. クラスを利用したインスタンスの作成
・インスタンスの作成は ```クラス名.new(引数)```

### 3.文字列の整列
・```April 2013```の表示は全長20文字の中央揃えで表示する
 →　```表示する文字.center(20)```

・```Su Mo Tu We Th Fr Sa```の表示
① 文字列で```Su Mo Tu We Th Fr Sa```と定義し表示
② 配列で```["Su","Mo","Tu","We","Th","Fr","Sa"]```と定義し、
　表示の際、文字列に変換する
 → 今回②を採用する。その際に「""」を何度も定義するのは、面倒なので省略した書き方を使用する

%W記法について
https://www.sejuku.net/blog/46939#i

・日付を2文字右揃えで表示する
 →　```表示する文字.rjust(2)```

### 4.日付の取得
・実行日時を基準とし、Dateクラスに用意されているメソッドを使用
以下のサイトを参考としました。
https://www.javadrive.jp/ruby/date_class/index8.html
https://docs.ruby-lang.org/ja/latest/class/Date.html#I_CWDAY

1.年の取得
2.月名の取得
3.月初日の取得
4.月初日の曜日の取得
5.月末日の取得

### 5.日付の表示処理
1.月初の曜日まで空白を表示する

#### 初日～末日までの表示で以下を繰り返す
2.表示を2文字表示の右づめにする（一桁の数字：「 1」、二桁の数字「10」）
3.1日ごと空白「" "」を用意する
4.土曜日まで日数を入力すると改行する

### 作成したソースコード
```ruby:calender.rb
# dateクラスの定義
require "date"

# 配列を定義
# 要素を何度も「""」でくくるのは面倒なので、%w記法を採用
youbi = %w[Su Mo Tu We Th Fr Sa]

today = Date.today
first_day = Date.new(today.year, today.month , 1);
last_day = Date.new(today.year, today.month , -1); 

#dateクラスのstrftimeで、本日の年、月名を取得して表示する
puts today.strftime("%B %Y").center(20)
puts youbi.join(" ")

# 1日目の日付を記載するための空白を設ける
# 曜日を数値（0~6）に変換するメソッドを利用して
# 1日分の記載量 × 数値で空白を決める
space = "   " * first_day.wday
count = first_day.wday
print space

# カレンダーの日数を初日～末日まで記載するための繰り返し文を作成する
# 繰り返し時 開始数値..終了数値を記載する
range = first_day.day..last_day.day
range.each do |day|

# 右詰めで日付を1日ずつ表示
# 土曜日まで記載すると改行
    print day.to_s.rjust(2)
    print " "
    count = count + 1
    if (count % 7 == 0)
        print("\n")
    end
end
```
## 問題1の気づき
・表示文字のそろえ方（中央揃え、右揃えなど）
・配列の%w記法
・each文の範囲の指定方法

## 問題2 (カラオケマシン問題)
> カラオケで歌うときによく使う、「キーの上げ下げ」をプログラム上でシミュレートする

この問題は、以下のように考えて解いていきました。
まずはクラスを使用せずに作成する。

### 1. 音階のパターンを抽出
・音階は「ド」～「シ#」の12種類

### 2. 配列の処理
・文字列を分解する
```配列名 = 文字列.split("")```

・配列への値の追加
```配列名.push(値)```

・配列の値を文字列に変換
```配列名.join```

### 3. 音階の変換処理
1.入力された音階（#、スペース、|）すべて1つずつ分解する
2.音階を配列の要素番号と紐づける（入力数値でキーを上げ下げするため）
3.キーの上げ下げの数値を入力する（音階は12段階のため -11 ～ +11 ）
4.1音ずつ手順2の音階 ＋ キー数値 で変換した音階の要素を格納

### 作成したソースコード
```ruby:music.rb
# 入力されるメロディ
melody = "C D E F |E D C   |E F G A |G F E   |C   C   |C   C   |CCDDEEFF|E D C   "
array = melody.split("")

# 音階
key = %w[C C# D D# E F F# G G# A A# B]

# メロディ（音階 → 数値変換用と音階変調用）
middle_melody = []
new_melody = []

# 1音ずつ → 対応する数値に変換
array.each do |n|
    if n == "#"
        middle_melody.push( middle_melody.pop + "#")
    else
        middle_melody.push(n)
    end
end

# キーチェンジの数値の入力（-11 ～ 11の入力）
puts "キーチェンジの数値を入力してください (-11 ～ 11)"
key_change = gets.to_i

while key_change < -11 || key_change > 11 do
    puts "範囲外です。^nキーチェンジの数値を入力してください (-11 ～ 11)"
    key_change = gets.to_i 
end

# 入力数値に従って変換する
middle_melody.each do |n|
    if n == " " || n == "|"
        new_melody.push(n)
    else 
        onkai = key.find_index(n).to_i + key_change
        if onkai >= 12
            onkai = onkai - 12
        end
        new_melody.push(key[onkai])
    end
end

# 配列を文字列に変換
puts new_melody.join
```
### 4. クラスを使用して作成する
・クラスの雛形
class クラス名
　　initialize
　　メソッド1
　　メソッド2
end

※ initializeメソッドは
　 newされた際に一度だけ自動的に実行されるメソッド

### 5. クラス内の変数のスコープ
・インスタンスごとの共有変数は@を付ける
```@変数名 = 何らかの値```

https://qiita.com/mogulla3/items/cd4d6e188c34c6819709

### 作成したソースコード
```ruby:music2.rb
# クラス定義
class KaraokeMachine  

  # initializeメソッド
  def initialize(melody)
    array = melody.split("")
    @middle_melody = []
    array.each do |n|
      if n == "#"
          @middle_melody.push( middle_melody.pop + "#")
      else
          @middle_melody.push(n)
      end
    end
  end

  # 音階変換メソッド
  def transpose(amount)
    key = %w[C C# D D# E F F# G G# A A# B]
    key_change = amount
    new_melody = []
    @middle_melody.each do |n|
      if n == " " || n == "|"
          new_melody.push(n)
      else 
          onkai = key.find_index(n).to_i + key_change
          if onkai >= 12
            onkai = onkai - 12
          end
          new_melody.push(key[onkai])
      end
    end
    print new_melody.join
  end
end

# 入力されるメロディ
melody = "C D E F |E D C   |E F G A |G F E   |C   C   |C   C   |CCDDEEFF|E D C   "

# インスタンスを作成。音階の変換を実施
karaoke = KaraokeMachine.new(melody)
karaoke.transpose(9)
```
## 問題2の気づき
・配列の扱い方

## 問題3（ビンゴゲーム問題）
> 1から75までの数字をランダムに配置して、ビンゴカードを作成する

>```
 B |  I |  N |  G |  O
13 | 22 | 32 | 48 | 61
 3 | 23 | 43 | 53 | 63
 4 | 19 |    | 60 | 65
12 | 16 | 44 | 50 | 75
 2 | 28 | 33 | 56 | 68
```

この問題は、以下のように考えて解いていきました。

## 1. case文
・case 変数
　when 値1 then
　when 値2 then

## 2. BINGOカード作成の手順
1."BINGO"の表示
2.それぞれの列で、値が重複しないようループを実施
3.表示がずれないように数字の表示（2桁）を右詰めする

```ruby:bingo.rb
# 表示のマスは空白を含め25箇所
range = 1..25 
B = []
I = []
N = []
G = []
O = []

# BINGOを表示
puts " B |  I |  N |  G |  O |"

# B,I,N,G,Oの列でそれぞれ
# 数字が重複しないようにrand関数で
# 値の割り出しを繰り返す
range.each do |count|
    case count % 5
    when 1 then
        num = rand(1..15)
        while B.include?(num) do
            num = rand(1..15)
        end
        B.push(num) 
        print num.to_s.rjust(2)
        print " | "   

    when 2 then
        num = rand(16..30)
        while I.include?(num) do
            num = rand(16..30)
        end
        I.push(num)
        print num.to_s.rjust(2)
        print " | "  

    when 3 then
        if count == 13 
            print "   | "
            next
        else
            num = rand(31..45)
            while N.include?(num) do
                num = rand(31..45)
            end
            N.push(num)
        end
        print num.to_s.rjust(2)
        print " | "  

    when 4 then
        num = rand(46..60)
        while G.include?(num) do
            num = rand(46..60)
        end
        G.push(num)
        print num.to_s.rjust(2)
        print " | "  

    else
        num = rand(61..75)
        while O.include?(num) do
            num = rand(61..75)
        end
        O.push(num)
        puts num.to_s.rjust(2) 
    end
end
```

## 3. クラスを使用して作成する
・bingoクラスを作成、今回initializeメソッドは不要

## 4. ランダムで表示する値を簡潔に記載する
・配列の要素を数個ランダムに返す
配列名.sample(個数)

https://ref.xaio.jp/ruby/classes/array/sample

```ruby:bingo2.rb
class Bingo

    def make_bingo

        range = 0..4

        # BINGOを表示
        puts " B |  I |  N |  G |  O |"

        b = (1..15).to_a.sample(5)
        i = (16..30).to_a.sample(5)
        n = (31..45).to_a.sample(5)
        n[2] = "  "
        g = (46..60).to_a.sample(5)
        o = (61..75).to_a.sample(5)

        range.each do |count|
            [b,i,n,g,o].each do |row|
                print row[count].to_s.rjust(2)
                print " | " 
                
                if row == o
                    printf "\n"
                end
            end
        end    
    end
end

b = Bingo.new
b.make_bingo
```

## おわりに
私のまとめが参考になれば幸いです。
以上です。