rook/val Public Public: anyone can read this; nobody can change it.
forked from benjivers/val
workbenchthis machine
Check the drawing against the simulation's own motion
A body drawn at the server's speed takes even steps; the chase this replaced took a long one just after each snapshot and shorter ones after it. The test runs a real match at 30Hz, samples it the way a snapshot does, draws it at 60Hz and measures the spread of the per-frame steps: under 0.15 now, 0.22 with the old exponential smooth, which is the choppiness with a number on it. Co-Authored-By: Claude Opus 5 <[email protected]>
1 file
changed +65
−0
src/interp.test.ts +65
| @@ -163,3 +163,68 @@ test("the moment being drawn is always bracketed by samples on hand", () => { | ||
| 163 | 163 | expect(last.x - drawn.x).toBeGreaterThan(0); |
| 164 | 164 | expect(last.x - drawn.x).toBeLessThan(20); |
| 165 | 165 | }); |
| 166 | ||
| 167 | // --------------------------------------------- against the real simulation | |
| 168 | ||
| 169 | /** | |
| 170 | * The match's own motion, sampled at the rate the server broadcasts it and | |
| 171 | * drawn at the rate a browser draws: what a bot walking across the map | |
| 172 | * actually looks like. The samples carry exactly what a snapshot carries — | |
| 173 | * a position rounded to a tenth, thirty times a second. | |
| 174 | * | |
| 175 | * The measure is the spread of the per-frame steps. A body drawn at the | |
| 176 | * server's speed takes even steps; the exponential chase this replaced took | |
| 177 | * a long step just after each snapshot and shorter ones after it, which is | |
| 178 | * choppiness with a number on it — twice this spread, measured the same way. | |
| 179 | */ | |
| 180 | test("the simulation's own motion is drawn at an even speed", async () => { | |
| 181 | const { runBots } = await import("./bots.ts"); | |
| 182 | const { newMatch, tick } = await import("./match.ts"); | |
| 183 | const round2 = (n: number) => Math.round(n * 10) / 10; | |
| 184 | ||
| 185 | // A match of its own: nothing here touches the arena everybody shares. | |
| 186 | const state = newMatch(); | |
| 187 | const step = () => { | |
| 188 | runBots(state, 1 / 30); | |
| 189 | tick(state, 1 / 30); | |
| 190 | }; | |
| 191 | for (let i = 0; i < 30 * 40 && state.phase !== "live"; i++) step(); | |
| 192 | const stream: { t: number; bodies: { id: string; x: number; y: number; angle: number }[] }[] = []; | |
| 193 | for (let i = 0; i < 60; i++) { | |
| 194 | step(); | |
| 195 | stream.push({ | |
| 196 | t: i / 30, | |
| 197 | bodies: state.players.map((p) => ({ id: p.id, x: round2(p.x), y: round2(p.y), angle: round2(p.angle) })), | |
| 198 | }); | |
| 199 | } | |
| 200 | ||
| 201 | const timeline = makeTimeline(); | |
| 202 | const paths = new Map<string, { x: number; y: number }[]>(); | |
| 203 | let next = 0; | |
| 204 | for (let f = 0; f / 60 < 1.8; f++) { | |
| 205 | const now = f / 60; | |
| 206 | while (next < stream.length && stream[next]!.t <= now) { | |
| 207 | arrive(timeline, stream[next]!.t); | |
| 208 | for (const p of stream[next]!.bodies) feed(timeline, p.id, p, stream[next]!.t); | |
| 209 | next += 1; | |
| 210 | } | |
| 211 | for (const p of stream[Math.max(0, next - 1)]!.bodies) { | |
| 212 | const at = bodyAt(timeline, p, now - delay(timeline)); | |
| 213 | paths.set(p.id, [...(paths.get(p.id) ?? []), { x: at.x, y: at.y }]); | |
| 214 | } | |
| 215 | } | |
| 216 | ||
| 217 | // Whoever walked furthest in that second and a half. | |
| 218 | const walked = [...paths.values()] | |
| 219 | .filter((path) => path.length > 80) | |
| 220 | .map((path) => path.slice(1).map((at, i) => Math.hypot(at.x - path[i]!.x, at.y - path[i]!.y))) | |
| 221 | .map((steps) => steps.filter((step) => step > 0.001)) | |
| 222 | .sort((a, b) => b.reduce((s, x) => s + x, 0) - a.reduce((s, x) => s + x, 0))[0]!; | |
| 223 | expect(walked.length).toBeGreaterThan(30); | |
| 224 | const mean = walked.reduce((s, x) => s + x, 0) / walked.length; | |
| 225 | const spread = Math.sqrt(walked.reduce((s, x) => s + (x - mean) ** 2, 0) / walked.length) / mean; | |
| 226 | // A body moving at the server's own speed: about four units every sixtieth | |
| 227 | // of a second, which is the run speed. | |
| 228 | expect(mean * 60).toBeGreaterThan(150); | |
| 229 | expect(spread).toBeLessThan(0.15); | |
| 230 | }); |