見出し画像

【旧裏】1ターン目に博士を引ける確率

旧裏では1ターン目にはかせを引けるかどうかがその後の展開, ひいては勝敗を大きく左右する。そこでシミュレーションを用い, 様々なドロソ構成で1ターン目にはかせを引ける確率を算出した。シミュレーションに用いたPythonコードは本ページ末尾に記載してある。

なおシミュレーションは小数点第一位までの精度が保証されるように100万回繰り返した。

調査対象ドロソ

  1. パソコン通信

  2. 新ポケモン図鑑

  3. クルミ

  4. マサキ

  5. マサキの転送装置

  6. エリカ

  7. ナツメの眼

  8. カスミの勝負

手元に複数枚のドロソが来た場合はこの優先度でカードを使うようにプログラムした。小規模のシミュレーションで予備検討した結果, 「はかせを探す」という目的の達成率を最大化するためにはこの優先度が良いことがわかっている。例えば基本的にカスミの勝負は最後に使ったほうがいい。これはカスミの勝負で裏が出るとせっかく手元に呼び込んだドロソが山札に帰ってしまうからだろう。

唯一の例外として, ナツメの眼とクルミが同時に手元に来た場合のみクルミより先にナツメの眼を打つように設定した(手札が減る前に打ったほうが強いため)。なお新ポケモン図鑑で並び替える優先度も前述の順番に従う。

また, 今回はドロソ以外のカードは全て場に出せない"ダミー"として取り扱っているため, カードの使用に伴うデッキ圧縮効果は考慮していない。そのためクルミやカスミの勝負が若干過小評価されていると考える。一般的に圧縮効果の影響を受けやすいのは「カードを戻す」という挙動をするカードであり, クルミとカスミの勝負がこれに該当する。また, ナツメの眼はおそらく若干過大評価されている。これは場にカードを展開しないという仮定により, ナツメの眼使用時の手札枚数が多くなるからである。

【忙しい人向け】シミュレーション結果から考える簡易1ターン目はかせ率算出式

忙しい方はここだけ見て頂ければ概ねの傾向を把握頂ける。計算結果を線形近似することで以下の確率を算出した。

もちろんカード同士のシナジーがあるので投入枚数に対する利得は単純に線形にはならない。例えば同じ2枚引きドロソでもナツメの眼投入時は手札を減らすクルミよりマサキを投入したほうが投入枚数に対する利得が多い。

  • 基準値が約65.4%

  • 後攻なら+5.2%

  • 新ポケモン図鑑1枚につき+0.8%

  • マサキの転送装置1枚につき+1.2%

  • カスミの勝負1枚につき+1.3%

  • 2枚引きドロソ1枚につき+1.4%

  • 3枚引きドロソ1枚につき+1.9%

  • ナツメの眼1枚につき+2.1%

  • パソコン通信1枚につき+4.2%

【時間のある方向け】シミュレーション結果

  • 見出しに記載がなくともすべての計算結果でウツギ4枚 + オーキド4枚のはかせ計8枚が投入されている。

基準点(ウツギ4枚 + オーキド4枚)

先攻時に3戦に1回程度は事故る。

  • 先行時: はかせが引ける確率65.4% (2.9戦に1回は引けない)

  • 後攻時: はかせが引ける確率70.6% (3.4戦に1回は引けない)

+ (マサキ4枚)

およそ5.3%博士を引ける確率が増加。

  • 先行時: はかせが引ける確率70.8% (3.4戦に一回は引けない)

  • 後攻時: はかせが引ける確率75.9% (4.1戦に一回は引けない)

+ (クルミ4枚)

+ (マサキ4枚)とほぼ同じ確率。クルミは戻したカードを再度引き込む可能性があるため理論上はマサキより若干不利なのだが, その効果はほぼ無視できることがわかった。

  • 先行時: はかせが引ける確率70.8% (3.4戦に一回は引けない)

  • 後攻時: はかせが引ける確率75.8% (4.1戦に一回は引けない)

 + (クルミ4枚 + マサキ4枚)

おそらく現在最もスタンダードなドロソ構成。+ (クルミ4枚)と比較して博士を引ける確率が約6%増加。

  • 先行時: はかせが引ける確率76.7% (4.3戦に一回は引けない)

  • 後攻時: はかせが引ける確率81.4% (5.4戦に一回は引けない)

 + (クルミ4枚 + マサキの転送装置4枚)

マサキとの性能差を図るためにシミュレーションを実施。実際にこの構成で使われることはまずないだろう。

+(クルミ4枚 + マサキ4枚)と比較して博士を引く確率は1%程度劣る。コイントスに失敗し50%で何も起こらず手詰まりになるより, マサキで確実に2枚を引いて何らかのドロソにヒットして次の展開に繋げられる確率のほうが高いからだろう。

なお余談だが, 今回のシミュレーションではナツメの眼と転送装置のどちらを先に使うかは全く博士率に関係がないことがわかった。ただし実際のプレイでは場に展開したいカードが手札に来ていることが殆どだろうので, ①先に転送装置を打つ, ②-1 表ならカードを展開する,  ②-2 裏ならナツメの眼で博士を探しに行く, という動きをすることになるだろう。

  • 先行時: はかせが引ける確率75.7% (4.1戦に一回は引けない)

  • 後攻時: はかせが引ける確率80.5% (5.1戦に一回は引けない)

+ (クルミ4枚 + カスミの勝負4枚)

前述の構成のマサキの代わりにカスミの勝負を投入 +(クルミ4枚+マサキ4枚)と比較して博士を引ける確率は1.1%しか劣っておらず意外にも高い。

余談だがカスミの勝負はクルミの後に使うと博士を引ける確率が0.5%程度上昇する。ケースによるものの, 博士を引きたいならクルミはカスミの勝負の前に打ったほうがよい。

  • 先行時: はかせが引ける確率75.6% (4.1戦に一回は引けない)

  • 後攻時: はかせが引ける確率80.2% (5.0戦に一回は引けない)

 + (クルミ4枚 + エリカ4枚)

さすがにエリカの3枚引きは強く, (クルミ4枚+マサキ4枚)と比較して博士を引ける確率が約2%増加する。一方でオーキドで必要なカードを切るリスクも増大する点には注意。

  • 先行時: はかせが引ける確率78.9% (4.7戦に一回は引けない)

  • 後攻時: はかせが引ける確率83.5% (6.1戦に一回は引けない)

+ (クルミ4枚 + ナツメの眼4枚)

emptyさんがナツメの眼を博士を引き込むカードとして提唱していたが, それを裏付けるように博士を引ける確率は+(クルミ4枚)と比較して8.4%も増加し非常に優れていることがわかった。

  • 先行時: はかせが引ける確率79.2% (4.8戦に一回は引けない)

  • 後攻時: はかせが引ける確率84.8% (6.6戦に一回は引けない)

+ (マサキ4枚 + ナツメの眼4枚)

クルミの代わりにナツメの眼と相性の良いマサキを追加。+(クルミ4枚 + ナツメの眼4枚)と比較して博士率がおよそ1.5%上昇。おそらくエリカ不採用&無殿堂&ドロソ16枚で到達可能な博士率の最高値がこの組み合わせだと考える。

  • 先行時: はかせが引ける確率81.0% (5.3戦に一回は引けない)

  • 後攻時: はかせが引ける確率86.2% (7.1戦に一回は引けない)

+ (エリカ4枚 + マサキ4枚 + ナツメの眼4枚)

手札が増える縦引きドロソ8枚とナツメの眼との組み合わせ。先行で90%近くで博士を引きたければここまでする必要がある(但しオーキドとの噛み合いは悪そうだ)。

  • 先行時: はかせが引ける確率89.0% (9.1戦に一回は引けない)

  • 後攻時: はかせが引ける確率92.8% (10.2戦に一回は引けない)

+ (クルミ4枚 + マサキ4枚 + 新ポケモン図鑑2枚)

クルミマサキの縦引きドロソ4投した上でこれらと相性の良い新ポケモン図鑑を2枚追加するという, たまに見かける組み合わせ。しかし博士を引ける確率に対する寄与は小さく, + (クルミ4枚 + マサキ4枚)と比較して1.6%程度の増加に留まった。これは新ポケモン図鑑で5枚確認した中にはかせが居たとしても, 縦引きドロソが手元にないと現在のターン中に引き込めないからだろう。

  • 先行時: はかせが引ける確率78.3% (4.6戦に一回は引けない)

  • 後攻時: はかせが引ける確率83.2% (6.0戦に一回は引けない)

+ (クルミ4枚 + パソコン通信1枚)

事実上9枚目の博士とも言えるパソコン通信は流石に強く, + (クルミ4枚)と比較して博士率は約4.6%も増加する。これはおよそマサキあるいはクルミ3枚分に匹敵する数値である。

  • 先行時: はかせが引ける確率75.3% (4.0戦に一回は引けない)

  • 後攻時: はかせが引ける確率80.1% (5.0戦に一回は引けない)

+ (クルミ4枚 + ナツメの眼2枚 + パソコン通信2枚)

個人的にお気に入りの構成。手札が減るクルミおよびナツメの眼の欠点を博士の大量ドローでカバーする。

  • 先行時: はかせが引ける確率83.0% (5.9戦に一回は引けない)

  • 後攻時: はかせが引ける確率87.6% (8.0戦に一回は引けない)

まとめ

  • 現在スタンダードな(ウツギ4枚 + オーキド4枚 + クルミ4枚 + マサキ4枚)だと先行時4.3戦に1回は博士が来ない。はかせドロー率の勝敗寄与率がかなり高い旧裏ではこれは少々不安な数値と言える。

  • 以下は個々のカードについてまとめる。

    • パソコン通信: 流石に殿堂カードなだけあって, 博士率に対する寄与がぶっちぎりで高いことがわかった。殿堂が余っていてカードスペースがないデッキには有効か(但し切ったカードを回収する夜廃を増やす必要があるため, スペースについてはそれほどメリットがない可能性もある)。

    • ナツメの眼:  このカードはパソコン通信以外では頭一つ抜けた博士サーチ率を持つことがわかった特にマサキとの組み合わせではおそらくエリカ不採用&無殿堂&ドロソ枚数16枚で到達可能な最高博士率を叩き出す。

    • クルミ:縦引き2枚引きドロソその①。博士サーチ率はそれほど高くないが, 手札を減らしながらはかせをサーチするためオーキド使用時に困りにくい。

    • マサキ:縦引き2枚引きドロソその②。博士サーチ率がクルミと同様にそれほど高くない上に, 手札を増やしながらはかせをサーチするためオーキドが来た際に困る。オーキド対策のためにも相性が良いナツメの眼と組み合わせたい。

    • カスミの勝負: 意外にも縦2枚引きドロソ達と遜色ない程度に博士を探すことができるさらに大暴走構築対策効果と, 今必要ないカードをデッキに戻して保護する効果とが付帯するのでもっと評価されてもいいカードかもしれない。(ただし, 他のドロソの場合ははかせに到達できなくともなんらかのカードが手に入るのに対し, カスミの勝負はじゃんけんに負けると手札がどん詰まりになってしまう危険性がある。序盤が大事で一旦劣勢になると巻き返しが辛いデッキでの採用は厳しいか)

    • マサキの転送装置:博士到達率ではマサキの劣化であることがわかった。また, カスミの勝負と同様に裏が出るとどうしようもないという課題を抱えている。博士システムに組み込むのではなく, 従来使われてきたように, マサキなどの縦引きドロソと併せて大量投入して山札を引き切るようなデッキで使うほうが良さそうだ。

    • 新ポケモン図鑑: 博士率に対する寄与はかなり低い。これは縦引きドロソとセットで手元に来ないと1ターン目に手元に引き込めないからだろう。1ターン目の展開が重要な速攻系デッキでの採用は厳しいか。また, ナツメの眼が流行ると相手に順番をリセットされてしまうのも厳しい。

  • 最後に, あくまで1ターン目の博士到達率は一つの切り口であり, 博士率の最大化を目的としてドロソ構成を考えることは危険ということを述べておきたい。

  • 例えばマサキとカスミの勝負は同程度の博士到達率を持つものの, それぞれの挙動はまったく異なる。マサキはオーキドを引いてしまった場合に手札を切るおそれがある一方で, 博士を引けなくとも何らかの有効札にたどり着ける可能性がある。一方でカスミの勝負はオーキドとのかみ合わせは悪くないものの, 手札が少ない状況でじゃんけんに負けてしまうと袋小路に陥る危険性がある。

Pythonコード

  • 基本的にjupyter notebookにコピペすれば動くと思います

#%%
import numpy as np
from tqdm import tqdm
import random
#%%
class Field:
    def __init__(self, first, trials):
        self.deck = None
        self.hand = None
        self.side = None
        self.pokemon = None
        self.with_hakase = 0
        self.trials = trials
        self.first = first
        # ポケモン図鑑ならこの順に並び替える。クルミで戻すカードはこの逆順で戻す
        self.priority = [
            "オーキドはかせ",
            "ウツギはかせ",
            "パソコン通信",
            "新ポケモン図鑑",
            "ナツメの眼",
            "クルミ",
            "マサキ",
            "エリカ",
            "マサキの転送装置",
            "カスミの勝負",
            "ダミー"
        ]

    def get_deck(self):
        deck = ["オーキドはかせ"] * 1
       #deck = deck + ["ウツギはかせ"] * 4
        deck = deck + ["マサキ"] * 4
       #deck = deck + ["マサキの転送装置"] * 4
       #deck = deck + ["エリカ"] * 4
       #deck = deck + ["クルミ"] * 4
       #deck = deck + ["ナツメの眼"] * 4
       #deck = deck + ["カスミの勝負"] * 4
       #deck = deck + ["パソコン通信"] * 2
       #deck = deck + ["新ポケモン図鑑"] * 4
        deck = deck + ["ダミー"] * (60 - len(deck))
        np.random.shuffle(deck)
        return deck

    def field_initialize(self):
        while True:
            # デッキ初期化
            self.deck = self.get_deck()
            # 7枚ドロー
            self.hand = self.deck[0: 7]
            self.deck = self.deck[7:]
            # たねポケチェック (ここではすべてのダミーカードがたねポケと仮定した)
            poke_indices = [idx for idx, ele in enumerate(self.hand) if ele == "ダミー"]
            if len(poke_indices) != 0:
                # たねポケを1枚だす
                self.pokemon = self.hand[poke_indices[0]]
                self.hand = self.hand[:poke_indices[0]] + self.hand[poke_indices[0] + 1:]
                break
        # サイド
        self.side = self.deck[0: 6]
        self.deck = self.deck[6:]
        # 後攻なら1まい引く
        if self.first is False:
            self.hand = self.hand + self.deck[0: 1]
            self.deck = self.deck[1:]

    def hakase_checker(self):
        if ("ウツギはかせ" in self.hand) or ("オーキドはかせ" in self.hand):
            self.with_hakase += 1
            return True

    def use_kurumi(self):
        self.hand.remove("クルミ") # クルミを消化
        self.hand = self.hand + self.deck[0: 2] # 2枚引く
        self.deck = self.deck[2:]
        # ドロソ以外を優先して2枚もとに戻す
        def return_specific_cards(target, returned_counter):
            while target in self.hand:
                if returned_counter == 2:
                    break
                returned_counter += 1
                self.hand.remove(target)
                self.deck = self.deck + [target]
            return returned_counter
        returned_counter = 0
        for target in reversed(self.priority):
            returned_counter = return_specific_cards(target=target, returned_counter=returned_counter)
        np.random.shuffle(self.deck)

    def use_natsume_no_me(self):
        self.hand.remove("ナツメの眼")
        # デッキにハンドをもどす
        self.deck = self.deck + self.hand
        np.random.shuffle(self.deck)
        # ハンド枚数ぶん引いてくる
        self.hand = self.deck[0: len(self.hand)] # handを上書き
        self.deck = self.deck[len(self.hand): ]  #deckを上書き         np.random.shuffle(self.deck)

    def use_kasumi_no_shobu(self, seed):
        self.hand.remove("カスミの勝負") # カスミの勝負を消化
        random.seed(a=seed)
        random_value = random.uniform(0, 1)
        if random_value > 0.5:
            # デッキにハンドをもどす
            self.deck = self.deck + self.hand
            np.random.shuffle(self.deck)
            # 5枚引いてくる
            self.hand = self.deck[0: 5] # handをover write
            self.deck = self.deck[5: ]
        else: #裏ならなにもおこらない
            pass

    def use_masaki(self):
        self.hand.remove("マサキ") # マサキを消化
        self.hand = self.hand + self.deck[0: 2] # 2枚引く
        self.deck = self.deck[2:]

    def use_masaki_no_tensousouchi(self):
        self.hand.remove("マサキの転送装置")
        rand_n = random.uniform(0, 1)
        if rand_n >= 0.5:
            self.hand = self.hand + self.deck[0: 4]
            self.deck = self.deck[4:]

    def use_erika(self):
        self.hand.remove("エリカ") # エリカを消化
        self.hand = self.hand + self.deck[0: 3] # 3枚引く
        self.deck = self.deck[3:]

    def use_shin_pokemon_zukan(self):
        self.hand.remove("新ポケモン図鑑") # カードを消化
        def put_specific_card_on_decktop(target):
            target_indices = [idx for idx, ele in enumerate(self.deck[0: 5]) if ele == target]
            non_target_indices = [idx for idx, ele in enumerate(self.deck[0: 5]) if ele != target]
            renumbered_top5 = [self.deck[idx] for idx in target_indices] + [self.deck[idx] for idx in non_target_indices]
            self.deck = renumbered_top5 + self.deck[5: ]
        # オーキドはかせ > パソコン通信 > ウツギはかせ > 新ポケモン図鑑 > クルミ > マサキの優先度で並び替える
        for target in reversed(self.priority):
            put_specific_card_on_decktop(target=target)

    def first_turn_simulations(self):
        for i in tqdm(range(self.trials)):
            # 初期化
            self.field_initialize()
            if self.hakase_checker():
                continue
            while len(self.deck) > 0:
                # 1. パソ通のチェック
                if "パソコン通信" in self.hand:
                    # 手札が3枚以上ならパソ通で博士引いてきて終了
                    if (len(self.hand) >= 3):
                        self.with_hakase += 1
                        break
                    else:
                        # 3枚より少ないならマサキを使う
                        if "マサキ" in self.hand:
                            self.use_masaki()
                        elif "マサキの転送装置" in self.hand:
                            self.use_masaki_no_tensousouchi()
                        else:
                            break
                # 2. ポケモン図鑑を使う. 縦引きドロソも持っているなら並び替えたカードを引くためにドロソを使う.
                # ここはプログラムがめんどくさかったので詳細の詰めをサボっている。要検討。
                elif "新ポケモン図鑑" in self.hand:
                    self.use_shin_pokemon_zukan()
                    if "クルミ" in self.hand:
                        self.use_kurumi()
                        if self.hakase_checker():
                            break
                    elif "マサキ" in self.hand:
                        self.use_masaki()
                        if self.hakase_checker():
                            break
                    elif "エリカ" in self.hand:
                        self.use_erika()
                        if self.hakase_checker():
                            break
                    elif "マサキの転送装置" in self.hand:
                        self.use_masaki_no_tensousouchi()
                        if self.hakase_checker():
                            break
                # 3. クルミを使う
                elif "クルミ" in self.hand and "ナツメの眼" not in self.hand:
                    self.use_kurumi()
                    if self.hakase_checker():
                        break
                # 4. マサキを使う
                elif "マサキ" in self.hand:
                    self.use_masaki()
                    if self.hakase_checker():
                        break
                # 5. エリカを使う
                elif "エリカ" in self.hand:
                    self.use_erika()
                    if self.hakase_checker():
                        break
                # 6. ナツメの眼
                elif "ナツメの眼" in self.hand:
                    self.use_natsume_no_me()
                    if self.hakase_checker():
                        break
                # 5. マサキの転送装置
                elif "マサキの転送装置" in self.hand:
                    self.use_masaki_no_tensousouchi()
                    if self.hakase_checker():
                        break
                # 7. カスミの勝負を使う
                elif "カスミの勝負" in self.hand:
                    self.use_kasumi_no_shobu(seed=i)
                    if self.hakase_checker():
                        break
                # やることがなくなったら終了
                else:
                    break

    def print_simulation_result(self):
        print(f"はかせ確率: {self.with_hakase/self.trials*100:.01f}")
        print(f"{1/(1 - self.with_hakase/self.trials):.01f}回に1回は博士がひけない")
#%%
field = Field(first=False, trials=1000000)
field.first_turn_simulations()
field.print_simulation_result()


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