Files
deepgrain-foundry/test/t_lakes.js
T
44r0n7andClaude Opus 5.5 80668a4baf Lakes fill only to their lower rim and keep clear of spawn and the dock (t96)
The lake lip was Math.min(surfY), the highest ground (y grows downward), so water was poured
above the lower rim and, once levelled, ran up to the trade dock on 4 of 12 seeds. The lip is
now the lower rim edge minus one, and each lake stays 80 grains from spawn. Seed hashes and
the shallow-ore hash re-baselined (changes only under lake basins); golden fixtures also
freeze surfY/dirtY/AMB from the previous generator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 19:45:51 -04:00

76 lines
4.7 KiB
JavaScript

// Generated lakes start flat, conserve water, and have dry tree bases.
const { load, suite, ticks, count, rect } = require('./harness');
const { createHash } = require('node:crypto');
const { run } = load();
const { check, done } = suite();
function surfacePools() {
return run(`(() => {
const pools = []; let pool = null;
for (let x = 0; x < W; x++) {
let y = 0; while (y < H && mat[y * W + x] === EMPTY) y++;
if (mat[y * W + x] !== WATER) { pool = null; continue; }
if (!pool) { pool = { x0: x, x1: x, width: 0, min: y, max: y }; pools.push(pool); }
pool.x1 = x; pool.width++; pool.min = Math.min(pool.min, y); pool.max = Math.max(pool.max, y);
}
return pools.filter(p => p.width > 20);
})()`);
}
for (const [seed, expected] of [
[7, '4c4778417e6cf6e9577b831e66111bd374068a14e15642bae304b8a45a68b879'],
[42, '907f2ed79b9fda1ae99c5ee55fa19ce85eff8ffdcd7dadac14a2deb42223217d'],
[424242, '40080ea6039d0d4247e36fd9a5837d0675100414f198d77419ecf500c7f9d6ad'],
[2, null],
[1, null],
// Before t96 round 3 these lakes ran over their lower rim up to the trade dock.
[5, null],
[6, null],
]) {
run(`generateWorld(${seed}, 2048, 1024, (f, label) => {
if (f === 0.99 && label === 'Finishing') globalThis.waterBeforeLevel = mat.reduce((n, m) => n + (m === WATER), 0);
}); weather.next = 1e15;`);
const hash = createHash('sha256');
for (const name of ['mat', 'temp']) hash.update(Buffer.from(run(name).buffer));
const actual = hash.digest('hex');
if (expected) check(`seed ${seed} matches levelled generation hash`, actual === expected, actual);
check(`seed ${seed} conserves water through levelling`, run('mat.reduce((n, m) => n + (m === WATER), 0) === waterBeforeLevel'));
const initial = surfacePools();
check(`seed ${seed} starts with flat surface pools`, initial.length > 0 && initial.every(p => p.max - p.min <= 1), JSON.stringify(initial));
check(`seed ${seed} keeps its lakes clear of the trade dock`, run(`(() => { const d = machines.find(m => m && m.type === 'dock');
return ${JSON.stringify(initial)}.every(p => p.x1 < d.x - 8 || p.x0 > d.x + 24 + 8); })()`), JSON.stringify(initial));
check(`seed ${seed} restores pinned generation switches`, run('LSTEP === 8 && LFILM === 4 && SORTP === 0.06 && CORNER_SEAL && POWDER_FAN'));
check(`seed ${seed} starts every water area awake`, run(`mat.every((m, i) => m !== WATER || awake[((i / W) >> 5) * CW + ((i % W) >> 5)] > 0)`));
check(`seed ${seed} has no submerged tree base`, run('treeBases.every(i => !treeHeld(i) || mat[i] !== WATER)'));
check(`seed ${seed} retains dry trees`, run('treeBases.filter(i => treeHeld(i)).length > 20'));
ticks(run, 600);
const settled = surfacePools();
check(`seed ${seed} stays flat after 600 ticks`, settled.length > 0 && settled.every(p => p.max - p.min <= 1), JSON.stringify(settled));
for (const lake of initial) check(`seed ${seed} original lake retains dry roots`, run(`treeBases.every(i => i % W < ${lake.x0} || i % W > ${lake.x1} || !treeHeld(i) || mat[i] !== WATER)`));
}
// A low rim with a much deeper empty basin beyond it must not steal the water.
for (const wall of ['STONE', 'SAND']) {
run('alloc(64, 64); mat.fill(STONE);');
rect(run, 5, 10, 24, 29, run('EMPTY'));
rect(run, 26, 10, 55, 45, run('EMPTY'));
rect(run, 25, 22, 25, 45, run(wall));
rect(run, 25, 10, 25, 21, run('EMPTY'));
rect(run, 5, 20, 14, 29, run('WATER'));
rect(run, 15, 27, 15, 29, run('WATER'));
run('globalThis.beforeLake = mat.slice(); levelWorldWater();');
check(`${wall} rim holds all 103 grains`, count(run, run('WATER')) === 103 && count(run, run('WATER'), 26, 0) === 0);
check(`${wall} basin fills from bottom upward`, count(run, run('WATER'), 5, 25, 24, 29) === 100);
check(`${wall} partial top row fills from the middle outward`, run('[13,14,15].every(x => mat[24 * W + x] === WATER)') && count(run, run('WATER'), 0, 0, 63, 24) === 3);
check(`${wall} levelling changes only water and air`, run('mat.every((m,i) => m === beforeLake[i] || ((m === EMPTY || m === WATER) && (beforeLake[i] === EMPTY || beforeLake[i] === WATER)))'));
}
// Diagonally touching small bodies remain separate; exactly 50 grains is eligible.
run('alloc(64,64); mat.fill(STONE);');
rect(run, 5, 20, 11, 26, run('WATER'));
rect(run, 12, 27, 18, 33, run('WATER'));
rect(run, 5, 10, 11, 19, run('EMPTY'));
rect(run, 30, 10, 49, 29, run('EMPTY'));
rect(run, 30, 20, 34, 29, run('WATER'));
run('globalThis.smallBefore = mat.slice(); levelWorldWater();');
check('diagonally touching 49-grain pools are left intact', run('mat.every((m,i) => i % W >= 30 || m === smallBefore[i])'));
check('50-grain body levels and conserves volume', count(run, run('WATER'), 30, 28, 49, 29) === 40 && count(run, run('WATER'), 30, 0) === 50);
done();