77 lines
2.7 KiB
JavaScript
77 lines
2.7 KiB
JavaScript
import { describe, it, expect } from 'vitest';
|
|
import { cloudFor, excludeVisibleBand, totalMass } from '../src/sim/clouds.js';
|
|
|
|
// Synthetic 3-lane chain: systems 0-1-2-3, each lane 2 days.
|
|
function chainGalaxy() {
|
|
return {
|
|
lanes: [
|
|
{ id: 0, a: 0, b: 1, days: 2, hazard: 0.1 },
|
|
{ id: 1, a: 1, b: 2, days: 2, hazard: 0.1 },
|
|
{ id: 2, a: 2, b: 3, days: 2, hazard: 0.1 },
|
|
],
|
|
};
|
|
}
|
|
|
|
function chainPlan() {
|
|
return {
|
|
id: 'p1', startTime: 0, arriveTime: 6, finalOutcome: 'ok',
|
|
originPlanet: 0, destPlanet: 1,
|
|
legs: [
|
|
{ laneId: 0, fromSys: 0, toSys: 1, depart: 0, arrive: 2, outcome: 'safe' },
|
|
{ laneId: 1, fromSys: 1, toSys: 2, depart: 2, arrive: 4, outcome: 'safe' },
|
|
{ laneId: 2, fromSys: 2, toSys: 3, depart: 4, arrive: 6, outcome: 'safe' },
|
|
],
|
|
};
|
|
}
|
|
|
|
const trackLength = (segs, galaxy) =>
|
|
segs.reduce((sum, s) => sum + (s.f1 - s.f0) * galaxy.lanes[s.laneId].days, 0);
|
|
|
|
describe('clouds', () => {
|
|
it('total mass is 1 while the journey is plausibly in progress', () => {
|
|
const g = chainGalaxy();
|
|
const plan = chainPlan();
|
|
for (const t of [0.5, 1.7, 3, 4.5]) {
|
|
const segs = cloudFor(g, plan, t);
|
|
expect(segs.length).toBeGreaterThan(0);
|
|
expect(totalMass(segs)).toBeCloseTo(1, 5);
|
|
for (const s of segs) {
|
|
expect(s.f0).toBeGreaterThanOrEqual(0);
|
|
expect(s.f1).toBeLessThanOrEqual(1);
|
|
expect(s.f1).toBeGreaterThan(s.f0);
|
|
}
|
|
}
|
|
});
|
|
|
|
it('uncertainty grows with elapsed legs (cloud covers more track later)', () => {
|
|
const g = chainGalaxy();
|
|
const plan = chainPlan();
|
|
const early = trackLength(cloudFor(g, plan, 1.0), g);
|
|
const late = trackLength(cloudFor(g, plan, 4.2), g);
|
|
expect(late).toBeGreaterThan(early);
|
|
});
|
|
|
|
it('mid-journey, mass spans more than one lane', () => {
|
|
const g = chainGalaxy();
|
|
const segs = cloudFor(g, chainPlan(), 3.5);
|
|
expect(new Set(segs.map((s) => s.laneId)).size).toBeGreaterThanOrEqual(2);
|
|
});
|
|
|
|
it('excludeVisibleBand removes the observed band and renormalizes', () => {
|
|
const segs = [
|
|
{ laneId: 0, fromSys: 0, toSys: 1, f0: 0.0, f1: 0.5, w: 0.5 },
|
|
{ laneId: 0, fromSys: 0, toSys: 1, f0: 0.5, f1: 1.0, w: 0.5 },
|
|
];
|
|
// Player sits in system 1: the band f > 0.85 on this lane is visible.
|
|
const out = excludeVisibleBand({ lanes: [{ id: 0, a: 0, b: 1, days: 2 }] }, segs, 1);
|
|
expect(totalMass(out)).toBeCloseTo(1, 5);
|
|
for (const s of out) expect(s.f1).toBeLessThanOrEqual(0.85 + 1e-9);
|
|
});
|
|
|
|
it('returns empty for finished or empty plans', () => {
|
|
const g = chainGalaxy();
|
|
expect(cloudFor(g, chainPlan(), 99)).toEqual([]);
|
|
expect(cloudFor(g, { ...chainPlan(), legs: [] }, 1)).toEqual([]);
|
|
});
|
|
});
|