From 22bb2d16d229f6d6ea41f16820d59bae9b1ab790 Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Sun, 6 Apr 2025 10:25:18 +0200 Subject: [PATCH] Fix encoding and decoding --- .../module/wordcoords/converter/Decoder.java | 20 +++-- .../module/wordcoords/converter/Encoder.java | 90 ++++++++++++++----- 2 files changed, 83 insertions(+), 27 deletions(-) diff --git a/src/main/java/eu/m724/tweaks/module/wordcoords/converter/Decoder.java b/src/main/java/eu/m724/tweaks/module/wordcoords/converter/Decoder.java index 1b74720..65ff762 100644 --- a/src/main/java/eu/m724/tweaks/module/wordcoords/converter/Decoder.java +++ b/src/main/java/eu/m724/tweaks/module/wordcoords/converter/Decoder.java @@ -10,6 +10,7 @@ import eu.m724.tweaks.DebugLogger; import eu.m724.tweaks.module.wordcoords.WordList; import java.util.NoSuchElementException; +import java.util.Arrays; public class Decoder { private final WordList wordList; @@ -33,6 +34,8 @@ public class Decoder { } public int[] decode(int[] wordIndexes) { + DebugLogger.finer("Decoding word indexes: %s", Arrays.toString(wordIndexes)); + int bitsRequired = wordIndexes.length * wordList.getBitsPerWord(); int bitsRequiredPerCoordinate = bitsRequired / 2; DebugLogger.finer("Bits required: %d (per coord: %d)", bitsRequired, bitsRequiredPerCoordinate); @@ -43,23 +46,26 @@ public class Decoder { int[] decodedCoords = decodeCoords(combinedValue, bitsRequiredPerCoordinate); - int xCoord = decodedCoords[0]; - int zCoord = decodedCoords[1]; - DebugLogger.info("Chunk: %d, %d", xCoord, zCoord); + int chunkX = decodedCoords[0]; + int chunkZ = decodedCoords[1]; + DebugLogger.finer("Chunk: %d, %d", chunkX, chunkZ); - return new int[] { xCoord * 16 + 8, zCoord * 16 + 8 }; // +8 to make it center of chunk + // +8 to make it center of chunk + int xCoord = chunkX * 16 + 8; + int zCoord = chunkZ * 16 + 8; + DebugLogger.finer("Decoded to coordinates: %d, %d", xCoord, zCoord); + + return new int[] { xCoord, zCoord }; } private long wordIndexesToCombinedValue(int[] wordIndexes) { long combinedValue = 0; for (int i=0; i>= bitsPerWord; - return combinedValue; } diff --git a/src/main/java/eu/m724/tweaks/module/wordcoords/converter/Encoder.java b/src/main/java/eu/m724/tweaks/module/wordcoords/converter/Encoder.java index caff8ac..75d03e2 100644 --- a/src/main/java/eu/m724/tweaks/module/wordcoords/converter/Encoder.java +++ b/src/main/java/eu/m724/tweaks/module/wordcoords/converter/Encoder.java @@ -8,7 +8,7 @@ package eu.m724.tweaks.module.wordcoords.converter; import eu.m724.tweaks.DebugLogger; import eu.m724.tweaks.module.wordcoords.WordList; - +import java.util.Arrays; public class Encoder { private final WordList wordList; private final int bitsPerWord; @@ -19,40 +19,90 @@ public class Encoder { } public String[] encode(int xCoord, int zCoord) { - xCoord = Math.floorDiv(xCoord, 16); - zCoord = Math.floorDiv(zCoord, 16); - DebugLogger.finer("Chunk: %d, %d", xCoord, zCoord); + int chunkX = Math.floorDiv(xCoord, 16); + int chunkZ = Math.floorDiv(zCoord, 16); + DebugLogger.finer("Chunk: %d, %d", chunkX, chunkZ); + // Calculate minimum bits required per coordinate based on range + int bitsRequiredPerCoordinate = findBitsRequiredPerCoordinate(chunkX, chunkZ); + int minTotalBits = bitsRequiredPerCoordinate * 2; + DebugLogger.finer("Min bits required per coordinate: %d (total: %d)", bitsRequiredPerCoordinate, minTotalBits); - int wordsRequired = findWordsRequired(xCoord, zCoord); - DebugLogger.finer("Words required: %d", wordsRequired); + // Calculate words required, ensuring total bits is sufficient and even + int wordsRequired = 0; + int actualTotalBits = 0; + if (minTotalBits > 0) { // Avoid division by zero if bitsPerWord is 0, or log(0) + wordsRequired = Math.ceilDiv(minTotalBits, bitsPerWord); + actualTotalBits = wordsRequired * bitsPerWord; + // Ensure total bits is sufficient + while (actualTotalBits < minTotalBits) { + wordsRequired++; + actualTotalBits = wordsRequired * bitsPerWord; + } + } // else: coords are 0 or -1, minTotalBits=0, wordsRequired=0, actualTotalBits=0. Need special handling? + // If x/z are 0/-1, findBitsRequired returns 1, minTotalBits=2. The loop handles it. - int bitsRequired = wordsRequired * bitsPerWord; - int bitsRequiredPerCoordinate = bitsRequired / 2; - DebugLogger.finer("Bits required: %d (per coord: %d)", bitsRequired, bitsRequiredPerCoordinate); + // Final bits per coordinate based on words + bitsRequiredPerCoordinate = actualTotalBits / 2; + DebugLogger.finer("Final Words required: %d", wordsRequired); + DebugLogger.finer("Final Bits required: %d (per coord: %d)", actualTotalBits, bitsRequiredPerCoordinate); - - int encodedX = encodeCoord(xCoord, bitsRequiredPerCoordinate); - int encodedZ = encodeCoord(zCoord, bitsRequiredPerCoordinate); + int encodedX = encodeCoord(chunkX, bitsRequiredPerCoordinate); + int encodedZ = encodeCoord(chunkZ, bitsRequiredPerCoordinate); DebugLogger.finer("Encoded coordinates: %d, %d", encodedX, encodedZ); - long combinedValue = ((long) encodedX << bitsRequiredPerCoordinate) | encodedZ; DebugLogger.finer("Combined value: %d", combinedValue); - int[] wordIndexes = combinedValueToWordIndexes(combinedValue, wordsRequired); + DebugLogger.finer("Word indexes: %s", Arrays.toString(wordIndexes)); + return wordList.getWords(wordIndexes); } + // Calculates the minimum number of bits required to represent the coordinate + // using the encoding scheme (offset + coord) & mask, such that the coordinate + // fits within the range [-(1 << (bits - 1)), (1 << (bits - 1)) - 1]. private int findBitsRequiredPerCoordinate(int x, int z) { - int n = Math.max(Math.abs(x), Math.abs(z)); - return n == 0 ? 1 : 32 - Integer.numberOfLeadingZeros(n); - } + int maxVal = Math.max(x, z); + int minVal = Math.min(x, z); - private int findWordsRequired(int x, int z) { - int brpc = findBitsRequiredPerCoordinate(x, z); - return Math.ceilDiv(brpc * 2, wordList.getBitsPerWord()); + // Determine the required positive magnitude for the encoding range's positive side. + // We need `(1 << (bits - 1)) >= max(maxVal + 1, -minVal)` + int requiredPositiveMagnitude = Math.max(maxVal + 1, -minVal); + + if (requiredPositiveMagnitude <= 0) { + // Occurs only if maxVal <= -1 and minVal >= 0, which is impossible, + // OR maxVal <= 0 and -minVal <= 0 => maxVal <= 0 and minVal >= 0. + // This means x and z are both 0. + // The range for 1 bit is [-1, 0]. If coords are 0, 1 bit is not enough for offset+coord. + // Example: bits=1. offset=1<<0=1. mask=(1<<1)-1=1. + // encodeCoord(0, 1) = (1+0)&1 = 1. + // decodeCoord(1, 1): val=1. mask=1. offset=1. (1&1)-1 = 0. Correct. + // What if we need to represent -1? encodeCoord(-1, 1) = (1-1)&1 = 0. + // decodeCoord(0, 1): val=0. (0&1)-1 = -1. Correct. + // So 1 bit works for range [-1, 0]. Let's check the condition: + // x=0, z=0 -> maxVal=0, minVal=0. reqPosMag = max(1, 0) = 1. + // x=-1, z=-1 -> maxVal=-1, minVal=-1. reqPosMag = max(0, 1) = 1. + // x=0, z=-1 -> maxVal=0, minVal=-1. reqPosMag = max(1, 1) = 1. + // So requiredPositiveMagnitude is 1 for the range [-1, 0]. + requiredPositiveMagnitude = 1; // Ensure it's at least 1 if coords are 0 or -1. + } + + // Calculate p = bits - 1 + // We need the smallest integer p such that (1 << p) >= requiredPositiveMagnitude. + // If requiredPositiveMagnitude = 1, we need 1 << p >= 1, smallest p is 0. + // If requiredPositiveMagnitude > 1, this is equivalent to finding the number of bits + // needed to represent (requiredPositiveMagnitude - 1) in binary. + int p; + if (requiredPositiveMagnitude == 1) { + p = 0; + } else { + p = 32 - Integer.numberOfLeadingZeros(requiredPositiveMagnitude - 1); + } + + // bits = p + 1 + return p + 1; } private int encodeCoord(int coord, int bitsRequiredPerCoordinate) {