Thunderous
Learning

Forty-nine percent

In pass 23 I opened two loot chests, and both paid a Charm of Reach. That could have been luck. But the roster has six items, so two in a row is about a one-in-six event, and something that small is exactly what gets wav…

In pass 23 I opened two loot chests, and both paid a Charm of Reach. That could have been luck. But the roster has six items, so two in a row is about a one-in-six event, and something that small is exactly what gets waved away. Claude measured it instead.

Evenly stepped seeds

Every chest seeded its own System.Random with something like salt * (round + 1) + .... That gives one seed per room, stepping up by the same amount each time. .NET's generator turns evenly stepped seeds into nearly the same first draw. So over 60 rooms, the chest's item ran:

gre cha cha arm arm mac mac war war ...

Every item twice running, in roster order. 49% of chests repeated the previous room's item, against about 17% expected. And two different runs turned out to be the same sequence, shifted along.

Every rate test we had passed. Over 60 rooms each item came up about equally often, which is exactly what a rate test checks. The totals were fine and the order was a staircase. A test that counts things can't see a pattern in the order they come in.

The fix is SeededDraw.Mix(salt, round, runSeed): each input goes through a murmur-style scrambler before it becomes a seed, so neighbouring rooms get unrelated streams. The new tests ask the question in the shape of the bug. They don't ask "how often does each item drop", they ask "how often does the next room repeat this one". They were watched failing on the old seeding before the new one went in.

The older loot had it too

The chests were only hours old. The same seeding pattern was all over the older loot, and the numbers were worse, measured over 300 rooms:

  • An enemy's item drop ran on a four-room cycle. Body 0's item never matched the previous room's, and matched the one four rooms back 91% of the time: arm mac kop gre, round and round.
  • Coin alternated. A body that paid coin never paid it in the next room, and paid it two rooms later 84% of the time.
  • A gem was never followed by another gem on the same body within eight rooms.
  • The whetstone followed itself 51% of the time, where 25% was expected.
  • Every run dropped exactly the same things. The kill drops never took the run's seed at all. The new key drops passed the body's index where Mix wanted the run, so keys ignored the run too.

Fixing this changes every seeded drop there has ever been, so an old run replayed would drop different loot. That was my call, and I said yes. Every kill roll (coin, item, gem, key) and the whetstone now seed through a four-input Mix(salt, round, index, runSeed). Ten new tests ask those shape questions. On the old seeds nine went red, each naming its own lag.

The rooms themselves

The room decks were next: which enemy set you get, and how the room is dressed. They had the same flaw, plus a second one of their own. The no-repeat rule, when it drew the same entry twice, bumped it to the next entry, which quietly favoured neighbours. Dealt the way the game deals:

  • 91% of the enemy deck's moves were one step round the deck (+1 36%, -1 55%), and it never came back to an entry within three rooms.
  • The dressing alternated. The room two back matched 82-91% of the time.

Pick now seeds through Mix and draws the no-repeat from all the other entries equally, so none is favoured. Ten more tests, and nine of them went red on the old draw. The tenth covers a deck of three, and a deck of three can't show a step bias: with the current entry excluded, one step up and one step down are the only two choices. Every room order a run deals is new.

What I'm keeping from it

Rate tests passed for all of these, and they'd have kept passing forever. The loot was fair by count and patterned by order, and you can only feel order when you play. If a run had felt samey, I couldn't have said why. Two Charms of Reach was the loudest the pattern ever got.

The rule I'm taking: a random stream gets a shape test, not only a rate test. Ask how often the next draw matches this one, and how often one draw matches the draw N back. Then break the generator and watch the test catch it.

Still open

  • The round counter never resets between runs. RoomDirector's round is counted per session, not per run. That means only the first run of a session is dealt the authored opening room, and forcing a run's seed only replays it exactly if you're the same number of runs into the session. This is read from the code, not seen in play. The counter was left alone originally because loot took no run seed, and resetting it would have made every run drop the same loot. That reason is gone now, but the counter hasn't changed yet.
  • None of this has been judged in a wearing. The streams are measured and tested, but no pass has asked whether the loot feels less samey, and that's the claim that matters.