I made blackjack with Ruby (I tried using minitest)

I made blackjack with Ruby (I tried using minitest)

What I was thinking when I wrote the file structure and code

-** card.rb (card class) ** --I used attr_reader so that the card data for each card cannot be rewritten (so that the data is not corrupted).

-** deck.rb (deck class) ** --At the stage of instantiation, I made sure the deck was shuffled and tested it. --I wrote the deck generation as concisely as possible using each statement. --Using class variables, when you draw a card, it now counts what number of the deck is being drawn. -** player.rb (player class) ** --Common to players and dealers, in order to use the same deck, I assigned an instance of the deck to a class variable and used the deck.

-** judge.rb (judge class) ** --The class for judging, for the sake of clarity, I dared to use a module separately from the player.

-** test.rb (operation check test) ** --minitest is * "Introduction to Ruby for those who want to become a professional" , and I haven't used it since I wrote it, so I used it to deepen my understanding. -* main.rb (main file that runs blackjack) ** --For clarity, I wrote only the overall flow of the game first ――From there, I thought about the necessary classes and methods and added them.

rule

--No bed. (Planned to be implemented at a later date) --"A" is recognized as "1" (will be converted to 10 at a later date) --Hit or stand only ――The one with the higher total score wins

card.rb (card class)

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 class)

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

    #Card reference method for testing
    def show
        52.times do |n|
            puts "[ #{cards[n].mark} #{cards[n].number} ]"
        end
    end
end

player.rb (player class)

player.rb


require './deck'
require './judge'


class Player
    include Judge
    attr_accessor :hands, :total_score, :name
    @@deck = Deck.new
    def initialize(name: "player")
        @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}Is in your hand"
        self.hands.each do |hand|
            puts "[ #{hand.mark} #{hand.number} ]"
        end
        puts "[Total score#{@total_score} ]"
    end

    #Test method

    def test
        puts "Test --------"
    end

    def total_score_calc
        self.hands.each do |hand|
            @total_score += hand.convert
        end
        #puts "total"#{@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 class)

judge.rb


module Judge
    # def game_judge(target)
    #     if self.total_score > target.total_score
    #         puts "Your total score[ #{self.total_score} ]"
    #         puts "Dealer total score[ #{target.total_score} ]"
    #         puts 'You win'
    #     elsif self.total_score == target.total_score
    #         puts "Your total score[ #{self.total_score} ]"
    #         puts "Dealer total score[ #{target.total_score} ]"
    #         puts 'It's a draw'
    #     else
    #         puts "Your total score[ #{self.total_score} ]"
    #         puts "Dealer total score[ #{target.total_score} ]"
    #         puts 'You lose'
    #     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 = "black Jack"
                true
            else
                false
            end
        else
            false
        end
    end

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

test.rb (operation check test)

test.rb


require 'minitest/autorun'
require './player'
class BlackJackTest < Minitest::Test
    #The test card mark is unified with "♦ ︎"
    @@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 pattern
        # 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 file that runs blackjack)

main.rb


require './player'

lose_message =<<~TEXT
You lose
TEXT
win_message =<<~TEXT
You win
TEXT
game_draw_message =<<~TEXT
It's a draw
TEXT
error_message = "Invalid character. Type it again"


class Play
    attr_accessor :player, :dealer

    def initialize
        @player = Player.new
        @dealer = Player.new(name: "dealer")
    end

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

while true #Loop processing for replay
    #First turn processing
    player = Player.new
    dealer = Player.new(name: "dealer")
    puts "Welcome to Blackjack"
    puts "Distribute cards"
    player.draw
    player.draw
    dealer.draw  
    dealer.show
    #Blackjack test
    # dealer.reset
    # dealer.in_blackjac 
    # dealer.show
    # player.reset
    # player.in_blackjack
    # player.show

    #Processing to confirm that the player is not blackjack
    if player.blackjack?
            puts "black Jack!"
            player.show
    else
        player.show
        puts h_or_s_message =<<~TEXT
Please select
        Push「h」,hit
        Push「s」,stand
        TEXT
    end

    #Hit, stand processing (player's turn)
    while true
        #In the case of blackjack, the process of forcibly standing
        if player.blackjack?
            command = 's'
        else
            command = gets.chomp #Choose hit or stand
        end
        #Processing in case of hit
        if command == "h"
            while true
                player.draw
                if player.burst? #Check if there is a burst
                    player.show
                    puts "burst! !!"
                    puts lose_message
                    break
                end
                #Confirmation of total score after draw
                player.show
                #In the case of the maximum value (21), the stand is forcibly processed.
                if player.total_score == 21
                    puts 'The total score has reached the maximum'
                    command = 's'
                    break
                end
                #If it is not the maximum value, hit again or process
                puts "Would you like to pull it again?"
                puts h_or_s_message
                while true
                    command = gets.chomp #Again, hit or stand selection
                    if command == 'h'
                        break
                    elsif command == 's'
                        break
                    else
                        puts error_message
                        redo
                    end
                end
                #Hit or stand handling
                if command == 'h' 
                    redo
                elsif command == 's'
                    break
                end
            end
            #Forced termination processing at the time of burst
            if player.burst?
                break
            end
        else
            # h,Processing when input other than s
            puts error_message
            redo
        end
        #Processing at stand (dealer's turn)
        if command == 's'
            #Dealer handling when player is blackjack
            if player.blackjack?
                dealer.draw
                dealer.show
                if !dealer.blackjack?
                    puts win_message
                    break
                else
                    puts game_draw_message
                    break
                end
            end
            #The dealer keeps hitting until he wins the player
            while player.total_score > dealer.total_score
                dealer.draw
                dealer.show
                if dealer.burst?
                    puts 'burst!'
                    puts win_message
                    break
                elsif dealer.total_score == 21 && player.total_score == 21
                    break
                end
            end
            if dealer.burst? #If there is a burst, processing ends
                break
            elsif dealer.total_score == 21 && player.total_score == 21 #Check if each other has not reached the maximum value
                puts game_draw_message
                break
            else
                puts lose_message
                break
            end
        end
    end
    #Replay processing
    puts <<~TEXT
Would you like to play again?
Please select
    Push「h」,Yes
    Push「s」,No
    TEXT
    while true
        command = gets.chomp #Choose to replay
        if command == "h"
            command = nil
            break
        elsif command == "s"
            break
        else
            puts error_message
            redo
        end
    end
    #Replay processing
    if command == nil
        redo
    else
        break
    end
end

What I learned

At first, I used "p" instead of minitest, but I understood that the work would be faster if I used it.

If you don't put "test" in the first word of the method name of minitest, I completely forgot not to test it and I was worried about 2 days but lol

Understanding will deepen if you actively use things that you have never used.

There are still many places that can be refactored, I was almost tired of making it, so I wrote Qiita once.

Recommended Posts

I made blackjack with Ruby (I tried using minitest)
I tried DI with Ruby
I tried using JOOQ with Gradle
I updated my own blackjack made with Ruby for my portfolio
I tried using Realm with Swift UI
I tried using Scalar DL with Docker
I tried using OnlineConverter with SpringBoot + JODConverter
I made a risky die with Ruby
I tried using OpenCV with Java + Tomcat
I tried using Gson
I tried using TestNG
I tried using Galasa
I made a portfolio with Ruby On Rails
[Ruby] I made a crawler with anemone and nokogiri.
I tried using azure cloud-init
I tried using Apache Wicket
I tried using Java REPL
I tried UPSERT with PostgreSQL.
I tried BIND with Docker
I tried Jets (ruby serverless)
I tried installing Ruby on Rails related plugin with vim-plug
I made an interpreter (compiler?) With about 80 lines in Ruby.
I tried to automate LibreOffice Calc with Ruby + PyCall.rb (Ubuntu 18.04)
I tried to get started with Swagger using Spring Boot
I tried using the CameraX library with Android Java Fragment
I tried using anakia + Jing now
Ruby: I made a FizzBuzz program!
I tried using Spring + Mybatis + DbUnit
I tried morphological analysis with MeCab
I made a GUI with Swing
I tried to interact with Java
I tried UDP communication with Java
I tried using Java8 Stream API
I tried using JWT in Java
I tried GraphQL with Spring Boot
[Android] I tried using Coordinator Layout.
I tried Flyway with Spring Boot
I tried using Pari gp container
I tried using WebAssembly Stadio (2018/4/17 version)
I tried customizing slim with Scaffold
I tried using Java memo LocalDate
I tried using GoogleHttpClient of Java
I made a command line interface with WinMerge Plugin using JD-Core
I tried to solve the problem of "multi-stage selection" with Ruby
I tried connecting to MySQL using JDBC Template with Spring MVC
I tried to reimplement Ruby Float (arg, exception: true) with builtin
I tried using Elasticsearch API in Java
[Ruby] I made a simple Ping client
I tried using Java's diagnostic tool Arthas
I tried using UICollectionViewListCell added from Xcode12.
I created and set my own Dialect with Thymeleaf and tried using it
I tried to get started with WebAssembly
I made various Fibonacci sequence functions (Ruby)
[Ruby] I tried to diet the if statement code with the ternary operator
I made an eco server with scala
Notes on using FCM with Ruby on Rails
I tried time-saving management learning with Studyplus.
It's new, but I tried using Groonga
I tried playing with BottomNavigationView a little ①
I tried to solve the tribonacci sequence problem in Ruby, with recursion.
I tried Lazy Initialization with Spring Boot 2.2.0