原神、スターレイル、ゼンレスゾーンを離れてからというもの、最近は鳴潮にはまっています (そう、「xxx さえ xxx できればいいが、xxx には考えることがたくさんある」あのゲームです)。こうした二次元ゲームといえば、忌々しいガチャは欠かせない要素。ガチャがあればキャラや武器の「当たり・外れ」が生まれ、それに伴い「xxxアシスタント」「xxx工房」のようなサードパーティミニプログラムが大量に生まれる。それらの重要な機能の一つがガチャ分析だ。
きっかけは、ある友人が私のスターレイルのガチャ履歴を見たがっていたこと。これまで使っていたミニプログラムを開いてみると、以前保存していたガチャ記録がすべて消えていた。スターレイルのクラウドゲームをダウンロードしてガチャリンクを再インポートしようとしたが、ミニプログラムは「ガチャアドレスエラー」とばかりで、結局履歴を復元できなかった。その後もずっと引っかかっていて、「自分用のガチャ記録分析ツールを作って、データを自分の手元に置いておこう」と思い立ち、このガチャ分析ツールを書いた。

Astrionyx
Astrionyx は Next.js ベースの Web アプリで、異なる種類のガチャ記録を分析可能。手動でのインポート・更新に対応し、API 経由でのデータ取り込みも行える。
また、Vercel および自分のサーバーへのデプロイに対応。MySQL と Vercel Postgres のデュアルデータベースをサポート。現在、海外トラフィックは Vercel にルーティングされ、Vercel Postgres を使用。国内トラフィックは自前サーバーにルーティングし、MySQL を使用。とてもとても便利だ。
Astrionyx の開発中には面白い問題が多数発生した。もし同じようなアプリを書きたい人がいたら、参考になれば幸い。
データのインポートと更新
データインポート
ほとんどのゲーム同様、鳴潮にはガチャデータをエクスポートするための公式 API は存在しない。ガチャ履歴の表示方法は、「ガチャ履歴」ボタンを押すとゲーム内ブラウザで一時的なリンクを開き、そこに履歴が表示されるというもの。この https://aki-gm-resources.aki-game.com/aki/gacha/index.html で始まるリンクをパケットキャプチャやログファイルから取得できる。
該当ページは POST で API https://gmserver-api.aki-game2.com/gacha/record/query にリクエストを送り、各ガチャプールの履歴を取得する。このリクエストには playerId、serverId、cardPoolId などの重要情報が含まれ、これらは URL パラメータから抽出可能:
cardPoolType は [1, 7] の値を取り、以下のように対応:
バックエンド API を作成し、公式 API に転送して各ガチャプールの履歴を取得すればよい。
データ更新
鳴潮などのゲームには「10連ガチャ」が存在する。

10連ガチャ
図のように「10連ガチャ」では同一アイテムが 2 個出現することがあり、抽選時間を含むすべての属性が完全に一致する。JSON で取得すると以下のようになる:
これにより、後続の更新でどの記録がインポート済みか判別できず、統計の正確さが損なわれる。そこで、同一時刻でも個別に識別可能な ID を構築する必要がある。
ガチャプールタイプID + タイムスタンプ + 抽選番号 で一意のIDを決定できる。抽選番号は、同一タイムスタンプ内で何番目の抽選かを 1 開始でカウントしたもの。上記 2 つの同名武器に uniqueId を付与すると 01174845445601 と 01174845445605 となる。
これでインポートデータと既存データが重複していても、新規ガチャ記録を正しく識別してデータベースを更新できる。
確率データの計算
ページの統計概要では、ECharts ライブラリを使いガチャ確率分析折れ線グラフを背景コンポーネントとして表示し、ガチャシステムの確率分布を可視化している。グラフデータは以下 2 関数で計算:
理論確率計算
B 站 UP 主「一棵平衡树」による 鳴潮ガチャシステム簡析 によれば、鳴潮のガチャ確率は以下モデルに従う($i$ はガチャ回数):
これを基に理論確率関数 calculateTheoreticalProbability を構築:
実測確率計算
ツールは個人利用が主でサンプルサイズが小さいため、特定の抽選回数にデータが存在しない場合が多い。単純に相対頻度で推定すると確率曲線が大きく乱れ、0% や 100% といった極端な値が出てしまう。

頻度推定
これを避けるため、ベイズ平滑化を導入して品質を向上させる。
ここで、
:$i$ 抽選目で観測された 5 星の数、$ni$:$i$ 抽選目の総ガチャ数、$P{\text{prior}}$:理論確率、$S$:平滑化係数
振る舞いは以下の通り:
が小さい(データが少ない)場合、$P{\text{posterior}}$ は理論確率 $P{\text{prior}}$ に近づく
が大きい(データが多い)場合、$P{\text{posterior}}$ は実測頻度 $\frac{ki}{n_i}$ に近づく
最適化後の効果:

ベイズ平滑化処理
実装コード:
自動デプロイ
前述の通り、Astrionyx は Vercel と自前サーバーの両方にデプロイされている。コードを push するたびにサーバーで pull&build を行うのは非常に面倒。このテーマの作者が書いた、テーマを自動ビルドしてリモートサーバーにデプロイするワークフローを少し改変して Astrionyx に流用した。
しかし、GitHub からサーバーへのネットワーク品質が非常に悪く、ビルド済みパッケージをサーバーに転送するのに平均 20 分以上かかっていた。ちょうど Cloudflare の R2 オブジェクトストレージには月 10 GB の無料ストレージとダウンロード無料があり、国内ダウンロード品質も良好なので、「中継地点」として活用できる。
オブジェクトストレージを使った主なワークフローは以下。アップロード・ダウンロードともに 5 回リトライを追加(単発ダウンロードはネットワークの揺れで失敗することもあるが、実測 5 回リトライでほぼ解決)
このワークフローにより、main ブランチへ push するたびに Astrionyx は自動ビルド&デプロイされる。R2 オブジェクトストレージを中継に使うことで、デプロイ時間が 20 分 → 4 分以内に短縮され、命を救われる。

R2オブジェクトストレージによる最適化
That's all🎉.