見出し画像

遊戯王MasterDuel:ランクマ勝率と対戦回数のシミュレーションによる分析および考察

みなさんこんにちは.沙里葉(さりは,Sariha)です.
今回は今までと趣向を変えて,「遊戯王MDランクマにおける勝率と対戦回数の関係」について考察していこうと思います.
全文無料ですので,よろしければ最後までお付き合いください.


はじめに

目的

本記事の目的は以下の2点です.

  1. プログラミングを駆使してMDランクマ(マスター帯)の対戦シミュレーションを実施する

  2. シミュレーション結果から,最高ランク到達の鍵となる勝率を導き出すと同時に,結果の妥当性について考察する

背景

私は、日常的に遊戯王マスターデュエル(MD)をそこそこプレイしており、ランクマッチ(ランクマ)における昇格の難しさや平均勝率の影響に興味を持っています。
最近、X(旧Twitter)で「試行回数さえ重ねれば昇格できるといえる勝率について計算した結果はあるのか」という投稿を目にしました。この投稿は多くのプレイヤーの関心を集め、私もプログラミングの力を借りて、この疑問に対する具体的な答えを見つけてみようと思いました。

ランクマにおける勝率と対戦回数の関係については、感覚的には理解しているつもりですが、実際の数値として具体的に示すことは容易ではありません(そもそも私はマスター帯にすら行けていません)。そこで、シミュレーションを通じてデータを収集し、勝率が昇格に与える影響を明確にすることに挑戦しました。この記事では、そのシミュレーション結果をもとに,MDにおける勝率と昇格の関係を詳しく解説していきます。

シミュレーションの結果を通じて,平均勝率がどのように昇格に影響を与えるのか、またどの程度の勝率があれば安定して最高ランクに到達できるのかを見ていきます。この記事を読むことで、プレイヤーの皆さんが自分の勝率を客観視するきっかけになれば幸いです.

このような背景に基づいて、私はMDにおけるランクマの勝率と要求される対戦回数の関係について、シミュレーションを用いて具体的なデータを提供することにしました。

本記事の対象者像

本記事の対象者は、以下のような読者層を想定しています。

  1. 遊戯王マスターデュエル(MD)のプレイヤー

    • ランクマッチの仕組みに関心があり、自分の勝率を見直したいと考えている。

  2. プログラミング初心者

    • Pythonやシミュレーションに興味があり、簡単なプログラムを実行してみたい。

    • Google Colabの使い方を知りたい。

  3. 統計やデータ解析に関心がある人

    • シミュレーションを通じてデータの解析方法や結果の読み取り方を学びたい。

忙しい人用

細かい話はいいからさっさと結果が知りたい人向けのリンクです。
説明不要の場合は
ここをクリック


「遊戯王マスターデュエル」について

本記事の読者にはプログラミング畑の方も多いと思うので、念のためご説明します。
「遊戯王マスターデュエル(通称MD)」は、人気カードゲーム「遊戯王OFFICIAL CARD GAME」をデジタル化した本格デジタルカードゲーム(DCG)です。KONAMIが開発・運営しており、2024年現在、世界中のプレイヤーに楽しまれています。このゲームは、デスクトップやモバイル、コンソールなど、様々なプラットフォームでプレイ可能です。

「遊戯王マスターデュエル」では、一般的なオンライン対戦型ゲームと同様にPvPのランクマッチが用意されています。プレイヤーは対戦を通じてランクを上げ、上位ランクを目指すことができます。ランクマッチはシーズン制で、各シーズンごとにプレイヤーのランキングがリセットされ、新たな挑戦が始まります。

本記事では、この「遊戯王マスターデュエル(MD)」のランクマッチ(マスターランク帯)を対象としたシミュレーションを実施します。

シミュレーションの概要

シミュレーションとは

シミュレーションとは、なんらかの現実のできごとをコンピュータ上でモデル化(計算式に落とし込むなど)し、その動作を模擬的に実現する手法です。具体的には、特定の条件などを設定し、それに基づいてシステムがどのように動作するかを仮想的に再現します。シミュレーションを行うことで、実際の環境でデータを収集することなく、様々なシナリオを試すことができ、結果を予測できます

シミュレーションの基本的な考え方

シミュレーションの基本的な考え方は、以下のステップに分けられます:

  1. モデルの構築: システムの動作を模倣するための数学的または論理的なモデルを作成します。MDの場合、ランクマや勝敗の条件をモデル化します。

  2. パラメータの設定: モデルに必要な入力パラメータ(例えば、勝率や試行回数)を設定します。

  3. シミュレーションの実行: パラメータに基づいてシミュレーションを繰り返し実行し、結果を収集します。

  4. 結果の分析: 収集したデータを分析し、目的に沿った結論を導き出します。

実測データを用いない理由

当たり前ですが,実測データ(実際のMDのプレイデータ)を用いることには多くの利点がありますが、以下の理由からシミュレーションを選択しました:

  1. データ収集の難しさ: 実際のゲームプレイデータを大量に収集することは非常に時間がかかり、コストがかかります。特に、統計的に有意な(妥当な)サンプルサイズを得るためには多くのプレイヤーの協力が必要です。

  2. 条件のコントロール: 実際のゲーム環境では、プレイヤーのスキルやデッキの構成、運など、多くの要素が影響を与えます。これらの条件を完全にコントロールすることは難しく、結果の解釈が複雑になります。

シミュレーションの強み

シミュレーションを使用することには、以下の強みがあります:

  1. コスト効率: コンピュータ上で仮想的に試行を繰り返すことで、現実世界での試行に比べてコストと時間を大幅に節約できます。

  2. 条件の自由な設定: 勝率や試行回数などの条件を自由に設定でき、さまざまなシナリオを試すことが可能です。これにより、特定の条件下での挙動を詳細に分析できます。

  3. 反復可能性: 同じ条件で何度でもシミュレーションを繰り返すことができるため、再現性の高い結果を得ることができます。これにより、結果の信頼性が向上します。

シミュレーションは、実際のデータ収集の困難さを克服し、自由に条件を設定して実験ができる強力なツールです。本記事では、このシミュレーション技術を用いて、MDにおけるランクマの勝率と対戦回数の関係を明らかにします。次のセクションでは、具体的なシミュレーションの設定と実行方法について詳しく説明します。

MDのランクマ(マスター帯)のモデル化

今回のシミュレーションでは、MDのマスター帯におけるランクマを以下のようにモデル化しました。

  1. ランクシステム: ランクママスターランクは1(最高ランク)から5(最低ランク)までの5段階に分かれています。プレイヤーは勝利数に応じて昇格し、敗北数に応じて降格します。

  2. 昇格と降格の条件:

    • 昇格: 5勝するとランクが1つ上がります。

    • 降格: 「あと5勝で昇格」が表示された状態で3連敗するとランクが1つ下がります。ただし、ランク5(最低ランク)からは降格しません。

  3. 対戦結果の処理:

    • 勝利数,敗北数の積み上げをそれぞれ「勝利カウンタ」「敗北カウンタ」と呼称します.

    • 勝利すると「勝利カウンタ」が1加算され、「敗北カウンタ」はリセットされます。

    • 敗北すると「敗北カウンタ」が1加算され、「勝利カウンタ」は1減少します(ただし0未満にはなりません)。

    • 特定の条件(5勝または「勝利カウンタ」が0の状態での3連敗)が満たされるとランクが変動します。

シミュレーションの条件(パラメータの設定)

今回のシミュレーションでは、以下の条件とパラメータを設定しました。

  1. 勝率の範囲: 勝率は0.40から0.80までの範囲で設定し、0.01刻みで計算を行います。これにより、広範な勝率の条件でシミュレーションを実行し、結果を比較することができます。0.40から0.80の範囲にした根拠は,予備実験を実施した結果0.40未満の勝率の範囲では最高ランク到達は困難であり,逆に高すぎる勝率では当たり前のように昇格してしまうので,シミュレーションで検証する意義が薄いと考えたからです.

  2. 試行回数: 各勝率について1万回の試行(ここでは「ある決められた回数の対戦を行う」ことを「試行」と表現しています)を実施します。これにより、じゅうぶん多いデータを得られます。

  3. 対戦回数の上限: 各試行における最大対戦回数を10000回に設定しました。この上限に達するまでに最高ランクに到達できない場合は、その試行を終了とします。

シミュレーションの実行プロセス

  1. 初期化: 各試行の開始時に、プレイヤーのランクを5、勝利カウンタと敗北カウンタを0に設定します。

  2. 対戦の繰り返し:

    • ランダムに対戦結果を生成し、勝率に基づいて勝敗を決定します。

    • 勝利または敗北に応じて、勝利カウンタと敗北カウンタを更新します。

    • カウンタの値が一定の条件を満たした場合にランクを変更し、カウンタをリセットします。

  3. 試行の終了:

    • プレイヤーがマスター1に到達するか、対戦回数が10000回に達するまで対戦を繰り返します。

    • 各試行の結果として、最高ランクに到達するまでの対戦回数を記録します。

  4. データの集計: 全ての試行が終了した後、各勝率における平均試行回数、分散、標準偏差などを計算し、結果をまとめます。

次のセクションでは、シミュレーションの具体的な実行方法について詳しく見ていきます。

プログラムの準備

Google Colabを使ったプログラムの実行方法

Google Colabは、インターネット接続があれば誰でも簡単にPythonコードを実行できるオンラインのプラットフォームです(本記事のプログラムはPythonで作成しています)。特別なソフトウェアのインストールは必要なく、ブラウザ(Safari,Chrome,Edgeなど)から直接操作できます。以下の手順に従って、Google Colabでシミュレーションプログラムを実行する方法を解説します。

Google Colabにアクセスする

  1. Googleアカウントの用意:

    • Google Colabを使用するにはGoogleアカウントが必要です。まだ持っていない場合は、Googleアカウントを作成してください。

  2. Google Colabにアクセス:

    • ウェブブラウザを開き、Google Colabにアクセスします。

    • 初めてアクセスする場合は、Googleアカウントでログインしてください。

新しいノートブックを作成する

  1. 新しいノートブックの作成:

    • Google Colabのホーム画面で「新しいノートブック」をクリックします。

    • これで新しいノートブックが作成され、Pythonコードを入力できる画面が表示されます。

必要なライブラリのインストール

Google Colabには多くのライブラリ(複数のツールが詰まった道具箱のようなもの)が事前にインストールされていますが、必要に応じて追加のライブラリをインストールできます.

  1. コードの実行:

    • セルの左側にある再生ボタンをクリックしてコードを実行します。

    • 数秒でインストールが完了します。

シミュレーションプログラムの実行

次に、シミュレーションプログラムをGoogle Colabにコピーして実行します。
以下が,実際のプログラムです.ブロックごとに分かれているので,分割してコピー&ペースト&実行すると失敗しても対処がしやすいです.

import random  # 乱数生成用のライブラリ
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

def simulate_rank_match(win_rate, trials=10000, max_steps=10000):
    """
    ランクマッチをシミュレートする関数
    :param win_rate: 勝率(0.40から0.80)
    :param trials: 試行回数
    :param max_steps: 各試行の最大対戦回数(デフォルトは10000)
    :return: 昇格に必要な試行回数と最終ランクのリスト
    """
    required_trials = []  # 昇格に要求される試行回数のリスト
    final_ranks = []      # 試行終了時点でのランクのリスト
    
    random.seed(42)  # ランダムシードを固定
    
    for i in range(trials):  # trialsで指定した回数まで繰り返す
        wins = 0             # 勝利カウンタ
        losses = 0           # 敗北カウンタ
        rank = 5             # 初期ランク(最低ランク)
        steps = 0            # 対戦回数
        
        while rank > 1 and steps < max_steps:  # 最高ランクに到達するまでまたは最大対戦回数に達するまで
            steps += 1  # 試行回数をインクリメント
            
            if random.random() < win_rate:  # 勝利した場合
                wins += 1                   # 勝利カウンタをインクリメント
                losses = 0                  # 敗北カウンタをリセット
                
                if wins == 5:               # 勝利カウンタが5累積した場合
                    rank -= 1               # ランクアップ
                    wins = 0                # 勝利カウンタをリセット
            else:                           # 敗北した場合
                if wins==0:                 # 勝利カウンタが0であれば
                    losses += 1             # 敗北カウンタをインクリメント
                else:                       # 勝利カウンタが0でなければ
                    wins = max(0, wins - 1) # 勝利カウンタをデクリメント(0未満にはならない)
                if losses == 3:             # 敗北カウンタが3累積した場合
                    if rank < 5:            # ランクが5より下の場合
                        rank += 1           # ランクダウン
                    losses = 0              # 敗北カウンタをリセット
        
        required_trials.append(steps)  # 最高ランクに到達するまでの試行回数を追加
        final_ranks.append(rank)       # 試行終了時点でのランクを追加
    
    return required_trials, final_ranks  # 必要な試行回数と最終ランクを返す

# 勝率を0.4から0.8まで0.01刻みでシミュレートする
win_rates = [i / 100.0 for i in range(40, 81)]
results = {win_rate: simulate_rank_match(win_rate) for win_rate in win_rates}

# 各勝率に対する平均試行回数と最終ランクを計算
average_trials = {win_rate: np.mean(trials) for win_rate, (trials, _) in results.items()}
average_ranks = {win_rate: np.mean(ranks) for win_rate, (_, ranks) in results.items()}
variances = {win_rate: np.var(trials) for win_rate, (trials, _) in results.items()}
std_devs = {win_rate: np.std(trials) for win_rate, (trials, _) in results.items()}

# 結果をテーブル形式で出力
result_table = pd.DataFrame({
    'Win Rate': list(average_trials.keys()),
    'Average Trials to Reach Rank 1': list(average_trials.values()),
    'Average Final Rank': list(average_ranks.values()),
    'Variance': list(variances.values()),
    'Standard Deviation': list(std_devs.values())
})

result_table = result_table.round(2)
print(result_table)
result_table.to_csv("mdsimuresult.csv")
# 平均試行回数、平均最終ランク,分散、標準偏差のプロット
fig, axs = plt.subplots(4, 1, figsize=(10, 18))

axs[0].plot(average_trials.keys(), average_trials.values(), marker='o')
axs[0].set_xlabel('Win Rate')
axs[0].set_ylabel('Average Trials to Reach Rank 1')
axs[0].set_title('Average Trials to Reach Highest Rank (Rank 1) vs Win Rate')
axs[0].grid(True)

axs[1].plot(average_ranks.keys(), average_ranks.values(), marker='o')
axs[1].set_xlabel('Win Rate')
axs[1].set_ylabel('Average Final Rank')
axs[1].set_title('Average Final Rank vs Win Rate')
axs[1].grid(True)

axs[2].plot(variances.keys(), variances.values(), marker='o')
axs[2].set_xlabel('Win Rate')
axs[2].set_ylabel('Variance')
axs[2].set_title('Variance of Trials to Reach Highest Rank (Rank 1) vs Win Rate')
axs[2].grid(True)

axs[3].plot(std_devs.keys(), std_devs.values(), marker='o')
axs[3].set_xlabel('Win Rate')
axs[3].set_ylabel('Standard Deviation')
axs[3].set_title('Standard Deviation of Trials to Reach Highest Rank (Rank 1) vs Win Rate')
axs[3].grid(True)

plt.tight_layout()
plt.show()

コードの実行

  • セルの左側にある再生ボタンをクリックしてコードを実行します。

  • 数分でシミュレーションが完了し、結果が表示されます。

プログラムの詳細

このセクションでは、シミュレーションプログラムの設定、ランクの計算方法、試行の実行についてより詳しく説明します。具体的なプログラムコードを基に、どのようにシミュレーションが動作するのか解説します。

シミュレーションの設定

シミュレーションの設定は、プログラムの動作を決定するための重要なステップです。今回は、勝率や試行回数などのパラメータを設定しました。

  1. 勝率の設定:

    • 勝率は0.40から0.80までの範囲で設定し、0.01刻みで計算を行います。これにより、広範な勝率の条件でシミュレーションを実行し、結果を比較できます。

    • 勝率の設定は、以下のコードで行われます。

    • win_rates = [i / 100.0 for i in range(40, 81)]

  2. 試行回数の設定:

    • 各勝率について1万回の試行を行います。これにより、十分多い(統計的に有意な)データを得られます.

    • 試行回数の設定は、シミュレーション関数内で以下のように指定されています。

    • results = {win_rate: simulate_rank_match(win_rate, trials=10000) for win_rate in win_rates}

  3. 対戦回数の上限:

    • 各試行における最大対戦回数を10000回に設定しました。この上限に達するまでに最高ランクに到達できない場合は、その試行を終了とします。

    • 対戦回数の上限は、シミュレーション関数の引数で指定されています。

    • def simulate_rank_match(win_rate, trials=10000, max_steps=10000):

ランクの計算方法

各試合でのランクの変動の仕組みを以下のようにモデル化しました.

  1. 初期化:

    • 各試行の開始時に、プレイヤーのランクを5(最低ランク)、勝利カウンタと敗北カウンタを0に設定します。

    • wins = 0
      losses = 0
      rank = 5
      steps = 0

  2. 勝利と敗北の処理:

    • 勝利すると「勝利カウンタ」が1加算され、「敗北カウンタ」は0にリセットされます。

    • 敗北すると「勝利カウンタ」は1減少します(ただし0未満にはなりません)。また,「勝利カウンタ」が0の場合のみ,「敗北カウンタ」を1加算します.

    • if random.random() < win_rate: # 勝利した場合
          wins += 1
          losses = 0
          if wins == 5:
              rank -= 1
              wins = 0
      else: # 敗北した場合
          if wins == 0:
              losses += 1
          else:
              wins = max(0, wins - 1)
          if losses == 3:
              if rank < 5:
                  rank += 1
              losses = 0

  3. ランクの変動:

    • 「勝利カウンタ」が5に達するとランクが1つ上がります。

    • 「敗北カウンタ」が3に達するとランクが1つ下がります。ただし、ランク5(最低ランク)からは降格しません。

試行の実行

プログラムがどのように試行を実行するかについて説明します。

  1. 試行の繰り返し:

    • 各試行は、指定された回数(ここでは1万回)だけ繰り返されます。

    • 各試行の開始時に、ランクとカウンタを初期化します。

    • for i in range(trials):
          wins = 0
          losses = 0
          rank = 5
          steps = 0
          while rank > 1 and steps < max_steps:
              steps += 1
              if random.random() < win_rate:
                  wins += 1
                  losses = 0
                  if wins == 5:
                      rank -= 1
                      wins = 0
              else:
                  if wins == 0:
                      losses += 1
                  else:
                      wins = max(0, wins - 1)
                  if losses == 3:
                      if rank < 5:
                          rank += 1
                      losses = 0

  2. 結果の記録:

    • 各試行の結果として、最高ランクに到達するまでの対戦回数を記録します。

    • 結果はリスト(数や文字を複数格納するためのダースのようなもの)に追加され、試行が終了した後に集計されます。

    • required_trials.append(steps)
      final_ranks.append(rank)

  3. データの集計:

    • 全ての試行が終了した後、各勝率における試行回数の平均、分散、標準偏差などを計算し、結果をまとめます。

    • average_trials = {win_rate: np.mean(trials) for win_rate, (trials, _) in results.items()}
      variances = {win_rate: np.var(trials) for win_rate, (trials, _) in results.items()}
      std_devs = {win_rate: np.std(trials) for win_rate, (trials, _) in results.items()}

結果

表でのまとめ

下記に,シミュレーションを実施した結果を表形式で示します.
Win Rate:勝率
Average Trials to Reach Rank 1:マスター1に到達するまでの平均対戦回数
Average Final Rank:10000回対戦終了後の平均到達ランク
Variance:マスター1に到達するまでの対戦回数の分散
Standard Deviation:マスター1に到達するまでの対戦回数の標準偏差

$$
\begin{array}{r|r|r|r|r|} \hline
& Win Rate & Average Trials to Reach Rank 1 & Average Final Rank & Variance & Standard Deviation \\
0 & 0.4 & 5127.95 & 1.77 & 12095515.55 & 3477.86 \\
1 & 0.41 & 3642.59 & 1.27 & 8815550.8 & 2969.1 \\
2 & 0.42 & 2409.13 & 1.05 & 4791495.89 & 2188.95 \\
3 & 0.43 & 1604.95 & 1.01 & 2268076.71 & 1506.01 \\
4 & 0.44 & 1092.06 & 1.0 & 1004793.03 & 1002.39 \\
5 & 0.45 & 778.0 & 1.0 & 479104.02 & 692.17 \\
6 & 0.46 & 572.28 & 1.0 & 236248.38 & 486.05 \\
7 & 0.47 & 432.11 & 1.0 & 127136.19 & 356.56 \\
8 & 0.48 & 339.07 & 1.0 & 68901.11 & 262.49 \\
9 & 0.49 & 274.0 & 1.0 & 40708.46 & 201.76 \\
10 & 0.5 & 226.11 & 1.0 & 24775.14 & 157.4 \\
11 & 0.51 & 190.25 & 1.0 & 15636.16 & 125.04 \\
12 & 0.52 & 164.65 & 1.0 & 10721.64 & 103.55 \\
13 & 0.53 & 144.59 & 1.0 & 7203.65 & 84.87 \\
14 & 0.54 & 128.33 & 1.0 & 5172.97 & 71.92 \\
15 & 0.55 & 115.14 & 1.0 & 3837.46 & 61.95 \\
16 & 0.56 & 104.3 & 1.0 & 2915.2 & 53.99 \\
17 & 0.57 & 95.07 & 1.0 & 2171.91 & 46.6 \\
18 & 0.58 & 87.54 & 1.0 & 1697.22 & 41.2 \\
19 & 0.59 & 80.93 & 1.0 & 1300.49 & 36.06 \\
20 & 0.6 & 75.25 & 1.0 & 1006.48 & 31.73 \\
21 & 0.61 & 70.2 & 1.0 & 823.69 & 28.7 \\
22 & 0.62 & 66.16 & 1.0 & 689.77 & 26.26 \\
23 & 0.63 & 62.35 & 1.0 & 572.92 & 23.94 \\
24 & 0.64 & 58.87 & 1.0 & 472.15 & 21.73 \\
25 & 0.65 & 55.66 & 1.0 & 381.45 & 19.53 \\
26 & 0.66 & 52.99 & 1.0 & 322.3 & 17.95 \\
27 & 0.67 & 50.55 & 1.0 & 276.31 & 16.62 \\
28 & 0.68 & 48.25 & 1.0 & 236.45 & 15.38 \\
29 & 0.69 & 46.12 & 1.0 & 200.0 & 14.14 \\
30 & 0.7 & 44.13 & 1.0 & 171.9 & 13.11 \\
31 & 0.71 & 42.49 & 1.0 & 148.47 & 12.18 \\
32 & 0.72 & 40.82 & 1.0 & 128.28 & 11.33 \\
33 & 0.73 & 39.41 & 1.0 & 111.41 & 10.55 \\
34 & 0.74 & 38.04 & 1.0 & 97.92 & 9.9 \\
35 & 0.75 & 36.71 & 1.0 & 85.45 & 9.24 \\
36 & 0.76 & 35.54 & 1.0 & 74.28 & 8.62 \\
37 & 0.77 & 34.4 & 1.0 & 65.89 & 8.12 \\
38 & 0.78 & 33.35 & 1.0 & 57.46 & 7.58 \\
39 & 0.79 & 32.34 & 1.0 & 52.19 & 7.22 \\
40 & 0.8 & 31.4 & 1.0 & 44.45 & 6.67 \\
\end{array}
$$

また,縦軸を勝率,横軸を対戦回数,要素をその度数(頻度)としたときの表の出力方法についても掲載しておきます.
あまりに大きすぎて表本体は載せられなかったので,気になる人は実際に自分で計算してみてください.

import numpy as np
import pandas as pd

# 既存のシミュレーション結果を使用して度数分布を計算しDataFrameに変換する関数
def convert_to_2d_dataframe(results, win_rates, bins=50):
    # ビンの境界を決定
    all_trials = [trial for trials, _ in results.values() for trial in trials]
    bin_edges = np.histogram_bin_edges(all_trials, bins=bins)
    
    # ビンの中心位置を計算して丸める
    bin_centers = np.round((bin_edges[:-1] + bin_edges[1:]) / 2).astype(int)
    
    # DataFrameを初期化
    df = pd.DataFrame(index=win_rates, columns=bin_centers)
    
    # 各勝率ごとの度数分布を計算
    for win_rate in win_rates:
        trials, _ = results[win_rate]
        hist, _ = np.histogram(trials, bins=bin_edges)
        
        # DataFrameに度数を追加
        df.loc[win_rate] = hist
    
    return df

# 度数分布を2次元DataFrameに変換
df_2d_histogram = convert_to_2d_dataframe(results, win_rates)

# 結果を表示
print(df_2d_histogram)

# CSVファイルとして保存
df_2d_histogram.to_csv("winrate_density.csv", float_format="%.2f")


平均試行回数および平均到達ランク

以下の図に示します.

(上)横軸:勝率,縦軸:マス1到達までの対戦回数の平均
(下)横軸:勝率,縦軸:10000回対戦終了時の到達ランクの平均

分散と標準偏差:


以下の図に示します.

(上)横軸:勝率,縦軸:マス1到達までの対戦回数の分散
(下)横軸:勝率,縦軸:マス1到達までの対戦回数の標準偏差

度数分布: 

以下の図に示します.

Win Rate:勝率
Trials to Reach Rank 1:マス1到達までの対戦回数
Frequency:度数

また,この図を生成するためのソースコードは以下の通りです.上のシミュレーション実行プログラムと度数分布の表作成プログラムを動作させた後に実行してください.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import colormaps, colors

# CSVファイルの読み込み
csv_file_path = 'winrate_density.csv'  # 実際のファイルパスに置き換えてください
df = pd.read_csv(csv_file_path, index_col=0)

# データの確認
print(df.head())

# 3次元プロットの作成
def plot_3d_histogram_with_gradient(df, alpha=0.3, cmap='viridis'):
    fig = plt.figure(figsize=(14, 10))
    ax = fig.add_subplot(111, projection='3d')
    
    # X, Y, Zのデータ準備
    xpos, ypos = np.meshgrid(df.columns.astype(float), df.index.astype(float))
    xpos = xpos.flatten()
    ypos = ypos.flatten()
    zpos = np.zeros_like(xpos)
    
    dx = dy = 0.5
    dz = df.values.flatten()

    # Normalize the colors based on z values
    norm = colors.Normalize(dz.min(), dz.max())
    cmap = colormaps.get_cmap(cmap)
    colors_mapped = cmap(norm(dz))
    
    # プロット
    ax.bar3d(xpos, ypos, zpos, dx, dy, dz, zsort='average', alpha=alpha, color=colors_mapped)

    ax.set_xlabel('Trials to Reach Rank 1')
    ax.set_ylabel('Win Rate')
    ax.set_zlabel('Frequency')
    ax.set_title('3D Histogram of Trials to Reach Rank 1 for Different Win Rates')

    # Add color bar which maps values to colors
    mappable = plt.cm.ScalarMappable(norm=norm, cmap=cmap)
    mappable.set_array(dz)
    fig.colorbar(mappable, shrink=0.5, aspect=5)
    
    plt.show()

# プロット関数の呼び出し
plot_3d_histogram_with_gradient(df, alpha=0.3, cmap='viridis')

実際のデータの解析

このセクションでは、シミュレーション結果の読み取り方について説明します。まず、シミュレーションの結果として生成されたグラフや表を確認しましょう。

グラフの読み取り方

  • 平均試行回数のグラフ

    • 横軸は勝率を示し、縦軸はマスター1ランクに到達するまでの平均試行回数を示しています。

    • 勝率が上がるほど、必要な対戦回数が減少することが明らかです。例えば、勝率が0.4の場合、およそ約5100回の対戦が必要ですが、勝率が0.8になると30回程度に減少します。

  • 分散のグラフ

    • 横軸は勝率を示し、縦軸は「マスター1に到達するまでの対戦回数」の分散を示しています。

    • 分散は、データがどれだけ散らばっているかを示します。勝率が低い場合、試行回数のばらつきが大きくなり、勝率が高い場合にはばらつきが小さくなっています。単調減少ですね.

  • 標準偏差のグラフ

    • 横軸は勝率を示し、縦軸は「マスター1に到達するまでの対戦回数」の標準偏差を示しています。

    • 標準偏差は、データの平均からのばらつきを示します。標準偏差が大きいほど、試行回数が平均から大きく外れることが多く、標準偏差が小さいほど、試行回数が平均に近いことを意味します。

  • 度数分布のグラフ

    • 横軸はマスター1に到達するまでの対戦回数です.右に行くほど,多くの対戦回数が必要であることを意味しています.

    • 縦軸は勝率です.奥に行くほど勝率が高いです.

    • 高さ(z軸)は対戦回数の度数分布(頻度)を示しています.この高さが高いほど,その勝率・その対戦回数の発生頻度が高いということです

    • 勝率が高い場合、対戦回数が少ない範囲の発生頻度が高く、勝率が低い場合、対戦回数が広い範囲に分布する(要するに対戦回数がばらつく)ことがわかります。このグラフは、データの分布特性やばらつきを視覚的に示しています.

表の読み取り方

  • 勝率、マスター1到達までの平均対戦回数、10000回試行後の平均到達ランク、対戦回数の分散、対戦回数の標準偏差が含まれています。

  • 各勝率ごとに、対応する数値が示されており、勝率が高くなるほど、平均試行回数が減少し、分散と標準偏差も減少することがわかります。

シミュレーションの妥当性

シミュレーションの結果が統計的に有意である(じゅうぶんなデータ量を持つ)ことを確認するために、分散、標準偏差、度数分布について説明します。

分散

  • 定義:分散はデータのばらつきを示す指標であり、個々のデータが平均からどれだけ離れているかを示します。分散が大きいと,データが平均から広く散らばっていることを意味します.例えば,対戦回数の分散が大きい場合,勝率がたとえ一定だとしても昇格までの対戦回数が大きく変動することを示します.

  • 解釈:勝率が低い場合、分散が大きくなることは、試行回数に大きなばらつきがあることを示します。これは、勝率が低いと昇格が難しく、対戦回数が多く必要になることが多いためです。

標準偏差

  • 定義:標準偏差は分散の平方根(√)であり、データの平均からのばらつきを示します。

  • 解釈標準偏差が大きい場合、対戦回数が平均から大きく外れることが多いことを意味します。勝率が低い場合、標準偏差が大きくなることは、対戦回数の平均からのばらつきが大きいことを示します。

    • 例えば、仮に勝率が0.50のときの標準偏差が100である場合、対戦回数の68%が平均±100回の範囲に収まることを意味します.

度数分布

  • 定義:度数分布は、データがどのように分布しているかを示す表またはグラフです。今回は表が掲載できなかったため3次元のグラフで示しています.

  • 解釈:度数分布を確認することで、対戦回数がどの範囲に多く集中しているかを把握できます。勝率が高い場合、対戦回数が少ない範囲に集中し、勝率が低い場合、(そもそもマスター1に登頂できていないため)度数分布上に出現していないのが確認できます.

以上の解析結果から、シミュレーションが有意であると判断できます。具体的には、分散や標準偏差が勝率に応じて変化することから、シミュレーションが実際のランクマの挙動を適切に反映していると考えられます。

これにより、シミュレーション結果を用いて、実際のプレイヤーがどの程度の勝率を目指すべきかを考える材料にできるといえます。

応用と改善

異なるシナリオの検討

シミュレーションをさらに発展させるために、以下のような異なる条件や設定でシミュレーションが実施できると考えます.

  1. デッキの強さの影響:

    • デッキの強さを変数として導入し、勝率に影響を与える要素としてシミュレーションする(有志が作成しているtier表が参考にできる?)。

    • 例えば、特定のデッキが強い環境下での勝率を調整し、その影響を分析する。

    • ここで,「環境」とは以下の定義をみたすとする(賛否あると思いますがここではこのように表現します).

      • 「KONAMIなどの公式機関が定めたカードプールとリミットレギュレーション(枚数制限)を遵守して構築したデッキの中で、特定の期間において勝率とシェアが突出して高いデッキ群のこと」

        • 補足:

          • カードプール: 公式が使用を許可した全てのカードの集合。

          • リミットレギュレーション: デッキ構築時に特定のカードの枚数制限を定めたルール。

          • 勝率: 公式大会やランクマッチなどでの試合における勝利の割合。

          • シェア: 全体のデッキの中で特定のデッキが占める割合。

          • 特定の期間: 通常、シーズンと呼ばれる一定期間を指し、その間に環境が変動することがある。(ex.OCGならリミットレギュレーションの更新頻度が基準,MDならランクマッチのシーズン(1ヶ月)が基準)

          • 具体例:例えば、あるシーズンに「ドラゴンデッキ(仮称)」が公式大会での勝率が70%であり、全デッキの40%以上を占めている場合、この「ドラゴンデッキ」はそのシーズンの「環境トップデッキ」とみなしてよい.

  2. 対戦相手の強さの変動:

    • ランクが上がるにつれて、対戦相手の強さも増すような設定を導入する(要するにランクによって勝率に傾斜をつける)。

    • 対戦相手の強さが変動するシナリオをシミュレートし、勝率と対戦回数の関係を再評価する。

  3. リアルタイムの調整:

    • (正直難しいとは思うが)実際のプレイヤーデータを用いて、シミュレーション結果をリアルタイムで調整する。

    • 例えば、プレイヤーが実際に対戦するデータを収集し、その結果をシミュレーションにフィードバックする。

プログラムの改善

シミュレーションプログラムをさらに改善するためのヒントや、さらなる分析を行う方法を以下に示します。

  1. コードの最適化:

    • 現在のプログラムはシンプルなランダム関数(randomライブラリのもの)を使用しています、より高速で効率的なアルゴリズムを導入することで、計算時間を短縮できます。

    • 並列処理を導入し、大規模なシミュレーションを高速化します。

  2. 視覚化(Visualization)の強化:

    • 現在のグラフに加えて、インタラクティブなビジュアライゼーションツール(例:PlotlyやBokeh)を使用することで、データの理解が深まります。

    • ユーザーが自分でデータを操作できるようにすることで、より直感的な分析が可能になります。

  3. 追加の統計分析:

    • 勝率と対戦回数の関係だけでなく、勝率と昇格速度、勝率とデッキの強さなどの関係も分析できるといいでしょう。

    • 例えば、回帰分析を行い、勝率が昇格速度に与える影響を定量的に評価でき(るような気がし)ます。

  4. ユーザーインターフェース(UI)の改善:

    • シミュレーションプログラムをより使いやすくするためのユーザーインターフェースを開発できるといいですね。

    • 例えば、勝率や試行回数を簡単に設定できるフォームを用意し、シミュレーション結果をリアルタイムで表示できると面白いと思います。

  5. モデルの再考

    • (al_knight君の指摘を受け追記)現在のシミュレーションは非常に簡略化されたモデルを使用している.例えば、10000回の対戦および10000回の試行の最中にいっさい勝率が変化していない点が挙げられる。現実のゲームプレイでは、プレイヤーのスキルやデッキの強さが対戦を重ねるごとに変化することに加えて,そもそも勝率とは

      • 瞬間瞬間で変化していくものである

      • 対戦を重ねるまで勝率はわからないものである(経験則によって初めてわかる)

    • ため,静的なものとしてとらえるのがナンセンスであり、このような静的なモデルは現実を正確に反映していない。

      • 今後の改善点として、動的なモデルの構築が挙げられる。具体的には、例えば試行中に勝率が変動する要素を取り入れたモデルを作成することで、より現実に近いシミュレーションが可能になる。これにより、プレイヤーがどのように進化し、デッキがどのように調整されるかを反映した、より精緻な分析が期待できる。

総括

学んだことの振り返り

今回の記事では、遊戯王マスターデュエル(MD)のランクマ(マスター帯)における勝率と対戦回数の関係についてシミュレーションを用いて解析しました。主なポイントを以下にまとめます。

  1. シミュレーションの重要性:

    • 実測データを用いずに、様々なシナリオを試すことができるため、効率的かつコストを抑えた方法でデータを収集できる。

    • 勝率や試行回数などの条件を自由に設定できるため、特定の条件下での挙動を詳細に分析可能。

  2. シミュレーションの設定と実行方法:

    • 勝率を0.40から0.80まで0.01刻みで設定し、各勝率について1万回の試行を行うことで、統計的に有意なデータを収集。

    • 各試行における最大対戦回数を10000回に設定し、勝率に応じた対戦回数と最終ランクを計算。

  3. 結果の解析:

    • 勝率が上がるほど、マスター1に到達するまでの対戦回数が減少し、分散や標準偏差も減少することが明らか.

    • 具体的には,

      • 勝率5割5分以上:(計算上)マス1昇格までの対戦回数の要求値は100回程度になる

      • 勝率4割3分以上:10000回対戦終了時に,マス1に到達している可能性が高い

        • 裏を返せば,勝率4割3分に届いていない場合,仮に1ヶ月に1万回対戦したとしてもマスター1には届かない可能性が高い

    • といえる.

    • 度数分布のグラフを用いて、対戦回数がどの範囲に多く集中しているかを視覚的に把握できる。

今後の展望

今後の分析や研究に向けたアイデアを以下に提案します。

  1. 異なるシナリオの検討:

    • デッキの強さや対戦相手の強さの変動を考慮したシミュレーションを実施し、より現実的なモデルを構築する。

    • 実際のプレイヤーデータを用いたリアルタイムの調整を試み、シミュレーション結果の精度を向上させる。

  2. プログラムの改善:

    • コードの最適化や並列処理の導入により、計算時間を短縮し、大規模なシミュレーションを迅速に実行できるようにする。

    • インタラクティブな可視化ツールを用いて、データの理解を深める。

    • 勝率と昇格速度、勝率とデッキの強さなどの関係を追加の統計分析により評価する。

    • 動的なモデルを構築し,よりMDの実態に即したシミュレーションを実施する.

  3. UIの改善:

    • シミュレーションプログラムをより使いやすくするためのユーザーインターフェースを開発し、プレイヤーが簡単に設定を変更できるようにする。

以上のように、シミュレーション技術を活用することで、MDプレイヤーの皆さんがより良い成績を収めるためのヒントが(私の余暇と実力さえあれば)今後も提供可能かもしれません。
今回の記事が、読者の皆さんが自身の勝率を客観的に評価する参考に少しでもなれば幸いです。

また,「じゃあ勝率55%に到達するにはどんな構築やプレイをすればいいのか!?」とお思いになった方もいらっしゃると思いますが,そこまでは本記事のスコープ外なので今回は掘り下げません.やるとしたら,勝率に寄与する要素としてメインデッキの誘発の枚数を盛り込むとかですかね.面白そうではあります.その辺の話は後発のnoteや専門家にお任せしようと思います(笑)

それではこの辺で.
Good luck.

謝辞

本記事は本投稿前に,複数名からレビューをいただきました.
この場を借りて,深く感謝申し上げます.
ありがとうございました.

  • SSM君

  • BTB溶液君

  • al_knight君

  • はのちさん


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