Rubyでブラックジャック作ってみた(minitest使ってみた)
ファイル構成とコードを書いた時に、考えていたこと
-
card.rb(カードクラス)
- 1枚ごとのカードデータの書き換えができないように(データが壊れないように)、attr_readerにを使った。
-
deck.rb(デッキクラス)
- インスタンスを生成した段階で、デッキがシャッフルされているようにし、それをテストした。
- each文を使って、デッキの生成をなるべく簡潔に書いた。
- クラス変数を使い、カードをドローしたときに、今、山札の何枚目がドローされているかカウントするようにした。
-
player.rb(プレイヤークラス)
- プレイヤーとディーラーで共通していて、同じデッキを使う為に、クラス変数にデッキのインスタンスを代入して、デッキを使用した。
-
judge.rb(ジャッジクラス)
- 判定するためのクラス、わかりやすくするために、あえてプレイヤーと別でモジュールを使用した。
-
test.rb(動作確認テスト)
- minitestは*「プロを目指す人のためのRuby入門」*で、書いて以来、使ったことがなかったので、理解を深める為に使ってみた。
-
main.rb(ブラックジャックを動かすメインファイル)
- わかりやすいようにゲームの全体の流れのみを最初に書いた
- そこから必要なクラスとメソッドを考えて、付け足していった。
ルール
- ベッドは無し。(後日実装予定)
- 「A」は「1」と認識(後日、10に変換できるようにする予定)
- ヒットかスタンドのみ
- トータルスコアが高い方が勝ち
card.rb (カードクラス)
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.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
# テスト用のカード参照メソッド
def show
52.times do |n|
puts "[ #{cards[n].mark} #{cards[n].number} ]"
end
end
end
player.rb (プレイヤークラス)
player.rb
require './deck'
require './judge'
class Player
include Judge
attr_accessor :hands, :total_score, :name
@@deck = Deck.new
def initialize(name: "プレイヤー")
@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}の手札です"
self.hands.each do |hand|
puts "[ #{hand.mark} #{hand.number} ]"
end
puts "[ トータルスコア #{@total_score} ]"
end
# テスト用メソッド
def test
puts "テストーーーーーー"
end
def total_score_calc
self.hands.each do |hand|
@total_score += hand.convert
end
#puts "トータル「#{@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.rb
module Judge
# def game_judge(target)
# if self.total_score > target.total_score
# puts "あなたのトータルスコア[ #{self.total_score} ]"
# puts "ディーラーのトータルスコア[ #{target.total_score} ]"
# puts 'あなたの勝ちです'
# elsif self.total_score == target.total_score
# puts "あなたのトータルスコア[ #{self.total_score} ]"
# puts "ディーラーのトータルスコア[ #{target.total_score} ]"
# puts '引き分けです'
# else
# puts "あなたのトータルスコア[ #{self.total_score} ]"
# puts "ディーラーのトータルスコア[ #{target.total_score} ]"
# puts 'あなたの負けです'
# 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 = "ブラックジャック"
true
else
false
end
else
false
end
end
def burst?
if self.total_score > 21
true
else
false
end
end
end
test.rb(動作確認テスト)
test.rb
require 'minitest/autorun'
require './player'
class BlackJackTest < Minitest::Test
# テストカードのマークは「 ♦︎ 」で統一
@@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 パターン
# 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.rb
require './player'
lose_message =<<~TEXT
あなたの負けです
TEXT
win_message =<<~TEXT
あなたの勝ちです
TEXT
game_draw_message =<<~TEXT
引き分けです
TEXT
error_message = "無効な文字です。もう一度入力してください"
class Play
attr_accessor :player, :dealer
def initialize
@player = Player.new
@dealer = Player.new(name: "ディーラー")
end
def test
@player.draw
@player.draw
@player.show
end
end
while true # 再プレイのためのループ処理
# 初ターンの処理
player = Player.new
dealer = Player.new(name: "ディーラー")
puts "ようこそ、ブラックジャックへ"
puts "カードを配ります"
player.draw
player.draw
dealer.draw
dealer.show
# ブラックジャックテスト
# dealer.reset
# dealer.in_blackjac
# dealer.show
# player.reset
# player.in_blackjack
# player.show
# プレイヤーがブラックジャックじゃないか確認の処理
if player.blackjack?
puts "ブラックジャック!"
player.show
else
player.show
puts h_or_s_message =<<~TEXT
選択してください
Push「h」, ヒット
Push「s」, スタンド
TEXT
end
# ヒット、スタンドの処理(プレイヤーのターン)
while true
# ブラックジャックの場合は強制的にスタンドさせる処理
if player.blackjack?
command = 's'
else
command = gets.chomp # ヒットかスタンドか選択
end
# ヒットの場合の処理
if command == "h"
while true
player.draw
if player.burst? # バーストしていないか確認
player.show
puts "バースト!!"
puts lose_message
break
end
# ドロー後のトータルスコアの確認
player.show
# 最大値(21)の場合は強制的にスタンドの処理
if player.total_score == 21
puts 'トータルスコアが最大値に達しました'
command = 's'
break
end
# 最大値ではない場合は、再度ヒットするか処理
puts "もう一度引きますか?"
puts h_or_s_message
while true
command = gets.chomp # 再度、ヒットかスタンドの選択
if command == 'h'
break
elsif command == 's'
break
else
puts error_message
redo
end
end
# ヒットかスタンドの処理
if command == 'h'
redo
elsif command == 's'
break
end
end
# バースト時の強制終了処理
if player.burst?
break
end
else
# h,s以外が入力されたときの処理
puts error_message
redo
end
# スタンド時の処理(ディーラーのターン)
if command == 's'
# プレイヤーがブラックジャックだった場合のディーラーの処理
if player.blackjack?
dealer.draw
dealer.show
if !dealer.blackjack?
puts win_message
break
else
puts game_draw_message
break
end
end
# プレイヤーに勝つまでディーラーはヒットし続ける処理
while player.total_score > dealer.total_score
dealer.draw
dealer.show
if dealer.burst?
puts 'バースト!'
puts win_message
break
elsif dealer.total_score == 21 && player.total_score == 21
break
end
end
if dealer.burst? # バーストしていた場合処理処理終了
break
elsif dealer.total_score == 21 && player.total_score == 21 # お互い最大値に達していないか確認
puts game_draw_message
break
else
puts lose_message
break
end
end
end
# 再プレイの処理
puts <<~TEXT
もう一回遊びますか?
選択してください
Push「h」, はい
Push「s」, いいえ
TEXT
while true
command = gets.chomp # 再プレイするか選択
if command == "h"
command = nil
break
elsif command == "s"
break
else
puts error_message
redo
end
end
# 再プレイの処理
if command == nil
redo
else
break
end
end
学べたこと
*「プロを目指す人のためのRuby入門」*で
「なぜこんなことをするんだろう」、
「いつこれは使うのだろう」、
と、思ってたことが、使うタイミングがよくわかった。
最初はminitestは使わず「p」を使ってテストしていたが、
使った方が作業が早くなるということを理解した。
minitestのメソッド名の最初の単語に「test」を入れないと、
テストしてくれないことをすっかり忘れてて、2日くらい悩んだけど笑
使ったことないものは積極的に使っていくと理解が深まる。
まだリファクタリングできるところは、たくさんあるんだけど、
作るの飽きそうになったので、一旦、Qiita書きました。