見出し画像

ChatGPTでポケカの確率を算出してみた

0.背景

噂のChatGptを触った際にポケカの確率計算を算出することができるのではないかと気になり、やってみることにしました。
ポケカでは特定のカードを4枚入れたデッキで初手に引ける確率は4割というお話を聞きますが、それ以外の複雑な状況における確率はあまり聞いたことがありません。ChatGptさんならやってくれることでしょう!!!


1. 博士の研究4枚を引ける確率を求める

背景でも記載しておりますが、ポケカでは特定カード4枚を入れたときに引ける確率は4割と言われていますので、まずは答えのわかっているものからやっていくのがいいでしょうという考えです。

何はともあれ始めてみましょう。

画像1:ChatGptに聞いてみた

まぁ、ChatGptさんは何も悪くない。私の質問が雑すぎますね。
私の想定では初手8枚(ドロー含め)で「博士の研究」を引ける確率を求めたかったのですが、デッキ(60枚)から1枚引いた時の確率を求めています。
とりあえずは状況が分かりましたので、認識を合わせていきます。

画像2:博士の研究が引ける確率
画像3:博士の研究が引ける確率

上記画像のように中々認識が合いません。
ChatGptを利用していると同じ間違いを繰り返す、間違いを認識してくれない等はよく発生するなと感じます。
英語でやると認識力が上がるらしいですね・・・気軽さは捨てられません。
そして色々指摘したところ、下記画像の結果になりました。


画像4:博士の研究が引ける確率

約65%ありますね。そんなわけないんですけど。
ここでは以下の2点が考慮が抜けておりますので、4割という数字になっていません。前提としてChatGptさんとはルール確認したのですが、考慮してくれないのでこの辺りを定義していかないといけません。

  • 初手7枚の内に種ポケモンが含まれている

  • サイドカードを用意する

2. 前提条件をインプットさせる

「初手7枚の内に種ポケモンが含まれている」を考慮させるにはデッキ内容を定めないといけません。私はロストギラティナデッキ(略称:ロスギラ)を使っているので、そちらを認識させていきます。
ロスギラには「博士の研究」が入っていませんので、代わりに「アクロマの実験」が引ける確率を求めます。

2-1. デッキ内容を読み込み

デッキコード:pXpy2S-InoDG8-XXyRyE
(実際に読み込ませたのとちょっと差異ありますがだいたいこれ)

画像1:ロスギラデッキインプット
画像2:ロスギラデッキインプット


画像3:ロスギラデッキ

2-2. 種ポケモンの数を読み込み


画像1:種ポケモン読み込み

ChatGptさんに種ポケモンを認識してもらえるなんて思っていません。
調教していきましょう。


画像2:種ポケモン読み込み

種ポケモンが合計6枚になっていますが、種類じゃなくて枚数であることは指摘しておきました。

3. ChatGptは複雑な計算は実施不可

前提条件が完了しましたので、いよいよ計算を実施してもらいましょう。
問い合わせ内容および解答は以下のようになります。

<問い合わせ内容>
山札はロスギラデッキを参照。 山札から7枚カードを引いて手札加えます。 手札7枚に種ポケモンが引けた場合、次に山札から6枚を引いてサイドカードとして扱います。 次に山札から1枚カードを引いて手札に加えます。 この手札8枚にアクロマの実験がある確率を教えてください。


画像1:解答

もう自分でやってくれとしかお返事をいただけなくなりました。
想定していた結果と違い、ChatGptは全てを想定して確率計算を算出してくれるという結果には至れませんでした。

4. 自分で計算するしかない!!!!

というわけで自分で計算するしかありません。
ただ馬鹿正直に手動計算するなんてナンセンスですので・・・・逆にもう計算モジュールを作ってもらいましょう。

画像1:計算モジュール
import math

def calculate_probability(deck_size, hand_size, pokemon_count, acroma_experiment_count):
    # 手札に種ポケモンが含まれる確率の計算
    pokemon_probability = math.comb(pokemon_count, 1) * math.comb(deck_size - pokemon_count, hand_size - 1) / math.comb(deck_size, hand_size)
    
    # 手札に種ポケモンが含まれた場合のアクロマの実験の確率の計算
    acroma_probability = math.comb(acroma_experiment_count, 1) * math.comb(deck_size - pokemon_count - acroma_experiment_count, hand_size - 1) / math.comb(deck_size - pokemon_count, hand_size)
    
    # 手札に種ポケモンが含まれた場合の最後の1枚がアクロマの実験である確率の計算
    last_card_probability = acroma_experiment_count / (deck_size - pokemon_count - hand_size + 1)
    
    # 手札にアクロマの実験が含まれる確率の計算
    probability = pokemon_probability * acroma_probability * last_card_probability
    
    return probability

# ロスギラデッキの情報
deck_size = 60
hand_size = 7
pokemon_count = 12  # 種ポケモンの総数
acroma_experiment_count = 4  # アクロマの実験の枚数

# 確率の計算
probability = calculate_probability(deck_size, hand_size, pokemon_count, acroma_experiment_count)
print("手札にアクロマの実験が含まれる確率: ", probability)

正直ダメダメですが、取っ掛かりとして十分なものをいただきました。
ここからは指摘・改善を思考錯誤していきました。
形になり始めたのは以下になります。

画像2:計算モジュール
import random

def simulate_probability():
    trials = 1000000  # シミュレーションの試行回数
    success = 0  # 成功回数(手札8枚でアクロマの実験がある場合)

    for _ in range(trials):
        deck = [card for card in range(deck_size)]  # 山札
        hand = random.sample(deck, hand_size)  # 手札をランダムに選ぶ

        if pokemon_card in hand:
            side = random.sample(deck, side_size)  # サイドカードをランダムに選ぶ
            hand.append(random.choice(deck))  # 山札から1枚引いて手札に加える

            if acro_card in hand:
                success += 1

    probability = success / trials
    return probability

# 使用例
deck_size = 60
hand_size = 7
side_size = 6
pokemon_card = 1  # 種ポケモンのカード番号
acro_card = 2  # アクロマの実験のカード番号

probability = simulate_probability()
print("手札8枚でアクロマの実験がある確率: {:.2%}".format(probability))


私はこの辺の算出詳しくないんですけど、確率の計算が難しいならシミュレーションして確率を算出してしまえばいいということですね。
あーなるほど、これだと思いましたね。
処理としては単純なので、ポケカで行うアクションをコード化するのは難しくないかと。これなら複雑な確率計算になってもできますね。

5. 結論:「アクロマの実験」を引ける確率42.09%

試行錯誤の末に特定カード4枚で処理に引ける確率4割にたどりつけました。
シミュレーションなので結果は実行する度に変動しますが、40~42%の幅で結果はでました。

import configparser
import os

from common.deck_utils import draw_cards, has_seed_pokemon, has_specific_card, initialize_deck

# デッキの定義をconfig.iniから読み込む
config_file = os.path.join("config", "config.ini")
config = configparser.ConfigParser()
config.read(config_file)

deckList = {}
sections = ['ポケモン', 'グッズ', 'サポート', 'スタジアム', 'エネルギー']

for section in sections:
    if section in config:
        deckList[section] = dict(config[section])

seed_pokemon = config.get('SeedPokemon', 'pokemon', fallback='').split('\n')
target_card = config.get('TargetCard', 'card', fallback='')

def simulate_probability():
    trials = 1000000  # シミュレーションの試行回数
    success = 0  # 成功回数(目的条件が達成した回数)

    for _ in range(trials):
        deck = initialize_deck(deckList)  # デッキを初期化
        hand, deck = draw_cards(deck, 7)
        
        if len(deck) != 53:
            raise ValueError("デッキ枚数が53枚ではありません")

        while len(hand) < 8:
            if has_seed_pokemon(hand, seed_pokemon):
                side, deck = draw_cards(deck, 6)  # サイドカードとして6枚引く
                
                if len(deck) != 47:
                    raise ValueError("デッキ枚数が47枚ではありません")
                
                draw_card, deck = draw_cards(deck, 1)  # 山札から1枚引く
                
                if len(deck) != 46:
                    raise ValueError("デッキ枚数が46枚ではありません")
                
                hand.append(draw_card[0])
        
                if has_specific_card(hand, target_card):
                    success += 1
                break
            else:
                # マリガン処理(試行回数は増やさない)
                deck.extend(hand)  # 手札を山札に戻す
                if len(deck) != 60:
                    raise ValueError("デッキ枚数が60枚ではありません")
                hand, deck = draw_cards(deck, 7)  # 手札を7枚引く

    probability = success / trials
    return probability

probability = simulate_probability()
print("手札8枚で{}がある確率: {:.2%}".format(target_card, probability))


この記事が気に入ったらサポートをしてみませんか?