Rubyでブラックジャック作ってみた(minitest使ってみた)

# Rubyでブラックジャック作ってみた(minitest使ってみた)
### ファイル構成とコードを書いた時に、考えていたこと
- **card.rb（カードクラス）**
    - １枚ごとのカードデータの書き換えができないように（データが壊れないように）、attr_readerにを使った。

- **deck.rb（デッキクラス）**
    - インスタンスを生成した段階で、デッキがシャッフルされているようにし、それをテストした。
    - each文を使って、デッキの生成をなるべく簡潔に書いた。
    - クラス変数を使い、カードをドローしたときに、今、山札の何枚目がドローされているかカウントするようにした。
- **player.rb（プレイヤークラス）**
    - プレイヤーとディーラーで共通していて、同じデッキを使う為に、クラス変数にデッキのインスタンスを代入して、デッキを使用した。

- **judge.rb（ジャッジクラス）**
    - 判定するためのクラス、わかりやすくするために、あえてプレイヤーと別でモジュールを使用した。

- **test.rb（動作確認テスト）**
    - minitestは*「プロを目指す人のためのRuby入門」*で、書いて以来、使ったことがなかったので、理解を深める為に使ってみた。
- **main.rb（ブラックジャックを動かすメインファイル）**
    - わかりやすいようにゲームの全体の流れのみを最初に書いた
    - そこから必要なクラスとメソッドを考えて、付け足していった。

### ルール
- ベッドは無し。（後日実装予定）
- 「A」は「１」と認識（後日、１０に変換できるようにする予定）
- ヒットかスタンドのみ
- トータルスコアが高い方が勝ち


## card.rb (カードクラス)
```card.rb
class Card 
    attr_reader :mark, :number
    def initialize(mark, number)
        @mark   = mark.freeze
        @number = number.freeze
    end

    def convert
        if @number == 'J' || @number == 'Q' || @number == 'K'
            10
        elsif @number == 'A'
            1
        else
            @number.to_i
        end
    end
end
```
 
## deck.rb (デッキクラス)
```deck.rb
require './card'

class Deck
    attr_accessor :cards
    @@draw_count = 0

    def initialize
        @cards = []
        create
        @cards = @cards.shuffle
    end

    def create
        nums = ['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K']
        mark = ['❤︎', '♦︎', '♤', '♧']
        mark.each do |m|
            nums.each do |n|
                @cards << Card.new(m, n)
            end
        end
    end

    def draw
        cards[@@draw_count]
    end

    def draw_count
        @@draw_count += 1
    end

    # テスト用のカード参照メソッド
    def show
        52.times do |n|
            puts "[ #{cards[n].mark} #{cards[n].number} ]"
        end
    end
end

```
## player.rb (プレイヤークラス)
```player.rb
require './deck'
require './judge'


class Player
    include Judge
    attr_accessor :hands, :total_score, :name
    @@deck = Deck.new
    def initialize(name: "プレイヤー")
        @hands = []
        @total_score = 0
        @name = name
    end

    def draw
        self.hands << @@deck.draw
        self.total_score += @@deck.draw.convert
        @@deck.draw_count
    end

    
    def show
        puts "#{self.name}の手札です"
        self.hands.each do |hand|
            puts "[ #{hand.mark} #{hand.number} ]"
        end
        puts "[ トータルスコア #{@total_score} ]"
    end

    # テスト用メソッド

    def test
        puts "テストーーーーーー"
    end

    def total_score_calc
        self.hands.each do |hand|
            @total_score += hand.convert
        end
        #puts "トータル「#{@total_score}」" 
    end

    def reset
        self.hands = []
        self.total_score = 0
    end

    def in_blackjack
        self.hands << Card.new("♦︎", "J")
        self.hands << Card.new("♦︎", "A")
    end
end
```

## judge.rb（ジャッジクラス）
```judge.rb
module Judge
    # def game_judge(target)
    #     if self.total_score > target.total_score
    #         puts "あなたのトータルスコア[ #{self.total_score} ]"
    #         puts "ディーラーのトータルスコア[ #{target.total_score} ]"
    #         puts 'あなたの勝ちです'
    #     elsif self.total_score == target.total_score
    #         puts "あなたのトータルスコア[ #{self.total_score} ]"
    #         puts "ディーラーのトータルスコア[ #{target.total_score} ]"
    #         puts '引き分けです'
    #     else
    #         puts "あなたのトータルスコア[ #{self.total_score} ]"
    #         puts "ディーラーのトータルスコア[ #{target.total_score} ]"
    #         puts 'あなたの負けです'
    #     end
    # end

    def blackjack?
        if self.hands[1].number == "A" || self.hands[0].number == "A" 
            if  self.hands[0].number == "J" || self.hands[0].number == "Q"|| self.hands[0].number == "K" || \
                self.hands[1].number == "J" || self.hands[1].number == "Q"|| self.hands[1].number == "K"
                self.total_score = "ブラックジャック"
                true
            else
                false
            end
        else
            false
        end
    end

    def burst?
        if self.total_score > 21
            true
        else
            false
        end
    end
end
```

## test.rb（動作確認テスト）
```test.rb
require 'minitest/autorun'
require './player'
class BlackJackTest < Minitest::Test
    # テストカードのマークは「 ♦︎ 」で統一
    @@test_J      = Card.new('♦︎', 'J')
    @@test_Q      = Card.new('♦︎', 'Q')
    @@test_K      = Card.new('♦︎', 'K')
    @@test_A      = Card.new('♦︎', 'A')
    @@test_5      = Card.new('♦︎', 5)
    @@test_10     = Card.new('♦︎', 10)
    @@test_player = Player.new

    def test_blackjack?
        blackjack_player = Player.new
        blackjack_player.hands << @@test_A
        blackjack_player.hands << @@test_J
        assert blackjack_player.blackjack?

        blackjack_player.reset
        blackjack_player.hands << @@test_J
        blackjack_player.hands << @@test_A
        assert blackjack_player.blackjack?

        blackjack_player.reset
        blackjack_player.hands << @@test_Q
        blackjack_player.hands << @@test_A
        assert blackjack_player.blackjack?

        blackjack_player.reset
        blackjack_player.hands << @@test_A
        blackjack_player.hands << @@test_Q
        assert blackjack_player.blackjack?

        blackjack_player.reset
        blackjack_player.hands << @@test_A
        blackjack_player.hands << @@test_K
        assert blackjack_player.blackjack?

        blackjack_player.reset
        blackjack_player.hands << @@test_K
        blackjack_player.hands << @@test_A
        assert blackjack_player.blackjack?

        # false パターン
        # blackjack_player.reset
        # blackjack_player.hands << @@test_A
        # blackjack_player.hands << @@test_10
        # assert blackjack_player.blackjack?

        # blackjack_player.reset
        # blackjack_player.hands << @@test_5
        # blackjack_player.hands << @@test_5
        # assert blackjack_player.blackjack?

        # blackjack_player.reset
        # blackjack_player.hands << @@test_J
        # blackjack_player.hands << @@test_Q
        # assert blackjack_player.blackjack?
        

    end

    def test_burst?
        burst_player = Player.new
        burst_player.hands << @@test_J
        burst_player.hands << @@test_J
        burst_player.hands << @@test_5
        burst_player.total_score_calc
        burst_player.total_score
        assert burst_player.burst?
        # false
        # burst_player.reset
        # burst_player.hands << @@test_10
        # burst_player.total_score
        # assert burst_player.burst?
    end

    def test_total_score_calc
        total_score_calc_player = Player.new
        total_score_calc_player.hands << @@test_10
        total_score_calc_player.hands << @@test_5
        total_score_calc_player.total_score_calc
        assert_equal 15, total_score_calc_player.total_score
        # false
        #assert_equal 10, total_score_calc_player.total_score
    end

    def test_convert
        assert_equal 10, @@test_J.convert
        assert_equal 10, @@test_Q.convert
        assert_equal 10, @@test_K.convert
        assert_equal 1, @@test_A.convert
        assert_equal 5, @@test_5.convert
    end

    def test_draw_count
        deck = Deck.new
        assert_equal 1, @@test_player.draw
        assert_equal 2, @@test_player.draw
        assert_equal 3, @@test_player.draw
    end
end

```

## main.rb（ブラックジャックを動かすメインファイル）
```main.rb
require './player'

lose_message =<<~TEXT
    あなたの負けです
TEXT
win_message =<<~TEXT
    あなたの勝ちです
TEXT
game_draw_message =<<~TEXT
    引き分けです
TEXT
error_message = "無効な文字です。もう一度入力してください"


class Play
    attr_accessor :player, :dealer

    def initialize
        @player = Player.new
        @dealer = Player.new(name: "ディーラー")
    end

    def test
        @player.draw
        @player.draw
        @player.show
    end
end

while true # 再プレイのためのループ処理
    # 初ターンの処理
    player = Player.new
    dealer = Player.new(name: "ディーラー")
    puts "ようこそ、ブラックジャックへ"
    puts "カードを配ります"
    player.draw
    player.draw
    dealer.draw  
    dealer.show
    # ブラックジャックテスト
    # dealer.reset
    # dealer.in_blackjac 
    # dealer.show
    # player.reset
    # player.in_blackjack
    # player.show

    # プレイヤーがブラックジャックじゃないか確認の処理
    if player.blackjack?
            puts "ブラックジャック！"
            player.show
    else
        player.show
        puts h_or_s_message =<<~TEXT
        選択してください
        Push「h」, ヒット
        Push「s」, スタンド
        TEXT
    end

    # ヒット、スタンドの処理（プレイヤーのターン）
    while true
        # ブラックジャックの場合は強制的にスタンドさせる処理
        if player.blackjack?
            command = 's'
        else
            command = gets.chomp # ヒットかスタンドか選択
        end
        # ヒットの場合の処理
        if command == "h"
            while true
                player.draw
                if player.burst? # バーストしていないか確認
                    player.show
                    puts "バースト！！"
                    puts lose_message
                    break
                end
                # ドロー後のトータルスコアの確認
                player.show
                # 最大値（２１）の場合は強制的にスタンドの処理
                if player.total_score == 21
                    puts 'トータルスコアが最大値に達しました'
                    command = 's'
                    break
                end
                # 最大値ではない場合は、再度ヒットするか処理
                puts "もう一度引きますか？"
                puts h_or_s_message
                while true
                    command = gets.chomp # 再度、ヒットかスタンドの選択
                    if command == 'h'
                        break
                    elsif command == 's'
                        break
                    else
                        puts error_message
                        redo
                    end
                end
                # ヒットかスタンドの処理
                if command == 'h' 
                    redo
                elsif command == 's'
                    break
                end
            end
            # バースト時の強制終了処理
            if player.burst?
                break
            end
        else
            # h,s以外が入力されたときの処理
            puts error_message
            redo
        end
        # スタンド時の処理（ディーラーのターン）
        if command == 's'
            # プレイヤーがブラックジャックだった場合のディーラーの処理 
            if player.blackjack?
                dealer.draw
                dealer.show
                if !dealer.blackjack?
                    puts win_message
                    break
                else
                    puts game_draw_message
                    break
                end
            end
            # プレイヤーに勝つまでディーラーはヒットし続ける処理
            while player.total_score > dealer.total_score
                dealer.draw
                dealer.show
                if dealer.burst?
                    puts 'バースト！'
                    puts win_message
                    break
                elsif dealer.total_score == 21 && player.total_score == 21
                    break
                end
            end
            if dealer.burst? # バーストしていた場合処理処理終了
                break
            elsif dealer.total_score == 21 && player.total_score == 21 # お互い最大値に達していないか確認
                puts game_draw_message
                break
            else
                puts lose_message
                break
            end
        end
    end
    # 再プレイの処理
    puts <<~TEXT
    もう一回遊びますか？
    選択してください
    Push「h」, はい
    Push「s」, いいえ
    TEXT
    while true
        command = gets.chomp # 再プレイするか選択
        if command == "h"
            command = nil
            break
        elsif command == "s"
            break
        else
            puts error_message
            redo
        end
    end
    # 再プレイの処理
    if command == nil
        redo
    else
        break
    end
end
```

### 学べたこと
*「プロを目指す人のためのRuby入門」*で
「なぜこんなことをするんだろう」、
「いつこれは使うのだろう」、
と、思ってたことが、使うタイミングがよくわかった。

最初はminitestは使わず「p」を使ってテストしていたが、
使った方が作業が早くなるということを理解した。

minitestのメソッド名の最初の単語に「test」を入れないと、
テストしてくれないことをすっかり忘れてて、２日くらい悩んだけど笑

使ったことないものは積極的に使っていくと理解が深まる。

まだリファクタリングできるところは、たくさんあるんだけど、
作るの飽きそうになったので、一旦、Qiita書きました。