Rubyで動くコンソールゲーム作ってみた!
今回Ruby学習の中でブラックジャックゲームを作成したので自身のために残しておきます!
オブジェクト指向についての知識がないまま作ってしまったので、
私と同じく初学者の方は下記コードをリファクタリングしてみてください!
私自身リファクタリングしたい部分たくさんありますので、修正後のコードは後日またあげます!
今回特に悩んだところはカードの定義です。
トランプのカードに対してキーに絵柄、そしてその中にさらに文字列とした各数字をキーにスコアを値として入れています。おそらく配列としてもっと簡単に定義することができたという反省点ありますが、、、
ルール
- 開始時にディーラーとプレイヤーに2枚ずつカードが配られる。
- プレイヤーは合計値が21に近づくようにカードを追加するか決めることができる。
- カードの合計値が21を超えてしまった時点で負け。
- プレイヤーはカードの合計値が21を超えなければ何度でも引くことができる。
- ディーラーはカードの合計値が17を超えるまで引き続ける。
- 10,J,Q,Kは10点
- Aは1点もしくは11点として手札の合計値が21に最も近い方を採用する。
現状の改善点
- プレイヤーとディーラーは共通メソッド多いのでプレイヤーを親クラスにし継承する。
- ジャッジの責務がゲームマスタークラスに一部入り込んでしまっているので修正。
- プレイヤー人数の増加に全く対応できていない。
- ジャッジクラスに責務がしっかり持たすことができていない。
- バーストの判定をモジュールを使用し無理やり行っている点。
ファイル一覧
game_master.rb
game_master.rb
require './deck'
require './player'
require './dealer'
require './judge'
#ゲームの全体統括
class GameMaster
include JudegModule
def initialize
#52枚のカード作成してデッキ作る。ドローはデッキから行う。
@card = Card.new
@deck = Deck.new
# Deckクラスのインスタンスを渡して参照できるようにしている。
@player = Player.new(@deck) #デッキからみたいなイメージ
@dealer = Dealer.new(@deck)
# プレイヤーとディーラーのインスタンスを渡す。バーストとかの条件分岐はここでする。
@judge = Judge.new(@player, @dealer)
start
player_draw_turn
end
def start
puts '----------------------------------------------'
puts 'ブラックジャックを開始します。'
#プレイヤーの最初の処理
@player.first_draw
@player.second_draw
#ディーラーの最初の処理
@dealer.first_draw
@dealer.second_draw
end
#プレイヤーのヒットするかどうかの判断とその繰り返しメソッド呼び出す。バーストしたら終了に分岐。
def player_draw_turn
score = @player.select_thing
if burst?(score)
puts 'バーストしました。あなたの負けです。'
end_turn
else
dealer_draw_turn
end
end
#プレイヤーのヒットが終わったら呼び出される。バーストしなければ得点の比較に入るジャッジターンを呼び出す。
def dealer_draw_turn
@dealer.show_second_draw
score = @dealer.total_score
if burst?(score)
puts 'バーストしました。'
puts 'プレイヤーの勝ちです。'
end_turn
else
judge_turn
end
end
#ジャッジクラスの勝敗決めを呼び出す。
def judge_turn
@judge.game_judge
end_turn
end
def end_turn
puts 'ブラックジャックゲームを終了します。'
puts '----------------------------------------------'
end
end
GameMaster.new
player.rb
player.rb
require './judge'
#ゲームをプレイする人物
class Player
include JudegModule
# Deckクラスのインスタンスを受け取る
def initialize(deck)
@deck = deck
@total_score = 0
@current_score = 0
end
#1枚目のドロー
def first_draw
@deck.draw
add_score
puts "あなたの引いたカードは#{@deck.selected_type}の#{@deck.selected_number}です。"
end
#2枚目のドロー
def second_draw
@deck.draw
add_score
puts "あなたの2枚目に引いたカードは#{@deck.selected_type}の#{@deck.selected_number}です。"
#current_score #2枚目引いた時点でcurrent_scoreに数字入っているのでここで返している。
end
#ドローするかどうかの判断
def select_thing
loop do
puts "あなたの現在の得点は#{@current_score}です。カードを引きますか?(Y/N)"
selected = gets.chomp.upcase
case selected
when 'Y'
hit
break
when 'N'
break
else
puts 'Y(yes)もしくはN(no)を選択してください。'
end
end
#ゲームマスタークラスで条件分岐するためにトータルスコア返している
total_score
end
#追加ドロー(ヒット)する場合の処理
def hit
@deck.draw
add_score
puts "あなたの引いたカードは#{@deck.selected_type}の#{@deck.selected_number}です。"
puts "あなたの現在の得点は#{@current_score}です"
re_hit
end
#現在のスコア
def total_score
@current_score
end
#追加ドロー(ヒット)した後にスコアの評価し判断する。
def re_hit
return 'Burst' if burst?(total_score)
select_thing
end
#current_scoreにドローしたスコアをプラスする。かつ、プラスする際にAを含んでいれば21以内で最大値になる方(1or11)にするメソッド
def add_score
@current_score += @deck.selected_score
return unless ['A'].include?(@deck.selected_number) && @current_score <= 11
@current_score += 10
end
end
dealer.rb
dealer.rb
#ディーラーの処理
class Dealer
include JudegModule
# Deckクラスのインスタンスを受け取る
def initialize(deck)
@deck = deck
@total_score = 0
@current_score = 0
end
#1枚目のドロー
def first_draw
@deck.draw
add_score
puts "ディーラーの引いたカードは#{@deck.selected_type}の#{@deck.selected_number}です。"
end
#2枚目のドロー
def second_draw
@deck.draw
add_score
#1度セカンドドロー変数に入れておいて開示のタイミングで変数取れるようにする。
@second_draw_type = @deck.selected_type
@second_draw_number = @deck.selected_number
puts 'ディーラーの2枚目のカードはわかりません。'
end
#2枚の合計点数
attr_reader :current_score
#2枚目のカード公開し、合計点数出す。
def show_second_draw
puts "ディーラーの引いた2枚目のカードは#{@second_draw_type}の#{@second_draw_number}でした。"
puts "ディーラーの現在の得点は#{@current_score}です。"
add_hit
end
#現在のスコア
def total_score
@current_score
end
#17以下の場合。追加でドローするかどうかの判断
def add_hit
return unless @current_score < 17
hit
total_score
end
#追加ドロー(ヒット)する場合の処理
def hit
@deck.draw
add_score
puts "ディーラーの引いたカードは#{@deck.selected_type}の#{@deck.selected_number}です。"
puts "ディーラーの現在の得点は#{@current_score}です。"
re_hit
end
#追加ドロー(ヒット)した後にスコアの評価し判断する。
def re_hit
return if burst?(total_score)
add_hit
end
#current_scoreにドローしたスコアをプラスする。かつ、プラスする際にAを含んでいれば21以内で最大値になる方(1or11)にするメソッド
def add_score
@current_score += @deck.selected_score
return unless ['A'].include?(@deck.selected_number) && @current_score <= 11
@current_score += 10
end
end
judge.rb
judge.rb
#ゲームの判定をするクラス
require './player'
#バーストの判定するモジュール
module JudegModule
def burst?(score)
score >= 22
end
end
#ゲームの判定をするクラス
class Judge
def initialize(player, dealer)
@player = player
@dealer = dealer
end
def game_judge
puts "あなたの得点は#{@player.total_score}です。"
puts "ディーラーの得点は#{@dealer.total_score}です。"
if @player.total_score > @dealer.total_score
puts 'あなたの勝ちです!'
elsif @player.total_score == @dealer.total_score
puts '引き分けです。'
else
puts 'ディーラーの勝ちです!'
end
end
end
deck.rb
deck.rb
require './card'
#52枚の中からランダムに1枚ドローするデッキクラス
class Deck < Card
attr_reader :selected_type, :selected_number, :selected_score
def draw
if @cards.empty?
puts 'カード切れ'
raise
else
#@cards = {"type" => {"numbers" => value数値},"type" => {"numbers" => value数値}...}の形で4種類あるのでまずtypeの部分のキーでランダムに取り出す。
@selected_type = @cards.keys.sample
#上でtypeキー選んだので文字列でキーとして入っているnumberの部分を抽選
@selected_number = @cards[selected_type].keys.sample
#2つのキーを抽選した変数できているのでその値を返す数値つまりスコアを変数に入れる。
@selected_score = @cards[selected_type][selected_number]
# 選択されたカードを削除する。
@cards[@selected_type].delete(@selected_number)
#絵柄の中の数値が空であれば絵柄を削除する。
@cards.delete(@selected_type) if @cards[@selected_type].empty?
end
end
end
card.rb
card.rb
#52枚のカードを作るクラス
class Card
attr_reader :cards
def initialize
#インスタンス生成時に初期値として入れられるのがインスタンス変数。今回はnewした時に引数取らないのでメソッドのとこは引数なし。その代わりそれぞれ配列など入れてる。
@types = ['クローバー', 'ダイアモンド', 'ハート', 'スペード']
@numbers = ['A', *(2..10).to_a.map(&:to_s), 'J', 'Q', 'K']
@cards = create_card(@types, @numbers)
end
#52枚のカードの定義
def create_card(types, numbers)
cards = {}
types.each do |type|
cards[type] = {} #cards = {"club" => {}, "heart" => {}, "spade" => {}, "diamond" => {} }の状態になっている。
numbers.each do |number| #まずキーとしてnumbersをeachで回す。下でそのバリューにあたる値をif等で作成。
card_value = number.to_i # 数字の場合はそのまま値を使う
card_value = 10 if ['J', 'Q', 'K'].include?(number) # card_valueにJ,Q,Kどれか含まれていたらJ,Q,Kは10の値を持つ
card_value = 1 if ['A'].include?(number) #Aは1の値を持つ
#{"type" => {"numbers" => value数値},"type" => {"numbers" => value数値}...}の形
cards[type][number] = card_value
end
end
cards
end
end
以上になります!
まだまだ学習中の身なのでおかしな部分多々あるかと思いますがご容赦ください。
引き続き頑張ります!