Give the rally its length back: deep balls, baseliners, approaches off short balls

Every match had collapsed into serve, both at the net, reflex volleys at
~0.7 s per shot. The rally ball landed on the service line by definition,
the match CPU stood on the bounce and volleyed 90-100% of everything, and
the auto-run chased the earliest contact.

Rally modes only (classic + every match; gore and the duel untouched):
rally balls land deep and mistiming only shortens them (no new long outs);
each shot latches the receiver's plan as it leaves the racket — from the
back he lets it bounce and meets it falling, up to 2.2 m behind the line,
a short ball pulls him in and he follows it to the net, nobody approaches
a man already there, and a volley below the tape is a weak block. Measured:
contacts 10.5-13.7 m from the net, back-court volleys 0-8%, 0.8-1.0 s per
baseline shot. Three new tests pin the shape; v361.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5.5 committed 2026-10-13 12:55:05 +02:00
1 parent f33d46366a
commit 4e7c816a25
8 files changed
+292 -29

No files matched your search

+13 -3
View File
@@ -84,7 +84,7 @@ const SECS = Number(process.env.SECS || 240);
TUNE.autoAssist = 1.15;
const pct = (a, f) => a.length ? Math.round(100 * a.filter(f).length / a.length) : 0;
const med = a => { const s = [...a].sort((x, y) => x - y); return s.length ? s[Math.floor(s.length / 2)] : NaN; };
const rows = [], land = [];
const rows = [], land = [], pts = [];
for (const rung of RUNGS) {
restartRun();
prof.prof.lg = 5; prof.prof.xp = Number(process.env.XP || 0); prof.takePromo();
@@ -98,7 +98,8 @@ for (const rung of RUNGS) {
const speeds = [];
const hits = []; // {side, z(dist from own baseline), bounced, t}
let prevLast = ball.lastHit, prevB = ball.bounces, rally = 0, rallies = [], lastHitT = null, t = 0;
let swipe = null;
let swipe = null, pts0 = [...score.pts], net = { P: false, O: false };
const won = { P: 0, O: 0, Pnet: 0, PnetWon: 0, Onet: 0, OnetWon: 0 };
for (let i = 0; i < SECS * 60; i++) {
G.modal = null; G.modalQ.length = 0;
if (G.mode === 'eaten' || G.done) { G.hp = G.hpMax; }
@@ -124,10 +125,18 @@ for (const rung of RUNGS) {
if (ch && !G.wasServe) {
const fromNet = Math.abs(ch.z - COURT.netZ);
speeds.push({ side: ball.lastHit, v: Math.hypot(ball.vx, ball.vz) });
if (fromNet < 6.4) net[ball.lastHit] = true;
hits.push({ side: ball.lastHit, fromNet, bounced: prevB > 0, dt: lastHitT === null ? null : t - lastHitT, y: ball.y });
}
lastHitT = t; rally++;
}
const d0 = score.pts[0] - pts0[0], d1 = score.pts[1] - pts0[1];
if (d0 > 0 || d1 > 0) {
const w = d0 > 0 ? 'P' : 'O'; won[w]++;
for (const sd of ['P', 'O']) if (net[sd]) { won[sd + 'net']++; if (w === sd) won[sd + 'netWon']++; }
}
if (d0 || d1) net = { P: false, O: false };
pts0 = [...score.pts];
if (G.phase !== 'rally' && rally) { rallies.push(rally); rally = 0; lastHitT = null; }
if (ball.active && prevB === 0 && ball.bounces === 1 && !G.wasServe) {
const fromBase = ball.lastHit === 'P' ? ball.z : COURT.L - ball.z;
@@ -151,6 +160,7 @@ for (const rung of RUNGS) {
land.push({ rung, hitter: side, bounces: l.length, medLandFromBaseline: +med(l).toFixed(1),
'deep<3m%': pct(l, x => x < 3), 'svcbox>5.5m%': pct(l, x => x > 5.5), medPace: +med(v).toFixed(1) });
}
pts.push({ rung, Pwon: won.P, Owon: won.O, 'P net pts': won.Pnet, 'P net won%': won.Pnet ? Math.round(100 * won.PnetWon / won.Pnet) : '', 'O net pts': won.Onet, 'O net won%': won.Onet ? Math.round(100 * won.OnetWon / won.Onet) : '' });
rows.push({ rung, side: 'rally', shots: rallies.length, medDistNet: med(rallies), '<6.4m(svcline)%': '', '<3.5m(net)%': '', 'volley%': '', medTimeToHit_s: 'shots/pt mean ' + (rallies.reduce((a, b) => a + b, 0) / rallies.length).toFixed(1) });
}
console.table(rows); console.table(land);
console.table(rows); console.table(land); console.table(pts);