Ruby コンソールゲーム　BlackJack

# Rubyで動くコンソールゲーム作ってみた！
今回Ruby学習の中でブラックジャックゲームを作成したので自身のために残しておきます！
オブジェクト指向についての知識がないまま作ってしまったので、
私と同じく初学者の方は下記コードをリファクタリングしてみてください！
私自身リファクタリングしたい部分たくさんありますので、修正後のコードは後日またあげます！

今回特に悩んだところはカードの定義です。
トランプのカードに対してキーに絵柄、そしてその中にさらに文字列とした各数字をキーにスコアを値として入れています。おそらく配列としてもっと簡単に定義することができたという反省点ありますが、、、


## ルール
- 開始時にディーラーとプレイヤーに2枚ずつカードが配られる。
- プレイヤーは合計値が21に近づくようにカードを追加するか決めることができる。
- カードの合計値が21を超えてしまった時点で負け。
- プレイヤーはカードの合計値が21を超えなければ何度でも引くことができる。
- ディーラーはカードの合計値が17を超えるまで引き続ける。
- 10,J,Q,Kは10点
- Aは1点もしくは11点として手札の合計値が21に最も近い方を採用する。

## 現状の改善点
- プレイヤーとディーラーは共通メソッド多いのでプレイヤーを親クラスにし継承する。
- ジャッジの責務がゲームマスタークラスに一部入り込んでしまっているので修正。
- プレイヤー人数の増加に全く対応できていない。
- ジャッジクラスに責務がしっかり持たすことができていない。
- バーストの判定をモジュールを使用し無理やり行っている点。


## ファイル一覧

### game_master.rb
```game_master.rb
require './deck'
require './player'
require './dealer'
require './judge'

#ゲームの全体統括
class GameMaster
    include JudegModule

    def initialize
        #52枚のカード作成してデッキ作る。ドローはデッキから行う。
        @card = Card.new
        @deck = Deck.new

        # Deckクラスのインスタンスを渡して参照できるようにしている。
        @player = Player.new(@deck) #デッキからみたいなイメージ
        @dealer = Dealer.new(@deck)

        # プレイヤーとディーラーのインスタンスを渡す。バーストとかの条件分岐はここでする。
        @judge = Judge.new(@player, @dealer)

        start
        player_draw_turn
    end

    def start
        puts '----------------------------------------------'
        puts 'ブラックジャックを開始します。'

        #プレイヤーの最初の処理
        @player.first_draw
        @player.second_draw

        #ディーラーの最初の処理
        @dealer.first_draw
        @dealer.second_draw
    end

    #プレイヤーのヒットするかどうかの判断とその繰り返しメソッド呼び出す。バーストしたら終了に分岐。
    def player_draw_turn
        score = @player.select_thing
        if burst?(score)
            puts 'バーストしました。あなたの負けです。'
            end_turn
        else
            dealer_draw_turn
        end
    end

    #プレイヤーのヒットが終わったら呼び出される。バーストしなければ得点の比較に入るジャッジターンを呼び出す。
    def dealer_draw_turn
        @dealer.show_second_draw
        score = @dealer.total_score
        if burst?(score)
            puts 'バーストしました。'
            puts 'プレイヤーの勝ちです。'
            end_turn
        else
            judge_turn
        end
    end

    #ジャッジクラスの勝敗決めを呼び出す。
    def judge_turn
        @judge.game_judge
        end_turn
    end

    def end_turn
        puts 'ブラックジャックゲームを終了します。'
        puts '----------------------------------------------'
    end
end

GameMaster.new
```

### player.rb
```player.rb
require './judge'

#ゲームをプレイする人物
class Player
    include JudegModule

    # Deckクラスのインスタンスを受け取る
    def initialize(deck)
        @deck = deck
        @total_score = 0
        @current_score = 0
    end

    #1枚目のドロー
    def first_draw
        @deck.draw
        add_score
        puts "あなたの引いたカードは#{@deck.selected_type}の#{@deck.selected_number}です。"
    end

    #2枚目のドロー
    def second_draw
        @deck.draw
        add_score
        puts "あなたの2枚目に引いたカードは#{@deck.selected_type}の#{@deck.selected_number}です。"
      #current_score #2枚目引いた時点でcurrent_scoreに数字入っているのでここで返している。
    end

    #ドローするかどうかの判断
    def select_thing
        loop do
            puts "あなたの現在の得点は#{@current_score}です。カードを引きますか？（Y/N）"
            selected = gets.chomp.upcase
            case selected
            when 'Y'
                hit
                break
            when 'N'
                break
            else
                puts 'Y(yes)もしくはN(no)を選択してください。'
            end
        end
        #ゲームマスタークラスで条件分岐するためにトータルスコア返している
        total_score
    end

    #追加ドロー（ヒット）する場合の処理
    def hit
        @deck.draw
        add_score
        puts "あなたの引いたカードは#{@deck.selected_type}の#{@deck.selected_number}です。"
        puts "あなたの現在の得点は#{@current_score}です"
        re_hit
    end

    #現在のスコア
    def total_score
        @current_score
    end

    #追加ドロー（ヒット）した後にスコアの評価し判断する。
    def re_hit
        return 'Burst' if burst?(total_score)

        select_thing
    end

    #current_scoreにドローしたスコアをプラスする。かつ、プラスする際にAを含んでいれば21以内で最大値になる方（1or11）にするメソッド
    def add_score
        @current_score += @deck.selected_score
        return unless ['A'].include?(@deck.selected_number) && @current_score <= 11

        @current_score += 10
    end
end
```


### dealer.rb
```dealer.rb
#ディーラーの処理
class Dealer
    include JudegModule

    # Deckクラスのインスタンスを受け取る
    def initialize(deck)
        @deck = deck
        @total_score = 0
        @current_score = 0
    end

    #1枚目のドロー
    def first_draw
        @deck.draw
        add_score
        puts "ディーラーの引いたカードは#{@deck.selected_type}の#{@deck.selected_number}です。"
    end

    #2枚目のドロー
    def second_draw
        @deck.draw
        add_score
        #1度セカンドドロー変数に入れておいて開示のタイミングで変数取れるようにする。
        @second_draw_type = @deck.selected_type
        @second_draw_number = @deck.selected_number
        puts 'ディーラーの2枚目のカードはわかりません。'
    end

    #2枚の合計点数
    attr_reader :current_score

    #2枚目のカード公開し、合計点数出す。
    def show_second_draw
        puts "ディーラーの引いた2枚目のカードは#{@second_draw_type}の#{@second_draw_number}でした。"
        puts "ディーラーの現在の得点は#{@current_score}です。"
        add_hit
    end

    #現在のスコア
    def total_score
        @current_score
    end

    #17以下の場合。追加でドローするかどうかの判断
    def add_hit
        return unless @current_score < 17

        hit

        total_score
    end

    #追加ドロー（ヒット）する場合の処理
    def hit
        @deck.draw
        add_score
        puts "ディーラーの引いたカードは#{@deck.selected_type}の#{@deck.selected_number}です。"
        puts "ディーラーの現在の得点は#{@current_score}です。"
        re_hit
    end

    #追加ドロー（ヒット）した後にスコアの評価し判断する。
    def re_hit
        return if burst?(total_score)

        add_hit
    end

    #current_scoreにドローしたスコアをプラスする。かつ、プラスする際にAを含んでいれば21以内で最大値になる方（1or11）にするメソッド
    def add_score
        @current_score += @deck.selected_score
        return unless ['A'].include?(@deck.selected_number) && @current_score <= 11

        @current_score += 10
    end
end
```

### judge.rb
```judge.rb
#ゲームの判定をするクラス

require './player'

#バーストの判定するモジュール
module JudegModule
    def burst?(score)
        score >= 22
    end
end

#ゲームの判定をするクラス
class Judge
    def initialize(player, dealer)
        @player = player
        @dealer = dealer
    end

    def game_judge
        puts "あなたの得点は#{@player.total_score}です。"
        puts "ディーラーの得点は#{@dealer.total_score}です。"
        if @player.total_score > @dealer.total_score
            puts 'あなたの勝ちです！'
        elsif @player.total_score == @dealer.total_score
            puts '引き分けです。'
        else
            puts 'ディーラーの勝ちです！'
        end
    end
end
```

### deck.rb
```deck.rb
require './card'

#52枚の中からランダムに1枚ドローするデッキクラス
class Deck < Card
    attr_reader :selected_type, :selected_number, :selected_score

    def draw
        if @cards.empty?
            puts 'カード切れ'
            raise
        else
            #@cards = {"type" => {"numbers" => value数値},"type" => {"numbers" => value数値}...}の形で4種類あるのでまずtypeの部分のキーでランダムに取り出す。
            @selected_type = @cards.keys.sample

            #上でtypeキー選んだので文字列でキーとして入っているnumberの部分を抽選
            @selected_number = @cards[selected_type].keys.sample

            #2つのキーを抽選した変数できているのでその値を返す数値つまりスコアを変数に入れる。
            @selected_score = @cards[selected_type][selected_number]

            # 選択されたカードを削除する。
            @cards[@selected_type].delete(@selected_number)

            #絵柄の中の数値が空であれば絵柄を削除する。
            @cards.delete(@selected_type) if @cards[@selected_type].empty?
        end
    end
end
```

### card.rb
```card.rb
#52枚のカードを作るクラス
class Card
  attr_reader :cards

  def initialize
    #インスタンス生成時に初期値として入れられるのがインスタンス変数。今回はnewした時に引数取らないのでメソッドのとこは引数なし。その代わりそれぞれ配列など入れてる。
    @types = ['クローバー', 'ダイアモンド', 'ハート', 'スペード']
    @numbers = ['A', *(2..10).to_a.map(&:to_s), 'J', 'Q', 'K']
    @cards = create_card(@types, @numbers)
  end

  #52枚のカードの定義
  def create_card(types, numbers)
     cards = {}
     types.each do |type|
       cards[type] = {} #cards = {"club" => {}, "heart" => {}, "spade" => {}, "diamond" => {} }の状態になっている。
       numbers.each do |number| #まずキーとしてnumbersをeachで回す。下でそのバリューにあたる値をif等で作成。
         card_value = number.to_i # 数字の場合はそのまま値を使う
         card_value = 10 if ['J', 'Q', 'K'].include?(number) # card_valueにJ,Q,Kどれか含まれていたらJ,Q,Kは10の値を持つ
         card_value = 1 if ['A'].include?(number) #Aは1の値を持つ
         #{"type" => {"numbers" => value数値},"type" => {"numbers" => value数値}...}の形
         cards[type][number] = card_value
       end
     end
     cards
  end
end
```

以上になります！
まだまだ学習中の身なのでおかしな部分多々あるかと思いますがご容赦ください。
引き続き頑張ります！