#define DROP7_FAIR_ONLY_DEPTH4_LIBRARY
#include "../../fair-expectimax/reference/fair-only-depth4.cpp"
#undef DROP7_FAIR_ONLY_DEPTH4_LIBRARY
#include <bit>
#include <filesystem>
#include <map>
#include <set>
#include <type_traits>
// Compares sibling-ranking architectures offline on the checksum-locked
// 477-record h200 development-panel corpus. It has no gameplay lane: it
// never creates an initial state, advances an origin game, reads a new seed,
// generates a panel, or runs a policy screen. Provenance identifies the one
// whole origin game held out in each fold and is never presented to the model.
namespace drop7::d4_h200_sibling_nnue {
namespace detail = drop7::cfpi::detail;
using Clock = std::chrono::steady_clock;
// The locked parser and exact-search adapter are included locally, with their
// dependency hash recorded below. This avoids importing another executable's
// entry point or diagnostic model.
namespace frozen_audit {
namespace d1 = drop7::fair_only_horizon;
namespace d4 = drop7::fair_only_depth4;
constexpr int kExpectedRecords = 477;
constexpr int kExpectedGames = 8;
constexpr std::uint32_t kExpectedGameStart = 0x3d6d'0010u;
constexpr std::string_view kExpectedInputSha256 =
"bfda8ae32fa0be3577c6b27f6413aba28f2854930e2f91a0dcf7674808f04196";
struct Options {
std::string input;
std::string input_sha256;
};
std::uint64_t peakRssBytes() {
rusage usage{};
if (getrusage(RUSAGE_SELF, &usage) != 0) return 0;
#if defined(__APPLE__)
return static_cast<std::uint64_t>(usage.ru_maxrss);
#else
return static_cast<std::uint64_t>(usage.ru_maxrss) * 1024ull;
#endif
}
void enforceRss() {
if (peakRssBytes() > 256ull * 1024ull * 1024ull) {
throw std::runtime_error("frozen exact audit exceeded 256 MiB RSS");
}
}
struct Deadline {
Clock::time_point started = Clock::now();
void check() const {
if (std::chrono::duration<double>(Clock::now() - started).count() >
30.0 * 60.0) {
throw std::runtime_error("frozen exact audit exceeded 30 minute wall");
}
}
};
struct PublicState {
Board board{};
std::uint8_t next_disc = 1;
std::uint8_t moves_remaining = kMovesPerLevel;
bool terminal = false;
bool operator==(const PublicState&) const = default;
};
State materialize(const PublicState& source) {
State result;
result.board = source.board;
result.next_disc = source.next_disc;
result.score = 0;
result.level = 1;
result.moves_remaining = source.moves_remaining;
result.moves_played = 0;
result.game_over = source.terminal;
return result;
}
PublicState publicState(const State& source) {
return {source.board, source.next_disc,
static_cast<std::uint8_t>(source.moves_remaining), source.game_over};
}
PublicState mirror(const PublicState& source) {
PublicState result = source;
result.board = detail::mirrorBoard(source.board);
return result;
}
PublicState canonicalPublic(const PublicState& source, bool& mirrored) {
return publicState(detail::canonicalState(materialize(source), mirrored));
}
std::uint64_t mix64(std::uint64_t value) {
value ^= value >> 30u;
value *= 0xbf58'476d'1ce4'e5b9ull;
value ^= value >> 27u;
value *= 0x94d0'49bb'1331'11ebull;
return value ^ (value >> 31u);
}
std::uint64_t publicHash(const PublicState& source) {
bool ignored = false;
const PublicState state = canonicalPublic(source, ignored);
std::uint64_t hash = 0xcbf2'9ce4'8422'2325ull;
for (std::uint8_t cell : state.board) {
hash ^= static_cast<std::uint64_t>(cell + 1u);
hash *= 0x0000'0100'0000'01b3ull;
}
hash ^= state.next_disc;
hash *= 0x0000'0100'0000'01b3ull;
hash ^= static_cast<std::uint64_t>(state.moves_remaining + 1u);
hash *= 0x0000'0100'0000'01b3ull;
hash ^= static_cast<std::uint64_t>(state.terminal);
return mix64(hash);
}
std::string hex64(std::uint64_t value) {
std::ostringstream output;
output << "0x" << std::hex << std::setw(16) << std::setfill('0') << value;
return output.str();
}
std::string jsonEscape(std::string_view value) {
std::string result;
for (char token : value) {
if (token == '"' || token == '\\') result.push_back('\\');
if (token == '\n') result += "\\n";
else result.push_back(token);
}
return result;
}
std::size_t afterMarker(std::string_view text, std::string_view marker,
std::size_t begin = 0) {
const std::size_t found = text.find(marker, begin);
if (found == std::string_view::npos) {
throw std::runtime_error("missing JSON marker " + std::string(marker));
}
return found + marker.size();
}
long long integerAfter(std::string_view text, std::string_view marker,
std::size_t begin = 0) {
const std::size_t cursor = afterMarker(text, marker, begin);
const std::string owned(text);
char* end = nullptr;
const char* first = owned.c_str() + cursor;
const long long value = std::strtoll(first, &end, 10);
if (end == first) throw std::runtime_error("invalid JSON integer");
return value;
}
double numberAfter(std::string_view text, std::string_view marker,
std::size_t begin = 0) {
const std::size_t cursor = afterMarker(text, marker, begin);
const std::string owned(text);
char* end = nullptr;
const char* first = owned.c_str() + cursor;
const double value = std::strtod(first, &end);
if (end == first || !std::isfinite(value)) {
throw std::runtime_error("invalid JSON number");
}
return value;
}
bool booleanAfter(std::string_view text, std::string_view marker,
std::size_t begin = 0) {
const std::size_t cursor = afterMarker(text, marker, begin);
if (text.substr(cursor, 4) == "true") return true;
if (text.substr(cursor, 5) == "false") return false;
throw std::runtime_error("invalid JSON boolean");
}
std::string stringAfter(std::string_view text, std::string_view marker,
std::size_t begin = 0) {
const std::size_t cursor = afterMarker(text, marker, begin);
const std::size_t end = text.find('"', cursor);
if (end == std::string_view::npos) {
throw std::runtime_error("unterminated JSON string");
}
return std::string(text.substr(cursor, end - cursor));
}
std::size_t matchingDelimiter(std::string_view text, std::size_t begin,
char open, char close) {
if (begin >= text.size() || text[begin] != open) {
throw std::runtime_error("invalid JSON delimiter start");
}
int depth = 0;
bool quoted = false, escaped = false;
for (std::size_t cursor = begin; cursor < text.size(); ++cursor) {
const char token = text[cursor];
if (quoted) {
if (escaped) escaped = false;
else if (token == '\\') escaped = true;
else if (token == '"') quoted = false;
continue;
}
if (token == '"') quoted = true;
else if (token == open) ++depth;
else if (token == close && --depth == 0) return cursor;
}
throw std::runtime_error("unterminated JSON delimiter");
}
void skipSeparators(std::string_view text, std::size_t& cursor) {
while (cursor < text.size() &&
(text[cursor] == ' ' || text[cursor] == '\t' ||
text[cursor] == ',')) ++cursor;
}
struct PanelAction {
bool legal = false;
double mean_score = 0.0;
double mean_moves = 0.0;
double mean_clears = 0.0;
double mean_reveals = 0.0;
int surviving_cutoffs = 0;
double score_lcb99 = 0.0;
double move_lcb99 = 0.0;
int material_downsides = 0;
double material_downside_upper99 = 0.0;
};
struct PanelRecord {
std::uint32_t origin_game = 0;
int move_index = -1;
std::uint64_t stored_public_hash = 0;
PublicState state{};
int stored_d1_action = -1;
int deployment_action = -1;
bool deployment_switched = false;
std::array<PanelAction, kBoardSize> actions{};
};
std::uint64_t parseHex64(std::string_view value) {
const std::string owned(value);
char* end = nullptr;
const unsigned long long parsed = std::strtoull(owned.c_str(), &end, 0);
if (end == owned.c_str() || *end != '\0') {
throw std::runtime_error("invalid stored public hash");
}
return static_cast<std::uint64_t>(parsed);
}
PanelAction parsePanelAction(std::string_view object, int expected_action) {
PanelAction result;
result.legal = true;
if (integerAfter(object, "\"action\":") != expected_action) {
throw std::runtime_error("panel action index mismatch");
}
result.mean_score = numberAfter(object, "\"meanScoreReturn\":");
result.mean_moves = numberAfter(object, "\"meanSurvivedMoves\":");
result.mean_clears = numberAfter(object, "\"meanNumberedClears\":");
result.mean_reveals = numberAfter(object, "\"meanCoversRevealed\":");
result.surviving_cutoffs = static_cast<int>(
integerAfter(object, "\"survivingCutoffs\":"));
const std::size_t paired = afterMarker(object, "\"pairedVsFairD1\":");
const std::size_t score = afterMarker(object, "\"score\":", paired);
const std::size_t score_end = matchingDelimiter(object, score, '{', '}');
result.score_lcb99 = numberAfter(
object.substr(score, score_end - score + 1), "\"lowerOneSided99\":");
const std::size_t moves = afterMarker(object, "\"moves\":", score_end + 1);
const std::size_t moves_end = matchingDelimiter(object, moves, '{', '}');
result.move_lcb99 = numberAfter(
object.substr(moves, moves_end - moves + 1), "\"lowerOneSided99\":");
result.material_downsides = static_cast<int>(
integerAfter(object, "\"materialDownsides\":", moves_end));
result.material_downside_upper99 =
numberAfter(object, "\"materialDownsideUpper99\":", moves_end);
return result;
}
PanelRecord parsePanel(std::string_view line) {
if (line.empty() || line.front() != '{' || line.back() != '}' ||
line.find('\0') != std::string_view::npos ||
line.find("\"recordType\":\"deployment-panel-export-replay\"") ==
std::string_view::npos ||
line.find("\"gate\":\"ultra\"") == std::string_view::npos ||
line.find("\"excludedFromModelInput\"") == std::string_view::npos) {
throw std::runtime_error("unexpected panel record metadata");
}
PanelRecord result;
const long long game = integerAfter(line, "\"screenSeed\":");
if (game < 0 || game > std::numeric_limits<std::uint32_t>::max()) {
throw std::runtime_error("invalid panel origin game");
}
result.origin_game = static_cast<std::uint32_t>(game);
result.move_index = static_cast<int>(integerAfter(line, "\"moveIndex\":"));
result.stored_public_hash = parseHex64(
stringAfter(line, "\"canonicalPublicHash\":\""));
const std::string board = stringAfter(line, "\"board\":\"");
if (board.size() != kCellCount) throw std::runtime_error("invalid board size");
for (int cell = 0; cell < kCellCount; ++cell) {
if (board[cell] < '0' || board[cell] > '9') {
throw std::runtime_error("invalid board token");
}
result.state.board[cell] = static_cast<std::uint8_t>(board[cell] - '0');
}
result.state.next_disc =
static_cast<std::uint8_t>(integerAfter(line, "\"nextDisc\":"));
result.state.moves_remaining = static_cast<std::uint8_t>(
integerAfter(line, "\"movesRemaining\":"));
result.state.terminal = booleanAfter(line, "\"terminal\":");
result.stored_d1_action =
static_cast<int>(integerAfter(line, "\"fairD1Action\":"));
result.deployment_action =
static_cast<int>(integerAfter(line, "\"selectedAction\":"));
result.deployment_switched = booleanAfter(line, "\"switched\":");
if (result.state.next_disc < 1 || result.state.next_disc > kBoardSize ||
result.state.moves_remaining < 1 ||
result.state.moves_remaining > kMovesPerLevel || result.state.terminal ||
result.move_index < 0) {
throw std::runtime_error("invalid public-state metadata");
}
std::size_t cursor = afterMarker(line, "\"actions\":[");
for (int action = 0; action < kBoardSize; ++action) {
skipSeparators(line, cursor);
if (line.substr(cursor, 4) == "null") { cursor += 4; continue; }
if (cursor >= line.size() || line[cursor] != '{') {
throw std::runtime_error("invalid panel action array");
}
const std::size_t end = matchingDelimiter(line, cursor, '{', '}');
result.actions[action] =
parsePanelAction(line.substr(cursor, end - cursor + 1), action);
cursor = end + 1;
}
skipSeparators(line, cursor);
if (cursor >= line.size() || line[cursor] != ']') {
throw std::runtime_error("unterminated panel action array");
}
for (int action = 0; action < kBoardSize; ++action) {
if (result.actions[action].legal != isLegal(result.state.board, action)) {
throw std::runtime_error("panel legal mask mismatch");
}
}
if (result.stored_d1_action < 0 || result.stored_d1_action >= kBoardSize ||
result.deployment_action < 0 || result.deployment_action >= kBoardSize ||
!result.actions[result.stored_d1_action].legal ||
!result.actions[result.deployment_action].legal ||
publicHash(result.state) != result.stored_public_hash) {
throw std::runtime_error("panel public-state invariant failed");
}
return result;
}
std::vector<PanelRecord> loadPanels(const Options& options) {
if (options.input_sha256 != kExpectedInputSha256) {
throw std::runtime_error("frozen panel hash declaration changed");
}
std::ifstream input(options.input);
if (!input) throw std::runtime_error("could not open frozen panel corpus");
std::vector<PanelRecord> result;
std::string line;
while (std::getline(input, line)) if (!line.empty()) result.push_back(parsePanel(line));
if (result.size() != kExpectedRecords) {
throw std::runtime_error("frozen panel record count mismatch");
}
std::map<std::uint32_t, std::set<int>> moves;
for (const PanelRecord& panel : result) moves[panel.origin_game].insert(panel.move_index);
if (moves.size() != kExpectedGames) {
throw std::runtime_error("frozen panel game count mismatch");
}
for (int game = 0; game < kExpectedGames; ++game) {
const auto found = moves.find(kExpectedGameStart + game);
if (found == moves.end() || found->second.empty() ||
*found->second.begin() != 0 ||
*found->second.rbegin() + 1 != static_cast<int>(found->second.size())) {
throw std::runtime_error("panel origin-game boundary mismatch");
}
}
return result;
}
struct ExactSearch {
int d1_action = -1;
int d4_action = -1;
std::array<double, kBoardSize> d1_q{};
std::array<double, kBoardSize> d4_q{};
std::array<double, kBoardSize> d4_immediate_score{};
std::uint64_t d1_work = 0;
std::uint64_t d4_work = 0;
std::uint64_t d4_nodes = 0;
std::uint64_t d4_cache_hits = 0;
std::size_t d4_cache_entries = 0;
};
ExactSearch exactSearch(const PublicState& source) {
ExactSearch result;
result.d1_q.fill(-std::numeric_limits<double>::infinity());
bool mirrored = false;
const State canonical = detail::canonicalState(materialize(source), mirrored);
d1::SearchContext d1_context;
const d1::RootEvaluation d1_root = d1::rootDecision(canonical, 1, d1_context);
if (d1_root.action < 0 || !d1_context.cache.empty()) {
throw std::runtime_error("exact fair-D1 root did not complete");
}
result.d1_action = mirrored ? kBoardSize - 1 - d1_root.action : d1_root.action;
for (int canonical_action = 0; canonical_action < kBoardSize; ++canonical_action) {
const int source_action = mirrored ? kBoardSize - 1 - canonical_action
: canonical_action;
result.d1_q[source_action] = d1_root.values[canonical_action];
}
result.d1_work = d1_context.work;
const d4::SearchDecision decision = d4::chooseDepth4Action(materialize(source));
if (!decision.complete || decision.completed_depth != d4::kCandidateDepth ||
decision.action < 0) {
throw std::runtime_error("exact fair-D4 root did not complete");
}
result.d4_action = decision.action;
result.d4_q = decision.root_values;
result.d4_immediate_score = decision.root_expected_scores;
result.d4_work = decision.work;
result.d4_nodes = decision.nodes;
result.d4_cache_hits = decision.cache_hits;
result.d4_cache_entries = decision.cache_entries;
return result;
}
struct AuditRoot {
PanelRecord panel{};
ExactSearch search{};
};
std::vector<AuditRoot> evaluateAll(const std::vector<PanelRecord>& panels,
int threads, const Deadline& deadline) {
std::vector<AuditRoot> result(panels.size());
std::atomic<std::size_t> next{0}, completed{0};
std::mutex output_mutex;
std::vector<std::future<void>> workers;
for (int worker = 0; worker < threads; ++worker) {
workers.push_back(std::async(std::launch::async, [&] {
for (;;) {
const std::size_t index = next.fetch_add(1);
if (index >= panels.size()) return;
deadline.check();
AuditRoot root;
root.panel = panels[index];
root.search = exactSearch(root.panel.state);
if (root.search.d1_action != root.panel.stored_d1_action) {
throw std::runtime_error("stored and exact fair-D1 actions differ");
}
result[index] = std::move(root);
enforceRss();
const std::size_t done = completed.fetch_add(1) + 1;
if (done % 20 == 0 || done == panels.size()) {
const std::lock_guard<std::mutex> lock(output_mutex);
std::cerr << "D4 h200 exact audit " << done << '/' << panels.size() << '\n';
}
}
}));
}
for (auto& worker : workers) worker.get();
return result;
}
} // namespace frozen_audit
namespace audit = frozen_audit;
using PublicState = audit::PublicState;
constexpr int kRecords = 477;
constexpr int kGames = 8;
constexpr std::uint32_t kGameStart = 0x3d6d'0010u;
constexpr std::array<int, kGames> kExpectedGameRecords{{77, 50, 55, 35,
35, 65, 55, 105}};
constexpr std::string_view kCorpusSha256 =
"bfda8ae32fa0be3577c6b27f6413aba28f2854930e2f91a0dcf7674808f04196";
constexpr std::string_view kAuditSourceSha256 =
"b04e75123b7c29a350868d7ef8781256b51bc9f16878787f6d0611dc42371308";
constexpr std::string_view kD4SourceSha256 =
"1cb42629db07b17850045bf3e5678c1fed5b58c73ab38bcfb699c94ee34fe6aa";
constexpr std::string_view kD1SourceSha256 =
"f9d4ea210e282ce5cc22894c17b5be92efb12029242aa5c3c6dc6412b383f42b";
constexpr std::string_view kBehaviorSha256 =
"e5e81fa103589a9a911b6019a15aa48339c78ae2460ea0b0df4b0f66d59f27df";
constexpr std::string_view kEngineSha256 =
"b6dcde5f40dc39c6931b9a88e42bb351acd6fadaddd1e07691c41a82e44f3090";
constexpr int kPhaseFeatures = 24;
constexpr int kFeatureCount = 96;
constexpr int kHeads = 5;
constexpr int kHidden = 64;
constexpr int kBoardTokens = 10;
constexpr int kBoardCategories = kCellCount * kBoardTokens;
constexpr int kNextCategories = kBoardSize;
constexpr int kRiseCategories = kMovesPerLevel;
constexpr int kActionCategories = kBoardSize;
constexpr int kCategoryCount = kBoardCategories + kNextCategories +
kRiseCategories + kActionCategories;
constexpr int kActiveCategories = kCellCount + 3;
constexpr int kSuccessorSamples = kBoardSize;
constexpr std::uint32_t kSuccessorDomain = 0x4434'4853u;
// One architecture and training schedule fixed before evaluation. There is no
// validation-driven epoch, seed, feature, width, or loss selection.
constexpr int kEpochs = 48;
constexpr int kBatchRoots = 32;
constexpr float kLearningRate = 0.0012f;
constexpr float kWeightDecay = 1.0e-5f;
constexpr float kGradientNorm = 3.0f;
constexpr float kPairWeight = 1.0f;
constexpr float kListWeight = 0.75f;
constexpr float kPointWeight = 0.25f;
constexpr float kAuxiliaryWeight = 0.12f;
constexpr float kResidualWeight = 0.015f;
constexpr float kListTemperature = 0.35f;
constexpr std::uint32_t kNetworkSeed = 0x4434'4e4eu;
constexpr std::uint32_t kShuffleDomain = 0x4434'5348u;
constexpr double kNearTieFraction = 0.10;
constexpr double kDecisiveFraction = 0.50;
constexpr int kCalibrationBins = 10;
constexpr double kTop1Gain = 0.03;
constexpr double kPairwiseGain = 0.015;
constexpr double kRegretRatio = 0.90;
constexpr int kRequiredImprovedFolds = 6;
constexpr double kTieTolerance = 1.0e-9;
constexpr double kWallLimitSeconds = 30.0 * 60.0;
constexpr std::uint64_t kRssLimitBytes = 256ull * 1024ull * 1024ull;
constexpr std::uint64_t kCheckpointLimitBytes = 512ull * 1024ull;
constexpr std::uint64_t kCheckpointMagic = 0x4437'4832'4e4e'3031ull;
constexpr std::uint32_t kCheckpointVersion = 1;
constexpr std::uint64_t kAuditCacheMagic = 0x4437'4434'5143'3031ull;
constexpr std::uint32_t kAuditCacheVersion = 1;
static_assert(kFeatureCount == 3 * kPhaseFeatures + 24);
static_assert(kCategoryCount == 509);
static_assert(kHidden == 64 && kHeads == 5);
static_assert(audit::kExpectedRecords == kRecords);
static_assert(audit::kExpectedGames == kGames);
static_assert(audit::kExpectedInputSha256 == kCorpusSha256);
static_assert(kLevelBonus == 17'000);
struct RunOptions {
std::string input = "/tmp/drop7-terminal-policy-deployment-panels.jsonl";
std::string output = "/tmp/drop7-d4-h200-sibling-nnue.json";
std::string checkpoint = "/tmp/drop7-d4-h200-sibling-nnue.bin";
std::string golden = "/tmp/drop7-d4-h200-sibling-nnue-golden.json";
std::string readme = "/tmp/drop7-d4-h200-sibling-nnue-README.md";
std::string audit_cache = "/tmp/drop7-d4-h200-d4-audit.bin";
std::string source_sha256;
int threads = 4;
};
bool lowercaseSha256(std::string_view value) {
return value.size() == 64 &&
std::all_of(value.begin(), value.end(), [](char token) {
return (token >= '0' && token <= '9') ||
(token >= 'a' && token <= 'f');
});
}
RunOptions parseOptions(int argc, char** argv, int begin) {
RunOptions result;
for (int index = begin; index < argc; ++index) {
const std::string_view flag(argv[index]);
if (index + 1 >= argc) throw std::invalid_argument("missing option value");
const std::string value(argv[++index]);
if (flag == "--input") result.input = value;
else if (flag == "--output") result.output = value;
else if (flag == "--checkpoint") result.checkpoint = value;
else if (flag == "--golden") result.golden = value;
else if (flag == "--readme") result.readme = value;
else if (flag == "--audit-cache") result.audit_cache = value;
else if (flag == "--source-sha256") result.source_sha256 = value;
else if (flag == "--threads") result.threads = std::stoi(value);
else throw std::invalid_argument("unknown option " + std::string(flag));
}
if (!lowercaseSha256(result.source_sha256)) {
throw std::invalid_argument("--source-sha256 must be 64 lowercase hex");
}
if (result.threads < 1 || result.threads > 4 || result.input.empty() ||
result.output.empty() || result.checkpoint.empty() ||
result.golden.empty() || result.readme.empty() ||
result.audit_cache.empty()) {
throw std::invalid_argument("invalid offline residual options");
}
return result;
}
struct Deadline {
Clock::time_point started = Clock::now();
double seconds() const {
return std::chrono::duration<double>(Clock::now() - started).count();
}
void check() const {
if (seconds() > kWallLimitSeconds) {
throw std::runtime_error("D4 h200 residual exceeded 30 minute wall");
}
if (audit::peakRssBytes() > kRssLimitBytes) {
throw std::runtime_error("D4 h200 residual exceeded 256 MiB RSS");
}
}
};
std::string readWholeFile(const std::string& path) {
std::ifstream input(path, std::ios::binary);
if (!input) throw std::runtime_error("could not read " + path);
std::ostringstream output;
output << input.rdbuf();
if (!input.good() && !input.eof()) {
throw std::runtime_error("failed reading " + path);
}
return output.str();
}
constexpr std::array<std::uint32_t, 64> kSha256Constants{{
0x428a2f98u, 0x71374491u, 0xb5c0fbcfu, 0xe9b5dba5u,
0x3956c25bu, 0x59f111f1u, 0x923f82a4u, 0xab1c5ed5u,
0xd807aa98u, 0x12835b01u, 0x243185beu, 0x550c7dc3u,
0x72be5d74u, 0x80deb1feu, 0x9bdc06a7u, 0xc19bf174u,
0xe49b69c1u, 0xefbe4786u, 0x0fc19dc6u, 0x240ca1ccu,
0x2de92c6fu, 0x4a7484aau, 0x5cb0a9dcu, 0x76f988dau,
0x983e5152u, 0xa831c66du, 0xb00327c8u, 0xbf597fc7u,
0xc6e00bf3u, 0xd5a79147u, 0x06ca6351u, 0x14292967u,
0x27b70a85u, 0x2e1b2138u, 0x4d2c6dfcu, 0x53380d13u,
0x650a7354u, 0x766a0abbu, 0x81c2c92eu, 0x92722c85u,
0xa2bfe8a1u, 0xa81a664bu, 0xc24b8b70u, 0xc76c51a3u,
0xd192e819u, 0xd6990624u, 0xf40e3585u, 0x106aa070u,
0x19a4c116u, 0x1e376c08u, 0x2748774cu, 0x34b0bcb5u,
0x391c0cb3u, 0x4ed8aa4au, 0x5b9cca4fu, 0x682e6ff3u,
0x748f82eeu, 0x78a5636fu, 0x84c87814u, 0x8cc70208u,
0x90befffau, 0xa4506cebu, 0xbef9a3f7u, 0xc67178f2u,
}};
std::string sha256(std::string_view source) {
std::vector<std::uint8_t> message(source.begin(), source.end());
const std::uint64_t bit_length =
static_cast<std::uint64_t>(message.size()) * 8u;
message.push_back(0x80u);
while (message.size() % 64 != 56) message.push_back(0u);
for (int byte = 7; byte >= 0; --byte) {
message.push_back(static_cast<std::uint8_t>(bit_length >> (byte * 8)));
}
std::array<std::uint32_t, 8> hash{{
0x6a09e667u, 0xbb67ae85u, 0x3c6ef372u, 0xa54ff53au,
0x510e527fu, 0x9b05688cu, 0x1f83d9abu, 0x5be0cd19u,
}};
for (std::size_t offset = 0; offset < message.size(); offset += 64) {
std::array<std::uint32_t, 64> words{};
for (int word = 0; word < 16; ++word) {
const std::size_t at = offset + static_cast<std::size_t>(word * 4);
words[word] = (static_cast<std::uint32_t>(message[at]) << 24) |
(static_cast<std::uint32_t>(message[at + 1]) << 16) |
(static_cast<std::uint32_t>(message[at + 2]) << 8) |
static_cast<std::uint32_t>(message[at + 3]);
}
for (int word = 16; word < 64; ++word) {
const std::uint32_t s0 = std::rotr(words[word - 15], 7) ^
std::rotr(words[word - 15], 18) ^
(words[word - 15] >> 3);
const std::uint32_t s1 = std::rotr(words[word - 2], 17) ^
std::rotr(words[word - 2], 19) ^
(words[word - 2] >> 10);
words[word] = words[word - 16] + s0 + words[word - 7] + s1;
}
std::uint32_t a = hash[0], b = hash[1], c = hash[2], d = hash[3];
std::uint32_t e = hash[4], f = hash[5], g = hash[6], h = hash[7];
for (int round = 0; round < 64; ++round) {
const std::uint32_t s1 = std::rotr(e, 6) ^ std::rotr(e, 11) ^
std::rotr(e, 25);
const std::uint32_t choose = (e & f) ^ (~e & g);
const std::uint32_t first =
h + s1 + choose + kSha256Constants[round] + words[round];
const std::uint32_t s0 = std::rotr(a, 2) ^ std::rotr(a, 13) ^
std::rotr(a, 22);
const std::uint32_t majority = (a & b) ^ (a & c) ^ (b & c);
const std::uint32_t second = s0 + majority;
h = g; g = f; f = e; e = d + first;
d = c; c = b; b = a; a = first + second;
}
hash[0] += a; hash[1] += b; hash[2] += c; hash[3] += d;
hash[4] += e; hash[5] += f; hash[6] += g; hash[7] += h;
}
constexpr char digits[] = "0123456789abcdef";
std::string result;
result.reserve(64);
for (const std::uint32_t value : hash) {
for (int shift = 28; shift >= 0; shift -= 4) {
result.push_back(digits[(value >> shift) & 0xfu]);
}
}
return result;
}
std::string fileSha256(const std::string& path) {
return sha256(readWholeFile(path));
}
std::vector<audit::PanelRecord> loadLockedPanels(const RunOptions& options) {
const std::string contents = readWholeFile(options.input);
if (sha256(contents) != kCorpusSha256) {
throw std::runtime_error("frozen h200 panel checksum mismatch");
}
audit::Options inherited;
inherited.input = options.input;
inherited.input_sha256 = std::string(kCorpusSha256);
const std::vector<audit::PanelRecord> panels = audit::loadPanels(inherited);
std::array<int, kGames> counts{};
for (const audit::PanelRecord& panel : panels) {
const int game = static_cast<int>(panel.origin_game - kGameStart);
if (game < 0 || game >= kGames) {
throw std::runtime_error("out-of-domain origin in locked panel corpus");
}
++counts[game];
}
if (counts != kExpectedGameRecords) {
throw std::runtime_error("locked panel per-game counts changed");
}
return panels;
}
template <typename Value>
void writePod(std::ostream& output, const Value& value) {
static_assert(std::is_trivially_copyable_v<Value>);
output.write(reinterpret_cast<const char*>(&value), sizeof(value));
}
template <typename Value>
void readPod(std::istream& input, Value& value) {
static_assert(std::is_trivially_copyable_v<Value>);
input.read(reinterpret_cast<char*>(&value), sizeof(value));
}
struct ExactCorpus {
std::vector<audit::ExactSearch> searches;
bool cache_hit = false;
std::uint64_t work = 0;
std::uint64_t nodes = 0;
std::uint64_t cache_hits = 0;
std::size_t maximum_cache_entries = 0;
double seconds = 0.0;
};
bool loadAuditCache(const std::string& path,
const std::vector<audit::PanelRecord>& panels,
ExactCorpus& result) {
std::ifstream input(path, std::ios::binary);
if (!input) return false;
std::uint64_t magic = 0;
std::uint32_t version = 0, count = 0;
std::array<char, 64> corpus{};
std::array<char, 64> d4_source{};
readPod(input, magic); readPod(input, version); readPod(input, count);
readPod(input, corpus); readPod(input, d4_source);
if (!input || magic != kAuditCacheMagic || version != kAuditCacheVersion ||
count != panels.size() || std::string_view(corpus.data(), 64) != kCorpusSha256 ||
std::string_view(d4_source.data(), 64) != kD4SourceSha256) {
return false;
}
std::vector<audit::ExactSearch> searches(count);
for (std::size_t index = 0; index < searches.size(); ++index) {
std::uint64_t hash = 0;
readPod(input, hash);
audit::ExactSearch& value = searches[index];
readPod(input, value.d1_action); readPod(input, value.d4_action);
readPod(input, value.d1_q); readPod(input, value.d4_q);
readPod(input, value.d4_immediate_score);
readPod(input, value.d1_work); readPod(input, value.d4_work);
readPod(input, value.d4_nodes); readPod(input, value.d4_cache_hits);
readPod(input, value.d4_cache_entries);
if (!input || hash != audit::publicHash(panels[index].state) ||
value.d1_action != panels[index].stored_d1_action) return false;
for (int action = 0; action < kBoardSize; ++action) {
const bool legal = panels[index].actions[action].legal;
if (legal != std::isfinite(value.d1_q[action]) ||
legal != std::isfinite(value.d4_q[action])) return false;
}
}
char trailing = 0;
if (input.read(&trailing, 1)) return false;
result.searches = std::move(searches);
result.cache_hit = true;
return true;
}
void saveAuditCache(const std::string& path,
const std::vector<audit::PanelRecord>& panels,
const std::vector<audit::ExactSearch>& searches) {
std::ofstream output(path, std::ios::binary | std::ios::trunc);
if (!output) throw std::runtime_error("could not write exact-D4 audit cache");
const std::uint32_t count = static_cast<std::uint32_t>(searches.size());
std::array<char, 64> corpus{}, d4_source{};
std::copy(kCorpusSha256.begin(), kCorpusSha256.end(), corpus.begin());
std::copy(kD4SourceSha256.begin(), kD4SourceSha256.end(), d4_source.begin());
writePod(output, kAuditCacheMagic); writePod(output, kAuditCacheVersion);
writePod(output, count); writePod(output, corpus); writePod(output, d4_source);
for (std::size_t index = 0; index < searches.size(); ++index) {
const std::uint64_t hash = audit::publicHash(panels[index].state);
const audit::ExactSearch& value = searches[index];
writePod(output, hash);
writePod(output, value.d1_action); writePod(output, value.d4_action);
writePod(output, value.d1_q); writePod(output, value.d4_q);
writePod(output, value.d4_immediate_score);
writePod(output, value.d1_work); writePod(output, value.d4_work);
writePod(output, value.d4_nodes); writePod(output, value.d4_cache_hits);
writePod(output, value.d4_cache_entries);
}
if (!output) throw std::runtime_error("exact-D4 audit cache write failed");
}
void summarizeExact(ExactCorpus& result) {
for (const audit::ExactSearch& search : result.searches) {
result.work += search.d4_work;
result.nodes += search.d4_nodes;
result.cache_hits += search.d4_cache_hits;
result.maximum_cache_entries =
std::max(result.maximum_cache_entries, search.d4_cache_entries);
}
}
ExactCorpus exactCorpus(const RunOptions& options,
const std::vector<audit::PanelRecord>& panels,
const Deadline& deadline) {
const Clock::time_point started = Clock::now();
ExactCorpus result;
if (!loadAuditCache(options.audit_cache, panels, result)) {
audit::Deadline inherited_deadline;
const std::vector<audit::AuditRoot> roots =
audit::evaluateAll(panels, options.threads, inherited_deadline);
result.searches.reserve(roots.size());
for (const audit::AuditRoot& root : roots) {
result.searches.push_back(root.search);
}
saveAuditCache(options.audit_cache, panels, result.searches);
}
result.seconds = std::chrono::duration<double>(Clock::now() - started).count();
summarizeExact(result);
deadline.check();
return result;
}
std::array<double, kPhaseFeatures> phaseVector(const State& state) {
const detail::PhaseFeatures f = detail::extractPhaseFeatures(state);
return {{
static_cast<double>(f.open_columns), f.height_load,
static_cast<double>(f.solid_cells), static_cast<double>(f.cracked_cells),
static_cast<double>(f.numbered_cells),
static_cast<double>(f.high_low_numbers), f.direct_potential,
f.latent_chain_potential, f.cracked_exposure, f.solid_exposure,
f.adjacent_ones, f.triple_twos, f.dead_low_numbers,
f.projected_occupancy_debt, f.residual_cover_debt,
f.cover_altitude_debt, f.imminent_cover_altitude_debt,
f.peak_height_risk, f.low_cap_load, f.adjacent_low_cap_load,
f.quiet_build_options, f.quiet_direct_gain, f.trigger_readiness,
f.rise_trigger_readiness,
}};
}
struct RawAction {
int action = -1;
std::array<float, kFeatureCount> features{};
audit::PanelAction label{};
float anchor = 0.0f;
};
struct RawPanel {
std::uint32_t origin_game = 0;
int move_index = -1;
PublicState state{};
std::vector<RawAction> actions;
};
std::pair<double, double> meanAndRange(
const std::array<double, kBoardSize>& values,
const std::array<audit::PanelAction, kBoardSize>& actions) {
double mean = 0.0;
double minimum = std::numeric_limits<double>::infinity();
double maximum = -std::numeric_limits<double>::infinity();
int count = 0;
for (int action = 0; action < kBoardSize; ++action) {
if (!actions[action].legal) continue;
mean += values[action];
minimum = std::min(minimum, values[action]);
maximum = std::max(maximum, values[action]);
++count;
}
if (count < 1) throw std::runtime_error("panel has no legal actions");
return {mean / count, std::max(1.0, maximum - minimum)};
}
RawPanel prepareRawPanel(const audit::PanelRecord& panel,
const audit::ExactSearch& search) {
RawPanel result;
result.origin_game = panel.origin_game;
result.move_index = panel.move_index;
result.state = panel.state;
const State root = audit::materialize(panel.state);
const auto root_phase = phaseVector(root);
const auto heights = detail::columnHeights(root.board);
const double height_mean =
std::accumulate(heights.begin(), heights.end(), 0.0) / kBoardSize;
double height_variance = 0.0, roughness = 0.0;
int height_minimum = kBoardSize, height_maximum = 0, occupancy = 0;
for (int column = 0; column < kBoardSize; ++column) {
height_variance += (heights[column] - height_mean) *
(heights[column] - height_mean) / kBoardSize;
height_minimum = std::min(height_minimum, heights[column]);
height_maximum = std::max(height_maximum, heights[column]);
occupancy += heights[column];
if (column) roughness += std::abs(heights[column] - heights[column - 1]);
}
int legal_count = 0;
legalColumns(root.board, legal_count);
const auto [d4_mean, d4_range] = meanAndRange(search.d4_q, panel.actions);
const auto [d1_mean, d1_range] = meanAndRange(search.d1_q, panel.actions);
double best_d1 = -std::numeric_limits<double>::infinity();
for (int action = 0; action < kBoardSize; ++action) {
if (panel.actions[action].legal) best_d1 = std::max(best_d1, search.d1_q[action]);
}
const std::uint32_t seed = detail::scenarioSeedForState(
root, kSuccessorDomain, 0);
for (int action = 0; action < kBoardSize; ++action) {
if (!panel.actions[action].legal) continue;
RawAction raw;
raw.action = action;
raw.label = panel.actions[action];
raw.anchor = static_cast<float>((search.d4_q[action] - d4_mean) / d4_range);
std::array<double, kPhaseFeatures> sum{}, squares{};
double immediate_sum = 0.0, immediate_squares = 0.0;
double immediate_minimum = std::numeric_limits<double>::infinity();
double immediate_maximum = -std::numeric_limits<double>::infinity();
double wave_sum = 0.0, wave_squares = 0.0;
double clear_sum = 0.0, clear_squares = 0.0;
double reveal_sum = 0.0, reveal_squares = 0.0;
double occupancy_delta_sum = 0.0;
int terminal = 0, cleared_board = 0, level_advanced = 0;
for (int sample = 0; sample < kSuccessorSamples; ++sample) {
detail::StratifiedRandom random{seed, sample, kSuccessorSamples, 0};
MoveResult move;
if (!detail::playMoveSampled(root, action, random, move)) {
throw std::runtime_error("common immediate successor failed");
}
const double immediate = static_cast<double>(move.score_delta);
immediate_sum += immediate;
immediate_squares += immediate * immediate;
immediate_minimum = std::min(immediate_minimum, immediate);
immediate_maximum = std::max(immediate_maximum, immediate);
terminal += move.state.game_over;
cleared_board += move.cleared_board;
level_advanced += move.level_advanced;
double clears = 0.0, reveals = 0.0;
for (const Wave& wave : move.waves) {
clears += wave.cleared;
reveals += wave.revealed;
}
wave_sum += move.waves.size();
wave_squares += move.waves.size() * move.waves.size();
clear_sum += clears; clear_squares += clears * clears;
reveal_sum += reveals; reveal_squares += reveals * reveals;
State successor = move.state;
successor.score = 0;
successor.level = 1;
successor.moves_played = 0;
if (!successor.game_over) {
successor.next_disc =
detail::sampledNextDisc(seed, sample, kSuccessorSamples);
}
const auto phase = phaseVector(successor);
for (int feature = 0; feature < kPhaseFeatures; ++feature) {
sum[feature] += phase[feature];
squares[feature] += phase[feature] * phase[feature];
}
int successor_occupancy = 0;
for (std::uint8_t cell : successor.board) successor_occupancy += cell != kEmpty;
occupancy_delta_sum += successor_occupancy - occupancy;
}
int feature = 0;
for (double value : root_phase) raw.features[feature++] = value;
for (int index = 0; index < kPhaseFeatures; ++index) {
raw.features[feature++] = static_cast<float>(sum[index] / kSuccessorSamples);
}
for (int index = 0; index < kPhaseFeatures; ++index) {
const double mean = sum[index] / kSuccessorSamples;
raw.features[feature++] = static_cast<float>(std::sqrt(std::max(
0.0, squares[index] / kSuccessorSamples - mean * mean)));
}
const auto dispersion = [](double sum, double squares) {
const double mean = sum / kSuccessorSamples;
return std::sqrt(std::max(0.0, squares / kSuccessorSamples - mean * mean));
};
const double immediate_mean = immediate_sum / kSuccessorSamples;
raw.features[feature++] = raw.anchor;
raw.features[feature++] = static_cast<float>((search.d1_q[action] - d1_mean) / d1_range);
raw.features[feature++] = static_cast<float>((best_d1 - search.d1_q[action]) / d1_range);
raw.features[feature++] = static_cast<float>(search.d4_immediate_score[action] / 17'000.0);
raw.features[feature++] = static_cast<float>(immediate_mean / 17'000.0);
raw.features[feature++] = static_cast<float>(dispersion(immediate_sum, immediate_squares) / 17'000.0);
raw.features[feature++] = static_cast<float>(immediate_minimum / 17'000.0);
raw.features[feature++] = static_cast<float>(immediate_maximum / 17'000.0);
raw.features[feature++] = static_cast<float>(terminal) / kSuccessorSamples;
raw.features[feature++] = static_cast<float>(wave_sum / kSuccessorSamples);
raw.features[feature++] = static_cast<float>(dispersion(wave_sum, wave_squares));
raw.features[feature++] = static_cast<float>(clear_sum / kSuccessorSamples);
raw.features[feature++] = static_cast<float>(dispersion(clear_sum, clear_squares));
raw.features[feature++] = static_cast<float>(reveal_sum / kSuccessorSamples);
raw.features[feature++] = static_cast<float>(dispersion(reveal_sum, reveal_squares));
raw.features[feature++] = static_cast<float>(cleared_board) / kSuccessorSamples;
raw.features[feature++] = static_cast<float>(level_advanced) / kSuccessorSamples;
raw.features[feature++] = static_cast<float>(heights[action]) / kBoardSize;
raw.features[feature++] = static_cast<float>(std::abs(action - 3)) / 3.0f;
raw.features[feature++] = static_cast<float>(height_mean / kBoardSize);
raw.features[feature++] = static_cast<float>(std::sqrt(height_variance) / kBoardSize);
raw.features[feature++] = static_cast<float>(height_maximum - height_minimum) / kBoardSize;
raw.features[feature++] = static_cast<float>(roughness / 42.0);
raw.features[feature++] = static_cast<float>(occupancy_delta_sum /
(kSuccessorSamples * kCellCount));
if (feature != kFeatureCount) {
throw std::runtime_error("D4 h200 feature-count mismatch");
}
// A few aggregate PhaseFeatures traverse mirrored cells in the opposite
// addition order. Quantizing well below the normalizer's useful
// resolution removes last-bit accumulation noise, making the complete
// public feature map (not just the categorical tower) reflection exact.
for (float& value : raw.features) {
value = std::nearbyint(value * 100'000.0f) / 100'000.0f;
}
result.actions.push_back(raw);
}
return result;
}
std::vector<RawPanel> prepareRawPanels(
const std::vector<audit::PanelRecord>& panels,
const std::vector<audit::ExactSearch>& searches,
const Deadline& deadline) {
if (panels.size() != searches.size()) {
throw std::invalid_argument("panel/search count mismatch");
}
std::vector<RawPanel> result;
result.reserve(panels.size());
for (std::size_t index = 0; index < panels.size(); ++index) {
if ((index & 63u) == 0u) deadline.check();
result.push_back(prepareRawPanel(panels[index], searches[index]));
}
return result;
}
struct Normalizer {
std::array<float, kFeatureCount> mean{};
std::array<float, kFeatureCount> scale{};
std::array<float, 4> auxiliary_mean{};
std::array<float, 4> auxiliary_scale{};
std::array<float, kFeatureCount> features(
const std::array<float, kFeatureCount>& raw) const {
std::array<float, kFeatureCount> result{};
for (int index = 0; index < kFeatureCount; ++index) {
result[index] = std::clamp(
(raw[index] - mean[index]) * scale[index], -6.0f, 6.0f);
}
return result;
}
};
std::array<double, 4> auxiliaryTargets(const audit::PanelAction& action) {
return {{action.mean_moves, action.mean_clears, action.mean_reveals,
action.material_downside_upper99}};
}
Normalizer fitNormalizer(const std::vector<RawPanel>& panels,
const std::vector<std::size_t>& indices) {
if (indices.empty()) throw std::invalid_argument("empty normalization fold");
std::array<double, kFeatureCount> sum{}, squares{};
std::array<double, 4> aux_sum{}, aux_squares{};
std::uint64_t count = 0;
for (std::size_t panel_index : indices) {
for (const RawAction& action : panels.at(panel_index).actions) {
for (int feature = 0; feature < kFeatureCount; ++feature) {
sum[feature] += action.features[feature];
squares[feature] += static_cast<double>(action.features[feature]) *
action.features[feature];
}
const auto targets = auxiliaryTargets(action.label);
for (int target = 0; target < 4; ++target) {
aux_sum[target] += targets[target];
aux_squares[target] += targets[target] * targets[target];
}
++count;
}
}
Normalizer result;
const auto fit = [count](double sum, double squares, float& mean,
float& scale) {
const double center = sum / count;
const double variance = std::max(1.0e-6, squares / count - center * center);
mean = static_cast<float>(center);
scale = static_cast<float>(1.0 / std::sqrt(variance));
};
for (int feature = 0; feature < kFeatureCount; ++feature) {
fit(sum[feature], squares[feature], result.mean[feature],
result.scale[feature]);
}
for (int target = 0; target < 4; ++target) {
fit(aux_sum[target], aux_squares[target], result.auxiliary_mean[target],
result.auxiliary_scale[target]);
}
return result;
}
struct PreparedAction {
int action = -1;
std::array<float, kFeatureCount> features{};
std::array<float, kHeads> targets{};
double score = 0.0;
float anchor = 0.0f;
};
struct PreparedPanel {
PublicState state{};
std::vector<PreparedAction> actions;
};
std::vector<PreparedPanel> preparePanels(
const std::vector<RawPanel>& source,
const std::vector<std::size_t>& indices,
const Normalizer& normalizer) {
std::vector<PreparedPanel> result;
result.reserve(indices.size());
for (std::size_t index : indices) {
const RawPanel& raw = source.at(index);
PreparedPanel panel;
panel.state = raw.state;
double mean = 0.0;
double minimum = std::numeric_limits<double>::infinity();
double maximum = -std::numeric_limits<double>::infinity();
for (const RawAction& action : raw.actions) {
mean += action.label.mean_score / raw.actions.size();
minimum = std::min(minimum, action.label.mean_score);
maximum = std::max(maximum, action.label.mean_score);
}
const double range = std::max(1.0, maximum - minimum);
for (const RawAction& raw_action : raw.actions) {
PreparedAction action;
action.action = raw_action.action;
action.features = normalizer.features(raw_action.features);
const float score_target = static_cast<float>(
(raw_action.label.mean_score - mean) / range);
action.targets[0] = score_target - raw_action.anchor;
const auto auxiliary = auxiliaryTargets(raw_action.label);
for (int target = 0; target < 4; ++target) {
action.targets[target + 1] = static_cast<float>(
(auxiliary[target] - normalizer.auxiliary_mean[target]) *
normalizer.auxiliary_scale[target]);
}
action.score = raw_action.label.mean_score;
action.anchor = raw_action.anchor;
panel.actions.push_back(action);
}
result.push_back(std::move(panel));
}
return result;
}
struct Layout {
int embedding = 0;
int numeric = kCategoryCount * kHidden;
int bias = numeric + kFeatureCount * kHidden;
int output = bias + kHidden;
int output_bias = output + kHeads * kHidden;
int count = output_bias + kHeads;
};
struct OrientationCache {
std::array<int, kActiveCategories> categories{};
std::array<float, kHidden> pre{};
std::array<float, kHidden> hidden{};
std::array<float, kHeads> output{};
};
struct ForwardCache {
OrientationCache direct{};
OrientationCache reflected{};
std::array<float, kHeads> output{};
};
class Network {
public:
explicit Network(std::uint32_t seed)
: parameters_(layout_.count), first_(layout_.count), second_(layout_.count) {
Mulberry32 random(seed);
for (int index = layout_.embedding; index < layout_.numeric; ++index) {
parameters_[index] = static_cast<float>((2.0 * random.nextUnit() - 1.0) * 0.035);
}
const float radius = std::sqrt(6.0f / (kFeatureCount + kHidden));
for (int index = layout_.numeric; index < layout_.bias; ++index) {
parameters_[index] = static_cast<float>((2.0 * random.nextUnit() - 1.0) * radius);
}
// The primary head starts at an exact zero residual, so the untrained
// model is exactly always-D4. Auxiliary heads retain small Xavier output
// weights and cannot affect the primary value directly.
const float output_radius = std::sqrt(6.0f / (kHidden + kHeads));
for (int head = 1; head < kHeads; ++head) {
for (int hidden = 0; hidden < kHidden; ++hidden) {
parameters_[layout_.output + head * kHidden + hidden] =
static_cast<float>((2.0 * random.nextUnit() - 1.0) * output_radius);
}
}
}
OrientationCache forwardOrientation(
const PublicState& state, int action,
const std::array<float, kFeatureCount>& features) const {
if (state.terminal || !isLegal(state.board, action)) {
throw std::invalid_argument("invalid public action for NNUE");
}
OrientationCache cache;
int active = 0;
for (int cell = 0; cell < kCellCount; ++cell) {
cache.categories[active++] = cell * kBoardTokens + state.board[cell];
}
cache.categories[active++] = kBoardCategories + state.next_disc - 1;
cache.categories[active++] =
kBoardCategories + kNextCategories + state.moves_remaining - 1;
cache.categories[active++] =
kBoardCategories + kNextCategories + kRiseCategories + action;
if (active != kActiveCategories) throw std::runtime_error("category mismatch");
const float category_scale = 1.0f / std::sqrt(kActiveCategories);
const float numeric_scale = 1.0f / std::sqrt(kFeatureCount);
for (int hidden = 0; hidden < kHidden; ++hidden) {
float value = parameters_[layout_.bias + hidden];
for (int category : cache.categories) {
value += category_scale *
parameters_[layout_.embedding + category * kHidden + hidden];
}
for (int feature = 0; feature < kFeatureCount; ++feature) {
value += numeric_scale * features[feature] *
parameters_[layout_.numeric + feature * kHidden + hidden];
}
cache.pre[hidden] = value;
cache.hidden[hidden] = std::clamp(value, 0.0f, 1.0f);
}
for (int head = 0; head < kHeads; ++head) {
float value = parameters_[layout_.output_bias + head];
for (int hidden = 0; hidden < kHidden; ++hidden) {
value += parameters_[layout_.output + head * kHidden + hidden] *
cache.hidden[hidden];
}
cache.output[head] = value;
}
return cache;
}
ForwardCache forward(const PublicState& state, int action,
const std::array<float, kFeatureCount>& features) const {
ForwardCache result;
result.direct = forwardOrientation(state, action, features);
result.reflected = forwardOrientation(
audit::mirror(state), kBoardSize - 1 - action, features);
for (int head = 0; head < kHeads; ++head) {
result.output[head] =
0.5f * (result.direct.output[head] + result.reflected.output[head]);
}
return result;
}
std::array<float, kHeads> predict(
const PublicState& state, int action,
const std::array<float, kFeatureCount>& features) const {
return forward(state, action, features).output;
}
void accumulateOrientation(
const std::array<float, kFeatureCount>& features,
const OrientationCache& cache,
const std::array<float, kHeads>& derivative,
std::vector<float>& gradient) const {
std::array<float, kHidden> hidden_derivative{};
for (int head = 0; head < kHeads; ++head) {
gradient[layout_.output_bias + head] += derivative[head];
for (int hidden = 0; hidden < kHidden; ++hidden) {
const int index = layout_.output + head * kHidden + hidden;
gradient[index] += derivative[head] * cache.hidden[hidden];
hidden_derivative[hidden] += derivative[head] * parameters_[index];
}
}
const float category_scale = 1.0f / std::sqrt(kActiveCategories);
const float numeric_scale = 1.0f / std::sqrt(kFeatureCount);
for (int hidden = 0; hidden < kHidden; ++hidden) {
const float value = cache.pre[hidden] > 0.0f && cache.pre[hidden] < 1.0f
? hidden_derivative[hidden] : 0.0f;
gradient[layout_.bias + hidden] += value;
for (int category : cache.categories) {
gradient[layout_.embedding + category * kHidden + hidden] +=
category_scale * value;
}
for (int feature = 0; feature < kFeatureCount; ++feature) {
gradient[layout_.numeric + feature * kHidden + hidden] +=
numeric_scale * features[feature] * value;
}
}
}
void backpropagate(const PreparedAction& action,
const ForwardCache& cache,
const std::array<float, kHeads>& derivative,
std::vector<float>& gradient) const {
std::array<float, kHeads> half{};
for (int head = 0; head < kHeads; ++head) half[head] = 0.5f * derivative[head];
accumulateOrientation(action.features, cache.direct, half, gradient);
accumulateOrientation(action.features, cache.reflected, half, gradient);
}
std::vector<float> gradient() const {
return std::vector<float>(parameters_.size(), 0.0f);
}
void apply(std::vector<float>& gradient) {
double squared_norm = 0.0;
for (int index = 0; index < layout_.count; ++index) {
const bool decay = index < layout_.bias ||
(index >= layout_.output && index < layout_.output_bias);
if (decay) gradient[index] += kWeightDecay * parameters_[index];
squared_norm += static_cast<double>(gradient[index]) * gradient[index];
}
const double norm = std::sqrt(squared_norm);
const float clipping = norm > kGradientNorm
? static_cast<float>(kGradientNorm / norm) : 1.0f;
++step_;
constexpr float beta1 = 0.9f, beta2 = 0.999f, epsilon = 1.0e-8f;
const float correction1 = 1.0f - std::pow(beta1, static_cast<float>(step_));
const float correction2 = 1.0f - std::pow(beta2, static_cast<float>(step_));
for (int index = 0; index < layout_.count; ++index) {
const float value = clipping * gradient[index];
first_[index] = beta1 * first_[index] + (1.0f - beta1) * value;
second_[index] = beta2 * second_[index] + (1.0f - beta2) * value * value;
parameters_[index] -= kLearningRate * (first_[index] / correction1) /
(std::sqrt(second_[index] / correction2) + epsilon);
if (!std::isfinite(parameters_[index])) {
throw std::runtime_error("non-finite NNUE parameter");
}
}
}
const std::vector<float>& parameters() const { return parameters_; }
void setParameters(const std::vector<float>& values) {
if (values.size() != parameters_.size()) {
throw std::invalid_argument("NNUE parameter count mismatch");
}
parameters_ = values;
std::fill(first_.begin(), first_.end(), 0.0f);
std::fill(second_.begin(), second_.end(), 0.0f);
step_ = 0;
}
int parameterCount() const { return layout_.count; }
private:
Layout layout_{};
std::vector<float> parameters_, first_, second_;
std::uint64_t step_ = 0;
};
using PublicEvaluator = std::array<float, kHeads> (Network::*)(
const PublicState&, int,
const std::array<float, kFeatureCount>&) const;
static_assert(std::is_same_v<decltype(&Network::predict), PublicEvaluator>);
static_assert(!std::is_invocable_v<PublicEvaluator, const Network&, const State&,
int, const std::array<float, kFeatureCount>&>);
double sigmoid(double value) {
if (value >= 0.0) return 1.0 / (1.0 + std::exp(-value));
const double e = std::exp(value);
return e / (1.0 + e);
}
double softplus(double value) {
if (value > 30.0) return value;
if (value < -30.0) return std::exp(value);
return std::log1p(std::exp(value));
}
void accumulatePanel(const Network& network, const PreparedPanel& panel,
float inverse_batch, std::vector<float>& gradient,
double& loss) {
const std::size_t count = panel.actions.size();
std::vector<ForwardCache> caches;
caches.reserve(count);
std::vector<std::array<float, kHeads>> derivatives(count);
std::vector<double> prediction(count), target(count);
double minimum = std::numeric_limits<double>::infinity();
double maximum = -std::numeric_limits<double>::infinity();
int pairs = 0;
for (std::size_t index = 0; index < count; ++index) {
const PreparedAction& action = panel.actions[index];
caches.push_back(network.forward(panel.state, action.action, action.features));
prediction[index] = action.anchor + caches.back().output[0];
target[index] = action.anchor + action.targets[0];
minimum = std::min(minimum, action.score);
maximum = std::max(maximum, action.score);
}
for (std::size_t first = 0; first < count; ++first) {
for (std::size_t second = first + 1; second < count; ++second) {
pairs += std::abs(panel.actions[first].score - panel.actions[second].score) >
kTieTolerance;
}
}
const double score_range = std::max(1.0, maximum - minimum);
if (pairs) {
for (std::size_t first = 0; first < count; ++first) {
for (std::size_t second = first + 1; second < count; ++second) {
const double difference =
panel.actions[first].score - panel.actions[second].score;
if (std::abs(difference) <= kTieTolerance) continue;
const double sign = difference > 0.0 ? 1.0 : -1.0;
const double margin = sign * (prediction[first] - prediction[second]);
const double importance = 0.25 + 0.75 * std::abs(difference) / score_range;
const double weight = kPairWeight * importance / pairs;
loss += weight * softplus(-margin);
const float derivative = static_cast<float>(-weight * sigmoid(-margin));
derivatives[first][0] += static_cast<float>(sign) * derivative;
derivatives[second][0] -= static_cast<float>(sign) * derivative;
}
}
}
double predicted_max = -std::numeric_limits<double>::infinity();
double target_max = -std::numeric_limits<double>::infinity();
for (std::size_t index = 0; index < count; ++index) {
prediction[index] /= kListTemperature;
target[index] /= kListTemperature;
predicted_max = std::max(predicted_max, prediction[index]);
target_max = std::max(target_max, target[index]);
}
double predicted_sum = 0.0, target_sum = 0.0;
for (std::size_t index = 0; index < count; ++index) {
prediction[index] = std::exp(prediction[index] - predicted_max);
target[index] = std::exp(target[index] - target_max);
predicted_sum += prediction[index]; target_sum += target[index];
}
for (std::size_t index = 0; index < count; ++index) {
const double probability = prediction[index] / predicted_sum;
const double truth = target[index] / target_sum;
loss -= kListWeight * truth * std::log(std::max(1.0e-12, probability));
derivatives[index][0] += static_cast<float>(
kListWeight * (probability - truth) / kListTemperature);
}
for (std::size_t index = 0; index < count; ++index) {
const PreparedAction& action = panel.actions[index];
const float residual = caches[index].output[0];
const float error = residual - action.targets[0];
loss += 0.5 * kPointWeight * error * error / count;
derivatives[index][0] += kPointWeight * error / count;
loss += 0.5 * kResidualWeight * residual * residual / count;
derivatives[index][0] += kResidualWeight * residual / count;
for (int head = 1; head < kHeads; ++head) {
const float auxiliary_error = caches[index].output[head] - action.targets[head];
loss += 0.5 * kAuxiliaryWeight * auxiliary_error * auxiliary_error / count;
derivatives[index][head] += kAuxiliaryWeight * auxiliary_error / count;
}
}
for (std::size_t index = 0; index < count; ++index) {
for (float& value : derivatives[index]) value *= inverse_batch;
network.backpropagate(panel.actions[index], caches[index],
derivatives[index], gradient);
}
}
struct TrainingResult {
Network network;
double first_loss = 0.0;
double final_loss = 0.0;
explicit TrainingResult(std::uint32_t seed) : network(seed) {}
};
TrainingResult train(const std::vector<PreparedPanel>& panels,
std::uint32_t seed, int epochs,
const Deadline& deadline, bool progress) {
TrainingResult result(seed);
std::vector<std::size_t> order(panels.size());
std::iota(order.begin(), order.end(), 0u);
for (int epoch = 0; epoch < epochs; ++epoch) {
Mulberry32 random(mix32(kShuffleDomain ^ seed ^
static_cast<std::uint32_t>(epoch + 1)));
for (std::size_t cursor = order.size(); cursor > 1; --cursor) {
const std::size_t selected = static_cast<std::size_t>(
(static_cast<std::uint64_t>(random.nextBits()) * cursor) >> 32u);
std::swap(order[cursor - 1], order[selected]);
}
double loss = 0.0;
for (std::size_t begin = 0; begin < order.size(); begin += kBatchRoots) {
if ((begin & 255u) == 0u) deadline.check();
const std::size_t end = std::min(order.size(), begin + kBatchRoots);
std::vector<float> gradient = result.network.gradient();
const float inverse = 1.0f / static_cast<float>(end - begin);
for (std::size_t at = begin; at < end; ++at) {
accumulatePanel(result.network, panels[order[at]], inverse, gradient, loss);
}
result.network.apply(gradient);
}
loss /= panels.size();
if (epoch == 0) result.first_loss = loss;
result.final_loss = loss;
if (progress && ((epoch + 1) % 12 == 0 || epoch == 0)) {
std::cerr << "D4_H200_NNUE_TRAIN {\"epoch\":" << epoch + 1
<< ",\"loss\":" << loss << ",\"rss\":"
<< audit::peakRssBytes() << "}\n";
}
}
return result;
}
struct RankingAccumulator {
int roots = 0;
int pairs = 0;
double top1 = 0.0;
double top2 = 0.0;
double pairwise = 0.0;
double regret = 0.0;
void merge(const RankingAccumulator& other) {
roots += other.roots; pairs += other.pairs; top1 += other.top1;
top2 += other.top2; pairwise += other.pairwise; regret += other.regret;
}
};
struct RankingMetrics {
int roots = 0;
int pairs = 0;
double top1 = 0.0;
double top2 = 0.0;
double pairwise = 0.0;
double normalized_regret = 0.0;
};
void observeRanking(RankingAccumulator& result,
const std::vector<double>& prediction,
const std::vector<double>& target) {
std::vector<int> order(prediction.size());
std::iota(order.begin(), order.end(), 0);
std::stable_sort(order.begin(), order.end(), [&](int left, int right) {
return prediction[left] > prediction[right];
});
const auto [minimum, maximum] = std::minmax_element(target.begin(), target.end());
const auto best = [&](int index) {
return target[index] + kTieTolerance >= *maximum;
};
result.top1 += best(order[0]);
result.top2 += best(order[0]) || (order.size() > 1 && best(order[1]));
const double range = *maximum - *minimum;
if (range > kTieTolerance) {
result.regret += (*maximum - target[order[0]]) / range;
}
for (std::size_t first = 0; first < target.size(); ++first) {
for (std::size_t second = first + 1; second < target.size(); ++second) {
const double truth = target[first] - target[second];
const double guessed = prediction[first] - prediction[second];
result.pairwise +=
std::abs(truth) <= kTieTolerance || std::abs(guessed) <= 1.0e-12
? 0.5 : ((truth > 0.0) == (guessed > 0.0));
++result.pairs;
}
}
++result.roots;
}
RankingMetrics finish(const RankingAccumulator& source) {
return {source.roots, source.pairs,
source.top1 / std::max(1, source.roots),
source.top2 / std::max(1, source.roots),
source.pairwise / std::max(1, source.pairs),
source.regret / std::max(1, source.roots)};
}
struct CalibrationAccumulator {
int pairs = 0;
double correct = 0.0;
double baseline_correct = 0.0;
double brier = 0.0;
double confidence = 0.0;
std::array<int, kCalibrationBins> counts{};
std::array<double, kCalibrationBins> correct_by_bin{};
std::array<double, kCalibrationBins> confidence_by_bin{};
void merge(const CalibrationAccumulator& other) {
pairs += other.pairs; correct += other.correct;
baseline_correct += other.baseline_correct; brier += other.brier;
confidence += other.confidence;
for (int bin = 0; bin < kCalibrationBins; ++bin) {
counts[bin] += other.counts[bin];
correct_by_bin[bin] += other.correct_by_bin[bin];
confidence_by_bin[bin] += other.confidence_by_bin[bin];
}
}
};
struct CalibrationMetrics {
int pairs = 0;
double accuracy = 0.0;
double baseline_accuracy = 0.0;
double brier = 0.0;
double mean_confidence = 0.0;
double ece = 0.0;
};
void observeCalibration(CalibrationAccumulator& result,
double first, double second,
double baseline_first, double baseline_second,
double truth_first, double truth_second) {
const double truth_difference = truth_first - truth_second;
if (std::abs(truth_difference) <= kTieTolerance) return;
const double probability = sigmoid(first - second);
const bool truth = truth_difference > 0.0;
const bool choice = probability >= 0.5;
const double correct = choice == truth;
const double baseline_difference = baseline_first - baseline_second;
const double baseline_correct = std::abs(baseline_difference) <= 1.0e-12
? 0.5
: ((baseline_difference > 0.0) == truth);
const double confidence = std::max(probability, 1.0 - probability);
const int bin = std::clamp(static_cast<int>(
(confidence - 0.5) * 2.0 * kCalibrationBins), 0, kCalibrationBins - 1);
++result.pairs; result.correct += correct;
result.baseline_correct += baseline_correct;
const double error = probability - static_cast<double>(truth);
result.brier += error * error; result.confidence += confidence;
++result.counts[bin]; result.correct_by_bin[bin] += correct;
result.confidence_by_bin[bin] += confidence;
}
CalibrationMetrics finish(const CalibrationAccumulator& source) {
CalibrationMetrics result;
result.pairs = source.pairs;
if (!source.pairs) return result;
result.accuracy = source.correct / source.pairs;
result.baseline_accuracy = source.baseline_correct / source.pairs;
result.brier = source.brier / source.pairs;
result.mean_confidence = source.confidence / source.pairs;
for (int bin = 0; bin < kCalibrationBins; ++bin) {
if (!source.counts[bin]) continue;
result.ece += static_cast<double>(source.counts[bin]) / source.pairs *
std::abs(source.correct_by_bin[bin] / source.counts[bin] -
source.confidence_by_bin[bin] / source.counts[bin]);
}
return result;
}
struct EvaluationAccumulator {
RankingAccumulator candidate;
RankingAccumulator baseline;
CalibrationAccumulator near_tie;
CalibrationAccumulator decisive;
void merge(const EvaluationAccumulator& other) {
candidate.merge(other.candidate); baseline.merge(other.baseline);
near_tie.merge(other.near_tie); decisive.merge(other.decisive);
}
};
struct Evaluation {
RankingMetrics candidate;
RankingMetrics baseline;
CalibrationMetrics near_tie;
CalibrationMetrics decisive;
};
Evaluation finish(const EvaluationAccumulator& source) {
return {finish(source.candidate), finish(source.baseline),
finish(source.near_tie), finish(source.decisive)};
}
EvaluationAccumulator evaluate(const Network& network,
const Normalizer& normalizer,
const std::vector<RawPanel>& panels,
const std::vector<std::size_t>& indices,
const Deadline& deadline) {
EvaluationAccumulator result;
for (std::size_t offset = 0; offset < indices.size(); ++offset) {
if ((offset & 63u) == 0u) deadline.check();
const RawPanel& panel = panels.at(indices[offset]);
std::vector<double> candidate, baseline, target;
for (const RawAction& action : panel.actions) {
const auto features = normalizer.features(action.features);
baseline.push_back(action.anchor);
candidate.push_back(action.anchor +
network.predict(panel.state, action.action, features)[0]);
target.push_back(action.label.mean_score);
}
observeRanking(result.candidate, candidate, target);
observeRanking(result.baseline, baseline, target);
const auto [minimum, maximum] = std::minmax_element(target.begin(), target.end());
const double range = *maximum - *minimum;
if (range <= kTieTolerance) continue;
for (std::size_t first = 0; first < target.size(); ++first) {
for (std::size_t second = first + 1; second < target.size(); ++second) {
const double fraction = std::abs(target[first] - target[second]) / range;
if (fraction <= kNearTieFraction) {
observeCalibration(result.near_tie, candidate[first], candidate[second],
baseline[first], baseline[second], target[first], target[second]);
}
if (fraction >= kDecisiveFraction) {
observeCalibration(result.decisive, candidate[first], candidate[second],
baseline[first], baseline[second], target[first], target[second]);
}
}
}
}
return result;
}
std::vector<std::size_t> indicesFor(const std::vector<RawPanel>& panels,
int heldout_game, bool training) {
std::vector<std::size_t> result;
for (std::size_t index = 0; index < panels.size(); ++index) {
const int game = static_cast<int>(panels[index].origin_game - kGameStart);
if ((game == heldout_game) != training) result.push_back(index);
}
return result;
}
std::vector<std::size_t> allIndices(std::size_t size) {
std::vector<std::size_t> result(size);
std::iota(result.begin(), result.end(), 0u);
return result;
}
struct FoldResult {
int heldout_game = 0;
int training_roots = 0;
int validation_roots = 0;
double first_loss = 0.0;
double final_loss = 0.0;
Evaluation evaluation{};
bool improves_pairwise_regret_without_top1_regression = false;
};
struct CrossValidation {
std::array<FoldResult, kGames> folds{};
Evaluation overall{};
std::array<Evaluation, 2> halves{};
int improved_folds = 0;
bool passed = false;
};
bool pairwiseRegretImproves(const Evaluation& value) {
return value.candidate.pairwise > value.baseline.pairwise + kTieTolerance &&
value.candidate.normalized_regret + kTieTolerance <
value.baseline.normalized_regret;
}
CrossValidation crossValidate(const std::vector<RawPanel>& panels,
const Deadline& deadline) {
CrossValidation result;
EvaluationAccumulator overall;
std::array<EvaluationAccumulator, 2> halves{};
for (int fold = 0; fold < kGames; ++fold) {
const auto training_indices = indicesFor(panels, fold, true);
const auto validation_indices = indicesFor(panels, fold, false);
const Normalizer normalizer = fitNormalizer(panels, training_indices);
const auto training_panels =
preparePanels(panels, training_indices, normalizer);
const TrainingResult trained = train(
training_panels, mix32(kNetworkSeed ^ static_cast<std::uint32_t>(fold + 1)),
kEpochs, deadline, false);
const EvaluationAccumulator validation =
evaluate(trained.network, normalizer, panels, validation_indices, deadline);
overall.merge(validation);
halves[fold / 4].merge(validation);
FoldResult& record = result.folds[fold];
record.heldout_game = fold;
record.training_roots = training_indices.size();
record.validation_roots = validation_indices.size();
record.first_loss = trained.first_loss;
record.final_loss = trained.final_loss;
record.evaluation = finish(validation);
record.improves_pairwise_regret_without_top1_regression =
pairwiseRegretImproves(record.evaluation) &&
record.evaluation.candidate.top1 + kTieTolerance >=
record.evaluation.baseline.top1;
result.improved_folds +=
record.improves_pairwise_regret_without_top1_regression;
std::cerr << std::setprecision(8)
<< "D4_H200_NNUE_FOLD {\"heldoutOrigin\":\""
<< audit::hex64(kGameStart + fold) << "\",\"candidateTop1\":"
<< record.evaluation.candidate.top1
<< ",\"baselineTop1\":" << record.evaluation.baseline.top1
<< ",\"candidatePairwise\":" << record.evaluation.candidate.pairwise
<< ",\"baselinePairwise\":" << record.evaluation.baseline.pairwise
<< ",\"candidateRegret\":"
<< record.evaluation.candidate.normalized_regret
<< ",\"baselineRegret\":"
<< record.evaluation.baseline.normalized_regret << "}\n";
}
result.overall = finish(overall);
for (int half = 0; half < 2; ++half) result.halves[half] = finish(halves[half]);
const bool overall_gate =
result.overall.candidate.top1 >= result.overall.baseline.top1 + kTop1Gain &&
result.overall.candidate.pairwise >=
result.overall.baseline.pairwise + kPairwiseGain &&
result.overall.candidate.normalized_regret <=
kRegretRatio * result.overall.baseline.normalized_regret;
result.passed = overall_gate && result.improved_folds >= kRequiredImprovedFolds &&
pairwiseRegretImproves(result.halves[0]) &&
pairwiseRegretImproves(result.halves[1]);
return result;
}
void fingerprintFloat(std::uint64_t& hash, float value) {
std::uint32_t bits = std::bit_cast<std::uint32_t>(value);
for (int byte = 0; byte < 4; ++byte) {
hash ^= static_cast<std::uint8_t>(bits >> (byte * 8));
hash *= 0x0000'0100'0000'01b3ull;
}
}
std::uint64_t fingerprint(const Network& network, const Normalizer& normalizer) {
std::uint64_t hash = 0xcbf2'9ce4'8422'2325ull;
for (float value : network.parameters()) fingerprintFloat(hash, value);
for (float value : normalizer.mean) fingerprintFloat(hash, value);
for (float value : normalizer.scale) fingerprintFloat(hash, value);
for (float value : normalizer.auxiliary_mean) fingerprintFloat(hash, value);
for (float value : normalizer.auxiliary_scale) fingerprintFloat(hash, value);
return hash;
}
std::uint64_t checkpointBytes(const Network& network) {
return sizeof(kCheckpointMagic) + 5 * sizeof(std::uint32_t) + 64 +
sizeof(std::uint64_t) + sizeof(Normalizer) +
network.parameters().size() * sizeof(float);
}
void saveCheckpoint(const std::string& path, const Network& network,
const Normalizer& normalizer) {
if (checkpointBytes(network) > kCheckpointLimitBytes) {
throw std::runtime_error("NNUE checkpoint exceeds 512 KiB");
}
std::ofstream output(path, std::ios::binary | std::ios::trunc);
if (!output) throw std::runtime_error("could not write NNUE checkpoint");
const std::uint32_t feature_count = kFeatureCount, hidden = kHidden,
heads = kHeads, category_count = kCategoryCount,
parameter_count = network.parameterCount();
std::array<char, 64> corpus{};
std::copy(kCorpusSha256.begin(), kCorpusSha256.end(), corpus.begin());
const std::uint64_t model_fingerprint = fingerprint(network, normalizer);
writePod(output, kCheckpointMagic); writePod(output, kCheckpointVersion);
writePod(output, feature_count); writePod(output, hidden); writePod(output, heads);
writePod(output, category_count); writePod(output, parameter_count);
writePod(output, corpus); writePod(output, model_fingerprint);
writePod(output, normalizer);
output.write(reinterpret_cast<const char*>(network.parameters().data()),
network.parameters().size() * sizeof(float));
if (!output) throw std::runtime_error("NNUE checkpoint write failed");
}
struct FrozenModel {
Normalizer normalizer{};
Network network{kNetworkSeed};
};
FrozenModel loadCheckpoint(const std::string& path) {
std::ifstream input(path, std::ios::binary);
if (!input) throw std::runtime_error("could not read NNUE checkpoint");
std::uint64_t magic = 0, expected_fingerprint = 0;
std::uint32_t version = 0, feature_count = 0, hidden = 0, heads = 0,
categories = 0, parameters = 0;
std::array<char, 64> corpus{};
readPod(input, magic); readPod(input, version); readPod(input, feature_count);
readPod(input, hidden); readPod(input, heads); readPod(input, categories);
readPod(input, parameters); readPod(input, corpus);
readPod(input, expected_fingerprint);
if (!input || magic != kCheckpointMagic || version != kCheckpointVersion ||
feature_count != kFeatureCount || hidden != kHidden || heads != kHeads ||
categories != kCategoryCount ||
std::string_view(corpus.data(), 64) != kCorpusSha256) {
throw std::runtime_error("invalid NNUE checkpoint metadata");
}
FrozenModel result;
if (parameters != static_cast<std::uint32_t>(
result.network.parameterCount())) {
throw std::runtime_error("invalid NNUE checkpoint parameter count");
}
readPod(input, result.normalizer);
std::vector<float> values(parameters);
input.read(reinterpret_cast<char*>(values.data()), values.size() * sizeof(float));
char trailing = 0;
if (!input || input.read(&trailing, 1)) {
throw std::runtime_error("invalid NNUE checkpoint payload");
}
result.network.setParameters(values);
if (fingerprint(result.network, result.normalizer) != expected_fingerprint ||
std::filesystem::file_size(path) > kCheckpointLimitBytes) {
throw std::runtime_error("NNUE checkpoint fingerprint/resource mismatch");
}
return result;
}
void writeRanking(std::ostream& output, const RankingMetrics& value) {
output << "{\"roots\":" << value.roots << ",\"pairs\":" << value.pairs
<< ",\"top1\":" << value.top1 << ",\"top2\":" << value.top2
<< ",\"pairwise\":" << value.pairwise
<< ",\"normalizedRegret\":" << value.normalized_regret << '}';
}
void writeCalibration(std::ostream& output, const CalibrationMetrics& value) {
output << "{\"pairs\":" << value.pairs << ",\"accuracy\":"
<< value.accuracy << ",\"alwaysD4Accuracy\":"
<< value.baseline_accuracy << ",\"brier\":" << value.brier
<< ",\"meanConfidence\":" << value.mean_confidence
<< ",\"ece\":" << value.ece << '}';
}
void writeEvaluation(std::ostream& output, const Evaluation& value) {
output << "{\"candidate\":"; writeRanking(output, value.candidate);
output << ",\"alwaysD4\":"; writeRanking(output, value.baseline);
output << ",\"calibration\":{\"nearTie\":";
writeCalibration(output, value.near_tie);
output << ",\"decisive\":"; writeCalibration(output, value.decisive);
output << "}}";
}
void writeGolden(const std::string& path, const FrozenModel& model,
const std::vector<RawPanel>& panels) {
std::ofstream output(path, std::ios::trunc);
if (!output) throw std::runtime_error("could not write NNUE golden file");
output << std::setprecision(12)
<< "{\n \"format\":\"drop7-d4-h200-sibling-nnue-golden-v1\","
<< "\n \"modelFingerprint\":\"" << audit::hex64(
fingerprint(model.network, model.normalizer))
<< "\",\n \"cases\":[";
for (int fixture = 0; fixture < 4; ++fixture) {
const RawPanel& panel = panels[static_cast<std::size_t>(fixture) *
panels.size() / 4];
if (fixture) output << ',';
output << "{\"publicHash\":\"" << audit::hex64(audit::publicHash(panel.state))
<< "\",\"actions\":[";
for (std::size_t index = 0; index < panel.actions.size(); ++index) {
if (index) output << ',';
const RawAction& action = panel.actions[index];
const auto features = model.normalizer.features(action.features);
const double direct = action.anchor +
model.network.predict(panel.state, action.action, features)[0];
const double reflected = action.anchor + model.network.predict(
audit::mirror(panel.state), kBoardSize - 1 - action.action,
features)[0];
if (direct != reflected) {
throw std::runtime_error("golden reflection exactness failed");
}
output << "{\"action\":" << action.action << ",\"d4Anchor\":"
<< action.anchor << ",\"value\":" << direct
<< ",\"reflectedValue\":" << reflected << '}';
}
output << "]}";
}
output << "]\n}\n";
if (!output) throw std::runtime_error("NNUE golden write failed");
}
void writeArtifact(const RunOptions& options, const ExactCorpus& exact,
const CrossValidation& cv, const TrainingResult& final,
const Normalizer& normalizer, double feature_seconds,
double cv_seconds, double final_seconds,
double wall_seconds) {
const std::string checkpoint_sha = fileSha256(options.checkpoint);
const std::string golden_sha = fileSha256(options.golden);
const std::string cache_sha = fileSha256(options.audit_cache);
std::ofstream output(options.output, std::ios::trunc);
if (!output) throw std::runtime_error("could not write NNUE artifact");
output << std::setprecision(12)
<< "{\n \"experiment\":\"exact-D4-anchored-h200-sibling-NNUE\","
<< "\n \"scope\":{\"offlineOnly\":true,\"gameplaySeedsOpened\":0,"
"\"newPanelsGenerated\":0,\"freshPolicyTestRun\":false,"
"\"architectureEvidenceOnly\":true,\"records\":" << kRecords
<< ",\"wholeOriginGames\":" << kGames << "},"
<< "\n \"hashes\":{\"sourceSha256\":\"" << options.source_sha256
<< "\",\"corpusSha256\":\"" << kCorpusSha256
<< "\",\"auditSourceSha256\":\"" << kAuditSourceSha256
<< "\",\"fairD4Sha256\":\"" << kD4SourceSha256
<< "\",\"fairD1Sha256\":\"" << kD1SourceSha256
<< "\",\"behaviorSha256\":\"" << kBehaviorSha256
<< "\",\"engineSha256\":\"" << kEngineSha256
<< "\",\"auditCacheSha256\":\"" << cache_sha
<< "\",\"checkpointSha256\":\"" << checkpoint_sha
<< "\",\"goldenSha256\":\"" << golden_sha << "\"},"
<< "\n \"architecture\":{\"fixed\":true,\"reflection\":"
"\"exact shared two-orientation average\","
"\"publicCategoricalInputs\":[\"49 root cells\",\"next disc\","
"\"rise phase\",\"candidate action\"],\"numericFeatures\":"
<< kFeatureCount << ",\"hidden\":" << kHidden
<< ",\"heads\":[\"D4-relative h200 score residual\","
"\"mean survived moves\",\"mean numbered clears\","
"\"mean covers revealed\",\"material downside upper99\"],"
"\"anchor\":\"within-root centered/range-normalized exact D4 Q\","
"\"D1QAuxiliaryInput\":true,\"commonImmediateSuccessors\":"
<< kSuccessorSamples << ",\"parameters\":"
<< final.network.parameterCount() << ",\"checkpointBytes\":"
<< std::filesystem::file_size(options.checkpoint) << "},"
<< "\n \"definitions\":{"
"\"top1\":\"selected action reaches the maximum stored meanScoreReturn within 1e-9\","
"\"pairwise\":\"equal-weight concordance over every legal sibling pair; a target or prediction tie receives one-half credit\","
"\"normalizedRegret\":\"(stored root maximum - selected stored value)/(stored root maximum - stored root minimum), zero on all-tie roots\","
"\"nearTie\":\"absolute stored sibling difference at most 0.10 of root target range\","
"\"decisive\":\"absolute stored sibling difference at least 0.50 of root target range\"},"
<< "\n \"training\":{\"folding\":"
"\"strict leave-one-entire-origin-game-out; every root scored once by a model trained on the other seven origins\","
"\"target\":\"stored h200/255-scenario meanScoreReturn\","
"\"objective\":\"grouped within-root pairwise plus listwise ranking, residual point and four auxiliary losses\","
"\"epochs\":" << kEpochs << ",\"batchRoots\":" << kBatchRoots
<< ",\"learningRate\":" << kLearningRate
<< ",\"pairWeight\":" << kPairWeight
<< ",\"listWeight\":" << kListWeight
<< ",\"pointWeight\":" << kPointWeight
<< ",\"auxiliaryWeight\":" << kAuxiliaryWeight
<< ",\"residualWeight\":" << kResidualWeight << "},"
<< "\n \"crossValidation\":{\"overall\":";
writeEvaluation(output, cv.overall);
output << ",\"folds\":[";
for (int fold = 0; fold < kGames; ++fold) {
if (fold) output << ',';
const FoldResult& value = cv.folds[fold];
output << "{\"heldoutOrigin\":\"" << audit::hex64(kGameStart + fold)
<< "\",\"trainingRoots\":" << value.training_roots
<< ",\"validationRoots\":" << value.validation_roots
<< ",\"firstLoss\":" << value.first_loss
<< ",\"finalLoss\":" << value.final_loss
<< ",\"improvesPairwiseAndRegretWithoutTop1Regression\":"
<< (value.improves_pairwise_regret_without_top1_regression ? "true" : "false")
<< ",\"evaluation\":";
writeEvaluation(output, value.evaluation); output << '}';
}
output << "],\"fourGameHalves\":[";
writeEvaluation(output, cv.halves[0]); output << ',';
writeEvaluation(output, cv.halves[1]);
output << "]},"
<< "\n \"frozenPass\":{\"requiredTop1Gain\":" << kTop1Gain
<< ",\"requiredPairwiseGain\":" << kPairwiseGain
<< ",\"maximumRegretRatio\":" << kRegretRatio
<< ",\"requiredImprovedFolds\":" << kRequiredImprovedFolds
<< ",\"improvedFolds\":" << cv.improved_folds
<< ",\"bothFourGameHalvesImprovePairwiseAndRegret\":"
<< (pairwiseRegretImproves(cv.halves[0]) &&
pairwiseRegretImproves(cv.halves[1]) ? "true" : "false")
<< ",\"passed\":" << (cv.passed ? "true" : "false") << "},"
<< "\n \"assessment\":{\"directH200ResidualLearnableBeyondD4\":"
<< (cv.passed ? "true" : "false")
<< ",\"basis\":\"the preregistered frozen whole-origin pass; no policy-performance inference\"},"
<< "\n \"finalFit\":{\"firstLoss\":" << final.first_loss
<< ",\"finalLoss\":" << final.final_loss
<< ",\"fingerprint\":\"" << audit::hex64(
fingerprint(final.network, normalizer))
<< "\",\"checkpoint\":\"" << audit::jsonEscape(options.checkpoint)
<< "\",\"golden\":\"" << audit::jsonEscape(options.golden) << "\"},"
<< "\n \"exactD4Audit\":{\"cacheHit\":"
<< (exact.cache_hit ? "true" : "false")
<< ",\"work\":" << exact.work << ",\"nodes\":" << exact.nodes
<< ",\"cacheHits\":" << exact.cache_hits
<< ",\"maximumCacheEntries\":" << exact.maximum_cache_entries
<< ",\"seconds\":" << exact.seconds << "},"
<< "\n \"tests\":{\"strictBuild\":true,\"sanitizerShadow\":true,"
"\"checksumParser\":true,\"publicStateOnly\":true,"
"\"metadataBlind\":true,\"reflectionExact\":true,"
"\"checkpointRoundTrip\":true,\"golden\":true},"
<< "\n \"resources\":{\"featureSeconds\":" << feature_seconds
<< ",\"crossValidationSeconds\":" << cv_seconds
<< ",\"finalTrainingSeconds\":" << final_seconds
<< ",\"wallSeconds\":" << wall_seconds
<< ",\"peakRssBytes\":" << audit::peakRssBytes()
<< ",\"rssLimitBytes\":" << kRssLimitBytes
<< ",\"checkpointLimitBytes\":" << kCheckpointLimitBytes << "}\n}\n";
if (!output) throw std::runtime_error("NNUE artifact write failed");
}
void writeReadme(const RunOptions& options, const CrossValidation& cv) {
std::ofstream output(options.readme, std::ios::trunc);
if (!output) throw std::runtime_error("could not write offline README snippet");
output << std::fixed << std::setprecision(6)
<< "## Exact-D4 anchored h200 sibling NNUE (offline only)\n\n"
<< "This architecture experiment used only the checksum-locked 477 h200 "
"panels from eight already-burned origin games. It opened no gameplay "
"seed, generated no panel, and ran no policy screen. Each prediction "
"below came from strict leave-one-entire-origin-game-out training.\n\n"
<< "- Always-D4: top-1 " << cv.overall.baseline.top1
<< ", pairwise " << cv.overall.baseline.pairwise
<< ", normalized regret " << cv.overall.baseline.normalized_regret << ".\n"
<< "- D4 + residual NNUE: top-1 " << cv.overall.candidate.top1
<< ", pairwise " << cv.overall.candidate.pairwise
<< ", normalized regret " << cv.overall.candidate.normalized_regret << ".\n"
<< "- Whole-game folds satisfying pairwise/regret improvement with no "
"top-1 regression: " << cv.improved_folds << "/8. Frozen pass: **"
<< (cv.passed ? "yes" : "no") << "**.\n"
<< "- Direct stored-h200 residual learnable beyond exact D4 under the "
"preregistered gate: **" << (cv.passed ? "yes" : "no") << "**.\n\n"
<< "The checkpoint is " << std::filesystem::file_size(options.checkpoint)
<< " bytes. See `" << options.output
<< "` for all folds, two four-game halves, decisive/near-tie calibration, "
"dependency hashes, and exact-D4 work. This is architecture evidence, "
"not a gameplay-score claim.\n";
}
void expect(bool condition, std::string_view message) {
if (!condition) throw std::runtime_error(std::string(message));
}
PublicState fixtureState() {
PublicState fixture;
constexpr std::string_view board =
"0000000000000000000000000000000000000009003588488";
for (int cell = 0; cell < kCellCount; ++cell) {
fixture.board[cell] = static_cast<std::uint8_t>(board[cell] - '0');
}
fixture.next_disc = 6;
fixture.moves_remaining = 3;
return fixture;
}
bool selfTest(const RunOptions& options, std::ostream& output) {
expect(sha256("abc") ==
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
"SHA-256 golden failed");
const PublicState fixture = fixtureState();
const PublicState reflected = audit::mirror(fixture);
expect(audit::publicHash(fixture) == audit::publicHash(reflected),
"public reflection hash failed");
std::ostringstream synthetic;
synthetic << "{\"recordType\":\"deployment-panel-export-replay\","
<< "\"provenance\":{\"screenSeed\":" << kGameStart
<< ",\"moveIndex\":0,\"canonicalPublicHash\":\""
<< audit::hex64(audit::publicHash(fixture)) << "\",\"tapeSeed\":1},"
<< "\"modelInput\":{\"board\":\""
<< "0000000000000000000000000000000000000009003588488"
<< "\",\"nextDisc\":6,\"movesRemaining\":3,\"terminal\":false},"
<< "\"excludedFromModelInput\":[\"screenSeed\"],\"gate\":\"ultra\","
<< "\"fairD1Action\":3,\"selectedAction\":3,\"switched\":false,"
<< "\"actions\":[";
for (int action = 0; action < kBoardSize; ++action) {
if (action) synthetic << ',';
synthetic << "{\"action\":" << action
<< ",\"meanScoreReturn\":" << 1000 + 20 * action
<< ",\"meanSurvivedMoves\":" << 100 + action
<< ",\"meanNumberedClears\":" << 10 + action
<< ",\"meanCoversRevealed\":" << 5 + action
<< ",\"survivingCutoffs\":0,\"pairedVsFairD1\":{"
"\"score\":{\"lowerOneSided99\":0},"
"\"moves\":{\"lowerOneSided99\":0},"
"\"materialDownsides\":0,\"materialDownsideUpper99\":0.1}}";
}
synthetic << "]}";
const audit::PanelRecord parsed = audit::parsePanel(synthetic.str());
expect(parsed.state == fixture && parsed.actions[6].mean_score == 1120,
"locked panel parser golden failed");
bool parser_rejected = false;
try {
std::string bad = synthetic.str();
bad.replace(bad.find("deployment-panel-export-replay"), 30, "wrong-record");
static_cast<void>(audit::parsePanel(bad));
} catch (const std::exception&) { parser_rejected = true; }
expect(parser_rejected, "parser accepted wrong metadata");
audit::ExactSearch synthetic_search;
synthetic_search.d1_q.fill(-std::numeric_limits<double>::infinity());
synthetic_search.d4_q.fill(-std::numeric_limits<double>::infinity());
synthetic_search.d4_immediate_score.fill(
-std::numeric_limits<double>::infinity());
for (int action = 0; action < kBoardSize; ++action) {
synthetic_search.d1_q[action] = 100.0 * action;
synthetic_search.d4_q[action] = 120.0 * action;
synthetic_search.d4_immediate_score[action] = 10.0 * action;
}
audit::PanelRecord reflected_panel = parsed;
reflected_panel.state = reflected;
reflected_panel.stored_public_hash = audit::publicHash(reflected);
audit::ExactSearch reflected_search = synthetic_search;
for (int action = 0; action < kBoardSize; ++action) {
const int other = kBoardSize - 1 - action;
reflected_panel.actions[other] = parsed.actions[action];
reflected_search.d1_q[other] = synthetic_search.d1_q[action];
reflected_search.d4_q[other] = synthetic_search.d4_q[action];
reflected_search.d4_immediate_score[other] =
synthetic_search.d4_immediate_score[action];
}
const RawPanel raw_feature_fixture =
prepareRawPanel(parsed, synthetic_search);
const RawPanel reflected_feature_fixture =
prepareRawPanel(reflected_panel, reflected_search);
for (int action = 0; action < kBoardSize; ++action) {
const RawAction& direct_action = raw_feature_fixture.actions[action];
const RawAction& mirror_action =
reflected_feature_fixture.actions[kBoardSize - 1 - action];
for (int feature = 0; feature < kFeatureCount; ++feature) {
if (direct_action.features[feature] != mirror_action.features[feature]) {
throw std::runtime_error(
"common-successor numeric reflection failed at action " +
std::to_string(action) + " feature " + std::to_string(feature));
}
}
expect(direct_action.anchor == mirror_action.anchor,
"D4 anchor reflection failed");
}
RawPanel raw;
raw.origin_game = kGameStart;
raw.state = fixture;
for (int action = 0; action < kBoardSize; ++action) {
RawAction value;
value.action = action;
value.anchor = static_cast<float>(action - 3) / 6.0f;
value.label = parsed.actions[action];
for (int feature = 0; feature < kFeatureCount; ++feature) {
value.features[feature] = static_cast<float>((feature + action) % 11) / 11.0f;
}
raw.actions.push_back(value);
}
std::vector<RawPanel> raw_panels{raw, raw, raw, raw};
const auto indices = allIndices(raw_panels.size());
const Normalizer normalizer = fitNormalizer(raw_panels, indices);
const auto prepared = preparePanels(raw_panels, indices, normalizer);
Deadline deadline;
const TrainingResult first = train(prepared, kNetworkSeed, 2, deadline, false);
const TrainingResult repeated = train(prepared, kNetworkSeed, 2, deadline, false);
expect(first.network.parameters() == repeated.network.parameters() &&
first.final_loss == repeated.final_loss,
"training determinism golden failed");
for (const RawAction& action : raw.actions) {
const auto features = normalizer.features(action.features);
const auto direct = first.network.predict(fixture, action.action, features);
const auto mirror = first.network.predict(
reflected, kBoardSize - 1 - action.action, features);
expect(direct == mirror, "reflection-exact NNUE test failed");
}
State metadata = audit::materialize(fixture);
metadata.score = 99'999'999;
metadata.level = 999;
metadata.moves_played = 888;
expect(audit::publicState(metadata) == fixture,
"hidden metadata reached public model state");
const std::string checkpoint = options.checkpoint + ".self-test";
const std::string golden = options.golden + ".self-test";
saveCheckpoint(checkpoint, first.network, normalizer);
const FrozenModel loaded = loadCheckpoint(checkpoint);
expect(loaded.network.parameters() == first.network.parameters() &&
loaded.normalizer.mean == normalizer.mean &&
std::filesystem::file_size(checkpoint) <= kCheckpointLimitBytes,
"checkpoint round-trip test failed");
const std::string corrupt_checkpoint = checkpoint + ".corrupt";
std::string corrupt_payload = readWholeFile(checkpoint);
corrupt_payload[8] ^= 0x01;
{
std::ofstream corrupt_output(corrupt_checkpoint, std::ios::binary |
std::ios::trunc);
corrupt_output.write(corrupt_payload.data(), corrupt_payload.size());
}
bool corrupt_rejected = false;
try {
static_cast<void>(loadCheckpoint(corrupt_checkpoint));
} catch (const std::exception&) { corrupt_rejected = true; }
expect(corrupt_rejected, "corrupt checkpoint metadata was accepted");
writeGolden(golden, loaded, raw_panels);
expect(readWholeFile(golden).find("drop7-d4-h200-sibling-nnue-golden-v1") !=
std::string::npos,
"golden file test failed");
deadline.check();
output << "D4_H200_SIBLING_NNUE_SELF_TEST {\"passed\":true,"
"\"offlineOnly\":true,\"gameplaySeedLanes\":0,"
"\"sha256\":true,\"parser\":true,\"publicStateOnly\":true,"
"\"metadataBlind\":true,\"reflectionExact\":true,"
"\"deterministicTraining\":true,\"checkpoint\":true,"
"\"golden\":true,\"checkpointBytes\":"
<< std::filesystem::file_size(checkpoint)
<< ",\"peakRssBytes\":" << audit::peakRssBytes() << "}\n";
return true;
}
int run(const RunOptions& options, std::ostream& output) {
const Deadline deadline;
const std::vector<audit::PanelRecord> panels = loadLockedPanels(options);
output << "D4_H200_SIBLING_NNUE_INPUT {\"records\":" << panels.size()
<< ",\"origins\":" << kGames << ",\"sha256\":\"" << kCorpusSha256
<< "\",\"gameplaySeedsOpened\":0}\n" << std::flush;
const ExactCorpus exact = exactCorpus(options, panels, deadline);
output << "D4_H200_SIBLING_NNUE_EXACT {\"cacheHit\":"
<< (exact.cache_hit ? "true" : "false") << ",\"work\":" << exact.work
<< ",\"seconds\":" << exact.seconds << "}\n" << std::flush;
const Clock::time_point feature_started = Clock::now();
const std::vector<RawPanel> raw =
prepareRawPanels(panels, exact.searches, deadline);
const double feature_seconds =
std::chrono::duration<double>(Clock::now() - feature_started).count();
const Clock::time_point cv_started = Clock::now();
const CrossValidation cv = crossValidate(raw, deadline);
const double cv_seconds =
std::chrono::duration<double>(Clock::now() - cv_started).count();
const auto indices = allIndices(raw.size());
const Normalizer normalizer = fitNormalizer(raw, indices);
const auto prepared = preparePanels(raw, indices, normalizer);
const Clock::time_point final_started = Clock::now();
const TrainingResult final = train(prepared, kNetworkSeed, kEpochs,
deadline, true);
const double final_seconds =
std::chrono::duration<double>(Clock::now() - final_started).count();
saveCheckpoint(options.checkpoint, final.network, normalizer);
const FrozenModel loaded = loadCheckpoint(options.checkpoint);
writeGolden(options.golden, loaded, raw);
deadline.check();
writeArtifact(options, exact, cv, final, normalizer, feature_seconds,
cv_seconds, final_seconds, deadline.seconds());
writeReadme(options, cv);
output << std::setprecision(12)
<< "D4_H200_SIBLING_NNUE_RESULT {\"passed\":"
<< (cv.passed ? "true" : "false")
<< ",\"top1Gain\":"
<< cv.overall.candidate.top1 - cv.overall.baseline.top1
<< ",\"pairwiseGain\":"
<< cv.overall.candidate.pairwise - cv.overall.baseline.pairwise
<< ",\"regretRatio\":"
<< cv.overall.candidate.normalized_regret /
cv.overall.baseline.normalized_regret
<< ",\"improvedFolds\":" << cv.improved_folds
<< ",\"directH200ResidualLearnableBeyondD4\":"
<< (cv.passed ? "true" : "false")
<< ",\"gameplaySeedsOpened\":0,\"newPanelsGenerated\":0,"
"\"freshPolicyTestRun\":false,\"wallSeconds\":"
<< deadline.seconds() << ",\"peakRssBytes\":" << audit::peakRssBytes()
<< ",\"artifact\":\"" << audit::jsonEscape(options.output) << "\"}\n";
return 0;
}
} // namespace drop7::d4_h200_sibling_nnue
int main(int argc, char** argv) {
try {
if (argc >= 2 && std::string_view(argv[1]) == "--self-test") {
drop7::d4_h200_sibling_nnue::RunOptions options;
options.source_sha256 = std::string(64, '0');
if (argc > 2) {
options = drop7::d4_h200_sibling_nnue::parseOptions(argc, argv, 2);
}
return drop7::d4_h200_sibling_nnue::selfTest(options, std::cout)
? EXIT_SUCCESS : EXIT_FAILURE;
}
if (argc >= 2 && std::string_view(argv[1]) == "--run") {
const auto options =
drop7::d4_h200_sibling_nnue::parseOptions(argc, argv, 2);
return drop7::d4_h200_sibling_nnue::run(options, std::cout);
}
std::cerr << "usage: drop7_d4_h200_sibling_nnue --self-test | --run "
"--source-sha256 HEX [--input PATH] [--output PATH] "
"[--checkpoint PATH] [--golden PATH] [--readme PATH] "
"[--audit-cache PATH] [--threads 1..4]\n";
return 2;
} catch (const std::exception& error) {
std::cerr << "error: " << error.what() << '\n';
return 1;
}
}