// 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, '669a4ee87496be27473d2987d9a8935be1a19b29a8f2433d306f32a1616f3bca'], [42, 'efeadd2bff0ce3eaf81e5a08a998ce767e9667e3b27b8623e928f202669f640e'], [424242, '24b5e6ccc4e7d5eb9d54c3ff0fa5dfe84185b83e75b07261ac007b4f920f0640'], [2, null], [1, 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} 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();