- The roof is rescanned only in area columns that changed since the last scan: a quiet Huge world goes from 4.60 to 1.88 ms per scan (every 20 ticks). - The heat pass skips react() for solids and powders below their first reaction threshold: Huge world digging 2.54 -> 2.34 ms/tick, quiet 1.39 -> 1.18. - Every suite's output is identical apart from timing lines; seeds make the same worlds. - test/t_sim_optimizations.js checks the dirty roof against a full scan and the heat loop against the reference loop byte for byte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
55 lines
2.1 KiB
JavaScript
55 lines
2.1 KiB
JavaScript
// Milestone checks (the ledger) and the sky roof line used for cooling.
|
|
// ---------- Ledger ----------
|
|
function checkLedger() {
|
|
const done = id => ledger[id];
|
|
const mark = id => { if (!ledger[id]) { ledger[id] = tick || 1; emit({ t: 'milestone', id }); } };
|
|
if (stats.dug >= 15) mark('dig');
|
|
if (seen[FIRE]) { mark('fire'); if (!player.lens) { player.lens = true; } }
|
|
if (seen[BRICK] || seen[RBRICK] || seen[BARBR]) mark('brick');
|
|
if (seen[CHARCOAL]) mark('charcoal');
|
|
if (stats.grown > 0) mark('grow');
|
|
if (seen[MCU]) mark('copper');
|
|
if (stats.bars > 0) mark('bars');
|
|
if (player.bitTier >= 1) mark('bit1');
|
|
let anyPower = false, steamOn = false;
|
|
for (const mm of machines) if (mm) { if ((mm.type === 'wheel' || mm.type === 'engine') && mm.out > 0.5) anyPower = true; if (mm.type === 'engine' && mm.out > 1) steamOn = true; }
|
|
if (anyPower && !POWER_FREE) mark('power');
|
|
if (stats.moved >= 300) mark('auto');
|
|
if (seen[MGL] || seen[GLASS] || seen[CULLET]) { mark('glass'); player.flask = true; }
|
|
if (steamOn && !POWER_FREE) mark('steam');
|
|
if (seen[MFE]) mark('iron');
|
|
if (player.bitTier >= 2) mark('bit2');
|
|
if (seen[MST]) mark('steel');
|
|
if (player.bitTier >= 3) mark('bit3');
|
|
if (player.tankTier >= 1) mark('tank');
|
|
if (depthU(bot.y) > 0.85) mark('deep');
|
|
if (stats.sold > 0) mark('sale');
|
|
if (ordersDone > 0) mark('order');
|
|
if (seen[MBZ] || seen[MBS]) mark('alloy');
|
|
if (seen[AGORE] || seen[AUORE] || seen[SILVER] || seen[GOLD]) mark('precious');
|
|
if (debt <= 0) mark('claim');
|
|
if (earned >= 50000) mark('rich1');
|
|
if (earned >= 200000) mark('rich2');
|
|
void done;
|
|
}
|
|
|
|
// Topmost non-air cell of each column: air above it can shed heat to the sky.
|
|
function updateRoof() {
|
|
scanRoof(0, W);
|
|
roofDirty.fill(0);
|
|
}
|
|
function updateDirtyRoof() {
|
|
for (let cx = 0; cx < CW; cx++) {
|
|
if (!roofDirty[cx]) continue;
|
|
scanRoof(cx << 5, Math.min(W, (cx + 1) << 5));
|
|
roofDirty[cx] = 0;
|
|
}
|
|
}
|
|
function scanRoof(x0, x1) {
|
|
for (let x = x0; x < x1; x++) {
|
|
let y = 0;
|
|
for (; y < H; y++) { const ph = PH[mat[y * W + x]]; if (ph === PH_SOLID || ph === PH_POWDER || ph === PH_LIQUID) break; }
|
|
roofY[x] = y;
|
|
}
|
|
}
|