Rust bitboard engine
A packed Drop7 engine that avoids unused leaf calculations and offers bounded private or shared search caches.
On this page
- A nibble-packed column-major Rust engine reproduces fair expectimax bit-for-bit faster than the C++ fast engine
- A centralized deterministic frontier scheduler keeps deep fair search workers busy across columns
- A depth-5-distilled, whole-game-evolved NNUE leaf beats the frozen fair leaf inside the same depth-3 search
- One Rust search-matrix config reproduces depth, strata, and leaf comparisons locally and on EC2
- Klein-Friedmann six-feature linear Q-learning transfers to corrected Hardcore as a survival heuristic, not a scoring policy
Show all 8Show fewer
- Rust bitboard engine: parity gates and throughput benchmark vs TypeScript and C++ engines
- Central frontier scheduler parity and within-decision scaling check
- Depth-5-distilled NNUE leaf refined by paired-fitness whole-game evolution inside the depth-3 fair search
- Rust search-matrix analytics and EC2 launch reproducibility check
- Port the six-feature linear Q-learner onto the Rust bitboard engine: parity gates and a diagnostic pilot on previously evaluated development seeds
Show all 12Show fewer
- Depth-5-distilled NNUE leaf refined by paired-fitness whole-game evolution inside the depth-3 fair search (v2: operational parameters fixed in the record body)
- Depth-4 sibling-value panel, linear Q fit, and pruned-search regret on held-out roots
- Continued whole-game evolution of the depth-5-distilled NNUE leaf from the first run's generation-60 population, with annealed mutation and a preregistered plateau stop (up to 1,000 generations)
- Second continuation of the depth-5-distilled NNUE leaf evolution: resume from the plateau population with a slower mutation-size decay (up to 1,000 further generations)
- Row-and-column n-tuple network trained by temporal-coherence TD on the Rust engine, screened as the leaf of the depth-3 seven-stratum fair search
- Rust exact leaf and cache efficiency checks
- Fresh-block replication of the row-and-column n-tuple leaf at larger scale: wider tables trained until the validation margins plateau, screened beside the first candidate
- Rust bitboard engine: parity gates and throughput benchmark vs TypeScript and C++ engines
- Central frontier scheduler parity and within-decision scaling check
- Rust search-matrix analytics and EC2 launch reproducibility check
- Port the six-feature linear Q-learner onto the Rust bitboard engine: parity gates and a diagnostic pilot on previously evaluated development seeds
- Depth-4 sibling-value panel, linear Q fit, and pruned-search regret on held-out roots
Show all 11Show fewer
- Depth-5-distilled NNUE leaf refined by paired-fitness whole-game evolution inside the depth-3 fair search (v2: operational parameters fixed in the record body)
- Continued whole-game evolution of the depth-5-distilled NNUE leaf from the first run's generation-60 population, with annealed mutation and a preregistered plateau stop (up to 1,000 generations)
- Second continuation of the depth-5-distilled NNUE leaf evolution: resume from the plateau population with a slower mutation-size decay (up to 1,000 further generations)
- Rust exact leaf and cache efficiency checks
- Row-and-column n-tuple network trained by temporal-coherence TD on the Rust engine, screened as the leaf of the depth-3 seven-stratum fair search
- Fresh-block replication of the row-and-column n-tuple leaf at larger scale: wider tables trained until the validation margins plateau, screened beside the first candidate
Motivation
Looking ahead means simulating many possible drops before choosing a column. The same small operations repeat throughout the search: move discs, evaluate the resulting board, and check whether an earlier branch already solved it. An unnecessary scan or memory access in those operations repeats too.
The C++ fast engine already stores rows in packed words, and uses certain optimizations to operate on them. When developing this bitboard engine, all of the assumptions were challenged, including whether to pack rows or columns in each 32-bit word.
How it works
- Store each column in a machine word. Each cell occupies a four bit nibble,
with the bottom cell at the low end. A rise shifts the word
and inserts a covered disc. Gravity gathers the surviving nibbles in their
existing order. On an x86 build with BMI2 instructions, that gather uses
PEXT; other targets use an equivalent portable implementation.
u32 words used by Board, where each column uses 4 bits per cell.- At a leaf, where look-ahead stops and the board is
scored, unpack the columns into a row-major byte array. The fair evaluator
reads cells and neighbours repeatedly, so this conversion happens once per
evaluation. BMI2 builds use
PDEPto spread a column's nibbles into bytes. Portable builds now extract those nibbles with direct shifts, avoiding the general-purpose bit-scatter loop. The feature definitions and their floating-point accumulation order stay the same. This makes the portable conversion explicit; a shorter source path alone does not establish fewer machine instructions.
pub fn to_bytes(&self) -> [u8; CELL_COUNT] {
let mut out = [0u8; CELL_COUNT];
for col in 0..BOARD_SIZE {
#[cfg(all(target_arch = "x86_64", target_feature = "bmi2"))]
{
let spread = pdep64(self.cols[col] as u64, 0x0F0F_0F0F_0F0F_0F0F);
for nibble in 0..BOARD_SIZE {
// nibble n of the column word is row (6 - n).
out[(BOARD_SIZE - 1 - nibble) * BOARD_SIZE + col] =
(spread >> (8 * nibble)) as u8 & 0xF;
}
}
#[cfg(not(all(target_arch = "x86_64", target_feature = "bmi2")))]
{
let word = self.cols[col];
for nibble in 0..BOARD_SIZE {
out[(BOARD_SIZE - 1 - nibble) * BOARD_SIZE + col] =
(word >> (4 * nibble)) as u8 & 0xF;
}
}
}
out
}- Build a release-support list only when the disc needs one. The leaf estimates whether other discs could clear and shorten a run that is too long for its number. When the run is already short enough, this release term is zero. Checking that condition before scanning neighbours avoids building and discarding a list. Solid covered discs also need only their two strongest neighbouring supports, which can be selected without sorting the full list. Cracked discs retain their sorted multiplication order so their value bits stay unchanged.
let value = cells[index] as i32;
let horizontal_excess = horizontal.length[column] as i32 - value;
let vertical_excess = vertical.length[row] as i32 - value;
// Release is exactly zero unless the run is longer than the disc's
// value. Avoid gathering a supporter inventory that cannot be read.
let horizontal_release = if horizontal_excess > 0 {
let mut support = [0.0f64; BOARD_SIZE];
let mut count = 0usize;
let mut scan_col = horizontal.start[column] as i32;
while scan_col <= horizontal.end[column] as i32 {
let supporter = row * BOARD_SIZE + scan_col as usize;
if supporter != index && scratch.present(supporter) {
support[count] = scratch.addition[supporter];
count += 1;
}
scan_col += 1;
}
release_readiness(horizontal_excess, &mut support, count)
} else {
0.0
};- Check the cache's depth threshold before building a key. A transposition table remembers the value of a position reached through another branch. The threshold is the remaining search depth at that position. Raising it skips shallow cache accesses, including key packing and hashing, while allowing deeper results to be reused. Capacity controls allocated memory separately: raising the threshold does not shrink an already allocated table.
// The shallow majority never packs a key or executes its three mixes.
let cache_key = if self.table.accepts_depth(depth) {
let key = PackedKey::new(state, depth);
let hash = hash_key(&key);
if let Some(cached) = self.table.lookup(&key, hash, depth) {
return Ok(cached);
}
Some((key, hash))
} else {
None
};- Choose who owns the table. Private tables let workers access their own memory without locks. The optional shared table lets a worker reuse another worker's completed result during the same decision. Slots are grouped into stripes, with one lock protecting each group's full keys and values. A worker tries the lock once; a busy stripe becomes a cache miss, so the worker continues its own search. Two workers can still compute the same subtree before either has published a result. Grouping slots bounds the number of locks, including runtime lock allocations on platforms that need them.
#[inline]
fn lookup(&mut self, key: &PackedKey, hash: u64, depth: i32) -> Option<f64> {
if !self.accepts_depth(depth) {
return None;
}
let index = hash as usize & self.storage.mask;
let stripe = self.storage.stripes[index & self.storage.stripe_mask]
.try_lock()
.ok()?;
let slot = &stripe[index >> self.storage.stripe_shift];
if slot.depth > 0 && slot.key == *key {
self.hits += 1;
Some(slot.value)
} else {
None
}
}- Reduce finished tasks in the original order. A central work queue distributes continuation subtrees across workers. Results go into fixed slots, then the coordinator combines them in the same column and chance-sample order as the sequential search. Shared storage is created afresh for each decision, so results cannot survive a change of evaluator or search settings. Private tables remain the default.
The existing gravity and cover-hit diagrams show the packed operations that precede leaf evaluation.
nonzero_flags marks one bit per occupied nibble, multiplying by 0xF selects all four bits, and PEXT gathers the selected nibbles in order. The recorded cascade uses the same operation for 0x90 → 0x09 and 0x10 → 0x01.The explosion bitplanes diagram shows an efficient mechanism for counting hits and double hits for a single wave of a chain reaction.
Results
The updated search ran 1.07× faster on six constructed root positions over five alternating repeats, with identical completed-search values and actions. The leaf batch also used fewer instructions. The optimization result retains the configuration, every repeat, and the limits of this local measurement.
The portable unpacking rewrite produced no credible standalone speedup. Its nearly unchanged instruction count suggests the compiler already simplified the old constant-mask scatter. The direct shifts remain a clearer expression of the fixed conversion.
The capacity sweep shows diminishing returns: the middle-sized table already removed most of the work saved by the largest tested table. Raising the minimum cached depth lost useful shallow hits without reducing allocated table memory. The default remains private storage with a minimum depth of one.
Cache capacity and search time
- Cache from depth 1
- Cache from depth 2
- Cache from depth 3
Notes and sources
Six constructed public roots per repeat, five alternating-order repeats on an interactive Apple M3 Pro. Depth 4, seven chance strata, completed searches with exact value checks. Whiskers show the minimum and maximum repeat times, not confidence intervals. CHECK mechanics evidence; these roots and elapsed times do not measure playing strength. The table view lists the plotted values and their sources.
Spec: web/content/figures/rust-cache-capacity.json · 1 source record · kind line
Sharing helped modestly when several workers searched the same decision. At four workers and the shallow cache threshold, it reduced median logical work by 5.2% and retired instructions by 3.9%, with a 1.03× ratio of latency medians. The one-worker shared arms were slightly slower. Both scopes had the same total entry budget; shared storage also needed its bounded lock allowance.
Notes and sources
Six constructed public roots per repeat, five alternating-order repeats on an interactive Apple M3 Pro. Depth 4, seven chance strata, completed searches with exact value checks. Whiskers show the minimum and maximum repeat times, not confidence intervals. CHECK mechanics evidence; these roots and elapsed times do not measure playing strength. The table view lists the plotted values and their sources. Both scopes have 65,536 total entries. Shared storage includes an additional bounded stripe-lock allowance. The frontier split is fixed at zero internal plies.
Spec: web/content/figures/rust-shared-cache.json · 1 source record · kind bar
What we learned
A column layout suits gravity, while a byte view suits the fair leaf's repeated cell reads. Avoiding the conversion would require making those reads cheaper elsewhere. The current changes instead preserve the evaluator and remove work that cannot contribute to its answer.
Cache hit rate alone leaves out the size of the subtree saved by each hit. Depth, table capacity, and worker ownership therefore need to be measured together with elapsed time, CPU use, and memory. Shared caching offers another trade-off: more opportunities for reuse, plus lock traffic and contention.
Chess engines suggest further experiments. Stockfish groups entries and uses depth and age when choosing replacements. Its compact signatures and racy multi-field updates have a different correctness contract from the complete key comparisons and synchronized values used here; copying that storage scheme would require changing this engine's exact-value guarantee. See Stockfish's table implementation.
Profile-guided optimization is another unmeasured option: representative execution profiles can guide inlining, code layout, and register allocation. Which of clustered full-key storage or compiler profiling saves more time on the intended CPU remains an open question.
Agent contextRecords and provenance
The current engineering check is
EX-20260905-rust-bitboard-improvements-check-c68c07b7,
under theory
TH-20260905-rust-bitboard-improvements-1c56b072.
Its comparator is the Rust implementation at
7adb6b36412b4bbef25623183dac1a905570cd1a. It opens no new gameplay cohort and
makes no policy-strength claim.
The current result is
RS-20260905T193830Z-9733627a.
The comparison summary is
artifacts/results/EX-20260905-rust-bitboard-improvements-check-c68c07b7/RUN-20260905T191558Z-f9e6cb1d/summary.json;
the machine profile is
research/system-profiles/MACH-20260905T191558Z-4a61bc91.json.
The host ran macOS on Apple M3 Pro with Chrome, WindowServer, and Codex active.
The check serialized its own measurements and paused its own builds and
tests; it had no exclusive CPU or affinity guarantee. These are local
engineering measurements, with no claim of maximum machine throughput.
The historical packed-engine result is
RS-20260824T075451Z-e89ea128,
recorded as valid + pass, mechanics-only evidence from the CHECK experiment
EX-20260824-rust-engine-parity-throughput-4036a91f.
Its timing host was the shared AMD Ryzen AI MAX+ 395 workstation. The result
retains metric summaries; its original local files under
runs/RUN-20260824T052018Z-b88c3e22/rust-engine/ may be absent from a clean
checkout. The historical table below transcribes the result and does not
claim that those missing artifacts were regenerated by the current check.
The historical frontier result is
RS-20260825T052959Z-1b3ed9a5,
recorded as valid + pass, mechanics-only CHECK evidence on a shared
12-logical-CPU arm64 machine. Its theory is
TH-20260825-central-frontier-scheduler-e4f547e0.
The search-matrix and resource-planning result is RS-20260825T052959Z-57698687. It covers the bounded launch workflow and allocation-free plans. No EC2 resource was created by that check, and its large-worker plans are not timing measurements.
Agent contextFull results table
Current before/after measurements from RS-20260905T193830Z-9733627a, using the same six constructed roots and five alternating repeats per arm:
| Workload | Baseline median seconds | Updated median seconds | Ratio of batch medians | Recorded instruction reduction |
|---|---|---|---|---|
| Depth 4, seven strata, gate 1, 16,384 entries | 12.052394167 | 11.257799041 | 1.07058× | 4.4384% |
| Leaf batch, 1.2M calls per repeat | 0.604415917 | 0.516645375 | 1.16989× | 10.6541% |
| Unpack batch, 1.2M calls per repeat | 0.004975042 | 0.005177292 | No credible gain | About 0.059%; effectively unchanged |
The depth-4 gate-1 comparison also had a 1.0678604115× median paired speed ratio, with repeat ratios from 1.0582717724× to 1.1158545109×. The table above uses ratios of batch medians.
The complete depth-4 search retained 39,288,466 logical work units in each arm. Its median process CPU time fell from 11.93 to 11.15 seconds. Median retired instructions fell from 222,843,607,698 to 212,952,842,410; the leaf batch fell from 10,346,642,712 to 9,244,298,981. These counts cover the timed process and its harness. They do not imply the same gain on other CPUs or board distributions. The unpack batch's CPU time is near the host's printed timer resolution and cannot support a precise CPU-time claim.
The unpack batch recorded 109,784,960 versus 109,720,197 median instructions. Its source rewrite did not demonstrate a useful instruction saving or a latency improvement. The combined leaf and search measurements support the retained optimization package; they do not isolate the contribution of every changed leaf branch.
The capacity sweep's gate-1 rows, each using five repetitions of six roots:
| Entries | Allocated table bytes | Median search seconds | Logical work units |
|---|---|---|---|
| 1,024 | 49,152 | 13.765320707 | 47,250,952 |
| 16,384 | 786,432 | 11.305829207 | 39,288,466 |
| 262,144 | 12,582,912 | 10.844751666 | 38,002,223 |
| No table | 0 | 17.604948875 | 64,858,647 |
At four workers, gate 1 and 65,536 aggregate entries, private and shared median decision times were 3.108119542 and 3.017273043 seconds. Median process CPU times were 11.75 and 11.37 seconds. Their table projections were 3,145,728 and 3,186,688 bytes, respectively; the shared difference covers stripes and runtime locks. The corresponding median peak process RSS values were 6,029,312 and 5,537,792 bytes. RSS includes the harness and runtime and is reported separately from the layout projection.
Sequential search timers exclude table allocation. Parallel decision timers include planning, allocation, execution, and reduction; do not compare those two timing definitions as a direct scheduler speed ratio. Counter measurements include the whole process. Chart whiskers show minimum and maximum repeat times, not confidence intervals. All 210 timed processes completed within the registered 1,800-second measurement budget.
Historical packed-engine measurements from RS-20260824T075451Z-e89ea128:
| Measurement | TypeScript | C++ reference | C++ fast | Rust packed |
|---|---|---|---|---|
| Engine moves/s | 649,471 | 6,799,180 | 6,511,760 | 12,838,933 |
| Leaf ns/evaluation | not recorded | not recorded | 187.5 | 155.6 |
| Depth 4, seven strata, ms/decision | not recorded | 3,247.8 | 1,071.5 | 907.6 with 64k entries |
| Depth 5, seven strata, ms/decision | not recorded | 23,992.6 | 7,817.3 | 7,047.1 with 1M entries |
| Engine moves/s, 16 game workers | not recorded | not recorded | 82,364,000 | 129,483,860 |
The retained result's depth-4 timing used 21 decisions, best of three; depth-5 timing used three decisions in one repeat. The move-throughput game's exact count and the leaf timing's evaluation count are not carried in the retained metric summary. The parity panels below are separate workloads and must not be used as the timing sample counts.
| Historical Rust cache arm | Depth 4, seven strata, ms/decision | Depth 5, seven strata, ms/decision |
|---|---|---|
| No table | 1,633.4 | 63,325.4 |
| Direct mapped, 64k entries | 907.6 | not recorded |
| Direct mapped, 256k entries | not recorded | 7,787.9 |
| Direct mapped, 1M entries | not recorded | 7,047.1 |
| Direct mapped, 4M entries | not recorded | 6,748.4 |
The same historical result records work falling from 11.9M to 6.3M units at depth 4 (47%) and from 582.7M to 59.5M at depth 5 (90%). Its reported node hit rates are 1.3% and 1.36%, respectively. These summaries describe the original measurement configurations.
| Historical allocation | Bytes recorded |
|---|---|
| Rust board | 28 |
| Rust searcher excluding table | 2,496 |
| Rust table, 64k entries | 3,145,728 |
| C++ fast table | 16,194,304 |
Historical scheduler measurements from RS-20260825T052959Z-1b3ed9a5:
| Configuration | Roots per repeat | Repeats | Root median seconds | Frontier median seconds | Recorded speedup | Task-phase busy fraction |
|---|---|---|---|---|---|---|
| Depth 4, seven strata, 12 workers | 8 | 3 | 4.076978 | 3.232507 | 1.2612× | 0.9951 |
| Depth 5, seven strata, 12 workers, bounded split | 3 | 3 | 22.034955 | 19.410905 | 1.1352× | 0.8823 |
| Depth 5, seven strata, over-expanded split | not separately recorded | not separately recorded | 24.959552 | 24.707506 | 1.0102× | 0.9963 |
The over-expanded split increased logical work from 381,642,037 to 539,523,663. Its higher worker occupancy did not meet the speed gate.
The historical allocation-free depth-7, seven-stratum plan for 192 workers selected one internal split ply, at most 2,401 frontier tasks, and projected 2,418,377,728 bytes under an 8 GiB guard. The old 16,777,216-entry table per worker was rejected before allocation at 154,618,822,656 bytes. These are plans from RS-20260825T052959Z-57698687, with private tables under that version's layout; the current shared-table layout must be planned again.
Agent contextValidity, gates and limitations
The current leaf regression test compares every feature's raw f64 bits
against a preserved test-only copy of the pre-change evaluator on constructed
public boards, including sparse positions, dense positions, holes, solids,
and cracked discs. The unpacking test compares direct extraction with the
original PDEP expansion and scalar cell reads. These mechanics checks do not
measure playing strength.
The current result records 40 passing Rust release tests, six C++ leaf
comparisons, and six complete depth-4/seven-stratum root comparisons. Native
bit parity uses -ffp-contract=off. The C++ trajectory check compared 575 moves
across 32 fixtures, and the TypeScript check compared 660 moves across 32
fixtures, with zero mismatches. The root npm suite passed 144 tests with two
skips. make -k test passed its native and TypeScript gates but failed the
aggregate target on 96 errors for missing historical artifacts. The web build, typecheck, lint and 70 web tests pass. The separate chart
library retains one pre-existing stale snapshot assertion (63/64 pass); the
route sweep passes 727/728, with /compete lacking local authentication
configuration. All eight missing-data render checks pass. These
checkout limitations remain disclosed in
RS-20260905T193830Z-9733627a.
Completed searches must retain identical legal-column values and actions. Different cache settings can change realized work counts, especially when shared-table access depends on scheduling. A fixed work limit can therefore stop configurations at different depths. Claims about unchanged bounded-work fallback require the same cache configuration.
Every shared-table entry belongs to one decision with one parameter set and one deterministic leaf definition. A hit compares the full packed board, next disc, moves until rise, and remaining depth. Terminal states return before lookup. The hash only selects a slot. All workers must produce the same leaf value for the same public state; each worker's scratch memory is private. A shared table does not persist across games or evaluator changes.
The exact comparison covers the canonical search view and its established column order. The inherited floating-point leaf accumulation and tied-action rules are preserved; this check does not establish bitwise reflection equivalence of independently evaluated raw, mirrored boards.
The original packed-engine result records these separate parity panels:
| Historical check | Scope recorded | Mismatches recorded |
|---|---|---|
| C++ and TypeScript trajectories | 512 center-policy games, 256 search-policy games, 256 TypeScript-driver games; 36,427 moves and 40,286 waves | 0 |
| Fair leaf | 150,854 states | 0 |
| Search values and actions | 105 depth-4/seven-stratum roots and 10 depth-5/seven-stratum roots | 0 |
| Cache independence | 105 depth-4/seven-stratum roots with the direct-mapped table | 0 |
These are recorded outcomes from
RS-20260824T075451Z-e89ea128.
The frontier record separately retains exact root/frontier values, actions,
and completed-task counts on its diagnostic roots. It also reports missing
historical artifacts that prevented the aggregate validation target from
finishing in that checkout, and native floating-point contraction that
required -ffp-contract=off for exact parity on arm64. Those limitations are
preserved in
RS-20260825T052959Z-1b3ed9a5.
The engine has no GPU, latent-mode, or native-scenario variant. Scripted-round and scenario work continues to use the existing engines. A simpler leaf, selective search, or ordinary chess alpha-beta pruning would change the algorithm and needs a separate protocol; Drop7 chance outcomes are averaged.
Agent contextSource files
The source browser below lists this std-only crate. The current control flow
is in src/board.rs, src/leaf.rs, src/search.rs, src/shared_table.rs,
and src/parallel.rs under approaches/fair-expectimax/rust-engine/.
The analyzer accepts the following cache controls:
| Option | Meaning |
|---|---|
--tt-from-depth N | Cache only nodes with at least N remaining decision plies; this does not change allocated capacity. |
--cache N | Entries per worker with private scope; total entries with shared scope. Capacity rounds up to a power of two. |
--tt-scope private | Each worker owns its table; the default. |
--tt-scope shared | Workers share one fresh table for this decision. |
--threads N | Worker count; compare scopes at equal total capacity as well as equal wall budget. |
--max-host-bytes N | Reject a table and frontier plan above the configured memory allowance before allocating it. |
For sequential cache experiments, bench search --tt gate2 --tt-capacity N
sets the depth threshold and entry count; --tt none disables caching.
analyze emits cache scope, threshold, entry count, and projected memory in its
plan and decision output.
./build.sh selects target-cpu=native. The trajectory, leaf, and search gates
compare against the emitters under cpp/ and the TypeScript driver under
ts/. bench_all.sh is the original timing harness; the optimization-check
harness follows the current registered comparison.
The bounded matrix workflow is documented in cluster/README.md. Its
allocation-free plan is available before any upload or launch. A live cloud
run needs its own registered experiment, resource lease, and operator budget.
Agent contextScoring mode
The engine preserves corrected five-move Hardcore scoring, including the
17,000-point rise award. Historical mechanics checks opened no new leased
seeds: the original packed-engine experiment used the already-opened
SEEDLEASE-A52-FAST diagnostic sub-blocks and probe block documented in its
protocol. The current optimization check also opens no new gameplay cohort.
Timing improvements do not establish an improvement in mean game score.
RecordsTheories, experiments and results that reference this directory
Claim: A 28-byte column-major board (7 x u32, 4 bits per cell) makes gravity, disc placement and legality constant-time bit operations (PEXT compaction on Zen 5, portable nibble loop elsewhere); combined with a packed-key open-addressed transposition table and per-thread searchers, the fair expectimax search runs bit-identical to the frozen C++ reference at higher throughput and lower memory, scaling near-linearly to all available cores.
This theory is currently untested at the proposal (no games played) level.
Claim: For completed fair expectimax at depth 5 and beyond, expanding a deterministic prefix frontier and registering its independent continuation subtrees in one shared work registry exposes enough fine-grained work to use more than the seven root-column workers while preserving every root-column f64 value bit and the selected action.
This theory is currently supported-as-tested at the mechanics-only (checks only, no games played) level.
Claim: A leaf-affordable NNUE evaluator (the 8,902-feature, 135-active sparse class of approaches/lifetime-objective/learned-leaf), whose weights are (a) initialised by distilling the sibling-complete root values of a depth-5 seven-stratum fair-expectimax teacher and (b) then refined by a mutation-only genetic algorithm whose fitness is the mean score of complete paired depth-3 seven-stratum games, deployed as the leaf of that same depth-3 seven-stratum search, achieves a higher mean whole-game score on never-read paired development games than the identical search using the frozen fair leaf.
This theory is currently not-supported-as-tested at the public-development (a cohort for deciding what to try next, not confirmation) level.
Claim: One immutable Rust search-matrix configuration can evaluate the same ordered public roots across multiple completed depths, chance-strata counts, and named leaf-weight files on a workstation or a large x86 EC2 instance, retaining per-column values, selected actions, work, scheduling, memory, and machine analytics sufficient for exact cross-depth comparison.
This theory is currently supported-as-tested at the mechanics-only (checks only, no games played) level.
Claim: The six-feature linear Q-learner of Erez Klein and Ben Friedmann (Stanford CS221 final report 'Drop7', code github.com/ekreate/cs221-final-project at commit 8cc8a0e) is a survival heuristic whose quality is fixed by its features, not by its training length or its regularisation: (i) its published figures reproduce from its own code (uniform-random about 31 moves, the trained agent about 49-50 moves on 10,000 test games with no exploration); (ii) because the step size is 1/t over MOVES, the weights are effectively frozen after a few hundred games, so a 50,000-game run and a 300-game run yield policies within 2 moves of each other and the ridge term has no measurable effect on the shipped configuration; (iii) transplanted onto corrected five-move Hardcore rules in the repository engine with a legal-column argmax and re-learned with survival reward, the same six features outlive uniform-random play by a paired-significant margin but reach a mean corrected score below two thirds of the fair depth-4 ledger mean (below 205,530 against 308,296) and lose to fair d4s7 on paired seeds; and (iv) a slower step schedule (per-game, harmonic, or constant) does not raise the transplanted policy's mean lifetime over the upstream schedule by more than the pilot cohort's paired detection floor.
This theory is currently mixed at the pilot (a small run to find bugs and project cost, not a strength claim) level.
Claim: On states visited by fair d4s7 play under corrected five-move Hardcore rules, a linear action value over cheap one-ply features (the seven-stratum mean of the eighteen fair-leaf terms of the afterstate, the mean score delta, the terminal fraction, the six Klein-Friedmann drop features, a rise-clock one-hot and a bias), fitted by ridge regression to exact fair-d4s7 sibling values on training-game roots, (a) reproduces d4's top choice on held-out roots more often than the exact one-ply search value does, but only by a small margin (top-1 stays below 0.65), because a linear one-ply evaluator cannot represent the four-ply value function; (b) nevertheless keeps d4's best action inside its top three on at least 95% of held-out roots; so that (c) a fair search that expands only the prior's top three siblings at every interior max node with two or more plies remaining reproduces the full-width d4s7 action on at least 95% of held-out roots with mean normalised regret at most 0.02, at no more than 35% of the full-width logical work; and (d) the same pruning at depth five, at no more than twice the full-width d4s7 work per root, chooses actions whose mean regret under exact d5s7 values is lower than full-width d4s7's.
This theory is currently mixed at the pilot (a small run to find bugs and project cost, not a strength claim) level.
Claim: An n-tuple value function over the public board, made of absolute-position seven-cell row tuples, seven-cell column tuples (rise-phase conditioned) and two-by-three and three-by-two window tuples (about 4.8 x 10^8 table entries), trained on-policy by TD(0) with temporal-coherence step sizes from at least 10^9 engine moves of one-ply chance-state play on the Rust bitboard engine, and deployed as the leaf of the stock depth-3 seven-stratum fair expectimax, achieves a higher mean whole-game score than the identical search with the frozen fair leaf on never-read paired development games.
This theory is currently supported-as-tested at the public-development (a cohort for deciding what to try next, not confirmation) level.
Claim: Avoiding unused leaf work and cache hashing reduces fixed-depth CPU cost without changing any value bits; cache scope and depth expose a measurable memory/work frontier.
This theory is currently mixed at the mechanics-only (checks only, no games played) level.
It compares rust-engine (drop7-rs cargo crate, std-only) against C++ fast engine + frozen native reference + TypeScript engine at the CHECK (mechanics checks only, no games played) level, using no-gameplay data.
valid run outcome: pass The run was valid and the outcome was pass (mechanics-only (checks only, no games played)). Read the result.
It compares rust-central-frontier-scheduler against rust-root-column-scheduler at the CHECK (mechanics checks only, no games played) level, using no-gameplay data.
valid run outcome: pass The run was valid and the outcome was pass (mechanics-only (checks only, no games played)). Read the result.
It compares d3s7-evolved-nnue-leaf against fair-d3s7 at the SCREEN (a 32-game paired screen) level, using public-development data.
No result has been recorded for it.
It compares rust-search-matrix against manual local Rust invocations and current one-shot decide binary at the CHECK (mechanics checks only, no games played) level, using no-gameplay data.
valid run outcome: pass The run was valid and the outcome was pass (mechanics-only (checks only, no games played)). Read the result.
It compares drop7-kf-linear-q (Rust crate on drop7-rs): six-feature linear Q policy, several trained weight sets against uniform-random legal policy and centre-first policy on identical seeds (primary); fair d4s7 (drop7-rs Searcher, FairLeaf, 65,536-entry depth table, terminal utility -1,000,000, policy seed 0xd7075eed) on the first 32 seeds as a diagnostic reference at the PILOT (a small run to find bugs and project cost, not a strength claim) level, using previously-evaluated-development data.
valid run outcome: pass The run was valid and the outcome was pass (pilot (a small run to find bugs and project cost, not a strength claim)). Read the result.
It compares d3s7-evolved-nnue-leaf against fair-d3s7 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 pruned fair expectimax (drop7-oneply-q crate: prune.rs) with priors centre, kf-cem, d1, lq against full-width fair d4s7 (drop7-rs Searcher, FairLeaf, 65,536-entry depth table from depth 1, terminal -1,000,000, policy seed 0xd7075eed) and exact full-width d5s7 on the sub-panel at the PILOT (a small run to find bugs and project cost, not a strength claim) level, using previously-evaluated-development data.
valid run outcome: fail The run was valid and the outcome was fail (pilot (a small run to find bugs and project cost, not a strength claim)). Read the result.
It compares d3s7-evolved-nnue-leaf-continued against fair-d3s7 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 d3s7-evolved-nnue-leaf-continued2 against fair-d3s7 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 d3s7-ntuple-scale-leaf against fair-d3s7 at the SCREEN (a 32-game paired screen) level, using public-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.
It compares rust-bitboard-improvements against Rust dev at 7adb6b36412b4bbef25623183dac1a905570cd1a at the CHECK (mechanics checks only, no games played) level, using no-gameplay data.
valid run outcome: pass The run was valid and the outcome was pass (mechanics-only (checks only, no games played)). Read the result.
It compares d3s7-ntuple-scale-wide-leaf against fair-d3s7 at the SCREEN (a 32-game paired screen) level, using public-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.
The run was valid; the outcome was pass, at the mechanics-only (checks only, no games played) level. Of 6 preregistered checks, 6 passed and 0 failed.
The Rust bitboard engine is trace-equivalent to the frozen C++ reference, the proven C++ fast engine, and the TypeScript engine on every observable, and is the fastest of the three. Board representation is seven u32 column words at 4 bits per cell (28 bytes): gravity is a single PEXT bit-gather per column, a row rise is (word << 4) | SOLID, and cover hits are counted board-wide with a 4-way bitboard parallel counter. All parity gates pass with zero mismatches: 3 trajectory arms (512 center + 256 search-policy games vs C++ playHeadlessMove; 256 games vs the TypeScript seededRandom driver) totalling 36,427 moves and 40,286 waves; 150,854 leaf states bit-identical as uint64 patterns; 105 d4s7 and 10 d5s7 roots with bit-identical per-column values and identical actions; the values gate re-run with the transposition table enabled proves cache-independence. Measured on the shared AMD Ryzen AI MAX+ 395 workstation (best-of-N, load 1.1-1.7): single-core engine throughput 12.8M moves/s vs C++ fast 6.5M (1.97x) and TypeScript 0.65M (19.8x); leaf 155.6 ns vs C++ fast 187.5 ns (1.20x); fair search at d4s7 908 ms/decision vs C++ fast 1,071 ms (1.18x) with a 3.1 MB direct-mapped table vs the C++ 16.2 MB LRU; d5s7 7,047 ms at 1M entries vs 7,817 ms (1.11x). Game-level scaling is shared-nothing and near-linear (10.3x on 16 physical cores on the shared machine; 14.1x in a clean run), with identical results at every worker count. A key recorded finding: the transposition table's 1.3% node hit rate is misleading — each hit prunes a whole subtree, so the table eliminates ~47% of work at d4s7 and ~90% at d5s7, and a cheap direct-mapped depth-preferred table captures nearly all of the strict-LRU table's payoff at a fifth of the memory. No strength claim; no new seeds opened.
Technical recordLimitations recorded with the result
- CHECK-tier engineering result: proves equivalence and measures speed/memory, but makes no policy-strength claim and advances no benchmark tier.
- Timing measured on a shared workstation (load average 1.1-1.7); ratios between back-to-back arms are the trustworthy quantity, absolute nanoseconds are not.
- The d5s7 arms ran 3 decisions each (1 repeat) because a single d5s7 decision costs 7-63 s; the d4s7 arms ran 21 decisions, best of 3.
- The direct-mapped table's hit rate at d5s7 (748k-825k hits/decision) trails the C++ strict-LRU table (911k); a set-associative table is the recorded reopening direction.
- No GPU, latent-mode, or native-scenario variant: scripted-round and scenario duties stay with the existing engines.
The run was valid; the outcome was pass, at the mechanics-only (checks only, no games played) level. Of 5 preregistered checks, 4 passed and 0 failed.
A deterministic central-frontier scheduler replaced at-most-seven root-column jobs with a bounded shared registry of continuation subtrees while retaining ordered floating-point reduction and the old root scheduler as a fallback. On one retained 12-logical-CPU arm64 profile, exact root/frontier comparisons had zero value, action, or task mismatches. The preregistered D5/S7 bounded split reduced the three-repeat median for three roots from 22.034955 s to 19.410905 s (1.1352x) with 0.8823 median worker busy fraction; D4/S7 over eight roots improved from 4.076978 s to 3.232507 s (1.2612x) with 0.9951 busy fraction. A deliberately over-expanded D5 prefix reached 0.9963 busy fraction but created about 41% more logical work and achieved only 1.0102x, which falsified that split and motivated the adaptive coarse split for expensive continuations. This supports the refactor strategy locally but is not a 192-core saturation or policy-strength result.
Technical recordLimitations recorded with the result
- Mechanics-only CHECK result on one shared 12-logical-CPU arm64 workstation; no x86 or 192-core timing was performed.
- The timing corpus is eight reusable diagnostic roots at D4 and three at D5, not complete games or a strength cohort.
- Private worker tables make logical work schedule-dependent. Fine granularity is not monotonically better: the over-expanded D5 arm increased logical work about 41% and failed the speed gate.
- Worker busy fraction measures timed search-task occupancy and excludes coordinator planning, initialization, and reduction; end-to-end wall time is the primary speed metric.
- Peak RSS and CPU seconds were not retained. Memory values are exact table-layout projections plus a conservative planner allowance.
- The default arm64 C++ compiler contracted floating operations; bit-exact native parity on this host required -ffp-contract=off. Actions did not change in the default 20-root diagnostic.
The run was valid; the outcome was pass, at the mechanics-only (checks only, no games played) level. Of 6 preregistered checks, 5 passed and 0 failed.
The reusable Rust analyzer emitted all 16 requested root x leaf x strata x depth decision rows, retaining every legal sibling's decimal and exact-f64 value, action changes across depths, task/work/cache counts, phase timings, memory projections, and per-worker load. Frozen named weights matched the compiled fair leaf exactly; frozen and perturbed leaves were worker-count independent. The D7/S7 allocation-free 192-worker plan selected one additional internal split ply, bounded the worst frontier at 2,401 tasks, and projected 2,418,377,728 bytes under an 8 GiB cap; the former 16,777,216-entry-per-worker cache was rejected at 154,618,822,656 bytes before allocation. Shell syntax and a fail-closed fake-AWS plan passed with only read-only describe, quota, and SSM operations accepted. No live AWS call, instance, Capacity Reservation, or 192-core execution occurred.
Technical recordLimitations recorded with the result
- No live AWS API was called and no instance was launched. The fake plan proves command routing and fail-closed plan behavior, not IAM, quota, Capacity Reservation availability, package availability, S3 access, or cloud-init behavior in a real account.
- The 192-worker result is an allocation-free memory/frontier plan, not an x86 throughput or CPU-saturation measurement.
- The smoke roots and perturbed weights are mechanics-only examples, not training data, teacher labels, or evidence that one leaf is stronger.
- The hourly price is deliberately a caller-asserted fail-closed input rather than a live price quote; the operator must refresh it immediately before a future authorized launch.
- An actual EC2 run requires a new registered run, retained machine profile, exact source/config/root/weight hashes, exclusive resource lease, and uploaded completion artifacts.
The run was valid; the outcome was pass, at the pilot (a small run to find bugs and project cost, not a strength claim) level. Of 6 preregistered checks, 6 passed and 0 failed.
On corrected five-move Hardcore rules the six Klein-Friedmann features are a survival heuristic worth about ten moves and 34,000 points over uniform-random play, an order of magnitude below fair depth-4 in points. Pilot on 256 previously evaluated development seeds (0xa5277000-0xa52770ff, 2,000-move cap, no game censored, 0 illegal decisions): uniform-random legal play 26.25 moves / 73263 points; centre-first 21.01 / 55083; the six features re-learned with the upstream schedule (three exploration seeds) 35.79, 35.81, 36.27 moves and 107147, 107231, 109068 points, a paired gain over random of +9.54 moves [LB95 +8.85] and +33884 points [LB95 +31361] for seed a. The authors' own Python-learned weights (reproduction seed 10) transplanted unchanged live 40.53 moves / 123968 points, +4.74 moves over the engine-learned weights [LB95 +3.77]: same features, same update, same schedule, different opening experience (empty board vs gray row) frozen in by the per-move 1/t step size. Fair d4s7 (drop7-rs, 65,536-entry table) on the first 32 seeds: 380205 points / 110.38 moves, beating the transplanted arm by +256507 points [LB95 +182668] on 31/32 paired games. Slower schedules: per-game 1/g diverged to weights near 1e9 (32.47 moves), harmonic tau 50,000 and the score-reward harmonic arm diverged to NaN (20.90 moves, the rightmost-column policy), constant 0.001 gained +0.50 moves over the upstream schedule [LB95 +0.12], the same size as the +0.48 [+0.07] spread between two identical-configuration seeds. Gates G1-G5 pass; G6 records that a slower stable schedule clears LB95 > 0 by under a move, so theory clause (iv) is not supported at the letter while clauses (iii-a) and (iii-b) are. All CHECK gates (feature parity 54,852 pairs / 0 mismatches; update parity 250 steps / exact; determinism and legality) passed before any diagnostic seed was read. Training ran at about 1.4 million moves per second (1.3 s per 50,000-game arm).
Technical recordLimitations recorded with the result
- Pilot tier on previously evaluated development seeds: the cohort had been read by the centre policy before and can never become confirmation evidence; nothing here advances a benchmark tier.
- Training re-read the first 50,000 seeds of an already-open training-role block without a new lease record; the coordinator has not yet confirmed that reuse. Any citation outside the pilot tier waits on that decision.
- The fair d4s7 arm covers 32 of the 256 seeds (237.6 s per game on the laptop); its 32-game deltas have detection floors near 82,000 points and 22 moves.
- Three arms (constant 0.01, constant 0.0001, score reward at constant 0.001) were added post hoc after the preregistered arms were trained; they are labelled post hoc everywhere and decide no gate.
- The G6 outcome is recorded as observed: the constant-0.001 arm's +0.50 moves clears its LB95 but equals the identical-configuration seed spread; the theory's clause (iv) is therefore judged not supported at the letter and the theory assessment is 'mixed'.
- Wall times were measured while other arms and the concurrent reproduction run shared the 12-core laptop; the 1.4 million moves per second figure is single-process throughput and not a clean benchmark.
- The rules engine is the repository's; board-level parity with the upstream simulator is not claimed, only feature and update parity.
The run was valid; the outcome was fail, at the pilot (a small run to find bugs and project cost, not a strength claim) level. Of 5 preregistered checks, 1 passed and 2 failed.
A sibling-complete panel of 5,398 roots from 48 fair-d4s7 games on already-read training seeds (mean 390,143 points, 112.5 moves, no game censored) was split 32 games for fitting and 16 held out (2,060 roots). The linear action value over 32 one-ply features, fitted by ridge regression to the exact depth-4 sibling values (lambda 0.001 at the grid edge for both the raw and the per-root-centred fit; held-out R^2 0.725 raw, 0.539 within root), is a WORSE ranker of depth-4's choice than the exact one-ply value it can represent: top-1 0.308 against 0.524 (paired difference -0.216, one-sided 95% bound -0.233 over games) and recall@3 0.604 against 0.809, so clauses (a) and (b) fail. A post-hoc check shows why: the features rebuild the exact one-ply value to 6e-11, and least squares halves the held-out error (7.0e9 against 1.4e10 for an affine map of the one-ply value) by predicting deaths within four plies, which cost a million points each, while ranking live siblings worse; refitting on per-root-centred targets clipped at -50,000 recovers exactly the one-ply value's ranking (top-1 0.535, recall@3 0.809) and nothing more. Depth-three exact values rank depth-4's choice into the top three on 95.0% of roots, depth-two on 90.0%, the six-feature CEM policy on 70.1%, centre order on 40.0%. Pruning the depth-4 search at interior max nodes with the exact one-ply value as prior at width three (both interior layers) reproduces the full-width decision on 92.3% of held-out roots (lower bound 91.8%) with mean normalised regret 0.0087 (upper bound 0.0098) and mean raw regret 74 points at 26.4% of the full-width logical work (prior calls included, 5.9% of it) and 3.8x faster wall time; width four gives 93.8% / 0.0059 at 40.9% work, width two 89.3% / 0.0145 at 13.8%. The six-feature prior, which costs no engine calls, reaches 88.4% / 0.0147 at width four (34.6% work); the fitted Q prunes worst (78.8% / 0.037 at width three); centre order is useless (69.1%). Every pruned root value was at or below the full-width value on every root (0 violations of the lower-bound property), and the width-seven searcher reproduced drop7-rs values, actions and work bit for bit. Clause (c) is inconclusive by the preregistered rule: the best configuration at or under 35% work passes the regret threshold (0.0087 <= 0.02) but its agreement of 92.3% sits between the 0.90 fail line and the 0.95 pass line. On the 128-root sub-panel with exact depth-5 values (mean 1.9e8 work per root, 37x depth 4), full-width depth 4 agrees with depth 5 on 79.7% of roots with normalised regret 0.0285 and raw regret 146 points per decision; pruned depth 5 with the one-ply prior at width two on all three interior layers (1.68x the depth-4 work) agrees on 80.5% with normalised regret 0.0174 (paired reduction +0.011, bound -0.0004) but HIGHER raw regret, 189 points (paired change -43, bound -207), so clause (d) is inconclusive at best and unfavourable in points: pruning three interior layers loses the high-stakes continuations that the extra ply was supposed to find. Wider depth-5 pruning (3,3,3 at 4.5x work) reaches only 82.8%. The theory's learned-prior claim is not supported; its mechanism claim is supported for the search's own one-ply value at depth 4 and not at depth 5. Pilot tier, training-role and held-out panel data, no new seeds.
Technical recordLimitations recorded with the result
- Pilot tier on training-role seeds: 48 games, 16 held-out games (2,060 roots) for the depth-4 metrics and 128 roots from those games for depth 5; game-clustered bounds are reported but the sub-panel's paired depth-5 differences straddle zero.
- Root-level regret against exact search values is a decision-quality proxy, not a strength measure; the program has seen short-horizon proxies invert three times, and the preregistered gameplay pilot EX-20260902-pruned-search-gameplay-pilot-bf465b1d is the strength test.
- The ridge lambda selected by cross-validation (0.001) is the smallest value of the preregistered grid; a smaller lambda was not tried. The post-hoc clipped refit is a diagnostic read after the held-out metrics and carries no evidential weight.
- The fitted Q used inside the pruned search is the per-root-centred fit, as preregistered; its poor ranking makes the lq pruning rows a test of a bad prior, not of the pruning mechanism.
- Wall budget: the panel (1,434 s) plus the stages (5,958 s, run while two pilot gameplay arms shared the 12 cores) took 7,392 s, 2.7% over the preregistered 7,200 s stop, which was not enforced by a watchdog; a first panel attempt of about 1,380 s was killed by the session harness at 47/48 games before writing anything. All stage outputs are deterministic functions of the inputs, so the overrun changes no number; wall-time figures are contended and only logical work should be compared.
- The comparator values are the frozen-leaf search's own values; agreement with exact depth 5 measures decision reproduction under the frozen leaf, not correctness.
The run was valid; the outcome was fail, at the public-development (a cohort for deciding what to try next, not confirmation) level. Of 9 preregistered checks, 4 passed and 4 failed.
Valid run of the frozen protocol EX-20260902-nnue-evolution-d3-v2-49c18bc2 on the whole workstation (32 threads), every stage completed, every artifact with zero illegal and zero incomplete decisions. Stage A: the depth-5 seven-stratum teacher played 177 complete games (21,618 sibling-complete labelled roots, 3 stopped at the 500-move cap) before the 46,800 s new-game cutoff; the teacher ran 77.5 s per root, four to seven times slower than the pilot projected, so the corpus is about a third of the 512 games the protocol allowed for. Stage B: the supervised warm start reached validation Huber 0.6873 rise units at epoch 8 of 16 on a whole-origin split (17,641/3,977 roots), and the deployment-faithful ordering probe put the depth-3 search with that leaf at top-1 agreement 0.4414 with the teacher on 256 held-out roots (mean teacher-value regret 2,395 points). Stage C: 60 generations of the mutation-only GA (population 32, 32 paired games per candidate per fresh block, fair-d3s7 and warm-start controls on every block). The population separated steadily from its warm start - ten-generation paired mean margins over the init control of +4,682, +15,853, +22,704, +32,278, +34,037, +40,431 points, best candidate +76,679 in the last block - but never approached the fair control: the population mean was above the fair leaf in 0 of the final 10 generations and the best candidate in 0 (mean margin -121,896), so the theory's training-signal falsifier fails. Elite re-selection on 128 fresh games froze candidate-29 at 202,237 (finalists spanned 187,352 to 202,237). Stage D: on the 64 never-read held-out games (0xa52e1300, opened once at 2026-09-03T02:41:29Z after the candidate's SHA-256 was recorded), the evolved candidate averaged 190,961 against the frozen fair leaf's 297,926 at the identical depth-3 configuration: paired delta -106,964 (bootstrap 95% bounds -146,580 to -69,983, Student-t lower bound -145,890, detection floor 38,357), W-T-L 14-0-50, both halves negative (-109,139 / -104,790), lower quartile 122,253 against 158,387. Every preregistered pass criterion except artifact integrity and candidate identity fails: scientific outcome fail for this exact configuration. The ablation arm shows what evolution did contribute: the unevolved warm start averaged 155,586 (-142,340 against the fair leaf), and the evolved candidate beat it on the same seeds by +35,375 (bootstrap lower bound +16,899, floor 18,949, W-T-L 36-0-28, both halves positive). Whole-game evolution with common random numbers therefore moves a 572k-weight leaf on the deployed objective, which the first (CMA-ES) leaf evolution could not show; it moved it about a quarter of the way from a warm start that plays at half the fair leaf's level. The reference arm reproduced the program's standing result: fair depth 4 over fair depth 3 +97,059 (lower bound +29,330). Read: the claim is not supported as tested; the mechanism's evolutionary leg is supported, its distillation leg is the weak link (a warm start that holds the teacher's values but not its ordering), and the budget (177 teacher games, 60 generations) was too small for evolution to cover the distance.
Technical recordLimitations recorded with the result
- Single 64-game held-out screen: the paired detection floor is about 38,000 points for the primary contrast and 19,000 for the ablation; the primary result is far outside its floor, the ablation clears its own.
- The teacher corpus reached 177 of the 512 games the protocol allowed for because the depth-5 teacher ran four to seven times slower than the pilot projected; the protocol makes the completed games the corpus, so the result is valid, but it rejects this configuration at this corpus size, not the design at 512 games.
- The supervised warm start plays at about half the fair leaf's level; the deployment-faithful probe (0.441 top-1) and the leaf-swing diagnostic (zero leaf 0.450 on a different sample) suggest imitation of state values barely improved the search's ordering over no leaf at all. Evolution then had roughly 150,000 paired points to make up in 60 generations and made up about 35,000 to 40,000.
- The per-game artifact holds four arms x 64 games (256 rows); the primary contrast pairs the candidate and fair-d3s7 rows by seed.
- The fair-d4s7 arm is diagnostic only and reproduces the standing depth-4-over-depth-3 result on these seeds; it is not part of the gate.
- Two operational faults during the unattended chain (a false liveness reading, then a script replacement that crashed the corpus stage's shell after its binary had exited 0) are recorded in the run record; the 'corpus: done' marker in pipeline.log was appended by hand with a note. No artifact was affected.
- The elite re-selection block (0xa52e0c80, 128 games) and every fitness block are training-lease seeds; the last generation's 32-game leaders read 20,000 to 60,000 above their 128-game re-selection means, which is the best-of-32 bias the re-selection exists to remove.
The run was valid; the outcome was fail, at the public-development (a cohort for deciding what to try next, not confirmation) level. Of 9 preregistered checks, 4 passed and 4 failed.
Valid run of the frozen protocol EX-20260903-nnue-evolution-continuation-d3-f8ce9181 (successor to RS-20260903T025751Z-6577b33e), every stage completed, every artifact with zero illegal and zero incomplete decisions. Stage C resumed from the first run's checkpointed generation-60 population (SHA-256 3da5021531898f08254d35cad5486fbbc12092770216bcdaea988d3724422cb8) with the first run's frozen candidate (SHA-256 edd0d2efd181de43f35d62c4df784cb2c789db3ae296be93a1c6c59082034a9f) as a third paired control and an annealed mutation size (sigma_rel from 0.05, time constant 400 generations, floor 0.01; it reached 0.0345 at the last generation). The preregistered plateau rule stopped the run after generation 149: the first check after generation 99 found the paired margin over the fair control rising at +238.6 points per generation (standard error 127.1, one-sided 95% lower bound +29.6) and continued; the second, after generation 149, found +37.3 per generation (standard error 119.3, lower bound -158.9), no detectable improvement over generations 50-149, and stopped. Fifty-generation averages of the population mean: 204,748, 228,706, 237,946; paired margin over the first run's candidate +8,982, +36,076, +45,613; over the fair control -116,503, -99,833, -88,948. The best candidate beat the fair control on 9 of 150 blocks (generations 59, 66, 73, 97, 100, 109, 115, 125, 137); the population mean beat it on none, so the theory's training-signal falsifier fails again (mean above fair in 0 of the last 10, margin -98,026). Elite re-selection on 128 fresh games (0xa52e32c0) froze candidate-12 at 264,466 (finalists 228,212 to 264,466). Stage D, 64 never-read games (0xa52ea000, opened once at 2026-09-03T16:21:00Z after the candidate's SHA-256 was recorded), five arms: the continued candidate averaged 251,667 against the frozen fair leaf's 320,108 at the identical depth-3 configuration, paired -68,441 (bootstrap 95% bounds -112,090 to -26,694, Student-t lower bound -112,274, floor 43,193), W-T-L 25-0-39, halves -112,383 / -24,500, lower quartile 171,440 against 189,414: every screen criterion fails, scientific outcome fail for this configuration. The preregistered secondary contrast answers the continuation's own question: the continued candidate beat the first run's frozen candidate on the same seeds by +36,278 (bootstrap lower bound +9,085, Student-t lower bound +8,543, floor 27,330, W-T-L 41-0-23, halves -7,140 / +79,697), so 150 further generations improved the leaf out of sample, by about a third of the remaining distance. The first run's candidate reproduced its earlier result on fresh seeds: -104,719 against the fair leaf here (lower bound -143,479) against -106,964 on the first screen. Over the warm start the continued candidate is +107,994 (lower bound +82,769, W-T-L 54-0-10). The reference arm gave fair depth 4 over fair depth 3 +71,799 (lower bound +25,857). Read: whole-game evolution keeps improving the leaf until roughly generation 100 of the continuation and then levels off about 70,000 paired points short of the frozen fair leaf on held-out games; the claim is not supported at this budget, and the plateau is the new fact. Whether the levelling is a property of the leaf class or of the annealed step size cannot be separated in this design.
Technical recordLimitations recorded with the result
- Single 64-game held-out screen: paired detection floors of about 43,000 points for the primary contrast and 27,000 for the continuation contrast; the primary result sits far outside its floor, the continuation contrast just outside its own, and its first-half estimate is negative, so 'improved out of sample' is established at the whole-block level only.
- The plateau rule and the annealed mutation size are confounded: the step size had fallen from 0.050 to 0.034 when the rule fired, so the design cannot say whether the leaf class ran out of improvement or the search did. A constant-sigma continuation from the same population would separate them.
- The plateau rule tests a fitted slope over 100 generations with a one-sided 95% lower bound; at the observed scatter it can miss real improvements below about 220 points per generation. The observed point estimate over generations 50-149 was +37 per generation.
- The per-game artifact holds five arms x 64 games (320 rows); each contrast pairs two arms by seed.
- The starting population, the warm-start control and the baseline control are products of the first run's training data; nothing in this run re-read that lease.
- The owner's commit e2b0d19 (wall-budget pinning and progress recovery on resume) landed on the branch mid-run; the run never resumed, the driver shells held the original script by an unlinked inode, and every stage ran the binaries built from commit e4fd018 as the run record states.
- The fair-d4s7 arm is diagnostic only. The first run's candidate reproduced its earlier screen result on these fresh seeds (-104,719 against -106,964), which is the closest thing to a replication the program has for that number.
The run was valid; the outcome was fail, at the public-development (a cohort for deciding what to try next, not confirmation) level. Of 8 preregistered checks, 3 passed and 4 failed.
Valid run of the frozen protocol EX-20260903-nnue-evolution-continuation2-d3-80eebad3 (second successor to the theory, third experiment in the series), every stage completed, every artifact with zero illegal and zero incomplete decisions. Stage C resumed from the second experiment's checkpointed generation-150 population (SHA-256 33dcc25b88ab56a0a80329bf11983b539883aa922900b03de3cea71e1da3529d) with that experiment's frozen candidate (SHA-256 759084fab97599818d01a5da6d18b9cc77172249f09a68d5104361bca385e09b) as the third paired control, testing whether a 3.75x slower mutation-size decay (time constant 1,500 generations vs. 400 before; sigma stayed at 0.0500 at generation 0, 0.0453 at the stop, versus 0.0345 at the equivalent generation last time) would let evolution get further past the point where the prior run plateaued. The identical preregistered plateau rule stopped this run at the identical generation as before, 149: the check after generation 99 found the paired margin over the fair control rising at +201.7 points per generation (lower bound +11.7, versus +239 and +30 in the second experiment) and continued; the check after generation 149 found +164.0 per generation (lower bound -28.5, versus +37 and -159 before) and stopped. The improvement rate at the stopping point was more than four times the prior run's (164 against 37 points/generation), and the best candidate beat the fair control on 26 of 150 blocks against 9 in the second experiment, but the lower bound of the fitted slope still crossed zero at the same 100-generation checkpoint, so the rule fired regardless. Fifty-generation population-mean averages: 235,353, 238,797, 244,800 (rising monotonically, unlike the second experiment's flat third block); paired margin over the immediately-prior candidate +4,301, -949, +13,589; over the fair control -99,479, -88,006, -77,654. Training-signal falsifier fails again (population mean above fair in 0 of the last 10 generations, margin -70,199; best above fair in 4 of the last 10). Elite re-selection on 128 fresh games (0xa52eb3c0) froze candidate-28 at 257,314 (finalists 232,499 to 257,314, a tighter spread than the second experiment's). Stage D, 64 never-read games (0xa52f2100, opened once at 2026-09-04T08:56:37Z after the candidate's SHA-256 was recorded), five arms: this candidate averaged 249,757 against the frozen fair leaf's 335,266, paired -85,509 (bootstrap 95% bounds -128,482 to -43,591, Student-t lower bound -129,423, floor 43,272), W-T-L 22-0-42, both halves negative (-59,644 / -111,374), lower quartile 156,534 against 176,108: every screen criterion fails, scientific outcome fail for this configuration, consistent with both prior screens. The preregistered secondary contrast is the key negative finding of this run: the candidate beat the immediately-prior (second experiment's) frozen candidate by only +13,572 on the same seeds, with a bootstrap 95% lower bound of -30,165 and a Student-t lower bound of -30,869 -- both crossing zero (detection floor 43,791), W-T-L 31-0-33. Unlike the second experiment's clearly positive +36,278 (lower bound +9,085) over the first experiment's candidate, this third experiment's gain over the second is NOT statistically distinguishable from zero at this screen size: three months of relatively larger mutations bought a point estimate about a third the size of the previous continuation's out-of-sample gain, and it is not confidently positive. The second experiment's candidate itself scored -99,081 against the fair leaf on this fresh block (lower bound -154,094), broadly consistent with its own screen result of -68,441 (lower bound -112,090), a second informal replication. Over the warm start the candidate is +106,846 (lower bound +76,970). The reference arm gave fair depth 4 minus fair depth 3 of -1,409 (bounds -62,290 to +58,933, crossing zero on this cohort, a diagnostic-only reading and not comparable across screens with different fresh seeds). Read together with the second experiment: slowing the mutation-size decay produced a visibly healthier training curve (a still-rising population mean through all three 50-generation blocks, more generations where the best candidate beat the fair control) but did NOT produce a statistically confirmed improvement over the immediately-prior candidate on held-out games, and the plateau rule still stopped the run at the same generation. The most defensible reading is that the annealing schedule was not the dominant cause of the earlier plateau -- something else (population size, games per candidate, or the leaf class's genuine ceiling under this search depth) is the binding constraint, though a schedule 3.75x slower is not proof that no schedule would help; a much slower schedule, or removing the anneal-driven exploration decay entirely in favour of a fixed sigma with more games per candidate, remains untested.
Technical recordLimitations recorded with the result
- Single 64-game held-out screen: paired detection floors of about 43,000 points for the primary contrast and 44,000 for the baseline contrast; the baseline-run1 (secondary) contrast's point estimate of +13,572 sits well inside its own floor, so 'no confirmed improvement over the prior candidate' is the correct reading, not 'no improvement occurred' -- a true effect below about 44,000 points could not be distinguished from zero at this sample size.
- The plateau rule fired at the identical generation (149) as the second experiment despite a 3.75x slower decay constant; this is evidence against the annealing schedule being the dominant cause of the second experiment's plateau, but it is a single comparison at one alternative time constant, not a sweep, and cannot rule out that some other (e.g. much slower, or non-exponential) schedule would behave differently.
- The per-game artifact holds five arms x 64 games (320 rows); each contrast pairs two arms by seed.
- The starting population and both controls are products of the first and second experiments' training data; nothing in this run re-read either prior lease.
- pipeline.sh's screen and compare stages hardcode the third control's arm name as 'baseline-run1' regardless of which run's candidate is actually supplied via $BASELINE; in this run that arm holds the SECOND experiment's frozen candidate, not the first. The mapping is recorded accurately in evolve/config.json's baselineSha256 field (matches the second experiment's candidate hash) and in this record's metrics.priorRuns; the label itself is cosmetic and should be parameterised in a future pipeline.sh edit made only while no stage is executing.
- The fair-d4s7 arm is diagnostic only and its contrast against fair-d3s7 crossed zero on this cohort's fresh seeds (-1,409, bounds -62,290 to +58,933); this is expected cohort-to-cohort variation on a small paired sample and is not comparable across the three screens, which drew different seed blocks.
The run was valid; the outcome was pass, at the mechanics-only (checks only, no games played) level. Of 5 preregistered checks, 4 passed and 0 failed.
Avoiding mathematically unused leaf work preserved exact values and reduced completed D4/S7 search from 12.052394167 to 11.257799041 median seconds per six constructed roots (1.07058x ratio of medians; 1.06786x paired median), with 4.4384% fewer retired instructions over five repeats. The leaf microbatch improved 1.16989x with 10.6541% fewer instructions. Unpack simplification had no credible independent speed gain. Capacity/depth sweeps quantify memory versus recomputation. A bounded full-key shared table saves work at multiple workers but offers only modest latency gains and loses slightly at one worker, so private gate 1 remains default. This is a local CHECK engineering result on an interactive M3 Pro, not stronger-play evidence.
Technical recordLimitations recorded with the result
- Constructed six-root mechanics workload, five repeats per arm; no gameplay-strength cohort, score confidence interval, protected/final seed, or million-point claim.
- Interactive Apple M3 Pro/macOS workstation with Chrome, WindowServer and Codex active; no affinity, exclusive CPU lease, thermal/frequency trace or maximum-throughput claim. Later capacity repeats experienced desktop contention; every repeat and min/max is retained.
- Sequential search timers exclude table allocation; parallel decision timers include planning, initialization, execution and reduction. Process counters include the harness. CPU seconds print to 0.01 seconds; tiny unpack timings are noisy.
- Exact bits are checked under the existing canonical search order. This preserves existing floating accumulation/tie behavior, not a new guarantee for independently evaluated raw mirrored boards.
- Shared hits/work depend on scheduling. Equal completed-depth values do not imply identical stopping depths or fallback actions at a fixed work budget across cache configurations.
- Tables compare complete keys and publish completed values under synchronized nonblocking access. Shared storage is fresh per decision/parameter/leaf definition, not reusable across games or changing evaluators.
- Equal-entry worker comparisons use 65,536 total entries, split zero internal plies, depth 4/strata 7 and 1/2/4 workers. Shared projection includes 40,960 extra bytes for stripes/runtime locks; allocator metadata and thread stacks are excluded from table estimates. Peak process RSS is separately measured.
- The standalone unpack rewrite produced no credible speedup or useful instruction reduction. The leaf-only ablation accounts for most combined improvement; early hash gating has no separately isolated performance claim.
- The existing C++ reference built with default arm64 floating contraction differed on one of six leaf/root fixtures for both baseline and candidate. Both were corrected by compiling the reference with -ffp-contract=off before final gates and candidate timing; original failed checks are retained.
- make -k test passed native/TypeScript checks but aggregate research validation reports 96 pre-existing missing historical artifacts. Those records/hashes were not rewritten. Web build, typecheck, lint and 70 web tests pass. The separate chart library has 63 passes and one pre-existing stale evolution-snapshot expectation; both its test and fixture match HEAD. The full route sweep passes 727/728; /compete lacks local GitHub authentication configuration. Both Rust pages/cards, charts and diagram pass desktop visual checks; absent-data rendering passes eight checks.
- This source adds table_scope to ParallelConfig. External callers using exhaustive struct literals must add the field or use Default; default private scope and depth threshold 1 are preserved.
The run was valid; the outcome was pass, at the public-development (a cohort for deciding what to try next, not confirmation) level. Of 7 preregistered checks, 7 passed and 0 failed.
Held-out screen, 256 never-read paired public-development games (0xa52f2140+): the frozen n-tuple tables as the leaf of the depth-3 seven-stratum fair search averaged 484,577 points and 140.21 moves against 314,438 points and 92.58 moves for the identical search with the frozen fair leaf: paired +170,139 points (bootstrap 95% lower bound +130,499, Student-t lower bound +130,560, upper bound +209,881, detection floor 39,440), W-T-L 167-0-89, halves +198,575 / +141,704, lower quartile 212,820 vs 175,832, moves +47.63. The preregistered gate PASSES. Against the program's standing reference, the fair leaf at depth 4 on the same seeds (377,803 points), the candidate is +106,775 paired (bootstrap LB +62,574, UB +151,706, W-T-L 160-0-96); diagnostic only. The same tables played directly one ply averaged 281,441 points, -32,997 paired against fair-d3s7 (LB -58,536); diagnostic. Fair-d4s7 minus fair-d3s7 on these seeds: +63,365 (LB +31,538). Candidate: the tables of the main run's validation point at 1,400,240,319 training moves (layout rows,cols,win23,win32,phase=all, alpha 1.0, selected from six pilot arms by the preregistered rule with a final validation margin of +123,950), whose paired margin on the 64-game training-role validation block was +270,023; SHA-256 0ade9d4e4080ebdd52a1474b1a13410dc8dfb77f5eba24b078aa7703c92ace0b. Main run: 4,000,004,271 training moves, 45,852,198 games, 20 validation points, mean 1,956,636 moves per second. Mechanism ablation (pilot, windows-only arm E): final validation margin -83,261 against the full layout's +95,051 (arm A) and +123,950 (arm C, selected). Training-signal check: at least one validation point of the main run had a positive paired margin.
Technical recordLimitations recorded with the result
- Public-development SCREEN tier, 256 paired games opened once; nothing here is a qualification claim, and protected and final cohorts stay sealed.
- The candidate is the validation point with the largest paired margin on a 64-game training-role block that was read at every validation point and also chose the configuration; that selection is upward-biased, which is why the held-out screen exists.
- Training used lock-free asynchronous updates from 32 threads, so the training run is not bit-reproducible; the frozen tables are hashed and every gameplay arm is deterministic and worker-count independent.
- The fair-d4s7 arm is the program's standing reference for context only; the preregistered comparator is the identical depth-3 search with the frozen fair leaf.
- Table files (1.9 GB weights, 5.8 GB with accumulators for the pilot layouts; 4.0 GB and 12 GB for the selected phase=all layout) are retained on the workstation with their SHA-256 and are not committed.
- Wall times were measured on a shared workstation with the web console building concurrently for part of the run; ratios between arms on the same seeds are the trustworthy quantity.
The run was valid; the outcome was pass, at the public-development (a cohort for deciding what to try next, not confirmation) level. Of 12 preregistered checks, 12 passed and 0 failed.
Held-out screen, 512 never-read paired public-development games (0xa52f2380+): the frozen n-tuple tables as the leaf of the depth-3 seven-stratum fair search averaged 481,869 points and 139.39 moves against 326,717 points and 95.87 moves for the identical search with the frozen fair leaf: paired +155,153 points (bootstrap 95% lower bound +126,819, Student-t lower bound +126,919, upper bound +183,307, detection floor 28,185), W-T-L 333-0-179, halves +159,105 / +151,201, lower quartile 241,610 vs 192,040, moves +43.52. The preregistered gate PASSES. Replication: the first experiment's frozen tables (SHA-256 0ade9d4e4080ebdd52a1474b1a13410dc8dfb77f5eba24b078aa7703c92ace0b) as the same leaf on these fresh seeds averaged 487,066 points and 140.83 moves; prior-d3s7 minus fair-d3s7: paired +160,349 (bootstrap 95% lower bound +129,753, Student-t lower bound +129,626, upper bound +191,264, detection floor 30,670), W-T-L 330-0-182, halves +133,349 / +187,349. The replication criteria PASS. Scale: the wider, longer-trained candidate against the first candidate on the same seeds is -5,196 paired (bootstrap LB -40,535, t LB -40,060, UB +29,158, floor 34,803, W-T-L 267-1-244): the preregistered scale verdict is inconclusive. Against the program's standing reference, the fair leaf at depth 4 on the same seeds (397,154 points), the candidate is +84,716 paired (bootstrap LB +54,794, UB +114,355, W-T-L 298-0-214); diagnostic only. The first candidate against fair-d4s7 on these seeds: +89,912 (LB +58,243); diagnostic. The same tables played directly one ply averaged 328,039 points, +1,323 paired against fair-d3s7 (LB -17,545); diagnostic. Fair-d4s7 minus fair-d3s7 on these seeds: +70,437 (LB +46,917). Candidate: the tables of the main run's validation point at 2,000,153,332 training moves (layout rows,cols,win23,win32,win24,win42,phase=all, alpha 1, 5,800,000,000 entries), whose paired margin on the 256-game training-role validation block was +187,500; SHA-256 824b0a39a90d8a5aae63438c1538d4c5022f0e0f09c75fb7e2d758a1d6c6fb8a. Main run: 4,500,370,590 training moves, 47,477,538 games, 9 validation points, mean 1,184,973 moves per second, stopped by the plateau rule (last 4 validation points mean +165,666, the 4 before them +173,783). Training-signal check: at least one validation point of the main run had a positive paired margin.
Technical recordLimitations recorded with the result
- Public-development SCREEN tier, 512 paired games opened once; nothing here is a qualification claim, and protected and final cohorts stay sealed.
- The candidate is the validation point with the largest paired margin on a 256-game training-role block that was read at every validation point and drove the plateau stop rule; that selection is upward-biased, which is why the held-out screen exists.
- Training used lock-free asynchronous updates from 32 threads, so the training run is not bit-reproducible; the frozen tables are hashed and every gameplay arm is deterministic and worker-count independent.
- The fair-d4s7 arm is the program's standing reference for context only; the preregistered comparator is the identical depth-3 search with the frozen fair leaf.
- Table files (23.2 GB weights, 69.6 GB with accumulators) are retained on the workstation with their SHA-256 and are not committed.
- Wall times were measured on a shared workstation; ratios between arms on the same seeds are the trustworthy quantity.
- The replication arm re-screens tables frozen by the first experiment on a block that experiment never read; it is a fresh-block replication by the same runner on the same machine, not by an independent runner.
Agent contextSource files, operational notes and how to reproduce
Directory: approaches/fair-expectimax/rust-engine