GP di Fortezza: guida semplice alla bona e Seria (si tiene premuto per il gas, lasciando si frena, lo sterzo va da solo e troppo forte in curva si scivola nella paglia), per il telefono; Ginocchio in terra resta con lo sterzo vero; prova nel browser del GP (Andrea)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GiECd9KMfjthRUnujCGSzZ
This commit is contained in:
2026-10-29 10:18:22 +01:00
co-authored by Claude Opus 5.5
parent 9d2cc2fcb3
commit 1b7511f6ca
7 changed files with 151 additions and 19 deletions
+56
View File
@@ -0,0 +1,56 @@
// Il GP di Fortezza nel browser vero: alla bona e seria la guida semplice (solo gas, lasciando si frena), a ginocchio
// in terra lo sterzo vero. npm run browser:gara — schermate in /tmp/regolo-gara.
import { chromium } from 'playwright';
import { mkdirSync } from 'node:fs';
import assert from 'node:assert/strict';
import { staticServer } from './helpers.mjs';
const out = process.env.OUT ?? '/tmp/regolo-gara';
mkdirSync(out, { recursive: true });
const server = await staticServer();
let browser;
const errors = [];
try {
browser = await chromium.launch({ args: ['--autoplay-policy=no-user-gesture-required', '--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'] });
for (const [name, mobile, viewport, level] of [
['touch', true, { width: 844, height: 390 }, 'seria'],
['portrait', true, { width: 390, height: 844 }, 'bona'],
['desktop', false, { width: 1280, height: 860 }, 'male'],
]) {
const page = await browser.newPage({ viewport, hasTouch: mobile, isMobile: mobile });
page.on('pageerror', e => errors.push(e.message));
await page.goto(`http://127.0.0.1:${server.port}/`);
await page.click('#splash');
await page.click('#new-game');
await page.click('#rione [data-rione="campomigliaio"]');
await page.click('#skip-intro');
await page.evaluate(async level => {
const { settle } = await import('/tests/helpers.mjs');
settle(game); game.run({ closeup: 'gara', level }); settle(game);
}, level);
await page.waitForSelector('#bocce-controls:not([hidden])');
// Salta la presentazione dei piloti e aspetta il via.
await page.evaluate(async () => { const g = await import('/src/engine/gara.js'); while (game.closeup.step === 'intro') g.press(game.closeup); });
await page.waitForFunction(() => game.closeup.step === 'race', null, { timeout: 15000 });
const easy = level !== 'male';
for (const id of ['#bocce-left', '#bocce-right', '#gara-brake', '#gara-pad'])
assert.equal(await page.locator(id).isVisible(), !easy, `${name}: ${id} ${easy ? 'nascosto' : 'visibile'}`);
assert.equal(await page.locator('#bocce-power').isVisible(), !easy, 'la sensibilità solo con lo sterzo vero');
// Il pulsante Gas accende il gas; un altro tocco lo spegne e nella guida semplice si frena.
if (mobile) await page.locator('#bocce-throw').tap(); else await page.locator('#bocce-throw').click();
await page.waitForFunction(() => game.closeup.riders.find(r => r.id === 'andrea').v > 40, null, { timeout: 15000 });
await page.screenshot({ path: `${out}/${name}-${level}.png` });
if (mobile) await page.locator('#bocce-throw').tap(); else await page.locator('#bocce-throw').click();
const v = await page.evaluate(() => game.closeup.riders.find(r => r.id === 'andrea').v);
await page.waitForTimeout(300);
const after = await page.evaluate(() => game.closeup.riders.find(r => r.id === 'andrea').v);
assert.ok(after < v, `${name}: senza gas rallenta (${v.toFixed(0)} → ${after.toFixed(0)})`);
await page.close();
}
assert.deepEqual(errors, []);
console.log(`ok: schermate in ${out}`);
} finally {
await browser?.close();
server.kill();
}