Skip to content

Commit d4611eb

Browse files
authored
Support filtering based on PV (#39)
Filter by material and uniqueness of PV
1 parent 4eaa5dc commit d4611eb

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

docs/generate_puzzles.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,16 @@ Currently the following options are available:
3232

3333
`material_limit` - maximum allowed material advantage. Positions with material imbalance exceeding this value are filtered out. Default: 64000 (effectively disabled).
3434

35+
`final_material_limit` - maximum allowed material imbalance after playing the validation PV. Helps avoid puzzles where the mating line ends with overwhelming extra material. Default: 64000 (effectively disabled).
36+
37+
`material_diff_limit` - maximum allowed absolute change in material imbalance between the candidate position and the final position reached by the validation PV. Default: 64000 (effectively disabled).
38+
3539
`mate_ply` - only keep positions with mate in at least this many plies. Filters out mates that are too short. When using `puzzle_depth` it applies to this validation search, otherwise uses the regular search result. Default: 1.
3640

3741
`second_pv_limit` - uses multipv 2 during puzzle verification and filters out positions where the second best line's evaluation is greater than or equal to this value (in centipawns). This helps ensure puzzle positions have a clear best solution by filtering out positions with strong alternative moves. Set to a value >= 32000 (VALUE_MATE) to disable this check. Default: 1000.
3842

43+
`second_pv_nonroot_limit` - when validating the PV recursively, re-search positions where the attacking side is to move again. If the second-best move at any of those points evaluates above this limit, the puzzle is rejected. The search depth shrinks as the PV progresses. Set to a value >= 32000 (VALUE_MATE) to disable. Default: VALUE_MATE_IN_MAX_PLY.
44+
3945
`random_move_min_ply` - the minimal ply at which a random move may be executed instead of a move chosen by search. Default: 1.
4046

4147
`random_move_max_ply` - the maximal ply at which a random move may be executed instead of a move chosen by search. Default: 150.

src/tools/puzzle_generator.cpp

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,11 @@ namespace Stockfish::Tools
5656
// Upper limit of evaluation value of generated situation
5757
int king_safety_limit = 64000;
5858
int material_limit = 64000;
59+
int final_material_limit = 64000;
60+
int material_diff_limit = 64000;
5961
int mate_ply = 1;
6062
int second_pv_limit = 1000;
63+
int second_pv_nonroot_limit = VALUE_MATE_IN_MAX_PLY;
6164

6265
// minimum ply with random move
6366
// maximum ply with random move
@@ -358,6 +361,61 @@ namespace Stockfish::Tools
358361
keep = false;
359362
}
360363
}
364+
365+
const bool check_material_limits = params.final_material_limit < 64000
366+
|| params.material_diff_limit < 64000;
367+
const bool check_recursive_uniqueness = params.second_pv_nonroot_limit < VALUE_MATE;
368+
369+
if (keep
370+
&& !search_pv2.empty()
371+
&& (check_material_limits || check_recursive_uniqueness))
372+
{
373+
const Color initial_side = pos.side_to_move();
374+
const auto material_balance = [initial_side](const Position& position) {
375+
return mg_value(initial_side == WHITE ? position.psq_score()
376+
: -position.psq_score());
377+
};
378+
379+
const Value initial_material = material_balance(pos);
380+
381+
std::vector<StateInfo, AlignedAllocator<StateInfo>> validation_states(search_pv2.size());
382+
size_t executed = 0;
383+
384+
for (Move pv_move : search_pv2)
385+
{
386+
if (!is_ok(pv_move))
387+
break;
388+
389+
pos.do_move(pv_move, validation_states[executed]);
390+
++executed;
391+
}
392+
393+
Value final_material = material_balance(pos);
394+
Value material_delta = final_material - initial_material;
395+
396+
if ( final_material > params.final_material_limit
397+
|| material_delta > params.material_diff_limit)
398+
keep = false;
399+
400+
while (executed > 0)
401+
{
402+
if (keep
403+
&& check_recursive_uniqueness
404+
&& pos.side_to_move() == initial_side)
405+
{
406+
int depth_remaining = std::max(1, params.puzzle_depth - static_cast<int>(executed) / 2);
407+
auto [nested_value, nested_pv] = Search::search(pos, depth_remaining, 2, 0);
408+
auto& nested_rm = pos.this_thread()->rootMoves;
409+
if (nested_rm.size() >= 2)
410+
{
411+
Value second_value = nested_rm[1].score;
412+
if (second_value >= params.second_pv_nonroot_limit)
413+
keep = false;
414+
}
415+
}
416+
pos.undo_move(search_pv2[--executed]);
417+
}
418+
}
361419
}
362420

363421
// As a fallback if puzzle_depth is not set
@@ -653,7 +711,7 @@ namespace Stockfish::Tools
653711
sfen_writer.write(th.id(), sfen);
654712
}
655713

656-
return false;
714+
return counter.load(std::memory_order_relaxed) >= limit;
657715
}
658716

659717
void PuzzleGenerator::report(uint64_t done, uint64_t draws, uint64_t new_done)
@@ -738,10 +796,16 @@ namespace Stockfish::Tools
738796
is >> params.king_safety_limit;
739797
else if (token == "material_limit")
740798
is >> params.material_limit;
799+
else if (token == "final_material_limit")
800+
is >> params.final_material_limit;
801+
else if (token == "material_diff_limit")
802+
is >> params.material_diff_limit;
741803
else if (token == "mate_ply")
742804
is >> params.mate_ply;
743805
else if (token == "second_pv_limit")
744806
is >> params.second_pv_limit;
807+
else if (token == "second_pv_nonroot_limit")
808+
is >> params.second_pv_nonroot_limit;
745809
else if (token == "random_move_min_ply")
746810
is >> params.random_move_minply;
747811
else if (token == "random_move_max_ply")
@@ -839,8 +903,11 @@ namespace Stockfish::Tools
839903
<< " - count = " << loop_max << endl
840904
<< " - king_safety_limit = " << params.king_safety_limit << endl
841905
<< " - material_limit = " << params.material_limit << endl
906+
<< " - final_material_limit = " << params.final_material_limit << endl
907+
<< " - material_diff_limit = " << params.material_diff_limit << endl
842908
<< " - mate_ply = " << params.mate_ply << endl
843909
<< " - second_pv_limit = " << params.second_pv_limit << endl
910+
<< " - second_pv_nonroot_limit = " << params.second_pv_nonroot_limit << endl
844911
<< " - num threads (UCI) = " << params.num_threads << endl
845912
<< " - random_move_min_ply = " << params.random_move_minply << endl
846913
<< " - random_move_max_ply = " << params.random_move_maxply << endl

0 commit comments

Comments
 (0)