depth 4leaf weights
Technique

Evolutionary optimisation

Try many slightly different versions, keep the ones that did best, make the next generation from them, and repeat; no gradient needed.

Read the primer: Evolutionary optimisation

On this page

Let an evolutionary optimiser adjust the eighteen numbers inside the search's board scorer by playing complete depth-4 games on a fresh block of seeds every generation, then test the frozen winner exactly once on games it has never seen.

The intuition

At the bottom of its four-move look-ahead the search has to say how promising a board is. That opinion is the leaf evaluator: nineteen hand-written terms — build readiness, latent chain potential, cover access, height, danger near a rise — each multiplied by a number someone chose by hand for a three-move search under the old 7,000-point scoring. Those numbers have never been refitted for the four-move search they now live in, or under the corrected 17,000-point rules (the fair leaf's own page says so).

Two earlier attempts to tune them were retired, and each failed in a way that does not say the numbers are right: one tuned at depth 3 on three-game batches and moved the result to depth 4 unchanged (machine-tuning the board evaluator); the other fitted one-move policies on a fixed set of games and lost on different games (evolution). Refitting the leaf toward a quantity the search already computes made things much worse (leaf reweight), which is evidence the leaf's job is to price what the search cannot see, and a reason to let whole games, not a proxy, choose the weights.

So this experiment removes the two known failure channels at once. The fitness of a candidate is the mean score of complete depth-4 games, at the configuration the policy is deployed in. And every generation plays a block of seeds that no earlier generation saw, so the optimiser cannot memorise games; it can only move toward weights that are better on average.

building the tree…
building the tree in the browser…
MAX node the position; the search picks the column with the highest expected value.chance branches every exact outcome of a drop — the next disc, and what any cracked gray disc turns out to be — weighted by its probability.leaf the opinion of the board scorer where the look-ahead stops. Click a column to expand it; click an outcome to play that transition and continue from it.Columns are numbered 1–7 from the left. Values come from the browser solver and are a demonstration, never research evidence; seed 0x5eed1005 is the same game as /play.
What the evolution is tuning: the numbers on the outcome boards. Change the weights and every leaf value moves, the averages move, and a different column can win, which is why the weights must be tested by whole games rather than by how sensible the numbers look.

How it works, step by step

  1. Weights become data. The bit-exact fast engine's leaf (fast-leaf.hpp) is copied with its eighteen constants replaced by an array, in the same accumulation order. At the frozen vector it returns the same bits; the search built on it (WeightedFastSearch, generated from fast-search.hpp by a checked substitution in build.sh) selects the same column with the same work.
  2. Gates before any seed. gate.cpp checks leaf bit-identity on every board a real search visits, search parity with the frozen fast search, determinism across thread counts, mirror symmetry, blindness to score/level/move number, and that perturbed weights still complete depth 4 with legal moves.
  3. Population evaluation. evaluate.cpp plays every individual on the same ordered block of seeds (common random numbers) and writes one row per game. The frozen vector is played on every block as a control.
  4. CMA-ES. evolve.py runs a (8/8, 16) covariance-matrix-adaptation strategy with mirrored sampling in sign-normalised coordinates (the frozen vector is ±1 on every axis; one step of σ is a relative change). Generation g plays seeds 0xa5290000 + 32g+31. The candidate is the distribution mean at the last generation, not the best sampled individual, whose fitness is inflated by having been selected on its own noise.
  5. One held-out screen. compare.py evaluates the frozen candidate against the frozen leaf on 64 never-read seeds (0xa52b0000+), paired, with a one-sided 95% bootstrap lower bound, both halves, and the lower quartile. The gate was fixed and hashed before the first training seed was read.

The policy reads only the visible board, the visible next disc and the moves until the next rise, exactly as the reference does.

What has happened so far

The pilot measured the cost of a decision on this machine with the fast engine: about 0.29 CPU-seconds at depth 4 with five chance samples and about 1.03 with seven, and it reproduced the recorded seven-sample and five-sample reference cohorts seed for seed on the first sixteen games. The six CHECK gates passed with zero mismatches (2,500 and 4,285 leaf boards; 50 and 160 parity moves). The evolution then started on the five-sample configuration: the contract comparator because at 3.7× less per game it affords roughly forty generations overnight instead of twelve, and the seven-sample transfer of the winner is a preregistered secondary measurement on the same held-out seeds.

Technical recordThe record
  • Theory TH-20260822-depth-native-leaf-weights-caa6f8ba; experiment EX-20260822-leaf-cmaes-d4s7-4f5f462a (frozen, SCREEN tier, protocol SHA-256 87d2d15d…); leases SL-20260822T020000Z-a5290000 (training, opened) and SL-20260822T020000Z-a52b0000 (held-out, reserved).
  • Pilot run RUN-20260822T013250Z-cb282a1c (seeds 0xa51d1000+16, already opened development data; runtime projection only). CHECK run RUN-20260822T013756Z-0f7ab038. Evolution run RUN-20260822T014412Z-a0c63063: λ = 16, μ = 8, σ₀ = 0.25, 32 games per individual per generation, depth 4, 5 strata, cache 60,000, work bound at the worst case, at most 40 generations or 14 wall-hours.
  • Gate: bootstrap and Student-t 95% lower bounds of the paired score delta above zero, lower-quartile non-regression, both halves positive, zero incomplete or illegal decisions. Failure records valid + fail for this exact configuration and adopts nothing.
  • Limit stated in advance: a 64-game paired cohort cannot see an effect below roughly 50,000 points. A null here is a non-measurement for smaller effects, not evidence that the frozen weights are optimal.

Sources

  • weighted-leaf.hpp, build.sh: the weighted leaf and the generated search
  • gate.cpp, evaluate.cpp — CHECK gates and the population evaluator
  • evolve.py, compare.py: the optimiser and the paired held-out comparison
  • screen.sh: the once-only held-out screen, scripted before the candidate existed so the most safety-critical command is reviewed rather than typed
  • decide.cpp: a one-shot decision binary for the benchmark playground: src/bench/native-policy.ts bridges it into the leaderboard as native-fair-d4-s7 (frozen leaf) so the research search itself, and later an evolved leaf, can play the scripted rounds. Scripted-round scores are a demonstration, never evidence.
RecordsTheories, experiments and results that reference this directory

Claim: For the corrected-score fair depth-4 search with the frozen five-stratum chance model (the contract comparator fair-d4), there exists a vector for the eighteen active fair-leaf weights whose mean whole-game score on fresh development seeds exceeds the frozen vector's by more than a 64-game paired cohort's detection floor, and a derivative-free optimiser that evaluates candidates by complete depth-4 games on a fresh seed block every generation can find such a vector within 40 generations of 16 candidates.

This theory is currently not-supported-as-tested at the public-development (a cohort for deciding what to try next, not confirmation) level.

Claim: The depth-3 factored-chance fair search with seven disc strata and six reveal samples (D3 N7M6, fair leaf, finding-16) beats the frozen fair depth-4 five-stratum search (D4 s5, N5 M1, finding-05) on the shared development cohort 0xa51d1000-0xa51d103f by a paired per-game score margin whose one-sided 95% whole-game bootstrap lower bound and Student-t lower bound are both above zero.

This theory is currently supported-as-tested at the public-development (a cohort for deciding what to try next, not confirmation) level.

It compares fast-d4s5-evolved-leaf against fair-d4 at the SCREEN (a 32-game paired screen) level, using public-development data.

valid run outcome: fail The run was valid and the outcome was fail (public-development (a cohort for deciding what to try next, not confirmation)). Read the result.

It compares D3 N7M6: factored-chance-fair-search, depth 3, discSamples 7, revealSamples 6, terminalUtility -1000000, maximumWork 51084852, 2000-move cap, corrected 17,000-point Hardcore scoring (finding-16 arm, RUN-20260821T035407Z-00483c6c) against D4 s5: parameterized-fair-search, depth 4, chanceSamples 5 (N5 M1), terminalUtility -1000000, maximumWork 3200000, 2000-move cap, corrected 17,000-point Hardcore scoring (finding-05 fresh-s5 arm, runs/RUN-A51D-s7confirm/fresh-s5.json; byte-identical games to runs/RUN-A52-LEAF/eval/s5-w000.json) at the CHECK (mechanics checks only, no games played) level, using previously-evaluated-development data.

valid run outcome: pass The run was valid and the outcome was pass (public-development (a cohort for deciding what to try next, not confirmation)). Read the result.

resultvalid runoutcome: failtier: public-developmentRS-20260822T120736Z-662b39ca

The run was valid; the outcome was fail, at the public-development (a cohort for deciding what to try next, not confirmation) level. Of 6 preregistered checks, 4 passed and 2 failed.

Held-out SCREEN of the CMA-ES leaf (distribution mean after 40 generations, stop=max-generations) against the frozen fair leaf, depth 4 five strata, 64 paired never-read games 0xa52b0000+: mean 289720 vs 320020, paired delta -30300 (bootstrap 95% LB -70928, t LB -71500, paired sd 197434, floor 40597), W-T-L 28-0-36, halves -53933/-6667, Q25 delta +8592. Gate: FAIL. Secondary seven-stratum transfer on the same seeds (diagnostic only): delta -47119 (LB -117910). Training-block population-mean-minus-control exceeded +20,000 in at least one of the last 10 generations.

Technical recordLimitations recorded with the resultRS-20260822T120736Z-662b39ca
  • Single 64-game development-tier screen on one machine; no replication yet (192 seeds of the held-out lease remain reserved for one).
  • Evolution and screen ran while a GPU training sweep shared the machine; wall times are not timing-grade, decisions are deterministic and unaffected.
  • The seven-stratum arm replays already-read held-out seeds and is diagnostic only.
  • The population-mean trajectory on training blocks carries the perturbation penalty of sampling at sigma and does not measure the candidate's own fitness.
  • Scripted-round (leaderboard) play of any vector is a demonstration and is not part of this result.

Full record →

resultvalid runoutcome: passtier: public-developmentRS-20260823T194200Z-42b113db

The run was valid; the outcome was pass, at the public-development (a cohort for deciding what to try next, not confirmation) level. Of 2 preregistered checks, 2 passed and 0 failed.

C0 reanalysis (K2 program section 8 row 0): the paired per-game bound behind the +79,115 head start is now printed, and it clears zero. On the 64 shared seeds 0xa51d1000-0xa51d103f, depth 3 with seven disc strata and six reveal samples (fair leaf, finding-16) scores 376,442 against fair depth 4 at five strata's 297,327 (finding-05): paired +79,115, one-sided 95% percentile bootstrap lower bound +29,033 (upper +129,722; 20,000 resamples, seed 0xb0071eaf), Student-t lower bound +27,548, W-T-L 35-0-29, halves +88,138 / +70,092, Q25 +34,872 (227,224 vs 192,352), median 322,859 vs 260,415, median paired delta +32,926, moves +22.30 (LB +8.67), paired sd 247,113, detection floor 50,813, at 3.27x the logical work per move (4,244,020 vs 1,296,034). Both gate bounds are positive, so the experiment passes and the theory is supported-as-tested at public-development tier. READ THE CAVEATS: the means and their difference were published before this gate was written, so this is a bound attached to a known sign on already-read development data, not a discovery; it is not promotable above this tier on this cohort. The delta is heavy-tailed: the five largest paired gains (seeds 0xa51d1012 +747,416, 0xa51d1020 +684,129, 0xa51d1008 +643,799, 0xa51d1001 +643,344, 0xa51d1033 +558,163) carry 64.7% of the summed delta, the sixteen largest carry 134% (the remaining 48 games net negative), and the worst loss is -332,950 (0xa51d103c); the minimum leave-one-out mean is still +68,507, so no single game is load-bearing, but against a detection floor of 50,813 the mean sits 1.56 floors above zero and the bootstrap lower bound only 0.57 floors above it. The flow statistics move with the score: numbered clears per move 2.0447 vs 1.9489, cover reveals per move 1.1423 vs 1.0697 (paired clears +53.9 per game, LB +21.1; reveals +31.8, LB +12.6), mean occupancy 23.49 vs 24.29. SECONDARY, not gated: against fair depth 4 at seven strata (398,498) the same arm is -22,056 with bounds (-92,005, +45,490), t lower bound -92,330, W-T-L 30-0-34, halves -27,121 / -16,991, Q25 +14,360, moves -5.20, at 0.86x the work; this reproduces finding-16's -22,056 point estimate exactly and its bootstrap lower bound to within 2,138 (finding-16 printed -89,867 from its own resampler; this run's pinned seed gives -92,005), and remains a wash. So the head start the K2 fallback route (c) stands on is real against D4 s5 but the same arm does not separate from D4 s7, which is the stronger comparator and the one the program's end state must beat on the strength-cost frontier.

Technical recordLimitations recorded with the resultRS-20260823T194200Z-42b113db
  • Bound computation on already-read development data whose means and sign were published (finding-16, finding-05) before the gate was written; diagnostic, not promotable above public-development on this cohort. A fresh-development replication under a new protocol is required before the +79k is used as anything but a planning prior.
  • The two arms were played in different runs (RUN-20260821T035407Z-00483c6c for D3 N7M6; the finding-05 fresh-s5 arm) by different binaries (factored-chance-fair-search vs parameterized-fair-search); the seed lists, move cap, terminal utility and scoring are identical but the pairing is across builds, not within one runner invocation. Engine parity between these families was established separately (finding-09/16 checks; finding-15 engine control), not re-run here.
  • Heavy tail: five games carry 64.7% of the summed delta; the bootstrap lower bound sits 0.57 detection floors above zero. 64 games cannot resolve anything below about 50,800.
  • The arm does not separate from D4 s7 (-22,056, bounds -92,005 to +45,490), so 'beats D4 s5' does not transfer to 'beats the current best fair comparator'.
  • The wrapper pinned at freeze failed to import (module-name collision) and was amended before any output existed; the amendment changed no statistic. The first pinned wrapper hash is retained in expectedArtifacts and the amended hash in amendments.

Full record →

Agent contextSource files, operational notes and how to reproduce

Directory: approaches/lifetime-objective/leaf-evolution