Skip to content

Instantly share code, notes, and snippets.

@syule
Last active January 4, 2024 22:07
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save syule/4acbc2e1aba251db6caa7d50a0afe546 to your computer and use it in GitHub Desktop.
Save syule/4acbc2e1aba251db6caa7d50a0afe546 to your computer and use it in GitHub Desktop.
// compile with: nvcc -ccbin=g++-5 -O3 -std=c++11 -arch compute_60 -x cu seedsearch.cpp -o seedsearch
// edit the flat_chunks variable with the x, z chunk coordinates of ocean monuments
// the tallest part of each monument is on the north-west corner of the center chunk
// reddit username osmotischen
#include <cuda.h>
#include <cuda_runtime.h>
#include <iostream>
#include <numeric>
#include <functional>
#include <fstream>
#include <stdio.h>
#include <cassert>
#include <vector>
#include <algorithm>
#include <chrono>
#include <inttypes.h>
#include <thrust/logical.h>
#include <thrust/sequence.h>
#include <thrust/functional.h>
#include <thrust/device_vector.h>
#include <thrust/device_ptr.h>
#include <thrust/extrema.h>
#include <thrust/device_malloc.h>
#include <thrust/device_free.h>
#include <thrust/execution_policy.h>
typedef int64_t lng;
typedef uint64_t ulng;
//2^28 checks per launch means we need 2^20 launches
const int lg_launches = 20;
const int lg_blocks = 23;
const int lg_threads = 5; //fits in a warp perfectly!
const int num_launches = 1 << lg_launches;
const int num_blocks = 1 << lg_blocks;
const int num_threads = 1 << lg_threads;
const int spacing = 32;
const int separation = 5;
const int gap = spacing - separation;
const lng struct_seed = 10387313;
const lng mask = (1LL << 48) - 1LL;
const lng mask32 = (1LL << 32) - 1LL;
const lng mask16 = (1LL << 16) - 1LL;
const lng lcg1 = 0x5DEECE66DLL;
const lng lcg2 = 0xBLL;
const lng lcg3 = 341873128712LL;
const lng lcg4 = 132897987541LL;
const lng inv_lcg1 = 0xDFE05BCB1365LL;
const bool check_all = true;
__device__ __host__ lng set_seed(lng seed) {
return (seed ^ lcg1) & mask;
}
__device__ __host__ lng update_seed(lng seed) {
return (seed * lcg1 + lcg2) & mask;
}
__device__ __host__ int nextInt(lng &seed) {
int bits;
int val;
do {
seed = update_seed(seed);
//unsigned to make this an arithmetic shift
bits = (int) ((ulng)seed >> (48-31));
val = bits % gap;
} while (bits-val+gap-1 < 0);
return val;
}
__device__ __host__ void rand4_for_seed(int arg1, int arg2, lng seed, int * buffer) {
//first compute the input seed
lng i = (lng)arg1 * lcg3 + (lng)arg2 * lcg4 + struct_seed + seed;
seed = set_seed(i);
for (int j = 0; j < 4; j++) {
buffer[j] = nextInt(seed);
}
}
__device__ __host__ bool check_chunk(int x, int z, lng world_seed) {
int i = x;
int j = z;
if (x < 0) {
x -= spacing - 1;
}
if (z < 0) {
z -= spacing - 1;
}
//rounding should be the same as in java
int k = x/spacing;
int l = z/spacing;
int rand4[4];
rand4_for_seed(k, l, world_seed, rand4);
k *= spacing;
l *= spacing;
k += (rand4[0] + rand4[1])/2;
l += (rand4[2] + rand4[3])/2;
return (i == k) && (j == l);
}
__device__ __host__ bool check_all_chunks(lng world_seed) {
const int flat_chunks[] = {
206, -39,
235, -83,
203, -127,
295, 47,
331, 52,
-11, -334,
-80, -307,
};
for (unsigned int i = 0; i < sizeof(flat_chunks)/sizeof(int)/2; i++) {
if (!check_chunk(flat_chunks[i*2], flat_chunks[i*2+1], world_seed)) {
return false;
}
}
return true;
}
__global__ void run_launch(int launch, bool * results) {
lng seed = ((lng)launch << (lg_blocks+lg_threads)) + ((lng)blockIdx.x << lg_threads) + threadIdx.x;
bool result = check_all_chunks(seed);
bool any_found = __any(result);
if (threadIdx.x == 0) {
results[blockIdx.x] = any_found;
}
}
lng seed_search() {
lng compatible_seed = -1;
bool *block_results;
bool max_elem;
cudaMalloc(&block_results, sizeof(bool)*num_blocks);
auto t0 = std::chrono::high_resolution_clock::now();
//can start at 624635 for debugging
for (int launch = 0; launch < num_launches; launch++) {
if (!(launch % (1 << 10))) {
auto t1 = std::chrono::high_resolution_clock::now();
long d0 = std::chrono::duration_cast<std::chrono::milliseconds>(t1-t0).count();
t0 = t1;
printf("seed %" PRIi64 ", launch %d, time %ld ms\n",
((lng)launch) << (lg_blocks + lg_threads),
launch, d0);
}
run_launch<<<num_blocks,num_threads>>>(launch, block_results);
thrust::device_vector<bool> thrust_results(block_results, block_results+num_blocks);
auto max_idx = thrust::max_element(thrust_results.begin(), thrust_results.end());
int index = max_idx - thrust_results.begin();
cudaMemcpy(&max_elem, block_results + index, sizeof(bool), cudaMemcpyDeviceToHost);
if (max_elem) {
lng seedstart = ((lng)launch << (lg_blocks+lg_threads)) + ((lng)index << lg_threads);
for (lng seed = seedstart; seed < seedstart+num_threads; seed++) {
if (check_all_chunks(seed)) {
if (!check_all) {
return seed;
} else if (compatible_seed == -1) {
printf("found candidate %" PRIi64 "\n", seed);
compatible_seed = seed;
} else {
printf("%" PRIi64 "\n", compatible_seed);
printf("%" PRIi64 "\n", seed);
return -2;
}
}
}
}
}
return compatible_seed;
}
void extend_seed(lng lower48) {
//assert(((lcg1 * inv_lcg1) & mask) == 1LL);
lng lower32 = lower48 & mask32;
lng upper32lower16 = (lower48 >> 32) & mask16;
for (lng i = 0; i < (1LL << 16); i++) {
lng seed2 = (lower32 << 16) | i;
lng seed1 = ((seed2-lcg2) * inv_lcg1) & mask;
lng upper32 = seed1 >> 16;
if (upper32lower16 == (upper32 & mask16)) {
lng candidate = (upper32 << 32) | lower32;
printf("candidate 64 bit extension: %" PRIi64 "\n", candidate);
}
}
}
int main(int argc, char **argv)
{
lng out = seed_search();
//lng out = 167675086332377LL;
//lng out = 255383536937374LL;
if (out == -1) {
printf("no compatible seeds found\n");
} else if (out == -2) {
printf("too many compatible seeds\n");
} else {
printf("lower 48 bits are %" PRIi64 "\n", out);
extend_seed((lng) out);
}
return 0;
}
@SpeedRider
Copy link

hello i was wondering if this still works and how do i run the program XD

@RedNicStone
Copy link

RedNicStone commented Feb 9, 2019

hello i was wondering if this still works and how do i run the program XD

It says right on the top :)

// compile with: nvcc -ccbin=g++-5 -O3 -std=c++11 -arch compute_60 -x cu seedsearch.cpp -o seedsearch

@Volian0
Copy link

Volian0 commented Mar 16, 2019

Very nice!
I had to include device_launch_parameters.h to compile it

@NaNraptor
Copy link

Does this still manage to find the correct seed for 1.14 and up? I havent heard of any major changes related to seeds but still curious if it works

@RB1O1
Copy link

RB1O1 commented Jul 28, 2019

What program do you run this in? Command Prompt? I'm not an experienced coder so I've no idea how to run this :/

Any help would be greatly appreciated :)

@syule
Copy link
Author

syule commented Jul 29, 2019

Does this still manage to find the correct seed for 1.14 and up? I havent heard of any major changes related to seeds but still curious if it works

i'm not aware of any changes to structure generation, so should still work

What program do you run this in?

you compile the code into an executable binary and run it

@programrunner9929
Copy link

Hey there! I have run into an issue where when I go to compile I get an error that says "g++-5 No such File or Directory". I am on a MAC and I have the xcode command line developer tools installed and I am not quite sure what the issue is exactly. Any help is greatly appreciated! and I apologize, I am not very knowledgable about programing.

@amcglau
Copy link

amcglau commented Oct 3, 2019

Hello! I was wondering what the theory is behind how you extend the lower 48 bits to a full 64 bit seed. I'm writing my own program, and I would rather develop an extension method myself.

@syule
Copy link
Author

syule commented Oct 6, 2019

Hello! I was wondering what the theory is behind how you extend the lower 48 bits to a full 64 bit seed. I'm writing my own program, and I would rather develop an extension method myself.

Hey, thanks for the interest.

When the world seed is left blank, a new java Random object is constructed, and the world seed is obtained by calling the nextLong method.

The random object itself has a 64 bit seed, which is NOT the same as the world seed. Call this seed M, and the world seed S. But since only 48 bits of state are ever used in the implementation of the java rng, only the right 48 bits of M are ever relevant.

More precisely, first the state of the rng is initialized as follows:
M = (M ^ lcg1) & ((1L << 48) - 1)

When nextLong is called, the result S is determined by two calls to the RNG, asking for 32 bytes at a time:
S = ((long)next(32) << 32) + next(32)

The code for next(32) computes:
M = (M * lcg1 + lcg2) & ((1L << 48) - 1)
result = (int)(M >> 16)

lcg1 and lcg2 are just some fixed constants, which you can look up. Let M' and M'' denote values of M immediately after the first and second calls to next(32) respectively. Now a bit of math which will be useful later: the integers modulo 2^48 are a finite field so lcg1 must have a multiplicative inverse, which I was able to find. This means that I can invert the mutation of the state when next(32) is called.

M'' = M' * lcg1 + lcg2
M' = (M'' - lcg2) * inv_lcg1

For clarity, I omitted the necessary ((1L << 48) - 1) from both expressions.

Let S1 and S2 denote the left and right halves of S, in other words the result from the first and second calls to next(32) respectively. We just want the left 16 bits of S1 -- we already know the rest.

Consider that we already know the middle 32 bits of M'' -- they are simply S2, which we know entirely. If we knew the right 16 bits of M'', then we would be able to recover M' using the inverse equation from before. Since the middle 32 bits of M' are the bits of S1, that would be sufficient to complete the task.

16 bits is quite trivial to brute force: we simply guess the right 16 bits of M'' and determine what would be S1 if our guess was correct. If our guess is correct, then we know the right 16 bits of S1 should match the right 16 bits of our guessed S1. However a match is no guarantee that our guess is correct -- hence we may output several candidate 64 bit completions of S without knowing the correct one. Fortunately there are rarely more than 1 or 2 matches.

By the way, you should be pretty careful since I'm not quite sure all the math/code is correct here -- I don't remember if i ever tested the seed extension logic.

@xdPhoenyx
Copy link

Hey, I have no experience with C++ and have only ever coded using python or HTML. I was wondering if there is a simple way to run this as I don't really understand how to compile and run C++ code.

@NaNraptor
Copy link

// compile with: nvcc -ccbin=g++-5 -O3 -std=c++11 -arch compute_60 -x cu seedsearch.cpp -o seedsearch
// edit the flat_chunks variable with the x, z chunk coordinates of ocean monuments

You need a c++ compiler, this one is nvcc (NVIDIA CUDA Compiler), so download that and in command line run the command above

@end-user
Copy link

You said this was intended to search based on monument positions but that other things were possible. How would I make changes to match other structures; for example villages?

@felipemslima
Copy link

I've tried to run but had no output

@MrLFW
Copy link

MrLFW commented Aug 5, 2020

I've tried to run but had no output

same
i also tried running
nvcc seedsearch.cpp -o seedsearch
and these errors came out
seedsearch.cpp(128): error C2065: 'blockIdx': undeclared identifier
seedsearch.cpp(128): error C2065: 'threadIdx': undeclared identifier
seedsearch.cpp(130): error C3861: '__any': identifier not found
seedsearch.cpp(132): error C2065: 'threadIdx': undeclared identifier
seedsearch.cpp(133): error C2065: 'blockIdx': undeclared identifier
seedsearch.cpp(158): error C2059: syntax error: '<'

@syule
Copy link
Author

syule commented Aug 6, 2020

@end-user

You said this was intended to search based on monument positions but that other things were possible. How would I make changes to match other structures; for example villages?

It's been quite a long time so I don't remember all the details, but I believe spacing, separation, and struct_seed are specific to the type of structure -- e.g. end cities or villages would have a different set of values for this -- but that otherwise, the algorithm for what's a valid place to put a structure is the same (afaik, but could be wrong). You'd have to dig around in the source code to find these values.

@MrLFW

i also tried running
nvcc seedsearch.cpp -o seedsearch

Since the file extension is .cpp, nvcc will assume the file contains only cpu code. The flag -x cu is necessary (in fact most of the flags above are necessary) for correct compilation.

@felipemslima

I've tried to run but had no output

I would need a bit more information than this -- does it compile without error? Does it exit without any output when you run it, or does it hang?

@dany144
Copy link

dany144 commented Oct 24, 2020

Hi, i'm wondering how to know the program is running, because when i compile it in command prompt, there are no errors but nothing happens either, so i'm not sure if i need to wait until it prints the results or if there is something wrong.

@syule
Copy link
Author

syule commented Oct 24, 2020

Hi, i'm wondering how to know the program is running, because when i compile it in command prompt, there are no errors but nothing happens either, so i'm not sure if i need to wait until it prints the results or if there is something wrong.

Compiling code translates it from a human readable language to a computer readable language. The compilation process outputs an executable file in that computer readable format -- using the provided command, the executable is named "seedsearch".

You still have to run the executable.

When the executable is run, it should print some status message every few seconds. Roughly speaking, a message is printed out for every 0.1% of progress.

@VioletVitto
Copy link

Hi, thank you for the amazing resource. I would like to ask you if you could explain me how to run this command. I have zero experience with c++, and I don't even know what program to use (I downloaded CUDA toolkit, but I have no idea on what to do). I know basically nothing about programming so I was wondering if you could direct me step-by-step on what I have to do to make it work.
Looking forward to your reply, best regards.

@syule
Copy link
Author

syule commented Nov 7, 2020

Hi, thank you for the amazing resource. I would like to ask you if you could explain me how to run this command. I have zero experience with c++, and I don't even know what program to use (I downloaded CUDA toolkit, but I have no idea on what to do). I know basically nothing about programming so I was wondering if you could direct me step-by-step on what I have to do to make it work.
Looking forward to your reply, best regards.

To be honest, this isn't very user friendly, because I'm lazy, and also don't know anything about {various compiler/linker/toolchain things} to put together a binary which "just works" for everyone. And I'm not really fit to provide support for various cuda library issues, because compiler/linker errors give me headaches too.

If you want something you can just run without trouble, there's better stuff out there, for example this just came up in my google search (i've not tried it) https://old.reddit.com/r/technicalminecraft/comments/8pmqq7/legerts_l64_fast_seed_cracker_reverse_seed_finding/
or https://github.com/pruby/slime-seed/tree/7d1d076de85336d99c2844161c0430cb1fda898b unless you're dead set on using ocean monuments for whatever reason, I suggest you try those

If you're still here for some reason, basically you have to you have to

  1. have an NVIDIA gpu
  2. install the cuda toolkit
  3. install the gcc/g++ compiler. on Linux. apparently windows has an equivalent thing called mingw, but I don't use windows so I don't know anything about it
  4. edit the flat_chunks variable until it contains the (x,z) chunk coordinates of some ocean monuments
  5. run the compilation command at the top of the file, possibly specifying a different compute architecture depending on your GPU
  6. solve any compiler errors, probably by fiddling around with LIBRARY_PATH until gcc finds CUDA
  7. run the executable

@VioletVitto
Copy link

Hi, thank you for the amazing resource. I would like to ask you if you could explain me how to run this command. I have zero experience with c++, and I don't even know what program to use (I downloaded CUDA toolkit, but I have no idea on what to do). I know basically nothing about programming so I was wondering if you could direct me step-by-step on what I have to do to make it work.
Looking forward to your reply, best regards.

To be honest, this isn't very user friendly, because I'm lazy, and also don't know anything about {various compiler/linker/toolchain things} to put together a binary which "just works" for everyone. And I'm not really fit to provide support for various cuda library issues, because compiler/linker errors give me headaches too.

If you want something you can just run without trouble, there's better stuff out there, for example this just came up in my google search (i've not tried it) https://old.reddit.com/r/technicalminecraft/comments/8pmqq7/legerts_l64_fast_seed_cracker_reverse_seed_finding/
or https://github.com/pruby/slime-seed/tree/7d1d076de85336d99c2844161c0430cb1fda898b unless you're dead set on using ocean monuments for whatever reason, I suggest you try those

If you're still here for some reason, basically you have to you have to

  1. have an NVIDIA gpu
  2. install the cuda toolkit
  3. install the gcc/g++ compiler. on Linux. apparently windows has an equivalent thing called mingw, but I don't use windows so I don't know anything about it
  4. edit the flat_chunks variable until it contains the (x,z) chunk coordinates of some ocean monuments
  5. run the compilation command at the top of the file, possibly specifying a different compute architecture depending on your GPU
  6. solve any compiler errors, probably by fiddling around with LIBRARY_PATH until gcc finds CUDA
  7. run the executable

Thank you, but I have Radeon gpu so I guess I'll have to try another method or maybe ask a friend what kind of gpu he has.
I'm really grateful for all the help and support you gave me! I'll let you know if I'll be able to use your code.

@seansibbet
Copy link

I've got the same problem as above. Running the command line produces no errors (in Windows 10), but no executable file appears in the folder I run the script from (I've got the cl.exe in the path, and run the command line from the same folder that the seedsearch.cpp file is in)

missing

@jh34ghu43gu
Copy link

Compiling and running is fine, however the actual result appears to be wrong? Def. doesn't work for 1.17 sadly because of terrain changes (I'm guessing), but even running the given chunks and going back to 1.13.1 (the version I assume this was made for) I cannot find temples at the first 2 given chunks (206,-39 and 235,-83). Candidate seed I was given was 109098491073356249 with the lower 48 bits being 167675086332377.
Should have figured it wasn't going to work for 1.17 oh well, server I'm trying to figure out the seed for has horrendous hostile mob rates so finding slime chunks to use in that other seed cracker is impossible, if anyone finds another landmark-based seed cracker let me know, thanks.

@syule
Copy link
Author

syule commented Aug 15, 2021

Should have figured it wasn't going to work for 1.17

I just tested, and it still works on 1.17.1 -- none of the terrain changes affect ocean monuments, with the exception that before, the tallest part of an ocean monument spawned in the center of a chunk, and now the tallest part is located at the north-west corner of the "center chunk". So, you should go to the highest part of the monument, then move a few blocks south east before reading off the chunk coordinates.

I just generated a world with the seed 2238026489121923591, lower 48 bits are 18949295497735, with the ocean monument coordinates

    const int flat_chunks[] = {
        77, 141,
        -73, -41,
        -77, -82,
        -58, -181,
        -239, -48,
        184, 78,
        169, 16,
    };

and I can confirm that it succeeds at finding the lower 48 bits, but the extension is wrong (I probably just got some math wrong). Depending on your use case, you might not care about the upper 16 bits, since they don't affect other slime chunks or other structure locations. If you do care, you could look at pruby's code for extending the first 16 bits, which is probably more correct.

I should warn that spigot/paper servers have an option for using a different seed for each structure type, in which case the seed derived from monuments might not be useful for searching for slime chunks, for instance. I don't know if this is the default spigot behavior.

@jh34ghu43gu
Copy link

jh34ghu43gu commented Aug 16, 2021

Ah, thanks for the reply I will look into it again later.

Edit: It does work 👍 SE corner of them temple is correct if you have the chunk borders open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment