はじめに
プロを目指す人のためのRuby入門 を写経しながら1周したので、
学習したことを試してみたい!と思い、難しすぎないプログラミング問題を探していた所、
プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし という記事を見つけ、挑戦してみることに。
ちなみに、実装に費やした時間は丸一日です。。。
記事に書いてある実装のヒントは見ずに書きました。
書いたコードはコチラです
BlackJackクラス
class BlackJack
def judge(player, dealer)
if player.score > dealer.score
puts "あなたの勝ちです!"
elsif player.score < dealer.score
puts "ディーラーの勝ちです。"
else
puts "引き分けです。"
end
end
def play
player = Player.new
dealer = Dealer.new
deck = Deck.new
puts "☆★☆★☆★☆★☆★ブラックジャックへようこそ!☆★☆★☆★☆★☆★"
puts "ゲームを開始します。"
deck.shuffle
player.first_turn(deck)
dealer.first_turn(deck)
player.calc_score
player.present_score
# プレイヤーのターン
while player.score < 22
begin
puts "カードを引きますか?引く場合はYを、引かない場合はNを入力してください。"
answer = gets.chomp
rescue
raise
retry
end
case answer
when 'Y'
player.turn(deck)
when 'N'
puts "あなたのターンは終了です。\n次はディーラーのターンです。"
break
end
end
if player.score > 21
puts "ディーラーの勝ちです。"
end
# ディーラーのターン
puts "ディーラーの2枚目のカードは#{dealer.card.info}でした。"
dealer.calc_score
dealer.present_score
while dealer.score <= 17
dealer.turn(deck)
end
# 結果発表
player.result
dealer.result
if dealer.score > 21
puts "あなたの勝ちです!"
else
judge(player, dealer)
end
puts "ブラックジャック終了!また遊んでね!"
end
end
game = BlackJack.new
game.play
ゲームの実行を担当するクラスです。
まだ全部読んではいませんが、オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方 では
単一の責任をもつクラスを作る
とのことなので、そこを意識してクラスの設計を行なっていきました。
Deckクラス
class Deck
attr_reader :cards
def initialize
@cards
create
end
def create
@cards = [
Card.new('ハート', 'A'),
Card.new('ハート', 2),
Card.new('ハート', 3),
Card.new('ハート', 4),
Card.new('ハート', 5),
Card.new('ハート', 6),
Card.new('ハート', 7),
Card.new('ハート', 8),
Card.new('ハート', 9),
Card.new('ハート', 10),
Card.new('ハート', 'J'),
Card.new('ハート', 'Q'),
Card.new('ハート', 'K'),
Card.new('クローバー', 'A'),
Card.new('クローバー', 2),
Card.new('クローバー', 3),
Card.new('クローバー', 4),
Card.new('クローバー', 5),
Card.new('クローバー', 6),
Card.new('クローバー', 7),
Card.new('クローバー', 8),
Card.new('クローバー', 9),
Card.new('クローバー', 10),
Card.new('クローバー', 'J'),
Card.new('クローバー', 'Q'),
Card.new('クローバー', 'K'),
Card.new('スペード', 'A'),
Card.new('スペード', 2),
Card.new('スペード', 3),
Card.new('スペード', 4),
Card.new('スペード', 5),
Card.new('スペード', 6),
Card.new('スペード', 7),
Card.new('スペード', 8),
Card.new('スペード', 9),
Card.new('スペード', 10),
Card.new('スペード', 'J'),
Card.new('スペード', 'Q'),
Card.new('スペード', 'K'),
Card.new('ダイヤ', 'A'),
Card.new('ダイヤ', 2),
Card.new('ダイヤ', 3),
Card.new('ダイヤ', 4),
Card.new('ダイヤ', 5),
Card.new('ダイヤ', 6),
Card.new('ダイヤ', 7),
Card.new('ダイヤ', 8),
Card.new('ダイヤ', 9),
Card.new('ダイヤ', 10),
Card.new('ダイヤ', 'J'),
Card.new('ダイヤ', 'Q'),
Card.new('ダイヤ', 'K')
]
end
def take_out_card
@cards.last
end
def remove(card)
@cards.delete(card)
end
def shuffle
i = @cards.length - 1
while (i > 0)
j = rand(@cards.length - 1)
@cards[j], @cards[i] = @cards[i], @cards[j]
i -= 1
end
return @cards
end
end
Deckクラスが一番悩んだ部分で、特にデッキを作成する所は自分でもひどいと思っています。
絶対もっといい方法があるとは思うですが、思い付かなかったのでゴリ押ししました。
shuffle
メソッドはドットインストールのJavaScriptの講座でフィッシャー・イェーツのアルゴリズムなるものを
知ったので、それをrubyで実装してみました。これで合ってるのかな。。。
Cardクラス
class Card
attr_reader :suit, :number
def initialize(suit, number)
@suit = suit
@number = number
end
def info
"#{suit}の#{number.to_s}"
end
def convert_number
if number == 'A'
1
elsif number == 'J' || number == 'Q' || number == 'K'
10
else
number
end
end
end
書いてて思ったのが、メソッドの名前を考えるのが難しいということです。
リーダブルコード読まないと。
Userクラス
class User
attr_reader :hands, :score, :role
def initialize
@hands = []
@score = 0
end
def draw_card(deck)
card = deck.take_out_card
hands << card
deck.remove(card)
end
def present_a_card
puts "#{role}の引いたカードは#{card.info}です。"
end
def calc_score
score = 0
hands.each do |card|
score += card.convert_number
end
@score = score
end
def present_score
puts "#{role}の現在の得点は#{score}点です。"
end
def result
puts "#{role}の得点は#{score}点です。"
end
def card
hands.last
end
def turn(deck)
draw_card(deck)
present_a_card
calc_score
present_score
end
end
calc_score
メソッドはどのクラスが持つべきなのか、Cardクラスに移譲した方が
いいんじゃないかとも思いましたが、結局どちらがいいのか分からないままUserクラスで実装。
あと、card
メソッドは、hands.last.suit
といった処理がたくさん出てきたので追加しました。
何故かチェーンが2つ以上続くと不安になります。。。
class Player < User
def initialize
super
@role = 'あなた'
end
def first_turn(deck)
2.times do
draw_card(deck)
present_a_card
end
end
end
class Dealer < User
def initialize
super
@role = 'ディーラー'
end
def first_turn(deck)
draw_card(deck)
present_a_card
draw_card(deck)
puts "ディーラーの2枚目のカードは分かりません。"
end
end
PlayerとDealerには共通の処理が多いので、継承を利用しました。
ただし、最初のターンだけはDealerは2枚目に引いたカードを見せないという条件があるので、
first_turn
メソッドをそれぞれ追加しました。
最後に
チェリー本のおかげで初心者向けの問題であれば、とりあえず実装出来るくらいにはなったのかな?と思いました。
だけどrubyらしい書き方、オブジェクト指向らしい書き方、読みやすいコードはまだ全然わかってないので、
これからはそこら辺も踏まえて学習を継続して行きたいです。
アウトプットのネタに困ったらこれ!?Ruby初心者向けのプログラミング問題を集めてみた(全10問)
次はコチラの問題にでも挑戦しようかな。
ご意見待ってます!