Pythonでブラックジャックを作成
はじめに
ラスベガスに行くことになったので、pythonでブラックジャックのプログラムを作成しようと考えた。
⚠ブラックジャックのルールはこちらのサイトで詳しく説明されている。
なぜこの記事を書こうと思ったか
ブラックジャックをPythonで作成する記事はあったが、「A」の扱いを1としか扱わず本来の11としても1としても扱う記事が少なかったので、この記事を書こうと思った。
作成手順
デッキ山の作成
初期手札の作成
プレイヤーのアクション
ディーラーのアクション
ゲームの結果
1−5を用いて実際にやってみる
1.デッキ山の作成
まずはデッキ山の作成を行なう。
格サイトによると、ベガスでは1組のデッキ(13数×4柄=52枚)を8組合わせシャッフルすることで1つのデッキ山を作成しているらしい。つまり8回連続で同じ絵柄&数字を引く可能性もあるということだ。
import random as rd
rd.seed(10)
def shuffled_deck(): # make shuffled deck
global full_decks
cards_deck = 4*[11, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10] # a cards deck A
deck_num = 8 # def deck num
full_decks = cards_deck * deck_num # number of cards deck
rd.shuffle(full_decks)
return full_decks
Aは最初から11として扱い条件によって1に変更の方が都合が良さそうなので、11として扱う。J, Q, Kは10としてしか扱わないので、デッキの際にそのまま10として作成した。
cards_deckで1組の山を定義し、full_deckで1*8組の山を定義しrandom関数を用いてシャッフルしたという流れだ。
再現性を保つためとチェックの為、rd.seedを固定しておく。
2.初期手札の作成
作成したデッキ山の上からカードを引き、引いたデッキ山のカードを逐一削除してく形でプログラムした。初期手札はプレイヤー、ディラーともに2枚すづである。
def first_hand(): # first hand for player and dealer
global dealer_hand, player_hand
dealer_hand = [] # reset the dealer hand
player_hand = [] # reset the player_hand
dealer_hand = full_decks[0:2] # dealer hand
del full_decks[0:2]
player_hand = full_decks[0:2] # player hand
del full_decks[0:2]
関数外でも用いる為、globalに設定。
dealer_hand = full_decks[0:2]では、full_decksの0&1番目を抽出し、dealer_handに格納する。そしてfull_decksの0&1番目をdelによって削除する。同じことをplayer_handでも行なう。
3.プレイヤーのアクション
プレイヤーのアクションは以下の3つ
Hit : デッキからカードを引く
Double Down: 掛け金を2倍にし1枚だけ引く
Stand : 現状の手で勝負
def players_action(player_hand):
global player_final_hand
print(player_hand)
if 11 in player_hand and 10 in player_hand: # BJ
player_final_hand = player_hand
else:
action = ''
while action != 'S': # while to become to True
if sum(player_hand) > 21:
break
else:
action = input("H: Hit, S: Stand, D: Doubledown :")
if action == 'H':
player_hand.append(full_decks[0]) # draw card from deck
del full_decks[0] # del a card from deck
if sum(player_hand) > 21 and 11 in player_hand:
player_hand[player_hand.index(11)] = 1
print(player_hand)
elif action == 'D':
player_hand.append(full_decks[0]) # draw card from deck
del full_decks[0] # del a card from deck
if sum(player_hand) > 21 and 11 in player_hand:
player_hand[player_hand.index(11)] = 1
break
else:
break
player_final_hand = player_hand
return player_final_hand
while文を用いて、ユーザからのinputが'S'になるまで処理を繰り返す。
ここでの11の扱いについては、ハンドが21を超えかつハンドに11がある場合は1に置き換える、という形で定義。
4.ディーラーのアクション
ディーラのアクションは単純だ、17以上になるまでカードを引きつづければよい。
def dealers_action(dealer_hand): # 0 < x < 17 draw, 17 <= x <= 21 stand, x > 22 Burst
global dealer_final_hand
while sum(dealer_hand) < 17:
dealer_hand.append(full_decks[0]) # draw card from deck
del full_decks[0] # del a card from deck
if sum(dealer_hand) > 21 and 11 in dealer_hand:
dealer_hand[dealer_hand.index(11)] = 1
print("Dealer's hand: ", dealer_hand)
dealer_final_hand = dealer_hand
return dealer_final_hand
デフォで11と定義したAの扱いは、ハンドが21を超えかつハンドに11がある場合は1に置き換える、という形で定義した。
5.ゲーム結果のプログラム
プレーヤーがバーストするとその時点でプレイヤーの負である。プレイヤーがBJでも相手もBJなら引き分けになる。
以上のことを踏まえて、以下に事象の順番で条件分岐を書いた。
プレイヤーがバーストした事象
引き分けの事象
プレイヤーがBJで勝つ事象
プレイヤーがBJ以外で勝つ事象
プレイヤーがバーストせず、ディーラーがバーストする事象
それ以外の事象(プレイヤーが負ける)
def play_result(player_final_hand, dealer_final_hand):
global result
if sum(player_final_hand) > 21:
result = 'lost'
elif player_final_hand == dealer_final_hand:
result = 'draw'
elif player_final_hand == [10, 11] or player_final_hand == [11, 10]:
result = 'BJ_win'
elif sum(player_final_hand) > sum(dealer_final_hand):
result = 'win'
elif sum(dealer_final_hand) > 21:
result = 'win'
else:
result = 'lost'
print(result)
今回はだた勝ち負けのプログラムとした。
6.ゲームをやってみる
1−5で定義した関数を呼び出し、実際に数回プレイする。
shuffled_deck()
x = ''
while x != 'yes':
first_hand()
players_action(player_hand)
dealers_action(dealer_hand)
play_result(player_final_hand, dealer_final_hand)
x = input('Do you wanna quit this game?(yes or no): ')
反省&今後
スプリットをサボってしまったので、スプリットを追加し、所持金とベット数でプレイできるようにする事。
BJには大負けしないと言われる、ベーシックストラテジー、カウンティングを用いて行なうパーフェクトストラテジーと呼ばれる戦略があるので、そのシュミレーションを可視化するプロラムを作ろうと考えている。