今回はブラックジャックの
シミュレーションをしてみました。


解説動画はこちら




今回は
ブラックジャックのシミュレーターを作って
遊んでみることとしました。

開発するブラックジャックのルールなどを
決めておきましょう。

こんなルールのプログラムを作ることとします。

・初期カードは52枚を4セット
・配ったら捨て残り10枚以下になったらカード交換
・プレイヤーとディーラーの2人対戦
・実行開始時、プレイヤーディーラーそれぞれカードを2枚引く
・先にプレイヤーがカードを引きディーラーが次に引く
・ディーラーは自分の手札が17以上になるまで引き続ける
・プレイヤーは18以上なら勝負、21を超えていたらバースト
・21に近い方の勝ちで掛け金の倍額をもらえ引き分けは戻し
・JとQとKは10として扱う
・ダブルダウンなし、スピリットなし、サレンダーなし
・その他特殊なルールなし
 

さて簡単なプログラムを組んでみることとします。

カードはスペード、ハート、クラブ、ダイヤの4種類で
1-13まであります。
それを4セット使うこととします。

最初は点数計算です
1は11とも計算できるルールがあるので
21を超えない場合は11として数え
21を超えても1とするようにしました。

# ライブラリ
import random

# 点数計算
def score_calc(hands):
    score = 0
    flg = False
    for hand in hands:
        s = int(hand.split(':')[1])
        if s>=10:
            score += 10
        elif s==1:
            score+=1
            flg=True
        else:
            score += s
    if score>21 and flg:
        score = 0
        for hand in hands:
            s = int(hand.split(':')[1])
            if s>=10:
                score += 10
            elif s==1:
                score+=1
            else:
                score += s
    return score

次に1ゲームの実行を行うことを考えます。

最初に2枚ずつ配り
そこからは1枚ずつ配ります。

そこまで複雑なものでも無く
プレイヤーは18以上
ディーラーは17以上になるまで
カードを引き続けます。

カードが揃ったら勝負に入り
バーストせず21に近い方が勝ちです。

# ゲーム
def game1(game_card):
    # 初期配
    player =  [game_card.pop(0)]
    player +=  [game_card.pop(0)]
    dealer =  [game_card.pop(0)]
    dealer +=  [game_card.pop(0)]
    p_flg , d_flg = False,False
    # ディール
    while True:
        # プレイヤーは18以上になるまで引く
        if score_calc(player)>=18:
            p_flg = True
        # ディーラーは17以上になるまで引く
        if score_calc(dealer)>=17:
            d_flg = True
        if not p_flg:
            player +=  [game_card.pop(0)]
        if not d_flg:
            dealer +=  [game_card.pop(0)]
        if p_flg and d_flg:
            break
    # 勝負
    p_num = score_calc(player)
    d_num = score_calc(dealer)
    win = 'draw'
    if p_num > 21 and d_num > 21:
        win = 'draw'
    elif p_num > 21 and d_num <=21:
        win = 'dealer'
    elif p_num <= 21 and d_num >21:
        win = 'player'
    elif p_num < d_num:
        win = 'dealer'
    elif p_num > d_num:
        win = 'player'
    result = {'player':p_num,'dealer':d_num,'win':win}
    return result

これで対戦の準備が出来ました。
カードを作って対戦してみることとしましょう。

100回ほど対戦し
途中カードが尽きそうになったら
カードを入れ替えます。

# カードのセット数
sets = 4
#スペード、ハート、クラブ、ダイヤ * 13枚
card = [c+':'+str(n)  for c in ['S','H','C','D'] for n in range(1,14)]
# カード作成
cards = card * sets
# シャッフル
random.shuffle(cards)

# 対戦
results = []
for i in range(100):
    # 1ゲームの結果
    tmp = game1(cards)
    results.append(tmp)
    
    # 10枚以下になったらカードを交換
    if len(cards)<=10:
        cards = card * sets
        random.shuffle(cards)

最終的に結果がどうなるのかも
集計してみることとします。

# 対戦結果のサマリー
calc = {'player': 0, 'dealer': 0}
for row in results:
    if row['win'] == 'player':
        calc['player'] +=1
    if row['win'] == 'dealer':
        calc['dealer'] +=1
print(calc)
{'player': 30, 'dealer': 42}

こんな感じでプレイヤーとディーラーの
勝ちを集計してみました。

これだけだとあまり面白くは無いですよね。
やはり醍醐味は「金を掛ける」
ということだと思いますんで
金を掛けるシミュレーションもしてみましょう。

資金は10万円
掛け金は1000円
勝ったら倍もらえるようにしました。

fund = 100000
latch = 1000

# カードのセット数
sets = 4
#スペード、ハート、クラブ、ダイヤ * 13枚
card = [c+':'+str(n)  for c in ['S','H','C','D'] for n in range(1,14)]
# カード作成
cards = card * sets
# シャッフル
random.shuffle(cards)

# 対戦
results = []
for i in range(100):
    # 1ゲームごとにかける
    fund -= latch
    tmp = game1(cards)
    win = tmp['win']
    results.append(tmp)

    # 勝ったら倍額貰える
    if win=='player':
        fund += latch * 2
    # 引き分けは戻し
    elif win=='draw':
        fund += latch

    # 10枚以下になったらカードを交換
    if len(cards)<=10:
        cards = card * sets
        random.shuffle(cards)
さて結果はどうなるんでしょうか!!!

# 対戦結果のサマリー
calc = {'player': 0, 'dealer': 0}
for row in results:
    if row['win'] == 'player':
        calc['player'] +=1
    if row['win'] == 'dealer':
        calc['dealer'] +=1
print(calc)
print('資金残り : ' , fund)
{'player': 41, 'dealer': 31}
資金残り :  110000

今回は勝ちが多くて儲かっていますね!!


ここからは掛け方を変えて
試してみることとしましょう。

最初はグッドマン法です。
これは、勝ちに応じて賭け金を「1、2、3、5」と
4段階で増やし負けた時点で1に賭け金を戻す手法です。

プログラムをちょっと改良して
掛け金を変えてみます。

fund = 100000
latch = 1000

# カードのセット数
sets = 4
#スペード、ハート、クラブ、ダイヤ * 13枚
card = [c+':'+str(n)  for c in ['S','H','C','D'] for n in range(1,14)]
# カード作成
cards = card * sets
# シャッフル
random.shuffle(cards)

# 対戦
results = []
for i in range(100):
    # 1ゲームごとにかける
    fund -= latch
    tmp = game1(cards)
    tmp['latch'] = latch
    win = tmp['win']

    # 勝ったら倍額貰える
    if win=='player':
        fund += latch * 2
        if latch < 3000:
            latch += 1000
        elif latch ==3000:
            latch += 2000
    # 引き分けは戻し
    elif win=='draw':
        fund += latch
    else:
        latch = 1000
    tmp['fund'] = fund
    results.append(tmp)
    # 10枚以下になったらカードを交換
    if len(cards)<=10:
        cards = card * sets
        random.shuffle(cards)

さて、結果は??
# 対戦結果のサマリー
calc = {'player': 0, 'dealer': 0}
for row in results:
    if row['win'] == 'player':
        calc['player'] +=1
    if row['win'] == 'dealer':
        calc['dealer'] +=1
print(calc)
print('資金残り : ' , fund)
{'player': 34, 'dealer': 35}
資金残り : 102000

負けが少し多くても
資金が初期金額を上回っていますね。

うまく掛け金が多い時に勝てれば
初期金額を上回れますが
そううまくは行きませんね。


次は10%投資法です。
これは掛け金を毎回、資金の10%で掛けてみる
というものです。

掛け金を資金の10分の1にするように
変えてみることとします。

fund = 100000

# カードのセット数
sets = 4
#スペード、ハート、クラブ、ダイヤ * 13枚
card = [c+':'+str(n)  for c in ['S','H','C','D'] for n in range(1,14)]
# カード作成
cards = card * sets
# シャッフル
random.shuffle(cards)

# 対戦
results = []
for i in range(100):
    # 1ゲームごとにかける , 資金の10%
    latch = fund//10
    fund -= latch
    tmp = game1(cards)
    tmp['latch'] = latch
    win = tmp['win']

    # 勝ったら倍額貰える
    if win=='player':
        fund += latch * 2
    # 引き分けは戻し
    elif win=='draw':
        fund += latch
    tmp['fund'] = fund
    results.append(tmp)
    # 10枚以下になったらカードを交換
    if len(cards)<=10:
        cards = card * sets
        random.shuffle(cards)
        
# 対戦結果のサマリー
calc = {'player': 0, 'dealer': 0}
for row in results:
    if row['win'] == 'player':
        calc['player'] +=1
    if row['win'] == 'dealer':
        calc['dealer'] +=1
print(calc)
print('資金残り : ' , fund)
{'player': 46, 'dealer': 35}
資金残り :  200700

勝ちが多ければ、そりゃあ増えますが
負けが混むと目も当てられません。

長く続けることは出来ますが
アップダウンが激しすぎますね。

最後は31システム投資法です。

1,1,1,2,2,4,4,8,8 に分けて賭ける手法で
2連勝すると初期金額を上回り
全部負けてもこの分しか負けません。

fund = 100000

# カードのセット数
sets = 4
#スペード、ハート、クラブ、ダイヤ * 13枚
card = [c+':'+str(n)  for c in ['S','H','C','D'] for n in range(1,14)]
# カード作成
cards = card * sets
# シャッフル
random.shuffle(cards)

# 対戦
results = []
for i in [1,1,1,2,2,4,4,8,8]:
    # 1ゲームごとにかける額は31の金額で
    latch = 1000 * i
    fund -= latch
    tmp = game1(cards)
    tmp['latch'] = latch
    win = tmp['win']

    # 勝ったら倍額貰える
    if win=='player':
        fund += latch * 2
    # 引き分けは戻し
    elif win=='draw':
        fund += latch
    tmp['fund'] = fund
    results.append(tmp)
    # 10枚以下になったらカードを交換
    if len(cards)<=10:
        cards = card * sets
        random.shuffle(cards)

# 対戦結果のサマリー
calc = {'player': 0, 'dealer': 0}
for row in results:
    if row['win'] == 'player':
        calc['player'] +=1
    if row['win'] == 'dealer':
        calc['dealer'] +=1
print(calc)
print('資金残り : ' , fund)
{'player': 5, 'dealer': 2}
資金残り : 103000

何度か試してみましたが
資金の推移としては連勝さえできれば
かなりの確率で初期金額を上回れます。

[{'player': 18, 'dealer': 25, 'win': 'player', 'latch': 1000, 'fund': 101000},
 {'player': 19, 'dealer': 22, 'win': 'player', 'latch': 1000, 'fund': 102000},
 {'player': 21, 'dealer': 19, 'win': 'player', 'latch': 1000, 'fund': 103000},
 {'player': 18, 'dealer': 17, 'win': 'player', 'latch': 2000, 'fund': 105000},
 {'player': 19, 'dealer': 20, 'win': 'dealer', 'latch': 2000, 'fund': 103000},
 {'player': 21, 'dealer': 22, 'win': 'player', 'latch': 4000, 'fund': 107000},
 {'player': 18, 'dealer': 19, 'win': 'dealer', 'latch': 4000, 'fund': 103000},
 {'player': 21, 'dealer': 21, 'win': 'draw', 'latch': 8000, 'fund': 103000},
 {'player': 22, 'dealer': 22, 'win': 'draw', 'latch': 8000, 'fund': 103000}]

推移はこんな感じですが
2連勝したらそこで打ち切る
というルールでやれば、回収金額の方が
多くなるかもしれません。

そうは上手くは行かないんですけどね・・・

この掛け方はいわゆる
マーチンゲール法というものの派生で
必ず勝てる掛け方ではありません。


まとめですが
シミュレーションでは初期金額を上回ることもあれば
大きく下回ることもあります。

必勝出来ないのであれば
リスクも当然あるので
やるべきでは無いでしょうねーーー

次は掛け方以外にも
ハンドの関係性からのカードの引き方や
攻略法も見ていきたいと思います。

今回はここまでです
それでは。