added population reversal

This commit is contained in:
Unknown
2020-01-28 18:26:52 -05:00
parent 2329c4098f
commit 823bb952de
3 changed files with 172 additions and 4 deletions
@@ -0,0 +1,41 @@
package kaptainwutax.seedcracker.magic;
/**
* Math utility library-- I have no idea how it works, don't ask.
* All credits to Matthew. (The man doesn't have a GitHub, shame!)
* */
public class MagicMath {
public static final long MASK_16 = 0xFFFFL;
public static final long MASK_32 = 0xFFFF_FFFFL;
public static final long MASK_48 = 0xFFFF_FFFF_FFFFL;
public static int countTrailingZeroes(long v) {
int c; // output: c will count v's trailing zero bits,
// so if v is 1101000 (base 2), then c will be 3
v = (v ^ (v - 1)) >> 1; // Set v's trailing 0s to 1s and zero rest
for(c = 0; v !=0; c++) {
v >>>= 1;
}
return c;
}
public static long modInverse(long x, int mod) { //Fast method for modular inverse mod powers of 2
long inv = 0;
long b = 1;
for(int i = 0; i < mod; i++) {
if((b & 1) == 1) {
inv |= 1L << i;
b = (b - x) >> 1;
} else {
b >>= 1;
}
}
return inv;
}
}
@@ -0,0 +1,121 @@
package kaptainwutax.seedcracker.magic;
import kaptainwutax.seedcracker.util.Rand;
import kaptainwutax.seedcracker.util.Seeds;
import kaptainwutax.seedcracker.util.math.LCG;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class PopulationReversal {
private static final LCG SKIP_2 = Rand.JAVA_LCG.combine(2);
private static final LCG SKIP_4 = Rand.JAVA_LCG.combine(4);
public static ArrayList<Long> getWorldSeeds(long populationSeed, int x, int z) {
ArrayList<Long> worldSeeds = new ArrayList<>();
if (x == 0 && z == 0) {
worldSeeds.add(populationSeed);
return worldSeeds;
}
long c; //a is upper 16 bits, b middle 16 bits, c lower 16 bits of worldseed.
long e = populationSeed & MagicMath.MASK_32; //The algorithm proceeds by solving for worldseed in 16 bit groups
long f = populationSeed & MagicMath.MASK_16; //as such, we need the 16 bit groups of chunkseed for later eqns.
boolean xEven = (x & 1) == 0;
boolean zEven = (z & 1) == 0;
long firstMultiplier = (SKIP_2.multiplier * x + SKIP_4.multiplier * z) & MagicMath.MASK_16;
int multTrailingZeroes = MagicMath.countTrailingZeroes(firstMultiplier); //TODO currently code blows up if this is 16, but you can use it to get bits of seed anyway if it is non zero
long firstMultInv = MagicMath.modInverse(firstMultiplier >> multTrailingZeroes,16);
//TODO We can recover more initial bits when x + z is divisible by a power of 2
if (xEven ^ zEven) { //bottom bit of x*a + z*b is odd so we xor by 1 to get bottom bit of worldseed.
c = (populationSeed & 1) ^ 1;
} else { //bottom bit of x*a + z*b is even so we xor by 0 to get bottom bit of worldseed.
c = (populationSeed & 1);
}
for (; c < (1L << 16); c += 2) { //iterate through all possible lower 16 bits of worldseed.
long target = (c ^ f) & MagicMath.MASK_16; //now that we've guessed 16 bits of worldseed we can undo the mask
//We need to handle the four different cases of the effect the two | 1s have on the seed
long magic = x * ((SKIP_2.multiplier * ((c ^ Rand.JAVA_LCG.multiplier) & MagicMath.MASK_16) + SKIP_2.addend) >>> 16) + z * ((SKIP_4.multiplier * ((c ^ Rand.JAVA_LCG.multiplier) & MagicMath.MASK_16) + SKIP_4.addend) >>> 16);
addWorldSeed(worldSeeds, target - (magic & MagicMath.MASK_16), multTrailingZeroes, firstMultInv, c, e, x, z, populationSeed); //case both nextLongs were odd
addWorldSeed(worldSeeds, target - ((magic + x) & MagicMath.MASK_16), multTrailingZeroes, firstMultInv, c, e, x, z, populationSeed); //case where x nextLong even
addWorldSeed(worldSeeds, target - ((magic + z) & MagicMath.MASK_16), multTrailingZeroes, firstMultInv, c, e, x, z, populationSeed); //case where z nextLong even
addWorldSeed(worldSeeds, target - ((magic + x + z) & MagicMath.MASK_16), multTrailingZeroes, firstMultInv, c, e, x, z, populationSeed); //case where both nextLongs even
}
return worldSeeds;
}
public static long makeSecondAddend(int x, long k, int z) {
return ((x*((((SKIP_2.multiplier * ((k ^ Rand.JAVA_LCG.multiplier) & MagicMath.MASK_32) + SKIP_2.addend) & MagicMath.MASK_48) >>> 16) | 1L) +
z*((((SKIP_4.multiplier * ((k ^ Rand.JAVA_LCG.multiplier) & MagicMath.MASK_32) + SKIP_4.addend) & MagicMath.MASK_48) >>> 16) | 1L)) >>> 16) & MagicMath.MASK_16;
}
public static void addWorldSeed(List<Long> worldSeeds, long firstAddend, int multTrailingZeroes, long firstMultInv, long c, long e, int x, int z, long populationSeed){
if(MagicMath.countTrailingZeroes(firstAddend) >= multTrailingZeroes) { //Does there exist a set of 16 bits which work for bits 17-32
long b = ((((firstMultInv * firstAddend)>>> multTrailingZeroes) ^ (Rand.JAVA_LCG.multiplier >> 16)) & ((1L << (16 - multTrailingZeroes)) - 1));
for(; b < (1L << 16); b += (1L << (16 - multTrailingZeroes))) { //if the previous multiplier had a power of 2 divisor, we get multiple solutions for b
long k = (b << 16) + c;
long target2 = (k ^ e) >> 16; //now that we know b, we can undo more of the mask
long secondAddend = makeSecondAddend(x, k, z);
if (MagicMath.countTrailingZeroes(target2 - secondAddend) >= multTrailingZeroes) { //Does there exist a set of 16 bits which work for bits 33-48
long a = ((((firstMultInv * (target2 - secondAddend)) >>> multTrailingZeroes) ^ (Rand.JAVA_LCG.multiplier >> 32)) & ((1L << (16-multTrailingZeroes)) - 1));
for(; a < (1L << 16); a += (1L << (16 - multTrailingZeroes))) { //if the previous multiplier had a power of 2 divisor, we get multiple solutions for a
if(Seeds.setPopulationSeed(null, (a << 32) + k, x, z) == populationSeed) { //lazy check if the test has succeeded
worldSeeds.add((a << 32) + k);
}
}
}
}
}
}
/*
* Left as reference if I need to test this mess again. :P
* */
public static void main(String[] args) {
long seed;
int x , z;
ArrayList<Long> seeds;
/*long seed = 40820992642153L;
int x = 2;
int z = 4;
ArrayList<Long> seeds = getSeedFromChunkseed(getChunkseed(seed, x, z), x, z);
System.out.println("start");
for (long a : seeds) {
System.out.println(a);
}
System.out.println("done");*/
Random r = new Random();
int failcount = 0;
System.out.println("start");
long start = System.currentTimeMillis();
for(int i = 0; i < 100000; i++){
seed = r.nextLong() & ((1L << 48)-1);
x = r.nextInt(16) - 8;
z = r.nextInt(16) - 8;
seeds = getWorldSeeds(Seeds.setPopulationSeed(null, seed,x,z), x, z);
if (!seeds.contains(seed)) {
System.out.println(seed);
System.out.println(x);
System.out.println(z);
failcount++;
System.out.println();
}
}
System.out.println("End: "+((System.currentTimeMillis()-start)/1000.0));
System.out.println(failcount+" failures.");
}
}
@@ -4,11 +4,12 @@ public class Seeds {
public static long setRegionSeed(Rand rand, long worldSeed, int regionX, int regionZ, int salt) {
long seed = (long)regionX * 341873128712L + (long)regionZ * 132897987541L + worldSeed + (long)salt;
rand.setSeed(seed, true);
if(rand != null)rand.setSeed(seed, true);
return seed;
}
public static long setPopulationSeed(Rand rand, long worldSeed, int posX, int posZ) {
if(rand == null)rand = new Rand(0L);
rand.setSeed(worldSeed, true);
long a = rand.nextLong() | 1L;
long b = rand.nextLong() | 1L;
@@ -18,6 +19,7 @@ public class Seeds {
}
public static long setStructureStartSeed(Rand rand, long worldSeed, int chunkX, int chunkZ) {
if(rand == null)rand = new Rand(0L);
rand.setSeed(worldSeed, true);
long a = rand.nextLong();
long b = rand.nextLong();
@@ -30,14 +32,18 @@ public class Seeds {
int sX = chunkX >> 4;
int sZ = chunkZ >> 4;
long seed = (long)(sX ^ sZ << 4) ^ worldSeed;
rand.setSeed(seed, true);
rand.nextInt();
if(rand != null) {
rand.setSeed(seed, true);
rand.nextInt();
}
return seed;
}
public static long setSlimeChunkSeed(Rand rand, long worldSeed, int chunkX, int chunkZ, long salt) {
long seed = worldSeed + (long)(chunkX * chunkX * 4987142) + (long)(chunkX * 5947611) + (long)(chunkZ * chunkZ) * 4392871L + (long)(chunkZ * 389711) ^ salt;
rand.setSeed(seed, true);
if(rand != null)rand.setSeed(seed, true);
return seed;
}