This bughunt isolated the remaining CDP2 validation failures to shared CPU/GPU move-generation count logic, not the transposition table, allocator, or CUDA-only behavior.
What Failed
Two legal-move families were being undercounted because the fast count path collapsed multiple legal source moves into one destination bit:
| Issue | Reduced FEN | SF18 d1 | Pre-fix CDP2 d1 | Fixed CDP2 d1 |
|---|---|---|---|---|
| Two promotion-capable pawns capture the same promotion target | 1r2k3/P1P5/8/8/8/8/8/6K1 w - - 0 1 |
21 | 17 | 21 |
| Two en-passant check evasions capture onto the same target | 7r/8/8/1PpP4/1K2Rp2/6k1/6P1/8 w - c6 0 5 |
9 | 8 | 9 |
Fix Summary
Promotion captures now count each promotion source family independently instead of unioning promotion destination bits. En-passant check evasion now counts adjacent legal source pawns to the captured checking pawn instead of counting the single en-passant target destination.
The en-passant fix is intentionally small and hot-path friendly:
nMoves += popCount((eastOne(enPassentCapturedPiece) | westOne(enPassentCapturedPiece)) & myPawns);
This works in the out-of-check counter because pinned pieces were already removed before myPawns is derived, and this branch only applies when the en-passant captured pawn is the checking piece.
Validation
The fixed CDP2 build passes the full 18-position validation corpus through d9.
| Validation | Result |
|---|---|
cpw_ep_endgame d9 |
50,086,749,815 |
Full --verify --max-depth 9 |
18 passed, 0 failed |
Speed Check
Focused interleaved speed checks against the frozen pre-fix binary were essentially flat. The largest apparent regression in the first short sample was deep_game_ply_64 d9; a seven-pass recheck narrowed it to -0.68% median with identical counts, which is within normal run-to-run noise for this workload.
| Position | Depth | Pre-fix Median (s) | Fixed Median (s) | Delta |
|---|---|---|---|---|
cpw_start |
9 | 0.221487 | 0.222829 | -0.61% |
cpw_ep_endgame |
9 | 0.005504 | 0.005490 | +0.25% |
cpw_promotions_castle |
9 | 5.428451 | 5.430717 | -0.04% |
cpw_tactical_bugcatcher |
9 | 8.881151 | 8.907042 | -0.29% |
deep_game_ply_64 |
9 | 0.481499 | 0.484776 | -0.68% |
Takeaway
The fix restores correctness for the remaining validation failures without changing the CDP2 architecture or introducing a second execution path. The important lesson is that destination-bit counting is unsafe anywhere multiple legal source moves can intentionally converge on one square.