curltastic/frontend/src/game-helpers.test.ts
Jason Dekarske 7af2fc9f35 feat(physics): spin curl model and theta trajectories
Replace constant-rate velocity rotate with ω0 spin + v_lat curl.
Path samples are (x, y, theta); client time is index/SAMPLE_RATE_HZ.

Ultraworked with [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <sisyphus@ohmyopencode>
2026-07-10 23:34:39 -07:00

70 lines
1.9 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
hogTrimStartIndex,
sampleTime,
trimPathToStartAtHogLine,
velocityToWeight,
weightToVelocity,
} from './game-helpers'
import { SAMPLE_RATE_HZ } from './protocol'
describe('trimPathToStartAtHogLine', () => {
it('trims path at the first hog-line crossing and preserves theta', () => {
// (x, y, theta) — time is derived from index after trim
const path: [number, number, number][] = [
[0, 2, 0.1],
[0, 20, 0.2],
[0, 21.5, 0.3],
[0, 30, 0.4],
]
const trimmed = trimPathToStartAtHogLine(path)
expect(trimmed[0][1]).toBe(20)
expect(trimmed[0][2]).toBe(0.2)
expect(trimmed[trimmed.length - 1][2]).toBe(0.4)
expect(trimmed).toHaveLength(3)
})
it('returns full path when hog line is never reached', () => {
const path: [number, number, number][] = [
[0, 2, 0],
[0, 10, 0.5],
]
expect(trimPathToStartAtHogLine(path)).toEqual(path)
})
})
describe('sampleTime', () => {
it('is index / SAMPLE_RATE_HZ', () => {
expect(sampleTime(0)).toBe(0)
expect(sampleTime(SAMPLE_RATE_HZ)).toBe(1)
expect(sampleTime(1)).toBeCloseTo(1 / SAMPLE_RATE_HZ)
})
})
describe('hogTrimStartIndex', () => {
it('returns index just before hog crossing', () => {
const path: [number, number, number][] = [
[0, 2, 0],
[0, 20, 0],
[0, 21.5, 0],
]
expect(hogTrimStartIndex(path)).toBe(1)
})
})
describe('velocity ↔ weight', () => {
it('maps endpoints correctly', () => {
expect(velocityToWeight(3.0)).toBe(1)
expect(velocityToWeight(6.45)).toBe(10)
expect(weightToVelocity(1)).toBe(3.0)
expect(weightToVelocity(10)).toBe(6.45)
})
it('clamps out-of-range inputs', () => {
expect(velocityToWeight(2.5)).toBe(1)
expect(velocityToWeight(7.0)).toBe(10)
expect(weightToVelocity(0)).toBe(3.0)
expect(weightToVelocity(11)).toBe(6.45)
})
})