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
  • 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);
}
}
@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