diff --git a/index.html b/index.html index 0bf9c32..d67378b 100644 --- a/index.html +++ b/index.html @@ -471,6 +471,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') { - + diff --git a/src/config.js b/src/config.js index 68c212b..af10f70 100644 --- a/src/config.js +++ b/src/config.js @@ -657,10 +657,14 @@ export const MENU = [ // how long its racket is (reach), the FLOOR of its shot quality (qMin — a raised floor means every // ball comes back with pace), how often it just misses (flub), and its serve. MEDIUM repeats the // shipped TUNE values dial for dial (pinned by test), so the middle chip IS the old game. +// `smart` is the probability the CPU aims at the OPEN court instead of a random lane (trySwing) — +// the dial that turns "returns everything" into "plays you". HARD got a second pass after +// Oliver's "still too easy": it now reads your shot in 0.22 s, covers court at 1.3x, floors its +// quality at 0.70, and hunts the corner you are not standing in four times out of five. export const DIFFS = [ - { id: 'easy', name: 'EASY', flub: 0.30, quality: 0.62, qMin: 0.45, speed: 0.80, reaction: 0.62, reach: 1.45, serve: 0.75 }, - { id: 'medium', name: 'MEDIUM', flub: 0.10, quality: 0.86, qMin: 0.45, speed: 1.0, reaction: 0.45, reach: 1.6, serve: 1.0 }, - { id: 'hard', name: 'HARD', flub: 0.02, quality: 1.0, qMin: 0.62, speed: 1.18, reaction: 0.28, reach: 1.75, serve: 1.1 }, + { id: 'easy', name: 'EASY', flub: 0.30, quality: 0.62, qMin: 0.45, speed: 0.80, reaction: 0.62, reach: 1.45, serve: 0.75, smart: 0 }, + { id: 'medium', name: 'MEDIUM', flub: 0.10, quality: 0.86, qMin: 0.45, speed: 1.0, reaction: 0.45, reach: 1.6, serve: 1.0, smart: 0 }, + { id: 'hard', name: 'HARD', flub: 0.01, quality: 1.0, qMin: 0.70, speed: 1.30, reaction: 0.22, reach: 1.85, serve: 1.15, smart: 0.8 }, ]; // THE TOURNAMENT (Oliver's pick over the endless ladder, 2026-08-13): four rounds up the club's diff --git a/src/gameplay.js b/src/gameplay.js index 591e835..d62a2e6 100644 --- a/src/gameplay.js +++ b/src/gameplay.js @@ -1174,8 +1174,15 @@ function trySwing(ch, isAI, qCap = TUNE.aiQualityMax) { if (Math.abs(t - center) * 1000 < TUNE.perfectMs) q = 1; ch.facing = dx >= 0 ? 1 : -1; // forehand right (righty), backhand left → mirrored sprite const flick = isAI ? null : takeFlickAim(); + // SMART AIM (G.diff.smart, classic difficulty): with this probability the CPU plays the OPEN + // COURT — the corner away from where you stand — instead of rolling a random lane. Random aim + // sends a third of everything straight back to your racket; hunting the open corner is what + // makes a hard CPU feel like it is PLAYING you. ax is an absolute court lane (see doHit). + const smart = isAI && G.diff && Math.random() < (G.diff.smart || 0); const aim = isAI - ? { ax: [ -1, 0, 1 ][Math.floor(Math.random() * 3)], az: Math.random() < 0.25 ? -1 : 0 } + ? { ax: smart ? (P.x > 0.6 ? -1 : P.x < -0.6 ? 1 : (Math.random() < 0.5 ? -1 : 1)) + : [ -1, 0, 1 ][Math.floor(Math.random() * 3)], + az: Math.random() < 0.25 ? -1 : 0 } : flick || { ax: axisX(), az: axisZ() * (ch.side === 'P' ? 1 : -1) }; // G.diff (classic difficulty) moves the FLOOR too: a raised qMin is what makes HARD's every // ball arrive with pace, where a cap alone just trims the best ones. Null outside classic, so diff --git a/test.js b/test.js index 2bd951a..c010594 100644 --- a/test.js +++ b/test.js @@ -1126,6 +1126,8 @@ test('classic wears the match feel, and the difficulty table is a ladder', () => assert.ok(e.reach < m.reach && m.reach < h.reach, 'harder swings a longer racket'); assert.ok(e.qMin <= m.qMin && m.qMin < h.qMin, 'harder raises the quality floor'); assert.ok(e.serve < m.serve && m.serve < h.serve, 'harder serves better'); + assert.ok(e.smart <= m.smart && m.smart < h.smart, 'harder plays the open court'); + assert.equal(m.smart, 0, 'MEDIUM aims at random, as the shipped classic did'); assert.equal(m.flub, TUNE.classicFlub, 'MEDIUM flub IS classicFlub'); assert.equal(m.quality, TUNE.classicQualityMax, 'MEDIUM quality IS classicQualityMax'); assert.equal(m.speed, 1.0, 'MEDIUM leaves aiSpeed alone');