Il flipper rivisto con la revisione di Codex: punti solo a un urto nuovo, sottopassi contro gli attraversamenti, molla che arriva sempre in campo (senza cancelletto), salvataggio col tempo della simulazione e in vista, corsie che contano attraversando la riga, traguardo acquisito appena raggiunto, annullare non lancia, due dita e tasto destro, pulsanti dei flipper sempre attivi

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GiECd9RWGMKV7n3K1mdy8T
This commit is contained in:
2026-10-31 20:24:01 +01:00
co-authored by Claude Fable 5.1
parent 7076123fcf
commit 1fc446d02a
14 changed files with 386 additions and 208 deletions
+36 -15
View File
@@ -1,5 +1,6 @@
// Il flipper del River nel browser vero: dal bar, la molla col mouse e col dito, i flipper coi pulsanti e con le
// frecce, il risultato. npm run browser:flipper — schermate in /tmp/regolo-flipper.
// Il flipper del River nel browser vero: dal bar, la molla col mouse e col dito, i flipper con due dita sulla scena,
// coi pulsanti e con le frecce, la pausa che lascia tutto, il risultato. npm run browser:flipper — schermate in
// /tmp/regolo-flipper.
import { chromium } from 'playwright';
import { mkdirSync } from 'node:fs';
import assert from 'node:assert/strict';
@@ -28,43 +29,63 @@ try {
assert.equal(await page.locator('#bocce-throw').textContent(), 'Lancia');
assert.ok(await page.locator('#flipper-help').isVisible());
assert.equal(await page.locator('#bocce-left').textContent(), '← Flipper');
assert.ok(!(await page.locator('#bocce-left').isDisabled()), 'i pulsanti dei flipper sono attivi anche prima del lancio');
await page.screenshot({ path: `${out}/${name}-tavola.png` });
const box = await page.locator('#scene').boundingBox(), P = (x, y) => [box.x + x / 640 * box.width, box.y + y / 360 * box.height];
// La molla: col mouse tenendo premuto sulla scena, col dito il pulsante Lancia.
if (mobile) await page.locator('#bocce-throw').tap();
else {
const cdp = mobile ? await page.context().newCDPSession(page) : null;
const touch = (type, points) => cdp.send('Input.dispatchTouchEvent', { type, touchPoints: points.map(([x, y]) => ({ x, y })) });
// La molla: tenendo premuto sulla scena, col mouse e col dito.
if (mobile) {
await touch('touchStart', [P(440, 320)]);
await page.waitForFunction(() => game.closeup.holding && game.closeup.power > 0.3, null, { polling: 'raf' });
await touch('touchEnd', []);
} else {
await page.mouse.move(...P(440, 320)); await page.mouse.down();
await page.waitForFunction(() => game.closeup.holding && game.closeup.power > 0.3, null, { polling: 'raf' });
await page.mouse.up();
}
await page.waitForFunction(() => game.closeup && !game.closeup.ball?.waiting, null, { timeout: 5000 });
// I flipper: col dito i pulsanti tenuti premuti, col mouse la metà della scena e poi le frecce.
// Due dita sulla scena: sinistra, poi anche destra, poi si alza la sinistra e la destra resta su.
if (mobile) {
const b = await page.locator('#bocce-left').boundingBox();
const cdp = await page.context().newCDPSession(page);
await cdp.send('Input.dispatchTouchEvent', { type: 'touchStart', touchPoints: [{ x: b.x + b.width / 2, y: b.y + b.height / 2 }] });
await touch('touchStart', [P(250, 200)]);
await page.waitForFunction(() => game.closeup.flippers.left.held.length > 0);
await touch('touchStart', [P(250, 200), P(400, 200)]);
await page.waitForFunction(() => game.closeup.flippers.right.held.length > 0 && game.closeup.flippers.left.held.length > 0, null, { timeout: 3000 });
await page.screenshot({ path: `${out}/${name}-flipper.png` });
await cdp.send('Input.dispatchTouchEvent', { type: 'touchEnd', touchPoints: [] });
await touch('touchEnd', [P(400, 200)]);
await page.waitForFunction(() => game.closeup.flippers.left.held.length === 0 && game.closeup.flippers.right.held.length > 0, null, { timeout: 3000 });
await touch('touchEnd', []);
await page.waitForFunction(() => game.closeup.flippers.right.held.length === 0);
// I pulsanti tenuti premuti.
const b = await page.locator('#bocce-left').boundingBox();
await touch('touchStart', [[b.x + b.width / 2, b.y + b.height / 2]]);
await page.waitForFunction(() => game.closeup.flippers.left.held.length > 0);
await touch('touchEnd', []);
await page.waitForFunction(() => game.closeup.flippers.left.held.length === 0);
} else {
await page.mouse.move(...P(400, 200)); await page.mouse.down();
await page.waitForFunction(() => game.closeup.flippers.right.held.length > 0);
await page.mouse.down({ button: 'right' });
await page.waitForFunction(() => game.closeup.flippers.right.held.length > 1, null, { timeout: 3000 });
await page.screenshot({ path: `${out}/${name}-flipper.png` });
await page.mouse.up();
await page.mouse.up(); await page.mouse.up({ button: 'right' });
await page.waitForFunction(() => game.closeup.flippers.right.held.length === 0);
await page.keyboard.down('ArrowLeft');
await page.waitForFunction(() => game.closeup.flippers.left.held.length > 0);
await page.keyboard.up('ArrowLeft');
// La pausa lascia tutto: tornando, il flipper è giù anche se il tasto era premuto.
await page.click('#pause-button');
await page.waitForFunction(() => game.closeup.flippers.left.held.length === 0);
await page.keyboard.up('ArrowLeft');
await page.click('#resume');
await page.waitForFunction(() => document.querySelector('#pause').hidden);
}
// La fine: si fa finire la partita e si guarda il quadro.
await page.evaluate(() => { const c = game.closeup; c.step = 'result'; c.ball = null; c.result = { winner: 'player', score: 7350, goal: c.skill.goal }; });
await page.evaluate(() => { const c = game.closeup; c.score = 7350; c.step = 'result'; c.ball = null; c.result = { winner: 'player', score: 7350, goal: c.skill.goal }; });
await page.waitForFunction(() => document.querySelector('#bocce-throw').textContent === 'Finita');
assert.match(await page.locator('#bocce-status').textContent(), /7350 punti/);
await page.screenshot({ path: `${out}/${name}-fine.png` });
await page.locator('#closeup-close').click();
await page.waitForFunction(() => game.state.flags.flipper_vinto);
if (mobile) await page.locator('#closeup-close').click(); else await page.keyboard.press('Enter'); // Invio chiude il risultato
await page.waitForFunction(() => game.state.flags.flipper_vinto && !game.closeup);
await page.close();
}
assert.deepEqual(errors, []);