Fix encoding and decoding

This commit is contained in:
Minecon724 2025-04-06 10:25:18 +02:00
parent 598902ef33
commit 22bb2d16d2
Signed by: Minecon724
GPG key ID: A02E6E67AB961189
2 changed files with 83 additions and 27 deletions

View file

@ -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<wordIndexes.length; i++) {
combinedValue |= wordIndexes[i];
combinedValue <<= bitsPerWord;
combinedValue |= wordIndexes[i];
}
combinedValue >>= bitsPerWord;
return combinedValue;
}

View file

@ -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) {