An aimed shot keeps its direction: the scatter can push it wider, never back past centre

Second round on the service-line flick report. The crossing-fired swing
alone did not move the feel, and Oliver's sharpened observation — the
flick only lands left when the opponent already stands left — pointed at
a magnet that does not exist: measured with the body teleported at
contact, the aim never tracks it. The real mechanism: at rushed quality
the symmetric scatter (aimError x (1-q)) outweighs the quality-shrunk
aim, so a mistimed left flick landed anywhere in a ~4 m blob around
'slightly left' — usually centre — and the eye anchors that on the
opponent. Its position was a proxy for preparation time all along.

TUNE.aimTrust: an outward scatter roll is untouched — the committed
swipe's chance of sailing keeps its exact tuned shape, flickReach stays
the only out dial — but an inward roll keeps only 0.3 of itself. Bad
timing costs width, never direction. Player lane shots only; the
opponent's symmetric scatter is its width mechanic. Verified at
worst-case q~0.08: 14/14 rushed left flicks landed left, 9/9 right
flicks right, where before it was a coin toss. The suite pins the
direction guarantee and the aimTrust/aimError/flickReach relationship.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-08-20 14:03:57 +02:00
1 parent 6f9dea4ffc
commit 60127436a0
5 files changed
+63 -2

No files matched your search

+24
View File
@@ -852,6 +852,30 @@ test('a gentle flick cannot be played out — only a committed one can', () => {
assert.ok(safe > 0.4 && safe < 1, 'the ring turns hot inside the swipe range, not at either end');
});
test('an aimed shot keeps its DIRECTION: scatter may push it wider, never back past centre', () => {
// Dial 1 (2026-08-20). At rushed quality the symmetric scatter outweighed the quality-shrunk
// aim, so a mistimed left flick landed "wherever" — usually centre, read as the ball following
// the opponent. The model mirrors doHit's lane path: outward rolls untouched (the chosen
// out-risk keeps its exact tuned shape), inward rolls keep only aimTrust of themselves.
const land = (s, q, u) => { // u in [-1,1]: the raw scatter roll
const reach = TUNE.flickReach + (1 - TUNE.flickReach) * q;
let e = u * TUNE.aimError * (1 - q);
if (s && e * Math.sign(s) < 0) e *= TUNE.aimTrust;
return s * TUNE.flickWidth * reach + e;
};
const line = COURT.W / 2;
for (let q = 0; q <= 1; q += 0.1) {
assert.ok(land(0.5, q, -1) > 0, 'a committed right swipe stays right at q=' + q.toFixed(1)
+ ': ' + land(0.5, q, -1).toFixed(2));
assert.ok(land(-0.5, q, 1) < 0, '...and a committed left swipe stays left, mirrored');
}
assert.ok(land(1, 0, 1) > line, 'the outward tail is untouched: a mistimed full swipe can still sail');
assert.ok(land(1, 1, 1) < line, 'and a perfectly timed one still never does');
// the knob relationship the direction guarantee rests on — retuning must keep it
assert.ok(TUNE.aimTrust * TUNE.aimError < 0.5 * TUNE.flickWidth * TUNE.flickReach,
'aimTrust*aimError must stay under a committed swipe\'s worst-case aim');
});
test('the flick aims from the LIVE swipe: contact reads G.swipe, no release needed', () => {
// The swing fires the moment the drag crosses flickMin now (input.js), so at contact the finger
// is usually still down and no released flickAim exists yet. The aim has to come from the live