How to create reproducible probability in map generation?

So for my game, I'm using perlin noise to generate regions of my map (water/land, forest/grass) but I'd also like to create some probability based generation too. For instance:

``````if(nextInt(10) > 2 && tile.adjacentTo(Type.WATER))
tile.setType(Type.SAND);
``````

This works fine, and is even reproduceable (based on a common seed) if the nextInt() calls are always in the same order. The issue is that in my game, the world is generated on demand, based on the player's location. This means, that if I explore the map differently, and the chunks of the map are generated in a different order, the randomness is no longer consistent.

How can I get this sort of randomness to be consistent, independent of call order?

-
How about setting the seed for each position? You can combine numbers to create a seed for your random function. – William Mariager Jun 24 '12 at 20:26

This is simple, create a `new Random()` object for each chunk, using that chunk's position as the seed.

If you think of `Random` as a really (really) long series of numbers, the seed is just where to start iterating through. So, if you always set the seed before generating the chunk, you'll always get the same integers, in the same order.

EDIT

But how do you ensure that each chunk gets a unique seed?

It's not too critical to get a unique seed for each chunk, unless you want to try to guarantee that each chunk is unique from the last. I say try because even if each seed is unique it doesn't necessarily guarantee the random numbers you get will be different. If your world has a maximum size, you can get a unique `long` from each position in the world with a method like this:

``````public long positionToLong(int x, int y, int z, int maxY, int maxZ) {
return (x * (MaxZ * maxY)) + (y * maxZ) + z;
}
``````

If it doesn't have a maximum size, you can use some algorithm like:

``````public long positionToLong(int x, int y, int z) {
return x + y + z + (x*x) + (y*y) + (z*z);
}
``````

Then you can do additional things to strive for unique seeds, like add the distance from the center of the world.

I should mention how I do this in my game. Since I'm generating terrain shape along with terrain type, I wanted to ensure that I'd have continuity between chunks. I did that by using a single world seed. Then the Perlin noise function takes the absolute world position as an input. This makes the transition between chunks seemless, which will not be the case if you're randomly generating each chunk distinct from the others.

-
But how do you ensure that each chunk gets a unique seed? Or does that even matter? – Nick Badal Jun 25 '12 at 3:31
@nickbadal "This is simple, create a new Random() object for each chunk, using that chunk's position as the seed. " Two chunks cannot have the same position, so their seed will be unique. – doppelgreener Jun 25 '12 at 4:04
@JonathanHobbs That's true, but it has to be converted into a single number in some way. I'm blanking on a way to do that for an arbitrary position, but I added a method for when the world has a maximum. The second would fail fairly often I think, for example `0:0:1`, `1:0:0` and `0:1:0` – Byte56 Jun 25 '12 at 4:08
@Byte56 I guess so. You also can't just do simple addition because for any set of three coordinates, any combination will produce the same seed (edit: which it seems you've noticed!) – doppelgreener Jun 25 '12 at 4:15
Well, if the OP only needs two numbers (3D is never mentioned) there's this option for generating unique seeds based off two positions: stackoverflow.com/questions/919612/… – Byte56 Jun 25 '12 at 4:25

I'm not sure if there's any commonly practiced method of accomplishing this, but here's what I usually do for things like this:

Since you are already using a Perlin noise function, you can simply use Perlin noise to get the seed for each chunk. Whenever you need to generate a new chunk, simply feed that chunk's coordinates into the Perlin function, and use the returned value as the seed for that chunk. This means you'll need to reseed your random number generator every time a chunk is generated, or you can simply create a new instance of the generator if it's a class.

Essentially, whenever I need to generate consistent data in arbitrary positions, I use a Perlin noise function because it's reasonably fast and consistent.

-