import test from 'node:test'; import assert from 'node:assert/strict'; import { ENVIRONMENT_ISLANDS, environmentWindAt, routeWeatherAt, semiRandomWindPattern, stormFrontAt, velocityWithEnvironment, } from '../src/environment-physics.js'; test('environment exposes collidable island geometry', () => { assert.equal(ENVIRONMENT_ISLANDS.length, 2); for (const island of ENVIRONMENT_ISLANDS) { assert.ok(island.radius > 0); assert.ok(island.turbulenceRadius > island.radius); } }); test('semi-random wind varies strength and direction across weather windows', () => { const samples = [0, 16000, 32000, 48000, 64000] .map((timeMs) => semiRandomWindPattern(timeMs)); const roundedStrengths = new Set(samples.map(({ strength }) => Math.round(strength * 10))); const roundedHeadings = new Set(samples.map(({ angle }) => Math.round(angle * 10))); assert.ok(roundedStrengths.size >= 3); assert.ok(roundedHeadings.size >= 3); for (const sample of samples) { assert.ok(sample.strength >= 4 && sample.strength < 28); } }); test('semi-random wind changes smoothly instead of jumping each frame', () => { const before = semiRandomWindPattern(15950); const after = semiRandomWindPattern(16050); const vector = ({ angle, strength }) => ({ x: Math.cos(angle) * strength, y: Math.sin(angle) * strength, }); const a = vector(before); const b = vector(after); assert.ok(Math.hypot(b.x - a.x, b.y - a.y) < 2.5); }); test('ambient wind changes over time and remains bounded', () => { const first = environmentWindAt({ x: 1200, y: 1200, timeMs: 0 }); const later = environmentWindAt({ x: 1200, y: 1200, timeMs: 24000 }); assert.ok(first.strength > 3 && first.strength < 55); assert.ok(later.strength > 3 && later.strength < 55); assert.notDeepEqual( [Math.round(first.x * 100), Math.round(first.y * 100)], [Math.round(later.x * 100), Math.round(later.y * 100)], ); }); test('islands produce a local turbulence contribution', () => { const island = ENVIRONMENT_ISLANDS[0]; const near = environmentWindAt({ x: island.x + island.radius + 20, y: island.y, timeMs: 6000, }); const far = environmentWindAt({ x: island.x + island.turbulenceRadius + 300, y: island.y, timeMs: 6000, }); assert.notEqual( Math.round(near.strength * 100), Math.round(far.strength * 100), ); }); test('environment velocity adds wind according to exposure', () => { const result = velocityWithEnvironment(100, -20, { x: 12, y: 8 }, 0.5); assert.equal(result.x, 106); assert.equal(result.y, -16); }); test('shipping lanes create calm cores with unstable edges and open sky beyond', () => { const routes = [{ id: 'shipping-1', from: { x: 1000, y: 1200 }, to: { x: 3600, y: 1200 }, width: 220, }]; const lane = routeWeatherAt({ x: 2200, y: 1200, routes }); const edge = routeWeatherAt({ x: 2200, y: 1500, routes }); const open = routeWeatherAt({ x: 2200, y: 2200, routes }); assert.equal(lane.zone, 'shipping-lane'); assert.ok(lane.routeInfluence > 0.9); assert.equal(edge.zone, 'route-edge'); assert.ok(edge.routeEdge > lane.routeEdge); assert.equal(open.zone, 'open-sky'); assert.ok(open.routeInfluence < 0.05); }); test('moving storm fronts can cross the world independent of route corridors', () => { const timeMs = 110000 * 0.25; const intensity = stormFrontAt({ x: 900, y: 5400 * 0.53, timeMs, worldWidth: 5400, worldHeight: 5400, }); assert.ok(intensity > 0.75); }); test('route-aware wind is calmer in a shipping lane than nearby open sky', () => { const routes = [{ id: 'shipping-1', from: { x: 1000, y: 1000 }, to: { x: 3600, y: 1000 }, width: 220, }]; const lane = environmentWindAt({ x: 2200, y: 1000, timeMs: 0, routes, islands: [], }); const open = environmentWindAt({ x: 2200, y: 2200, timeMs: 0, routes, islands: [], }); assert.equal(lane.zone, 'shipping-lane'); assert.equal(open.zone, 'open-sky'); assert.ok(lane.strength < open.strength); });