Ruby
programming
BitStarDay 14

[初級者向け]Rubyで作るブラックジャック

More than 1 year has passed since last update.

こんにちは BitStar新米エンジニアのNarachiiです。

今回の記事では、Rubyでブラックジャックを作る方法を
紹介したいと思います。
なお、今回の実装ではカードの絵柄を考慮しませんので
ご了承ください。

それでは、ブラックジャックを以下の2つの順序で作成していきたいと思います。
1. プレイヤーとディーラーの手札作成
2. ゲーム部分の実装

1.手札の作成

ブラックジャックでは各々が2枚づつ初期の手札を持ちます。
この際、ディーラーは手札をプレイヤーに一枚開示しなければなりません。
上記のことを踏まえて実装して行きましょう!

まずはディーラとプレイヤーがカードを格納出来るように
空配列の変数を定義します。

bj.rb
  dealer_cards = []
  player_cards = []

続いてカードをドローする機能を実装していきます。
ブラックジャックでドローされるカードは1 ~ 11までの数字です。
これらの範囲からランダムに数値を入手し
先程の配列に代入することでドローしたことになります。
手札が2枚になるまでwhileを使って
処理を繰り返しましょう。
プレイヤーのドロー機能から紹介します。

bj.rb
dealer_cards = []                                                                                                                                                                                                                    
player_cards = []
 ###追記  
  while player_cards.length <= 2
    player_cards.push(rand(1..11))
    if player_cards.length == 2
      puts "You have #{player_cards}"
    end
  end

カードの枚数はlengthメソッドで配列の要素をカウントすることで判定出来ます。
手札が1枚以下の場合はpushメソッドで
randメソッドでランダムで生成された1 ~ 11までの値を変数に代入します。
2枚になった場合、手札を表示させます。

ディーラーのドローも同様に実装しますが
カードを1枚隠して表示させる必要があるので
dealer_cards[1]とすることで2つめに引いた
カードのみを表示するようにします。

bj.rb
dealer_cards = []                                                                                                                                                                                                                    
player_cards = []

  while player_cards.length != 2
    player_cards.push(rand(1..11))
    if player_cards.length == 2
      puts "You have #{player_cards}"
    end
  end

   while dealer_cards.length != 2
     dealer_cards.push(rand(1..11))
     if dealer_cards.length == 2
       puts "Dealer has X & #{dealer_cards[1]}"
     end
   end

2. ゲーム部分の実装

2.1 カード合計の判定

BrackJackはカードを引いていき、手札の合計が21に近いほど勝利になります。
プレイヤーは21に近づけるためにカードを何枚でもドロー出来ます。
合計が21を超えた場合はその時点で負けになります。
また、最初に引いたカードの合計が21であれば
その時点で勝ちになります。
上記のルールを踏まえてゲーム部分を実装していきましょう。

まずはじめに、
最初に引いたカードの合計が21かどうかを判定します。
実際のゲームでは起こりませんが
今回の実装では1 ~ 11の数値を毎回ランダムに取得する仕様なので
初期のカード合計が21を超えている可能性がありますが、
ここは特別ルールとして負け判定をつけましょう。。。。。

カード合計判定には、
1.変数に格納されている要素の値を合計
2.合計が21 or 21以上 の場合は勝敗判定をしゲームを終了
の処理が必要になります。
1にはinjdectメソッドを使用し合計値を出します。
2はifで条件分岐し、exitを記載することで
ゲームを終了させます。
これらの方法で実装したコードが以下になります。

bj.rb
~ 省略 ~
  if dealer_cards.inject(:+) == 21
    puts ("Dealer has 21 and wins!")
    exit
  elsif dealer_cards.inject(:+) > 21
    puts ("Dealer has busted!")
    exit
  end

  if player_cards.inject(:+) > 21
    puts "You BUSTED! Dealer wins."
    exit
  elsif player_cards.inject(:+) == 21
    puts "You have BLACKJACK! You WIN!!!"
    exit
  end

injectメソッドは引数に(:+)を指定することで
配列の要素を合計した値を返してくれます。
この返り値を活用して条件分岐することで
カードの合計を判定します。
また、判定はディーラーから行うようにし
どちらも条件を満たさなかった場合は
次節で実装のHit or Stay処理に入ります。

2.2 Stay or Hit

最後にBrackJackの肝である
Stay or Hitを実装していきます。
前節で実装したカード合計の判定処理で
条件を満たさなかった場合、プレイヤーは

1.カードを引く(Hit)
2.引かずに勝負 (Stay)
の2択を選べるようにします。

1を選択した場合、引いたカードを足した合計が
22以上 → 勝負に負ける
21以下 → 再度2択の選択

2を選択した場合は
お互いの合計値を表示し、比べることで
勝敗をつけるように実装すれば良いでしょう。

以下が上記の機能を実装した
コードになります。

bj.rb
while player_cards.inject(:+) < 21
 puts "Do you want to stay or hit?"
 action_taken = gets.chomp
 if action_taken == "hit"
  player_cards.push(rand(1..11))
  if player_cards.inject(:+) > 21
    puts "You BUSTED! Dealer wins."
    exit
  end
  puts "You now have a total of #{player_cards.inject(:+)} from these cards #{player_cards}}"
 else
   puts "The dealer has a total of #{dealer_cards.inject(:+)} with #{dealer_cards}"
   puts "You have a total of #{player_cards.inject(:+)} with #{player_cards}"
 if dealer_cards.inject(:+) > player_cards.inject(:+)
   puts "Dealer wins!"
   break
 else
   puts "You win"
   break
  end
 end
end

最初にwhileで21以下の場合は処理をループするようにします。
ループ内では、まずプレイヤーにstayかhitの入力を求めることで
カードを引くか選択させます。 これはgetメソッドを使用すれば良いでしょう。
入力がhitであれば前章と同じ方法でカードを引かせ、
合計値が21以上に表示させループの先頭に戻ります。

入力値がhit以外の場合はstayと判定します。
stayの場合は双方の
手札の合計とカードを表示させ、
ディーラーの手札 > プレイヤーであれば "Dealer ins"
逆の場合は "You win"と表示させ
ループから抜けます。

最後に、21を超えている場合は
負けの処理を実装して完成になります。

bj.rb
dealer_cards = []                                                                                                                                                                                                                    
player_cards = []

  while player_cards.length != 2
    player_cards.push(rand(1..11))
    if player_cards.length == 2
      puts "You have #{player_cards}"
    end
  end

   while dealer_cards.length != 2
     dealer_cards.push(rand(1..11))
   end
  if dealer_cards.inject(:+) == 21
    puts ("Dealer has 21 and wins!")
    exit
  elsif dealer_cards.inject(:+) > 21
    puts ("Dealer has busted!")
    exit
  end

  if player_cards.inject(:+) > 21
    puts "You BUSTED! Dealer wins."
    exit
  elsif player_cards.inject(:+) == 21
    puts "You have BLACKJACK! You WIN!!!"
    exit
  end

while player_cards.inject(:+) < 21
 puts "Do you want to stay or hit?"
 action_taken = gets.chomp
 if action_taken == "hit"
  player_cards.push(rand(1..11))
  if player_cards.inject(:+) > 21
    puts "You BUSTED! Dealer wins."
    exit
  end
  puts "You now have a total of #{player_cards.inject(:+)} from these cards #{player_cards}}"
 else
   puts "The dealer has a total of #{dealer_cards.inject(:+)} with #{dealer_cards}"
   puts "You have a total of #{player_cards.inject(:+)} with #{player_cards}"
 if dealer_cards.inject(:+) > player_cards.inject(:+)
   puts "Dealer wins!"
   break
 else
   puts "You win"
   break
  end
 end
end