Pythonですべてのアクションを網羅したブラックジャックを作ってみる(その1)
目次
ブラックジャックの還元率の高さを確かめたいのがモチベーション
ブラックジャックはカジノの中でも還元率の高いゲームとして知られています。
ルーレットは還元率の高いヨーロピアンルーレットでも還元率が97.3%であるのに対し、ブラックジャックは98~102%と言われています。
(ヨーロピアンルーレットの特殊ルールでアンプリゾン方式やラパルタージュ方式が採用されているカジノに行けば98.6%の還元率になりますが、そのようなカジノは少数です)
なぜ98~102%までの幅があるのかと言うと、ベーシックストラテジーと呼ばれる定石に沿ってプレーすれば98%、それにカウンティングというテクニックを使うことによって最大4%上積みされるためです。
還元率が100%を超えるということは、ゲームを多く繰り返すほど手持ちのお金が増えていく確率が高いということです。
本当にそんなにうまい話がこの世に存在するのでしょうか?
それをシミュレーションで確かめたくなりました。
ところがブラックジャックにはプレーヤーが打てるアクションにいろいろな選択肢があり、すべてを網羅するシミュレーションソフトを作るのは困難そうに思えます。
それに敢えて挑戦してみることにしました。
今回はその第一弾です。
続編で機能追加していきます。
今回のコードで網羅する機能
- デックをシャッフルする
- はじめに配られる2枚の手札がナチュラルブラックジャックになっているかを判断する
- ディーラーの2枚の手札は最初の1枚だけを公開する
- ヒットかスタンドを選択し、ヒットなら1枚カードを追加した後に新しいスコアを計算し、バスト(21を超える)なら負けを確定させる
- スタンドならディーラーのスコアが17以上になるまでカードを追加し、プレーヤーのスコアと比較してプレーヤーの勝ち負けを確定させる
- ディーラーがバストすればプレーヤーの勝ちを確定させる
- Aの値は基本は1とカウントするが、手札にAが含まれスコアが11以下の場合にはAを11とカウントする(ソフトハンド)
- Aを11とカウントしたがスコアが22以上となる場合には再びAを1とカウントする(ハードハンド)
- はじめに配られる2枚のカードを見てダブルダウンを選択でき、選択した場合には賭金を2倍にしてあと1枚だけ引きスタンドする
Pythonコード
冗長なコードだということはわかっています。
シンプルなコードにして下さる方がいらっしゃれば有り難いです。
自分でも続編で機能増強しながら改良していくつもりです。
import random
suits = ['♤', '♡', '☘', '♢'] #スートの定義
cards = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'] #数の定義
values = {'A': 1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, '10':10, 'J':10, 'Q':10, 'K':10} #値の定義
class shuffle_deck: #デックをシャッフルする
def __init__(self):
self.deck = []
for suit in suits:
for card in cards:
self.deck.append((card, suit))
def shuffle(self):
random.shuffle(self.deck)
return self.deck
class draw_card: #カードを1枚引く
def __init__(self, deck):
self.deck = deck
def draw(self):
if len(self.deck) == 0:
return None
card = self.deck.pop(0)
return card
class value_card: #カードの値を判断
def __init__(self, card, ace_flg):
self.card = card
self.ace_flg = ace_flg
def value(self):
key = self.card[0]
value = values[key]
if value == 1:
self.ace_flg = True
return value, self.ace_flg
class update_value: #手札のスコアを更新
def __init__(self,current_value, card, soft_flg, ace_flg):
self.current_value = current_value
self.card = card
self.soft_flg = soft_flg
self.ace_flg = ace_flg
def update(self):
Value = value_card(self.card, self.ace_flg)
value, self.ace_flg = Value.value()
updated_value = self.current_value + value
if updated_value < 12 and self.ace_flg:
self.soft_flg = True
updated_value += 10
if updated_value > 21 and self.soft_flg:
self.soft_flg = False
updated_value -= 10
return updated_value, self.soft_flg, self.ace_flg
class bet: #最初に2枚ずつ配られた手札のスコアを計算
p_score = 0
p_soft_flg = False
p_ace_flg = False
d_score = 0
d_soft_flg = False
d_ace_flg = False
deck = []
def init_draw(self):
Shuffle = shuffle_deck()
bet.deck = Shuffle.shuffle()
score = 0
ace_flg = False
soft_flg = False
Deck = draw_card(bet.deck)
card = Deck.draw()
p_1 = card
Update = update_value(score, card, soft_flg, ace_flg)
bet.p_score, bet.p_soft_flg, bet.p_ace_flg = Update.update()
Deck = draw_card(bet.deck)
card = Deck.draw()
d_1 = card
Update = update_value(score, card, soft_flg, ace_flg)
bet.d_score, bet.d_soft_flg, bet.d_ace_flg = Update.update()
Deck = draw_card(bet.deck)
card = Deck.draw()
p_2 = card
Update = update_value(bet.p_score, card, bet.p_soft_flg, bet.p_ace_flg)
bet.p_score, bet.p_soft_flg, bet.p_ace_flg = Update.update()
hand_p = 'ソフト' if bet.p_soft_flg == True else 'ハード'
print('あなたの手札は {} と {} です。'.format(p_1, p_2))
print('あなたのスコアは {} 、{}ハンドです。'.format(bet.p_score, hand_p))
Deck = draw_card(bet.deck)
card = Deck.draw()
d_2 = card
Update = update_value(bet.d_score, card, bet.d_soft_flg, bet.d_ace_flg)
bet.d_score, bet.d_soft_flg, bet.d_ace_flg = Update.update()
hand_d = 'ソフト' if bet.d_soft_flg == True else 'ハード'
print('ディーラーの手札は {} です。'.format(d_1))
Judge = judge()
Judge.choice()
class judge(bet): #ナチュラルブラックジャックでなければダブルダウンするかどうかを選択
pass
def choice(self):
if bet.p_score == 21:
print('ナチュラルブラックジャック!あなたの勝ちです!')
else:
double_choice = input('ダブルダウンしますか?【y or n】:')
if double_choice == 'y':
Double = double()
Double.run()
else:
Play = play()
Play.run()
class double(bet): #ダブルダウンを選択した場合の結果
pass
def run(self):
Deck = draw_card(bet.deck)
card = Deck.draw()
Update = update_value(bet.p_score, card, bet.p_soft_flg, bet.p_ace_flg)
bet.p_score, bet.p_soft_flg, bet.p_ace_flg = Update.update()
hand_p = 'ソフト' if bet.p_soft_flg == True else 'ハード'
hand_d = 'ソフト' if bet.d_soft_flg == True else 'ハード'
print('あなたのスコアは {} 、{}ハンドです。'.format(bet.p_score, hand_p))
while bet.d_score < 17:
Deck = draw_card(bet.deck)
card = Deck.draw()
Update = update_value(bet.d_score, card, bet.d_soft_flg, bet.d_ace_flg)
bet.d_score, bet.d_soft_flg, bet.d_ace_flg = Update.update()
print('ディーラーのスコアは {} 、{}ハンドです。'.format(bet.d_score, hand_d))
if bet.p_score > 21:
print('バストしました!あなたの負けです!')
repeat_flg = False
else:
if bet.d_score > 21:
print('ディーラーがバストしました!あなたの勝ちです!')
elif bet.d_score == bet.p_score:
print('ドローです!')
elif bet.d_score > bet.p_score:
print('あなたの負けです!')
else:
print('あなたの勝ちです!')
class play(bet): #ヒットまたはスタンドを何回か選択した後、結果を表示
pass
def run(self):
repeat_flg = True
while repeat_flg:
choice = input('ヒットしますか?スタンドしますか?【h or s】:')
if choice == 'h':
Deck = draw_card(bet.deck)
card = Deck.draw()
Update = update_value(bet.p_score, card, bet.p_soft_flg, bet.p_ace_flg)
bet.p_score, bet.p_soft_flg, bet.p_ace_flg = Update.update()
hand_p = 'ソフト' if bet.p_soft_flg == True else 'ハード'
hand_d = 'ソフト' if bet.d_soft_flg == True else 'ハード'
print('あなたのスコアは {} 、{}ハンドです。'.format(bet.p_score, hand_p))
print('ディーラーのスコアは {} 、{}ハンドです。'.format(bet.d_score, hand_d))
if bet.p_score > 21:
print('バストしました!あなたの負けです!')
repeat_flg = False
elif choice == 's':
while bet.d_score < 17:
Deck = draw_card(bet.deck)
card = Deck.draw()
Update = update_value(bet.d_score, card, bet.d_soft_flg, bet.d_ace_flg)
bet.d_score, bet.d_soft_flg, bet.d_ace_flg = Update.update()
hand_p = 'ソフト' if bet.p_soft_flg == True else 'ハード'
hand_d = 'ソフト' if bet.d_soft_flg == True else 'ハード'
print('あなたのスコアは {} 、{}ハンドです。'.format(bet.p_score, hand_p))
print('ディーラーのスコアは {} 、{}ハンドです。'.format(bet.d_score, hand_d))
if bet.d_score > 21:
print('ディーラーがバストしました!あなたの勝ちです!')
elif bet.d_score == bet.p_score:
print('ドローです!')
elif bet.d_score > bet.p_score:
print('あなたの負けです!')
else:
print('あなたの勝ちです!')
repeat_flg = False
else:
print('もう一度正確に入力して下さい。【h or s】:')
Bet = bet()
Bet.init_draw()
実行結果
はじめのスコアが高くてスタンドしたら勝った!
あなたの手札は ('J', '♤') と ('9', '♡') です。
あなたのスコアは 19 、ハードハンドです。
ディーラーの手札は ('8', '♡') です。
ダブルダウンしますか?【y or n】:n
ヒットしますか?スタンドしますか?【h or s】:s
あなたのスコアは 19 、ハードハンドです。
ディーラーのスコアは 22 、ハードハンドです。
ディーラーがバストしました!あなたの勝ちです!
はじめのスコアが低かったのでヒットしたら負けた!
あなたの手札は ('4', '♤') と ('Q', '♢') です。
あなたのスコアは 14 、ハードハンドです。
ディーラーの手札は ('J', '♤') です。
ダブルダウンしますか?【y or n】:n
ヒットしますか?スタンドしますか?【h or s】:h
あなたのスコアは 22 、ハードハンドです。
ディーラーのスコアは 20 、ハードハンドです。
バストしました!あなたの負けです!
はじめのスコアが低かったけどダブルダウンしたら勝った!
あなたの手札は ('A', '♤') と ('3', '♢') です。
あなたのスコアは 14 、ソフトハンドです。
ディーラーの手札は ('5', '☘') です。
ダブルダウンしますか?【y or n】:y
あなたのスコアは 20 、ソフトハンドです。
ディーラーのスコアは 17 、ハードハンドです。
あなたの勝ちです!
ナチュラルブラックジャックで勝った!
あなたの手札は ('A', '♢') と ('J', '♢') です。
あなたのスコアは 21 、ソフトハンドです。
ディーラーの手札は ('Q', '♢') です。
ナチュラルブラックジャック!あなたの勝ちです!
つづく…