プログラム化する際には、対象とする事象をどんなデータ構造で表すかを きちんと設計できれば、残りは自然と決まってくる。今回は既に、トランプの カードをどのように表現するか3パターンで考察してあるので、それらのうちか らブラックジャックの作成に有利なものを採択すればい。
問題がさほど複雑ではないので、今回の場合3つのデータ構造にはっきりした 優劣は見当たらず、どれを選んでも差し支えない。そこで採択する規準として「ハッ シュの練習」を重視することにして、今回は3番目の「53個のハッシュを配列」に したデータ構造を採択してプログラム化を進めよう。
簡略化して設計したゲームを実現するために必要な部品を考えていこう。
まずは、どんなデータをどういう変数に格納する必要があるかを考えるとこ ろから開始する。データの格納形式によって自ずとそれを処理するのに必要なメ ソッドが決まってくる。
シャッフルされたカードを保持する配列が必要なのは 前回触れたとおりである。さらに、コンピュータに配られたカード と、プレイヤーに配られたカードを保持する配列も必要だろう。
あとは、コンピュータ、プレイヤ両者の得点を記録する変数も必要だが、こ れは単純な数値なので、とくに難しくないだろう。
もう一度実行例を確認しよう。
% ./blackjack.rb ゲーム開始 わたしの手: [ ??? ] ハートのK あなたの手: ダイヤの3 クラブのK もう一枚引きますか?(yかnで) y あなたの手: ダイヤの3 クラブのK ハートの7 もう一枚引きますか?(yかnで) n 勝負! わたしの手: ダイヤの8 ハートのK あなたの手: ダイヤの3 クラブのK ハートの7 わたし 18 : 20 あなた で、あなた の勝ちです!
上記の実行例を見ると、
が必要だと分かる。これらのメソッドを設計していこう。
カードを配るためにはまず、持ちカードを保存する配列と、
その配列にカードを格納していく部分を作らなければならない。
コンピュータが持っているカードの配列を com
変数、プレイヤーの
持っているカードの配列を player
変数、
にそれぞれ格納することにしよう。
カードを52枚作り、シャッフルする部分を作る。 これは前回作成した2つのメソッドのままでよいだろう。 ただし、ジョーカーは作らないものとする。
# カード1枚の情報を持つハッシュを52個入れる配列を作るメソッド
def createCardsHashArray()
card = Array.new # 空の配列を作る
for suit in ["ハート", "スペード", "ダイヤ", "クラブ"]
card << {"suit" => suit, "n" => "A"}
# ここで代入しているのがハッシュ
2.upto(10) do |n| # nが2から10まで変動して繰り返す
card << {"suit" => suit, "n" => n.to_s}
end
for n in ["J", "Q", "K"] # nに"J","Q","K"が順に代入される
card << {"suit" => suit, "n" => n}
end
end
# 前回はここにジョーカー作成があった。それを削除。
card # 最後に card配列 を呼び主に返す
end
# 受け取った配列をシャッフルするメソッド
# これは前回のものと全く同じ
def shuffle(a)
srand
0.upto(a.length-1) do |i|
j = rand(a.length) # 交換相手をランダムに選ぶ
w = a[i]
a[i] = a[j]
a[j] = w
end
a
end
カードを配る部分を作る。
カードを1枚ずつ配るには、カードを格納した配列の先頭から順に要素 を取り出していく。そのためには、今何枚目かを覚えておかなければなら ないので、そのための変数を用意して、順に取り出す。
com = Array.new player = Array.new cardNo = -1 # 今何枚目かを保存する変数 cards = shuffle(createCardsHashArray()) # コンピュータの配列comに2枚配る。 com << cards[cardNo+=1] # +=1 すると 1足した結果自体も返す com << cards[cardNo+=1] # 2枚目を配る # プレイヤの配列playerに2枚配る。 player << cards[cardNo+=1] # +=1 すると 1足した結果自体も返す player << cards[cardNo+=1] # 2枚目を配る
配られたカードを表示する部分を作る。
まずは、カードを1枚だけ引数で受け取ってそれを表示するメソッドを 作ろう。
def dispCard(card) 〜〜〜定義〜〜〜 end
となるのだが、少し詳しく考察しよう。 cardには1枚分のハッシュが入る。たとえば、以下のとおり。
{"suit" => "ハート", "n" => "4"}
ここから、スートと数を取り出して表示するには添字に keyを指定すればよいので。
def dispCard(card) printf("%s の %s\n", card["suit"], card["n"]) end
となる。これを、コンピュータの手、プレイヤーの手について表示す ればよい。ただし、コンピュータは1枚隠しておくので、2枚目だけ表示す る。
puts "わたしの手:" # コンピュータの1枚目を表示 puts " [ ??? ]" # コンピュータの2枚目を表示 dispCard(com[1]) # 2枚目は com[1] に入っている # プレイヤー側 puts "あなたの手:" dispCard(player[0]) dispCard(player[1])
コンピュータ、プレイヤー側、いずれも配列に全ての持ちカードが入ってい る。これらの数の部分を足して計算するメソッドが必要となる。計算のときの規 則は以下のようになる。
"2"〜"9"
なら整数化(.to_i
)してそのまま足す。
"10", "J", "Q", "K"
なら10を足す。
"A"
なら11を足す。21を超過したら10を引く。
問題となるのは、"A"
の処理である。21を超過した場合に
「"A"
があった場合のみ10を引く」という処理が必要になる、しか
も"A"
が複数枚あった場合は、枚数分だけ「21を超過していたら10
引く」を繰り返す必要がある。ということで、合計点メソッドの要点は、
"A"
の枚数を数える変数を用意する。
"A"
は11で計算する。
"A"
の枚数を上限として10を引く」を繰り返す。
となる。これをメソッドにすると以下のようになる。
def point(arr) ace = 0 # "A"の枚数 sum = 0 # 合計点 for card in arr # arr配列全ての要素に対して繰り返す n = card["n"] # card["n"]で数を表すvalueが得られる case n # nの値によって場合分け when "A" # "A"ならば… ace += 1 # 枚数を1増やしておく sum += 11 # まずは11点として加算 when "10", "J", "Q", "K" sum += 10 # 絵札と10は10点 else # それ以外(つまり2〜9)はそのものを整数にして足す sum += n.to_i end # when終わり end # for終わり while sum > 21 && ace > 0 # 21より大きく、かつ、"A"の枚数が1以上なら sum -= 10 # 10を引く ace -= 1 # "A"を1つ使ったので減らす end sum # 最後に合計点を呼び主に返す end
カードを配り終わったあとで、プレイヤーにもう一枚引くか確認する。 これは、プレイヤーからの入力が y(es) である間繰り返せばよい。y なら プレイヤーの持ちカードに一枚追加する。
while true STDERR.print "もう一枚引きますか?(yかnで) " answer = gets if /^y/i =~ answer player << cards[cardNo+=1] puts "もう一枚引きます。引いたカードはこれです。" dispCard(player[-1]) else break # while true を抜ける end end
プレイヤーが引き終わったあとで、コンピュータがさらに一枚引くか決める。 これはコンピュータの持ちカードの得点が16以下である間繰り返す。
while point(com) <= 16 puts "わたしはもう一枚引きます" com << cards[cardNo+=1] end
プレイヤー、コンピュータ、ともに必要なだけカードを引き直したら、 最後に両者の手を全て表示して比べる。
puts "勝負!" puts "わたしの手:" for c in com # コンピュータの手持ちカード全て繰り返す dispCard(c) end puts "あなたの手:" player.each do |c| # プレイヤーの手持ちカード全て繰り返す dispCard(c) end # 得点を比べる printf("わたし %d : %d あなた", point(com), point(player))
以上でプログラムの骨格は揃った。これらを全て組み合わせて プログラムが完成する。
#!/usr/koeki/bin/ruby def createCardsHashArray() card = Array.new # 空の配列を作る for suit in ["ハート", "スペード", "ダイヤ", "クラブ"] card << {"suit" => suit, "n" => "A"} # ここで代入しているのがハッシュ 2.upto(10) do |n| card << {"suit" => suit, "n" => n.to_s} end for n in ["J", "Q", "K"] card << {"suit" => suit, "n" => n} end end card # 最後に card配列 を呼び主に返す end def shuffle(a) srand 0.upto(a.length-1) do |i| j = rand(a.length) # 交換相手をランダムに選ぶ w = a[i] a[i] = a[j] a[j] = w end a end def dispCard(card) printf("%s の %s\n", card["suit"], card["n"]) end def point(arr) ace = 0 # "A"の枚数 sum = 0 # 合計点 for card in arr # arr配列全ての要素に対して繰り返す n = card["n"] # card["n"]で数が入る case n # nの値によって場合分け when "A" # "A"ならば… ace += 1 # 枚数を1増やしておく sum += 11 # まずは11点として加算 when "10", "J", "Q", "K" sum += 10 # 絵札と10は10点 else # それ以外(つまり2〜9)はそのものを整数にして足す sum += n.to_i end # when終わり end # for終わり while sum > 21 && ace > 0 # 21より大きく、かつ、"A"の枚数が1以上なら sum -= 10 # 10を引く ace -= 1 # "A"を1つ使ったので減らす end sum end # カードを作ってシャッフルしたものを cards配列に格納 cards=shuffle(createCardsHashArray) # カードを数える変数。-1で初期化 cardNo=-1 # コンピュータの持ちカードを格納する配列 com=[] com << cards[cardNo+=1] com << cards[cardNo+=1] # プレイヤーの持ちカードを格納する配列 player=[] player << cards[cardNo+=1] player << cards[cardNo+=1] # 最初の手持ちを表示する puts "わたしの手:" # コンピュータの1枚目を表示 puts " [ ??? ]" # コンピュータの2枚目を表示 dispCard(com[1]) # 2枚目は com[1] に入っている # プレイヤー側 puts "あなたの手:" dispCard(player[0]) dispCard(player[1]) # プレイヤーが満足するまで引かせる while true STDERR.print "もう一枚引きますか?(yかnで) " answer = gets if /^y/i =~ answer player << cards[cardNo+=1] puts "もう一枚引きます。引いたカードはこれです。" dispCard(player[-1])# [-1]は配列の最後の要素 else break # while true を抜ける end end # コンピュータが16以下なら引き続ける while point(com) <= 16 puts "わたしはもう一枚引きます" com << cards[cardNo+=1] end # いよいよ勝負 puts "勝負!" puts "わたしの手:" for c in com # コンピュータの手持ちカード全て繰り返す dispCard(c) end puts "あなたの手:" player.each do |c| # プレイヤーの手持ちカード全て繰り返す dispCard(c) end # 得点を比べる cp, pp = point(com), point(player) printf("わたし %d : %d あなた\n", cp, pp) if cp >= 22 # 22以上なら0点ということにしてしまう cp = 0 # (コンピュータ) end if pp >= 22 # 22以上なら0点ということにしてしまう pp = 0 # (プレイヤー) end if pp > cp # コンピュータより点が高ければ puts "あなたの勝ちです。参りました!" else # そうでなければ(引き分け含む) puts "わたしの勝ちです。べろべろばあ〜" end