Basket: la revisione di Codex, i punti gravi

Dalla mira iniziale (52°) da sotto non entrava con nessuna forza: ora 64°, forza minima più bassa e prima postazione
un passo indietro, così da ogni postazione entra in una fascia di forza contigua di almeno l'8% (test). Il palo
fermava anche sopra la sua cima e contava come tiro pulito; il tabellone lasciava passare da dietro. Il canestro
conta appena entra (anche il risultato all'ultimo pallone), la pausa fra un pallone e l'altro è più corta, la palla
entrata non rotola fino in fondo. Andrea resta a braccia alzate finché la palla vola (niente doppio pallone), il
rilascio parte sopra la sua testa. La guida si ferma al primo urto e ha un bordo scuro con la punta; il lampo del
canestro si spegne; l'obiettivo del livello è sempre in vista; i microurti sul ferro non tintinnano; dietro Andrea
non si carica; l'aiuto spiega il clic che fissa la mira e le soglie.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GiECdBYE8csKd4tcFvwntT
This commit is contained in:
2026-10-27 23:39:30 +01:00
co-authored by Claude Fable 5.1
parent f1de62f01e
commit 8b957c7cf7
6 changed files with 133 additions and 52 deletions
+46 -4
View File
@@ -1,7 +1,7 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { newBasket, press, release, cancel, update, shoot, setAngle, setPower, aimScreen, nudge, arc, status, canThrow, releasePoint,
powerAt, SWING, BALLS, SPOTS, LEVELS, RIM, BOARD, FLOOR, BALL, ANGLE } from '../src/engine/basket.js';
import { newBasket, press, release, cancel, update, shoot, setAngle, setPower, aimScreen, nudge, arc, guide, status, canThrow, releasePoint,
powerAt, SWING, BALLS, SPOTS, LEVELS, RIM, BOARD, POLE, FLOOR, BALL, ANGLE } from '../src/engine/basket.js';
import { story } from '../src/story/story.js';
import { Game } from '../src/engine/game.js';
import { settle } from './helpers.mjs';
@@ -43,7 +43,7 @@ test('la mira: l\'angolo segue il punto toccato, resta fra i limiti, non si tira
});
test('la parabola della guida comincia dalle mani e scende per la gravità', () => {
const c = newBasket(3), r = releasePoint(c), pts = arc(c, 1);
const c = newBasket(3), r = releasePoint(c), pts = arc(c, 1.5);
assert.deepEqual(pts[0], r);
assert.ok(pts[5].y < r.y, 'prima sale');
assert.ok(pts.at(-1).y > pts[5].y, 'poi scende');
@@ -65,6 +65,48 @@ test('da ogni postazione esiste un tiro che entra, e un tiro entrato conta canes
}
});
test('con l\'angolo iniziale, da ogni postazione entra in una fascia di forza contigua di almeno l\'8%', () => {
for (const [i, s] of SPOTS.entries()) {
const made = [];
for (let p = 0; p <= 1.0001; p += 0.005) {
const c = newBasket(1); c.spot = i; setPower(c, p); shoot(c);
made.push(fly(c).scored);
}
let best = 0, run = 0;
for (const v of made) { run = v ? run + 1 : 0; best = Math.max(best, run); }
assert.ok(best >= 16, `${s.name}: la fascia migliore è ${(best - 1) * 0.5}%`);
}
});
test('il palo ferma solo dalla cima in giù; il tabellone rimanda anche da dietro; il canestro conta subito', () => {
const c = newBasket(11); setAngle(c, 78); setPower(c, 1); shoot(c);
let b = fly(c);
assert.ok(!c.events.some(e => e.kind === 'basket-swish'), 'sopra il palo la palla vola via: non è un tiro pulito');
const d = newBasket(12);
d.ball = { x: POLE.x - 30, y: FLOOR - 30, vx: 300, vy: 0, time: 0, bounces: 0, rim: false, board: false, under: false, scored: false, dead: false, spin: 0 };
d.step = 'flight'; for (let i = 0; i < 30; i++) update(d, 1 / 120);
assert.ok(d.ball.board && d.ball.vx < 0, 'il palo la rimanda indietro come un tabellone');
const e = newBasket(13);
e.ball = { x: BOARD.x + BOARD.depth + 30, y: (BOARD.top + BOARD.bottom) / 2, vx: -300, vy: 0, time: 0, bounces: 0, rim: false, board: false, under: false, scored: false, dead: false, spin: 0 };
e.step = 'flight'; for (let i = 0; i < 30; i++) update(e, 1 / 120);
assert.ok(e.ball.board && e.ball.x > BOARD.x, 'da dietro non lo attraversa');
const f = newBasket(14); f.thrown = BALLS - 1; f.skill = LEVELS.bona; f.baskets = 2;
const shot = goodShot(f); setAngle(f, shot.angle); setPower(f, shot.power); shoot(f);
for (let i = 0; i < 2000 && !f.ball.scored; i++) update(f, 1 / 120);
assert.equal(f.baskets, 3, 'il canestro conta appena entra');
assert.ok(typeof f.ball.scoredAt === 'number');
assert.equal(f.result?.winner, 'player', 'all\'ultimo pallone il risultato c\'è già mentre la palla scende');
fly(f); rest(f);
assert.equal(f.step, 'result'); assert.equal(f.result.baskets, 3);
});
test('la guida si ferma al primo urto', () => {
const c = newBasket(15); c.spot = 1; setAngle(c, 30); setPower(c, 1);
const g = guide(c, 3), last = g.at(-1);
assert.ok(g.length < arc(c, 3).length, 'non arriva in fondo');
assert.ok(last.x + BALL > BOARD.x - 2 || last.y + BALL > FLOOR - 2 || Math.hypot(last.x - RIM.x1, last.y - RIM.y) < BALL + 3, 'si ferma su tabellone, campo o ferro');
});
test('un tiro corto batte sul campo e rimbalza; uno lungo prende il tabellone e torna indietro', () => {
const c = newBasket(4); c.spot = 3; // da tre, piano: non arriva nemmeno al palo
setAngle(c, 45); setPower(c, 0); shoot(c);
@@ -73,7 +115,7 @@ test('un tiro corto batte sul campo e rimbalza; uno lungo prende il tabellone e
assert.ok(c.events.some(e => e.kind === 'basket-bounce'));
assert.ok(b.y + BALL <= FLOOR + 0.01, 'non sprofonda');
const d = newBasket(5); d.spot = 1;
setAngle(d, 30); setPower(d, 1); shoot(d);
setAngle(d, 34); setPower(d, 1); shoot(d);
b = fly(d);
assert.ok(b.board, 'ha preso il tabellone');
assert.ok(c.events.every(e => e.kind !== 'basket-board') && d.events.some(e => e.kind === 'basket-board'));