mirror of
https://github.com/Cubitect/cubiomes.git
synced 2025-01-07 03:06:38 +08:00
675 lines
16 KiB
C
675 lines
16 KiB
C
#include "quadbase.h"
|
|
#include "util.h"
|
|
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
#include <windows.h>
|
|
typedef HANDLE thread_id_t;
|
|
#include <direct.h>
|
|
#define IS_DIR_SEP(C) ((C) == '/' || (C) == '\\')
|
|
#define stat _stat
|
|
#define mkdir(P,X) _mkdir(P)
|
|
#define S_IFDIR _S_IFDIR
|
|
|
|
#ifndef _S_ISTYPE
|
|
#define _S_ISTYPE(mode, mask) (((mode) & _S_IFMT) == (mask))
|
|
#endif
|
|
|
|
#ifndef S_ISDIR
|
|
#define S_ISDIR(mode) _S_ISTYPE((mode), _S_IFDIR)
|
|
#endif
|
|
|
|
#else
|
|
|
|
#define USE_PTHREAD
|
|
#include <pthread.h>
|
|
typedef pthread_t thread_id_t;
|
|
#define IS_DIR_SEP(C) ((C) == '/')
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//==============================================================================
|
|
// Multi-Structure Checks
|
|
//==============================================================================
|
|
|
|
int getQuadHutCst(uint64_t low20)
|
|
{
|
|
const uint64_t *cst;
|
|
for (cst = low20QuadIdeal; *cst; cst++)
|
|
if (*cst == low20)
|
|
return CST_IDEAL;
|
|
for (cst = low20QuadClassic; *cst; cst++)
|
|
if (*cst == low20)
|
|
return CST_CLASSIC;
|
|
for (cst = low20QuadHutNormal; *cst; cst++)
|
|
if (*cst == low20)
|
|
return CST_NORMAL;
|
|
for (cst = low20QuadHutBarely; *cst; cst++)
|
|
if (*cst == low20)
|
|
return CST_BARELY;
|
|
return CST_NONE;
|
|
}
|
|
|
|
// TODO: accurate seed testers for two or three structures in range
|
|
|
|
|
|
static int blocksInRange(Pos *p, int n, int x, int z, int ax, int az, double rsq)
|
|
{
|
|
int i, cnt;
|
|
|
|
cnt = 0;
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
double dx = p[i].x - x;
|
|
double dz = p[i].z - z;
|
|
int px, pz;
|
|
|
|
for (px = 0; px < ax; px++)
|
|
{
|
|
for (pz = 0; pz < az; pz++)
|
|
{
|
|
double ddx = px + dx;
|
|
double ddz = pz + dz;
|
|
cnt += (ddx*ddx + ddz*ddz <= rsq);
|
|
}
|
|
}
|
|
}
|
|
return cnt;
|
|
}
|
|
|
|
STRUCT(afk_meta_t)
|
|
{
|
|
Pos *p;
|
|
int n;
|
|
int *buf;
|
|
int x0, z0, w, h, ax, az;
|
|
double rsq;
|
|
int best;
|
|
int sumn;
|
|
int64_t sumx, sumz;
|
|
};
|
|
|
|
static void checkAfkDist(afk_meta_t *d, int x, int z)
|
|
{
|
|
if (x < 0 || z < 0 || x >= d->w || z >= d->h)
|
|
return;
|
|
if (d->buf[z*d->w+x])
|
|
return;
|
|
|
|
int q = blocksInRange(d->p, d->n, x+d->x0, z+d->z0, d->ax, d->az, d->rsq);
|
|
d->buf[z*d->w+x] = q;
|
|
if (q >= d->best)
|
|
{
|
|
if (q > d->best)
|
|
{
|
|
d->best = q;
|
|
d->sumn = 1;
|
|
d->sumx = d->x0+x;
|
|
d->sumz = d->z0+z;
|
|
}
|
|
else
|
|
{
|
|
d->sumn += 1;
|
|
d->sumx += d->x0+x;
|
|
d->sumz += d->z0+z;
|
|
}
|
|
checkAfkDist(d, x, z-1);
|
|
checkAfkDist(d, x, z+1);
|
|
checkAfkDist(d, x-1, z);
|
|
checkAfkDist(d, x+1, z);
|
|
checkAfkDist(d, x-1, z-1);
|
|
checkAfkDist(d, x-1, z+1);
|
|
checkAfkDist(d, x+1, z-1);
|
|
checkAfkDist(d, x+1, z+1);
|
|
}
|
|
}
|
|
|
|
|
|
Pos getOptimalAfk(Pos p[4], int ax, int ay, int az, int *spcnt)
|
|
{
|
|
int64_t minX = INT_MAX, minZ = INT_MAX, maxX = INT_MIN, maxZ = INT_MIN;
|
|
int64_t w, h, i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (p[i].x < minX) minX = p[i].x;
|
|
if (p[i].z < minZ) minZ = p[i].z;
|
|
if (p[i].x > maxX) maxX = p[i].x;
|
|
if (p[i].z > maxZ) maxZ = p[i].z;
|
|
}
|
|
|
|
minX += ax/2;
|
|
minZ += az/2;
|
|
maxX += ax/2;
|
|
maxZ += az/2;
|
|
|
|
double rsq = 128.0*128.0 - ay*ay/4.0;
|
|
|
|
w = maxX - minX;
|
|
h = maxZ - minZ;
|
|
Pos afk = {p[0].x + ax / 2, p[0].z + az / 2};
|
|
int cnt = ax*az;
|
|
|
|
afk_meta_t d;
|
|
d.p = p;
|
|
d.n = 4;
|
|
d.buf = (int*) calloc(w*h, sizeof(int));
|
|
d.x0 = minX;
|
|
d.z0 = minZ;
|
|
d.w = w;
|
|
d.h = h;
|
|
d.ax = ax;
|
|
d.az = az;
|
|
d.rsq = rsq;
|
|
|
|
int v[6];
|
|
Pos dsp[6] = {
|
|
{(p[0].x + p[2].x) / 2, (p[0].z + p[2].z) / 2},
|
|
{(p[1].x + p[3].x) / 2, (p[1].z + p[3].z) / 2},
|
|
{(p[0].x + p[1].x) / 2, (p[0].z + p[1].z) / 2},
|
|
{(p[2].x + p[3].x) / 2, (p[2].z + p[3].z) / 2},
|
|
{(p[0].x + p[3].x) / 2, (p[0].z + p[3].z) / 2},
|
|
{(p[1].x + p[2].x) / 2, (p[1].z + p[2].z) / 2},
|
|
};
|
|
for (i = 0; i < 6; i++)
|
|
v[i] = blocksInRange(p, 4, dsp[i].x, dsp[i].z, ax, az, rsq);
|
|
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
// pick out the highest
|
|
int j, jmax = 0, vmax = 0;
|
|
for (j = 0; j < 6; j++)
|
|
{
|
|
if (v[j] > vmax)
|
|
{
|
|
jmax = j;
|
|
vmax = v[j];
|
|
}
|
|
}
|
|
if (vmax <= ax*az) // highest is less or equal to a single structure
|
|
break;
|
|
|
|
d.best = vmax;
|
|
d.sumn = 0;
|
|
d.sumx = 0;
|
|
d.sumz = 0;
|
|
checkAfkDist(&d, dsp[jmax].x - d.x0, dsp[jmax].z - d.z0);
|
|
if (d.best > cnt)
|
|
{
|
|
cnt = d.best;
|
|
afk.x = (int) round(d.sumx / (double)d.sumn);
|
|
afk.z = (int) round(d.sumz / (double)d.sumn);
|
|
if (cnt >= 3*ax*az)
|
|
break;
|
|
}
|
|
v[jmax] = 0;
|
|
}
|
|
|
|
if (spcnt)
|
|
*spcnt = cnt;
|
|
|
|
free(d.buf);
|
|
|
|
return afk;
|
|
}
|
|
|
|
|
|
#define MAX_PATHLEN 4096
|
|
|
|
STRUCT(linked_seeds_t)
|
|
{
|
|
uint64_t seeds[100];
|
|
size_t len;
|
|
linked_seeds_t *next;
|
|
};
|
|
|
|
STRUCT(threadinfo_t)
|
|
{
|
|
// seed range
|
|
uint64_t start, end;
|
|
const uint64_t *lowBits;
|
|
int lowBitN;
|
|
char skipStart;
|
|
|
|
// testing function
|
|
int (*check)(uint64_t, void*);
|
|
void *data;
|
|
|
|
// abort check
|
|
volatile char *stop;
|
|
|
|
// output
|
|
char path[MAX_PATHLEN];
|
|
FILE *fp;
|
|
linked_seeds_t ls;
|
|
};
|
|
|
|
|
|
static int mkdirp(char *path)
|
|
{
|
|
int err = 0, len = strlen(path);
|
|
char *p = path;
|
|
|
|
#if defined(_WIN32)
|
|
if (p[1] == ':') p += 2;
|
|
#endif
|
|
while (IS_DIR_SEP(*p)) p++;
|
|
|
|
while (!err && p < path+len)
|
|
{
|
|
char *q = p;
|
|
while (*q && !IS_DIR_SEP(*q))
|
|
q++;
|
|
|
|
if (p != path) p[-1] = '/';
|
|
*q = 0;
|
|
|
|
struct stat st;
|
|
if (stat(path, &st) == -1)
|
|
err = mkdir(path, 0755);
|
|
else if (!S_ISDIR(st.st_mode))
|
|
err = 1;
|
|
|
|
p = q+1;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
#ifdef USE_PTHREAD
|
|
static void *searchAll48Thread(void *data)
|
|
#else
|
|
static DWORD WINAPI searchAll48Thread(LPVOID data)
|
|
#endif
|
|
{
|
|
// TODO TEST:
|
|
// lower bits with various ranges
|
|
|
|
threadinfo_t *info = (threadinfo_t*)data;
|
|
|
|
uint64_t seed = info->start;
|
|
uint64_t end = info->end;
|
|
linked_seeds_t *lp = &info->ls;
|
|
lp->len = 0;
|
|
lp->next = NULL;
|
|
|
|
if (info->lowBits)
|
|
{
|
|
uint64_t hstep = 1ULL << info->lowBitN;
|
|
uint64_t hmask = ~(hstep - 1);
|
|
uint64_t mid;
|
|
int idx, cnt;
|
|
|
|
for (cnt = 0; info->lowBits[cnt]; cnt++);
|
|
|
|
mid = info->start & hmask;
|
|
for (idx = 0; (seed = mid | info->lowBits[idx]) < info->start; idx++);
|
|
|
|
while (seed <= end)
|
|
{
|
|
if unlikely(info->check(seed, info->data))
|
|
{
|
|
if (seed == info->start && info->skipStart) {} // skip
|
|
else if (info->fp)
|
|
{
|
|
fprintf(info->fp, "%" PRId64"\n", (int64_t)seed);
|
|
fflush(info->fp);
|
|
}
|
|
else
|
|
{
|
|
lp->seeds[lp->len] = seed;
|
|
lp->len++;
|
|
if (lp->len >= sizeof(lp->seeds)/sizeof(uint64_t))
|
|
{
|
|
linked_seeds_t *n =
|
|
(linked_seeds_t*) malloc(sizeof(linked_seeds_t));
|
|
if (n == NULL)
|
|
exit(1);
|
|
lp->next = n;
|
|
lp = n;
|
|
lp->len = 0;
|
|
lp->next = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
idx++;
|
|
if (idx >= cnt)
|
|
{
|
|
idx = 0;
|
|
mid += hstep;
|
|
if (info->stop && *info->stop)
|
|
break;
|
|
}
|
|
|
|
seed = mid | info->lowBits[idx];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (seed <= end)
|
|
{
|
|
if unlikely(info->check(seed, info->data))
|
|
{
|
|
if (seed == info->start && info->skipStart) {} // skip
|
|
else if (info->fp)
|
|
{
|
|
fprintf(info->fp, "%" PRId64"\n", (int64_t)seed);
|
|
fflush(info->fp);
|
|
}
|
|
else
|
|
{
|
|
lp->seeds[lp->len] = seed;
|
|
lp->len++;
|
|
if (lp->len >= sizeof(lp->seeds)/sizeof(uint64_t))
|
|
{
|
|
linked_seeds_t *n =
|
|
(linked_seeds_t*) malloc(sizeof(linked_seeds_t));
|
|
if (n == NULL)
|
|
exit(1);
|
|
lp->next = n;
|
|
lp = n;
|
|
lp->len = 0;
|
|
lp->next = NULL;
|
|
}
|
|
}
|
|
}
|
|
seed++;
|
|
if ((seed & 0xfff) == 0 && info->stop && *info->stop)
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef USE_PTHREAD
|
|
pthread_exit(NULL);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
int searchAll48(
|
|
uint64_t ** seedbuf,
|
|
uint64_t * buflen,
|
|
const char * path,
|
|
int threads,
|
|
const uint64_t * lowBits,
|
|
int lowBitN,
|
|
int (*check)(uint64_t s48, void *data),
|
|
void * data,
|
|
volatile char * stop
|
|
)
|
|
{
|
|
threadinfo_t *info = (threadinfo_t*) malloc(threads* sizeof(*info));
|
|
thread_id_t *tids = (thread_id_t*) malloc(threads* sizeof(*tids));
|
|
int i, t;
|
|
int err = 0;
|
|
|
|
if (path)
|
|
{
|
|
size_t pathlen = strlen(path);
|
|
char dpath[MAX_PATHLEN];
|
|
|
|
// split path into directory and file and create missing directories
|
|
if (pathlen + 8 >= sizeof(dpath))
|
|
goto L_err;
|
|
strcpy(dpath, path);
|
|
|
|
for (i = pathlen-1; i >= 0; i--)
|
|
{
|
|
if (IS_DIR_SEP(dpath[i]))
|
|
{
|
|
dpath[i] = 0;
|
|
if (mkdirp(dpath))
|
|
goto L_err;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (seedbuf == NULL || buflen == NULL)
|
|
{
|
|
// no file and no buffer return: no output possible
|
|
goto L_err;
|
|
}
|
|
|
|
// prepare the thread info and load progress if present
|
|
for (t = 0; t < threads; t++)
|
|
{
|
|
info[t].start = (t * (MASK48+1) / threads);
|
|
info[t].end = ((t+1) * (MASK48+1) / threads - 1);
|
|
info[t].lowBits = lowBits;
|
|
info[t].lowBitN = lowBitN;
|
|
info[t].skipStart = 0;
|
|
info[t].check = check;
|
|
info[t].data = data;
|
|
info[t].stop = stop;
|
|
|
|
if (path)
|
|
{
|
|
// progress file of this thread
|
|
snprintf(info[t].path, sizeof(info[t].path), "%s.part%d", path, t);
|
|
FILE *fp = fopen(info[t].path, "a+");
|
|
if (fp == NULL)
|
|
goto L_err;
|
|
|
|
int c, nnl = 0;
|
|
char buf[32];
|
|
|
|
// find the last newline
|
|
for (i = 1; i < 32; i++)
|
|
{
|
|
if (fseek(fp, -i, SEEK_END)) break;
|
|
c = fgetc(fp);
|
|
if (c <= 0 || (nnl && c == '\n')) break;
|
|
nnl |= (c != '\n');
|
|
}
|
|
|
|
if (i < 32 && !fseek(fp, 1-i, SEEK_END) && fread(buf, i-1, 1, fp) > 0)
|
|
{
|
|
// read the last entry, and replace the start seed accordingly
|
|
int64_t lentry;
|
|
if (sscanf(buf, "%" PRId64, &lentry) == 1)
|
|
{
|
|
info[t].start = lentry;
|
|
info[t].skipStart = 1;
|
|
printf("Continuing thread %d at seed %" PRId64 "\n",
|
|
t, lentry);
|
|
}
|
|
}
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
info[t].fp = fp;
|
|
}
|
|
else
|
|
{
|
|
info[t].path[0] = 0;
|
|
info[t].fp = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
// run the threads
|
|
#ifdef USE_PTHREAD
|
|
|
|
for (t = 0; t < threads; t++)
|
|
{
|
|
pthread_create(&tids[t], NULL, searchAll48Thread, (void*)&info[t]);
|
|
}
|
|
|
|
for (t = 0; t < threads; t++)
|
|
{
|
|
pthread_join(tids[t], NULL);
|
|
}
|
|
|
|
#else
|
|
|
|
for (t = 0; t < threads; t++)
|
|
{
|
|
tids[t] = CreateThread(NULL, 0, searchAll48Thread,
|
|
(LPVOID)&info[t], 0, NULL);
|
|
}
|
|
|
|
WaitForMultipleObjects(threads, tids, TRUE, INFINITE);
|
|
|
|
#endif
|
|
|
|
if (stop && *stop)
|
|
goto L_err;
|
|
|
|
if (path)
|
|
{
|
|
// merge partial files
|
|
FILE *fp = fopen(path, "w");
|
|
if (fp == NULL)
|
|
goto L_err;
|
|
|
|
for (t = 0; t < threads; t++)
|
|
{
|
|
rewind(info[t].fp);
|
|
|
|
char buffer[4097];
|
|
size_t n;
|
|
while ((n = fread(buffer, sizeof(char), 4096, info[t].fp)))
|
|
{
|
|
if (!fwrite(buffer, sizeof(char), n, fp))
|
|
{
|
|
fclose(fp);
|
|
goto L_err;
|
|
}
|
|
}
|
|
|
|
fclose(info[t].fp);
|
|
remove(info[t].path);
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
if (seedbuf && buflen)
|
|
{
|
|
*seedbuf = loadSavedSeeds(path, buflen);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// merge linked seed buffers
|
|
*buflen = 0;
|
|
|
|
for (t = 0; t < threads; t++)
|
|
{
|
|
linked_seeds_t *lp = &info[t].ls;
|
|
do
|
|
{
|
|
*buflen += lp->len;
|
|
lp = lp->next;
|
|
}
|
|
while (lp);
|
|
}
|
|
|
|
*seedbuf = (uint64_t*) malloc((*buflen) * sizeof(uint64_t));
|
|
if (*seedbuf == NULL)
|
|
exit(1);
|
|
|
|
i = 0;
|
|
for (t = 0; t < threads; t++)
|
|
{
|
|
linked_seeds_t *lp = &info[t].ls;
|
|
do
|
|
{
|
|
memcpy(*seedbuf + i, lp->seeds, lp->len * sizeof(uint64_t));
|
|
i += lp->len;
|
|
linked_seeds_t *tmp = lp;
|
|
lp = lp->next;
|
|
if (tmp != &info[t].ls)
|
|
free(tmp);
|
|
}
|
|
while (lp);
|
|
}
|
|
}
|
|
|
|
if (0)
|
|
L_err:
|
|
err = 1;
|
|
|
|
free(tids);
|
|
free(info);
|
|
|
|
return err;
|
|
}
|
|
|
|
static inline
|
|
int scanForQuadBits(const StructureConfig sconf, int radius, uint64_t s48,
|
|
uint64_t lbit, int lbitn, uint64_t invB, int64_t x, int64_t z,
|
|
int64_t w, int64_t h, Pos *qplist, int n)
|
|
{
|
|
const uint64_t m = (1ULL << lbitn);
|
|
const uint64_t A = 341873128712ULL;
|
|
// for lbitn=20: invB = 132477LL;
|
|
|
|
if (n < 1)
|
|
return 0;
|
|
lbit &= m-1;
|
|
|
|
int64_t i, j;
|
|
int cnt = 0;
|
|
for (i = x; i <= x+w; i++)
|
|
{
|
|
uint64_t sx = s48 + A * i;
|
|
j = (z & ~(m-1)) | ((lbit - sx) * invB & (m-1));
|
|
if (j < z)
|
|
j += m;
|
|
for (; j <= z+h; j += m)
|
|
{
|
|
uint64_t sp = moveStructure(s48, -i, -j);
|
|
if ((sp & (m-1)) != lbit)
|
|
continue;
|
|
|
|
if (isQuadBase(sconf, sp, radius))
|
|
{
|
|
qplist[cnt].x = i;
|
|
qplist[cnt].z = j;
|
|
cnt++;
|
|
if (cnt >= n)
|
|
return cnt;
|
|
}
|
|
}
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
int scanForQuads(
|
|
const StructureConfig sconf, int radius, uint64_t s48,
|
|
const uint64_t *lowBits, int lowBitN, uint64_t salt,
|
|
int x, int z, int w, int h, Pos *qplist, int n)
|
|
{
|
|
int i, cnt = 0;
|
|
uint64_t invB;
|
|
if (lowBitN == 20)
|
|
invB = 132477ULL;
|
|
else if (lowBitN == 48)
|
|
invB = 211541297333629ULL;
|
|
else
|
|
invB = mulInv(132897987541ULL, (1ULL << lowBitN));
|
|
|
|
for (i = 0; lowBits[i]; i++)
|
|
{
|
|
cnt += scanForQuadBits(sconf, radius, s48, lowBits[i]-salt, lowBitN, invB,
|
|
x, z, w, h, qplist+cnt, n-cnt);
|
|
if (cnt >= n)
|
|
break;
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
|