Stop calling "passed you" while the ball is still on its way to him

Oliver's v366 log: a LOST "passed you" with no swing. The match gave the
CPU the point the moment its ball crossed 0.3 m behind the baseline — a
line from when the player stood inside it. Since step 1 he waits up to
2.2 m behind the line for deep balls, and a ball going by ~1.5 m beside
him only enters his reach just in front of him, so the point was called
before it was ever playable. In the match "past you" now means past his
reach. The swing log's hits also say whether the timing was early or
late. v367.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5.5 committed 2026-10-14 13:03:30 +02:00
1 parent 5bbde720ad
commit dfdb699f9d
4 files changed
+73 -4

No files matched your search

+40
View File
@@ -1397,6 +1397,46 @@ test('THE SWING LOG writes in the match too: a whiff, a hit with its tap, and wh
} finally { TUNE.autoAssist = assist; }
});
test('a deep ball the player waits for BEHIND his baseline is still his to hit — not "passed you"', () => {
// Oliver's v366 log vs THE BRUTE: "LOST passed you" with no swing. The match called the point the
// moment its ball crossed 0.3 m behind the baseline — but since step 1 the player waits up to
// rallyBehind behind it for a deep, kicking ball, so the ball was taken away on its way to him.
const flub = TUNE.matchFlub; TUNE.matchFlub = 0;
const assist = TUNE.autoAssist; TUNE.autoAssist = 1.15;
try {
startMatch(); G.interlude.introT = 0;
const zb = oppOf(); zb.swingCool = 999;
G.phase = 'rally'; G.wasServe = false; G.missLog.length = 0;
Object.assign(P, { x: 0, z: COURT.L + 1.8, swingT: -1, tgt: null, vx: 0, vz: 0 });
TUNE.autoAssist = 0; TUNE.matchAssist = 0; // he holds his deep mark: the ball comes to him
const v = solveShot(0, 1, 1, 1.25, COURT.L - 2.2, 27, TUNE.gravity * 1.6); // deep, a little wide, heavy topspin
Object.assign(ball, { active: true, scored: false, lastHit: 'O', bounces: 0, rx: null, spin: 1.15, soft: false,
x: 0, y: 1, z: 1, vx: v.vx, vy: v.vy, vz: v.vz, g: TUNE.gravity * 1.6 });
let crossed = false, lift = -1;
for (let i = 0; i < 3 * 60 && ball.active && ball.lastHit === 'O'; i++) {
if (lift < 0 && ringCue()?.gold) lift = i + 6;
if (i === lift) keyPressed('Space', 0.1);
update(DT);
if (ball.z > COURT.L + 0.3) crossed = true;
}
assert.ok(crossed, 'the ball really did cross the old line');
assert.equal(ball.lastHit, 'P', 'and he returned it: ' + JSON.stringify(G.missLog.map(e => e.result + ' ' + e.reason)));
// ...while a ball that truly gets past him is still called, and says whether he swung
TUNE.autoAssist = assist; TUNE.matchAssist = 1.15;
freshSplatter(); startMatch(); G.interlude.introT = 0;
oppOf().swingCool = 999;
G.phase = 'rally'; G.wasServe = false; G.missLog.length = 0;
Object.assign(P, { x: 0, z: 23, swingT: -1 });
const w = solveShot(0, 1, 1, 3.9, COURT.L - 3, 28); // wide, past him
Object.assign(ball, { active: true, scored: false, lastHit: 'O', bounces: 0, rx: null, spin: 1, soft: false,
x: 0, y: 1, z: 1, vx: w.vx, vy: w.vy, vz: w.vz, g: TUNE.gravity });
TUNE.autoAssist = 0; TUNE.matchAssist = 0; // a statue: it must go by
for (let i = 0; i < 3 * 60 && G.phase === 'rally'; i++) update(DT);
const lost = G.missLog.find(e => e.result === 'LOST');
assert.ok(lost && /never swung, it came within/.test(lost.reason), 'the verdict says how close it came: ' + (lost && lost.reason));
} finally { TUNE.matchFlub = flub; TUNE.autoAssist = assist; TUNE.matchAssist = 1.15; }
});
test('rally depth: deep by default, mistiming lands it SHORTER, never longer', () => {
// the only out a rally ball may find is wide — depth must never put one past the baseline
startMatch(); G.interlude.introT = 0;