1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自分のブラックジャックの戦略が強いかどうか検証してうぬぼれから脱却する

Last updated at Posted at 2024-06-10

動機

カジノって面白いですよね.ポーカーやバカラ,ブラックジャックなど暇つぶしについついやってしまいます.
そんななか昨日,誰しもが一度は思う「俺ってめっちゃ強いんじゃね?」が再燃しました.
今度カジノに行ったとき,自信を持ってやるために,シミュレーションで確認して(確率的に)強いことを証明しよう!

ルール

ダブルダウンは有り
スプリットはめんどうなので無し
(ベーシックストラテジーではAAや88ではスプリットはした方が良いらしい 参考).
ナチュラルブラックジャックは配当1.5
プレイヤーのバーストはディーラーの手によらずプレイヤーの負け

コード

何の変哲もない普通のpythonコードです.
デッキの状態によって変化が起こるか気になったので,デッキが無限にある状態と1セットのみの場合の2パターンを検証しました.

bj.py

import random

# カードデッキを初期化
suits = ['Hearts', 'Diamonds', 'Clubs', 'Spades']
ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
single_deck = [(rank, suit) for suit in suits for rank in ranks]

def shuffle_deck(deck):
    random.shuffle(deck)
    return deck

def deal_card(deck, infinite_deck):
    if infinite_deck:
        return random.choice(single_deck)
    else:
        return deck.pop()

def calculate_hand_value(hand):
    value = 0
    aces = 0
    for card, suit in hand:
        if card in ['J', 'Q', 'K']:
            value += 10
        elif card == 'A':
            aces += 1
            value += 11
        else:
            value += int(card)
    while value > 21 and aces:
        value -= 10
        aces -= 1
    return value

def is_blackjack(hand):
    return len(hand) == 2 and calculate_hand_value(hand) == 21

def player_decision(player_hand, dealer_card):
    player_value = calculate_hand_value(player_hand)
    dealer_value = calculate_hand_value([dealer_card])

    # 基本戦略に基づくプレイヤーの決定
    if player_value >= 17:
        return "stand"
    elif player_value <= 11:
        return "hit"
    else:
        if dealer_value >= 7:
            return "hit"
        else:
            return "stand"

def play_blackjack_simulation(count, infinite_deck=False):
    player_wins = 0
    dealer_wins = 0
    ties = 0
    double_down_wins = 0
    double_down_losses = 0
    natural_blackjacks = 0
    total_payout = 0  # 収支の計算

    for _ in range(count):  # 100,000回のシミュレーション
        if infinite_deck:
            deck = single_deck.copy()
        else:
            deck = shuffle_deck(single_deck.copy())
        
        player_hand = [deal_card(deck, infinite_deck), deal_card(deck, infinite_deck)]
        dealer_hand = [deal_card(deck, infinite_deck), deal_card(deck, infinite_deck)]

        # ナチュラルブラックジャックの判定
        if is_blackjack(player_hand):
            if is_blackjack(dealer_hand):
                ties += 1
            else:
                player_wins += 1
                natural_blackjacks += 1
                total_payout += 1.5  # ナチュラルブラックジャックのペイアウト
            continue
        elif is_blackjack(dealer_hand):
            dealer_wins += 1
            total_payout -= 1
            continue

        # プレイヤーのターン
        double_down = False
        if calculate_hand_value(player_hand) == 11:
            player_hand.append(deal_card(deck, infinite_deck))
            double_down = True

        while not double_down and player_decision(player_hand, dealer_hand[0]) == "hit":
            player_hand.append(deal_card(deck, infinite_deck))
            if calculate_hand_value(player_hand) > 21:
                break

        # ディーラーのターン
        while calculate_hand_value(dealer_hand) < 17:
            dealer_hand.append(deal_card(deck, infinite_deck))

        player_value = calculate_hand_value(player_hand)
        dealer_value = calculate_hand_value(dealer_hand)

        if player_value > 21:
            if double_down:
                double_down_losses += 1
                total_payout -= 2  # ダブルダウンの敗北
            else:
                dealer_wins += 1
                total_payout -= 1  # 通常の敗北
        elif dealer_value > 21 or player_value > dealer_value:
            if double_down:
                double_down_wins += 1
                total_payout += 2  # ダブルダウンの勝利
            else:
                player_wins += 1
                total_payout += 1  # 通常の勝利
        elif player_value < dealer_value:
            if double_down:
                double_down_losses += 1
                total_payout -= 2  # ダブルダウンの敗北
            else:
                dealer_wins += 1
                total_payout -= 1  # 通常の敗北
        else:
            ties += 1

    # 結果の表示
    print(f"プレイヤーの勝ち数: {player_wins}")
    print(f"ディーラーの勝ち数: {dealer_wins}")
    print(f"引き分け数: {ties}")
    print(f"ナチュラルブラックジャックの勝ち数: {natural_blackjacks}")
    print(f"ダブルダウンの勝ち数: {double_down_wins}")
    print(f"ダブルダウンの負け数: {double_down_losses}")
    print(f"最終収支: {total_payout}")

count = 1000000
print(f"総ゲーム回数:{count}")
# デッキが無限の場合
print("☆無限デッキの場合の結果:")
play_blackjack_simulation(count, infinite_deck=True)
print("\n")
# デッキが1セットの場合の結果
print("☆1セットのデッキの場合の結果:")
play_blackjack_simulation(count, infinite_deck=False)

実行結果

総ゲーム回数:1000000
☆無限デッキの場合の結果:
プレイヤーの勝ち数: 403170
ディーラーの勝ち数: 468785
引き分け数: 86416
ナチュラルブラックジャックの勝ち数: 44609
ダブルダウンの勝ち数: 25231
ダブルダウンの負け数: 16398
最終収支: -25644.5


☆1セットのデッキの場合の結果:
プレイヤーの勝ち数: 406788
ディーラーの勝ち数: 467542
引き分け数: 83349
ナチュラルブラックジャックの勝ち数: 46387
ダブルダウンの勝ち数: 25512
ダブルダウンの負け数: 16809
最終収支: -20154.5

普通に負け...
97.5~98%のようですね.
ギャンブルはほどほどにしましょう.

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?