[Ruby] Rubyで動くブラックジャック作ってみた

Rubyの基礎文法を勉強したときに知識を定着させるために作成したブラックジャックゲームです。
ちなみに、プログラミングを勉強して初めて作った成果物。

# 実行動画

![ezgif.com-gif-maker (5).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/974719/ac23f06f-59dd-6f31-c642-cdcd0433c8f4.gif)


# 実装部分
実装の前にマインドマップを作って各クラスの各メソッドが何をするのかを明確にしてから作りました。

- マインドマップ
![スクリーンショット 2021-10-08 9.05.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/974719/51c7874b-e0c8-d37c-4c96-3de284e4ce1c.png)

## Blackjackクラス
```ruby:blackjack.rb
require "./deck.rb"
require "./card.rb"
require "./user.rb"
require "./dealer.rb"
require "./message.rb"

#ゲーム進行
class Blackjack

  include Message

  def game_start
  
    start_msg

    deck = Deck.new
    user = User.new
    dealer = Dealer.new

    #手札配る、手札表示
    user.user_first_draw_card(deck) 
    dealer.dealer_first_draw(deck)

    #ユーザーターン
      #Hitならもう一枚引く、スタンドならループ抜ける
      while true

        select_msg

        #行動選択
        select_num = gets.to_i
        if select_num == 1
          user.draw(deck)
          user.show_hand
          
          if user.cal_score_user >= 22
            burst_msg
            return 
          end
        elsif select_num == 2
          break
        else
          error_msg
        end

      end

      #ディーラーターン
      #17以上になるまで引く
      while true 
        if dealer.cal_score_dealer <= 17
            dealer.draw(deck)
        else
            break
        end
      end
      user.show_hand
      dealer.show_hand
      judge(user, dealer)

  end

  
  def judge(user, dealer)
    user_score = user.cal_score_user
    dealer_score = dealer.cal_score_dealer
    if  user_score == dealer_score
      draw_msg
    elsif user_score == 21
      blackjack_msg
    elsif dealer_score >= 22
      dealer_burst_msg
    elsif dealer_score > user_score
      user_lose_msg
    else
      user_win_msg
    end
  end
end
```
ゲームコントローラー部分。
Userはカードを引くか引かないかの行動を選べて、Dealerは18以上になるまでカードを引き続ける処理を書いてます。
プレイヤーが21以上の点数になったら即バーストで負け、21ちょうどならブラックジャックの表記が出て勝ち。
メッセージ部分は`message.rb`に切り出してます。

## Deckクラス
```ruby:deck.rb
#山札作成、シャフル、ドロー
class Deck
  #山札用の空の配列を用意
  def initialize
    @cards = []
  end

  #山札作成、シャッフル
  def create
    mark = ["ハート","クローバー","ダイヤ","スペード"]
    number = ["A","2","3","4","5","6","7","8","9","10","J","Q","K"]
    mark.each do |mark|
        number.each do |number|
            card = Card.new(mark,number)
            @cards << card
        end
    end
    @cards.shuffle!
  end

  #山札から1枚引く
  def draw
    @cards.pop
  end
end

```
ここではカードデッキの生成とカードを引く処理の部分です。
カードをデッキから引く処理は配列の末尾要素を削除する`pop`メソッドを使うことで実現しています。

## Cardクラス

```ruby:card.rb 
#カード情報
class Card
  attr_reader :mark,:number
  #カードオブジェクトの値受け取り
  def initialize(mark,number)
    @mark = mark
    @number = number
  end

  #カード情報表示
  def info
    return "#{@mark}の#{@number}"
  end
  
  #スコア計算方法
  def score
    if @number == "J" || @number == "Q" || @number == "K"
      @nunmber = 10
    elsif @number == "A"
      @number = 1
    else
      @number.to_i
    end
  end
end
```
スコア計算の部分は、JQKを10点Aは１点として扱っています。
本当のBJだとAは１点か11点のどちらか都合の良い方で扱えるけど難しくて実装断念した‥
**Done is better than perfect** ってことで今回はとりあえずこれで、今後時間があれば実装します 笑

## Userクラス

```ruby:user.rb
class User
  def initialize
    @user_hands = []
  end

  #最初に引く、手札公開
  def user_first_draw_card(deck)
    2.times do
        deck.create
        card = deck.draw
        @user_hands << card
    end

    puts "====あなたの手札======="
    @user_hands.each.with_index(1) do |user_hand, i|
        puts "#{i}枚目 : #{user_hand.info}"
    end 
    puts "あなたの合計点数は#{cal_score_user}です"
    puts "========================"
  end
        

  def cal_score_user
    user_score = 0
    @user_hands.each do |hand|
      user_score += hand.score
    end
    return user_score  #合計のスコアスコアを返す
  end

  #1枚引く
  def draw(deck)
    deck.create
    card = deck.draw
    @user_hands << card
  end

  #手札表示
  def show_hand
    puts ""
    puts "====あなたの手札======="
    @user_hands.each.with_index(1) do |user_hand, i|
        puts "#{i}枚目 : #{user_hand.info}"
    end 
    puts "あなたの合計点数は#{cal_score_user}です"
    puts "========================"
    puts ""
  end
end
```
Userは最初に2枚手札を配られて、Hitを選択したときに1枚ずつデッキからカードを引くっていう処理。

## Dealerクラス

```ruby:dealer.rb
class Dealer
  def initialize
      @dealer_hands = []
  end

  #最初に引く、手札公開
  def dealer_first_draw(deck)
    2.times do
        deck.create
        card = deck.draw
        @dealer_hands << card
    end
    puts <<~TEXT
    ====ディーラーの手札====
    1枚目：#{@dealer_hands.first.info}
    2枚目：伏せられている
    ========================
    TEXT
  end

  #ディーラーのスコア計算
  def cal_score_dealer
    dealer_score = 0
    @dealer_hands.each do |hand|
        dealer_score += hand.score
    end
    return dealer_score  #2枚の合計のスコア
  end

  def draw(deck)
    deck.create
    card = deck.draw
    @dealer_hands << card
  end

  def show_hand
    puts ""
    puts "====ディーラーの手札===="
    @dealer_hands.each.with_index(1) do |dealer_hand, i|
        puts "#{i}枚目 : #{dealer_hand.info}"
    end 
    puts "あなたの合計点数は#{cal_score_dealer}です"
    puts "========================"
    puts ""
  end
end
```
やっていることはUserと大体同じだけどDealerの場合は2枚配られてカードを1枚伏せてる状態にしてる。

## Messageモジュール
```ruby:message.rb
module Message
  def start_msg
    puts <<~TEXT
    =======================

    Welcome to Blackjack!!!

    =======================
    TEXT
  end

  def select_msg
    puts <<~TEXT
      どうしますか？

      1.Hit(カードを引く) 
      2.Stand(今の手札で勝負)

    TEXT
  end

  def burst_msg
    puts "バースト！！"
    puts "合計点数が21を超えたのであなたの負けです。"
  end

  def error_msg
    puts "1 or 2 を入力してください。"
  end

  def draw_msg
    puts "同点なので引き分けです。"
  end

  def blackjack_msg
    puts "ブラックジャック！！ あなたの勝ちです！"
  end

  def dealer_burst_msg
    puts "ディーラーがバーストしました。あなたの勝ちです！"
  end

  def user_lose_msg
    puts "ディーラーの勝ちです！"
  end

  def user_win_msg
    puts "あなたの勝ちです！"
  end
end
```
Blackjacクラスの出力メッセージはメッセージモジュールとして切り出して処理のみにすることで可読性を上げています。

## Main
```ruby:main.rb
require "./blackjack.rb"

blackjack = Blackjack.new
blackjack.game_start
```
最後に 実行するmain部分。
ここで`blackjack.game_start`を呼び出すことでゲームが始まる。

#　感想
実際にコードに落とし込む作業は基礎文法を勉強しているときとは比べ物にならないくらい大変でした。
それでも作っているときの楽しさ、実際に完成して動いた時の感動は忘れられません。
次はAの処理や掛け金機能を追加して、よりゲーム性を高めていきたいと思います。