chore: archive early implementations and chunk checker tools

This commit is contained in:
Yaossg 2026-10-11 12:29:56 +08:00
parent 7a06f94577
commit 69f7f7a34b
9 changed files with 1408 additions and 0 deletions

403
codex/slime_cpu.cpp Normal file
View file

@ -0,0 +1,403 @@
#include <algorithm>
#include <atomic>
#include <chrono>
#include <climits>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <mutex>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
#if defined(_MSC_VER)
#include <intrin.h>
#endif
namespace {
constexpr uint64_t kJavaMultiplier = 0x5DEECE66DULL;
constexpr uint64_t kJavaAddend = 0xBULL;
constexpr uint64_t kJavaMask = (1ULL << 48) - 1;
constexpr uint64_t kSlimeXor = 987234911ULL;
struct Args {
int64_t startSeed = 0;
int64_t endSeed = 1'000'000;
int32_t n = 3;
int32_t required = -1;
int32_t radius = 100;
int32_t threads = std::max(1u, std::thread::hardware_concurrency());
int64_t seedBatch = 256;
int32_t maxResults = 32;
bool allLocations = false;
};
struct Hit {
int64_t seed;
int32_t x;
int32_t z;
int32_t count;
};
[[noreturn]] void usage(int exitCode = 1) {
std::cerr
<< "Usage: slime_cpu [args...]\n"
<< " -s <startSeed> First world seed, inclusive (default: 0)\n"
<< " -e <endSeed> Last world seed, exclusive (default: 1000000)\n"
<< " -n <chunkSize> N for the NxN window (default: 3)\n"
<< " -d <numChunks> Required slime chunks in the window (default: N*N)\n"
<< " -r <radius> Search start positions from -r..r on both axes (default: 100)\n"
<< " -p <threads> CPU worker threads (default: hardware concurrency)\n"
<< " --batch <seeds> Seeds fetched per worker batch (default: 256)\n"
<< " --max-results <n> Print at most n hits, while still counting all hits (default: 32)\n"
<< " --all-locations Count every matching location, not just the first per seed\n"
<< " --help Show this help\n";
std::exit(exitCode);
}
const char* requireValue(int argc, char** argv, int& i) {
if (i + 1 >= argc) {
usage();
}
return argv[++i];
}
Args parseArgs(int argc, char** argv) {
Args args;
for (int i = 1; i < argc; ++i) {
const std::string arg = argv[i];
if (arg == "--help") {
usage(0);
} else if (arg == "-s") {
args.startSeed = std::stoll(requireValue(argc, argv, i));
} else if (arg == "-e") {
args.endSeed = std::stoll(requireValue(argc, argv, i));
} else if (arg == "-n") {
args.n = std::stoi(requireValue(argc, argv, i));
} else if (arg == "-d") {
args.required = std::stoi(requireValue(argc, argv, i));
} else if (arg == "-r") {
args.radius = std::stoi(requireValue(argc, argv, i));
} else if (arg == "-p") {
args.threads = std::stoi(requireValue(argc, argv, i));
} else if (arg == "--batch") {
args.seedBatch = std::stoll(requireValue(argc, argv, i));
} else if (arg == "--max-results") {
args.maxResults = std::stoi(requireValue(argc, argv, i));
} else if (arg == "--all-locations") {
args.allLocations = true;
} else {
usage();
}
}
const int64_t cells = int64_t(args.n) * args.n;
if (args.required < 0) {
args.required = static_cast<int32_t>(cells);
}
if (args.startSeed >= args.endSeed || args.n <= 0 || args.radius < 0 ||
args.threads <= 0 || args.seedBatch <= 0 || args.maxResults < 0 ||
cells > INT32_MAX || args.required <= 0 || args.required > cells) {
usage();
}
return args;
}
uint64_t javaIntAsLongBits(uint32_t value) {
return (value & 0x80000000u) ? (0xFFFFFFFF00000000ULL | value) : uint64_t(value);
}
uint64_t coordTerm(int32_t chunkX, int32_t chunkZ) {
const uint32_t x = static_cast<uint32_t>(chunkX);
const uint32_t z = static_cast<uint32_t>(chunkZ);
uint64_t term = 0;
term += javaIntAsLongBits(x * x * 4987142u);
term += javaIntAsLongBits(x * 5947611u);
term += javaIntAsLongBits(z * z) * 4392871ULL;
term += javaIntAsLongBits(z * 389711u);
return term;
}
bool isSlimeFromTerm(uint64_t worldSeedBits, uint64_t term) {
uint64_t randomSeed = ((worldSeedBits + term) ^ kSlimeXor ^ kJavaMultiplier) & kJavaMask;
for (;;) {
randomSeed = (randomSeed * kJavaMultiplier + kJavaAddend) & kJavaMask;
const uint32_t bits = static_cast<uint32_t>(randomSeed >> 17);
const uint32_t value = bits % 10u;
if (bits - value + 9u <= 0x7FFFFFFFu) {
return value == 0;
}
}
}
int trailingZeroes(uint64_t value) {
#if defined(_MSC_VER)
unsigned long index = 0;
_BitScanForward64(&index, value);
return static_cast<int>(index);
#else
return __builtin_ctzll(value);
#endif
}
uint64_t lowBitsMask(int32_t bits) {
const int32_t rem = bits & 63;
return rem == 0 ? ~0ULL : ((1ULL << rem) - 1ULL);
}
struct SearchSpace {
int32_t n;
int32_t radius;
int32_t window;
int32_t chunkWidth;
int32_t chunkRows;
int32_t chunkWords;
int32_t windowWords;
uint64_t lastWindowMask;
std::vector<uint64_t> terms;
SearchSpace(int32_t radiusIn, int32_t nIn)
: n(nIn),
radius(radiusIn),
window(radiusIn * 2 + 1),
chunkWidth(window + nIn - 1),
chunkRows(window + nIn - 1),
chunkWords((chunkWidth + 63) / 64),
windowWords((window + 63) / 64),
lastWindowMask(lowBitsMask(window)),
terms(size_t(chunkWidth) * chunkRows) {
for (int32_t z = 0; z < chunkRows; ++z) {
for (int32_t x = 0; x < chunkWidth; ++x) {
terms[size_t(z) * chunkWidth + x] = coordTerm(x - radius, z - radius);
}
}
}
};
struct Workspace {
std::vector<uint64_t> rows;
std::vector<uint64_t> horizontal;
std::vector<uint8_t> rowValues;
std::vector<int32_t> rowRing;
std::vector<int32_t> vertical;
explicit Workspace(const SearchSpace& space)
: rows(size_t(space.chunkRows) * space.chunkWords),
horizontal(size_t(space.chunkRows) * space.chunkWords),
rowValues(space.chunkWidth),
rowRing(size_t(space.n) * space.window),
vertical(space.window) {}
};
struct ResultSink {
std::mutex outputMutex;
std::atomic<uint64_t> hits{0};
int32_t maxResults;
explicit ResultSink(int32_t maxResultsIn) : maxResults(maxResultsIn) {}
void record(const Hit& hit) {
const uint64_t index = hits.fetch_add(1, std::memory_order_relaxed);
if (index >= static_cast<uint64_t>(maxResults)) {
return;
}
std::lock_guard<std::mutex> lock(outputMutex);
std::cout << "hit seed=" << hit.seed
<< " x=" << hit.x
<< " z=" << hit.z
<< " count=" << hit.count << '\n';
}
};
uint64_t shiftedWord(const uint64_t* row, int32_t words, int32_t word, int32_t shift) {
const int32_t whole = shift >> 6;
const int32_t bits = shift & 63;
const int32_t src = word + whole;
const uint64_t lo = src < words ? row[src] : 0ULL;
if (bits == 0) {
return lo;
}
const uint64_t hi = src + 1 < words ? row[src + 1] : 0ULL;
return (lo >> bits) | (hi << (64 - bits));
}
uint64_t findFullSquare(
int64_t seed,
const SearchSpace& space,
Workspace& workspace,
ResultSink& sink,
bool allLocations
) {
const uint64_t seedBits = static_cast<uint64_t>(seed);
std::fill(workspace.rows.begin(), workspace.rows.end(), 0ULL);
for (int32_t z = 0; z < space.chunkRows; ++z) {
uint64_t* row = workspace.rows.data() + size_t(z) * space.chunkWords;
const uint64_t* termRow = space.terms.data() + size_t(z) * space.chunkWidth;
for (int32_t x = 0; x < space.chunkWidth; ++x) {
if (isSlimeFromTerm(seedBits, termRow[x])) {
row[x >> 6] |= 1ULL << (x & 63);
}
}
}
for (int32_t z = 0; z < space.chunkRows; ++z) {
const uint64_t* row = workspace.rows.data() + size_t(z) * space.chunkWords;
uint64_t* out = workspace.horizontal.data() + size_t(z) * space.chunkWords;
for (int32_t word = 0; word < space.chunkWords; ++word) {
uint64_t mask = row[word];
for (int32_t shift = 1; shift < space.n; ++shift) {
mask &= shiftedWord(row, space.chunkWords, word, shift);
}
out[word] = mask;
}
}
uint64_t found = 0;
for (int32_t z = 0; z < space.window; ++z) {
for (int32_t word = 0; word < space.windowWords; ++word) {
uint64_t mask = ~0ULL;
for (int32_t dz = 0; dz < space.n; ++dz) {
mask &= workspace.horizontal[(size_t(z + dz) * space.chunkWords) + word];
}
if (word == space.windowWords - 1) {
mask &= space.lastWindowMask;
}
while (mask != 0) {
const int32_t bit = trailingZeroes(mask);
const int32_t x = word * 64 + bit;
sink.record({seed, x - space.radius, z - space.radius, space.n * space.n});
++found;
if (!allLocations) {
return found;
}
mask &= mask - 1;
}
}
}
return found;
}
uint64_t findAtLeastCount(
int64_t seed,
int32_t required,
const SearchSpace& space,
Workspace& workspace,
ResultSink& sink,
bool allLocations
) {
const uint64_t seedBits = static_cast<uint64_t>(seed);
std::fill(workspace.vertical.begin(), workspace.vertical.end(), 0);
uint64_t found = 0;
for (int32_t z = 0; z < space.chunkRows; ++z) {
const uint64_t* termRow = space.terms.data() + size_t(z) * space.chunkWidth;
for (int32_t x = 0; x < space.chunkWidth; ++x) {
workspace.rowValues[x] = isSlimeFromTerm(seedBits, termRow[x]) ? 1 : 0;
}
int32_t* ringRow = workspace.rowRing.data() + size_t(z % space.n) * space.window;
if (z >= space.n) {
for (int32_t x = 0; x < space.window; ++x) {
workspace.vertical[x] -= ringRow[x];
}
}
int32_t rowSum = 0;
for (int32_t x = 0; x < space.n; ++x) {
rowSum += workspace.rowValues[x];
}
for (int32_t x = 0; x < space.window; ++x) {
if (x != 0) {
rowSum += workspace.rowValues[x + space.n - 1];
rowSum -= workspace.rowValues[x - 1];
}
ringRow[x] = rowSum;
workspace.vertical[x] += rowSum;
}
if (z + 1 < space.n) {
continue;
}
const int32_t startZ = z - space.n + 1;
for (int32_t x = 0; x < space.window; ++x) {
const int32_t count = workspace.vertical[x];
if (count >= required) {
sink.record({seed, x - space.radius, startZ - space.radius, count});
++found;
if (!allLocations) {
return found;
}
}
}
}
return found;
}
void worker(
const Args& args,
const SearchSpace& space,
std::atomic<int64_t>& nextSeed,
ResultSink& sink
) {
Workspace workspace(space);
for (;;) {
const int64_t begin = nextSeed.fetch_add(args.seedBatch, std::memory_order_relaxed);
if (begin >= args.endSeed) {
return;
}
const int64_t end = std::min(args.endSeed, begin + args.seedBatch);
for (int64_t seed = begin; seed < end; ++seed) {
if (args.required == args.n * args.n) {
findFullSquare(seed, space, workspace, sink, args.allLocations);
} else {
findAtLeastCount(seed, args.required, space, workspace, sink, args.allLocations);
}
}
}
}
} // namespace
int main(int argc, char** argv) {
try {
const Args args = parseArgs(argc, argv);
const SearchSpace space(args.radius, args.n);
ResultSink sink(args.maxResults);
std::atomic<int64_t> nextSeed(args.startSeed);
std::cout << "CPU search: seeds [" << args.startSeed << ", " << args.endSeed
<< "), N=" << args.n << ", required=" << args.required
<< ", radius=" << args.radius << ", threads=" << args.threads
<< (args.allLocations ? ", all locations\n" : ", first location per seed\n");
const auto started = std::chrono::steady_clock::now();
std::vector<std::thread> workers;
workers.reserve(args.threads);
for (int32_t i = 0; i < args.threads; ++i) {
workers.emplace_back(worker, std::cref(args), std::cref(space), std::ref(nextSeed), std::ref(sink));
}
for (std::thread& thread : workers) {
thread.join();
}
const auto finished = std::chrono::steady_clock::now();
const std::chrono::duration<double> elapsed = finished - started;
std::cout << "checked " << (args.endSeed - args.startSeed)
<< " seeds, hits=" << sink.hits.load(std::memory_order_relaxed)
<< ", elapsed=" << elapsed.count() << "s\n";
return 0;
} catch (const std::exception& ex) {
std::cerr << "error: " << ex.what() << '\n';
return 1;
}
}