ZH version is available. Content is displayed in original English for accuracy.
Advertisement
Advertisement
⚡ Community Insights
Discussion Sentiment
50% Positive
Analyzed from 2136 words in the discussion.
Trending Topics
#seed#game#random#rng#same#seeds#should#card#different#library

Discussion (44 Comments)Read Original on HackerNews
This is the correct conclusion - game developers should consider gameplay-relevant random generators part of their gameplay code rather than platform code.
(Note this is roughly what slay the spire did, but if they were to use a 'master' RNG output as the source of these sub-seeds then these correlations would also not be a problem. With a custom implementation they could save the RNG state directly as opposed to hacking around calling the RNG X times on loading a save)
Standard library invocations - including random number generation - often break entirely when targeting wasm freestanding for instance, as in that case there is really very little "platform" to speak of.
If the stdLib changes and you need to use the same, then you're unfortunately going to be suck with porting the previous version into your own library. It's pretty forward thinking from the devs here, I would love to see my boss' face if I told him we need time to port some of the stdLib incase they update it in the future.
I had to check for my own curiosity, but it looks like the Random class has not been updated in 12 or so years. At least in the inital subset of framework to core.
https://github.com/microsoft/referencesource/commits/main/ms...
This is a pretty funny abbreviation since CRNG is sometimes "cryptographic random number generator", which would not be susceptible to this correlation. Albeit I think CSRNG is more common.
The game needs a RNG that's stable when seeded, for reproducible runs. I look for the same kind of qualities when doing generative art.
In comparison, a CSPRNG should be safe from oracle attacks, which is essentially the opposite goal.
The only difference is that if you don't know the seed it is computationally difficult to predict the next value given the previous ones. But that's not something any game dev is ever going to want to do (or waste time trying to do)
Why is this important? Feels like fixing what seems to be a non-issue lead to a bunch of real issues.
With a good RNG it should not be possible to predict future numbers based on past numbers so players cannot manipulate card rewards in their favour based on combat actions, right?
However, one of their design goals is that people playing on the same seed should have roughly the same game, it should feel "fair". Some things you probably want to be fairly random, for instance your card choices can depend on what cards you chose before. But it's also important that people choosing the exact same cards (and taking the same path, maybe?) should be offered the same options.
In STS1, the order of relics was fixed from the start as I recall. So if you skipped a shop, you'd get exactly the same relics in the next shop as you would have in the one you skipped. Good for seed fairness, but a little odd.
Imagine the game of two players having the same state X. While combat, one player would trigger a random action, the other doesn't. After the combat, both should still get the same randomized reward options. This wouldn't work with just a singular RNG.
This way, you can see how e.g. players of different skill level navigate the "same" run (same seed), without everything diverging completely on the very first (meaninglessly small) combat choice.
You can be safe from RNG manipulation while still suffering from RNG prediction. Players can't modify the events that are going to happen, but if they can predict them, it's still a problem.
The situation is like there's a bug in the blackjack table where, instead of shuffling the whole shoe together, each deck in it was shuffled on its own in the same way and then the identical decks are stacked together. Once you've seen 52 cards, you know the repeating pattern and can play with perfect or near-perfect knowledge of what's about to be dealt.
That requirement is what made this problem difficult for the devs to solve.
The issue is that knowing the offset of seeds helps predict outputs.
Instead of calling RNG(seed+hash(string)) 10x, make one RNG(seed) and call that 10 times to get random seeds for your 10 rngs. Now you have perfect determinism and no correlation.
It's also more robust than calling RNG 10 times since if you use the same algorithm to seed as for the RNG proper then you will get the same sequences in each instance, just offset.
>The way Slay the Spire allows you to save and resume runs is by storing the total number of times each RNG has been called, and then calling each RNG that many times (throwing away the result) whenever a save file is loaded.
Depending on what the game is like (I know nothing about it), that could make sense, even if it is inelegant.
This doesn't really have any impact on the gameplay, and isn't related to the correlation problem, it's just a constraint on the class of RNG algorithms in use, they need to be deterministic with recoverable state.
On the other hand, it's STUPENDOUSLY useful to have "default" random functionality in your core library, for the "just give me a random number" or "shuffle this array, I don't care how" users, who don't really care about the details. But if you do that: always seed it with some external entropy (current time or /dev/random or whatever), don't even allow users to seed it. That means you can improve it in the future, because users already can't ever rely on the sequence. If the users do want to rely on the sequence, they should have to specify the exact engine they want.
TL;DR: System.Random in C# should not ever have been seedable, big mistake.
To avoid regression I have some simple code examples I compile and execute, and I compare their output to "known good" versions.
I reached a point where I wanted to write a "sort array" routine and my immediate thought was to generate an array of 50 random numbers, sort them, and print them. But of course that wouldn't give me predictable output for my test-driver.
In the end I decided I'd do that when run interactively, but for testing purposes I'd just sort the characters in a string "The quick brown fox .." and while it isn't super-convincing it's enough to let me see regressions in my sorting function and/or array indexing runtime code.
(Generally, when you just press 'start game', you'll get a truly random seed, but then you can also put that seed in again to get the same RNG).
Minecraft does this too with world generation for example.
I hope the StS team is made aware of this and is able to make the earlier outcomes a bit more evenly spread, so that the distribution matches more closely with what people would intuit them to be.