Skip to content

Instantly share code, notes, and snippets.

@BrainStone
Forked from mjtb49/AdditionUnderXOR.java
Last active November 12, 2021 15:16
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BrainStone/8f4a453b05189645a4d077d0ace72fcc to your computer and use it in GitHub Desktop.
Save BrainStone/8f4a453b05189645a4d077d0ace72fcc to your computer and use it in GitHub Desktop.
out/
.idea/
*.iml
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
public class Main {
public static void printTableForPublic(
MinecraftOreOffsetFinder.Disk disk, MinecraftOreOffsetFinder.Ore ore, int maxLines) {
int total = 0;
for (int i = 0; i < 16; i++) {
if (MinecraftOreOffsetFinder.residueOrSeedReliable(disk, ore, i)) {
total++;
}
}
System.out.println(
disk + " " + ore + " will work reliably on " + (total / 16.0 * 100) + "% of seeds.");
LinkedHashMap<ArrayList<Integer>, Double> offsets =
MinecraftOreOffsetFinder.getOffsets(disk, ore);
int i = 0;
for (Map.Entry<ArrayList<Integer>, Double> entry : offsets.entrySet()) {
if (++i > maxLines) {
break;
}
System.out.printf(
"Z offset %d occurs with probability %.2f%%\n",
entry.getKey().get(0), entry.getValue() * 100);
}
System.out.println();
}
private static void printStatsForAllSeeds() {
final int MAX_LINES = 5;
for (MinecraftOreOffsetFinder.Disk disk : MinecraftOreOffsetFinder.Disk.values()) {
for (MinecraftOreOffsetFinder.Ore ore : MinecraftOreOffsetFinder.Ore.values()) {
printTableForPublic(disk, ore, MAX_LINES);
}
}
}
private static void printStatsForSeed(long seed) {
for (MinecraftOreOffsetFinder.Disk disk : MinecraftOreOffsetFinder.Disk.values()) {
System.out.println(disk + ":");
for (MinecraftOreOffsetFinder.Ore ore : MinecraftOreOffsetFinder.Ore.values()) {
boolean reliable = MinecraftOreOffsetFinder.residueOrSeedReliable(disk, ore, seed);
int offset = MinecraftOreOffsetFinder.getOffsetsForSeed(disk, ore, seed).get(0);
System.out.printf("%s offset: %d%s reliable\n", ore, offset, reliable ? "" : " NOT");
}
System.out.println();
}
}
public static void main(String[] args) {
if (args.length == 0) {
printStatsForAllSeeds();
} else {
printStatsForSeed(Long.parseLong(args[0]));
}
}
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
public class MinecraftOreOffsetFinder {
// Constant used in java.util.Random to multiply the current seed with and also to XOR the initial
// seed
private static final long lmult = 0x5deece66dL;
private static final long mask = (1L << 48) - 1;
private static final int seedsToSearch = 1000000;
public static enum Ore {
COAL(60005),
IRON(60006),
GOLD(60007),
REDSTONE(60008),
DIAMOND(60009),
LAPIS(60010);
public final int salt;
private Ore(int salt) {
this.salt = salt;
}
}
public enum Disk {
SWAMP_CLAY(60011),
RIVER_CLAY(60012),
GRAVEL(60013);
public final int salt;
private Disk(int salt) {
this.salt = salt;
}
}
private static int seedToOffset(long seed) {
final int precision = 16;
final long blockToSeed = (1L << 48) / precision;
long s1 = (seed * lmult) & mask;
long s2 = (s1 * lmult) & mask;
return (int) (s2 / blockToSeed);
}
private static ArrayList<Long> getOffsetsForSeedRaw(long[] salts, Ore ore, long seed) {
long targetSalt = ore.salt;
ArrayList<Long> offsets = new ArrayList<>(salts.length);
for (long salt : salts) {
offsets.add((((seed + targetSalt) ^ lmult) - ((seed + salt) ^ lmult)) & mask);
}
return offsets;
}
private static ArrayList<Integer> getOffsetsForSeed(long[] salts, Ore ore, long seed) {
return getOffsetsForSeedRaw(salts, ore, seed).stream()
.map(MinecraftOreOffsetFinder::seedToOffset)
.collect(Collectors.toCollection(ArrayList::new));
}
public static ArrayList<Integer> getOffsetsForSeed(Disk disk, Ore ore, long seed) {
return getOffsetsForSeed(new long[] {disk.salt}, ore, seed);
}
private static HashMap<ArrayList<Long>, Integer> getOffsetsRaw(Disk disk, Ore ore) {
HashMap<ArrayList<Long>, Integer> offsets = new HashMap<>();
Random r = new Random();
long targetSalt = ore.salt;
long[] measuredSalts = {disk.salt}; // intent is eventually to handle multiple decorators
long seed;
for (int i = 0; i < seedsToSearch; i++) {
seed = r.nextLong() & mask;
ArrayList<Long> key = getOffsetsForSeedRaw(measuredSalts, ore, seed);
if (offsets.containsKey(key)) {
int current = offsets.get(key);
offsets.replace(key, current + 1);
} else {
offsets.put(key, 1);
}
}
return offsets;
}
public static LinkedHashMap<ArrayList<Integer>, Double> getOffsets(Disk disk, Ore ore) {
HashMap<ArrayList<Long>, Integer> rawOffsets = getOffsetsRaw(disk, ore);
return rawOffsets.entrySet().stream()
.sorted(Map.Entry.<ArrayList<Long>, Integer>comparingByValue().reversed())
.collect(
Collectors.toMap(
entry ->
entry.getKey().stream()
.map(MinecraftOreOffsetFinder::seedToOffset)
.collect(Collectors.toCollection(ArrayList::new)),
entry -> entry.getValue() / ((double) seedsToSearch),
(u, v) -> u,
LinkedHashMap::new));
}
public static boolean residueOrSeedReliable(Disk disk, Ore ore, long residueOrSeed) {
return ((disk.salt + residueOrSeed) & 0x10) == ((ore.salt + residueOrSeed) & 0x10);
}
}
@CB30
Copy link

CB30 commented Jun 19, 2021

hey. how can you print the offsets on a certain seed?

@BrainStone
Copy link
Author

@joogle12 Pass the seed as the first start paramter.

@CB30
Copy link

CB30 commented Jun 19, 2021

@joogle12 Pass the seed as the first start paramter.

sorry, im new at this. can you show me how?

@BrainStone
Copy link
Author

BrainStone commented Jun 19, 2021

sorry, im new at this. can you show me how?

@joogle12 Well if you don't know how to run a java program from source, I can't really help you. This isn't some trivial task and does require some background knowledge.
If you do know how to run this program then I kinda also can't help you because how to specify a startup parameter depends heavily on how you run the program. Just know that if you run it through the command line you can specify the parameter as the last parameter. And if you run it through an IDE you can specify the start parameters in the run configuration somewhere.

@CB30
Copy link

CB30 commented Jun 22, 2021

sorry, im new at this. can you show me how?

@joogle12 Well if you don't know how to run a java program from source, I can't really help you. This isn't some trivial task and does require some background knowledge.
If you do know how to run this program then I kinda also can't help you because how to specify a startup parameter depends heavily on how you run the program. Just know that if you run it through the command line you can specify the parameter as the last parameter. And if you run it through an IDE you can specify the start parameters in the run configuration somewhere.

hey. thanks for the help man. I made a 1.17 version by taking a few tips and updating the salts.

@BrainStone
Copy link
Author

@joogle12 not sure why you didn't just use my gist, to update the salts there. Then you'd still have the dual program.

@grnassar
Copy link

What needs to be done to update for 1.17?

@CB30
Copy link

CB30 commented Nov 12, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment