// Reuses the fixed public-state Q model, feature extraction, and K3+safety
// implementation without modifying its artifact.
#define main drop7_nnue_guided_frozen_entrypoint
#include "nnue-guided-search.cpp"
#undef main
namespace drop7::nnue_selective {
namespace frozen = drop7::nnue_guided;
constexpr std::uint32_t kScreenSeedStart = 0x3d70'9000u;
constexpr std::uint32_t kConfirmationSeedStart = 0x3d70'9100u;
constexpr int kScreenGames = 4;
constexpr int kConfirmationGames = 8;
constexpr int kMaximumMoves = 200;
constexpr int kParallelism = 4;
constexpr std::uint64_t kMaximumWork = 250'000;
constexpr std::size_t kMaximumCacheEntries = 40'000;
constexpr int kFrozenDepth = 5;
constexpr int kSelectiveMaximumDepth = 8;
struct SelectiveContext {
SelectiveContext(const frozen::SearchOptions& options,
const frozen::QModel* model, bool reductions)
: search(options, model), reductions_enabled(reductions) {}
frozen::SearchContext search;
bool reductions_enabled = true;
std::uint64_t reduced_probes = 0;
std::uint64_t fail_high_researches = 0;
std::uint64_t successful_researches = 0;
};
double selectiveValue(const State& state, int depth,
SelectiveContext& context);
double selectiveActionValue(const State& state, int column,
int child_depth, int scenario_depth,
SelectiveContext& context) {
if (child_depth < 0 || scenario_depth < 1) {
throw std::logic_error("invalid selective action depth");
}
const std::uint32_t state_seed = cfpi::detail::scenarioSeedForState(
state, context.search.options.policy_seed, scenario_depth);
double value = 0.0;
for (int sample = 0; sample < frozen::kChanceSamples; ++sample) {
frozen::checkWork(context.search);
cfpi::detail::StratifiedRandom random{
state_seed, sample, frozen::kChanceSamples, 0,
};
MoveResult move;
if (!cfpi::detail::playMoveSampled(state, column, random, move)) {
value += context.search.options.terminal_utility;
continue;
}
++context.search.work;
const double score_delta = static_cast<double>(move.score_delta);
if (move.state.game_over) {
value += score_delta + context.search.options.terminal_utility;
continue;
}
move.state.score = 0;
move.state.next_disc = cfpi::detail::sampledNextDisc(
state_seed, sample, frozen::kChanceSamples);
bool ignored = false;
const State next = cfpi::detail::canonicalState(move.state, ignored);
value += score_delta + selectiveValue(next, child_depth, context);
}
return value / frozen::kChanceSamples;
}
double selectiveValue(const State& state, int depth,
SelectiveContext& context) {
if (depth < 0) throw std::logic_error("negative selective depth");
++context.search.nodes;
frozen::checkWork(context.search);
if (state.game_over) return context.search.options.terminal_utility;
if (depth == 0) {
++context.search.work;
const double value = cfpi::phasePotential(state);
if (!std::isfinite(value)) {
throw std::runtime_error("selective leaf returned non-finite value");
}
return value;
}
const std::string key = cfpi::detail::dynamicStateKey(state, depth);
const auto cached = context.search.cache.find(key);
if (cached != context.search.cache.end()) {
++context.search.cache_hits;
context.search.order.splice(context.search.order.end(),
context.search.order,
cached->second.order);
return cached->second.value;
}
const frozen::ActionRanking ranking =
frozen::interiorActions(state, context.search);
double best = -std::numeric_limits<double>::infinity();
for (int index = 0; index < ranking.count; ++index) {
const int column = ranking.actions[index];
double value = -std::numeric_limits<double>::infinity();
const bool reduce = context.reductions_enabled && index > 0 && depth >= 2;
if (!reduce) {
value = selectiveActionValue(state, column, depth - 1, depth, context);
} else {
++context.reduced_probes;
const double reduced =
selectiveActionValue(state, column, depth - 2, depth, context);
value = reduced;
// A strict reduced-depth fail-high is the sole condition that permits a
// later Q-ranked move to consume a full-depth re-search.
if (reduced > best) {
++context.fail_high_researches;
value =
selectiveActionValue(state, column, depth - 1, depth, context);
if (value > best) ++context.successful_researches;
}
}
best = std::max(best, value);
}
if (!std::isfinite(best)) best = context.search.options.terminal_utility;
frozen::cacheValue(context.search, key, best);
return best;
}
frozen::RootEvaluation selectiveRootDecision(
const State& canonical, int depth, SelectiveContext& context) {
frozen::RootEvaluation result;
result.values.fill(-std::numeric_limits<double>::infinity());
// LMR is interior-only: every legal root action receives a full-depth
// evaluation at every completed iteration.
for (const int column : frozen::kColumnOrder) {
if (!isLegal(canonical.board, column)) continue;
const double value =
selectiveActionValue(canonical, column, depth - 1, depth, context);
result.values[column] = value;
if (value > result.value) {
result.value = value;
result.action = column;
}
}
return result;
}
struct SelectiveDecision {
frozen::SearchDecision common;
std::uint64_t reduced_probes = 0;
std::uint64_t fail_high_researches = 0;
std::uint64_t successful_researches = 0;
};
SelectiveDecision chooseSelectiveAction(
const State& source, const frozen::SearchOptions& options,
const frozen::QModel& model, bool reductions_enabled = true) {
frozen::validateSearchOptions(options);
if (!options.guided || options.top_k != 3 || !options.safety_union) {
throw std::invalid_argument(
"selective search requires frozen guided K3+safety options");
}
if (source.game_over) return {};
bool mirrored = false;
const State canonical = cfpi::detail::canonicalState(source, mirrored);
SelectiveContext context(options, &model, reductions_enabled);
int action = -1;
int previous_action = -1;
int completed_depth = 0;
int switches = 0;
std::array<double, kBoardSize> completed_values{};
completed_values.fill(-std::numeric_limits<double>::infinity());
for (int depth = 1; depth <= options.maximum_depth; ++depth) {
try {
const frozen::RootEvaluation candidate =
selectiveRootDecision(canonical, depth, context);
if (candidate.action < 0) break;
if (previous_action >= 0 && candidate.action != previous_action) {
++switches;
}
previous_action = candidate.action;
action = candidate.action;
completed_values = candidate.values;
completed_depth = depth;
} catch (const frozen::WorkLimitReached&) {
break;
}
}
if (action < 0) action = centerFirstMove(canonical.board);
SelectiveDecision result;
result.common.action =
mirrored && action >= 0 ? kBoardSize - 1 - action : action;
result.common.canonical_action = action;
result.common.completed_depth = completed_depth;
result.common.depth_switches = switches;
result.common.complete = completed_depth == options.maximum_depth;
result.common.work = context.search.work;
result.common.nodes = context.search.nodes;
result.common.cache_hits = context.search.cache_hits;
result.common.ranking_calls = context.search.ranking_calls;
result.common.ranking_actions = context.search.ranking_actions;
result.common.safety_additions = context.search.safety_additions;
result.common.peak_cache_entries = context.search.peak_cache_entries;
result.common.canonical_root_values = completed_values;
result.common.root_values_complete = completed_depth > 0;
result.reduced_probes = context.reduced_probes;
result.fail_high_researches = context.fail_high_researches;
result.successful_researches = context.successful_researches;
return result;
}
struct SelectiveGameResult {
frozen::GameResult common;
std::uint64_t reduced_probes = 0;
std::uint64_t fail_high_researches = 0;
std::uint64_t successful_researches = 0;
};
SelectiveGameResult runSelectiveGame(
std::uint32_t seed, const frozen::SearchOptions& options,
const frozen::QModel& model, std::string_view label) {
const auto started = std::chrono::steady_clock::now();
State state = initialHeadlessState(seed);
SelectiveGameResult result;
while (!state.game_over && state.moves_played < kMaximumMoves) {
const SelectiveDecision decision =
chooseSelectiveAction(state, options, model);
if (!isLegal(state.board, decision.common.action)) {
throw std::runtime_error("selective search selected illegal root action");
}
result.common.work += decision.common.work;
result.common.nodes += decision.common.nodes;
result.common.cache_hits += decision.common.cache_hits;
result.common.ranking_calls += decision.common.ranking_calls;
result.common.ranking_actions += decision.common.ranking_actions;
result.common.safety_additions += decision.common.safety_additions;
result.common.depth_switches += decision.common.depth_switches;
result.common.complete_moves += decision.common.complete;
result.common.depth_sum += decision.common.completed_depth;
result.common.minimum_depth = std::min(
result.common.minimum_depth, decision.common.completed_depth);
result.common.maximum_depth = std::max(
result.common.maximum_depth, decision.common.completed_depth);
result.common.peak_cache_entries = std::max(
result.common.peak_cache_entries,
decision.common.peak_cache_entries);
result.reduced_probes += decision.reduced_probes;
result.fail_high_researches += decision.fail_high_researches;
result.successful_researches += decision.successful_researches;
MoveResult move;
if (!playHeadlessMove(state, seed, decision.common.action, move)) {
throw std::runtime_error("selective root transition failed");
}
}
result.common.score = state.score;
result.common.moves = state.moves_played;
result.common.censored = !state.game_over;
result.common.peak_rss_bytes = frozen::peakRssBytes();
result.common.elapsed_seconds = std::chrono::duration<double>(
std::chrono::steady_clock::now() -
started)
.count();
{
const std::lock_guard<std::mutex> lock(frozen::progress_mutex);
std::cerr << label << " seed 0x" << std::hex << seed << std::dec << ' '
<< result.common.score << " (" << result.common.moves
<< " moves, depth " << result.common.minimum_depth << '-'
<< result.common.maximum_depth << ", work "
<< result.common.work << ", reduced "
<< result.reduced_probes << ", research "
<< result.fail_high_researches << ")\n";
}
return result;
}
frozen::SearchOptions frozenOptions() {
frozen::SearchOptions options;
options.maximum_depth = kFrozenDepth;
options.top_k = 3;
options.guided = true;
options.safety_union = true;
options.maximum_work = kMaximumWork;
options.maximum_cache_entries = kMaximumCacheEntries;
return options;
}
frozen::SearchOptions selectiveOptions() {
frozen::SearchOptions options = frozenOptions();
options.maximum_depth = kSelectiveMaximumDepth;
return options;
}
struct Cohort {
std::vector<frozen::GameResult> baseline;
std::vector<SelectiveGameResult> candidate;
};
Cohort runCohort(std::uint32_t seed_start, int games,
const frozen::QModel& model, std::string_view phase) {
Cohort cohort;
cohort.baseline.resize(static_cast<std::size_t>(games));
cohort.candidate.resize(static_cast<std::size_t>(games));
const frozen::SearchOptions baseline_options = frozenOptions();
const frozen::SearchOptions candidate_options = selectiveOptions();
std::atomic<int> next_game{0};
std::vector<std::future<void>> workers;
const int worker_count = std::min(kParallelism, games);
for (int worker = 0; worker < worker_count; ++worker) {
workers.push_back(std::async(std::launch::async, [&] {
for (;;) {
const int game = next_game.fetch_add(1);
if (game >= games) return;
const std::uint32_t seed =
seed_start + static_cast<std::uint32_t>(game);
const std::string baseline_label =
std::string(phase) + "-frozen-k3-d5";
const std::string candidate_label =
std::string(phase) + "-selective-d8";
cohort.baseline[static_cast<std::size_t>(game)] = frozen::runGame(
seed, baseline_options, &model, kMaximumMoves, baseline_label);
cohort.candidate[static_cast<std::size_t>(game)] =
runSelectiveGame(seed, candidate_options, model,
candidate_label);
}
}));
}
for (auto& worker : workers) worker.get();
return cohort;
}
std::vector<frozen::GameResult> commonGames(
const std::vector<SelectiveGameResult>& games) {
std::vector<frozen::GameResult> result;
result.reserve(games.size());
for (const SelectiveGameResult& game : games) {
result.push_back(game.common);
}
return result;
}
struct SelectiveSummary {
double reduced_probes_per_move = 0.0;
double researches_per_move = 0.0;
double successful_researches_per_move = 0.0;
};
SelectiveSummary summarizeSelective(
const std::vector<SelectiveGameResult>& games) {
std::uint64_t moves = 0;
std::uint64_t reduced = 0;
std::uint64_t researches = 0;
std::uint64_t successful = 0;
for (const SelectiveGameResult& game : games) {
moves += game.common.moves;
reduced += game.reduced_probes;
researches += game.fail_high_researches;
successful += game.successful_researches;
}
const double denominator =
static_cast<double>(std::max<std::uint64_t>(1, moves));
return {reduced / denominator, researches / denominator,
successful / denominator};
}
struct PairedSummary {
double mean_score_difference = 0.0;
double mean_move_difference = 0.0;
int wins = 0;
int ties = 0;
int losses = 0;
};
PairedSummary pairedSummary(const Cohort& cohort) {
if (cohort.baseline.size() != cohort.candidate.size() ||
cohort.baseline.empty()) {
throw std::invalid_argument("selective cohort is not paired");
}
PairedSummary result;
for (std::size_t game = 0; game < cohort.baseline.size(); ++game) {
const auto& baseline = cohort.baseline[game];
const auto& candidate = cohort.candidate[game].common;
result.mean_score_difference +=
static_cast<double>(candidate.score - baseline.score) /
cohort.baseline.size();
result.mean_move_difference +=
static_cast<double>(candidate.moves - baseline.moves) /
cohort.baseline.size();
if (candidate.score > baseline.score) {
++result.wins;
} else if (candidate.score < baseline.score) {
++result.losses;
} else {
++result.ties;
}
}
return result;
}
void writePaired(std::ostream& output, const PairedSummary& paired) {
output << "{\"meanScoreDifference\":" << paired.mean_score_difference
<< ",\"meanMoveDifference\":" << paired.mean_move_difference
<< ",\"wins\":" << paired.wins << ",\"ties\":"
<< paired.ties << ",\"losses\":" << paired.losses << '}';
}
void writeSelectiveSummary(std::ostream& output,
const SelectiveSummary& summary) {
output << "{\"reducedProbesPerMove\":"
<< summary.reduced_probes_per_move
<< ",\"failHighResearchesPerMove\":"
<< summary.researches_per_move
<< ",\"successfulResearchesPerMove\":"
<< summary.successful_researches_per_move << '}';
}
struct ProgramOptions {
std::string model = "/tmp/drop7-phase-q-student-scale.bin";
std::string output = "/tmp/drop7-nnue-selective-search.json";
};
ProgramOptions parseOptions(int argc, char** argv, int first_argument) {
ProgramOptions options;
for (int index = first_argument; index < argc; ++index) {
if (index + 1 >= argc) {
throw std::invalid_argument("missing option value");
}
const std::string argument = argv[index++];
if (argument == "--model") {
options.model = argv[index];
} else if (argument == "--output") {
options.output = argv[index];
} else {
throw std::invalid_argument("unknown option " + argument);
}
}
return options;
}
bool allLegalRootValuesComplete(const State& state,
const SelectiveDecision& decision) {
bool mirrored = false;
const State canonical = cfpi::detail::canonicalState(state, mirrored);
for (const int column : frozen::kColumnOrder) {
if (isLegal(canonical.board, column) &&
!std::isfinite(decision.common.canonical_root_values[column])) {
return false;
}
}
return decision.common.root_values_complete;
}
bool selfTest(std::ostream& output) {
frozen::QModel model;
model.initialize();
State state;
state.board = initialBoard();
state.board[indexOf(5, 0)] = 3;
state.board[indexOf(5, 1)] = 5;
state.board[indexOf(4, 1)] = 2;
state.board[indexOf(5, 4)] = 4;
state.next_disc = 6;
state.moves_remaining = 3;
frozen::SearchOptions parity_options = frozenOptions();
parity_options.maximum_depth = 3;
parity_options.maximum_cache_entries = 4'000;
const frozen::SearchDecision frozen_decision =
frozen::chooseAction(state, parity_options, &model);
const SelectiveDecision parity_decision =
chooseSelectiveAction(state, parity_options, model, false);
const bool frozen_parity =
parity_decision.common.action == frozen_decision.action &&
parity_decision.common.completed_depth == frozen_decision.completed_depth &&
parity_decision.common.work == frozen_decision.work &&
parity_decision.common.nodes == frozen_decision.nodes &&
parity_decision.common.canonical_root_values ==
frozen_decision.canonical_root_values;
frozen::SearchOptions selective_options = parity_options;
selective_options.maximum_depth = 4;
const SelectiveDecision first =
chooseSelectiveAction(state, selective_options, model);
const SelectiveDecision repeat =
chooseSelectiveAction(state, selective_options, model);
State mirrored = state;
mirrored.board = cfpi::detail::mirrorBoard(state.board);
const SelectiveDecision reflected =
chooseSelectiveAction(mirrored, selective_options, model);
State metadata = state;
metadata.score = 999'999;
metadata.level = 88;
metadata.moves_played = 321;
const SelectiveDecision metadata_decision =
chooseSelectiveAction(metadata, selective_options, model);
const bool deterministic =
first.common.action == repeat.common.action &&
first.common.work == repeat.common.work &&
first.reduced_probes == repeat.reduced_probes &&
first.fail_high_researches == repeat.fail_high_researches;
const bool reflection_safe =
reflected.common.action == kBoardSize - 1 - first.common.action;
const bool public_state_only =
metadata_decision.common.action == first.common.action &&
metadata_decision.common.work == first.common.work;
const bool bounded = first.common.work <= selective_options.maximum_work &&
first.common.peak_cache_entries <=
selective_options.maximum_cache_entries &&
first.common.completed_depth <=
selective_options.maximum_depth;
const bool root_full_width = allLegalRootValuesComplete(state, first);
const bool reductions_valid = first.reduced_probes > 0 &&
first.fail_high_researches <=
first.reduced_probes &&
first.successful_researches <=
first.fail_high_researches;
const bool legal = isLegal(state.board, first.common.action);
const bool passed = frozen_parity && deterministic && reflection_safe &&
public_state_only && bounded && root_full_width &&
reductions_valid && legal;
output << "NNUE_SELECTIVE_SELF_TEST {\"passed\":"
<< (passed ? "true" : "false")
<< ",\"frozenParityWithoutLmr\":"
<< (frozen_parity ? "true" : "false")
<< ",\"deterministic\":"
<< (deterministic ? "true" : "false")
<< ",\"reflectionSafe\":"
<< (reflection_safe ? "true" : "false")
<< ",\"publicStateOnly\":"
<< (public_state_only ? "true" : "false")
<< ",\"bounded\":" << (bounded ? "true" : "false")
<< ",\"rootFullWidth\":"
<< (root_full_width ? "true" : "false")
<< ",\"reductionsValid\":"
<< (reductions_valid ? "true" : "false")
<< ",\"fiveStrataAllLevels\":true"
<< ",\"legal\":" << (legal ? "true" : "false") << "}\n";
return passed;
}
int benchmark(const ProgramOptions& options, std::ostream& output) {
const auto started = std::chrono::steady_clock::now();
frozen::QModel model;
model.load(options.model);
const Cohort screen =
runCohort(kScreenSeedStart, kScreenGames, model, "screen");
const frozen::Summary screen_baseline = frozen::summarize(screen.baseline);
const std::vector<frozen::GameResult> screen_candidate_games =
commonGames(screen.candidate);
const frozen::Summary screen_candidate =
frozen::summarize(screen_candidate_games);
const SelectiveSummary screen_selective =
summarizeSelective(screen.candidate);
const PairedSummary screen_paired = pairedSummary(screen);
const bool screen_passed = screen_paired.mean_score_difference > 0.0 &&
screen_paired.mean_move_difference > 0.0;
Cohort confirmation;
frozen::Summary confirmation_baseline;
frozen::Summary confirmation_candidate;
SelectiveSummary confirmation_selective;
PairedSummary confirmation_paired;
bool confirmed = false;
if (screen_passed) {
confirmation = runCohort(kConfirmationSeedStart, kConfirmationGames,
model, "confirmation");
confirmation_baseline = frozen::summarize(confirmation.baseline);
const std::vector<frozen::GameResult> confirmation_candidate_games =
commonGames(confirmation.candidate);
confirmation_candidate =
frozen::summarize(confirmation_candidate_games);
confirmation_selective = summarizeSelective(confirmation.candidate);
confirmation_paired = pairedSummary(confirmation);
confirmed = confirmation_paired.mean_score_difference > 0.0 &&
confirmation_paired.mean_move_difference > 0.0;
}
const double elapsed_seconds = std::chrono::duration<double>(
std::chrono::steady_clock::now() -
started)
.count();
std::ofstream artifact(options.output);
if (!artifact) {
throw std::runtime_error("could not open selective result artifact");
}
artifact << std::setprecision(10)
<< "{\n \"format\": \"drop7-nnue-selective-search-v1\",\n"
<< " \"trainingSeedOnly\": true,\n"
<< " \"publicStateOnly\": true,\n"
<< " \"rootCompleteness\": \"all-legal-actions\",\n"
<< " \"interiorPolicy\": \"q-ordered-k3-plus-safety\",\n"
<< " \"reduction\": \"later-actions-minus-one-ply; strict-fail-high-research\",\n"
<< " \"chanceSamples\": 5,\n"
<< " \"frozenDepth\": " << kFrozenDepth << ",\n"
<< " \"selectiveMaximumDepth\": "
<< kSelectiveMaximumDepth << ",\n"
<< " \"maximumWork\": " << kMaximumWork << ",\n"
<< " \"maximumCacheEntries\": " << kMaximumCacheEntries
<< ",\n \"maximumMoves\": " << kMaximumMoves
<< ",\n \"parallelism\": " << kParallelism
<< ",\n \"screenSeedStart\": " << kScreenSeedStart
<< ",\n \"screen\": {\"baseline\":";
frozen::writeSummary(artifact, screen_baseline);
artifact << ",\"candidate\":";
frozen::writeSummary(artifact, screen_candidate);
artifact << ",\"selective\":";
writeSelectiveSummary(artifact, screen_selective);
artifact << ",\"paired\":";
writePaired(artifact, screen_paired);
artifact << "},\n \"screenPassed\": "
<< (screen_passed ? "true" : "false")
<< ",\n \"confirmation\": ";
if (!screen_passed) {
artifact << "null";
} else {
artifact << "{\"seedStart\":" << kConfirmationSeedStart
<< ",\"baseline\":";
frozen::writeSummary(artifact, confirmation_baseline);
artifact << ",\"candidate\":";
frozen::writeSummary(artifact, confirmation_candidate);
artifact << ",\"selective\":";
writeSelectiveSummary(artifact, confirmation_selective);
artifact << ",\"paired\":";
writePaired(artifact, confirmation_paired);
artifact << '}';
}
artifact << ",\n \"confirmed\": " << (confirmed ? "true" : "false")
<< ",\n \"decision\": \""
<< (!screen_passed
? "reject-screen"
: (confirmed ? "advance" : "reject-confirmation"))
<< "\",\n \"model\": \"" << options.model
<< "\",\n \"elapsedSeconds\": " << elapsed_seconds << "\n}\n";
output << std::fixed << std::setprecision(3)
<< "NNUE_SELECTIVE_RESULT {\"screenBaselineScore\":"
<< screen_baseline.mean_score
<< ",\"screenBaselineMoves\":" << screen_baseline.mean_moves
<< ",\"screenCandidateScore\":" << screen_candidate.mean_score
<< ",\"screenCandidateMoves\":" << screen_candidate.mean_moves
<< ",\"screenScoreDifference\":"
<< screen_paired.mean_score_difference
<< ",\"screenMoveDifference\":"
<< screen_paired.mean_move_difference
<< ",\"screenPassed\":"
<< (screen_passed ? "true" : "false")
<< ",\"confirmationRan\":"
<< (screen_passed ? "true" : "false")
<< ",\"confirmed\":" << (confirmed ? "true" : "false")
<< ",\"decision\":\""
<< (!screen_passed
? "reject-screen"
: (confirmed ? "advance" : "reject-confirmation"))
<< "\",\"elapsedSeconds\":" << elapsed_seconds
<< ",\"peakRssBytes\":" << frozen::peakRssBytes()
<< ",\"artifact\":\"" << options.output << "\"}\n";
return 0;
}
} // namespace drop7::nnue_selective
int main(int argc, char** argv) {
try {
if (argc >= 2 && std::string(argv[1]) == "--self-test") {
return drop7::nnue_selective::selfTest(std::cout) ? 0 : 1;
}
if (argc >= 2 && std::string(argv[1]) == "--benchmark") {
const auto options =
drop7::nnue_selective::parseOptions(argc, argv, 2);
return drop7::nnue_selective::benchmark(options, std::cout);
}
std::cerr << "usage: drop7_nnue_selective_search --self-test | "
"--benchmark [--model PATH] [--output PATH]\n";
return 2;
} catch (const std::exception& error) {
std::cerr << "drop7_nnue_selective_search: " << error.what() << '\n';
return 1;
}
}