Files
deepgrain-foundry/test/t_load.js
T

72 lines
4.3 KiB
JavaScript

// Reload wakes nearby areas first, without losing distant water, fire or saved state.
const { load, suite, flatWorld, rect, count, ticks } = require('./harness');
const { run } = load();
const { check, done } = suite();
flatWorld(run, 1024, 256, 200);
run(`
bot.x = 510; bot.y = 170;
setCell(192 * W + 16, WOOD, AMB[192], F_BURN); life[192 * W + 16] = FUEL[WOOD];
temp[210 * W + 16] = AMB[210] + 200;
temp[210 * W + 512] = AMB[210] + 200;
`);
rect(run, 48, 160, 49, 199, run('STONE'));
rect(run, 78, 160, 79, 199, run('STONE'));
rect(run, 50, 170, 60, 179, run('WATER'));
run('globalThis.loadSave = serializeWorld(); deserializeWorld(loadSave.meta, loadSave.buffers);');
check('nearby areas wake immediately, with heat', run('awake[6 * CW + 16] > 0 && hot[6 * CW + 16] > 0'));
check('distant areas wait, including their heat', run('awake[6 * CW] === 0 && hot[6 * CW] === 0'));
ticks(run, 1);
check('nearby areas are awake on the first tick while far areas wait', run('awake[6 * CW + 16] > 0 && awake[6 * CW] === 0'));
check('the order contains every area once and goes outward from the saved bot', run(`(() => {
const visited = new Uint8Array(NCH);
let previous = -1;
for (const c of loadWakeOrder) {
const d = (c % CW * 32 + 16 - (bot.x + bot.w / 2)) ** 2 + (((c / CW) | 0) * 32 + 16 - (bot.y + bot.h / 2)) ** 2;
if (visited[c] || d < previous) return false;
visited[c] = 1; previous = d;
}
return visited.every(v => v === 1);
})()`));
check('waiting leaves distant fire and water unchanged', run('life[192 * W + 16] === FUEL[WOOD] && mat[170 * W + 50] === WATER'));
ticks(run, 120);
check('all areas of a small world have had their turn', run('loadWakeOrder === null && loadWakePending === null'));
check('distant burning wood resumes burning after its turn', run('life[192 * W + 16] < FUEL[WOOD] || mat[192 * W + 16] !== WOOD'));
check('distant stored heat resumes flowing', run('temp[210 * W + 16] < AMB[210] + 190'));
ticks(run, 600);
check('distant water settles without losing grains', count(run, run('WATER')) === 110 && count(run, run('WATER'), 50, 196, 77, 199) === 110);
check('the settled water area goes back to sleep', run('awake[6 * CW + 1] === 0 && awake[6 * CW + 2] === 0'));
// Moving to a waiting area must apply its saved heat hint, even at ambient with F_BURN.
run('deserializeWorld(loadSave.meta, loadSave.buffers); bot.x = 14; bot.y = 190;');
check('the destination is still waiting before moving wakes it', run('loadWakePending[6 * CW] === 2 && hot[6 * CW] === 0'));
ticks(run, 8);
check('moving near a waiting area wakes it within eight ticks, including heat', run('loadWakePending[6 * CW] === 0 && hot[6 * CW] > 0'));
// Burning is probabilistic: waking does not promise a fuel draw on that exact tick.
ticks(run, 32);
check('newly woken fuel resumes burning', run('life[192 * W + 16] < FUEL[WOOD]'));
// Saving midway keeps the actual cells, and loading starts a fresh queue at the new bot.
run('globalThis.midLoadSave = serializeWorld(); deserializeWorld(midLoadSave.meta, midLoadSave.buffers);');
check('saving midway preserves all cell buffers', run(`(() => {
const saved = serializeWorld();
return ['mat', 'temp', 'life', 'flags'].every(key => saved.buffers[key].every((v, i) => v === midLoadSave.buffers[key][i]));
})()`));
check('loading midway starts a new queue around the current bot', run('loadWakeIndex === 0 && loadWakePending[6 * CW] === 0 && loadWakePending[6 * CW + 16] > 0'));
run('alloc(64, 64);');
check('alloc clears a previous load queue', run('loadWakeOrder === null && loadWakePending === null && loadWakeIndex === 0'));
run('deserializeWorld(loadSave.meta, loadSave.buffers); generateWorld(77, 256, 256, null);');
check('generating a new world clears the load queue', run('loadWakeOrder === null && loadWakePending === null && loadWakeIndex === 0'));
// The Huge-world budget is checked with real ticks on quiet terrain.
flatWorld(run, 6144, 2048, 1900);
run('startLoadWake();');
check('a Huge world has 12288 scheduled areas', run('loadWakeOrder.length === 12288'));
ticks(run, 1023);
check('the final areas are still queued at tick 1023', run('loadWakeOrder !== null && loadWakePending.some(v => v > 0)'));
ticks(run, 1);
check('every Huge-world area has had its turn by tick 1024 (17.07 seconds)', run('loadWakeOrder === null && loadWakePending === null && loadWakeIndex === 12288'));
done();