A seed looks like one die. It's thirteen. StS1 derives every random decision in a run from a single number, but it doesn't roll them all on one stream — it splits the work across named streams so that shuffling your deck doesn't shift which event appears next floor.

Thirteen streams, thirteen owners

Exactly 13 streams exist. Twelve are created directly from the run seed, each starting at counter 0; the map stream is created once per act, with an act-specific offset. Each owns one slice:

StreamOwns
monsterRngencounter table rolls, boss list shuffle
eventRng? room substitution, event/shrine selection, event outcomes
merchantRngshop stock tier, price jitter, sale card
cardRngcard reward rarity rolls, colorless rarity, curse picks
treasureRngchest size/rewards, gold amounts, elite relic tier
relicRngrelic tier rolls, relic pool shuffles
potionRngdrop chance, potion rarity, key pick
monsterHpRngHP range rolls (reseeded per floor)
aiRngmonster AI decisions (reseeded per floor)
shuffleRngdraw pile shuffles (reseeded per floor)
cardRandomRngtruly-random card generation (reseeded per floor)
miscRngboss gold variance, scene randomization (reseeded per floor)
mapRngmap paths + room distribution

Five of them (monsterHp, ai, shuffle, cardRandom, misc) reseed every floor transition to seed + floorNum — in-combat churn can't drift the strategic layer. The PRNG underneath is libGDX's RandomXS128 (xorshift128+), and saves persist each stream as a (seed, counter) pair: reload and the game fast-forwards counters to replay state exactly.

What knowing the seed buys (and doesn't)

With the seed you can precompute the full map of every act — path layout, room distribution — before picking a starting bonus, plus encounter order, event sequence, and shop stock. What you cannot precompute is anything downstream of decisions: card rewards consume cardRng only when you take them, shops only when visited. Seed knowledge is a map and a schedule, not a playthrough.

The encoding itself: base-34 over the alphabet 0123456789ABCDEFGHIJKLMNPQRSTUVWXYZ — letter O omitted, typed O normalizes to 0 — with an unoffensive-seed filter that rerolls any string the bad-word checker flags.

The act-transition card-RNG clamp quirk

Crossing into a new act, dungeonTransitionSetup() fast-forwards cardRng into fixed bands:

if (cardRng.counter > 0 && cardRng.counter < 250) cardRng.setCounter(250);
else if (counter > 250 && < 500) setCounter(500);
else if (counter > 500 && < 750) setCounter(750);

Quoted verbatim from the extraction. Read as design: however many card rewards you took in act N — heavy drafting or none — act N+1 begins with the card-RNG counter pushed past the nearest 250 boundary. Card luck doesn't carry cleanly across acts, and two players who drafted wildly differently converge on comparable reward streams next act. The bands cap at 750; a counter already beyond stays put.

Neow's untouchable stream

Neow's blessing roll constructs its own generator on the spot: rng = new Random(Settings.seed) inside NeowEvent.blessing() — a fresh stream from the raw seed, shared with nothing. Your Neow offer is fully determined by the seed alone, independent of everything before and after. That's why opening blessings can be tabulated exactly, and why the offer shape is stable: 19 reward types across 4 offer categories with 5 drawbacks, the boss-relic option gated alone in category 3. The screen itself lives in StS2 too — Neow still sells power for drawbacks there, under a different RNG regime entirely.

StS2's registry diverges

The sequel threw out the fixed 13-stream board. Its architecture:

  • Run streams (shared): UpFront generates everything at run start — monsters, events, relics offered; Shuffle handles draw pile; UnknownMapPoint rolls unknown map points; TreasureRoomRelics arbitrates contested loot; Combat* streams split in-combat randomness five ways; MonsterAi, Niche, CombatOrbs fill the rest.
  • Player streams (per player): Rewards (cards, potions, relic rarity), Shops, Transformations.

Two structural differences from StS1. First, UpFront moves the biggest decisions earlier: knowing a StS2 seed reveals the entire encounter and event schedule up front, where StS1's streams burn down as you play. Second, reward dice are personal — each player's Rewards stream seeds independently in co-op, so your draft isn't perturbed by anyone else's pickups. Seeds hash through a djb2 variant (GetDeterministicHashCode) rather than StS1's base-34 parse, and named streams derive as runSeed + hash(streamName). Odds state is data now too: card-rarity pity starts at −0.05 offset, potion chance at 0.4, both serialized alongside the counters.

Honest variance: designers spent randomness where listed

Both registries read like budgets. StS1 quarantined cosmetic noise onto an unseeded global (MathUtils.random: shop chatter, chest-opening angles, VFX — explicitly outside the deterministic streams), reseeded combat churn per floor, and clamped the card stream at act borders. StS2 spent its registry on fairness instead: per-player reward streams, upfront schedule generation, dedicated arbitration for contested treasure. Neither game hides a die under the table — the variance lives exactly where these files say it does. Where it matters most is ascension, whose scaling sits on top of both registries (ascension scaling); tools that exploit seed determinism live in the site's tool set (see tools).