0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-05-17

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

ファイル構成とコードを書いた時に、考えていたこと

  • card.rb(カードクラス)

    • 1枚ごとのカードデータの書き換えができないように(データが壊れないように)、attr_readerにを使った。
  • deck.rb(デッキクラス)

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

    • プレイヤーとディーラーで共通していて、同じデッキを使う為に、クラス変数にデッキのインスタンスを代入して、デッキを使用した。
  • judge.rb(ジャッジクラス)

    • 判定するためのクラス、わかりやすくするために、あえてプレイヤーと別でモジュールを使用した。
  • test.rb(動作確認テスト)

    • minitestは*「プロを目指す人のためのRuby入門」*で、書いて以来、使ったことがなかったので、理解を深める為に使ってみた。
  • main.rb(ブラックジャックを動かすメインファイル)

    • わかりやすいようにゲームの全体の流れのみを最初に書いた
    • そこから必要なクラスとメソッドを考えて、付け足していった。

ルール

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

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
                # 最大値(21)の場合は強制的にスタンドの処理
                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」を入れないと、
テストしてくれないことをすっかり忘れてて、2日くらい悩んだけど笑

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

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?