The opponent is complete: 3 wound stages x 11 states, and a gate for wounds that never happened

The serve and the fall now exist at every wound stage, so a one-armed opponent
still serves and a headless one still goes down, instead of reverting to a
whole body to do it. 33 frames, no gaps.

THE BALL IS OUT OF THE SERVE SPRITES. Oliver: it was painted into three frames
and the game draws the ball itself, so every one of them would have ridden
along at the wrong place, the wrong time and in the wrong number. Hands open
and empty now, poses unchanged as if a ball were there.

THE FOURTH GATE, and the one nothing else could see: "copy this exactly, change
ONE thing" loses when the change is a REMOVAL. Taking a head off worked every
time — it is dramatic. Taking an ARM off mostly did not: five of seven wound
frames came back as the reference faithfully redrawn, 0.02-0.05 of their own
silhouette different, and the ladder quietly HEALED the body the score had just
maimed. Frame count, background, clipping, detail, kit and stride all pass that
sheet. A batch now declares how far it must move from the sheet it was drawn
from — a copy scores 0.02-0.05, a real amputation 0.12-0.38, a decapitation
0.27-0.75 — and the whole set now measures 0.18-0.65.

AND THE BACKGROUND RULE MOVED TO THE FRONT OF EVERY PROMPT. Six rejections in
eleven rolls were "background is not magenta", and the generator was not
glitching: it was painting the zombies ON A TENNIS COURT. The chained restyle
subjects are long, and the magenta instruction sat behind three hundred words
of "keep everything exactly as the reference", where it stopped being honoured.
Same sentence, said before anything can bury it.

One design consequence kept rather than papered over: the missing arm IS the
tossing arm, so a wounded opponent tosses off the racket face the way one-armed
players actually do. The wound changes how he plays, which is what slice 3 has
been asking for.

Known and NOT fixed: the shorts hue drifts from -9 to -19 down the ladder. Each
stage is measured against the one before it, so a legal 4-point hop three times
over is a 14-point total nobody authorised. The fix is to anchor every stage to
the healthy frame with a total budget instead of a per-hop one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-12 12:45:26 +02:00
1 parent 4bbdf6357a
commit 5c0db0a22e
24 files changed
+181 -24

No files matched your search

Binary file not shown.
+43 -5
View File
@@ -55,6 +55,9 @@ CANVAS = (
"The canvas is a WIDE LANDSCAPE STRIP and every figure stands side by side in ONE SINGLE HORIZONTAL "
"ROW on the same ground line — never a grid, never two rows, never stacked."
)
BG_FIRST = ("THE BACKGROUND OF THE OUTPUT IMAGE MUST BE ONE FLAT SOLID MAGENTA #FF00FF. No court, "
"no grass, no lines, no scenery, no sky, no gradient — nothing behind the figures but flat "
"magenta, edge to edge. ")
STYLE = RES + FIDELITY + CANVAS
NEW_STYLE = RES + (
"Match the reference's DRAWING STYLE, its overall figure height and its facing — but the "
@@ -177,16 +180,40 @@ def kit_off(outs, ref_name, tol=(12, 10, 1.5, 4)):
# opt-in per batch (`stride: [[i, j]]`) rather than applied to everything with two frames in it.
STRIDE_H, STRIDE_TOP, STRIDE_LEG, STRIDE_HIP = 0.04, 0.20, 0.30, 0.62
def stride_stats(pa, pb):
def _masks(pa, pb):
a, b = Image.open(pa).convert('RGBA'), Image.open(pb).convert('RGBA')
w, h = max(a.width, b.width), max(a.height, b.height)
def m(im):
c = Image.new('RGBA', (w, h)); c.paste(im, ((w - im.width) // 2, h - im.height))
return np.array(c)[..., 3] > 110
ma, mb = m(a), m(b)
return a, b, m(a), m(b), h
def _d(x, y):
return round(float((x ^ y).sum() / max(1, (x | y).sum())), 3)
def stride_stats(pa, pb):
a, b, ma, mb, h = _masks(pa, pb)
hip = int(h * STRIDE_HIP)
d = lambda x, y: round(float((x ^ y).sum() / max(1, (x | y).sum())), 3)
return round(a.height / max(1, b.height), 3), d(ma[:hip], mb[:hip]), d(ma[hip:], mb[hip:])
return round(a.height / max(1, b.height), 3), _d(ma[:hip], mb[:hip]), _d(ma[hip:], mb[hip:])
# ---- DID THE CHANGE ACTUALLY HAPPEN? A chained restyle asks for one sheet redrawn with ONE THING
# different, and "copy this exactly" reliably beats "…but change that": removing a HEAD works every
# time (it is dramatic), removing an ARM mostly did not — five of seven wound frames came back as
# the reference redrawn, differing by 0.02-0.05 of their own silhouette, and the ladder silently
# healed the body it had just maimed. So a batch can declare how much it must differ from the sheet
# it was drawn FROM. Measured against the known numbers: a copy scores 0.02-0.05, a real amputation
# 0.12-0.38, a decapitation 0.27-0.75. Nothing else in the pipeline can see this — the art is
# beautiful, correctly coloured, correctly posed and completely wrong.
def changed_off(outs, refs, want):
for o, r in zip(outs, refs):
rp = os.path.join(HERE, r + '.png')
if not os.path.exists(rp): continue
_, _, ma, mb, _ = _masks(o, rp)
d = _d(ma, mb)
if d < want:
return (f'{os.path.basename(o)} is {r} redrawn — only {d:.3f} of the silhouette differs '
f'(want >= {want}). The thing that was supposed to change did not change.')
return None
def stride_off(outs, pairs):
for i, j in pairs:
@@ -245,7 +272,14 @@ def run(name, spec, dry=False):
ref = ref_sheet(shown, f'{SCRATCH}/{name}_ref.png', spec.get('scale', 2.0),
frames=len(sprites) + (1 if lead else 0))
sheet = f'{SCRATCH}/{name}_hi.png'
prompt = f"{spec['subject']} {STATE_STYLE if spec.get('state') else POSE_STYLE if spec.get('pose') else NEW_STYLE if spec.get('newart') else STYLE}"
# THE BACKGROUND RULE GOES FIRST, not only at the end of the style block. The chained restyle
# subjects are long, and with the magenta instruction sitting behind three hundred words of
# "keep everything exactly as the reference" the generator started painting the figures on a
# COURT — grey-blue, grey-green, dark green backgrounds, six rejections in eleven rolls. Same
# sentence, said before anything else has had a chance to bury it.
prompt = (BG_FIRST + spec['subject'] + ' '
+ (STATE_STYLE if spec.get('state') else POSE_STYLE if spec.get('pose')
else NEW_STYLE if spec.get('newart') else STYLE))
if dry:
print(f'[{name}] would generate {len(sprites)} frames: {", ".join(sprites)}'); return True
print(f'[{name}] generating {len(sprites)} frames...')
@@ -287,6 +321,10 @@ def run(name, spec, dry=False):
if spec.get('stride'):
bad = stride_off(outs, spec['stride'])
if bad: print(f'[{name}] REJECTED: {bad}'); return False
# a chained restyle declares how far it must move from the sheet it was drawn from
if spec.get('changed') and len(spec.get('ref_only', [])) == len(sprites):
bad = changed_off(outs, spec['ref_only'], spec['changed'])
if bad: print(f'[{name}] REJECTED: {bad}'); return False
for o, s in zip(outs, sprites):
if spec.get('check', 'finer') == 'finer': colour_match(o, s)
Image.open(o).save(os.path.join(HERE, s + '.png'))
+104 -4
View File
@@ -1302,12 +1302,12 @@
"anchor": 0,
"palette_ref": "z_r1",
"palette_tol": [
12,
10,
20,
14,
99,
4
5
],
"subject": "Three frames of the zombie SERVING. The FIRST reference image is the ZOMBIE TENNIS PLAYER this sheet is of — the same dead man, and he must be IDENTICAL to it in every frame: dirty yellow-blond hair, a RED CLOTH HEADBAND, a torn red short-sleeved tennis shirt, WARM CHARCOAL GREY tennis shorts above the knee (a slightly brownish dark grey, never blue-grey, never pale), bare grey-green dead legs, white socks and white tennis shoes, grey-green corpse skin, sunken eyes, a wooden racket with a pale strung head. Same build, same height, same colours, same drawing style. The REMAINING reference images are there ONLY to show the POSES to copy — never the character, never the clothes, never the skin. Copy each pose exactly. Frame 1: he stands at the baseline about to serve, bouncing the ball on the ground in front of him — copy reference image 2: upright, racket held low across the body, the free hand down with a tennis ball just under it. Frame 2: the TOSS — copy reference image 3: the free arm thrown STRAIGHT UP above the head with the ball leaving the fingertips, the racket dropping back behind the shoulder, weight rocking onto the back foot, the head tipped up watching the ball. Frame 3: the STRIKE — copy reference image 4: the racket at FULL STRETCH overhead at the moment of contact, the body extended upward on the toes, the free arm coming down across the chest. Output exactly 3 figures side by side in ONE horizontal row, all standing on the same ground line, with empty magenta between them. "
"subject": "Three frames of the zombie SERVING. The FIRST reference image is the ZOMBIE TENNIS PLAYER this sheet is of — the same dead man, and he must be IDENTICAL to it in every frame: dirty yellow-blond hair, a RED CLOTH HEADBAND, a torn red short-sleeved tennis shirt, WARM CHARCOAL GREY tennis shorts above the knee (a slightly brownish dark grey, never blue-grey, never pale), bare grey-green dead legs, white socks and white tennis shoes, grey-green corpse skin, sunken eyes, a wooden racket with a pale strung head. Same build, same height, same colours, same drawing style. The REMAINING reference images are there ONLY to show the POSES to copy — never the character, never the clothes, never the skin. Copy each pose exactly. Frame 1: he stands at the baseline about to serve, bouncing the ball on the ground in front of him — copy reference image 2: upright, racket held low across the body, the free hand down and EMPTY, palm open, as if he had just let a ball go. Frame 2: the TOSS — copy reference image 3: the free arm thrown STRAIGHT UP above the head the hand OPEN AND EMPTY at the top of the throw, the racket dropping back behind the shoulder, weight rocking onto the back foot, the head tipped up watching the ball. Frame 3: the STRIKE — copy reference image 4: the racket at FULL STRETCH overhead at the top of the swing, the body extended upward on the toes, the free arm coming down across the chest. Output exactly 3 figures side by side in ONE horizontal row, all standing on the same ground line, with empty magenta between them. THERE IS NO TENNIS BALL ANYWHERE IN ANY FRAME. Do not draw a ball in his hand, in the air, on the ground or on the racket — the ball is drawn separately by the game. His hands and the air around him are EMPTY, and the poses are the same as if a ball were there. "
},
"racket_fall": {
"sprites": [
@@ -1333,5 +1333,105 @@
4
],
"subject": "Four frames of the zombie COLLAPSING AND GETTING BACK UP. The FIRST reference image is the ZOMBIE TENNIS PLAYER this sheet is of — the same dead man, and he must be IDENTICAL to it in every frame: dirty yellow-blond hair, a RED CLOTH HEADBAND, a torn red short-sleeved tennis shirt, WARM CHARCOAL GREY tennis shorts above the knee (a slightly brownish dark grey, never blue-grey, never pale), bare grey-green dead legs, white socks and white tennis shoes, grey-green corpse skin, sunken eyes, a wooden racket with a pale strung head. Same build, same height, same colours, same drawing style. The REMAINING reference images are there ONLY to show the POSES to copy — never the character, never the clothes, never the skin. Copy each pose exactly. Frame 1: the knees buckle — copy reference image 2: still upright but folding, one knee giving way, the racket arm flailing out for balance. Frame 2: DOWN ON HIS KNEES, doubled over, head low, one hand on the ground, still gripping the racket in the other — copy the crumpled posture of reference image 3. Frame 3: FLAT ON THE GROUND, face down, sprawled, the racket loose beside the outstretched hand — copy the collapsed posture of reference image 4. Draw him lying down: this figure is WIDE AND LOW, not standing. Frame 4: DRAGGING HIMSELF BACK UP — up on one knee with the other foot planted, one hand pushing off the ground, head coming up, the racket gripped again, halfway to standing. Output exactly 4 figures side by side in ONE horizontal row, all standing on the same ground line, with empty magenta between them. "
},
"racket_serve_w1": {
"sprites": [
"z_rw1bounce",
"z_rw1toss",
"z_rw1serve"
],
"ref_only": [
"z_rbounce",
"z_rtoss",
"z_rserve"
],
"state": true,
"check": "loose",
"anchor": 0,
"palette_ref": "z_rbounce",
"palette_tol": [
26,
14,
99,
5
],
"subject": "The reference image is a SPRITE SHEET of ONE zombie tennis player, 3 frames. REDRAW THE WHOLE SHEET with ONE THING CHANGED: his LEFT ARM — the one WITHOUT the racket — IS COMPLETELY GONE. He has ONLY ONE ARM in every frame: no left arm, no left hand, no left sleeve anywhere, just a ragged torn stump at the left shoulder gushing blood down his side, and his shirt slashed open across the ribs. Where the reference uses that arm, the stump is thrown out in its place instead. COUNT THE ARMS: there must be EXACTLY ONE ARM in every single frame — the one holding the racket. The other shoulder ends in a stump and nothing hangs from it. A frame with two arms in it is wrong. This is the whole change being asked for; do not copy the reference's second arm across. EVERYTHING ELSE STAYS EXACTLY AS IT IS IN THE REFERENCE — the same character, the same red shirt, the same WARM CHARCOAL shorts and their exact shade, the same grey-green skin, the same white socks and shoes, the same wooden racket, the same 3 poses in the same order, the same body positions, the same facing, the same heights. Output exactly 3 figures side by side in ONE horizontal row. Because he has only one arm he can no longer throw the ball up with a free hand: in the TOSS frame that arm's stump is flung upward instead and the racket hand flicks up in front of his face, as a one-armed player tosses off the racket face. THERE IS NO TENNIS BALL ANYWHERE IN ANY FRAME. Do not draw a ball in his hand, in the air, on the ground or on the racket — the ball is drawn separately by the game. His hands and the air around him are EMPTY, and the poses are the same as if a ball were there. ",
"changed": 0.1
},
"racket_serve_w2": {
"sprites": [
"z_rw2bounce",
"z_rw2toss",
"z_rw2serve"
],
"ref_only": [
"z_rw1bounce",
"z_rw1toss",
"z_rw1serve"
],
"state": true,
"check": "loose",
"anchor": 0,
"palette_ref": "z_rw1bounce",
"palette_tol": [
26,
14,
99,
5
],
"subject": "The reference image is a SPRITE SHEET of ONE zombie tennis player, 3 frames. REDRAW THE WHOLE SHEET with ONE THING CHANGED: his HEAD IS COMPLETELY GONE — no head, no face, no hair, no headband anywhere above the shoulders, only a ragged gushing neck stump, so every figure is A CLEAR HEAD SHORTER than in the reference. He is ALREADY missing his left arm in the reference and it STAYS gone — he still has only one arm. EVERYTHING ELSE STAYS EXACTLY AS IT IS IN THE REFERENCE — the same character, the same red shirt, the same WARM CHARCOAL shorts and their exact shade, the same grey-green skin, the same white socks and shoes, the same wooden racket, the same 3 poses in the same order, the same body positions, the same facing, the same heights. Output exactly 3 figures side by side in ONE horizontal row. THERE IS NO TENNIS BALL ANYWHERE IN ANY FRAME. Do not draw a ball in his hand, in the air, on the ground or on the racket — the ball is drawn separately by the game. His hands and the air around him are EMPTY, and the poses are the same as if a ball were there. ",
"changed": 0.15
},
"racket_fall_w1": {
"sprites": [
"z_rw1fall1",
"z_rw1fall2",
"z_rw1fall3",
"z_rw1rise"
],
"ref_only": [
"z_rfall1",
"z_rfall2",
"z_rfall3",
"z_rrise"
],
"state": true,
"check": "loose",
"anchor": 0,
"palette_ref": "z_rfall1",
"palette_tol": [
26,
14,
99,
5
],
"subject": "The reference image is a SPRITE SHEET of ONE zombie tennis player, 4 frames. REDRAW THE WHOLE SHEET with ONE THING CHANGED: his LEFT ARM — the one WITHOUT the racket — IS COMPLETELY GONE. He has ONLY ONE ARM in every frame: no left arm, no left hand, no left sleeve anywhere, just a ragged torn stump at the left shoulder gushing blood down his side, and his shirt slashed open across the ribs. Where the reference uses that arm, the stump is thrown out in its place instead. COUNT THE ARMS: there must be EXACTLY ONE ARM in every single frame — the one holding the racket. The other shoulder ends in a stump and nothing hangs from it. A frame with two arms in it is wrong. This is the whole change being asked for; do not copy the reference's second arm across. EVERYTHING ELSE STAYS EXACTLY AS IT IS IN THE REFERENCE — the same character, the same red shirt, the same WARM CHARCOAL shorts and their exact shade, the same grey-green skin, the same white socks and shoes, the same wooden racket, the same 4 poses in the same order, the same body positions, the same facing, the same heights. Output exactly 4 figures side by side in ONE horizontal row. THERE IS NO TENNIS BALL ANYWHERE IN ANY FRAME. Do not draw a ball in his hand, in the air, on the ground or on the racket — the ball is drawn separately by the game. His hands and the air around him are EMPTY, and the poses are the same as if a ball were there. ",
"changed": 0.1
},
"racket_fall_w2": {
"sprites": [
"z_rw2fall1",
"z_rw2fall2",
"z_rw2fall3",
"z_rw2rise"
],
"ref_only": [
"z_rw1fall1",
"z_rw1fall2",
"z_rw1fall3",
"z_rw1rise"
],
"state": true,
"check": "loose",
"anchor": 0,
"palette_ref": "z_rw1fall1",
"palette_tol": [
26,
14,
99,
5
],
"subject": "The reference image is a SPRITE SHEET of ONE zombie tennis player, 4 frames. REDRAW THE WHOLE SHEET with ONE THING CHANGED: his HEAD IS COMPLETELY GONE — no head, no face, no hair, no headband anywhere above the shoulders, only a ragged gushing neck stump, so every figure is A CLEAR HEAD SHORTER than in the reference. He is ALREADY missing his left arm in the reference and it STAYS gone — he still has only one arm. EVERYTHING ELSE STAYS EXACTLY AS IT IS IN THE REFERENCE — the same character, the same red shirt, the same WARM CHARCOAL shorts and their exact shade, the same grey-green skin, the same white socks and shoes, the same wooden racket, the same 4 poses in the same order, the same body positions, the same facing, the same heights. Output exactly 4 figures side by side in ONE horizontal row. THERE IS NO TENNIS BALL ANYWHERE IN ANY FRAME. Do not draw a ball in his hand, in the air, on the ground or on the racket — the ball is drawn separately by the game. His hands and the air around him are EMPTY, and the poses are the same as if a ball were there. ",
"changed": 0.15
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB