// The heavy-world benchmark: same song, same seed, same planted population — // only the decision cadence varies. Sim-only (no render), field 128. import { buildSong } from '../src/song/song.js'; import { analyse } from '../src/audio/analyse.js'; import { decodeWav, mono } from '../src/audio/wav.js'; import { World } from '../src/sim/world.js'; import { readFileSync } from 'node:fs'; const name = process.argv[2] ?? 'runner'; const frames = Number(process.argv[3] ?? 300); const wav = decodeWav(readFileSync(new URL(`../../flow-state/test/songs/${name}.wav`, import.meta.url))); const song = buildSong(analyse(mono(wav.channels), wav.sampleRate), 12345); function plant(w) { // identical starting points: a dense 80×80 metropolis west of centre const F = w.field; for (let y = 24; y < 104; y++) { for (let x = 24; x < 104; x++) { const i = y * F + x; for (let s = 0; s < w.m; s++) w.pop[s][i] = 1.6; } } w.recount(); } const configs = []; const argCfg = (a) => { if (a.startsWith('w')) return { workers: Number(a.slice(1)) }; if (a.startsWith('v')) return { vmEvery: Number(a.slice(1)) }; return null; }; for (const a of process.argv.slice(2)) { const cfg = argCfg(a); if (cfg) configs.push(cfg); } if (!configs.length) configs.push({ workers: 0, vmEvery: 2 }, { workers: 2 }, { workers: 4 }); for (const cfg of configs) { const w = new World(song, { fps: 30, field: 128, genesis: 0, vmEvery: cfg.vmEvery ?? 2, workers: cfg.workers ?? 0 }); plant(w); const start = w.count; // warm-up for (let f = 0; f < 30; f++) w.step(); const t0 = process.hrtime.bigint(); for (let f = 0; f < frames; f++) w.step(); const ms = Number(process.hrtime.bigint() - t0) / 1e6 / frames; console.log(`workers=${w.pool?.length ?? 0} vmEvery=${w.vmEvery} sim ${ms.toFixed(1)} ms/frame (start ${Math.round(start)}, now ${Math.round(w.count)})`); w.dispose?.(); }