Skip to content

Record the last move when adjudicating a game - #2429

Open
Menkib64 wants to merge 1 commit into
LeelaChessZero:masterfrom
Menkib64:selfplay_record_the_last_move
Open

Record the last move when adjudicating a game#2429
Menkib64 wants to merge 1 commit into
LeelaChessZero:masterfrom
Menkib64:selfplay_record_the_last_move

Conversation

@Menkib64

Copy link
Copy Markdown
Contributor

The idea raises from an example training game where the last move is a major blunder from winning to losing position. https://lichess.org/bL37LY5j The position after blunder is mate in 3. Network predicts 24.9 plies left. This change includes the last position in game which reached the clear evaluation is proven mate in 3.
This hopefully includes a few provable mates that network quite doesn't manage to predict at root yet. My limited local test showed that most of end positions are just a minor shift from a really bad evaluation (Q=0.96-0.98) to threshold. I managed to see a proven mate where best_m=12 while previous move best_m=played_m=84.77. Previous move best_q=played_q=0.983. orig_q=0.978 improved to orig_q=-0.981 for the newly included move.
This is only limited observation that change includes expected data. It doesn't prove that including these positions will improve training.

Thread 26 "lc0" hit Breakpoint 3, lczero::V6TrainingDataArray::Write (this=0x7ffc70000ec8, writer=0x7ffd228d7570, result=lczero::GameResult::WHITE_WON, adjudicated=true)
    at ../src/trainingdata/trainingdata.cc:81
81	                                bool adjudicated) const {
// The new last position
$66 = {version = 6, input_format = 1, probabilities = {-1 <repeats 830 times>, 0.1864831, -1 <repeats 903 times>, 0.649561942, 0.0838548169, -1, -1, -1, -1, -1, -1, 0.0413016267, 
    0.0387984999, -1 <repeats 114 times>}, planes = {67108864, 0, 0, 0, 0, 288230376151711744, 0, 0, 72057594037927936, 0, 1, 33554432, 0, 67108864, 0, 0, 0, 0, 288230376151711744, 256, 
    0, 72057594037927936, 0, 0, 33554432, 0, 67108864, 0, 0, 0, 0, 1125899906842624, 256, 0, 72057594037927936, 0, 0, 33554432, 0, 67108864, 0, 0, 0, 0, 1125899906842624, 65536, 0, 
    72057594037927936, 0, 0, 33554432, 0, 67108864, 0, 0, 0, 0, 288230376151711744, 65536, 0, 72057594037927936, 0, 0, 33554432, 0, 67108864, 0, 0, 0, 0, 288230376151711744, 65536, 0, 
    72057594037927936, 0, 0, 131072, 0, 67108864, 0, 0, 0, 0, 144115188075855872, 65536, 0, 72057594037927936, 0, 0, 131072, 0, 67108864, 0, 0, 0, 0, 144115188075855872, 16777216, 0, 
    72057594037927936, 0, 0, 131072, 0}, castling_us_ooo = 0 '\000', castling_us_oo = 0 '\000', castling_them_ooo = 0 '\000', castling_them_oo = 0 '\000', 
  side_to_move_or_enpassant = 1 '\001', rule50_count = 0 '\000', invariance_info = 8 '\b', dummy = 0 '\000', root_q = -0.999976754, best_q = -1, root_d = 2.30808328e-05, best_d = 0, 
  root_m = 11.6314878, best_m = 12, plies_left = 0, result_q = 0, result_d = 1, played_q = -1, played_d = 0, played_m = 12, orig_q = -0.98142916, orig_d = 0.0184646565, orig_m = 107.1875, 
  visits = 800, played_idx = 1734, best_idx = 1734, policy_kld = 0.296707332, q_st = 0}
// The old last position
$67 = {version = 6, input_format = 1, probabilities = {-1 <repeats 179 times>, 0.0378973112, -1, 0.0916870385, -1, -1, 0.0317848399, -1, 0.0330073349, -1, 0.0330073349, -1, 0.0403422974, 
    -1, 0.0305623468, -1 <repeats 902 times>, 0.0623471886, -1, 0.0207823962, -1, -1, -1, -1, -1, 0.0855745748, 0.0183374081, -1, 0.0476772599, 0.0281173587, 0.017114915, 
    -1 <repeats 488 times>, 0.00122249394, -1 <repeats 257 times>, 0.306845963, 0.108801953, 0.00488997577}, planes = {281474976710656, 0, 1, 0, 0, 8589934592, 17179869184, 0, 0, 0, 0, 4, 
    0, 281474976710656, 0, 1, 0, 0, 8589934592, 17179869184, 0, 0, 0, 0, 1024, 0, 1099511627776, 0, 1, 0, 0, 8589934592, 17179869184, 0, 0, 0, 0, 1024, 0, 1099511627776, 0, 1, 0, 0, 
    8589934592, 17179869184, 0, 0, 0, 0, 4, 0, 1099511627776, 0, 1, 0, 0, 2199023255552, 17179869184, 0, 0, 0, 0, 4, 0, 1099511627776, 0, 1, 0, 0, 2199023255552, 17179869184, 0, 0, 0, 0, 
    2, 0, 4294967296, 0, 1, 0, 0, 2199023255552, 17179869184, 0, 0, 0, 0, 2, 0, 4294967296, 0, 1, 0, 0, 2199023255552, 17179869184, 0, 0, 0, 0, 4, 0}, castling_us_ooo = 0 '\000', 
  castling_us_oo = 0 '\000', castling_them_ooo = 0 '\000', castling_them_oo = 0 '\000', side_to_move_or_enpassant = 0 '\000', rule50_count = 1 '\001', invariance_info = 0 '\000', 
  dummy = 0 '\000', root_q = 0.979605615, best_q = 0.982774496, root_d = 0.0203391518, best_d = 0.0171612799, root_m = 98.0093994, best_m = 84.7791443, plies_left = 0, result_q = 0, 
  result_d = 1, played_q = 0.982774496, played_d = 0.0171612799, played_m = 84.7791443, orig_q = 0.977579951, orig_d = 0.0223817118, orig_m = 109.1875, visits = 819, played_idx = 1855, 
  best_idx = 1855, policy_kld = 0.249673605, q_st = 0}

The idea raises from an example training game where the last move is a
major blunder from winning to losing position. https://lichess.org/bL37LY5j
The position after blunder is mate in 3. Network predicts 24.9 plies
left. This change includes the last position in game which reached the
clear evaluation is proven mate in 3.
This hopefully includes a few provable mates that network quite doesn't
manage to predict at root yet. My limited local test showed that most of
end positions are just a minor shift from a really bad evaluation
(Q=0.96-0.98) to threshold. I managed to see a proven mate where
best_m=12 while previous move best_m=played_m=84.77. Previous move
best_q=played_q=0.983. orig_q=0.978 improved to orig_q=-0.981 for the
newly included move.
This is only limited observation that change includes expected data. It
doesn't prove that including these positions will improve training.
Copilot AI review requested due to automatic review settings July 15, 2026 18:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts self-play adjudication timing so that when a game is ended early via resign/adjudication thresholds, the final “clearly decided” position is still captured in the training dataset (instead of being skipped by an early break).

Changes:

  • Moves the resign/adjudication check to occur after training_data_.Add(...) so the final position is recorded before the game terminates.
  • Keeps the adjudication behavior unchanged, but ensures the last training sample is retained when adjudication triggers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants