-
-
Save mjtb49/4fc1befca5431c693fe157cfb9b509b4 to your computer and use it in GitHub Desktop.
| import java.util.*; | |
| public class AdditionUnderXOR { | |
| static final int SS = 1000000; | |
| enum Ore { | |
| COAL(60005), | |
| IRON(60006), | |
| GOLD(60007), | |
| REDSTONE(60008), | |
| DIAMOND(60009), | |
| LAPIS(60010); | |
| public final int salt; | |
| Ore(int salt) { | |
| this.salt = salt; | |
| } | |
| } | |
| enum Disk { | |
| SWAMP_CLAY(60011), | |
| RIVER_CLAY(60012), | |
| GRAVEL(60013); | |
| public final int salt; | |
| Disk(int salt) { | |
| this.salt = salt; | |
| } | |
| } | |
| static long powMod(long base, int pow) { | |
| long answer = 1; | |
| while (pow != 0) { | |
| if ((pow & 1) == 1) | |
| answer = (base * answer) & 0xffff_ffff_ffffL; | |
| base = (base * base) & 0xffff_ffff_ffffL; | |
| pow >>>= 1; | |
| } | |
| return answer; | |
| } | |
| /* | |
| * Adapted from https://www.geeksforgeeks.org/sorting-a-hashmap-according-to-values/ | |
| */ | |
| static HashMap<ArrayList<Long>, Integer> sortByValue(HashMap<ArrayList<Long>, Integer> hm) | |
| { | |
| // Create a list from elements of HashMap | |
| List<Map.Entry<ArrayList<Long>, Integer> > list = new LinkedList<>(hm.entrySet()); | |
| // Sort the list | |
| list.sort(Map.Entry.comparingByValue()); | |
| // put data from sorted list to hashmap | |
| HashMap<ArrayList<Long>, Integer> temp = new LinkedHashMap<>(); | |
| for (Map.Entry<ArrayList<Long>, Integer> aa : list) { | |
| temp.put(aa.getKey(), aa.getValue()); | |
| } | |
| return temp; | |
| } | |
| /* | |
| * Adapted from https://www.geeksforgeeks.org/sorting-a-hashmap-according-to-values/ | |
| */ | |
| static HashMap<ArrayList<Long>, Integer> sortByValueDescending(HashMap<ArrayList<Long>, Integer> hm) | |
| { | |
| // Create a list from elements of HashMap | |
| List<Map.Entry<ArrayList<Long>, Integer> > list = new LinkedList<>(hm.entrySet()); | |
| // Sort the list | |
| list.sort(Map.Entry.comparingByValue()); | |
| Collections.reverse(list); | |
| // put data from sorted list to hashmap | |
| HashMap<ArrayList<Long>, Integer> temp = new LinkedHashMap<>(); | |
| for (Map.Entry<ArrayList<Long>, Integer> aa : list) { | |
| temp.put(aa.getKey(), aa.getValue()); | |
| } | |
| return temp; | |
| } | |
| static HashMap<ArrayList<Long>, Integer> getOffsets(Disk disk, Ore ore) { | |
| return getOffsets(disk, ore, false, 0); | |
| } | |
| static HashMap<ArrayList<Long>, Integer> getOffsets(Disk disk, Ore ore, boolean useResidue, int residue) { | |
| 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 lmult = 0x5deece66dL; | |
| long mask = 0xffff_ffff_ffffL; | |
| long seed; | |
| for (int i = 0; i < SS; i++) { | |
| if (useResidue) | |
| seed = ((r.nextLong() << 4) + residue) & mask; | |
| else | |
| seed = r.nextLong() & mask; | |
| ArrayList<Long> key = new ArrayList<>(measuredSalts.length); | |
| for (long salt: measuredSalts) { | |
| key.add((((seed + targetSalt) ^ lmult) - ((seed + salt) ^ lmult)) & mask); | |
| } | |
| if (offsets.containsKey(key)) { | |
| int current = offsets.get(key); | |
| offsets.replace(key, current + 1); | |
| } else { | |
| offsets.put(key, 1); | |
| } | |
| } | |
| return offsets; | |
| } | |
| static boolean residueReliable(Disk disk, Ore ore, int residue) { | |
| return getOffsets(disk, ore, true, residue).size() == 1; | |
| } | |
| static void printTableForPublic(Disk disk, Ore ore, int maxLines) { | |
| int total = 0; | |
| for (int i = 0; i < 16; i++) //lmao this is terrible | |
| if (residueReliable(disk,ore,i)) total++; | |
| System.out.println(disk + " " + ore + " will work reliably on " + total / 16.0 * 100 +"% of seeds."); | |
| HashMap<ArrayList<Long>, Integer> offsets = getOffsets(disk, ore); | |
| int i = 0; | |
| for (Map.Entry<ArrayList<Long>, Integer> en : sortByValueDescending(offsets).entrySet()) { | |
| if ( i >= maxLines) | |
| continue; | |
| i++; | |
| ArrayList<Long> vecs = en.getKey(); | |
| int precision = 16; | |
| long blockToSeed = (1L << 48) / precision; | |
| long s1 = (vecs.get(0) * 0x5deece66dL) & 0xffff_ffff_ffffL; | |
| long s2 = (s1 * 0x5deece66dL) & 0xffff_ffff_ffffL; | |
| //long s3 = (s2 * lmult) & mask; //these correspond to the size to depth conversion - too complex | |
| //for public | |
| //System.out.print( s1 / blockToSeed + " "); | |
| System.out.print("Z offset " + s2 / blockToSeed + " occurs with probability "); | |
| //System.out.print(s3 / blockToSeed + " "); | |
| System.out.println(en.getValue() / (double) SS); | |
| } | |
| System.out.println(); | |
| offsets.clear(); | |
| } | |
| static void printTableForMe(Disk disk, Ore ore) { | |
| HashMap<ArrayList<Long>, Integer> offsets = getOffsets(disk, ore); | |
| long lmult = 0x5deece66dL; | |
| long mask = 0xffff_ffff_ffffL; | |
| for (Map.Entry<ArrayList<Long>, Integer> en : sortByValue(offsets).entrySet()) { | |
| ArrayList<Long> vecs = en.getKey(); | |
| //for (int call = 0; call < 3; call++) { | |
| for (int i = 0; i < vecs.size(); i++) { | |
| if (i == 0) { | |
| long s1 = (vecs.get(0) * lmult) & mask; | |
| long s2 = (s1 * lmult) & mask; | |
| long s3 = (s2 * lmult) & mask; | |
| System.out.printf("%012X", s1); | |
| System.out.print(" " + String.format("%012X", s2)); | |
| System.out.print(" " + String.format("%012X", s3)); | |
| System.out.println(" " + en.getValue() / (double) SS); | |
| } else { | |
| long offset = vecs.get(i) - vecs.get(0); | |
| long s1 = (offset * lmult) & mask; | |
| long s2 = (s1 * lmult) & mask; | |
| long s3 = (s2 * lmult) & mask; | |
| System.out.printf("%012X", s1); | |
| System.out.print(" " + String.format("%012X", s2)); | |
| System.out.print(" " + String.format("%012X", s3)); | |
| System.out.println(" " + en.getValue() / (double) SS); | |
| } | |
| } | |
| } | |
| System.out.println(); | |
| offsets.clear(); | |
| } | |
| public static void main(String[] args) { | |
| final int MAX_LINES = 5; | |
| for (Disk disk: Disk.values()) { | |
| for (Ore ore : Ore.values()) { | |
| printTableForPublic(disk, ore, MAX_LINES); | |
| } | |
| } | |
| } | |
| } |
Would be super cool if you added code to spit out the numbers for a known seed!
Set
SSto 1, remove this:
if (useResidue) seed = ((r.nextLong() << 4) + residue) & mask; else seed = r.nextLong() & mask;
and change the initialization of seed tolong seed =;
(If I'm not wrong)
Thanks for the comment. But I forked the project already and modified it, so it's all good ^^
Would it be possible to release this under a free software license?
You could just put something like the following text at the top:
// Copyright (C) 2021 mjtb49 <https://github.com/mjtb49>
//
// SPDX-License-Identifier: Apache-2.0(Obviously, this text is for Apache-2.0. Replace it with whatever SPDX license expression under which you want to license the program, such as CC0-1.0 or MIT.)
This way, people can (legally) modify and run the code. One person already made a fork, but it's of questionable legality because it doesn't have a license. (Edit: while the idea-expression divide could apply here, I doubt many people who want to use this code are lawyers.)
*You have to make sure that you indicate your seed as long by adding
Lto the end of your seed ^^