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
30 lines
869 B
JavaScript
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]);
|
|
});
|
|
});
|