はじめに
Ruby学習の一環で、ブラックジャックゲームを作成しました。
なお、オブジェクト指向について未熟なこともあり適切なクラス分けが
できていない点、ご容赦ください。この点はリファクタリングを通して
修正していく予定です。
ゲームのルール
基本的なルールはブラックジャックそのままです。
参加プレーヤー3人(うち2人はCPU)とディーラーで得点を競います。
・ゲーム開始時にベットする金額を設定後、カードをディールします。
・ディール後、カードを任意の回数ヒットできます。ただし21点を超えたら敗北です。
・ヒットを終えたらCPUとディーラーがヒットします。17点を超えるまでヒットし続けるように実装してます。
・ディーラーがヒットを終えたら結果発表。勝利したらベットした額 x 2倍の金額を得ます。
ただし、まだ実装中ということもあり、ダブルダウン/スプリット/サレンダーと
いった基本的なオプションは未実装です。
# frozen_string_literal: true
class BlackJack
SUIT_LIST = %w[クローバー スペード ハート ダイヤ].freeze
NUMBERS = ['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K'].freeze
# @@deck は 0〜51の数で絵柄(suit)とカードナンバーを表現する
# 引いたカードが 15の場合、スペードの3となる
# suit/ナンバー| A 2 3 4 5 6 7 8 9 10 J Q K
# -------------+--------------------------------------------------
# クローバー | 0, 1, 2, 3, 4, 5, 6 , 7, 8, 9, 10, 11, 12,
# スペード |13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
# ハート |26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38
# ダイヤ |39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
@@deck = (0..51).to_a
# ゲームに参加するプレイヤーの数
@@active_players = 0
# ディーラーの得点
@@dealer_score = 0
# ディーラーのブラックジャックフラグ
@@black_jack_flag = false
def initialize
@hand = []
end
def draw_card
card = @@deck.sample
# 絵柄を suit に代入
suit = SUIT_LIST[(card / 13)]
# カードナンバーを card_number に代入
card_number = NUMBERS[card % 13]
@hand << case card_number
when 'A'
11
when 'J', 'Q', 'K'
10
else # card_number が数字のとき
card_number
end
@@deck.delete(card)
"#{suit}の#{card_number}"
end
def calculate_hand_score(hand = @hand)
sum = hand.inject(:+)
return sum if sum <= 21
# --------この先、カードの合計点sumが21点以上の処理--------
# Aのカード(11)が存在する場合、手札で最初に見つかった11を1に変える
if hand.include?(11)
hand[hand.index(11)] = 1
hand.inject(:+)
else
sum
end
end
end
class User < BlackJack
def initialize
super
@name = 'あなた'
@money = 10_000
@bet_size = 0
@black_jack_flag = false
# プレイヤー数を1増やす
@@active_players += 1
end
def set_bet_amount
puts 'ベットしてください。'
@bet_size = gets.chomp.to_i
# bet_siseは 1以上10000未満でのみ受け付ける
if (1..10_000).include?(@bet_size)
@money -= @bet_size
else # if条件に該当しない場合、再帰呼び出しでループ
set_bet_amount
end
end
def deal
2.times do
puts "#{@name}の引いたカードは#{draw_card}です。"
end
return unless calculate_hand_score == 21
puts 'ブラックジャック!'
@black_jack_flag = true
end
def hit_or_stand
# 21点未満の場合、カードを引くかどうかを選択
while calculate_hand_score < 21
puts "#{@name}の現在の得点は#{calculate_hand_score}です。カードを引きますか?(Y/N)"
answer = gets.chomp.downcase
if answer == 'y'
puts "#{@name}の引いたカードは#{draw_card}です。"
elsif answer == 'n'
return
else
next
end
end
# 21点以下の場合、メソッドを抜ける
return if calculate_hand_score <= 21
# 21点超えで負けの場合
puts "#{@name}の得点は#{calculate_hand_score}です。"
puts "#{@name}の負けです。"
puts "#{@name}の持ち金は#{@money.to_i}です。"
@@active_players -= 1
end
def player_results
# 21点超えで負けの場合
return if calculate_hand_score > 21
# 自身がブラックジャックの場合
if @black_jack_flag && !@@black_jack_flag
@money += @bet_size * 2.5
puts "#{@name}の勝ちです!"
puts "#{@name}の持ち金は#{@money.to_i}です。"
return
# 自身とディーラーがブラックジャックの場合
elsif @black_jack_flag && @@black_jack_flag
@money += @bet_size
puts "#{@name}は引き分けです!"
puts "#{@name}の持ち金は#{@money.to_i}です。"
return
end
# ディーラーの得点が21点を超えている場合
if @@dealer_score > 21
@money += @bet_size * 2
puts "#{@name}の勝ちです!"
puts "#{@name}の持ち金は#{@money.to_i}です。"
return
end
puts "#{@name}の得点は#{calculate_hand_score}です。"
# ディーラーより得点が高い場合
if calculate_hand_score > @@dealer_score
puts "#{@name}の勝ちです!"
@money += @bet_size * 2
# ディーラーより得点が低い場合
elsif @@dealer_score > calculate_hand_score
puts "#{@name}の負けです!"
else # 引き分けの場合
puts "#{@name}は引き分けです!"
@money += @bet_size
end
puts "#{@name}の持ち金は#{@money.to_i}です。"
end
end
class Cpu < User
@@num_of_cpu = 0
def initialize
super
# CPU1,CPU2といった命名規則で名前をつける
@@num_of_cpu += 1
@name = "CPU#{@@num_of_cpu}"
end
def set_bet_amount
@bet_size = @money / 10
@money -= @bet_size
end
def hit_or_stand
# 17点未満の場合、ヒットする
while calculate_hand_score < 17
puts "#{@name}の現在の得点は#{calculate_hand_score}です。"
puts "#{@name}の引いたカードは#{draw_card}です。"
end
# 21点以下の場合、スタンドする
return if calculate_hand_score <= 21
# 21点超えの処理
puts "#{@name}の負けです。"
puts "#{@name}の持ち金は#{@money.to_i}です。"
@@active_players -= 1
end
end
class Dealer < BlackJack
def initialize
super
@name = 'ディーラー'
end
def deal
puts "#{@name}の引いたカードは#{draw_card}です。"
@second_card = draw_card
puts 'ディーラーの引いた2枚目のカードはわかりません。'
end
def draw_card
@last_card = super
end
def hit_or_stand
# プレーヤー全員がバーストしていた場合、hit_or_standメソッドを抜ける
return if @@active_players.zero?
puts "#{@name}の引いた2枚目のカードは#{@second_card}でした。"
# ブラックジャックの場合は処理を抜け結果発表へ
if calculate_hand_score == 21
puts 'ブラックジャック!'
@@black_jack_flag = true
return
end
# 17点未満の場合、ヒットする
while calculate_hand_score < 17
puts "#{@name}の現在の得点は#{calculate_hand_score}です。"
puts "#{@name}の引いたカードは#{draw_card}です。"
end
# 17点超えた場合、得点を出力しスコアに反映
puts "#{@name}の得点は#{calculate_hand_score}です。"
@@dealer_score += calculate_hand_score
end
end
# # インスタンス生成
you = User.new
cpu1 = Cpu.new
cpu2 = Cpu.new
dealer = Dealer.new
puts 'ブラックジャックを開始します。'
# ベット額を設定
you.set_bet_amount
cpu1.set_bet_amount
cpu2.set_bet_amount
# ディーラーがカードを配る
you.deal
cpu1.deal
cpu2.deal
dealer.deal
# ヒットかスタンドか選択
you.hit_or_stand
cpu1.hit_or_stand
cpu2.hit_or_stand
dealer.hit_or_stand
# # プレーヤーの勝敗を判定
you.player_results
cpu1.player_results
cpu2.player_results
puts 'ブラックジャックを終了します。'
実行例
$ ruby black_jack3.rb
ブラックジャックを開始します。
ベットしてください。
1000
あなたの引いたカードはスペードのKです。
あなたの引いたカードはハートのAです。
ブラックジャック!
CPU1の引いたカードはクローバーの4です。
CPU1の引いたカードはクローバーの5です。
CPU2の引いたカードはスペードの2です。
CPU2の引いたカードはダイヤのAです。
ディーラーの引いたカードはハートの2です。
ディーラーの引いた2枚目のカードはわかりません。
CPU1の現在の得点は9です。
CPU1の引いたカードはクローバーの6です。
CPU1の現在の得点は15です。
CPU1の引いたカードはクローバーの10です。
CPU1の負けです。
CPU1の持ち金は9000です。
CPU2の現在の得点は13です。
CPU2の引いたカードはスペードの10です。
CPU2の現在の得点は13です。
CPU2の引いたカードはスペードの8です。
ディーラーの引いた2枚目のカードはハートのJでした。
ディーラーの現在の得点は12です。
ディーラーの引いたカードはダイヤの3です。
ディーラーの現在の得点は15です。
ディーラーの引いたカードはダイヤのJです。
ディーラーの得点は25です。
あなたの勝ちです!
あなたの持ち金は11500です。
CPU2の勝ちです!
CPU2の持ち金は11000です。
ブラックジャックを終了します。
最後に
ここまで実装し終えたあとにオブジェクト指向の実装の考え方(クラス毎にファイルを分けるなど)に
気づいたのですが、忘備録ということで一旦不完全なまま記事にあげました。
今後はオブジェクト指向についてしっかりと学んだあと、再挑戦する予定です。見苦しいコートですが
ここまで見ていただき、ありがとうございました。