Files
2026-07-22 00:34:09 -05:00

55 lines
2.3 KiB
JavaScript

// Validates the client-side dead-reckoning: applies real server snapshots,
// simulates at 60 fps like the browser, and measures prediction error
// between the locally simulated head and the server's authoritative head.
import WebSocket from 'ws';
import { applySnakeSnaps, simulateSnakes, State } from '../public/js/client/src/state.js';
const state = new State();
const ws = new WebSocket('ws://localhost:8080/ws');
let myAngle = 0.7;
let myBoost = false;
let samples = 0, sumErr = 0, maxErr = 0, resets = 0;
ws.on('open', () => ws.send(JSON.stringify({ t: 'join', name: 'SimTest' })));
ws.on('message', (d) => {
const m = JSON.parse(d);
if (m.t === 'init') { state.myId = m.id; state.arena = m.arena; return; }
if (m.t !== 'snap') return;
const before = state.snakes.get(state.myId);
const prevHead = before ? [before.pts[0], before.pts[1]] : null;
applySnakeSnaps(state, m.snakes);
if (m.you) state.you = m.you;
for (const ev of m.ev) if (ev.t === 'die' && ev.id === state.myId) ws.send(JSON.stringify({ t: 'respawn' }));
const me = state.snakes.get(state.myId);
const snap = m.snakes.find((s) => s.id === state.myId);
if (me && snap) {
if (prevHead && Math.hypot(prevHead[0] - snap.p[0], prevHead[1] - snap.p[1]) > 140) resets++;
const err = Math.hypot(me.pts[0] - snap.p[0], me.pts[1] - snap.p[1]);
sumErr += err; samples++;
if (err > maxErr) maxErr = err;
}
});
// mimic the browser: input at ~33 Hz, simulation at 60 fps
setInterval(() => {
myAngle += (Math.random() - 0.5) * 0.3; // wander like a mouse would
myBoost = Math.random() < 0.1; // sometimes boost
const me = state.snakes.get(state.myId);
if (me && Math.hypot(me.pts[0], me.pts[1]) > 3000) {
myAngle = Math.atan2(-me.pts[1], -me.pts[0]); // steer back to safety
myBoost = false;
}
ws.send(JSON.stringify({ t: 'in', a: myAngle, b: myBoost }));
}, 30);
setInterval(() => simulateSnakes(state, 1 / 60, myAngle, myBoost && state.you.score > 20), 1000 / 60);
setTimeout(() => {
console.log(`samples=${samples} avgErr=${(sumErr / Math.max(1, samples)).toFixed(2)}px maxErr=${maxErr.toFixed(2)}px hardResets=${resets}`);
if (samples > 100 && sumErr / samples < 25 && resets === 0) {
console.log('SIM TEST PASSED (prediction tracks server closely)');
process.exit(0);
}
console.log('SIM TEST FAILED');
process.exit(1);
}, 20000);