Back to News
Advertisement
Advertisement

⚡ Community Insights

Discussion Sentiment

46% Positive

Analyzed from 2103 words in the discussion.

Trending Topics

#seed#game#rng#random#same#seeds#should#card#library#different

Discussion (43 Comments)Read Original on HackerNews

account42about 1 hour ago
> Implementing a PRNG within the codebase instead of calling the C# standard library has an additional advantage: seeds are guaranteed to be the same on all platforms. In Spire 1, seeds on the desktop version of the game were different from seeds on the mobile version of the game, because the standard library implementation of PRNG differed between platforms. It is also worth mentioning that the standard library implementation might change over time, which would break all past seeds.

This is the correct conclusion - game developers should consider gameplay-relevant random generators part of their gameplay code rather than platform code.

fc417fc802about 1 hour ago
More than just that, procgen as a whole requires an entirely different level of vigilance to avoid nondeterminism creeping in if the game requires it to be reproducible. None of the inputs to the procgen algorithm can be allowed to even so much as brush up against code you aren't actively exerting complete control over, and care is required to avoid inadvertently encountering any platform specific hardware quirks.
rcxdude34 minutes ago
It can also be a good idea to split the RNG into a tree for different areas (i.e. seed multiple RNGs from the main one), so that adjusting the generation for one aspect doesn't shift around everything else in a seed (especially in something like Minecraft where different parts of the world might be generated on different versions of the game).

(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)

maxbond30 minutes ago
What's burned me before is iterating over hash maps. B-tree maps (or hash maps that are guaranteed to iterate in insertion order, or any fixed order) are your friend.
godwinson__4-821 minutes ago
Sometimes it is useful to deal with a platform where such things are not even available, never mind platform dependent. Then see how quickly your code breaks.

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.

bilekasabout 1 hour ago
> It is also worth mentioning that the standard library implementation might change over time, which would break all past seeds.

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...

cevn5 minutes ago
I wonder if this can explain something happening to me. If I select "random" at character select, I had a run of 30 or 40 where I never received the Silent. Defect seem to come up more often than it should, and Ironclad less often.
FromTheFirstInabout 2 hours ago
This is such a great article- I’ve had so many runs where it’s felt like “why am I always getting this random card?” And now I’ll know! Thank you!
stdc105about 2 hours ago
Interestingly, StS2 got this problem because it was using C# System.Random in Godot, while the RNG class in GDScript (Godot Engine's own scripting language) is using PCG32 which should be free of this particular problem.
jszymborski36 minutes ago
> The phenomenon of "correlated RNG" (or "CRNG")

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.

Lalabadie26 minutes ago
The criteria for calling a RNG "cryptographically secure" are incompatible with the game design goals here.

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.

HansHamster8 minutes ago
I would expect all RNG algorithms to be deterministic and stable with their seed, but the cryptographically secure ones to have some additional properties like making it unfeasible to reverse the seed from the output, having a very long period or strong guarantees on the distribution of the output. It's just that using a 'secure' algorithm is often overkill for a game when you don't really need those extra guarantees.
gpm13 minutes ago
CSPRNGs are absolutely seedable deterministic functions that will result in entirety reprodible runs.

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)

iliveinberlinabout 1 hour ago
This is also the cause of the thing in Minecraft where you find surface clay, move X blocks over, and dig straight down into diamonds.
handoflixue44 minutes ago
"Appendix: How?" is a neat walkthrough of discovering this by trying to find a specific seed, and learning that the correlated randomness made the outcome he was searching for vanishingly rare.
xdertzabout 2 hours ago
> the game used several distinct pseudorandom number generators, to prevent e.g. randomness within a combat from influencing future card rewards.

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?

vintermannabout 2 hours ago
I don't think it's deliberate RNG manipulation they worry about. It's a single player (or coop) game after all.

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.

bulbarabout 2 hours ago
For a singular seed, they wanted the resulting run to be stable in the sense that small deviations in decision making does not result in a vastly different result (as far as random events are concerned)

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.

myrmidonabout 1 hour ago
This is exactly it.

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.

rcxdude19 minutes ago
Some degree of stability in seeds is desirable, partly because of the compatitive elements (multiple players playing the same seed should have roughly the same game), but also when updating the game is means that if they tweak one area the rest of the seed will stay roughly the same. (Maybe less important for games with short runs compared to sandbox games like Minecraft where the world might be generated by very different versions if the game and you don't want blatent seams when it happens)
hannasanarion30 minutes ago
You're confusing RNG manipulation (really clearly bad, basically cheating by removing the randomness from the game) from RNG prediction (less bad but still unfun, being able to predict future random states).

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.

eigabout 2 hours ago
The game stores and allows you to see the RNG seed that controls the run's events and layout. The developers want players to be able to share seeds that produce interesting runs.

That requirement is what made this problem difficult for the devs to solve.

margalabargalaabout 1 hour ago
This shouldn't actually be difficult to solve though.

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.

AlotOfReading1 minute ago
That's assuming the game initialization order is deterministic. Doing RNG(hash(seed + hash(string))) for each generator accomplishes the same thing without that assumption.
Arcorannabout 1 hour ago
My first solution was RNG(hash(seed.toString() + string)), which would get rid of the correlation while still being deterministic based on the seed.

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.

torgoguysabout 2 hours ago
Because they appear to have a curious way of doing their saves. From the article:

>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.

hannasanarion27 minutes ago
That's just to restore the internal state when you reload your save file, so that, for instnace, if you save and quit while looking at a set of card rewards, but haven't made your choice yet, when you reload, you will see the same set of rewards (you can't just reload your save to reroll).

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.

iNicabout 2 hours ago
For things like daily runs / seeded runs part of the fun is getting the same card rewards.
bzaxabout 2 hours ago
You could observe future random numbers by taking combat actions, and then reset to the start of the fight and play a line which consumes fewer random numbers in order to manipulate your card rewards. Maybe you could generate the card reward at the start of the fight, but what if they play a card which impacts the card reward, e.g. by creating an extra card reward.
yccs27about 2 hours ago
I guess it's mainly a limit to savescumming.
OskarSabout 2 hours ago
I've always thought that random number generators are one of the best examples of Hyrum's law ("all observable behaviours are part of your API"): once you release a random number generators that either uses a default seed or allows you to seed it, you can't ever change it, it's a huge breach of backwards compatibility. Imagine if you did a Minecraft style game that relied on the behaviour of some PRNG, and then you changed the implementation? The entire game will break. That's why GNU libc still uses a terrible LCG for rand() despite the fact that much better generators exist: they can't ever fix it, because srand() exists and people rely on it.

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.

stevekemp33 minutes ago
I had a similar realization recently; I was writing a compiler so I implemented a "random" function as part of the runtime.

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.

FromTheFirstInabout 2 hours ago
If you’ve played the game it makes sense to have the seed be settable and shareable. In Slay the Spire it can be exciting to have an outrageously unlikely starting state or early option, and in order for players to share this with each other the seed has to be user controlled. It’s a big part of what gives the game its community!
account42about 1 hour ago
GP isn't saying you should never have seedable random generators, just that they should not be part of the standard library because then the API promise is no longer that you get random numbers but that you get a very specific sequence of numbers which fixes the implementation as part of the API contract.
haeseongabout 2 hours ago
That split is exactly what .NET 6 did. The parameterless Random switched to xoshiro256*, but new Random(seed) stays pinned to the old Numerical Recipes subtractive generator so historical seeds still reproduce, and that legacy generator is the affine one whose first output is linear in abs(seed), which is the whole root cause of this bug.
0xdecryptabout 1 hour ago
Really interesting read. The fact that Rebound is literally impossible to get is hilarious and completely unexpected.
abstractcontrolabout 1 hour ago
Why don't they just pass the time into the RNG in order to randomize it instead of using fixed seeds?
rcxdude32 minutes ago
It's a big thing for competition. In a procedural game there's a lot of variation between seeds so the only 'fair' way is to start with the same seed.

(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).

ixwtabout 1 hour ago
People often want to share their seeds so that players can play the same game they did. If there was an interesting series of results for example, which gave you a good set of cards.

Minecraft does this too with world generation for example.

wbobeirneabout 1 hour ago
Being able to share and replay seeds is a big part of the StS community.
Advertisement
cbondurantabout 2 hours ago
The trash heap event gave me the same relic the first 3 times in a row that I got it before it gave me anything else. I wonder that's another example of this correlation?

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.

FromTheFirstInabout 2 hours ago
It always gives me Clash :(
d357r0y3rabout 1 hour ago
I feel vindicated. I knew this game was bullshit and it couldn't possibly have been a skill issue.