Skip to content

feat(ale): a Breakout ladder that spans the range a player reaches - #29

Merged
drewstone merged 4 commits into
mainfrom
feat/breakout-ceiling
Aug 24, 2026
Merged

feat(ale): a Breakout ladder that spans the range a player reaches#29
drewstone merged 4 commits into
mainfrom
feat/breakout-ceiling

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

The defect

The packaged ALE Breakout contract was derived from a reference that reached score 5 over 210 inputs, so its top achievement was score >= 4. Nothing in it could tell 7 points from 24. 0.8.0's achievement-only scoring stopped it ranking the worse player FIRST; it left every program that plays at all tied at the same three achievements, and the search with no gradient above them.

The measurement

Three deterministic controls, ale-py 0.12.1, seed 0, replay-attested. screen-blind repeats a fixed three-word cycle and is the strongest program a sweep of all 340 input patterns of period four or less found.

control game score @300 @600 lives left achievements before after @300 after @600
screen-blind 7 7 0 3 of 5 3 of 7 3 of 7
steer-from-ascii 6 7 0 (dead at 375) 3 of 5 3 of 7 3 of 7
steer-from-ram 9 24 5 3 of 5 4 of 7 5 of 7

The RAM control now ranks strictly first at both budgets, and two rungs stay open above it.

The contract

A new reference, recorded once at seed 0 from a predictive paddle controller that reads the RAM ball and paddle channels, estimates the ball velocity from the previous decision, reflects the predicted path off the side walls, and steers to the crossing point. It reaches score 64 over 839 inputs.

The reference declares trigger points that double — 1, 2, 4, 8, 16, 32, 64 — and deriveContract samples the score that actually held at each, so the packaged checks are score >= 1, 2, 4, 8, 18, 32, 64. The fifth trigger reads 18 because the score steps 14 to 18 when the run clears a four-point row.

PackagedContract.calibrate passes with no declaration at all. All seven trivial baselines still score zero, at 210, 300, 450, 600, 900 and 1,200 inputs.

What the contract no longer states

  • The two hash milestones. The substitution sweep measures 56 of 96 single-input substitutions still satisfying them, at 21 of 32 turns, for at least 5.22e11 distinct 32-input logs. No independent control has ever landed on that state, so they were two points of denominator only the reference could score against.
  • life-lost. It is lives == 4, earned by dying. Removing it makes the whole-contract score equal the achievement score, so no consumer can pick the number that ranks a control that died above one that did not.
  • The requires chain. engineState.score rises and never falls, measured over the reference and every baseline, so score >= 18 cannot pass before score >= 8. collapse.collapses is now false, and the seven rungs open at seven distinct inputs (32, 71, 137, 259, 404, 636, 839).

ale.test.mts keeps a demonstration contract derived from the same reference that carries all three, so the screen-frame and save-file tiers, the collision sweep, the attrition classifier and the collapse gate stay under test on the real emulator.

What breaks

The packaged reference file, the derived contract and its contract hash all change. A stored attestation against the old contract does not verify against the new one, and its milestone ids no longer exist. Pass the previous reference through makeAle({ game: 'breakout', reference }) to keep the old contract. No adapter behaviour changed: the worker, the evidence keys, the observation, the vocabulary and the seed handling are untouched.

Every pinned number that moved is updated in the same PR: ale.test.mts, README.md, docs/adapters.md, and the measured claims in calibration.ts, schema.ts and adapters/ale.ts. The version field is untouched.

…ladder

The packaged reference reached score 5 over 210 inputs, so the contract's
top milestone could only be score >= 4. Nothing in it separated a program
that scores 7 from one that scores 24.

The new script is a predictive paddle controller recorded once at seed 0.
It reaches score 64 over 839 inputs and passes 1, 2, 4, 8, 18, 32 and 64
on the way, so the derived ladder spans the range a real player reaches.
…hinery live

The packaged contract is now seven rungs of one legible progression, so the
test asserts the derived ladder, runs `PackagedContract.calibrate` with no
declaration to make, and requires the RAM control to rank STRICTLY above both
the ASCII control and the strongest screen-blind program at 300 and 600
decisions. It also asserts headroom: the strongest control must not reach the
top rung.

A demonstration contract derived from the same reference keeps the two hash
tiers, a requires chain and the attrition milestone under test, with the
collision sweep re-pinned at 56 of 96 substitutions.
…on moved

The packaged Breakout contract carries seven legible rungs, no hash and no
attrition milestone, so the README and the adapter guide can no longer use it
as the example of an opaque check, a collapse to one event or a point for
dying. Those examples now name the demonstration contract in `ale.test.mts`,
with the collision sweep re-measured at 56 of 96 substitutions and a family
bound of 5.22e11.

The determinism claim moves from 211 snapshots to 840, and the CHANGELOG
states what a consumer holding an attestation against the old contract has to
do.
@drewstone
drewstone merged commit 270834b into main Aug 24, 2026
6 checks passed
@drewstone
drewstone deleted the feat/breakout-ceiling branch August 24, 2026 19:14
@drewstone drewstone mentioned this pull request Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant