- Areas within 150 grains of the bot start at once; the others 12 a tick, nearest first, and any the bot flies toward start straight away. A Huge world is fully started after 17 s. - Huge world, first 600 ticks: average 15.2 -> 5.5 ms/tick, worst 79.7 -> 16.2. - New test/t_load.js. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68 lines
4.1 KiB
JavaScript
68 lines
4.1 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 && 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();
|