The opponents come back to life: the friendly refresh, open string beds, and a steadier chase

Oliver's batch, delivered in dependency order. "The opponents look pretty
harsh, almost already pre-zombie" — so BOTH tennis opponents got a new
identity: warm skin, human eyes, an easy near-smile, rolled anchor-first so
every later sheet inherits the face. The male's bounce pair also ADOPTS the
female's palm-out posture (his old crouched receiver stance was the wrong
pose for bouncing a ball — her better one is now the shared convention), and
the pre-serve ball was re-measured onto the drawn palms: BOUNCE_IDLE's oDx
0.45 -> 0.24 / oApex 0.55 -> 0.72 for his close carry, with her wider reach
riding TOURS (oDx 0.48 / oApex 0.73) the same way the player's pair already
does.

"parts of the body as well as the inner of the racket are solid" — the beds
were the cutter's own doing: _fine.key repaints enclosed magenta pale grey
(protection against accidental magenta strings), which defeated the beds we
now draw magenta ON PURPOSE. key()/main() grow a bed=True switch (the
famroll convention, used by _runroll and the refresh cuts); the run cycles'
beds went from ~70% to ~44% opaque — strings, not paddles. Enclosed pockets
of a roll's off-spec background (one sheet came back on blue) are scrubbed
by corner-colour distance too.

Both RUN CYCLES re-rolled off the friendly anchors: his landed 8 clean
frames (racket +0.05..+0.30, one side); hers took four rolls — the gates
caught a white background, a crossing racket, near-dup neighbours — and the
keeper drew a 10-frame cycle, from which the best 8 were chosen by
maximising the minimum adjacent stride (29.7%, smoother than his 22.7).
o_run7/8 return to the SPR list.

AND THE CHASE STOPS DITHERING (Oliver: "switching between a left and right
movement increasingly faster the nearer the ball comes"): intercept()
re-solves every frame and the earliest-reachable point jumps around its
solution as the flight shortens, flipping his run direction each time. The
CPU now commits to its stored target unless a fresh solve moves it by more
than 0.35 m. SPRV 121, main.js v244, 66 tests, every opponent frame verified
200 in the live game.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-08-14 13:16:55 +02:00
1 parent dd8dbd04e9
commit 80bf6c4cee
41 files changed
+37 -14

No files matched your search

+4 -2
View File
@@ -103,7 +103,9 @@ def prompt(k):
'behind the back to the left, never changes sides between neighbouring figures. The right '
'arm swings a LITTLE; the racket head stays visibly right of the body\'s midline throughout. '
'A single figure with the racket on the left side makes the whole animation flicker and is '
'WRONG.\n\n'
'WRONG. THE INSIDE OF EVERY RACKET HEAD IS EXACTLY THE BACKGROUND MAGENTA #FF00FF — never '
'white, never grey, never solid: it cuts to transparency, the same rule the family sheets '
'follow (a white-filled bed shipped as a solid paddle once — that is what this line prevents).\n\n'
f'THE {N} FIGURES ARE ONE CONTINUOUS RUN CYCLE, {c["view"]}. Evenly spaced, every figure on '
'one shared ground line, every figure at the same scale as its neighbours. THE FIGURES '
'NEVER TOUCH OR OVERLAP: leave a clear band of empty magenta between every pair of '
@@ -184,7 +186,7 @@ def cut(k, install=False, n=0):
outdir = HERE if install else os.path.join(SCRATCH, f'runcut_{k}')
os.makedirs(outdir, exist_ok=True)
outs = [os.path.join(outdir, f'{pre}{i}{suf}.png') for i in range(1, got + 1)]
_fine.main(sheet, outs, TARGET_H, [], -1)
_fine.main(sheet, outs, TARGET_H, [], -1, bed=True)
# ANCHOR EVERY FRAME ON ITS FEET. drawChar centres a frame by its total width, and the racket's
# reach varies wildly across a cycle (the cut female frames measured -28..+15 px of feet drift
# from frame centre) — so the BODY teleported sideways every frame, which is exactly Oliver's