dex/test/pinball.test.js
chris 9fa46493a8 Hidden mini-game: single-table pinball (#/pinball)
A small canvas pinball engine (src/lib/pinball.js): fixed-timestep physics
with 5 substeps, circle-vs-segment collision, two rotating flippers whose
angular velocity drives the ball, 3 pop bumpers, 2 slingshots, a charged
plunger, 3 balls. Lighting all 3 bumpers "catches" a random Pokémon for a
bonus. Ball-search kicks a wedged ball toward centre; three strikes and it
drains. High score in pdx.pinball.

Hidden like "Who's that Pokémon?" — no nav entry; the two games
cross-link from each other's back-nav, or reach it at #/pinball.

Controls: ← / → (also A/L, Z) flippers, hold Space / the Launch button for
the plunger; on touch, tap the table's left or right half.

Rendering: the canvas is sized from its container with a DPR-capped
backing store (crisp on any display). `_frame()` is exposed so the physics
can be driven headlessly. 3 unit tests for the geometry helpers.

The physics are stable and the game loop is complete; feel (flipper
strength, ball speed, table balance) still wants hands-on tuning.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Ve7HLspzeG2xDPtJQ8vmu
2026-09-10 14:18:11 -04:00

30 lines
869 B
JavaScript

import { describe, it, expect } from 'vitest';
import { _test } from '../src/lib/pinball.js';
const { closestOnSeg, clamp } = _test;
describe('clamp', () => {
it('bounds a value', () => {
expect(clamp(5, 0, 10)).toBe(5);
expect(clamp(-3, 0, 10)).toBe(0);
expect(clamp(99, 0, 10)).toBe(10);
});
});
describe('closestOnSeg', () => {
it('projects onto the segment interior', () => {
const [x, y, t] = closestOnSeg(5, 5, 0, 0, 10, 0);
expect([x, y]).toEqual([5, 0]);
expect(t).toBeCloseTo(0.5);
});
it('clamps to the endpoints', () => {
expect(closestOnSeg(-4, 2, 0, 0, 10, 0).slice(0, 2)).toEqual([0, 0]);
expect(closestOnSeg(20, -1, 0, 0, 10, 0).slice(0, 2)).toEqual([10, 0]);
});
it('handles a degenerate (zero-length) segment', () => {
expect(closestOnSeg(3, 4, 1, 1, 1, 1).slice(0, 2)).toEqual([1, 1]);
});
});