Skip to content

Instantly share code, notes, and snippets.

@syule
Last active January 4, 2024 22:07
Show Gist options
  • 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;
}
@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