// Pong (pong.html) on every track, judged against the pure model. import { test, expect, TRACKS } from '../lib/app.mjs' // The player's paddle is the white 8 x 64 rect in column 2 (x = 16); its top row in cells is y / 8. const lastScene = (shapes) => shapes.slice(Math.max(0, shapes.findLastIndex((s) => s.tag === 'Rect' && s.w === 640))) const paddleOf = (shapes) => lastScene(shapes).find((s) => s.tag === 'Rect' && s.x === 16 && s.h === 64).y / 8 const textsOf = (shapes) => lastScene(shapes).filter((s) => s.tag === 'Text').map((s) => s.text) for (const track of TRACKS) { test.describe(`${track}: pong`, () => { test('holding and releasing keys moves the paddle as the model says', async ({ app }) => { const a = await (await app(track, 'pong')).start() const p0 = paddleOf(await a.expectInSync()) await a.down('ArrowUp') await a.advance(300) const p1 = paddleOf(await a.expectInSync('holding ArrowUp')) await a.up('ArrowUp') await a.advance(300) const p2 = paddleOf(await a.expectInSync('released')) await a.down('ArrowDown') await a.advance(600) const p3 = paddleOf(await a.expectInSync('holding ArrowDown')) await a.up('ArrowDown') await a.advance(100) await a.expectInSync('released again') // One cell per 30 ms game frame while held, none after release. expect(p0).toBe(21) expect(p1).toBeLessThan(p0) expect(p2).toBe(p1) expect(p3).toBeGreaterThan(p2) }) test('a whole match on the fake clock ends with the model\'s score and the right winner text', async ({ app }) => { const a = await (await app(track, 'pong')).start() // A simple player: every 90 ms, hold the key that brings the paddle's middle toward the ball. let held = null for (let i = 0; i < 1000 && a.state.status === 'Playing'; i++) { const { ball, player } = a.state const want = player + 4 < ball.y - 1 ? 'ArrowDown' : player + 4 > ball.y + 1 ? 'ArrowUp' : null if (want !== held) { if (held) await a.up(held) if (want) await a.down(want) held = want } await a.advance(90) if (i % 10 === 0) await a.expectInSync(`match, ${i * 90} ms`) } if (held) await a.up(held) await a.advance(500) const end = await a.expectInSync('match over') const { score } = a.state expect(a.state.status).toBe('Over') expect(Math.max(score.player, score.cpu)).toBe(5) expect(textsOf(end)).toEqual(expect.arrayContaining([`${score.player} ${score.cpu}`, score.player > score.cpu ? 'You Win!' : 'CPU Wins', 'Press R to restart'])) }) test('R restarts and keeps a held key', async ({ app }) => { const a = await (await app(track, 'pong')).start() await a.advance(300) await a.down('ArrowDown') await a.advance(150) const before = paddleOf(await a.expectInSync('holding ArrowDown')) await a.press('r') const restarted = paddleOf(await a.expectInSync('after R')) await a.advance(300) const after = paddleOf(await a.expectInSync('after R, still holding')) await a.up('ArrowDown') await a.expectInSync('released') expect(before).toBeGreaterThan(21) expect(restarted).toBe(21) expect(after).toBeGreaterThan(21) }) }) }