morgan clement
morgan clement

Reputation: 11

Generating random number from multiple seeds

Recently i have been messing with perlin noise in C# to generate heigt map. For my project i need a way to efficiently generate a pseudo random value for a specific coordinate in a seeded-map. The easy solution would be to generate a random value array the size or the map, but in my case i think it would be better have a method to get the value.

What i want to archieve would look like this :


int GetRandomValue(int xCoordinate, int yCoordinate, int mapSeed) {
    //do stuff
    return randomValue;
}

The method should then return a pseudoRandom value, but having the same inputs in parameters should always give the same random result.

I tried using XOR operations on the coordinates ans the seed to generate a "final" seed that would then generate the random number, it worked well but i quickly ran into a problem, because the result of an XOR operation is the same no matter the order of the inputs, the random output for the coordinates 1;0 for the map x would be the same as the result for the coordinates 0;1, and i cannot seem to find a way to generate a seed (int) from 3 ints without having this issue.

Upvotes: 1

Views: 442

Answers (2)

Leo
Leo

Reputation: 1303

Essentially what you want is a hash or compression function. There are an infinite number of such functions, with varying degrees of speed and quality.

Let's call these functions f(a, b, c) = d.

Some simple examples of functions that take 3 integers and produce one integer are.

These functions all work, and they're reasonably fast, but frankly the quality is awful. We could go the other extreme and use a cryptographic hash, but that would probably be too slow to evaluate for each position of a map in a performant way.

So we'll need to do a little work and find something balanced.

Here's an example function that is of decent quality for deterministic/seeded maps. It takes three 32-bit integers, and outputs a single 32-bit integer, as the question asked.

If needed; it is also possible to increase/decrease the number of inputs and outputs, or turn the values from 32-bit integers to 64-bit ones.

Another knob that can be adjusted is the speed/quality balance. You might notice a repeated section in the middle of the code. You can duplicate that part more for more mixing, or cut some of the lines for less mixing but faster execution.

Here's the C code.

uint32_t rotl32(uint32_t n, uint8_t k) {
  uint32_t a = n << k;
  uint32_t b = n >> (32 - k);
  return a | b;
}

uint32_t three_input_random(uint32_t x, uint32_t y, uint32_t z) {
  uint32_t a = x;
  uint32_t b = y;
  uint32_t c = z;

  b ^= rotl32(a + c, 7);
  c ^= rotl32(b + a, 9);
  a ^= rotl32(c + b, 18);
  b ^= rotl32(a + c, 7);
  c ^= rotl32(b + a, 9);
  a ^= rotl32(c + b, 18);
  b ^= rotl32(a + c, 7);
  c ^= rotl32(b + a, 9);
  a ^= rotl32(c + b, 18);

  return a + b + c + x + y + z;
}

There are a lot of different ways to construct functions like this, and some decent tooling to evaluate the speed and quality of them. Some keywords to research are bit-mixers, compression functions, avalanche diagrams etc.

Here is the avalanche diagram for this function. The mixing is probably good enough to use with a 2D height-map.

enter image description here

Upvotes: 1

MikeP
MikeP

Reputation: 20

Maybe this would work for you:

int GenerateSeed(int value1, int value2, int value3)
{
    // Concatenate the three values into a string
    string concatenatedValues = value1.ToString() + value2.ToString() + value3.ToString();

    // Compute the hash of the concatenated string
    using (var sha256 = SHA256.Create())
    {
        byte[] hashBytes = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(concatenatedValues));
        int seed = BitConverter.ToInt32(hashBytes, 0);
        return seed;
    }
}

Upvotes: 0

Related Questions