Difficoltà alle bocce e al biliardo, scelta nel dialogo con l'avversario: «Alla bona», «Seria», «A fassi male». Tarata con partite simulate contro un giocatore attento: al biliardo vince 9 su 12, circa 4 su 10 e quasi mai; alle bocce 72%, 50% e 35% delle mani. Battute nuove per Mariano e l'Ingegnere, test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
This commit is contained in:
2026-10-25 23:46:46 +01:00
co-authored by Claude Opus 5.5
parent bd66ea45ad
commit 94e9da4009
25 changed files with 136 additions and 34 deletions
+15 -6
View File
@@ -36,8 +36,16 @@ export function reach(v) {
export const canThrow = c => c.step === 'aim' && c.current === 'player';
export const moving = c => list(c).some(b => b.vx || b.vy);
// Quanto è bravo l'Ingegnere: quanti colpi prova a vuoto (direzioni intorno al tavolo, ritocchi vicino alla biglia
// avversaria, forze) e quanto gli trema la mano. «A fassi male» è il campione vero: vince quasi sempre.
export const LEVELS = {
bona: { angles: 0, fine: 0.15, powers: [0.6], wobble: 0.16, powerNoise: 0.2 },
seria: { angles: 14, fine: 0.09, powers: [0.5, 0.8], wobble: 0.072, powerNoise: 0.1 },
male: { angles: 64, fine: 0.025, powers: [0.3, 0.5, 0.7, 0.9], wobble: 0.03, powerNoise: 0.05 },
};
export function newBiliardo(seed = Date.now() >>> 0, def = {}) {
const c = { step: 'aim', t: 0, seed, current: 'player', target: def.target ?? TARGET, power: 0.45, angle: 0,
const level = LEVELS[def.level] ? def.level : 'seria';
const c = { step: 'aim', t: 0, seed, level, skill: LEVELS[level], current: 'player', target: def.target ?? TARGET, power: 0.45, angle: 0,
holding: false, holdTime: 0, accumulator: 0, events: [], points: { player: 0, cpu: 0 }, last: null, shot: null, plan: null };
c.balls = { player: ball('player', LEN * 0.18, WID * 0.5), cpu: ball('cpu', LEN * 0.81, WID * 0.25), pallino: ball('pallino', LEN * 0.88, WID * 0.7) };
c.pins = castle();
@@ -210,10 +218,10 @@ function finish(c) {
// L'avversario prova tanti colpi a vuoto e sceglie quello che rende di più; poi la mano gli trema un poco.
// Le prove si fanno a pezzi, un po' per fotogramma: tutte insieme fermerebbero il gioco per un decimo di secondo.
function cpuCandidates(c) {
const base = angleTo(c.balls.cpu, c.balls.player), angles = [];
for (let i = 0; i < 64; i++) angles.push(i / 64 * Math.PI * 2);
for (let d = -0.3; d <= 0.3; d += 0.025) angles.push(base + d);
return angles.flatMap(angle => [0.3, 0.5, 0.7, 0.9].map(power => ({ angle, power })));
const k = c.skill ?? LEVELS.male, base = angleTo(c.balls.cpu, c.balls.player), angles = [];
for (let i = 0; i < k.angles; i++) angles.push(i / k.angles * Math.PI * 2);
for (let d = -0.3; d <= 0.3; d += k.fine) angles.push(base + d);
return angles.flatMap(angle => k.powers.map(power => ({ angle, power })));
}
function cpuThink(c, budget = 12) {
const t = c.thinking ??= { todo: cpuCandidates(c), best: null };
@@ -222,7 +230,8 @@ function cpuThink(c, budget = 12) {
if (!t.best || gain > t.best.gain) t.best = { angle, power, gain };
}
if (t.todo.length) return;
c.plan = { angle: t.best.angle + deviation(c) * (c.wobble ?? 0.03), power: clamp(t.best.power + deviation(c) * 0.05, 0.05, 1) };
const k = c.skill ?? LEVELS.male;
c.plan = { angle: t.best.angle + deviation(c) * (c.wobble ?? k.wobble), power: clamp(t.best.power + deviation(c) * k.powerNoise, 0.05, 1) };
c.angle = c.plan.angle; c.power = c.plan.power; c.thinking = null;
}