Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
2
Help us understand the problem. What is going on with this article?
@Moscwa

Black Jackはプレイヤーが有利説は本当か?

More than 3 years have passed since last update.

絶対誰かがすでに書いてると思いますが・・・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から、少量のビットコインがタダでもらえます。)

さて、それはさておき、結果は次の通りでした。
無題.png

わかりにくいですが、
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

あれっ あんまり変わってない?

2
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Moscwa

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
2
Help us understand the problem. What is going on with this article?