prospecting false negatives showing lack of randomness
Gaelmare opened this issue ยท 9 comments
As previously discussed, I've experienced a Z-axis bias in positive and false negative propicking.
For the blocks at x=-111, y=144, and z from 300 to 320, I saw false negative propick results on EVERY block.
For the adjacent blocks at x=-112, y=144, and z from 300 to 320, I saw VL vein sample propick results on EVERY block.
Here's a screenshot of the area.
- TFC Version: build 175
- TNFC modpack, 12.2.62.1
Have not reproduced in TFC alone. Recently heard from one other TNFC player who experienced something similar with 17 blocks in a row. They didn't specify which axis.
Link to their message:
https://discord.com/channels/104658769123753984/104658769123753984/820879646995513344
Looks like a statistical probabilty. Three anecdotes aren't enough to proclaim a lack of randomness.
A "fix" that would make the result feel more random could be a bias towards a result which is different from ones of nearby blocks.
Have increased my prospecting skill to Master, and now get no false negatives in that area at all. Playing in SSP, so not a test world. Will attempt to reproduce as a 0 skill player.
Reproduced with Expert skill in the same location in a very unrandom pattern. Every other block in the field within range of the vein gives a positive reading. The chert brick blocks give false negatives.
This is with TFC 175 alone, no other mods. (Worldedit was added to highlight issue after false negatives were confirmed)
The location is the same, x=-111, z=300+, y=144 and 145 depending on which chert brick line.
Attempting to reproduce in a random seed with a vein selected for its closeness to the surface resulted in information in the screenshot for a player with 0 prospecting skill. Again, every other block except for the red wool will give a positive reading, as long as the vein is in range.
I believe there is a problem. At the very least, I'm not prospecting running N or S any more.
Just had a look on TerraFirmaCraft/src/main/java/net/dries007/tfc/objects/items/metal/ItemProspectorPick.java
Seeing as the seed for the rng is set to be the position of the block that is clicked, this behavior is expected.
I suspect that pos.toLong() (wherd pos is the block position) returns a very small number. Generally, large numbers should be used as seeds for this type of rng.
To fix this, I would add 10^10 to pos.toLong() to set the Random seed.
This effectively moves the "unrandom" region outside of world borders (and if not, just use a bigger number)
Another option is to add the world seed, as in:
Random.setSeed(pos.toLong() + world_seed)
As discussed internally, the solution is most likely that we should multiply by a large prime number and test further.
Multiplying by a large number should do it, although I insist that adding a large number is better. (Either way, it doesn't have to be prime, just large)
idc how much you "insist" what's better, adding a large number does nothing once you actually figure out what the problem is. (Hint: it's not that the random is being seeded with small numbers.) It's the same root cause documented here. Which is the kind of justification I would've liked to see for this issue.
Now, not saying the below is perfect either. It seems to have some diagonal bias at first viewing.
And because I know better than to suggest random improvements to hash functions without like, at least testing them somewhat...
asLong()
....................#####################
.........................................
.........................................
#########################################
.........................................
....................#####################
.........................................
.........................................
#########################################
.........................................
....................#####################
.........................................
.........................................
#########################################
.........................................
.........................................
####################.....................
.........................................
#########################################
.........................................
.........................................
####################.....................
.........................................
#########################################
.........................................
.........................................
########################............#####
.........................................
#########################################
.........................................
.........................................
#########################################
.........................................
....................#####################
.........................................
.........................................
#########################################
.........................................
....................#####################
.........................................
.........................................
asLongPlusLargeNumber()
.........................................
....................###################..
.........................................
#########################################
.........................................
.........................................
....................#####################
.........................................
#########################################
.........................................
.........................................
#########################################
.........................................
####################...................##
.........................................
.........................................
#########################################
.........................................
####################...................##
.........................................
.........................................
#########################################
.........................................
####################.....................
.........................................
.........................................
#########################################
.........................................
.........................................
....................###################..
.........................................
#########################################
.........................................
.........................................
....................###################..
.........................................
#########################################
.........................................
.........................................
....................#####################
.........................................
asLongMultipliedByLargeNumber()
###.##...#..###...##..#......#.#..#.#.#..
.......##..#....##.....#....#.#..#.#.#..#
.#..#....#..##....##..#......#..#......#.
..........##...##....#..#.#.#.#..#.......
...#..#.............#....#.#.#..#......#.
#.#..#....##..###....#..#.#....#..#.#.#..
...#..###........#..#..#.#.#.......#.#..#
###.##...#..###...##..#...#....#..#.#.#..
......###........#..#..#...##.#..#.#.#..#
##..##...#..###...##..#......#..#......#.
..........##...##....#..#...#.#..#.......
...#...............#.....#...#..#......#.
..#.......##...##....#..#.#....#..#.#.#..
...#..##.........#..#..#.#.#.......#.#..#
###.##...##..##...#...#...#....#..#.#.#..
...#..###........#..#..#...##.#..#.#.#..#
###.##...#..###...##..#......#..#...#.##.
........#..#...###...#..#...#.#..#.#....#
.........#..##.....#..#..#...#..#......#.
..........##...##....#..#.#.#..#.##......
...#..##............#..#.#.#....#......#.
###.##....#...#...#..#..#.#....#..#.#.#..
...#..###........#..#..#.#.#..#....#.#..#
###.##...#..###...##..#......#.#..#.#.#..
.......##..#...###..........#.#..#.#.#..#
.#..#....#..##....##..#......#..#......#.
..........##...##....#..#.#.#.#..#.......
...#..#.............#....#.#.#..#......#.
#.#..#....#...##.....#..#.#....#..#.#.#..
...#..###........#..#..#.#.#.......#.#..#
###.##...#..###...##..#...#....#..#.#.#..
......###.......##..#..#...##.#..#.#.#..#
##..#....#..###...##..#......#..#......#.
..........##...##....#..#...#.#..#.......
...#...............#.....#...#..#......#.
..#.......##...##....#..#.#....#..#.#.#..
...#..###........#..#..#.#.#.......#.#..#
###.##...##.###...#...#...#....#..#.#.#..
...#..###........#..#..#...##.#..#.#.#..#
###.##...#..###...##..#......#..#...#.##.
........#..#...###...#..#...#.#..#.#....#
Source code, which mimics the prospector's pick result and prints the above images:
import java.util.Random;
class Main {
public static void main(String args[]) {
System.out.println("asLong()");
testArea(Main::asLong);
System.out.println("asLongPlusLargeNumber()");
testArea(Main::asLongPlusLargeNumber);
System.out.println("asLongMultipliedByLargeNumber()");
testArea(Main::asLongMultipliedByLargeNumber);
}
static void testArea(Hash hash) {
Random random = new Random();
for (int x = -20; x <= 20; x++) {
for (int y = -20; y <= 20; y++) {
random.setSeed(hash.hash(x, y, 140));
System.out.print(random.nextFloat() < 0.3 ? "#" : ".");
}
System.out.println("");
}
}
interface Hash { long hash(int x, int y, int z); }
static final int PACKED_X_LENGTH = 1 + 25;
static final int PACKED_Z_LENGTH = PACKED_X_LENGTH;
static final int PACKED_Y_LENGTH = 64 - PACKED_X_LENGTH - PACKED_Z_LENGTH;
static final long PACKED_X_MASK = (1L << PACKED_X_LENGTH) - 1L;
static final long PACKED_Y_MASK = (1L << PACKED_Y_LENGTH) - 1L;
static final long PACKED_Z_MASK = (1L << PACKED_Z_LENGTH) - 1L;
static final int Z_OFFSET = PACKED_Y_LENGTH;
static final int X_OFFSET = PACKED_Y_LENGTH + PACKED_Z_LENGTH;
static long asLong(int x, int y, int z) {
long i = 0L;
i = i | ((long)x & PACKED_X_MASK) << X_OFFSET;
i = i | ((long)y & PACKED_Y_MASK) << 0;
return i | ((long)z & PACKED_Z_MASK) << Z_OFFSET;
}
static long asLongPlusLargeNumber(int x, int y, int z) {
return asLong(x, y, z) + 18593920493L;
}
static long asLongMultipliedByLargeNumber(int x, int y, int z) {
return asLong(x, y, z) * 29349231L;
}
}