SOURCE / PINNED RELEASE
Made of little things.
Napplet Machines V8
- Release
- b3f614e739f0…
- Author-recorded commit
- 534f19acb4ba…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-b5572362d4a
Archive hash verified: 0f7feb97e2c3d336…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import assert from 'node:assert/strict';
import test from 'node:test';
import { load } from './load.mjs';
const {
Road,
presets,
spline,
validateTrack,
decodeTrack,
roadEdge,
roadBoundary,
TRACK_HALF_SIZE,
MAX_POINT_WIDTH,
} = load('src/track.ts');
const {
createWorld,
spawn,
advanceBody,
stepWorld,
collide,
cars,
idleInput,
DT,
} = load('src/physics.ts');
const { validWorld, validInput, validRoom } = load('src/network.ts');
const driver = (car = 0) => ({ id: 'you', name: 'You', car, ai: false });
test('closed spline is continuous in position and tangent at the seam', () => {
for (const track of presets) {
assert.ok(validateTrack(track));
const road = new Road(track),
a = spline(track, 0),
b = spline(track, track.points.length),
l = road.samples[0],
r = road.samples.at(-1);
assert.ok(Math.hypot(a.x - b.x, a.y - b.y, a.z - b.z) < 1e-8);
assert.ok(Math.hypot(l.tx - r.tx, l.ty - r.ty) < 1e-6);
assert.ok(road.length > 150);
}
});
test('width, height and wall flags survive track sampling', () => {
const t = structuredClone(presets[0]);
t.points[0].width = 13;
t.points[1].width = 5;
t.points[1].wallThickness = 1.8;
t.points[0].z = 20;
t.points[0].wall = false;
const r = new Road(t);
assert.equal(r.samples[0].width, 13);
const mid = r.samples.findIndex((p) => Math.abs(p.u - 0.5) < 1e-9);
assert.equal(r.samples[mid].width, 9);
assert.equal(r.samples.find((p) => p.u === 1).width, 5);
assert.equal(r.samples.find((p) => p.u === 1).wallThickness, 1.8);
assert.equal(r.samples.at(-1).width, 13);
for (let i = 0; r.samples[i].u <= 1; i++)
assert.ok(r.samples[i].width >= 5 && r.samples[i].width <= 13);
const between = r.at(
(r.samples[mid].distance + r.samples[mid + 1].distance) / 2,
);
assert.ok(between.width < 9 && between.width > r.samples[mid + 1].width);
assert.ok(
Math.abs(
between.distance -
(r.samples[mid].distance + r.samples[mid + 1].distance) / 2,
) < 1e-8,
);
assert.equal(r.samples[0].z, 20);
assert.equal(r.samples[0].wall, false);
const p = r.nearest(t.points[0].x, t.points[0].y);
assert.ok(Math.abs(p.offset) < 1e-9);
});
test('mass-weighted impact conserves planar momentum and separates cars', () => {
const road = new Road(presets[0]),
a = spawn(driver(0), road, 0),
b = spawn({ ...driver(2), id: 'other' }, road, 1);
Object.assign(a, { x: 0, y: 0, z: 3, vx: 15, vy: 0, angle: 0 });
Object.assign(b, { x: 1.8, y: 0, z: 3, vx: 0, vy: 0, angle: 0 });
const before = a.vx * cars[0].mass + b.vx * cars[2].mass;
collide(a, b);
assert.ok(b.vx > 0);
assert.ok(a.vx < 15);
assert.ok(
Math.abs(a.vx * cars[0].mass + b.vx * cars[2].mass - before) < 1e-9,
);
assert.ok(b.x - a.x > 1.8);
});
test('a fall recovers to the last road checkpoint without advancing a lap', () => {
const road = new Road(presets[0]),
b = spawn(driver(), road, 0);
b.x = 80;
b.y = 80;
b.z = -6;
advanceBody(b, idleInput(), road);
assert.equal(b.falls, 1);
assert.ok(b.z > 0);
assert.equal(b.lap, 0);
assert.ok(b.respawn > 0);
});
test('held throttle accelerates and brake dissipates speed', () => {
const r = new Road(presets[1]),
b = spawn(driver(), r, 0);
for (let i = 0; i < 45; i++)
advanceBody(b, { throttle: 1, steer: 0, brake: false, reset: false }, r);
const speed = Math.hypot(b.vx, b.vy);
assert.ok(speed > 7);
for (let i = 0; i < 15; i++)
advanceBody(b, { ...idleInput(), brake: true }, r);
assert.ok(Math.hypot(b.vx, b.vy) < speed);
});
test('AI racers complete ordered laps on all supplied circuits', () => {
for (const track of presets) {
const r = new Road(track),
w = createWorld(
Array.from({ length: 6 }, (_, i) => ({
id: `ai-${i}`,
name: `Bot ${i}`,
car: i % 4,
ai: true,
})),
r,
);
w.countdown = 0;
// A human spectator keeps the race timer alive without granting a finish.
w.bodies[0].ai = false;
w.bodies[0].id = 'spectator';
for (let i = 0; i < 60 * Math.max(180, (r.length * 3) / 9 + 60); i++) {
stepWorld(w, r, new Map());
if (w.bodies.slice(1).every((b) => b.finished)) break;
}
console.log(
track.name,
w.bodies.slice(1).map((b) => ({
lap: b.lap,
time: Math.round(b.finished),
falls: b.falls,
progress: Math.round(b.progress),
})),
);
assert.ok(
w.bodies.slice(1).every((b) => b.finished > 0),
`${track.name}: not every AI finished`,
);
assert.ok(
w.bodies.every((b) => [b.x, b.y, b.z, b.vx, b.vy].every(Number.isFinite)),
);
}
});
test('race finish requires three ordered laps, not a teleport to the start', () => {
const r = new Road(presets[1]),
w = createWorld([driver()], r);
w.countdown = 0;
const b = w.bodies[0];
b.progress = r.length * 3;
b.nextGate = 0;
stepWorld(w, r, new Map());
assert.equal(b.finished, 0);
assert.equal(w.ended, false);
});
test('simulation is repeatable with the same tick inputs', () => {
const r = new Road(presets[1]),
a = createWorld([driver()], r),
b = createWorld([driver()], r);
const inp = new Map([
['you', { throttle: 1, steer: 0.1, brake: false, reset: false }],
]);
for (let i = 0; i < 360; i++) {
stepWorld(a, r, inp);
stepWorld(b, r, inp);
}
assert.equal(JSON.stringify(a), JSON.stringify(b));
assert.equal(DT, 1 / 60);
});
test('peer validation rejects malformed states and unbounded controls', () => {
const w = createWorld([driver()], new Road(presets[0]));
assert.ok(validWorld(w));
assert.equal(validWorld({ ...w, tick: NaN }), false);
const bad = structuredClone(w);
bad.bodies[0].x = Infinity;
assert.equal(validWorld(bad), false);
assert.equal(validWorld({ ...w, bodies: [w.bodies[0], w.bodies[0]] }), false);
assert.ok(validInput(idleInput()));
assert.equal(validInput({ ...idleInput(), steer: 5 }), false);
assert.equal(validInput({ ...idleInput(), throttle: NaN }), false);
assert.equal(
validRoom({ room: 'r', name: 'x', capacity: 99, peers: [] }),
false,
);
});
test('malformed track neighbors are rejected without throwing', () => {
const t = structuredClone(presets[0]);
t.points[1] = null;
assert.equal(validateTrack(t), false);
});
test('host rejects an invalid edited track before announcing the race', async () => {
const { Multiplayer } = load('src/network.ts');
const host = new Multiplayer();
host.actor = host.authority = 'test-host';
const track = structuredClone(presets[0]);
track.points[1] = { ...track.points[0] };
await assert.rejects(
host.begin(track, createWorld([driver()], new Road(presets[0]))),
/crowded track points/,
);
assert.equal(host.start, null);
});
test('earlier saved drafts upgrade width at every point without mutating the source', () => {
const old = {
version: 1,
name: 'Earlier draft',
width: 11,
points: presets[0].points.map(({ x, y, z, wall }) => ({ x, y, z, wall })),
};
const upgraded = decodeTrack(old);
assert.ok(validateTrack(upgraded));
assert.ok(
upgraded.points.every((p) => p.width === 14.3 && p.wallThickness === 0.6),
);
assert.equal(old.points[0].width, undefined);
assert.equal(decodeTrack({ ...old, width: Infinity }), null);
assert.equal(
decodeTrack({ ...old, points: [null, ...old.points.slice(1)] }),
null,
);
for (const property of ['width', 'wallThickness']) {
const invalid = structuredClone(upgraded);
invalid.points[1][property] = NaN;
assert.equal(validateTrack(invalid), false);
}
});
test('version 2 drafts widen once, retain section settings and enforce the new width ceiling', () => {
const old = structuredClone(presets[0]);
old.version = 2;
old.points.forEach((p, i) => {
p.width = i === 0 ? 16 : 7.5;
});
const before = JSON.stringify(old),
upgraded = decodeTrack(old);
assert.ok(validateTrack(upgraded));
assert.equal(upgraded.version, 3);
assert.equal(upgraded.points[0].width, 20.8);
assert.equal(upgraded.points[1].width, 9.75);
assert.equal(JSON.stringify(old), before);
assert.equal(JSON.stringify(decodeTrack(upgraded)), JSON.stringify(upgraded));
for (let i = 0; i < old.points.length; i++) {
const { width: _, ...legacy } = old.points[i];
const { width: __, ...current } = upgraded.points[i];
assert.equal(JSON.stringify(current), JSON.stringify(legacy));
}
upgraded.points[0].width = MAX_POINT_WIDTH + 0.01;
assert.equal(validateTrack(upgraded), false);
for (const width of [4.99, 16.01, Infinity, NaN]) {
old.points[0].width = width;
assert.equal(decodeTrack(old), null);
}
});
test('placement waits for a location, splits the clicked section and inherits its settings', () => {
const { TrackEditor } = load('src/editor.ts');
const track = structuredClone(presets[0]);
track.points[3].width = 5;
track.points[4].width = 15;
track.points[3].wallThickness = 1.4;
const editor = new TrackEditor({ addEventListener() {}, style: {} }, track);
editor.setTool('add');
assert.equal(track.points.length, 10);
const middle = editor.road.samples.find((p) => p.u === 3.5);
const xy = { x: middle.x - middle.ty * 2, y: middle.y + middle.tx * 2 }; // The click is deliberately off the old curve.
assert.ok(editor.addAt(xy.x, xy.y));
assert.equal(editor.selected, 4);
assert.equal(track.points.length, 11);
assert.ok(Math.abs(track.points[4].x - xy.x) < 1e-9);
assert.ok(Math.abs(track.points[4].y - xy.y) < 1e-9);
assert.ok(Math.abs(track.points[4].width - 10) < 0.1);
assert.equal(track.points[4].wall, false);
assert.equal(track.points[4].wallThickness, 1.4);
assert.ok(validateTrack(track));
assert.equal(editor.addAt(xy.x, xy.y), false);
assert.equal(track.points.length, 11);
editor.setTool('move');
assert.equal(editor.tool, 'move');
});
function square(width = 10, thickness = 1) {
return {
version: 3,
name: 'Square',
points: [
[-30, -30],
[30, -30],
[30, 30],
[-30, 30],
].map(([x, y]) => ({
x,
y,
z: 3,
width,
wall: true,
wallThickness: thickness,
})),
};
}
test('road support uses local width, with safe single-file starts on narrow tracks', () => {
const t = square();
t.points[0].width = 5;
t.points[2].width = 16;
t.points.forEach((p) => (p.wall = false));
const r = new Road(t);
for (const [index, grounded] of [
[0, false],
[2, true],
]) {
const p = r.samples.find((p) => p.u === index),
xy = roadEdge(p, 4),
b = spawn(driver(), r, 0);
Object.assign(b, { ...xy, z: p.z + 0.65 });
advanceBody(b, idleInput(), r);
assert.equal(b.grounded, grounded);
}
const narrow = new Road(square(5, 2));
const bodies = Array.from({ length: 6 }, (_, i) =>
spawn({ ...driver(), id: String(i) }, narrow, i),
);
assert.ok(bodies.every((b) => !narrow.wallContact(b.x, b.y, b.z)));
for (let i = 1; i < 6; i++)
assert.ok(
Math.hypot(bodies[i].x - bodies[i - 1].x, bodies[i].y - bodies[i - 1].y) >
3,
);
});
test('solid walls collide at inner/outer faces and section ends, with open gaps', () => {
const t = square(10, 2);
t.points[1].wall = false;
const r = new Road(t),
p = r.samples.find((p) => p.u === 0.5),
inner = roadEdge(p, 3.8),
outer = roadEdge(p, 6.2);
const innerHit = r.wallContact(inner.x, inner.y, 3.65),
outerHit = r.wallContact(outer.x, outer.y, 3.65);
assert.ok(innerHit && outerHit);
assert.ok(innerHit.nx * -p.ty + innerHit.ny * p.tx < -0.9);
assert.ok(outerHit.nx * -p.ty + outerHit.ny * p.tx > 0.9);
const thin = new Road(square(10, 0.3));
assert.equal(thin.wallContact(inner.x, inner.y, 3.65), null);
const end = r.samples.find((p) => p.u === 1),
edge = roadEdge(end, 5);
const capHit = r.wallContact(
edge.x + end.tx * 0.3,
edge.y + end.ty * 0.3,
3.65,
);
assert.ok(capHit && capHit.nx * end.tx + capHit.ny * end.ty > 0.9);
const gap = roadEdge(
r.samples.find((p) => p.u === 1.5),
5,
);
assert.equal(r.wallContact(gap.x, gap.y, 3.65), null);
assert.equal(r.wallContact(inner.x, inner.y, 6), null);
const b = spawn(driver(), r, 0);
Object.assign(b, {
...inner,
z: 3.65,
vx: -p.ty * 4,
vy: p.tx * 4,
angle: Math.atan2(p.tx, -p.ty),
});
advanceBody(b, idleInput(), r);
assert.ok(
b.vx * -p.ty + b.vy * p.tx < 0,
'impact bounces away from the wall',
);
});
test('rear bumper offsets do not turn a forward push into a sideways shove', () => {
const road = new Road(presets[0]);
for (const offset of [-0.25, 0, 0.25]) {
const a = spawn(driver(), road, 0),
b = spawn({ ...driver(), id: 'ahead' }, road, 1);
Object.assign(a, { x: 0, y: 0, z: 3.65, angle: 0, vx: 20, vy: 0, spin: 0 });
Object.assign(b, {
x: 1.9,
y: offset,
z: 3.65,
angle: 0,
vx: 8,
vy: 0,
spin: 0,
});
collide(a, b);
assert.ok(b.vx > 8, 'the front car receives the push');
assert.ok(
Math.abs(a.vy) < 0.01 && Math.abs(b.vy) < 0.01,
`rear offset ${offset}: lateral velocities ${a.vy}, ${b.vy}`,
);
assert.equal(a.angle, 0, 'a collision never teleports the heading');
}
});
test('grazing bumper spin tends to zero with impact strength', () => {
const road = new Road(presets[0]),
a = spawn(driver(), road, 0),
b = spawn({ ...driver(), id: 'ahead' }, road, 1);
Object.assign(a, {
x: 0,
y: 0,
z: 3.65,
angle: 0,
vx: 10.0001,
vy: 3,
spin: 0,
});
Object.assign(b, { x: 1.9, y: 0, z: 3.65, angle: 0, vx: 10, vy: 0, spin: 0 });
collide(a, b);
assert.ok(
Math.abs(a.spin) < 0.005 && Math.abs(b.spin) < 0.005,
`grazing spin ${a.spin}, ${b.spin}`,
);
});
test('parked cars follow the starting grid slope', () => {
const track = square();
[10, 18, 10, 3].forEach((z, i) => (track.points[i].z = z));
const road = new Road(track),
b = spawn(driver(), road, 0),
p = road.nearest(b.x, b.y, b.z - 0.65);
assert.ok(Math.abs(p.slope) > 0.1, 'fixture is on a slope');
assert.ok(
Math.abs(b.pitch - Math.atan(p.slope)) < 0.06,
`pitch ${b.pitch}, slope ${p.slope}`,
);
});
test('a parked car facing across a slope rolls with the surface instead of pointing its nose uphill', () => {
const track = square();
track.points[1].z = 18;
const road = new Road(track),
p = road.samples.find((p) => p.u === 0.5),
b = spawn(driver(), road, 0);
Object.assign(b, {
x: p.x,
y: p.y,
z: p.z + 0.65,
angle: Math.atan2(p.ty, p.tx) + Math.PI / 2,
vx: 0,
vy: 0,
vz: 0,
});
for (let i = 0; i < 30; i++) advanceBody(b, idleInput(), road);
assert.ok(Math.abs(b.pitch) < 0.07, `cross-slope pitch ${b.pitch}`);
assert.ok(Math.abs(b.roll) > 0.08, `cross-slope roll ${b.roll}`);
});
// Broad level asphalt isolates tire response from barriers and track geometry.
const asphalt = {
length: 10000,
nearest: (x, y) => ({
x,
y,
z: 3,
width: 500,
left: 250,
right: 250,
offset: 0,
tx: 1,
ty: 0,
slope: 0,
distance: x,
}),
bounds: { minX: -1000, maxX: 1000, minY: -1000, maxY: 1000 },
deck: { at: () => ({ z: 3, gx: 0, gy: 0 }) },
wallContact: () => null,
};
function rollingCar(car = 0) {
return Object.assign(spawn(driver(car), new Road(presets[0]), 0), {
x: 0,
y: 0,
z: 3.65,
vx: 18,
vy: 0,
vz: 0,
angle: 0,
pitch: 0,
roll: 0,
});
}
test('steering builds yaw through tire forces and does not redirect momentum instantly', () => {
const b = rollingCar();
advanceBody(b, { ...idleInput(), steer: 1 }, asphalt);
assert.ok(b.steering > 0 && b.steering < 0.06);
assert.ok(b.angle > 0 && b.angle < 0.005, `first-tick yaw ${b.angle}`);
assert.ok(b.vx > 17.8 && Math.abs(b.vy) < 0.3);
const parked = rollingCar();
parked.vx = 0;
for (let i = 0; i < 60; i++)
advanceBody(parked, { ...idleInput(), steer: 1 }, asphalt);
assert.equal(
parked.angle,
0,
'steering alone cannot rotate a stationary car',
);
});
test('handbrake initiates a momentum-carrying drift, with release and countersteer recovery', () => {
const { slipAngle } = load('src/physics.ts');
for (let car = 0; car < 4; car++) {
const grip = rollingCar(car),
drift = rollingCar(car);
for (let i = 0; i < 45; i++) {
advanceBody(grip, { ...idleInput(), throttle: 0.6, steer: 0.6 }, asphalt);
advanceBody(
drift,
{ ...idleInput(), throttle: 0.6, steer: 0.6, brake: true },
asphalt,
);
}
const slip = Math.abs(slipAngle(drift));
assert.ok(
slip > Math.abs(slipAngle(grip)) + 0.12,
`${cars[car].name}: drift breaks rear grip`,
);
assert.ok(
Math.hypot(drift.vx, drift.vy) > 10,
'handbrake preserves useful momentum',
);
for (let i = 0; i < 120; i++)
advanceBody(
drift,
{
...idleInput(),
throttle: 0.5,
steer: Math.max(-1, Math.min(1, slipAngle(drift) * 2)),
},
asphalt,
);
assert.ok(
Math.abs(slipAngle(drift)) < 0.12,
`${cars[car].name}: slide catches progressively`,
);
assert.ok(drift.handbrake < 0.001);
}
});
test('slope pose follows heading at rest, in reverse, uphill and downhill', () => {
const { surfacePose } = load('src/physics.ts');
const p = { tx: 1, ty: 0, slope: 0.4 };
assert.ok(Math.abs(surfacePose(p, 0).pitch - Math.atan(0.4)) < 1e-9);
assert.ok(Math.abs(surfacePose(p, Math.PI).pitch + Math.atan(0.4)) < 1e-9);
assert.ok(Math.abs(surfacePose(p, Math.PI / 2).roll + Math.atan(0.4)) < 1e-9);
const t = square();
[10, 18, 10, 3].forEach((z, i) => (t.points[i].z = z));
const road = new Road(t),
b = spawn(driver(), road, 0);
for (let i = 0; i < 20; i++)
advanceBody(b, { ...idleInput(), throttle: -1 }, road);
assert.ok(b.pitch > 0, 'reversing does not flip the surface tilt');
});
test('suspension uses relative road velocity on fast climbs, then releases at a crest', () => {
const b = rollingCar();
b.vz = 9;
const uphill = {
...asphalt,
deck: { at: (x) => ({ z: 3 + x * 0.5, gx: 0.5, gy: 0 }) },
nearest: (x, y) => ({
...asphalt.nearest(x, y),
z: 3 + x * 0.5,
slope: 0.5,
}),
};
advanceBody(b, idleInput(), uphill);
assert.equal(
b.grounded,
true,
'uphill vertical speed greater than 5 remains supported',
);
b.vz = 8;
advanceBody(b, idleInput(), asphalt);
assert.equal(b.grounded, false, 'a crest can launch the chassis');
assert.ok(b.vz < 8, 'airborne gravity acts');
});
test('rotated chassis contacts conserve momentum without manufacturing energy', () => {
const { yawInertia } = load('src/contacts.ts');
let seed = 872;
const rand = () => {
seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0;
return seed / 4294967296;
};
for (let i = 0; i < 400; i++) {
const a = rollingCar(i % 4),
b = rollingCar((i + 1) % 4);
b.id = 'other';
Object.assign(a, {
vx: rand() * 20 - 10,
vy: rand() * 20 - 10,
spin: rand() * 4 - 2,
angle: rand() * 6.28,
});
Object.assign(b, {
x: rand() * 2,
y: rand() * 2,
vx: rand() * 20 - 10,
vy: rand() * 20 - 10,
spin: rand() * 4 - 2,
angle: rand() * 6.28,
});
const ma = cars[a.car].mass,
mb = cars[b.car].mass;
const energy = () =>
(ma * (a.vx * a.vx + a.vy * a.vy)) / 2 +
(mb * (b.vx * b.vx + b.vy * b.vy)) / 2 +
(yawInertia(ma) * a.spin * a.spin) / 2 +
(yawInertia(mb) * b.spin * b.spin) / 2;
const initial = energy(),
px = a.vx * ma + b.vx * mb,
py = a.vy * ma + b.vy * mb;
collide(a, b);
assert.ok(Math.abs(a.vx * ma + b.vx * mb - px) < 1e-8);
assert.ok(Math.abs(a.vy * ma + b.vy * mb - py) < 1e-8);
assert.ok(
energy() <= initial + 1e-8,
`contact ${i} gained energy: ${initial} -> ${energy()}`,
);
assert.ok([a.x, a.y, b.x, b.y, a.spin, b.spin].every(Number.isFinite));
}
});
test('race buildings are deterministic and keep their whole footprint clear of roads and neighbors', () => {
const { raceBuildings } = load('src/scenery.ts');
for (const t of [...presets, square(16, 2)]) {
const road = new Road(t),
buildings = raceBuildings(road);
assert.ok(
buildings.length >= 5,
`${t.name}: ${buildings.length} buildings`,
);
assert.equal(
JSON.stringify(buildings),
JSON.stringify(raceBuildings(new Road(structuredClone(t)))),
);
assert.equal(new Set(buildings.map((b) => b.kind)).size, 3);
buildings.forEach((b, i) => {
for (const p of road.samples)
assert.ok(
Math.hypot(b.x - p.x, b.y - p.y) >
b.radius + p.width / 2 + (p.wall ? p.wallThickness / 2 : 0) + 1.4,
);
for (const other of buildings.slice(i + 1))
assert.ok(
Math.hypot(b.x - other.x, b.y - other.y) >
b.radius + other.radius + 2,
);
});
}
});
test('steering and handbrake prediction state is required and bounded on the wire', () => {
const world = createWorld([driver()], new Road(presets[0]));
assert.equal(validWorld(world), true);
for (const [key, value] of [
['steering', undefined],
['steering', 1.01],
['handbrake', NaN],
['handbrake', -0.01],
['handbrake', 1.01],
]) {
const bad = structuredClone(world);
bad.bodies[0][key] = value;
assert.equal(validWorld(bad), false, `${key}: ${value}`);
}
});
test('acceleration lifts the nose and service braking compresses the front suspension', () => {
const accelerating = rollingCar(),
braking = rollingCar();
advanceBody(accelerating, { ...idleInput(), throttle: 1 }, asphalt);
advanceBody(braking, { ...idleInput(), throttle: -1 }, asphalt);
assert.ok(accelerating.pitch > 0, 'rear squat under acceleration');
assert.ok(braking.pitch < 0, 'nose dive under braking');
});
const { ControllerDriving, mixDriving, steeringAt, pedalAt } =
load('src/controls.ts');
function samplePad(index = 2, mapping = 'standard') {
return {
index,
id: `Controller ${index}`,
mapping,
connected: true,
axes: [0, 0],
buttons: Array.from({ length: 17 }, () => ({ value: 0, pressed: false })),
};
}
test('controller claim, analog input and one-shot actions use the selected browser slot', () => {
const a = samplePad(2),
b = samplePad(7),
pads = [null, null, a, null, null, null, null, b];
const controller = new ControllerDriving({
read: () => pads,
active: () => true,
});
a.buttons[0].value = 1;
assert.equal(
controller.poll().selected,
null,
'a held connection button is not a fresh claim',
);
a.buttons[0].value = 0;
controller.poll();
a.buttons[0].value = 1;
assert.equal(controller.poll().selected, 2);
a.buttons[0].value = 0;
a.buttons[7].value = 0.7;
a.buttons[6].value = 0.2;
a.axes[0] = -0.6;
b.buttons[0].value = 1;
b.axes[0] = 1;
const frame = controller.poll();
assert.equal(frame.selected, 2, 'second controller cannot steal the car');
assert.ok(Math.abs(frame.drive.throttle - 0.5) < 1e-8);
assert.ok(frame.drive.steer > 0.4 && frame.drive.steer < 0.6);
a.axes[0] = 0.1;
assert.equal(controller.poll().drive.steer, 0, 'stick dead zone');
a.buttons[1].value = 1;
a.buttons[3].value = 1;
a.buttons[9].value = 1;
const first = controller.poll(),
held = controller.poll();
assert.ok(first.drive.brake && first.recover && first.pause);
assert.ok(
held.drive.brake && !held.recover && !held.pause,
'held actions do not repeat edges',
);
controller.dispose();
assert.equal(controller.poll().status, 'closed');
});
test('controller disconnect, replacement, focus loss and unknown mappings cannot leave stale drive', () => {
let active = true;
const a = samplePad(),
pads = [a];
const c = new ControllerDriving({ read: () => pads, active: () => active });
c.poll();
a.buttons[0].value = 1;
c.poll();
a.buttons[0].value = 0;
a.buttons[7].value = 1;
assert.equal(c.poll().drive.throttle, 1);
active = false;
assert.equal(c.poll().drive.throttle, 0);
active = true;
a.buttons[9].value = 1;
assert.equal(c.poll().pause, false, 'focus return suppresses phantom pause');
const replacement = samplePad();
replacement.id = 'Different controller';
replacement.buttons[0].value = 1;
pads[0] = replacement;
const lost = c.poll();
assert.ok(lost.disconnected);
assert.equal(lost.selected, null);
assert.equal(lost.drive.throttle, 0);
replacement.buttons[0].value = 0;
c.poll();
replacement.buttons[0].value = 1;
assert.equal(c.poll().selected, 2);
pads.length = 0;
const empty = c.poll();
assert.ok(empty.disconnected);
assert.equal(empty.drive.throttle, 0);
const unknown = samplePad(3, '');
pads.push(unknown);
c.poll();
unknown.buttons[0].value = 1;
const unmapped = c.poll();
assert.ok(unmapped.unmapped);
assert.equal(unmapped.selected, null);
c.dispose();
const missing = new ControllerDriving({
read: () => {
throw new Error('unavailable');
},
active: () => true,
});
assert.equal(missing.poll().status, 'unavailable');
missing.dispose();
});
test('touch steering is proportional and the pedal pad supports one-thumb gas, brake and powered drift', () => {
assert.equal(steeringAt(100, 20, 160), 0);
assert.equal(steeringAt(20, 20, 160), 1);
assert.equal(steeringAt(180, 20, 160), -1);
assert.ok(steeringAt(125, 20, 160) > -1 && steeringAt(125, 20, 160) < -0.3);
assert.equal(pedalAt(40, 20, 132, 110), 'go');
assert.equal(pedalAt(30, 85, 132, 110), 'stop');
assert.equal(pedalAt(105, 85, 132, 110), 'drift');
assert.equal(pedalAt(-1, 30, 132, 110), '');
assert.equal(
mixDriving({ ...idleInput(), steer: 0.4 }, { ...idleInput(), steer: -0.4 })
.steer,
0,
);
assert.equal(
mixDriving(
{ ...idleInput(), throttle: 0.7 },
{ ...idleInput(), throttle: -1 },
).throttle,
-1,
);
assert.equal(
mixDriving(
{ ...idleInput(), steer: 0.6 },
{ ...idleInput(), throttle: 1, brake: true },
).steer,
0.6,
);
});
test('tight elevated corners close without inverted road or thick-wall triangles', () => {
const original = structuredClone(presets[2]);
original.points.forEach((p) => {
p.width = MAX_POINT_WIDTH;
p.wall = true;
p.wallThickness = 2;
});
const area = (a, b, c) =>
(b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
for (const track of [original, ...presets]) {
const road = new Road(track);
for (let i = 0; i < road.samples.length - 1; i++) {
const a = road.samples[i],
b = road.samples[i + 1];
assert.ok(
area(a.rightEdge, b.rightEdge, b.leftEdge) >= -1e-8,
`${track.name}: inverted road at ${a.u}`,
);
assert.ok(
area(a.rightEdge, b.leftEdge, a.leftEdge) >= -1e-8,
`${track.name}: inverted pocket at ${a.u}`,
);
}
for (const {
corners: [a, b, c, d],
} of road.walls)
assert.ok(
area(a, b, c) * area(a, c, d) >= -1e-9,
`${track.name}: folded thick wall`,
);
}
});
test('doubled workshop area roundtrips, drives and synchronizes beyond the old limits', () => {
assert.equal(TRACK_HALF_SIZE, 130);
const track = square();
track.points.forEach((p) => {
p.x = Math.sign(p.x) * 130;
p.y = Math.sign(p.y) * 130;
});
assert.ok(validateTrack(track));
assert.equal(JSON.stringify(decodeTrack(track)), JSON.stringify(track));
const road = new Road(track),
b = spawn(driver(), road, 0);
const center = roadEdge(road.at(road.length * 0.3), 0);
Object.assign(b, center, { z: center.z + 0.65, vx: 0, vy: 0 });
advanceBody(b, idleInput(), road);
assert.equal(b.falls, 0);
assert.ok(b.grounded);
assert.ok(validWorld({ ...createWorld([driver()], road), bodies: [b] }));
track.points[0].x = 130.01;
assert.equal(validateTrack(track), false);
const { TrackEditor } = load('src/editor.ts');
const editor = new TrackEditor(
{ clientWidth: 580, clientHeight: 580, addEventListener() {}, style: {} },
presets[0],
);
editor.zoom = 1;
assert.equal(editor.fromScreen(550, 30).x, 130);
assert.equal(editor.fromScreen(550, 30).y, -130);
assert.equal(editor.placement(131, 0).valid, false);
});
test('every authored course varies point widths, open edges and solid wall thickness', () => {
assert.equal(presets.length, 7);
for (const track of presets) {
assert.ok(new Set(track.points.map((p) => p.width)).size >= 3, track.name);
assert.ok(
track.points.some((p) => p.wall) && track.points.some((p) => !p.wall),
track.name,
);
assert.ok(
new Set(track.points.filter((p) => p.wall).map((p) => p.wallThickness))
.size >= 2,
track.name,
);
}
assert.ok(
presets
.slice(3)
.every(
(t) =>
t.points.some((p) => Math.abs(p.x) > 65) &&
t.points.some((p) => Math.abs(p.y) > 65),
),
);
});
test('closed pockets are drivable and preserve authored wall thickness', () => {
const t = structuredClone(presets[2]);
t.points.forEach((p) => {
p.width = MAX_POINT_WIDTH;
p.wall = true;
p.wallThickness = 2;
});
const road = new Road(t);
assert.ok(road.closedSections.size > 0);
for (const w of road.walls) {
assert.ok(
Math.abs(
Math.hypot(
w.corners[0].x - w.corners[3].x,
w.corners[0].y - w.corners[3].y,
) - 2,
) < 1e-8,
);
assert.ok(
Math.abs(
Math.hypot(
w.corners[1].x - w.corners[2].x,
w.corners[1].y - w.corners[2].y,
) - 2,
) < 1e-8,
);
}
t.points.forEach((p) => (p.wall = false));
const open = new Road(t),
p = open.samples.find(
(p) =>
Math.hypot(
p.leftEdge.x - (p.x - (p.ty * p.width) / 2),
p.leftEdge.y - (p.y + (p.tx * p.width) / 2),
) > 1,
);
assert.ok(p);
const inside = roadBoundary(p, 1, -0.3),
outside = roadBoundary(p, 1, 0.5);
for (const [position, expected] of [
[inside, true],
[outside, false],
]) {
const b = spawn(driver(), open, 0);
Object.assign(b, position, {
z: position.z + 0.65,
vx: 0,
vy: 0,
respawn: 0,
});
advanceBody(b, idleInput(), open);
assert.equal(b.grounded, expected);
}
});
test('figure-eight crossing selects the correct deck and road sampling remains bounded', () => {
const road = new Road(presets.find((t) => t.name === 'Skybridge Eight'));
assert.ok(road.nearest(0, 0, 18).z > 16);
assert.ok(road.nearest(0, 0, 3).z < 5);
assert.ok(road.deck.at(0, 0, 18).z > 16);
assert.ok(road.deck.at(0, 0, 3).z < 5);
for (const track of presets) {
const r = new Road(track);
assert.ok(r.samples.length < 1300);
for (let i = 0; i < r.samples.length - 1; i++) {
const a = r.samples[i],
b = r.samples[i + 1];
assert.ok(Math.hypot(a.x - b.x, a.y - b.y) <= 1.501);
}
}
});
