勝率54%、均衡点は25%! しかもそのデータの5%が見えていなかった — タムナのボット難易度
感覚は証拠になりません。だから本番データを見にいきました。人間1+ボット3のソロ戦で、人間の勝率が54%。4人なら均衡点は25%のはずです。
記録日: 2026-08-01
最終更新日: 2026-08-21
はじまりは「なんか弱くない?」という感覚 💭
ソロのボット戦は、既定の難易度が medium でした。
それで十分だろうと思って置いた値です。
ところが、遊び込んでいる人にはどうも手応えが軽いのではないか、という感じがありました。
でも感じは感じです。仕様変更の根拠にはなりません。
なので、まず数字を取りにいきました。
データを見た:54% 対 25% 📊
本番データの監査結果はこうでした。
人間1+ボット3のソロ戦で、人間の勝率54%。
4人卓なら均衡点は25%です。
倍以上でした。
既定の medium が、遊び込んだ人に対して明確に弱いという事実がここで確定しました。
感覚は仮説で、監査結果が証拠です。 📌

ところが、そのデータの5%が見えていなかった ⚠️
集計を実装に落とす段になって、面倒な事実に気づきました。
games テーブルの参照ポリシーは is_room_member(room_id) です。
これは「いま」その部屋のメンバーとして残っているかを見ます。
対局が終わって部屋を出ると、あるいは部屋が再マッチングで使い回されると、過去の完了ゲームが隠れます。
実測で、ソロ戦174件のうち9件が現時点の基準では見えませんでした。
5%です。
弱いという結論自体は変わらない差ですが、気づかずに配線していたら「見えている分だけ」で判定するコードになっていました。

だから読む場所を変えた 🔑
回避策は、集計対象を別の場所から読むことでした。
game_participants のスナップショットは対局開始時点で固定されます。
あとから部屋を出ても、この記録は動きません。
そこを service-role で直接読むようにして、隠れる5%を拾い直しました。
データを信じる前に、そのデータがどう集まっているかを先に確かめる。 🧐
基準線は2勝にした 🎯
上げる条件は、過去のソロ戦で2勝以上です。
2勝未満は medium のまま据え置きます。
理由はコードのコメントにそのまま書いてあります。初回の人や初心者の離脱を防ぐためです。
最初の一戦から強いボットに当たる体験は、難易度調整ではなく門前払いになります。
早々に降りた対局は集計から外しました。数え方が甘いと基準線の意味が消えます。

選んだ人の選択は上書きしない 🙅
この判定が効くのは、難易度が未選択のときの既定値だけです。
クイックプレイでも部屋主のUIでも、明示的に選んだ値はそのまま尊重します。
low を選んだ人が勝ち続けても、勝手に high へは上がりません。
適応させたかったのは既定値であって、人の意思ではありませんでした。
新しい列もマイグレーションもなし 📦
実装は lib/bot-difficulty.ts の79行です。
テストも79行、ケースは6件書きました。
新しい列は足していません。マイグレーションも走らせていません。
既存テーブルを都度参照するだけで足ります。ユーザーごとの対局数は多くありません。
ボットの思考そのものを決める難易度定数にも、一切手を入れていません。
おわりに 🏁
この件で手元に残ったのは、二段構えの確認手順です。
感覚が出てきたら、まず数字を取る。
そして数字が出てきたら、その数字の集め方を疑う。
54%対25%だけ見て終わっていたら、実装は5%欠けた集計の上に乗っていました。
証拠の中身より先に、証拠の出どころを見る。次もそうします! 📈