世界を動かす技術を、日本語で。

「Slay the Spire 2」における相関ランダム性

2026年6月16日原文(tck.mn)

概要

Slay the Spire 2 のシングルプレイでは、複数の RNG(乱数生成器) が想定外に相関している。 この相関により、NeowのBonesなどのリリックや初戦のドロップ確率などに 大きな偏り が生じる。 C#のSystem.Random の特性が主な原因で、同じシードから異なるRNGが「似た」出力を生成。 プレイヤーが意図せず 将来のランダムイベントを予測できる 現象が発生。 この問題は Slay the Spire 1 でも既知であり、2でも形を変えて再現している。

Slay the Spire 2におけるRNGの相関現象

  • 複数の独立したRNG を用いることでイベント間の影響を避ける設計
    • 例:戦闘中のランダム性がカード報酬に影響しないようにする意図
  • 各RNGは seed + hash(識別子) で初期化
    • 例:Rng UpFront = new Rng(seed + hash("up_front"))
  • しかし C#のSystem.Random のアルゴリズムが「ほぼ線形」なため
    • シード差が既知の場合、出力の差も予測しやすい
  • これにより 異なるRNG同士で出力が“似る”
    • 1つのRNGの出力結果から他のRNGの出力範囲を狭められる

Neow's Bonesの呪い偏り

  • UnderdocksでNeow's Bonesを選ぶと 約54%の確率でDebt の呪いが出現
    • ClumsyやGuiltyなどの軽い呪いはほぼ出ない
  • Overgrowthでは Writheが約74%、Shameが約19%と極端な偏り
  • New LeafやKaleidoscope が同時出現すると相関が壊れるが、通常は強いバイアスが残る
  • プレイヤーの体感「Debtばかり出る」は 実際に統計的裏付けがある

他のNeowリリックにも影響

  • Large Capsule:Underdocksでは出現自体が1.65%と低確率、出た場合は レアリックの出現率が高い
  • Small Capsule:呪いリリックの有無やActで 出現リックのレアリティ分布が大きく変動
  • Leafy Poultice、Hefty Tablet:変化カードや選択カードも 偏ったプールから選ばれる
    • 例:Hefty TabletはUnderdocksでJuggernautが7割超

初戦のポーション・カード・ターゲットにも相関

  • Underdocks初戦の ポーションドロップ率76%、Overgrowthでは 4%
  • Lightning Orbのターゲットも 左側が75% など、呪いリリックによって更に細かく予測可能
  • イベントや戦闘でのランダム挙動が 事前に予測しやすくなる

問題の本質と今後の影響

  • System.Randomの線形性 が主な原因
    • シード差が既知であれば、どのRNGも「似た」乱数列を生成
  • プレイヤーは 過去のランダム結果から将来の結果を推測可能
  • Slay the Spire 1 でも同様の問題があり、2でも根本的な解消には至っていない
  • 影響範囲は広く、 カジュアルプレイヤーにも実害 が及ぶ
  • 今後のパッチや設計見直しで より独立性の高いRNG実装 が求められる

まとめ

  • CRNG(Correlated RNG)問題 はSlay the Spire 2のリプレイ性やゲームバランスに影響
  • 現在のバージョン(v0.107.0)では 一部リリックやイベントの出現率が極端に偏る
  • コミュニティでも体感的な「偏り」が議論されていたが、 実際に技術的裏付けが判明
  • 今後のアップデートで RNGの独立性強化 が期待される

Hackerたちの意見

面白いことに、StS2はGodotでC#のSystem.Randomを使ってたからこの問題が起きたんだ。GDScript(Godotエンジンのスクリプト言語)のRNGクラスはPCG32を使ってて、この特定の問題からは解放されてるはずだよ。

この記事、めっちゃいいね!「なんでいつもこのランダムカードが出るんだ?」って思うことが多かったけど、これで理由が分かる!ありがとう!

これって、Minecraftで表面の粘土を見つけて、Xブロック移動してから真下に掘るとダイヤモンドが出てくる現象の原因でもあるよね。

コードベース内でPRNGを実装することで、C#の標準ライブラリを呼び出す代わりに、もう一つの利点があるんだ。それは、すべてのプラットフォームでシードが同じになることが保証されること。Spire 1では、デスクトップ版のシードがモバイル版のシードと異なってたのは、PRNGの標準ライブラリの実装がプラットフォームによって違ったから。標準ライブラリの実装は時間とともに変わる可能性があることも言及しておく価値があるね。そうなると、過去のシードが壊れちゃう。これが正しい結論だと思う。ゲーム開発者は、ゲームプレイに関連するランダム生成器をプラットフォームコードではなく、ゲームプレイコードの一部として考えるべきだね。

それだけじゃなくて、プロシージャル生成全体は、ゲームが再現可能である必要がある場合、非決定性が入り込まないようにするために、まったく別のレベルの注意が必要なんだ。プロシージャル生成アルゴリズムへの入力は、完全にコントロールしていないコードに触れることすら許されないし、プラットフォーム特有のハードウェアの癖に無意識に遭遇しないように気をつける必要があるよ。

標準ライブラリの実装は時間とともに変わる可能性があることも言及しておく価値があるね。もしstdLibが変わって、同じものを使う必要があるなら、残念ながら前のバージョンを自分のライブラリに移植する羽目になるよ。ここでの開発者たちはかなり先を見越してるね。もし「将来的にstdLibが更新されるかもしれないから、移植する時間が必要だ」って言ったら、上司の顔が見たいな。自分の好奇心で確認したけど、Randomクラスは12年くらい更新されてないみたい。少なくともフレームワークの初期部分からコアにかけてはね。

時々、そういう機能が全くないプラットフォームで作業するのが役立つこともあるよね。プラットフォーム依存なんて関係なしに。そしたら、自分のコードがどれだけ早く壊れるか見てみて。例えば、wasmのフリースタンディングをターゲットにした場合、標準ライブラリの呼び出し、特に乱数生成なんかは、ほとんど完全に壊れちゃうことが多いから、実際には「プラットフォーム」と呼べるものがほとんどないんだよね。

「相関RNG」(CRNG)の現象は面白い略語だね。CRNGは時々「暗号的ランダム数生成器」を指すことがあって、これはこの相関には影響されないんだ。ただ、CSRNGの方が一般的だと思う。

RNGを「暗号的に安全」と呼ぶ基準は、ここでのゲームデザインの目標とは相容れないんだ。ゲームには、再現可能なプレイのためにシードが安定したRNGが必要なんだよ。生成アートをやるときも、同じような特性を求めてる。対照的に、CSPRNGはオラクル攻撃から安全であるべきで、これは基本的に逆の目標だね。

これが私に起こっていることの説明になるのかな。キャラクター選択で「ランダム」を選ぶと、30回か40回連続でサイレントが出なかったことがあるんだ。デフェクトが出る頻度が高すぎる気がするし、アイアンクラッドはあまり出ない。

この記事と、オリジナルのSlay the Spireでの勝てないシードの発見を組み合わせると…「RNG地獄」みたいなものの存在をずっと考えてたんだ。ゲームが時間を乱数シードとして使って、ハッシュ関数やゲームメカニクスの quirks のせいで、例えば4日間ずっと勝てない状態になることがあるんじゃないかって。(時々、自分がその中にいる気がする!)[0] https://oohbleh.github.io/losing-seed/

記事全体を読む時間がなかったけど、HackerNewsを読んでST2をプレイしている人たちのクロスセクションが見れて嬉しい。STS1とSTS2は私のお気に入りのゲームで、ここでこれを見かけてすごく笑顔になったよ。シェアしてくれてありがとう。

Hacker Newsで議論の続きを見る