バカラの勝率の計算

カジノでバカラを遊んでみて、これはハマる人が多いのも頷けるという感想を持った。ほとんど単純な丁半博打なのですが、徐々に状態が確定していく演出があって、当たったときのフワッと気が舞い上がる感覚がクセになります。次にやったときにもっと楽しめるように、少しバカラの統計を調べて遊んでみようと思う。

今日は1回のゲームでの確率を調べてみます。モンテカルロ法でやるのも芸がないので、頑張って手計算で調べていきましょう。ただバカラのルールは複雑だから、紙と鉛筆だと間違ってしまうので、Python でコードを書きながらやっていきます。

勝率の計算

まずはプレーヤー、バンカーそれぞれの勝率を求めます。プレーヤーが勝つ場合を場合分けして、それぞれの確率を求めて、足し合わせるというトップダウンのアプローチでも計算できますが、今回はいろいろな条件下での確率を計算してみたいので、ボトムアップなアプローチでやってみたいと思います。結局はいずれでも同じことですが。

まず、プレーヤーとバンカーにカードが全て配られて手札が確定した状態のすべての場合のそれぞれの確率を計算してしまいましょう。紙と鉛筆だとかなりつらいですが、計算機上でやっているのでこういうことができます。

手札の状態というか場の状態を表すデータ構造 Hand を定義します。

Hand = namedtuple('Hand', ["player", "banker"])

player フィールドがプレーヤーのカード、banker フィールドがバンカーのカードで、それぞれに配られたカードのタプルが入ります。例えば プレーヤーが A, J, 5 でバンカーが 2, 3, 4 のカードを順に引いたらなら、Hand(player=(1,0,5), banker=(2,3,4)) となります。3枚目を引かない場合は タプルの3つ目は None です。10, J, Q, K を区別する必要はないのでこれらのカードはすべて0で表しましょう。

この Hand 型の値 をキーとして、Hand が示す手札の状態になる確率を p_hand連想配列に入れていきます。

p_hand = dict()

すべてのカードの場合を for ループを回してそれぞれの確率を計算してきます。

# すべての最初2枚の場合について
for p1, p2, b1, b2 in product(list(range(10)), repeat=4):

    # 最初2枚の点数
    p = (p1 + p2) % 10
    b = (b1 + b2) % 10

    # プレーヤー、バンカーが両方とも2枚のとき。
    if p >= 8 or b >= 8 or (6 <= p <= 7 and 6 <= b <= 7):
        hand = Hand(player=(p1,p2,None), banker=(b1,b2,None))
        p_hand[hand] = p_cards(p1,p2,b1,b2)
        continue

    # プレーヤーが3枚目を引く。
    if p <= 5:
        for p3 in range(10):
            # テーブルに従って、バンカーが3枚目を引くかを判断する。
            if HIT_TABLE[b][p3]:
                for b3 in range(10):
                    hand = Hand(player=(p1,p2,p3), banker=(b1,b2,b3))
                    p_hand[hand] = p_cards(p1,p2,p3,b1,b2,b3)
                continue

            # バンカーは3枚目を引かない。
            hand = Hand(player=(p1,p2,p3), banker=(b1,b2,None))
            p_hand[hand] = p_hand[hand] = p_cards(p1,p2,p3,b1,b2)
        continue

    # バンカーのみ3枚目を引く
    for b3 in range(10):
        hand = Hand(player=(p1,p2,None), banker=(b1,b2,b3))
        p_hand[hand] = p_cards(p1,p2,b1,b2,b3)

さて、p_cards は引数のカードを引く確率となります。for ループで場の状態を全て数え上げて、それぞれの状態になる確率を求めていっています。p_cards は以下で計算しました。

def p_cards(*cards):
    """引数に与えられたカードを引く確率"""
    # 8 デッキと仮定する。
    N = 8
    # 0 から 9 のカードの枚数。
    deck = [4 * 13 * N] + [13 * N for i in range(1, 10)]

    p = 1.0
    for c in cards:
        # ほしいカードがなくなった場合は確率0
        if deck[c] <= 0:
            return 0.0
        p *= deck[c] / sum(deck)
        deck[c] -= 1
    return p

8デッキとして、カードの山の中のほしいカードの割当をかけあわせています。カードが尽きた場合は減らさずに0を返します。

2ゲーム目以降はカードが減っているので、以前のゲームの結果を反映すると確率は変わってきます。いわゆるカウンティングは呼ばれる必勝法はこの原理を利用します。しかし、終わったゲームの知識を反映しない立場に立つなら、何回目のゲームでも欲しいカードを引く確率は同じです。そのため、deck を毎回同じ値で初期化しても結果は変わりません。

HIT_TABLE は以下です。ディーラはこれを暗記して間違えないようにしなければならないので、大変ですね。人がやる仕事ではないが、人がやるから面白い。娯楽とはそういうものです。

# 縦が Banker の2枚目までの点数、横がPlayer の3枚目のカード
HIT_TABLE = [
    [1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,0,1],
    [0,0,1,1,1,1,1,1,0,0],
    [0,0,0,0,1,1,1,1,0,0],
    [0,0,0,0,0,0,1,1,0,0],
    [0,0,0,0,0,0,0,0,0,0],
]

これで1ゲームが終わったときのすべての状態とその確率を挙げることができました。全部で 339400 通りの状態がありました。

プレーヤ、バンカーそれぞれの勝率、タイとなる確率は、状態群から欲しい場合をフィルターして確率を足し合わせれば計算できます。

def point(hand):
    return sum(card for card in hand if card) % 10

player_win = sum(p for h, p in p_hand.items() if point(h.player) > point(h.banker))
banker_win = sum(p for h, p in p_hand.items() if point(h.player) < point(h.banker))
tie = sum(p for h, p in p_hand.items() if point(h.player) == point(h.banker))

結果以下のような値を得ました。

Winner Probability
Player 0.446177979
Banker 0.458480647
Tie 0.095341374

それっぽい値を得ることができました。サイトによって値が違うのはデッキの数など置いている仮定が違ったりしているのだと思われます。

次に色々な場合の確率を求めていきましょう。

カードを引く枚数

プレーヤの毎数 バンカーの毎数 確率
2 2 0.378750705
3 2 0.185579254
2 3 0.117666050
3 3 0.318003992

4割弱のゲームは最初の2枚で終わります。またバンカーだけ引く場合は少ないようです。感覚にあってますね。

最初2枚が分かったあとの勝率

バカラの醍醐味は、3枚目があとから配られたり、カードを絞ってめくったりして、状態が徐々に確定していくところですよね。最初2枚が確定している状態で、その後の勝率を見ていきましょう。

バンカー、プレーヤーのいずれかの最初2枚が明らかな状態での、もう片方の勝率は以下のとおりとなります。ただし、引き分けの場合は除いています。

バンカーの最初2枚の点数 プレーヤーの勝率 プレーヤーの最初2枚の点数 バンカーの勝率
0 0.699536232 0 0.701654826
1 0.680185869 1 0.695725285
2 0.662796859 2 0.685655013
3 0.645613308 3 0.669359519
4 0.613826427 4 0.642328585
5 0.556476320 5 0.600026889
6 0.497409035 6 0.498438328
7 0.332291914 7 0.332291914
8 0.104645082 8 0.104645082
9 0.000000000 9 0.000000000

基本的に点数が9 に違いほど有利です。相手が9だとすでに負けが確定していて、 8でもほぼ負けです。しかし、相手が 0 であっても、油断はできないことがわかります。絞るときの念じ方が重要だと言えるでしょう。

3枚目以降の逆転の可能性

双方の最初の2枚が確定して、その後に逆転する可能性を調べてみます。

最初2枚のプレーヤーとバンカーの点数差とプレーヤーの勝率の関係は以下のとおりです。2枚目で勝敗が確定している場合と引き分けの場合は除外しています。

点数差 プレーヤーの勝率
-7 0.166794800
-6 0.222154613
-5 0.256331981
-4 0.286572484
-3 0.307923668
-2 0.331206776
-1 0.387202181
0 0.496529573
1 0.595966939
2 0.640109980
3 0.662224521
4 0.689246802
5 0.720494534
6 0.782535047
7 0.833205200

基本的に点数差が大きい方が有利なようです。しかし、3枚目以降のカードで逆転の可能性も残されています。適度に逆転の可能性があって、ゲームバランスがよいと感じました。ただカードが引かれるのを眺めているだけなのですが、このあたりのハラハラドキドキがバカラの魅力だと思います。