import { makeWorld, tick, sim, check, section, report, CFG, Input } from './harness.mjs'; const { Arena, ARENAS } = await import('../src/arena.js'); section('CASCADE — the arena'); { const w = makeWorld(99); const arena = new Arena(); const def = arena.room(0); check('arena builds a field', def.particles.length > 20, `${def.particles.length} atoms`); // minimum separation respected (field must not explode on frame 1) let tooClose = 0; for (let i = 0; i < def.particles.length; i++) for (let j = i + 1; j < def.particles.length; j++) { const a = def.particles[i], b = def.particles[j]; if (Math.hypot(a.x - b.x, a.y - b.y) < 50) tooClose++; } check('field starts at natural packing', tooClose === 0, `${tooClose} overlaps`); w.load(def); arena.enter(w, 0); sim(w, 3); check('arena ignites quickly (no dead air)', w.radicals.count > 0, `n=${w.radicals.count} at 3s`); // survivability: does an unattended arena reach a plateau, not a wipe? const trace = []; for (let s = 0; s < 8; s++) { sim(w, 5); trace.push(`${(s+1)*5}s:${w.radicals.count}`); } console.log(' unattended 40s -> ' + trace.join(' ')); check('unattended arena does not fill the screen', w.radicals.count < 60, `n=${w.radicals.count}`); check('waves are finite (a fight you can finish)', arena.wave <= arena.spec().waves, `${arena.wave}/${arena.spec().waves} waves`); } section('CASCADE — difficulty actually ramps'); { const counts = ARENAS.map((s, i) => { const w = makeWorld(7); const a = new Arena(); const def = a.room(i); w.load(def); a.enter(w, i); sim(w, 20); return { i, atoms: def.particles.length, n: w.radicals.count }; }); console.log(' ' + counts.map(c => `a${c.i}:${c.atoms}atoms/${c.n}rad`).join(' ')); check('later arenas are denser', counts[7].atoms > counts[0].atoms, `${counts[0].atoms} -> ${counts[7].atoms}`); check('later arenas are hotter at 20s', counts[7].n >= counts[0].n, `${counts[0].n} -> ${counts[7].n}`); } section('CASCADE — win and lose conditions'); { // LOSE: a hyper-dense arena left unattended must melt down const w = makeWorld(3); const a = new Arena(); const def = a.room(7); // the densest w.load(def); a.enter(w, 7); let melted = null; for (let i = 0; i < 120 * 90 && melted === null; i++) { tick(w); if (a.meltdown) melted = i / 120; } check('unattended late arena MELTS DOWN (a real lose state)', melted !== null, melted ? `at ${melted.toFixed(1)}s` : 'never melted'); // WIN: quench everything by hand and the arena clears const w2 = makeWorld(11); const a2 = new Arena(); const d2 = a2.room(0); w2.load(d2); a2.enter(w2, 0); let cleared = null; for (let i = 0; i < 120 * 120 && cleared === null; i++) { tick(w2); // stand-in for a competent player: quench one radical per half second if (i % 60 === 0) { const r = w2.particles.find(p => p.radical); if (r) { r.radical = false; } } if (a2.cleared) cleared = i / 120; } check('arena CLEARS when the player keeps up', cleared !== null, cleared ? `at ${cleared.toFixed(1)}s` : 'never cleared'); } process.exit(report() ? 0 : 1);