ブラックジャックでの戦略をさらに少しだけ考えてみる

モデル化とシミュレーション

こんにちは。前回に引き続き、ブラックジャックの戦略を考えてみます。

新しい方針として

ブラックジャックのルールをもう一度見直してみます。

ディーラーは17を超えるまでカードを引き続けなければなりません。

ということは、ディーラーの手としてあり得るのは、バースト・17・18・19・20・21のいずれかしかありません。

そのため、プレイヤーが16以下で勝つには、ディーラーがバーストするのを期待するしかありません。

同じように、プレイヤーの手が18だった場合には、ディーラーがバーストか17のときに勝ち、18のとき引き分け、19~21のときに負けになります。

ディーラーの手がどうなるかは、プレイヤーからはアップカード(表になっているカード)からしか判断できないのですが、アップカードごとの確率は以前求めていました。

プレイヤーの手とディーラーのアップカードに対応する確率をもとに検討してみます。

今回の作戦

それでは、今回はとりあえず今までより負けないようにしたいということで、その時点でのプレイヤー手とディーラーのアップカードから予想される合計を比較して、負けない(勝つか引き分ける)確率が40%を超えるまでカードを引き続けてみます。

たぶん確率じゃなくて期待値で考えなければならないと思うのですが、プログラムを書き直す余裕がないという裏の事情で確率で判断することにします。

そのプログラムです。解説は省きます。

import random

def draw():
    x = random.randint( 0, 51 )
    while x in ca:
        x = random.randint( 0, 51 )
    ca.append(x)
    return x

def calc( hand ):
    s = 0
    flag = False
    for h in hand:
        s += 10 if ( h % 13 ) > 9 else ( ( h % 13 ) + 1 )
        if h % 13 ==0:
            flag = True
    if s < 12 and flag:
        s += 10
    return s

upcard_prob = { 2 : [ 0.3533, 0.1386, 0.1325, 0.1321, 0.1236, 0.1199 ],
                3 : [ 0.3758, 0.1307, 0.1313, 0.1235, 0.1228, 0.1160 ],
                4 : [ 0.4023, 0.1308, 0.1140, 0.1218, 0.1165, 0.1147 ],
                5 : [ 0.4291, 0.1193, 0.1229, 0.1174, 0.1049, 0.1064 ],
                6 : [ 0.4205, 0.1668, 0.1062, 0.1073, 0.1010, 0.0982 ],
                7 : [ 0.2598, 0.3724, 0.1382, 0.0776, 0.0789, 0.0732 ],
                8 : [ 0.2390, 0.1313, 0.3625, 0.1294, 0.0678, 0.0700 ],
                9 : [ 0.2340, 0.1213, 0.1036, 0.3578, 0.1222, 0.0611 ],
               10 : [ 0.2141, 0.1143, 0.1131, 0.1146, 0.3293, 0.1148 ],
               11 : [ 0.1163, 0.1259, 0.1308, 0.1291, 0.1318, 0.3660 ]
               }

strategy = [ 0     , 0     , 0     , 0     , 1.0000,
             1.0000, 1.0000, 1.0000, 1.0000, 1.0000,
             1.0000, 1.0000, 0.6884, 0.6137, 0.5397,
             0.4629, 0.4005, 0.3210, 0.2446, 0.1633,
             0.0813, 0.0000 ]
point = 0
cnt = {}
wi = 0
lo = 0
dr = 0
tr = 1000000
for i in range( 0, tr ):
    ca = []
    c_hand = [ draw() ]
    d_hand = [ draw() ]
    upcard = calc( d_hand )
    c_hand.append( draw() )
    d_hand.append( draw() )
    cp = calc( c_hand )
    dp = calc( d_hand )
    calc_p = cp - 16 + 1 if cp > 16 else 0
    if cp==21 and dp!=21:
        wi += 1
        point += 1.5
        continue
    elif cp!=21 and dp==21:
        lo += 1
        point -= 1.0
        continue
    windraw_prob = upcard_prob[upcard][0] + sum( upcard_prob[upcard][1:calc_p] )
    while cp < 21 and windraw_prob < 0.4:
        c_hand.append( draw() )
        cp = calc( c_hand )
        calc_p = cp - 16 +1 if cp > 16 else 0
        windraw_prob = upcard_prob[upcard][0] + sum( upcard_prob[upcard][1:calc_p] )

    if cp > 21:
        lo += 1
        point -= 1.0
        continue
    while dp < 17:
        d_hand.append( draw() )
        dp = calc( d_hand )
    if dp > 21:
        wi += 1
        point += 1.0
        continue
    if cp > dp:
        wi += 1
        point += 1.0
    elif cp < dp:
        lo += 1
        point -= 1.0
    else:
        dr += 1
print('プレーヤー: 勝ち{}回 引き分け{}回 負け{}回'.format( wi, dr, lo ))
print('プレーヤー: 勝ち{:0.2f}% 引き分け{:0.2f}% 負け{:0.2f}% 掛金{:0.1f}'
        .format( wi*100/tr, dr*100/tr, lo*100/tr , point ))

実行結果

次のような結果が得られました。

プレーヤー: 勝ち417409回 引き分け79082回 負け503509回
プレーヤー: 勝ち41.74% 引き分け7.91% 負け50.35% 掛金-62699.0

最初にやってみた「ディーラーと同じルールで引いてみる」作戦とほとんど同じ結果です。

ディーラーに負けない確率を10%きざみで変えてみましたが、30%のときが最も高く勝率が42%程度で100万回での負けた金額は5万7千ドル程度でした。

プレーヤー: 勝ち420691回 引き分け78135回 負け501174回
プレーヤー: 勝ち42.07% 引き分け7.81% 負け50.12% 掛金-57135.5

期待値というキーワードを出してしまったので、次は期待値が大きくなることを考えてみます。

今回はこれでおしまいにします。それではまた。

Posted by 春日井 優