ブラックジャックゲーム(オブジェクト指向で実装)
ブラックジャックについて
今回は、Rubyを使ってブラックジャックというゲームを実装しました。
ブラックジャックは、プレイヤーとディーラーがカードを引いて、合計点数が21に近い方が勝つというシンプルなゲームです。この記事では、実装したコードをクラスごとにファイルを分け、それぞれのメソッドごとに解説していきます。
Cardクラス
このクラスは、カードのスートと数字を保持し、カードの文字列表現を提供します。
class Card
SUITS = ["スペード", "ハート", "ダイヤ", "クローバー"].freeze
NUMBERS = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"].freeze
attr_reader :suit, :number
def initialize(suit, number)
@suit = suit
@number = number
end
def value
case @number
when "A"
1
when "K", "Q", "J", "10"
10
else
@number.to_i
end
end
def to_s
"#{@suit}の#{@number}"
end
end
このCardクラスは、カードのスートと数字を保持するための@suitと@numberインスタンス変数を持っています。initializeメソッドでこれらの変数が設定され、to_sメソッドでカードの文字列表現を返します。
また、カードの値を計算するためにvalueメソッドを追加しています。このメソッドは、カードの値を返すことで、プレイヤーの手札の計算に役立ちます。
Deckクラス
このクラスは、52枚のカードを持つデッキを作成し、シャッフルしてカードを引く機能を提供します。
class Deck
def initialize
@cards = []
Card::SUITS.each do |suit|
Card::NUMBERS.each do |number|
@cards << Card.new(suit, number)
end
end
@cards.shuffle!
end
def draw(number)
@cards.pop(number)
end
end
initializeメソッドでは、Card::SUITSとCard::NUMBERSを使用して52枚のカードを作成し、@cardsインスタンス変数に格納します。デッキが作成されたら、shuffle!メソッドでカードをシャッフルします。
drawメソッドでは、引数で指定された枚数のカードをデッキから引き、そのカードを返します。popメソッドは、配列の最後の要素を取り出すために使用されています。
ブラックジャックゲームを始める
具体的なクラスを作成する前に、ブラックジャックゲームを始めるにあたってgameクラスを動かすとゲームが始まるイメージで設定します。
require_relative 'card'
require_relative 'game'
#require_relativeは、同じディレクトリにあるファイルを読み込むための記述
game = Game.new
game.start
Gameクラス
次に、ゲーム全体を管理するGameクラスです。
このクラスでは、ゲームの開始、プレイヤーとディーラーのターン、結果の表示、ゲームの終了といった一連の流れを制御します。
class Game
def initialize
@deck = Deck.new
@player = Player.new('あなた')
@dealer = Dealer.new('ディーラー')
end
def start
puts "ブラックジャックを開始します。"
@player.draw(@deck, 2)
@dealer.draw(@deck, 2)
puts "#{@player.name}の引いたカードは#{@player.hand}です。"
@dealer.show_initial_card
@player.play_turn(@deck)
if @player.hand.calculate_value > 21
puts "#{@dealer.name}の勝ちです!"
else
@dealer.play_turn(@deck)
if @dealer.hand.calculate_value > 21 || @player.hand.calculate_value > @dealer.hand.calculate_value
puts "#{@player.name}の勝ちです!"
elsif @player.hand.calculate_value == @dealer.hand.calculate_value
puts "引き分けです。"
else
puts "#{@dealer.name}の勝ちです!"
end
end
puts "ブラックジャックを終了します。"
end
end
Gameクラスでは、プレイヤーとディーラーがHandクラスを使用するようになりました。これにより、ゲームの流れがよりわかりやすくなっています。また、プレイヤーとディーラーの得点計算はHandクラスのcalculate_valueメソッドを使用しています。これで、Handクラスを含むブラックジャックの実装が完成しました。
PlayerクラスとDealerクラス
プレイヤーを表現するPlayerクラスです。プレイヤーは名前と手札のカードを持ちます。また、カードを引いたり、手札の合計点数を計算したりできます。
require_relative 'deck'
require_relative 'hand'
require_relative 'card'
class Player #プレイヤーを管理するクラス
attr_reader :name, :hand
def initialize(name)
@name = name
@hand = Hand.new
end
def draw(deck, number) # 山札からカードを引く
new_cards = deck.draw(number)
number.times { @hand.add_card(deck.draw(1).first) }
end
def show_initial_card # 初期手札を表示する
puts "#{@name}の引いたカードは#{@hand.cards[0].to_s}です。"
puts "#{@name}の引いたカードは#{@hand.cards[1].to_s}です。"
end
def play_turn(deck) # ターンを実行する
while true # 無限ループ
puts "#{name}の現在の得点は#{@hand.calculate_value}です。カードを引きますか?(Y/N)"
input = gets.chomp.upcase # YかNを入力する
if input == "Y"
draw(deck, 1)
puts "#{name}の引いたカードは#{@hand.cards.last}です。"
if @hand.calculate_value > 21 # 21を超えたら終了
puts "#{name}の得点が21を超えました。"
break
end
elsif input == "N" # Nを入力したら終了
break
else
puts "無効な入力です。YまたはNを入力してください。" # YでもNでもない場合はエラー
end
end
end
end
class Dealer < Player #ディーラーを管理するクラス
def show_initial_card
puts "#{@name}の引いたカードは#{@hand.cards[0].to_s}です。"
puts "#{@name}の引いた2枚目のカードはわかりません。"
end
def play_turn(deck)
puts "#{@name}の引いた2枚目のカードは#{@hand.cards[1].to_s}でした。"
while @hand.calculate_value < 17 # 17以上になるまで引き続ける
draw(deck, 1)
puts "#{@name}の引いたカードは#{@hand.cards.last}です。"
end
puts "#{@name}の現在の得点は#{@hand.calculate_value}です。"
end
end
継承を利用してコードが簡潔になります。Player クラスと Dealer クラスで独自のターンの挙動を定義し、Game クラス内でそれらを呼び出すようにしました。
Discussion