Reversing a procedural generation

Started by
30 comments, last by Aardvajk 10 years, 1 month ago

Procedurally generated data has a random seed and random values generated by the process. Here is an example of what I mean:

You have a row of windows you want to either be broken or unbroken. You set you seed and loop through the "windows" randomly assigning "broken" or "unbroken" to each according to the output of the random number generator.

This is simple and straight forward......

What if I wanted to do that in reverse? If I started with all the windows "unbroken" and I throw rocks randomly and broke a few of them. I would like to reverse the process until I get THE random seed that would result in generating the windows into that state.

I'm sure it CAN be done, but how?

This process could be very useful in games that have a huge amount of "object states". Like a planet with mineral deposits. If the player collects a certain amount of them, it would be silly for the player to be able to come back to the planet and get those deposits again. And saving the state of each mineral deposit on every planet in every solar system would be crazy!

Advertisement

I don't see what your last paragraph has to do with the rest of the post. If the player collects a certain amount of mineral deposits, you better write that down somewhere so the next time you generate this planet you can remove the appropriate amount of mineral resources. The information of whatever the player has done to the environment can't be magically folded into a new seed for the PRNG, in particular because the seed is probably not long enough to encode all the possible things the player might have done to the planet.

To put it another way: If my building has N windows, then I need N bits of information to store whether or not each one is broken. If I have a 32 bit random seed, I can generate 2^32 unique sequences. If I have 33 windows on my building, it's impossible to generate every possible combination (there are 2^33).

If I have a random seed of 12345 and with the process, the windows generate:

1,0,1,1,0,0,0,0

where 1=="broken" and 0=="unbroken"

Then every time I go to generate the windows with seed 12345 it will look exactly the same.

If I break a window:

1,0,1,1,0,1,0,0 then the seed 12345 won't generate this result.

There IS some seed that will generate 1,0,1,1,0,1,0,0..... I want to go from the result to the seed. It's not magic, it's logic I am not fully aware of.

It could probably be done. There are only a few problems wink.png :

  1. The seed has to be long enough to contain all the possible combinations, and that could be pretty long depending on the situation, as said previously.
  2. The code generating the environment needs to be known. This won't be a problem if you program it yourself for your game.
  3. You would probably need to do a brute-force "attack" to get the seed that would be used to generate that environment. This won't be a problem for small things, but for large chunks of data, this will take a lot of time.

The seed, as stated before, needs to be long enough to hold the data. Technically, you could make it as space-efficient as possible. But that few bytes of data really don't matter. It would be nice to have them, but having a readable format which is fast to read and write (see point three) is more important.

I'm new here but I know my way around procedural generation.

Going from a seed to a world is a one way operation only, as explained osmanb. Your example with windows is flawed, because in it, your seed is larger than all the possible states of the windows. If you're generating a whole world / universe / whatever out of a 32 bit (or 64 / 128 .. doesn't matter) seed, things won't work in reverse.

The point you seem to be missing is that a seed, whatever clever procedural worldgen you've written doesn't and can't store all possible states of your world. Otherwise the seed would need to be as big as the sum of all the possible combinations/states of this world (as it is in your example). In other words, it would be a savegame, not a seed.

The first poster told you the correct way to do things and that's also why savegames of procedurally generated games (at least those in which the terrain can be altered) only goes bigger as time goes on. They regenerate the fresh world out of the seed, and then add the changes made by the player. And it's usually not as time consuming or memory wasting as you seem to think.

This process could be very useful in games that have a huge amount of "object states". Like a planet with mineral deposits. If the player collects a certain amount of them, it would be silly for the player to be able to come back to the planet and get those deposits again. And saving the state of each mineral deposit on every planet in every solar system would be crazy!

What if there are twenty thousand mineral deposits on a single planet?


[save seed]
[save 1 bit for each deposit]

Total size: 20,000 bits = 2.5kb - less than a single 32x32 bitmap.


But what if you need more details, what if you need alot more information, for an infinite number of planets?
Sadly, there is no magic compression algorithm. sad.png Videogames have to work with their limitations.

Procedural generation works awesome for generation, but any changes have to be saved using conventional means (including normal compression methods) and re-applied to the re-generated result.

This is a SUPER SIMPLE random number generator:


int RNSeed=12345;
int randomnum(void){
	RNSeed=abs(RNSeed*8786545643)%987654321;
	return RNSeed;
}

With this initial seed, the first time it's called it results in 770159010, and the second time it results in 808599291.

This will happen EVERY time I start with 12345. The problem with reversing the calculation is the modulus of 987654321. Any seed value greater than that number will be some value that can't be found and therefore the previous seed us unknown. The solution would involve a random number generator that can be reversed-- I don't know how to make that (or if it can be made at all).

All of you are either think I don't know how random generators work or you are missing my point entirely.

This is a SUPER SIMPLE random number generator:

int RNSeed=12345;
int randomnum(void){
	RNSeed=abs(RNSeed*8786545643)%987654321;
	return RNSeed;
}

With this initial seed, the first time it's called it results in 770159010, and the second time it results in 808599291.
This will happen EVERY time I start with 12345. The problem with reversing the calculation is the modulus of 987654321. Any seed value greater than that number will be some value that can't be found and therefore the previous seed us unknown. The solution would involve a random number generator that can be reversed-- I don't know how to make that (or if it can be made at all).

All of you are either think I don't know how random generators work or you are missing my point entirely.


They're trying to explain to you that what you ask is impossible for anything non-trivial. Find another way.

Edit: to further clarify, pseudo-random generators CAN be reverse engineered. That is part of what people do when attempting to crack cryptographic schemes. If you can reverse-engineer the generator you can crack the encryption. It's a very difficult task. Some classes of PRNGs are much easier than others. And the greater the period, the more difficult it can be. What you are asking for is to reverse-engineer a PRNG of such an enormous period (for any non-trivial game state) that you could literally burn billions of hours of computing time trying to reverse it.

I understand what you are trying to achieve. If you COULD somehow magically come up with the seed required to represent any given game state, then you can reduce the workload of saving the world. However, the act of coming up with that seed would be such a Herculean effort of engineering and time, that it's just not feasible. You CERTAINLY won't be saving yourself any time; quite the opposite, actually.

You have 2 windows and a 1 bit seed. 0 is both windows broken, 1 is both windows intact. You want a seed that leads to only 1 broken window... there isn't one. Now think larger scale and you'll understand why you can't do what you want to do.

This topic is closed to new replies.

Advertisement