// 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();