diff --git a/finders.c b/finders.c index eaf93c1..a39288c 100644 --- a/finders.c +++ b/finders.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -3233,15 +3234,15 @@ L_HAS_PROTO_MUSHROOM: ids = (int*) calloc(getMinLayerCacheSize(entry, w, h), sizeof(int)); filter_data_t fd[9]; - swapMap(fd+0, &filter, l+L_OCEAN_MIX_4, mapFilterOceanMix); - swapMap(fd+1, &filter, l+L_RIVER_MIX_4, mapFilterRiverMix); - swapMap(fd+2, &filter, l+L_SHORE_16, mapFilterShore); - swapMap(fd+3, &filter, l+L_SUNFLOWER_64, mapFilterRareBiome); - swapMap(fd+4, &filter, l+L_BIOME_EDGE_64, mapFilterBiomeEdge); - swapMap(fd+5, &filter, l+L_OCEAN_TEMP_256, mapFilterOceanTemp); - swapMap(fd+6, &filter, l+L_BIOME_256, mapFilterBiome); - swapMap(fd+7, &filter, l+L_MUSHROOM_256, mapFilterMushroom); - swapMap(fd+8, &filter, l+L_SPECIAL_1024, mapFilterSpecial); + swapMap(fd+0, &filter, l+L_OCEAN_MIX_4, mapFilterOceanMix); + swapMap(fd+1, &filter, l+L_RIVER_MIX_4, mapFilterRiverMix); + swapMap(fd+2, &filter, l+L_SHORE_16, mapFilterShore); + swapMap(fd+3, &filter, l+L_SUNFLOWER_64, mapFilterRareBiome); + swapMap(fd+4, &filter, l+L_BIOME_EDGE_64, mapFilterBiomeEdge); + swapMap(fd+5, &filter, l+L_OCEAN_TEMP_256, mapFilterOceanTemp); + swapMap(fd+6, &filter, l+L_BIOME_256, mapFilterBiome); + swapMap(fd+7, &filter, l+L_MUSHROOM_256, mapFilterMushroom); + swapMap(fd+8, &filter, l+L_SPECIAL_1024, mapFilterSpecial); setLayerSeed(entry, seed); int ret = !entry->getMap(entry, ids, x, z, w, h); @@ -3670,6 +3671,10 @@ void genPotential(uint64_t *mL, uint64_t *mM, int layer, int mc, int id) genPotential(mL, mM, L_VORONOI_1, mc, id); break; + case L_OCEAN_MIX_4: + if (mc <= MC_1_12) goto L_bad_layer; + // fallthrough + case L_VORONOI_1: if (id < 128) *mL |= 1ULL << id; else *mM |= 1ULL << (id-128); @@ -3687,6 +3692,439 @@ void genPotential(uint64_t *mL, uint64_t *mM, int layer, int mc, int id) +double getParaDescent(const DoublePerlinNoise *para, double factor, + int x, int z, int w, int h, int i0, int j0, int maxrad, + int maxiter, double alpha, void *data, int (*func)(void*,int,int,double)) +{ + /// Do a gradient descent on a grid... + /// To start with, we will just consider a step size of 1 in one axis: + /// Try going in positive x: if gradient is upwards go to negative x + /// then do the same with z - if all 4 directions go upwards then we have + /// found a minimum, otherwise repeat. + /// We can remember and try the direction from the previous cycle first to + /// reduce the number of wrong guesses. + /// + /// We can also use a larger step size than 1, as long as we are believe + /// the minimum is not in between. To determine if this is viable, we check + /// the step size of 1 first, and then jump if the gradient appears large + /// enough in that direction. + /// + ///TODO: + /// The perlin noise can be sampled continuously, so more established + /// minima algorithms can also be considered... + + int dirx = 0, dirz = 0, dira; + int k, i, j; + double v, vd, va; + v = factor * sampleDoublePerlin(para, x+i0, 0, z+j0); + if (func) + { + if (func(data, x+i0, z+j0, factor < 0 ? -v : v)) + return nan(""); + } + + i = i0; j = j0; + for (k = 0; k < maxiter; k++) + { + if (dirx == 0) dirx = +1; + if (i+dirx >= 0 && i+dirx < w) + vd = factor * sampleDoublePerlin(para, x+i+dirx, 0, z+j); + else vd = v; + if (vd >= v) + { + dirx *= -1; + if (i+dirx >= 0 && i+dirx < w) + vd = factor * sampleDoublePerlin(para, x+i+dirx, 0, z+j); + else vd = v; + if (vd >= v) + dirx = 0; + } + if (dirx) + { + dira = (int)(dirx * alpha * (v - vd)); + if (abs(dira) > 2 && i+dira >= 0 && i+dira < w) + { // try jumping by more than 1 + va = factor * sampleDoublePerlin(para, x+i+dira, 0, z+j); + if (va < vd) + { + i += dira; + v = va; + goto L_x_end; + } + } + v = vd; + i += dirx; + L_x_end: + if (func) + { + if (func(data, x+i, z+j, factor < 0 ? -v : v)) + return nan(""); + } + } + + if (dirz == 0) dirz = +1; + if (j+dirz >= 0 && j+dirz < h) + vd = factor * sampleDoublePerlin(para, x+i, 0, z+j+dirz); + else vd = v; + if (vd >= v) + { + dirz *= -1; + if (j+dirz >= 0 && j+dirz < h) + vd = factor * sampleDoublePerlin(para, x+i, 0, z+j+dirz); + else vd = v; + if (vd >= v) + dirz = 0; + } + if (dirz) + { + dira = (int)(dirz * alpha * (v - vd)); + if (abs(dira) > 2 && j+dira >= 0 && j+dira < h) + { // try jumping by more than 1 + va = factor * sampleDoublePerlin(para, x+i, 0, z+j+dira); + if (va < vd) + { + j += dira; + v = va; + goto L_z_end; + } + } + j += dirz; + v = vd; + L_z_end: + if (func) + { + if (func(data, x+i, z+j, factor < 0 ? -v : v)) + return nan(""); + } + } + if (dirx == 0 && dirz == 0) + { // this is very likely a fix point + // but there could be a minimum along a diagonal path in rare cases + int c; + for (c = 0; c < 4; c++) + { + dirx = (c & 1) ? -1 : +1; + dirz = (c & 2) ? -1 : +1; + if (i+dirx < 0 || i+dirx >= w || j+dirz < 0 || j+dirz >= h) + continue; + vd = factor * sampleDoublePerlin(para, x+i+dirx, 0, z+j+dirz); + if (vd < v) + { + v = vd; + i += dirx; + j += dirz; + break; + } + } + if (c >= 4) + break; + } + if (abs(i - i0) > maxrad || abs(j - j0) > maxrad) + break; // we have gone too far from the origin + } + + return v; +} + + +int getParaRange(const DoublePerlinNoise *para, double *pmin, double *pmax, + int x, int z, int w, int h, void *data, int (*func)(void*,int,int,double)) +{ + const double beta = 1.5; + const double factor = 10000; + const double perlin_grad = 2.0 * 1.875; // max perlin noise gradient + double v, lmin, lmax, dr, vdif, small_regime; + char *skip = NULL; + int i, j, step, ii, jj, ww, hh; + int maxrad, maxiter; + int err = 1; + + *pmin = DBL_MAX; + *pmax = -DBL_MAX; + + lmin = DBL_MAX, lmax = 0; + for (i = 0; i < para->octA.octcnt; i++) + { + double lac = para->octA.octaves[i].lacunarity; + if (lac < lmin) lmin = lac; + if (lac > lmax) lmax = lac; + } + + // Sort out the small area cases where we are less likely to improve upon + // checking all positions. + small_regime = 1e3 * sqrt(lmax); + if (w*h < small_regime) + { + for (j = 0; j < h; j++) + { + for (i = 0; i < w; i++) + { + v = factor * sampleDoublePerlin(para, x+i, 0, z+j); + if (func) + { + err = func(data, x+i, z+j, v); + if (err) + return err; + } + if (v < *pmin) *pmin = v; + if (v > *pmax) *pmax = v; + } + } + return 0; + } + + // Start with the largest noise period to get some bounds for pmin, pmax + step = (int) (0.5 / lmin - FLT_EPSILON) + 1; + + dr = lmax / lmin * beta; + for (j = 0; j < h; j += step) + { + for (i = 0; i < w; i += step) + { + v = getParaDescent(para, +factor, x, z, w, h, i, j, + step, step, dr, data, func); + if (v != v) goto L_end; + if (v < *pmin) *pmin = v; + v = -getParaDescent(para, -factor, x, z, w, h, i, j, + step, step, dr, data, func); + if (v != v) goto L_end; + if (v > *pmax) *pmax = v; + } + } + + //(*(double*)data) = -1e9+1; // testing + if (lmin == lmax) + return 0; + + step = (int) (1.0 / (perlin_grad * lmax + FLT_EPSILON)) + 1; + + /// We can determine the maximum contribution we expect from all noise + /// periods for a distance of step. If this does not account for the + /// necessary difference, we can skip that point. + vdif = 0; + for (i = 0; i < para->octA.octcnt; i++) + { + const PerlinNoise *p = para->octA.octaves + i; + double contrib = step * p->lacunarity * 1.0; + if (contrib > 1.0) contrib = 1; + vdif += contrib * p->amplitude; + } + for (i = 0; i < para->octB.octcnt; i++) + { + const double lac_factB = 337.0 / 331.0; + const PerlinNoise *p = para->octB.octaves + i; + double contrib = step * p->lacunarity * lac_factB; + if (contrib > 1.0) contrib = 1; + vdif += contrib * p->amplitude; + } + vdif = fabs(factor * vdif * para->amplitude); + //printf("%g %g %g\n", para->amplitude, 1./lmin, 1./lmax); + //printf("first pass: [%g %g] diff=%g step:%d\n", *pmin, *pmax, vdif, step); + + maxrad = step; + maxiter = step*2; + ww = (w+step-1) / step; + hh = (h+step-1) / step; + skip = (char*) malloc(ww * hh * sizeof(*skip)); + + // look for minima + memset(skip, 0, ww * hh * sizeof(*skip)); + + for (jj = 0; jj <= hh; jj++) + { + j = jj * step; if (j >= h) j = h-1; + for (ii = 0; ii <= ww; ii++) + { + i = ii * step; if (i >= w) i = w-1; + if (skip[jj*ww+ii]) continue; + + v = factor * sampleDoublePerlin(para, x+i, 0, z+j); + if (func) + { + int e = func(data, x+i, z+j, v); + if (e) + { + err = e; + goto L_end; + } + } + // not looking for maxima yet, but we'll update the bounds anyway + if (v > *pmax) *pmax = v; + + dr = beta * (v - *pmin) / vdif; + if (dr > 1.0) + { // difference is too large -> mark visinity to be skipped + int a, b, r = (int) dr; + for (b = 0; b < r; b++) + { + if (b+jj < 0 || b+jj >= hh) continue; + for (a = -r+1; a < r; a++) + { + if (a+ii < 0 || a+ii >= ww) continue; + skip[(b+jj)*ww + (a+ii)] = 1; + } + } + continue; + } + v = getParaDescent(para, +factor, x, z, w, h, i, j, + maxrad, maxiter, dr, data, func); + if (v != v) goto L_end; + if (v < *pmin) *pmin = v; + } + } + + // look for maxima + memset(skip, 0, ww * hh * sizeof(*skip)); + + for (jj = 0; jj <= hh; jj++) + { + j = jj * step; if (j >= h) j = h-1; + for (ii = 0; ii <= ww; ii++) + { + i = ii * step; if (i >= w) i = w-1; + if (skip[jj*ww+ii]) continue; + + v = -factor * sampleDoublePerlin(para, x+i, 0, z+j); + if (func) + { + int e = func(data, x+i, z+j, -v); + if (e) + { + err = e; + goto L_end; + } + } + + dr = beta * (v + *pmax) / vdif; + if (dr > 1.0) + { // difference too large -> mark visinity to be skipped + int a, b, r = (int) dr; + for (b = 0; b < r; b++) + { + if (b+jj < 0 || b+jj >= hh) continue; + for (a = -r+1; a < r; a++) + { + if (a+ii < 0 || a+ii >= ww) continue; + skip[(b+jj)*ww + (a+ii)] = 1; + } + } + continue; + } + v = -getParaDescent(para, -factor, x, z, w, h, i, j, + maxrad, maxiter, dr, data, func); + if (v != v) goto L_end; + if (v > *pmax) *pmax = v; + } + } + + err = 0; +L_end: + if (skip) + free(skip); + return err; +} + +#define IMIN INT_MIN +#define IMAX INT_MAX +static const int g_biome_para_range_18[][13] = { +/// biome temperature humidity continental. erosion depth weirdness +{ocean , -1500, 2000, IMIN, IMAX, -4550,-1900, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{plains , -4500, 5500, IMIN, 1000, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{desert , 5500, IMAX, IMIN, IMAX, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{windswept_hills , IMIN, 2000, IMIN, 1000, -1899, IMAX, 4500, 5500, IMIN, IMAX, IMIN, IMAX}, +{forest , -4500, 5500, -1000, 3000, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{taiga , IMIN,-1500, 1000, IMAX, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{swamp , -4500, IMAX, IMIN, IMAX, -1100, IMAX, 5500, IMAX, IMIN, IMAX, IMIN, IMAX}, +{river , -4500, IMAX, IMIN, IMAX, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, -500, 500}, +{frozen_ocean , IMIN,-4501, IMIN, IMAX, -4550,-1900, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{frozen_river , IMIN,-4501, IMIN, IMAX, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, -500, 500}, +{snowy_plains , IMIN,-4500, IMIN, 1000, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{mushroom_fields , IMIN, IMAX, IMIN, IMAX, IMIN,-10500, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{beach , -4500, 5500, IMIN, IMAX, -1900,-1100, -2225, IMAX, IMIN, IMAX, IMIN, 2666}, +{jungle , 2000, 5500, 1000, IMAX, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{sparse_jungle , 2000, 5500, 1000, 3000, -1899, IMAX, IMIN, IMAX, IMIN, IMAX, -500, IMAX}, +{deep_ocean , -1500, 2000, IMIN, IMAX,-10500,-4551, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{stony_shore , IMIN, IMAX, IMIN, IMAX, -1900,-1100, IMIN,-2225, IMIN, IMAX, IMIN, IMAX}, +{snowy_beach , IMIN,-4500, IMIN, IMAX, -1900,-1100, -2225, IMAX, IMIN, IMAX, IMIN, 2666}, +{birch_forest , -1500, 2000, 1000, 3000, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{dark_forest , -1500, 2000, 3000, IMAX, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{snowy_taiga , IMIN,-4500, -1000, IMAX, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{old_growth_pine_taiga , -4500,-1500, 3000, IMAX, -1899, IMAX, IMIN, IMAX, IMIN, IMAX, -500, IMAX}, +{windswept_forest , IMIN, 2000, 1000, IMAX, -1899, IMAX, 4500, 5500, IMIN, IMAX, IMIN, IMAX}, +{savanna , 2000, 5500, IMIN,-1000, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{savanna_plateau , 2000, 5500, IMIN,-1000, -1100, IMAX, IMIN, 500, IMIN, IMAX, IMIN, IMAX}, +{badlands , 5500, IMAX, IMIN, 1000, -1899, IMAX, IMIN, 500, IMIN, IMAX, IMIN, IMAX}, +{wooded_badlands , 5500, IMAX, 1000, IMAX, -1899, IMAX, IMIN, 500, IMIN, IMAX, IMIN, IMAX}, +{warm_ocean , 5500, IMAX, IMIN, IMAX,-10500,-1900, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{lukewarm_ocean , 2001, 5500, IMIN, IMAX, -4550,-1900, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{cold_ocean , -4500,-1501, IMIN, IMAX, -4550,-1900, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{deep_lukewarm_ocean , 2001, 5500, IMIN, IMAX,-10500,-4551, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{deep_cold_ocean , -4500,-1501, IMIN, IMAX,-10500,-4551, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{deep_frozen_ocean , IMIN,-4501, IMIN, IMAX,-10500,-4551, IMIN, IMAX, IMIN, IMAX, IMIN, IMAX}, +{sunflower_plains , -1500, 2000, IMIN,-3500, -1899, IMAX, IMIN, IMAX, IMIN, IMAX, -500, IMAX}, +{windswept_gravelly_hills, IMIN,-1500, IMIN,-1000, -1899, IMAX, 4500, 5500, IMIN, IMAX, IMIN, IMAX}, +{flower_forest , -1500, 2000, IMIN,-3500, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, IMIN, -500}, +{ice_spikes , IMIN,-4500, IMIN,-3500, -1899, IMAX, IMIN, IMAX, IMIN, IMAX, -500, IMAX}, +{old_growth_birch_forest , -1500, 2000, 1000, 3000, -1899, IMAX, IMIN, IMAX, IMIN, IMAX, -500, IMAX}, +{old_growth_spruce_taiga , -4500,-1500, 3000, IMAX, -1900, IMAX, IMIN, IMAX, IMIN, IMAX, IMIN, -500}, +{windswept_savanna , -1500, IMAX, IMIN, 3000, -1899, 300, 4500, 5500, IMIN, IMAX, 501, IMAX}, +{eroded_badlands , 5500, IMAX, IMIN,-1000, -1899, IMAX, IMIN, 500, IMIN, IMAX, IMIN, IMAX}, +{bamboo_jungle , 2000, 5500, 3000, IMAX, -1899, IMAX, IMIN, IMAX, IMIN, IMAX, -500, IMAX}, +{dripstone_caves , IMIN, IMAX, IMIN, 6999, 3001, IMAX, IMIN, IMAX, 1000, 9500, IMIN, IMAX}, +{lush_caves , IMIN, IMAX, 2001, IMAX, IMIN, IMAX, IMIN, IMAX, 1000, 9500, IMIN, IMAX}, +{meadow , -4500, 2000, IMIN, 3000, 300, IMAX, -7799, 500, IMIN, IMAX, IMIN, IMAX}, +{grove , IMIN, 2000, -1000, IMAX, -1899, IMAX, IMIN,-3750, IMIN, IMAX, IMIN, IMAX}, +{snowy_slopes , IMIN, 2000, IMIN,-1000, -1899, IMAX, IMIN,-3750, IMIN, IMAX, IMIN, IMAX}, +{jagged_peaks , IMIN, 2000, IMIN, IMAX, -1899, IMAX, IMIN,-3750, IMIN, IMAX, -9333,-4001}, +{frozen_peaks , IMIN, 2000, IMIN, IMAX, -1899, IMAX, IMIN,-3750, IMIN, IMAX, 4000, 9333}, +{stony_peaks , 2000, 5500, IMIN, IMAX, -1899, IMAX, IMIN,-3750, IMIN, IMAX, -9333, 9333}, +}; + +/** + * Gets the min/max possible noise parameter values at which the given biome + * can generate. The values are in min/max pairs in order: + * temperature, humidity, continentalness, erosion, depth, weirdness. + */ +const int *getBiomeParaLimits(int mc, int id) +{ + if (mc < MC_1_18) + return NULL; + int i, n = sizeof(g_biome_para_range_18) / sizeof(g_biome_para_range_18[0]); + for (i = 0; i < n; i++) + { + if (g_biome_para_range_18[i][0] == id) + return &g_biome_para_range_18[i][1]; + } + return NULL; +} + +/** + * Determines which biomes are able to generate given climate parameter limits. + * Possible biomes are marked non-zero in the 'ids'. + */ +void getPossibleBiomesForLimits(char ids[256], int mc, int limits[6][2]) +{ + int i, j, n; + memset(ids, 0, 256*sizeof(char)); + + if (mc >= MC_1_18) + { + n = sizeof(g_biome_para_range_18) / sizeof(g_biome_para_range_18[0]); + for (i = 0; i < n; i++) + { + const int *bp = &g_biome_para_range_18[i][1]; + int id = bp[-1]; + for (j = 0; j < 6; j++) + { + if (limits[j][0] <= bp[2*j+1] && limits[j][1] >= bp[2*j+0]) + ids[id]++; + } + } + for (i = 0; i < 256; i++) + ids[i] = ids[i] >= 6; + } +} + diff --git a/finders.h b/finders.h index 31a8cbf..e9372d3 100644 --- a/finders.h +++ b/finders.h @@ -729,6 +729,61 @@ int canBiomeGenerate(int layerId, int mc, int biomeID); */ void genPotential(uint64_t *mL, uint64_t *mM, int layer, int mc, int id); + +//============================================================================== +// Biome Noise Finders (for 1.18+) +//============================================================================== + +/** + * Runs a gradient descent towards the minimum of the noise parameter times a + * given factor. The algorithm is restricted to the area (x,z,w,h) and starts + * at (i0,j0) relative to (x,z). The iteration is terminated when either + * 1) a fix point has been reached, + * 2) maxiter iterations have been completed, + * 3) or the sampling position has moved more than maxrad away from (i0,j0). + * + * Alpha is an optimization argument that is used to determine the length of + * large steps based on the current gradient. + * + * Optionally, the iteration can also call the custom function: + * func(data, x, z, factor*para_noise(x,z)); + * + * The return value is the minimum value reached. + */ +double getParaDescent(const DoublePerlinNoise *para, double factor, + int x, int z, int w, int h, int i0, int j0, int maxrad, + int maxiter, double alpha, void *data, int (*func)(void*,int,int,double)); + +/** + * Determines the value range of a climate noise parameter over the given area. + * The sampling has scale 1:4 and sampling shift is not considered, so biomes + * could potentially *leak* in at the boarders. + * An optional function: + * func(data, x, z, climate_noise(x,z)) + * is called in each gradient descent iteration. If this function returns + * non-zero the search is aborted, the results are undefined and a non-zero + * error is returned. + * + * The results are written to pmin and pmax (which would be cast to an integer + * during boime mapping). + */ +int getParaRange(const DoublePerlinNoise *para, double *pmin, double *pmax, + int x, int z, int w, int h, void *data, int (*func)(void*,int,int,double)); + +/** + * Gets the min/max possible noise parameter values at which the given biome + * can generate. The values are in min/max pairs in order of: + * temperature, humidity, continentalness, erosion, depth, weirdness. + */ +const int *getBiomeParaLimits(int mc, int id); + +/** + * Determines which biomes are able to generate given a set of climate + * parameter limits. Possible biomes are marked non-zero in the 'ids'. + */ +void getPossibleBiomesForLimits(char ids[256], int mc, int limits[6][2]); + + //============================================================================== // Implementaions for Functions that Ideally Should be Inlined //============================================================================== diff --git a/tests.c b/tests.c index b1d8af8..d8cc32c 100644 --- a/tests.c +++ b/tests.c @@ -207,10 +207,151 @@ int testGeneration() } +int k_tot; +struct _f_para { double v; double *buf; int x, z, w, h; }; +void _f1(void *data, int x, int z, double v) +{ + struct _f_para d = *(struct _f_para*) data; + d.buf[(x-d.x)*d.w + (z-d.z)] = d.v; + k_tot++; +} + +void testNoiseRangeFinder() +{ + int n = 100; + unsigned char *pix = (unsigned char*) malloc(n*n*3); + double *buf = (double*) malloc(n*n*8); + double bmin, bmax; + int i, j; + + int64_t seed = 0, seed_max = 200; + int bad = 0; + for (seed = 0; seed < seed_max; seed++) + { + //printf("%ld\n", seed); + Generator g; + setupGenerator(&g, MC_1_18, 0); + applySeed(&g, 0, seed); + + int x = 100, z = -100; + bmin = 99999, bmax = -99999; + for (i = 0; i < n; i++) + { + for (j = 0; j < n; j++) + { + double t = sampleDoublePerlin(&g.bn.humidity, x+i, 0, z+j); + if (t < bmin) bmin = t; + if (t > bmax) bmax = t; + buf[i*n+j] = t; + } + } + + struct _f_para f_p = {-1e9, buf, x, z, n, n }; + double tmin, tmax; + int k = k_tot; + getParaRange(&g.bn.humidity, &tmin, &tmax, x, z, n, n, &f_p, _f1); + if (abs(tmin-bmin*1e4)>.01||abs(tmax-bmax*1e4)>.01) + { + printf("=========================== BAD ============================\n"); + printf("seed:%-8ld temp = [%g %g], best = [%g %g]\n", + seed, tmin, tmax, bmin * 10000, bmax * 10000); + bad++; + } + //printf("wh:%d (%d) -> k:%d\n", 2*n, n*n, k_tot - k); + + for (i = 0; i < n; i++) + { + for (j = 0; j < n; j++) + { + unsigned char *p = pix + 3*(i*n+j); + double b = buf[i*n+j]; + if (b == bmin || b == bmax) { + p[1] = 0xff; p[0] = p[2] = 0; + continue; + } + if (b == -1e9) { + p[0] = 0xff; p[1] = p[2] = 0; + continue; + } + if (b == -1e9+1) { + p[2] = 0xff; p[0] = p[1] = 0; + continue; + } + p[0] = p[1] = p[2] = (unsigned char) + ((b - bmin) / (bmax - bmin) * 256); + } + } + if (bad >= 10) break; + } + + printf("bad:%d k_tot: %d / %d ~ %g : %d\n", bad, k_tot, seed, k_tot / (double)seed, n*n); + savePPM("img.ppm", pix, n, n); +} + + +int64_t bbounds[256][6][2]; // [biome][np][min/max] + +void _f2(void *data, int x, int z, double v) +{ + int64_t np[6]; + Generator *g = (Generator*) data; + int id = sampleBiomeNoise(&g->bn, np, x, -64+rand()%384, z, 0, SAMPLE_NO_SHIFT); + int i; + for (i = 0; i < 6; i++) + { + if (np[i] < bbounds[id][i][0]) bbounds[id][i][0] = np[i]; + if (np[i] > bbounds[id][i][1]) bbounds[id][i][1] = np[i]; + } +} + +void findBiomeParaBounds() +{ + int i, j; + for (i = 0; i < 256; i++) + { + for (j = 0; j < 6; j++) + { + bbounds[i][j][0] = +1e8; + bbounds[i][j][1] = -1e8; + } + } + + Generator g; + setupGenerator(&g, MC_1_18, 0); + int64_t s; + int r = 1000; + for (s = 1000; s < 20000; s++) + { + int64_t seed = ((int64_t)hash32(s) << 32) ^ hash32(rand()); + applySeed(&g, 0, seed); + double tmin, tmax; + int x = rand() % 10000 - 5000; + int z = rand() % 10000 - 5000; + getParaRange(&g.bn.temperature, &tmin, &tmax, x-r, z-r, r, r, &g, _f2); + getParaRange(&g.bn.humidity, &tmin, &tmax, x-r, z-r, r, r, &g, _f2); + getParaRange(&g.bn.erosion, &tmin, &tmax, x-r, z-r, r, r, &g, _f2); + getParaRange(&g.bn.continentalness, &tmin, &tmax, x-r, z-r, r, r, &g, _f2); + getParaRange(&g.bn.weirdness, &tmin, &tmax, x-r, z-r, r, r, &g, _f2); + } + + for (i = 0; i < 256; i++) + { + if (!isOverworld(MC_1_18, i)) + continue; + + printf("{%-24s", biome2str(MC_1_18, i)); + for (j = 0; j < 6; j++) + { + printf(", %6ld,%6ld", bbounds[i][j][0], bbounds[i][j][1]); + } + printf("},\n"); + } +} + + int main() { - testGeneration(); - + findBiomeParaBounds(); return 0; }