0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DQX の「負けるほど儲かるカジノ」を、データエンジニアならどう見つけるか

0
Last updated at Posted at 2026-09-26

はじめに

こんにちは。最近、AI の精度を上げるためのデータ基盤づくりに興味を持って調べているうちに、データ基盤そのものが面白くなってきました。データエンジニアリングは仕事で断続的にやっていますが、専業ではありません。

調べものと下書きには生成 AI をかなり使っています。事実と数字は出典を確かめ、試算は試算と明記しました。SQL は架空のスキーマに対して書いたもので、実データでは動かしていません。

この記事では、むかしドラクエ10(DQX)で起こったある問題を題材に、ゲーム内経済の異常をデータ基盤でどう捕まえるかを考えます。後半では、イベントで見て面白かった Snowflake がこの題材にどう使えるかも、試しに当ててみました。


ざっくり言うと

  • 2015年1月、DQX のカジノで「8人そろって負けるほど、8人全員が儲かる」状態が起きた(通称「カジノ逆天井問題」)
  • 島の中だけ見れば、払い出しは明らかに多い。ところが全体でならした払い出し率(RTP)だけを見ていると、この偏りは埋もれうる
  • 捕まえるなら、値の大きさではなく「誰と誰が一緒にいたか」という関係を見る
  • その SQL が書けるかどうかは、island_id や raid_id のように同じ場にいた人を後から復元できるキーを記録していたかで決まる

当時の運営の内部を評価する記事ではない。「外から見て、何を記録しておけば捕まえられたか」を考える思考実験の題材として借りている。


何が起きたか:式にすると1行

損失: 島の累積負け → 8人で割り勘
利得: レイド配当   → 8人それぞれに満額

カジノのポーカーには「島」と呼ばれる8席のまとまりがある。島全体の負けが一定額まで積もると、負けが込んだ人への救済として「カジノレイド」が必ず起きる。レイド中は賭けてもコインが減らず、配当は8人それぞれに満額で出る。

負けは8人で分け合い、救済は8人全員が満額で受け取る。

コストを共有して報酬を個別に配る構造は、そのまま裁定機会になる。

運営も修正のときに、「全員で損をすると儲かる」状況はゲームデザインとして変えるべきだった、と書いている。約1週間で修正された。

事件の詳細(日付・規模・修正内容・処分)
項目 内容
発生 2015年1月上旬(Ver.2.4.0 系)
対象 ポーカー
運営の認知 1月9日(提案広場でディレクターが回答)
修正 1月15日(2.4.0f)
レイドが起きる閾値 2F の島で約5万枚 / 3F で約50万枚。プレイヤーの試行による推定で、公式は非公表
規模 「3日で500万枚」「1000万枚超も珍しくない」(当時のブログのコメント欄)。最高額の景品キングスライムハウスが250万枚

修正内容: レイドの発生条件を「単純抽選のみ」に変えた。「負け額の累積」という入力そのものを消した形になる。あわせて発生確率を大きく上げ、全員を同じ条件にそろえた。

処分: 「不具合を不正に利用していたものではなく、外部ツール等の不正行為でもない」として、処罰もコインの回収もなし。同じ時期の「永久レイド」(こちらは不具合)だけが回収の対象になった。

残ったもの: DQ10大辞典には「荒稼ぎしたプレイヤーとの間に格差ができてしまった」とある。提案広場には「一度レイドが起きると一定期間起きないので、その後普通に遊ぶプレイヤーが割りを食う」という指摘もあった。救済の枠を先に使われると、後から来た人がその分を受け取れなくなる。


全体の RTP だけでは埋もれる

島ひとつを取り出せば、払い出し率(RTP)は 100% を明らかに超えている。問題は、監視がどの粒度で集計しているかだ。

全プレイヤー・全期間でならした RTP なら、一部の島の偏りは薄まる。しかも払い出しは「救済」という設計どおりの経路から出ているので、払い出しの仕組みが壊れているようには見えない。

思いつきやすい監視を並べると、どれも鳴りにくい。

見張るもの 鳴りにくい理由
全体の RTP 一部の島の偏りが、全体の中で薄まる
払い出しの経路 救済という正規の経路から出ている
個人の勝ち続け 儲けが8人に分かれるので、一人ひとりの伸びは島全体ほど目立たない
通報・コミュニティ 仕様の範囲内なので「不正」として通報されにくい

全体を集計すると、関係は消える。


捕まえるなら「関係」の異常

値の大きさではなく、誰と誰がどうそろったかを見る。

指標 何を見るか 読み方
島単位の収支のばらつき レイド1周期で、8人全員がプラス、しかも差が小さい 救済の本来の姿(負けた人が戻す)と逆の形
同じメンバー構成の反復 同じ8人組が何回レイドにそろったか 固定パーティーでも起きる。単独では決めない
一斉の離席と着席 レイドが始まった直後に、8人がそろって離席し、そろって座り直す ふつうの遊び方では起きにくい。ただしメンテナンスや切断でも離席は起きるので、座り直しまで見る
救済の受け手と負担者のズレ 救済で得た額 ÷ 自分が負けた額 「救済された人は本当に負けていたか」を測っている

どれも単独では決め手にならない。固定パーティーは同じ顔ぶれで何度も座るし、手口によっては毎回違う島へ移ることもある。複数の指標を重ねて、同時に立った島を見るのが現実的だ。

閾値も同じで、この記事の「5回」「10秒」は説明のための仮置きにすぎない。実際には、問題が起きていない期間の分布(固定パーティーがどれくらい同じ顔ぶれで座るか)を先に測って、そこから外れたものを拾う。

指標の選び方にも、ひとつ筋がある。救済措置を作ったのなら、「救済された人は本当に困っていたか」を測る。設計の意図を、そのまま SQL にする発想だ。

上限は片側にしか掛かっていなかった

コインは1枚20ゴールドで、手持ち1000枚まで買える。この上限が縛るのは「ゴールド→コイン」の向きだけで、「コイン→ゴールド」の向きには上限がなかった。

景品そのものは取引できないが、景品を通じてゴールドに変える経路はあった。制限を掛けた向きと、実際にお金が流れた向きが逆だった。

コインがゴールドに変わる経路と、経済への影響

経路は2つ

経路 仕組み
メダル売り 景品のメダルで挑めるボス戦への同行権を売る。アイテムは動かず、ゴールドだけが動く(バザーの外の対人取引)。相場は1枚1万〜1.2万G
ふくびき券経由 メダル → ボス討伐 → ふくびき券 → 当選した景品。この景品は取引できる

レートの非対称

コイン購入:          1枚 20G(手持ち1000枚まで)
換金したときの期待値: 1枚 約8.2G   ← 買って換金すると6割損

8.2G は、おみくじボックス32,119個の開封実測と相場(どちらも2022年)から出した期待値の試算で、2015年の値ではない。1000万枚を同じ条件で換算すると約8,200万G 相当になる(レイドが起きるまでに負けたコインと時間は含めていない)。あくまで近年の条件での参考値。

当日の書き込み(2015-01-09)として「既に換金から多額のゴールド格差すら始まってる」「このまま放置だとカジノコインのインフレ起こす」が残っている。

ゴールドを刷ったわけではない

コインの交換で直接ゴールドが発行されるわけではない。2015-01-13 の記事にも「ゴールドが直接増加するわけではない」とある。中心は、すでにあるゴールドの持ち主が入れ替わったことと、取引できる景品の供給が増えたことだった可能性が高い(景品を店に売った分のゴールドなど、間接の発行までは追えていない)。

そうだとすると、ゴールドの総量やインフレ率を見ていても捕まりにくい。一方で、後から普通に遊ぶ人に割を食わせる形になっていたことは、提案広場の指摘のとおりだ。


AWS で組むならこう

図1 検知基盤の全体像

ゲーム側は架空のスタブで、実在の構成とは関係ない。流れは、ゲームサーバが出すイベントを Kinesis で受けて、2本に分けるというもの。

  • 即時: Managed Flink で、一斉の離席と着席のような「その場で分かる」形を拾う
  • 後から: Firehose で S3 に落とし、Glue で Iceberg に整えて、Athena で「同じ8人の反復」のような集計を回す

一番大事なのはテーブル設計

このカラムが無いと、後から一生問えないというものに★を付けた。

casino_events
  ├─ event_id
  ├─ world_id
  ├─ island_id    ★ 無いと島単位の分析ができない
  ├─ raid_id      ★ レイド1周期のID(島の負けの積み上げ開始〜レイド終了まで同じ値)
  ├─ char_id        全ワールドで一意
  ├─ seat_no        席の占有を再構成したいときに要る
  ├─ event_type   ★ sit / leave / spin / raid_start / raid_end / raid_payout
  ├─ bet_coin       実際に残高から減った額(レイド中の賭けは 0)
  ├─ payout_coin    実際に残高へ入った額(該当しない行は 0)
  ├─ ts             timestamp(3)(ミリ秒精度)
  └─ event_seq      同じミリ秒の中の順序

後から「島ごとに見たい」「同じレイドにいた8人を知りたい」と思っても、island_id と raid_id が記録されていなければ何も言えない。

何を検出できるかは、スキーマを決めた時点で決まる。

クエリ(Athena)

緑の行が要所。

同じ8人組が何回レイドにそろったか:

  WITH raid_members AS (
    SELECT world_id, island_id, raid_id,
+          array_join(array_sort(array_agg(DISTINCT char_id)), ',') AS member_set
    FROM casino_events
    WHERE event_type = 'raid_payout'
    GROUP BY world_id, island_id, raid_id
+   HAVING COUNT(DISTINCT char_id) = 8
  )
  SELECT member_set, COUNT(*) AS times
  FROM raid_members
  GROUP BY member_set
+ HAVING COUNT(*) >= 5          -- 仮置き。正常期間の分布から決める
  ORDER BY times DESC;

レイド1周期で、8人全員がプラスだった島:

  WITH per_char AS (
    SELECT world_id, island_id, raid_id, char_id,
+          SUM(COALESCE(payout_coin, 0) - COALESCE(bet_coin, 0)) AS net
    FROM casino_events
    WHERE event_type IN ('spin', 'raid_payout')
    GROUP BY 1, 2, 3, 4
  )
  SELECT world_id, island_id, raid_id,
         SUM(net)         AS island_net,
         stddev_samp(net) AS net_spread
  FROM per_char
  GROUP BY 1, 2, 3
  HAVING COUNT(*) = 8
+    AND count_if(net > 0) = 8
  ORDER BY net_spread ASC;

レイド開始の直後に8人がそろって離席し、5分以内にそろって座り直した島:

  WITH raid_start AS (
    SELECT world_id, island_id, raid_id, ts AS start_ts
    FROM casino_events
    WHERE event_type = 'raid_start'
  ),
  left_soon AS (                -- 開始から10秒以内に離席
    SELECT r.world_id, r.island_id, r.raid_id, e.char_id, e.ts AS leave_ts
    FROM raid_start r
    JOIN casino_events e
      ON  e.world_id  = r.world_id
      AND e.island_id = r.island_id
      AND e.event_type = 'leave'
+     AND e.ts BETWEEN r.start_ts AND r.start_ts + INTERVAL '10' SECOND
  ),
  sat_again AS (                -- 離席から5分以内にどこかへ着席
    SELECT DISTINCT l.world_id, l.island_id, l.raid_id, l.char_id
    FROM left_soon l
    JOIN casino_events e
      ON  e.world_id = l.world_id
      AND e.char_id  = l.char_id
      AND e.event_type = 'sit'
+     AND e.ts BETWEEN l.leave_ts AND l.leave_ts + INTERVAL '5' MINUTE
  )
  SELECT world_id, island_id, raid_id
  FROM sat_again
  GROUP BY 1, 2, 3
+ HAVING COUNT(DISTINCT char_id) = 8;

同じことを流れの中でやるなら、Flink で島ごとに状態とタイマーを持たせる。

図2 一斉の離席と着席を即時に拾う

実装の補足(窓の取り方・Iceberg を使う理由)
  • Flink の単純なセッション窓は、イベントが続く限り窓が伸びてしまい「最初と最後が10秒以内」を表せない。図2のように、キー付きの状態とタイマーで「開いた・集まった・来なかった」を自分で管理するほうが素直
  • Iceberg にしておくと、修正の前後でデータを比べるときに time travel で「修正前の時点のテーブル」をそのまま引ける。集計ロジックを直したときの遡及修正もしやすい

ここまで作れば検出できる。ただし、本当にここまで要ったのかは後で問い直す。


Snowflake なら丸投げできるか

2026年の Snowflake World Tour Tokyo で見て面白かったので取り上げる。関係者でも、案件でもない。

図3 Snowflake と組む

つなぎ方はこれだけではない(S3 からの Snowpipe、Kinesis を直接読む Openflow のコネクタ、S3 の Iceberg をそのまま読む形など)。ここでは、Snowflake 側に取り込み先のテーブルと認証・権限を用意したうえで、Firehose の配送先に Snowflake を指定する直接配送にした。

やりたいこと Snowflake の機能 逆天井に届くか
取り込み Snowpipe Streaming(Firehose から直接配送できる) 届く(配管として)
集計の自動更新 Dynamic Tables 届く(配管として)
時系列の異常 ANOMALY_DETECTION 生のイベントのままでは届かない
セグメントの異常 Top Insights 生のイベントのままでは届かない
誰と誰がそろったか 標準の機能は見当たらない 自分で SQL を書く

下の2つが届かないのは、見ているものが違うからだ。ANOMALY_DETECTION は時系列ごとの逸脱を見る。Top Insights は次元で切ったセグメントの差を説明する。どちらも「同じ8人が何度もそろう」という主体どうしの関係を直接は見ない。

逆に言えば、関係を表す特徴量(同じ顔ぶれの反復回数など)を自分で SQL にして時系列に直せば、ANOMALY_DETECTION に載せることはできる。配管と監視は任せられて、特徴量を作るところだけが手元に残る。

この10年で自動化された範囲と、されなかった範囲の境目が、ちょうど逆天井の位置にある。

Snowflake の仕様メモ(2026-09 時点・公式ドキュメントで確認)
  • ANOMALY_DETECTION: 2023-12 に GA。関数ではなくクラスとメソッド(CREATE SNOWFLAKE.ML.ANOMALY_DETECTION → !DETECT_ANOMALIES)。タイムスタンプ列が必須。予測区間の上下からの逸脱で判定する。複数系列に対応するが、公式の表現は "independent threads of events" で、系列は独立に扱われる。外生変数は1系列を説明する補助入力で、多変量の異常検知ではない。公式は「構造的な異常を検出できない」とは書いていない。系列どうしの関係を見るという記述がない、が正確な言い方
  • Top Insights: 決定木でセグメントに分けて、メトリックの差を説明する。分けるのは次元で、主体どうしの関係ではない
  • Dynamic Tables: 最小の TARGET_LAG は1分。リアルタイムとは言えない
  • Snowpipe Streaming: アーキテクチャが2系統あり、classic は廃止が告知済み。現行の high-performance は公称でテーブルあたり最大 20 GB/s、取り込みから検索可能まで最短5秒(構成とワークロードによる。Firehose 経由での保証値ではない)
  • Time Travel: 標準は1日で追加料金なし。90日は Enterprise 以上で、ストレージ料金がかかる
  • Alerts: スケジュールで定期的に評価する型に加えて、新しく入った行だけで評価する型(alert on new data)もある。後者は対象が1つのテーブルかビューに限られ、JOIN や CTE は使えない
  • グラフ分析: 公式ドキュメント上、グラフアルゴリズムを提供する標準機能は見当たらない(Task graph はワークフローの DAG、Data Lineage はメタデータで、どちらも別物)

記録さえあれば、DWH は要らなかった

ここまで基盤を組んでおいて何だが、2015年当時でも道具はそろっていた。

カジノの払い出しは、コインを増やす処理である以上、どこかに記録されていたはずだ。レイドごとに誰が受け取ったかが分かる粒度で残っていたなら、DB に SQL を1本投げるだけで答えは出た。

2015年の DB でも動く SQL

もちろん、払い出しの処理があることと、分析できる形で履歴が残っていることは別の話だ。残高だけ更新して明細を持たない実装でも、ゲームは動く。ここに書いたのは「その粒度の記録があれば」という条件つきの話になる。

2015年に「無かったもの / あったもの / 要らなかったもの」
無かった Iceberg(2017〜)/ Glue(2017)/ Athena(2016)/ Firehose(2015-10 GA)
あった Hadoop・Hive / Redshift(2013 GA)/ Kinesis(2013 GA)
要らなかった 上の全部(記録が残っていれば)

外から振り返ると、道具より指標の問題に見える

仮にプレイ時間や DAU を中心に見ていたなら、むしろ成功に見えたはずだ。

カジノで儲かる → カジノに人が張り付く → プレイ時間が伸びる → DAU が保たれる → 「人気コンテンツ」

こうした指標は、どれも「問題なし」と言う形になる。

では、経済そのものを測る考え方がなかったのかというと、そうではない。通貨がどこから入ってどこへ抜けるか(source / sink / flow)を測る考え方は、2014年の時点ですでにあった。ただ、今回確かめた当時の国内の公開資料には、それを日常の監視に落とし込む話は出てこなかった。

「考え方はあったが、公開資料では運用の話が見当たらない」の根拠

考え方はあった

  • Lehdonvirta & Castronova『Virtual Economies: Design and Analysis』(MIT Press, 2014)
  • GDC 2014「Economic Balancing and Improved Monetization Through Clever Sink Design」— 通貨の吸い込み先6種・アイテムの吸い込み先11種と、スプレッドシートでのシミュレーション手法
  • Trevor McCalmont「15 Metrics All Game Developers Should Know By Heart」(Gamasutra, 2015-07-09)— 15指標のうち3つが Source / Sink / Flow。「チャートが指数関数的に上振れすると、プレイヤーが通貨を持ちすぎて課金しなくなる」と通貨供給の健全性を論じている

確かめた国内の KPI 解説には出てこない

  • gloops 井澤正志「ソーシャルゲームのKPI分析」(2013-07)— DAU / MAU / 継続率 / 課金率 / ARPU と行動指標。経済の指標はない
  • Real Analytics「ソシャゲ分析講座」(2013-11)— 「DAU × 課金率 × ARPPU」が柱。経済の指標はない
  • 現在の GameAnalytics の定番22指標の一覧にも、インフレ率・通貨の流通量・資産の分布は入っていない(製品の機能としては、Resource Events で source / sink を記録できる)

どれもソーシャルゲーム向けの公開資料で、MMO 運営の非公開の監視までは分からない。

データエンジニアの仕事はどこにあるか

何を測るべきかを見つけるのは、分析する側の仕事だ。データエンジニアの仕事は、見つかった指標が後から測れる状態にしておくことで、こちらは遡って取り返せない。パイプラインの精緻さより、「後で何を問えるか」を決めるスキーマ設計のほうが効く。

実際、11年前のゲーム内経済を外から数字で組み直すことはできなかった。運営内部のデータは公開されていない。この記事の金額が近年の条件からの試算なのはそのためだ。

運営の対応について(速さ・修正の筋・判定のぶれなさ・後日談)

速さ: 1月9日に認知して、1月15日に修正。約1週間で、当時の MMO としては速い。

修正の筋: 閾値を上げて粘るのではなく、「負け額の累積」という、設計の意図と違う動機を生む入力そのものを消した。閾値を上げ続けるモグラたたきより、入力を消す修正のほうが筋がいい。

判定がぶれない: 10年分並べても基準が一貫している。

事件 判定 処分
逆天井(2015-01) 仕様の逆用 処罰なし・回収なし
永久レイド(2015-01) 不具合の利用 回収あり
スレイプニール(2015-16) 外部ツール 29件永久停止・書類送検5名(2016-10-13 警視庁)
クロスペンデュラム(2016-01) 不具合 約1700キャラ停止・600件巻き戻し(発見から修正まで1日)
キャラデリ金策(2019) 仕様 処罰なし・回数制限(月5回→1回)と時間の制限
ハッピーハート(2025) 仕様 Ver7.6 で店売り価格を 300G→10G

後日談: 1月15日の修正でレイドの発生確率を大きく上げたため、今度はコインが出やすくなり、連日満席で席の取り合いになった。1月22日に1階のレートを変えて追加で対応している。長い目で見ると、配布施策と重なってコインの価値が下がり、カジノは過疎化した。

修正の後にもコインの供給量を測っていれば、1週間で気づける場面だった。監視は事件の最中だけでなく、直した後にも効く。


10年経っても、ベンチマークは1つのまま

2015年の時点で、ゲーム内経済を常に監視していた運営はあった。EVE Online だ。

その年の EVE Vegas で CCP Games が出した追跡指標には、通貨の流入と流出の内訳、マネーサプライ、貨幣の流通速度、物価指数4本が並んでいる。この記事で「測れるようにしておきたい」と書いたものは、ほとんどそろっていた。

その体制は今も続いている。CCP は2025年に中央銀行のエコノミストを経済部門のトップに迎えた。そのプレスリリースは、EVE を「ゲーム内経済のベンチマーク」と紹介している。

公開情報で確かめた範囲では、10年経ってもベンチマークは1つのままだった。

一方で、ゲーム内経済の破綻は今も起きている。2023年の EverQuest、2024年の Last Epoch、2025年の Corepunk。どれも複製バグが原因で、監視していても発生そのものは止められない。止められないが、気づくまでの時間は縮められる。

基盤の部品は、今ならマネージドサービスでそろう。残る仕事は、何を記録して何を測るかを決めることだ。

EVE Online の経済監視と、近年の破綻事例

EVE Online / CCP Games

  • 2007年、実際の経済学者 Eyjólfur Guðmundsson を Lead Economist として雇用
  • 2009〜2011年 Quarterly Economic Newsletter(最大50ページ)→ 2016年から Monthly Economic Report(2026年8月号まで継続を確認)
  • EVE Vegas 2015(2015-11-05)の発表で挙がった追跡指標: ISK(ゲーム内通貨)の流入・流出の内訳、アクティブな口座の月初・月末の残高、マネーサプライの推移、貨幣の流通速度、連鎖ラスパイレス式の物価指数4本(消費財・鉱物・一次生産者・二次生産者)、生産額と破壊額、地域ごとの貿易収支

公開情報で EVE が突出して見える理由

  • Jagex(RuneScape)が経済学者を雇ったと公表したのは2021年
  • CCP が Head of Economy の職を置いたのは2025年3月(アイスランド中央銀行の Stefán Þórarinsson)
  • 経済設計の SaaS である Machinations の商用提供は2018年から

他社が内部で監視していた可能性までは否定できない。ここで言えるのは、公開されている範囲で10年続いているのは EVE だ、ということまで。

近年の破綻事例(いずれも複製バグ)

  • EverQuest(2023-11): プラチナの複製で、期間限定サーバーの通貨価値が消えた
  • Last Epoch(2024-03): アイテムの複製
  • Corepunk(2025-04): コインの複製で、マーケットを一時閉鎖

おわりに

DQX は、子どもが生まれるのを機にずいぶん前に引退しました。当時の運営が、それまで遊んだどの MMO と比べても「神運営」だったことは間違いありません。

一方の EVE Online は、ずっと遊んでみたかったゲームなので、経済をきちんと監視していると知って「だろうね」と思いました。細かいところは知らなくても、全体からにじみ出る「ちゃんとしている感じ」に惹かれるんだと思います。2026年7月に EVE を支える基盤「Carbon」をオープンソースにしたのも、さすがとしか言えません。

ゲーム開発のほうはいろいろやっていて、この記事に興味を持ってくれた方なら楽しめそうなものを準備中です。それはまた別の機会に。


参考資料

事件

レートの実測

Snowflake

近年の事例・Carbon

KPI・経済監視

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?