-
Notifications
You must be signed in to change notification settings - Fork 105
/
quadbase.h
523 lines (438 loc) · 18.8 KB
/
quadbase.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
#ifndef QUADBASE_H_
#define QUADBASE_H_
#include "finders.h"
#include <stdio.h>
#include <math.h>
#ifdef __cplusplus
extern "C"
{
#endif
/** Quad-Witch-Huts
*
* For a quad-structure, we mainly care about relative positioning, so we can
* get away with just checking the regions near the origin: (0,0),(0,1),(1,0)
* and (1,1) and then move the structures to the desired position.
*
* Futhermore, the PRNG that determines the chunk positions inside each region,
* performs some modular arithmatic on the 48-bit numbers which causes some
* restrictions on the lower bits when looking for near perfect structure
* positions. This is difficult to prove, but can be used to reduce the number
* of free bits to 28 which can be comfortably brute-forced to get the entire
* set of quad-structure candidates. Each of the seeds found this way
* describes entire set of possible quad-witch-huts (with degrees of freedom
* for region-transposition, as well as the top 16-bit bits).
*/
// lower 20 bits, only the very best constellations
// (the structure salt has to be subtracted before use)
static const uint64_t low20QuadIdeal[] =
{
0x43f18,0xc751a,0xf520a, 0
};
// lower 20 bits, the classic quad-structure constellations
static const uint64_t low20QuadClassic[] =
{
0x43f18,0x79a0a,0xc751a,0xf520a, 0
};
// for any valid quad-structure constellation with a structure size:
// (7+1,7+43+1,9+1) which corresponds to a fall-damage based quad-witch-farm,
// but may require a perfect player position
static const uint64_t low20QuadHutNormal[] =
{
0x43f18,0x65118,0x75618,0x79a0a, 0x89718,0x9371a,0xa5a08,0xb5e18,
0xc751a,0xf520a, 0
};
// for any valid quad-structure constellation with a structure size:
// (7+1,7+1,9+1) which corresponds to quad-witch-farms without drop chute
static const uint64_t low20QuadHutBarely[] =
{
0x1272d,0x17908,0x367b9,0x43f18, 0x487c9,0x487ce,0x50aa7,0x647b5,
0x65118,0x75618,0x79a0a,0x89718, 0x9371a,0x967ec,0xa3d0a,0xa5918,
0xa591d,0xa5a08,0xb5e18,0xc6749, 0xc6d9a,0xc751a,0xd7108,0xd717a,
0xe2739,0xe9918,0xee1c4,0xf520a, 0
};
// categorize a constellation
enum { CST_NONE, CST_IDEAL, CST_CLASSIC, CST_NORMAL, CST_BARELY };
int getQuadHutCst(uint64_t low20);
//==============================================================================
// Multi-Structure-Base Checks
//==============================================================================
/* This function determines if the lower 48-bits of a seed qualify as a
* quad-base. This implies that the four structures in the adjacent regions
* (0,0)-(1,1) will attempt to generate close enough together to be within the
* specified block radius of a single block position. The quad-structure can be
* moved to a different location by applying moveStructure() to the quad-base.
* The upper 16 bits of the seed can be chosen freely, as they do not affect
* structure positions.
*
* This function is a wrapper for more specific filtering functions which can
* be found below. Using the correct quad-base finder directly can be faster as
* it is more likely to avoid code branching and offers more control over the
* quality of the structure positions.
*
* The return value is zero if the seed is not a quad-base, and equal to the
* radius of the enclosing sphere if it is, and can be used as a measure of
* quality for the quad-base (smaller is better).
*/
static inline float isQuadBase(const StructureConfig sconf, uint64_t seed, int radius);
/* Determines if the specified seed qualifies as a quad-base, given a required
* structure size. The structure size should include the actual dimensions of
* the structure and any additional size requirements where despawning shall
* not occur (such as fall damage drop chutes). A smaller size requirement can
* yield more seeds and relax constraints for other structure positions.
* (Since most structures use the same positioning algorithm with an offset,
* this also affects restrictions on the placement of other structure types.)
*
* The function variants are:
* isQuadBaseFeature24Classic() - finds only the classic constellations
* isQuadBaseFeature24() - optimisation for region=32,range=24,radius=128
* isQuadBaseFeature() - for small features (chunkRange not a power of 2)
* isQuadBaseLarge() - for large structures (chunkRange not a power of 2)
*
* The function returns the actual block radius to the furthest block inside
* any of the four structures or zero if the seed does not satisfy the
* quad-base requirements.
*
* @sconf : structure configuration
* @seed : world seed (only the lower 48-bits are relevant)
* @ax,ay,az : required structure size
* @radius : maximum radius for a sphere that encloses all four structures
*
* Implementation sidenote:
* Inline actually matters here, as these functions are not small and compilers
* generally don't want to inline them. However, these functions usually return
* so quickly that the function call is a major contributor to the overall time.
*/
static inline ATTR(always_inline, const)
float isQuadBaseFeature24Classic (const StructureConfig sconf, uint64_t seed);
static inline ATTR(always_inline, const)
float isQuadBaseFeature24 (const StructureConfig sconf, uint64_t seed,
int ax, int ay, int az);
static inline ATTR(always_inline, const)
float isQuadBaseFeature (const StructureConfig sconf, uint64_t seed,
int ax, int ay, int az, int radius);
static inline ATTR(always_inline, const)
float isQuadBaseLarge (const StructureConfig sconf, uint64_t seed,
int ax, int ay, int az, int radius);
/* Starts a multi-threaded search through all 48-bit seeds. Since this can
* potentially be a lengthy calculation, results can be written to temporary
* files immediately, in order to save progress in case of interruption. Seeds
* are tested using the function 'check' which takes a 48-bit seed and a custom
* 'data' argument. The output can be a dynamically allocated seed buffer
* and/or a destination file [which can be loaded using loadSavedSeeds()].
* Optionally, only a subset of the lower 20 bits are searched.
*
* @seedbuf output seed buffer (nullable for file only)
* @buflen length of output buffer (nullable)
* @path output file path (nullable, also toggles temporary files)
* @threads number of threads to use
* @lowBits lower bit subset (nullable)
* @lowBitN number of bits in the subset values
* @check the testing function, should return non-zero for desired seeds
* @data custom data argument passed to 'check'
* @stop occasional check for abort (nullable)
*
* Returns zero upon success.
*/
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 // should be atomic, but is fine as stop flag
);
/* Finds the optimal AFK location for four structures of size (ax,ay,az),
* located at the positions of 'p'. The AFK position is determined by looking
* for whole block coordinates which offer the maximum number of spawning
* spaces on the horizontal plane, which have the vertical structure height, ay,
* inside the enclosing sphere of radius 128 blocks. If there are multiple
* positions of this type (such as when all structures can be enclosed
* completly inside the sphere with some tollerance) then an average of those
* equally valid positions is returned.
*
* @p : positions of the structures
* @ax,ay,az : size of one structure
* @spcnt : output number of planar spawning spaces in reach (nullable)
*
* Returns an optimal block-coordinate to operate a farm.
*/
Pos getOptimalAfk(Pos p[4], int ax, int ay, int az, int *spcnt);
/* Scans the seed 's48' for quad-structures in the given area of region
* coordiantes. The search is performed for only a specific set of lower bits
* of the transformed bases (each constellation of quad-structures is
* considered separately).
*
* @sconf : structure config
* @radius : radius for isQuadBase (use 128 for quad-huts)
* @s48 : 48-bit seed to scan
* @lowBits : consider transformations that yield one of these lower bits
* @lowBitN : number of bits in the subset values (0 < lowBitN <= 48)
* @salt : salt subtracted from subset values (useful for protobases)
* @x,z,w,h : area to scan in region coordinates (inclusive)
* @qplist : output region coordinates for the descovered quad-structures
* @n : maximum number of quad-structures to look for
*<
* Returns the number of quad-structures found (up to 'n').
*/
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);
//==============================================================================
// Implementaions for Functions that Ideally Should be Inlined
//==============================================================================
static ATTR(const)
float getEnclosingRadius(
int x0, int z0, int x1, int z1, int x2, int z2, int x3, int z3,
int ax, int ay, int az, int reg, int gap)
{
// convert chunks to blocks
x0 = (x0 << 4);
z0 = (z0 << 4);
x1 = ((reg+x1) << 4) + ax;
z1 = ((reg+z1) << 4) + az;
x2 = ((reg+x2) << 4) + ax;
z2 = (z2 << 4);
x3 = (x3 << 4);
z3 = ((reg+z3) << 4) + az;
int sqrad = 0x7fffffff;
// build the inner rectangle containing the center point
int cbx0 = (x1 > x2 ? x1 : x2) - gap;
int cbz0 = (z1 > z3 ? z1 : z3) - gap;
int cbx1 = (x0 < x3 ? x0 : x3) + gap;
int cbz1 = (z0 < z2 ? z0 : z2) + gap;
int x, z;
// brute force the ideal center position
for (z = cbz0; z <= cbz1; z++)
{
for (x = cbx0; x <= cbx1; x++)
{
int sq = 0;
int s;
s = (x-x0)*(x-x0) + (z-z0)*(z-z0); if (s > sq) sq = s;
s = (x-x1)*(x-x1) + (z-z1)*(z-z1); if (s > sq) sq = s;
s = (x-x2)*(x-x2) + (z-z2)*(z-z2); if (s > sq) sq = s;
s = (x-x3)*(x-x3) + (z-z3)*(z-z3); if (s > sq) sq = s;
if (sq < sqrad)
sqrad = sq;
}
}
return sqrad < 0x7fffffff ? sqrtf(sqrad + ay*ay/4.0f) : 0xffff;
}
static inline float isQuadBase(const StructureConfig sconf, uint64_t seed, int radius)
{
switch(sconf.structType)
{
case Swamp_Hut:
if (radius == 128)
return isQuadBaseFeature24(sconf, seed, 7+1, 7+1, 9+1);//7+1, 7+43+1, 9+1);
else
return isQuadBaseFeature(sconf, seed, 7+1, 7+1, 9+1, radius);
case Desert_Pyramid:
case Jungle_Pyramid:
case Igloo:
case Village:
// nothing special spawns here, why would you want these?
if (radius == 128)
return isQuadBaseFeature24(sconf, seed, 0, 0, 0);
else
return isQuadBaseFeature(sconf, seed, 0, 0, 0, radius);
case Outpost:
// Outposts are tricky. They require an additional 1 in 5 PRNG pass to
// generate and no village nearby. Also perfect quad-outposts don't
// exist as they are too large, given that the generation point will
// always be 8 chunks apart. However, the watchtower can be offset to
// the generation attempt by a chunk or two (TODO: investivgate this!).
return isQuadBaseFeature(sconf, seed, 72, 54, 72, radius);
case Monument:
return isQuadBaseLarge(sconf, seed, 58, 23, 58, radius);
//case Mansion:
case Ocean_Ruin:
case Shipwreck:
case Ruined_Portal:
return isQuadBaseFeature(sconf, seed, 0, 0, 0, radius);
default:
fprintf(stderr, "isQuadBase: not implemented for structure type %d\n",
sconf.structType);
exit(-1);
}
return 0;
}
// optimised version for regionSize=32,chunkRange=24,radius=128
static inline ATTR(always_inline, const)
float isQuadBaseFeature24(const StructureConfig sconf, uint64_t seed,
int ax, int ay, int az)
{
seed += sconf.salt;
uint64_t s00 = seed;
uint64_t s11 = 341873128712ULL + 132897987541ULL + seed;
const uint64_t K = 0x5deece66dULL;
int x0, z0, x1, z1, x2, z2, x3, z3;
int x, z;
// check that the two structures in the opposing diagonal quadrants are
// close enough together
s00 ^= K;
JAVA_NEXT_INT24(s00, x0); if likely(x0 < 20) return 0;
JAVA_NEXT_INT24(s00, z0); if likely(z0 < 20) return 0;
s11 ^= K;
JAVA_NEXT_INT24(s11, x1); if likely(x1 > x0-20) return 0;
JAVA_NEXT_INT24(s11, z1); if likely(z1 > z0-20) return 0;
x = x1 + 32 - x0;
z = z1 + 32 - z0;
if (x*x + z*z > 255)
return 0;
uint64_t s01 = 341873128712ULL + seed;
uint64_t s10 = 132897987541ULL + seed;
s01 ^= K;
JAVA_NEXT_INT24(s01, x2); if likely(x2 >= 4) return 0;
JAVA_NEXT_INT24(s01, z2); if likely(z2 < 20) return 0;
s10 ^= K;
JAVA_NEXT_INT24(s10, x3); if likely(x3 < 20) return 0;
JAVA_NEXT_INT24(s10, z3); if likely(z3 >= 4) return 0;
x = x2 + 32 - x3;
z = z3 + 32 - z2;
if (x*x + z*z > 255)
return 0;
// only approx. 1 in 100M seeds makes it here, now we have to determine if
// there is a sphere, centered on a block, which is in range of all four
// structures
float sqrad = getEnclosingRadius(x0,z0,x1,z1,x2,z2,x3,z3,ax,ay,az,32,128);
return sqrad < 128 ? sqrad : 0;
}
// variant of isQuadBaseFeature24 which finds only the classic constellations
static inline ATTR(always_inline, const)
float isQuadBaseFeature24Classic(const StructureConfig sconf, uint64_t seed)
{
seed += sconf.salt;
uint64_t s00 = seed;
uint64_t s11 = 341873128712ULL + 132897987541ULL + seed;
const uint64_t K = 0x5deece66dULL;
int p;
// check that the two structures in the opposing diagonal quadrants are
// close enough together
s00 ^= K;
JAVA_NEXT_INT24(s00, p); if likely(p < 22) return 0;
JAVA_NEXT_INT24(s00, p); if likely(p < 22) return 0;
s11 ^= K;
JAVA_NEXT_INT24(s11, p); if likely(p > 1) return 0;
JAVA_NEXT_INT24(s11, p); if likely(p > 1) return 0;
uint64_t s01 = 341873128712ULL + seed;
uint64_t s10 = 132897987541ULL + seed;
s01 ^= K;
JAVA_NEXT_INT24(s01, p); if likely(p > 1) return 0;
JAVA_NEXT_INT24(s01, p); if likely(p < 22) return 0;
s10 ^= K;
JAVA_NEXT_INT24(s10, p); if likely(p < 22) return 0;
JAVA_NEXT_INT24(s10, p); if likely(p > 1) return 0;
return 1; // should actually return one of 122.781311 or 127.887650
}
static inline ATTR(always_inline, const)
float isQuadBaseFeature(const StructureConfig sconf, uint64_t seed,
int ax, int ay, int az, int radius)
{
seed += sconf.salt;
uint64_t s00 = seed;
uint64_t s11 = 341873128712ULL + 132897987541ULL + seed;
const uint64_t M = (1ULL << 48) - 1;
const uint64_t K = 0x5deece66dULL;
const uint64_t b = 0xb;
int x0, z0, x1, z1, x2, z2, x3, z3;
int x, z;
const int R = sconf.regionSize;
const int C = sconf.chunkRange;
int cd = radius/8;
int rm = R - (int)sqrtf(cd*cd - (R-C+1)*(R-C+1));
uint64_t s;
s = s00 ^ K;
s = (s * K + b) & M; x0 = (int)(s >> 17) % C; if likely(x0 <= rm) return 0;
s = (s * K + b) & M; z0 = (int)(s >> 17) % C; if likely(z0 <= rm) return 0;
s = s11 ^ K;
s = (s * K + b) & M; x1 = (int)(s >> 17) % C; if likely(x1 >= x0-rm) return 0;
s = (s * K + b) & M; z1 = (int)(s >> 17) % C; if likely(z1 >= z0-rm) return 0;
// check that the two structures in the opposing diagonal quadrants are
// close enough together
x = x1 + R - x0;
z = z1 + R - z0;
if likely(x*x + z*z > cd*cd)
return 0;
uint64_t s01 = 341873128712ULL + seed;
uint64_t s10 = 132897987541ULL + seed;
s = s01 ^ K;
s = (s * K + b) & M; x2 = (int)(s >> 17) % C; if likely(x2 >= C-rm) return 0;
s = (s * K + b) & M; z2 = (int)(s >> 17) % C; if likely(z2 <= rm) return 0;
s = s10 ^ K;
s = (s * K + b) & M; x3 = (int)(s >> 17) % C; if likely(x3 <= rm) return 0;
s = (s * K + b) & M; z3 = (int)(s >> 17) % C; if likely(z3 >= C-rm) return 0;
x = x2 + R - x3;
z = z3 + R - z2;
if likely(x*x + z*z > cd*cd)
return 0;
float sqrad = getEnclosingRadius(
x0,z0,x1,z1,x2,z2,x3,z3,ax,ay,az,sconf.regionSize,radius);
return sqrad < radius ? sqrad : 0;
}
static inline ATTR(always_inline, const)
float isQuadBaseLarge(const StructureConfig sconf, uint64_t seed,
int ax, int ay, int az, int radius)
{
// Good quad-monument bases are very rare indeed and the search takes much
// longer since it cannot be abbreviated by the low-20-bit method. For a
// complete list of bases see the implementation of cubiomes-viewer.
const uint64_t M = (1ULL << 48) - 1;
const uint64_t K = 0x5deece66dULL;
const uint64_t b = 0xb;
seed += sconf.salt;
uint64_t s00 = seed;
uint64_t s01 = 341873128712ULL + seed;
uint64_t s10 = 132897987541ULL + seed;
uint64_t s11 = 341873128712ULL + 132897987541ULL + seed;
// p1 = nextInt(range); p2 = nextInt(range); pos = (p1+p2)>>1
const int R = sconf.regionSize;
const int C = sconf.chunkRange;
int rm = (int)(2 * R + ((ax<az?ax:az) - 2*radius + 7) / 8);
uint64_t s;
int p;
int x0,z0,x1,z1,x2,z2,x3,z3;
s = s00 ^ K;
s = (s * K + b) & M; p = (int)(s >> 17) % C;
s = (s * K + b) & M; p += (int)(s >> 17) % C; if likely(p <= rm) return 0;
x0 = p;
s = (s * K + b) & M; p = (int)(s >> 17) % C;
s = (s * K + b) & M; p += (int)(s >> 17) % C; if likely(p <= rm) return 0;
z0 = p;
s = s11 ^ K;
s = (s * K + b) & M; p = (int)(s >> 17) % C;
s = (s * K + b) & M; p += (int)(s >> 17) % C; if likely(p > x0-rm) return 0;
x1 = p;
s = (s * K + b) & M; p = (int)(s >> 17) % C;
s = (s * K + b) & M; p += (int)(s >> 17) % C; if likely(p > z0-rm) return 0;
z1 = p;
s = ((x1-x0)>>1)*((x1-x0)>>1) + ((z1-z0)>>1)*((z1-z0)>>1);
if (s > (uint64_t)4*radius*radius)
return 0;
s = s01 ^ K;
s = (s * K + b) & M; p = (int)(s >> 17) % C;
s = (s * K + b) & M; p += (int)(s >> 17) % C; if likely(p > x0-rm) return 0;
x2 = p;
s = (s * K + b) & M; p = (int)(s >> 17) % C;
s = (s * K + b) & M; p += (int)(s >> 17) % C; if likely(p <= rm) return 0;
z2 = p;
s = s10 ^ K;
s = (s * K + b) & M; p = (int)(s >> 17) % C;
s = (s * K + b) & M; p += (int)(s >> 17) % C; if likely(p <= rm) return 0;
x3 = p;
s = (s * K + b) & M; p = (int)(s >> 17) % C;
s = (s * K + b) & M; p += (int)(s >> 17) % C; if likely(p > z0-rm) return 0;
z3 = p;
float sqrad = getEnclosingRadius(
x0>>1,z0>>1, x1>>1,z1>>1, x2>>1,z2>>1, x3>>1,z3>>1,
ax,ay,az, sconf.regionSize, radius);
return sqrad < radius ? sqrad : 0;
}
#ifdef __cplusplus
}
#endif
#endif // QUADBASE_H_