The rule-bot table carries the third set now, which is the point of having a
rule bot at all: a full 50-level acceptance pass takes one second, so every
selection change can be checked against the previous one before any agent time
is spent. Blind rotations 0.24 → 0.12 → 0.14, worst streak 4 → 1 → 1, dead taps
zero throughout.
The band table shows all three rounds side by side, each against the levels it
actually played. Round 3 is reported as what it is: an unconfirmed hypothesis.
It raised contrast and held rotation depth, and the back band's rating did not
follow. On a rating that retests at 0.55 that settles nothing either way, so
the page says the two are indistinguishable and names the design reasons the
third set is the one being handed over — rather than quoting whichever number
makes the last change look good.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The round comparison was about to be wrong. levels/campaign-50-r2.json was
copied from levels/campaign-50.json to preserve "the set round 2 played", but
select-50 writes that path in place and several selection experiments ran
between the round and the copy. The file kept 28 of the 50 boards round 2 had
actually played and 46 of round 3's — so the comparison was scoring one round
against another round's levels, and it read as round 3 regressing.
Runs store the whole board and its campaign index, so a round is its own record
and cannot drift. round-set.mjs rebuilds it, the report uses that, and the
snapshot file is gone rather than left lying around looking authoritative.
With the rounds correctly matched, round 3 is not a regression and not an
improvement: back band 2.17 against round 2's 2.39, on a rating that retests at
0.55, which is not enough to separate seventeen levels. The report says that
rather than picking whichever reading flatters the change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Round 2 rated 16 runs a 3 and 84 a 2, which is too little spread to rank levels
by — but enough to ask what the 3s had in common. Contrast: 32.6 against 20.5,
a 59% gap and by far the largest of any feature. Rotation depth came second at
+1.2 turns. Board size and longest route ran the *other* way: the levels rated
harder were slightly smaller with shorter routes, which is what the design brief
has said all along — 难是难在用户无法一下子判断, not in how much board there is.
So within a depth, picking evenly across the ranked slice was diluting the one
thing the brief calls the point of the game. The depth quota already supplies
the difficulty range; inside a depth there is nothing to spread for, so it takes
the highest-contrast levels instead.
Taken alone that collapses the route grammars — two families filled 36 of the
50 — so the family cap comes down to 0.26. The pair beats the shipped round on
both axes rather than trading one for the other: contrast 14.8 / 22.0 / 27.9 →
15.2 / 24.9 / 28.6, families {18,12,8,2,10} → {13,13,5,9,10}. Same rule bot,
mechanics unchanged: worst blind streak still 1, dead taps still 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The sheet told the reviewer that difficulty and stuck were "the only two
ratings that retest reliably (r = 0.93 / 0.85)". On this set, with 50 retest
pairs to measure against, they are 0.55 and 0.45 — so the line was telling the
reviewer to trust the one number that had just failed. It now computes both
from the round in hand and says the ratings are readable by band only.
The complaints it quoted were round 1's, written about levels this edition no
longer contains. They are pulled from the round's own reports now.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The round accidentally ran every level twice, which turned into the most useful
thing it produced: 45 retest pairs on this exact set. 84% of them replayed the
identical action sequence, so the agent is near-deterministic and a second run
of a level buys almost nothing — the mechanical metrics retest at r = 1.00 for
that reason, not because they survived a hard test.
The ratings do not. Difficulty retests at r = 0.55 here, against the 0.93 this
project had on record from a smaller sample, and every pair that disagreed had
played the same trajectory: same moves, same outcome, different number. The
report now prints the reliability it measured rather than quoting the older
figure, and says which one it replaces.
Averaged by band over ~20 runs, though, the ratings finally track the curve —
2.00 / 2.05 / 2.40 with stuck at 1.21 / 1.15 / 1.74, where round 1 was a flat
line. So the rule is not "ratings are useless", it is "ratings are a band
statistic, never a per-level rank", and that is what the rules section says.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Round 1 played all fifty levels and came back with the mechanics clean and one
complaint repeated across the reports: 短暂以为转错了方向 — a turn that changed
nothing. That one is mechanical rather than perceptual, so it can be gated.
The first attempt gated `doubleDead`, and it was wrong. That counts dead states,
which scales with the state space: three-turn levels have ~187 reachable stuck
states and five-turn ones ~739, so a raw count made deep levels look eight times
worse when the rate is 2% against 5%. It would have erased every level past
three turns — the opposite of the brief, where turning is the signature and is
not to be rationed. The gate is `maxDeadTurns`, the longest run of turns before
something opens, at 1: one beat of suspense, then a release. Eleven of the fifty
shipped at 2.
Selection could not express the curve either. Spreading picks evenly over the
ranked pool returns whatever the pool is made of, and the pool is 88%
three-turn, so no weighting moved the distribution off {3: 46}. A quota per
depth states the curve outright, deepest first so the scarce end picks before a
family cap is spent on levels the shallow end has hundreds of.
Same rule bot, both sets: blind rotations 0.24 → 0.12, worst blind streak 4 → 1,
decisions unchanged at 10.3. The same amount to do, half the wasted motion.
Rotation now runs 1.8 / 3.0 / 4.1 across the bands against 1.8 / 3.0 / 3.5.
Verdicts are keyed by the board rather than by its id, because a reselect
re-issues ids from the ordinal and `c50-01-v05` is not the same board before and
after — the review sheet was one reselect away from printing one level's verdict
under another's thumbnail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
refit-weights regresses the weights select-50 scores with against what agents
actually experienced, so a round of play is a flag on the next selection rather
than an edit. Ridge, because seven collinear features on fifty levels otherwise
hand back large coefficients that cancel.
Most of the work here is in refusing to produce a number. The response averages
the difficulty rating with two continuous measures of the same thing, and a
component with no spread is dropped by name instead of being silently averaged
in as zeros — which is what the first version did, reporting R² = 0.75 for a
fit that never saw a rating. When the rating is flat the script now says so and
says what the remaining fit is really measuring, which is board size.
legibility() asks what it costs to know what you may tap: how far the eye must
follow the cheapest free piece, and how many pieces are held by somebody their
line never touches. It was built to explain why ratings came back flat. It does
not — it correlates -0.02 with them — so it stays a measurement, printed on the
review sheet beside the longest route on the board, and gates nothing.
prototype-d5 takes --seedFrom so a wide sweep shards across cores.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Opening the c50 edition threw `campaign.legacyStorageKeys is not iterable`
and left the board blank — the header still drew, so the level set looked
loaded. Every other edition carries the array; the hand-written c50 entry
did not, and 197 passing tests had nothing to say about it, because nothing
tested the registration itself.
The test now walks every edition and asserts the shape app.js relies on,
including that the chapter list can actually hold the levels. It fails on the
entry as it shipped.
The task board was reading levels/prototypes/d5.json while the campaign sat in
levels/campaign-50.json, so it showed "候选关卡 18 / 50" with fifty levels
built. It reads the shipping set when there is one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The deep-lock construction cannot go below three turns, so it cannot supply an
opening band — every level it makes lands mid-difficulty and the curve comes
out flat. harvest-easy pulls the shallow end from the shipped v0.5 set, but
only the 3% of it that holds up: 568 of 1000 need no rotation at all and 51
more are clearable by tapping at random, which is what the monkey threshold is
for. 33 levels survive, at one and two turns.
select-50 scores candidates on mechanical features only, spreads the pick
evenly across that range, and caps any one route grammar at 36% so the campaign
cannot quietly become one template repeated — the shipped 1000 turned out to be
thirty. Weights are a flag, so refitting them from a round of agent ratings is
a re-run rather than an edit.
The result is a curve rather than a plateau: difficulty score 3.85 / 6.24 /
7.60 across the three bands, rotations 1.8 / 3.0 / 3.5, and every level keeps
three or more opening choices with a show of at least a second.
The 50 register as a playable campaign edition, so review happens in the real
game; docs/review-50.html is the handover sheet, one card per level with the
opening position drawn from the solver rather than sketched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The chain locks all four of its pieces at the opening by construction, and the
pieces grown around it are mostly locked too, so a level built from it hands a
player two rim pieces and a third of a second of animation before any thinking
starts. Measured across the 235-level pool: median two tappable pieces at the
opening, and the best departure available plays for 0.57s.
Growth now reserves a couple of slots for showcase pieces — tappable at the
opening, with a route long enough to read as a journey and short enough to stay
under the animation ceiling. Median opening choices go 2 to 3 and the best
opening show 0.72s to 1.12s, without shortcutting the lock: each candidate is
still put through the solver.
Rotation quota is gone from the board. Turning is free and it is what the game
is about, so depth is grown rather than capped; what is gated is whether each
turn pays out, whether pieces swing between hopeless and trivial, and whether
there is a show up front.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The chain built its own worst moment: hero and tail deadlock at orientations 0
and +2 and open only at +1, with +3/+4/+5 left entirely to chance. That is why
some levels released at exactly one of six orientations, and why every model
kept writing "I don't know which way to turn" — at +2 one direction was right
and the other five were dead ends.
createDeepLock now traces candidates at all six orientations and requires the
tail to also release at +3, certified with the real swept tracer. A player
stuck at +2 turns either way and something moves. Measured by the rule bot
over the regenerated set: blind rotations, wrong-way turns and extra rotations
all go to zero, from 1.17 / 0.44 / 1.22 per level. Endgame orientation counts
shift from {1:4, 2:10, 3:4} to {2:3, 3:13, 4:1}.
It costs yield: switchback r3 and crossing r3 now produce nothing, and the set
comes out at 17 levels rather than 18. The test witnesses moved with it — a
witness is whichever seed still satisfies the construction, so they are
re-derived from the sweep rather than pinned.
docs/level-board.html is the task board for getting to 50 levels. Task state is
in bench/board.json; every number on the page — candidate count, rotation
quota, blind turns, monkey actions-per-piece, the agent batches — is measured
from the level set and the run archive at build time.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Regenerating levels/prototypes/d5.json renumbers every level, so the previous
build — which resolved runs through the current set's ordinals — silently
dropped every run played against an earlier set. The report I committed one
commit ago was built that way: 130 runs instead of 229, and the test-retest
table down to 7 paired levels from 18.
Levels are now keyed by id throughout. Runs carry their own level, so ids the
current set no longer contains are read back out of the archive and given an
ordinal after the live ones; the cross-model table intersects on ids rather
than assuming two sets number the same level alike.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every model that played the D5 set complained about the same moment: two
pieces left, interlocked, and nothing on the board saying which way to turn.
codex-spark took it to the end — it cycled all six orientations on L18 and
gave up on a level that was solvable, with clarity rated 5. Replaying the
position shows exactly one of the six orientations releases anything.
deadTurnAudit now reports endgameMinOpen: across reachable states with three
pieces or fewer left, the fewest orientations that release anything. It is
computed inside the walk that was already happening, so it costs nothing. The
generator requires at least two.
The gate picks out precisely the four levels every model stalled on, with no
false positives — the other fourteen already had two or three. Regenerating
drops the spiral family entirely: its structure produces single-exit endgames
by construction, not by unlucky seed.
Measured on the regenerated set, rule bot first and then grok-4.6 xhigh over
all 18:
blind rotations 1.17 -> 0.29 per level
extra rotations 1.22 -> 0.35
dead taps 2 -> 0
run time 55.5s -> 34.7s
stuck rating 1.89 -> 1.41 (this metric repeats at r = 0.85)
"which way do I turn" complaints 22% -> 12% of levels
The gate is a floor, not a difficulty dial: difficulty also fell, 2.39 -> 2.12,
because the levels it removes are the hard ones. And it treats a symptom — the
t0/t3 interlock is what the four-piece chain in createDeepLock builds by
construction, so the complaint is halved rather than gone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
One batch of 18 runs was recorded as codex-spark at xhigh but had actually run
as gpt-6-astra at low effort: the CLI config was rewritten between the check
and the batch, and codex resolved the new model silently. The archives are
relabelled from the CLI's own startup header rather than from what the agent
was told to call itself, and the report now says to trust that header over the
self-reported name. The re-run verifies the resolved model after every level
and aborts the batch on a mismatch.
With the real codex-spark batch added, five independent runs of four models
from three vendors now cover the same 18 levels on the fixed board, and 16 of
17 comparable levels agree exactly on the rotation triple.
codex-spark gave up on L18 after cycling all six orientations, reporting no
exit anywhere — replaying the position shows orientation 1 releases t0, and
that it is the only one of the six that does. The level is solvable and the
routes are legible (it rated clarity 5), but nothing on the board says which
way to turn: a 1-in-6 guess with a dead end behind every other door. That is
the terminal-interlock complaint the other models kept raising, at its limit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
design-review.html is the gameplay and UI read on the shipped campaign, with
boards rendered from the repo's own geometry rather than redrawn.
bench-reliability.html asks which of the bench's numbers are worth acting on,
and answers it by running the same 18 levels twice under identical conditions.
The mechanical rotation counters reproduce (r = 0.82-0.92) and three different
models agree on them level for level, so they measure the level rather than
the player — a 12-second rule-bot sweep gets the same answer as hours of
model time. Difficulty and stuck ratings also hold up (r = 0.93 / 0.85), but
the ratings for rotation, clarity and fun do not (0.32 / 0.19 / 0.56): asking
a model whether rotation matters is noise, while counting blind rotations is
not.
Every figure is computed from bench/runs by build-bench-report.mjs; nothing in
the page is hand-written, so it can be regenerated as runs accumulate.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The shipped 1000 levels have a hole: 568 of them need no rotation at all, and
none needs more than two — the mechanic the game is built on is mostly
decoration. This fills the empty band with levels that need at least three.
createDeepLock builds a four-piece chain where each link only releases at a
later orientation, and growDeepLevel packs the rest of the board around it
without opening a shortcut. Candidate pools are built per orientation and left
untruncated; ranking by bend count first starved the packer and capped levels
at four to six pieces instead of twelve.
deadTurnAudit enumerates every reachable (orientation, remaining-set) state to
prove no dead ends exist, and counts how long a player can be left with
nothing to do — solvable and feeling solvable are different things, which is
what "level 2 is impossible" turned out to be about.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Four ways in, depending on what you have: a shell agent driving bench.mjs, a
direct-API loop for models with only an endpoint (plain text in, one JSON line
out, so weak and local models can play), a rule-following bot that sweeps 1000
levels in 12 seconds, and a folder you can hand to someone else.
bench-kit.mjs builds that folder: CLI, engine, level pack, observer page and
tile atlas, about 1.5 MB, needing only Node 20 — no repo, no install, no
network. The engine files are discovered by following imports so a new module
can't be left behind, every path rewrite is asserted so a rename fails the
build loudly, and the builder plays a real game inside the result before
declaring it done.
bench-report.mjs aggregates two ways: per level (is this level any good) and
per model (how does this agent play). bench-import.mjs brings runs played
elsewhere back in under a tag.
bench-pack.mjs is the earlier, smaller export with no observer page; superseded
by bench-kit but kept in history rather than dropped silently.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Let any agent with a shell play a level the way a person does: look at the
board, decide what to tap or which way to turn, read the feedback, decide
again. Every step carries the agent's own reason, so a human can watch and
judge the level from how it was played.
observe() returns what the eye sees — each piece's drawn route, which cells it
crosses, who is sitting on it, and who it brushes past — never "which pieces
can leave". act() goes through the real Game rules; metrics() scores the run
afterwards with the solver.
Pieces are wide, so a route with nobody standing on it can still be blocked by
a neighbour the curve hugs. That is invisible in a cell listing, so every
obstacle is reported, on-route and grazing alike: reporting only the leading
one hid the rest, and in 113 of 581 such cases clearing the blocker sprang a
collision the agent had never been shown.
AGENT.md is the single file an agent reads — rules, board reading, commands,
rating scale, worked example.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add the paper direction with thin tracks, outlined hexagons and line arrows, without exterior color markers or piece numbers. Add a unified catalog for five designs and their 25 themes, with direct review links.
Organize each design into an authored design.md, generated tokens.json and preview. Add Agent routing, shared integration guidance, a machine-readable manifest and an executable rendering example. Preserve legacy preview and document links.
Validation: 16 focused design, theme and package tests passed; npm run design and static build passed. Confirmed all eight authored documents survive generation unchanged. Browser checks covered mobile, tablet and desktop layouts, theme links and migrated previews. Native engine integration and visual verification remain pending.
Restore v0.1 and v0.2 archives with separate progress, publish the curved v0.3 campaign, and document the original 24-level design with reproducible evidence. Refine board presentation and movement, and keep video lab tooling within its own directory.