import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, '..');
const DATA_DIR = path.join(ROOT, 'games/nordschleife/data');
function readJson(fileName) {
return JSON.parse(fs.readFileSync(path.join(DATA_DIR, fileName), 'utf8'));
}
function fail(message) {
console.error(`Nordschleife validation failed: ${message}`);
process.exit(1);
}
function distance2d(a, b) {
return Math.hypot(b.x - a.x, b.z - a.z);
}
function validateTrack(track) {
if (track.version !== 2) fail('track.json must be version 2');
if (track.lengthM < 20500 || track.lengthM > 21100) fail(`unexpected length ${track.lengthM}`);
if (!Array.isArray(track.samples) || track.samples.length < 3500) fail('not enough track samples');
if (!track.validation?.ok) fail('track validation metadata is not ok');
let maxSpacing = 0;
let maxTurnDeg = 0;
for (let i = 0; i < track.samples.length; i++) {
const a = track.samples[(i - 1 + track.samples.length) % track.samples.length];
const b = track.samples[i];
const c = track.samples[(i + 1) % track.samples.length];
for (const key of ['x', 'y', 'z', 't', 'distanceM']) {
if (!Number.isFinite(b[key])) fail(`sample ${i} has invalid ${key}`);
}
maxSpacing = Math.max(maxSpacing, distance2d(b, c));
const v1 = { x: b.x - a.x, z: b.z - a.z };
const v2 = { x: c.x - b.x, z: c.z - b.z };
const n1 = Math.hypot(v1.x, v1.z);
const n2 = Math.hypot(v2.x, v2.z);
if (n1 > 0 && n2 > 0) {
const dot = Math.max(-1, Math.min(1, (v1.x * v2.x + v1.z * v2.z) / (n1 * n2)));
maxTurnDeg = Math.max(maxTurnDeg, Math.acos(dot) * 180 / Math.PI);
}
}
if (maxSpacing > 6) fail(`sample spacing too large: ${maxSpacing.toFixed(2)}m`);
if (maxTurnDeg > 25) fail(`turn spike too sharp: ${maxTurnDeg.toFixed(2)}deg`);
}
function validateSectors(sectors) {
if (!Array.isArray(sectors.sectors) || sectors.sectors.length < 30) fail('not enough sectors');
let prev = 0;
for (const sector of sectors.sectors) {
if (sector.t0 < prev - 1e-6 || sector.t1 <= sector.t0) fail(`bad sector range for ${sector.name}`);
prev = sector.t1;
}
if (Math.abs(sectors.sectors[0].t0) > 1e-6) fail('first sector must start at 0');
if (Math.abs(sectors.sectors[sectors.sectors.length - 1].t1 - 1) > 1e-6) fail('last sector must end at 1');
}
function validateCheckpoints(checkpoints) {
if (!Array.isArray(checkpoints.checkpoints) || checkpoints.checkpoints.length !== 20) fail('expected 20 checkpoints');
for (let i = 0; i < checkpoints.checkpoints.length; i++) {
const cp = checkpoints.checkpoints[i];
if (cp.index !== i) fail(`checkpoint ${i} has wrong index`);
if (!Number.isFinite(cp.x) || !Number.isFinite(cp.y) || !Number.isFinite(cp.z)) fail(`checkpoint ${i} has invalid coordinates`);
if (i > 0 && cp.t <= checkpoints.checkpoints[i - 1].t) fail(`checkpoint ${i} is out of order`);
}
}
validateTrack(readJson('track.json'));
validateSectors(readJson('sectors.json'));
validateCheckpoints(readJson('checkpoints.json'));
console.log('Nordschleife data validation passed');