絶対誰かがすでに書いてると思いますが・・・w
pythonでBlackJackのアルゴリズムを組んで結局ディーラーとプレイヤーのどちらが有利なのかを検証してみたいと思います!
##ブラックジャックとは?
プレーヤー・ディーラーで引いたカードの合計値を競うゲームです。
カードの強さは次の通りです
強い 21→20→19→18→17→16以下→22以上 弱い
プレイヤー・ディーラーともに22以上の場合はプレイヤーの負けです。
①掛け金をきめて、ベットする(今回はすべて25$として行っています。)
②プレイヤー・ディーラーに2枚ずつカードが配られる
(ただし、ディーラーのカードの1枚は裏向けになっている)
③プレイヤーは次の行動ができる
1・ヒット→カードを1枚引く
2・スタンド→カードを引くのをやめ、ディーラーに番を渡す
3・ダブルダウン→掛け金を2倍にする代わりに、必ずあと1回だけひく
4・スプリット→自分のカードがぞろ目だった時に、2枚を分けて別々のゲームとして行う
5・サレンダー→掛け金の半分をディーラーにわたい、ゲームを降りる
④プレイヤーが22を超えなかった場合、ディーラーがカードを引く
(このとき、ディーラーは17以上になるまで引き続ける義務がある)
⑤最終的なカードの強さで勝敗が決まる。
※絵札はすべて10とカウントします。
※Aは1か11の都合の良いほうにカウントできます。
※プレイヤーがはじめから10又は絵札+Aのペアだった場合、ブラックジャックになり、配当が上がります。
詳しいルールはウィキペディアへどうぞ。
##検証!
なんか上のルールを見る限りは一瞬プレイヤーのほうが有利な感じもしますけど、実際はどうなんでしょうか?
と気になったので、1万回回してみることにしました。
(スプリット・ダブル・サレンダーなどを実装している時間はありませんでした・・・もしかしたらまた今度やるかもです。)
ヒットする最大値はinputします。
(例えば自分のカードの合計が14だった時にヒットするかどうか みたいなことを決めます。)
ものすごく雑で安っぽい(?)コードですがそれでもよければどうぞ・・
#設定-----------------------------------------------------------
IP=10000#ゲームを行う回数
Money=0#金額の初期値
MinChip=25#ベッド数
Stop=input()#ヒットする最大値
Stop=int(Stop)
StopA=18#ヒットするソフト(A=11)最大値
Deck=6#使用するデッキ数
Cmax=6#デッキのシャッフルを何ゲームごとにするか
#ログ-----------------------------------------------------------
MaxM=Money#もっとも儲かていた時
MinM=0#最も損をしていた時
Rate=0 #勝率
BustRate=0 #ディーラーのバストをカウント
BustRateIP=IP#ディーラー・バストレートの母数
#関数-----------------------------------------------------------
import random
import time
def functionA(E1,E2,s):#Aカード対策関数
if E1[0]=="A":
if E2[0]=="A":
return [12,s+2]
else:
return [11+int(E2[:-1]),s+1]
elif E2[0]=="A":
return [int(E1[:-1])+11,s+1]
else:
return [int(E1[:-1])+int(E2[:-1]),s]
def functionJQK(E):#JKQカード対策用関数
if E[0]=="J" or E[0]=="Q" or E[0]=="K":
return "10z"
else:
return E
def functionAA(E,XT,s):#Aカード対策2
if E[0]=="A":
return [XT+11,s+1]
else:
return [XT+int(E[:-1]),s]
#処理-----------------------------------------------------------
counter=0
Trump=["2c","2s","2h","2d","3c","3s","3h","3d","4c","4s","4h","4d"
,"5c","5s","5h","5d","6c","6s","6h","6d","7c","7s","7h","7d"
,"8c","8s","8h","8d","9c","9s","9h","9d","10c","10s","10h","10d"
,"Jc","Js","Jh","Jd","Qc","Qs","Qh","Qd","Kc","Ks","Kh","Kd"
,"Ac","As","Ah","Ad"]
if True:
for z in range(int(IP)):
s1=0 #Aカード対策
s2=0 #Aカード対策
v=0 #ディーラーBJ対策
counter+=1
if counter>Cmax:
Trump=["2c","2s","2h","2d","3c","3s","3h","3d","4c","4s","4h","4d"
,"5c","5s","5h","5d","6c","6s","6h","6d","7c","7s","7h","7d"
,"8c","8s","8h","8d","9c","9s","9h","9d","10c","10s","10h","10d"
,"Jc","Js","Jh","Jd","Qc","Qs","Qh","Qd","Kc","Ks","Kh","Kd"
,"Ac","As","Ah","Ad"]
Trump=Trump*Deck
counter=0
#プレイヤー
#トランプからランダムにひとつ抽出
R1=random.randint(0,len(Trump)-1)
PC1=Trump.pop(R1)
R2=random.randint(0,len(Trump)-1)
PC2=Trump.pop(R2)
print(PC1)
print(PC2)
#JQK対策
PC1=functionJQK(PC1)
PC2=functionJQK(PC2)
#A対策
PT=functionA(PC1,PC2,s1)[0]
s1=functionA(PC1,PC2,s1)[1]
#ディーラー
R3=random.randint(0,len(Trump)-1)
DC1=Trump.pop(R3)
R4=random.randint(0,len(Trump)-1)
DC2=Trump.pop(R4)
print(DC1,"Dealer")
#JQK対策
DC1=functionJQK(DC1)
DC2=functionJQK(DC2)
#A対策
DT=functionA(DC1,DC2,s2)[0]
s2=functionA(DC1,DC2,s2)[1]
if DT==21 and PT==21:
print("Drow")
v=1
elif DT==21:
print(DC2,"Dealer")
print("you lose!")
Money=Money-MinChip
v=1
Rate+=1
elif PT==21:
Money=Money+MinChip*1
print ("BJ!")
v=1
Rate+=1
BustRateIP-=1
#hit/stand処理
while v==0:
if PT<=Stop or (PT<=StopA and s1>0) :
RH=random.randint(0,len(Trump)-1)
HC=Trump.pop(RH)
print(HC)
HC=functionJQK(HC)
PT=functionAA(HC,PT,s1)[0]
s1=functionAA(HC,PT,s1)[1]
if PT>21:
if s1>0:
PT=PT-10
s1=s1-1
else :
Money=Money-MinChip
print("You lose!")
BustRateIP-=1
break
elif True:
print(DC2,"Dealer")
while True:
if 16<DT<22:
print (DT,"DEALER TOTAL")
if PT>DT:
print("You win!")
Money=Money+MinChip
Rate+=1
elif PT<DT:
print("You losee....")
Money=Money-MinChip
else:
print("Draw")
break
elif 21<DT:
if s2>0:
s2=s2-1
DT=DT-10
else:
print("Dealer Bust")
Money=Money+MinChip
Rate+=1
BustRate+=1
print("You Win!")
break
RH=random.randint(0,len(Trump)-1)
DHC=Trump.pop(RH)
print(DHC,"Dealer")
DHC=functionJQK(DHC)
DT=functionAA(DHC,DT,s2)[0]
s2=functionAA(DHC,DT,s2)[1]
break
print(Money)
print("")
if MaxM<Money:
MaxM=Money
elif MinM>Money:
MinM=Money
print(Money,"result")
print(MaxM,"maximum")
print(MinM,"minimum")
print(Rate*100/IP,"Rate")
print(BustRate*100/BustRateIP,"Dealer Bust Rate")
かなり無駄があるコードな気もしますが・・・・・
##結果
25ドル×1万回で25万ドル(三千万円弱)を賭けた結果がこちらになります。
#最大ヒット値12
-21850 result
75 maximum
-21950 minimum
46.9 Rate
28.515454941227688 Dealer Bust Rate
#最大ヒット値13
-14500 result
25 maximum
-14775 minimum
47.71 Rate
29.3588429563831 Dealer Bust Rate
#最大ヒット値14
-13325 result
300 maximum
-13500 minimum
47.87 Rate
29.046048613655795 Dealer Bust Rate
#最大ヒット値15
-16425 result
450 maximum
-16925 minimum
46.91 Rate
28.943196829590487 Dealer Bust Rate
#最大ヒット値16
-15675 result
275 maximum
-15750 minimum
46.24 Rate
28.960943257184965 Dealer Bust Rate
##結論
なんだかんだで結局ヒット・スタンドだけではディーラーに勝つことはできないという結論に落ち着きました。
あとは、勝率を見る限り15くらいでスタンドしておくのが一番だということがわかりました。
とはいっても、勝率47%なら、スプリット・サレンダー・ダブル入れたら勝てそうじゃないか?
##実践
実際のゲームでプレイしてみました。
crypto-games.net
リアルマネーで勝負するのは怖いけど、ノーマネーのゲームもつまらない・・・なんて思ったので、「ノーリスク」ローリターンなビットコインカジノにしました。
(your account → REWARDから、少量のビットコインがタダでもらえます。)
わかりにくいですが、
443回のベッドで0.01BTC(8000円くらい)を賭けて
勝ちが0.005BTC(4500円くらい?)
負けが0.0042BTC(3500円くらい?) という結果になりました。
(課金してないのに結果として1000円儲かってる・・・ )
予想に反して意外な結果でした。
(その時の感みたいなのが働いたりもしたのかな?w)
##追記1
サレンダー処理を追加しました。
自分がかなり不利な場合(ディーラーのカードが10かAで、プレイヤーの合計が14,15,16の時)に掛け金を半額だけ渡してゲームおります。
#最大ヒット値13
-14400.0 result
512.5 maximum
-14437.5 minimum
46.5 Rate
27.460083795719623 Dealer Bust Rate
#最大ヒット値14
-10362.5 result
12.5 maximum
-10362.5 minimum
47.06 Rate
28.72340425531915 Dealer Bust Rate
あれっ あんまり変わってない?