Kartik Chugh ヅ - 1 year ago 36

Java Question

**Edit: Removed mentioning of hashCode()**

I'm working on a seed-based terrain generator so that if a user enters a specific String into the text field, it will produce the same world every time. The way I was planning to do this was to use an algorithm that produces a 19-digit-long long (pun intended). The program will then utilize value of certain indices, or a combination of values of certain indicies, and other computations to set certain aspects of the world, like biomes, amplitude, and variance. In essence, whenever the same seed is inputted, the same internal 19-digit long will be produced by the algorithm, and by consequence, the same world.

How can I create a custom hashing method that produces a 19-digit long for every input value? Before, I was using this:

`private void generate() {`

long[] seedBuilder = new long[5]; // So I can print certain parts of the process for debugging purposes

String clientSeed = seedField.getText(); // The input aka seed

if (clientSeed.equals("")) clientSeed = String.valueOf(System.currentTimeMillis()); // use time if none is given

// Basic hash of seed

seedBuilder[0] = (long)clientSeed.hashCode();

// primary-char hash * concluding-char hash (for reducing collisions)

long comboID = clientSeed.substring(0,1).hashCode() * clientSeed.substring(clientSeed.length()-1).hashCode();

// Basic hash * comboID

seedBuilder[1] = seedBuilder[0] * comboID;

// Absolute value

seedBuilder[2] = Math.abs(seedBuilder[1]);

// Raised to the 1.27

seedBuilder[3] = (long)(Math.pow(seedBuilder[2], 1.27));

// Multiplied by 10.1 until digits = 19

seedBuilder[4] = (long)(Math.pow(9.9,(19-(seedBuilder[3]+"").length())) * seedBuilder[3]);

long generator = seedBuilder[4]

}

Basically, I got this through a lot of experimentation. The power of 1.27 was chosen arbitrarily and the 9.9 was just because anything over 10.1 could produce Long.MAX_VALUE, but I still wanted to get 19 digits. The problem is that some brute-force random-string testing showed that the distribution is really uneven; 60% of the results (aka seedBuilder[4]) began with 3 or 4, and 6 or 7 appear as the first index value like 5% of the time, I believe I recall correctly. Whatever aspects of the world that would be controlled by the first index would be the same a lot of the time.

I want to do this in a less arbitrary, and more professional and safe way to produce a 19-digit long I can use for generation. Thanks. Please ask me to clarify something if it is unclear.

Answer Source

First of all, `hashCode()`

returns `int`

, not `long`

, so overriding `hashCode()`

is out.

If you want to generate a 19-digit (i.e. 64-bit) numeric value from a `String`

, probably the simplest way is to use `java.security.MessageDigest`

to "hash" the string to MD5, SHA1 or SHA256, extract 64 bits and convert to `long`

using, one of the techniques described in this StackOverflow answer.

Be aware that without adjustment this returns a value between -2^{63} and +2^{63}-1 so you'll likely want to ensure the sign bit is off. Then also note that some values may not have exactly 19 decimal digits, as the result space then includes all numbers between 0 and +2^{63}-1. Of that range 10% have one or more leading zeros, so if you always want 19 digits then you will need to preserve leading zeros or reduce the range to 0..10^{18}-1 and add 10^{18}.