weather-patch-test.js
43 lines · in natura-src-1.0.0.zip as natura/weather-patch-test.js · a test
// weather-patch-test.js -- assertions for the local-sky system (cloud/rain/moisture the light + plants read).
const N = require('./natura.js');
const { makeWeatherPatch } = require('./weather-patch.js');
const { rngFrom, World } = N;
let pass = 0, fail = 0;
const ok = (c, m) => { if (c) pass++; else { fail++; console.log(' FAIL: ' + m); } };
function run(seed, secs, dt) { const sys = makeWeatherPatch({}); const st = sys.init(rngFrom(seed)); const trace = [];
for (let t = 0; t < secs; t += dt) { sys.step(st, {}, dt); trace.push({ cloud: st.cloud, rain: st.rain, moisture: st.moisture }); } return trace; }
console.log('weather-patch assertions:');
// 1. everything stays in [0,1] over a long run
{ const tr = run(3, 300, 0.5);
ok(tr.every(f => f.cloud >= 0 && f.cloud <= 1 && f.rain >= 0 && f.rain <= 1 && f.moisture >= 0 && f.moisture <= 1), 'cloud/rain/moisture stay in [0,1]'); }
// 2. rain only under a heavy sky
{ const tr = run(3, 600, 0.5);
ok(tr.every(f => f.rain <= 1e-4 || f.cloud > 0.6), 'rain only falls when the sky is overcast enough (cloud > 0.6)'); }
// 3. the sky VARIES: both wet spells and dry spells, cloud swinging clear<->overcast
{ const tr = run(5, 600, 0.5);
const wet = tr.some(f => f.rain > 0.1), dry = tr.some(f => f.rain === 0);
const maxC = Math.max(...tr.map(f => f.cloud)), minC = Math.min(...tr.map(f => f.cloud));
ok(wet && dry, 'the sky varies over 10 min -- both wet spells (rain) and dry spells');
ok(maxC - minC > 0.4, 'cloud swings between clearer and more overcast (' + minC.toFixed(2) + '..' + maxC.toFixed(2) + ')'); }
// 4. soil moisture responds -- rises in rain, drains when dry
{ const tr = run(7, 600, 0.5); const ms = tr.map(f => f.moisture);
ok(Math.max(...ms) - Math.min(...ms) > 0.1, 'soil moisture rises and falls with the weather (' + Math.min(...ms).toFixed(2) + '..' + Math.max(...ms).toFixed(2) + ')');
let up = false, down = false; for (let i = 1; i < ms.length; i++) { if (ms[i] > ms[i - 1] + 1e-6) up = true; if (ms[i] < ms[i - 1] - 1e-6) down = true; }
ok(up && down, 'moisture both wets (in rain) and drains (when dry)'); }
// 5. determinism
{ const a = run(9, 120, 0.5), b = run(9, 120, 0.5);
ok(a.every((f, i) => f.cloud === b[i].cloud && f.moisture === b[i].moisture), 'same seed -> identical weather (reproducible)'); }
// 6. composes in a Natura World
{ const w = World({ seed: 2, systems: [makeWeatherPatch({})] }); w.run(20, 1); const F = w.fields();
ok(F.weather && F.weather.cloud >= 0 && F.weather.cloud <= 1 && 'moisture' in F.weather, 'weather-patch composes in a World and exposes cloud/rain/moisture'); }
console.log(`\n${pass} passed, ${fail} failed`);
process.exit(fail ? 1 : 0);