#define DROP7_FAIR_ONLY_DEPTH4_LIBRARY
#include "../../fair-expectimax/reference/fair-only-depth4.cpp"
#undef DROP7_FAIR_ONLY_DEPTH4_LIBRARY
#include <atomic>
#include <bit>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <fstream>
#include <future>
#include <iomanip>
#include <iostream>
#include <map>
#include <mutex>
#include <numeric>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <sys/resource.h>
#include <utility>
#include <vector>
// Offline-only audit of previously evaluated h200 development panels. This file
// contains no gameplay-seed lane and never advances a real game. It rebuilds
// each stored public root, evaluates the exact fixed fair-D1 and fair-D4 root
// action/Q functions, and measures their sibling rankings against the stored
// common-tape h200 mean-score-return teacher. Provenance is used only for
// whole-game diagnostic splitting and stratification, never as model input.
namespace drop7::terminal_panel_d4_signal_audit {
namespace d1 = drop7::fair_only_horizon;
namespace d4 = drop7::fair_only_depth4;
using Clock = std::chrono::steady_clock;
constexpr int kExpectedRecords = 477;
constexpr int kExpectedGames = 8;
constexpr std::uint32_t kExpectedGameStart = 0x3d6d'0010u;
constexpr int kMaximumThreads = 4;
constexpr int kDefaultThreads = 2;
constexpr double kWallLimitSeconds = 45.0 * 60.0;
constexpr std::uint64_t kRssLimitBytes = 256ull * 1024ull * 1024ull;
constexpr double kTieTolerance = 1.0e-9;
constexpr std::string_view kExpectedInputSha256 =
"bfda8ae32fa0be3577c6b27f6413aba28f2854930e2f91a0dcf7674808f04196";
constexpr std::string_view kEngineSha256 =
"b6dcde5f40dc39c6931b9a88e42bb351acd6fadaddd1e07691c41a82e44f3090";
constexpr std::string_view kD1SourceSha256 =
"f9d4ea210e282ce5cc22894c17b5be92efb12029242aa5c3c6dc6412b383f42b";
constexpr std::string_view kD4SourceSha256 =
"1cb42629db07b17850045bf3e5678c1fed5b58c73ab38bcfb699c94ee34fe6aa";
constexpr std::array<int, kBoardSize> kActionOrder{{3, 2, 4, 1, 5, 0, 6}};
static_assert(kLevelBonus == 17'000);
static_assert(d1::kChanceSamples == 5);
static_assert(d4::kCandidateDepth == 4);
static_assert(kExpectedGameStart + kExpectedGames == 0x3d6d'0018u);
struct Options {
std::string input = "/tmp/drop7-terminal-policy-deployment-panels.jsonl";
std::string input_sha256 = std::string(kExpectedInputSha256);
std::string output = "/tmp/drop7-terminal-panel-d4-signal-audit.json";
std::string readme = "/tmp/drop7-terminal-panel-d4-signal-audit-README.md";
int threads = kDefaultThreads;
};
Options parseOptions(int argc, char** argv, int begin) {
Options result;
for (int index = begin; index < argc; index += 2) {
if (index + 1 >= argc) throw std::invalid_argument("missing option value");
const std::string flag = argv[index];
if (flag == "--input") result.input = argv[index + 1];
else if (flag == "--input-sha256") result.input_sha256 = argv[index + 1];
else if (flag == "--output") result.output = argv[index + 1];
else if (flag == "--readme") result.readme = argv[index + 1];
else if (flag == "--threads") {
result.threads = std::stoi(argv[index + 1]);
if (result.threads < 1 || result.threads > kMaximumThreads) {
throw std::invalid_argument("threads must be in [1,4]");
}
} else {
throw std::invalid_argument("unknown option " + flag);
}
}
if (result.input_sha256 != kExpectedInputSha256) {
throw std::invalid_argument("panel SHA-256 does not match frozen corpus");
}
return result;
}
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() > kRssLimitBytes) {
throw std::runtime_error("terminal-panel audit exceeded 256 MiB RSS");
}
}
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("terminal-panel audit exceeded 45 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 = cfpi::detail::mirrorBoard(source.board);
return result;
}
PublicState canonicalPublic(const PublicState& source, bool& mirrored) {
return publicState(cfpi::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 (const 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 (const char character : value) {
if (character == '"' || character == '\\') result.push_back('\\');
if (character == '\n') {
result += "\\n";
} else {
result.push_back(character);
}
}
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;
bool 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_at =
afterMarker(object, "\"pairedVsFairD1\":");
const std::size_t score_at = afterMarker(object, "\"score\":", paired_at);
const std::size_t score_end =
matchingDelimiter(object, score_at, '{', '}');
result.score_lcb99 = numberAfter(
object.substr(score_at, score_end - score_at + 1),
"\"lowerOneSided99\":");
const std::size_t moves_at =
afterMarker(object, "\"moves\":", score_end + 1);
const std::size_t moves_end =
matchingDelimiter(object, moves_at, '{', '}');
result.move_lcb99 = numberAfter(
object.substr(moves_at, moves_end - moves_at + 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.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');
if (result.state.board[cell] > kCracked) {
throw std::runtime_error("out-of-domain board token");
}
}
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.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) {
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 std::uint32_t seed =
kExpectedGameStart + static_cast<std::uint32_t>(game);
const auto found = moves.find(seed);
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) {
if (source.terminal) throw std::invalid_argument("terminal search root");
ExactSearch result;
result.d1_q.fill(-std::numeric_limits<double>::infinity());
bool mirrored = false;
const State canonical =
cfpi::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.work > 2 * kBoardSize * d1::kChanceSamples ||
!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 d4_decision = d4::chooseDepth4Action(materialize(source));
if (!d4_decision.complete ||
d4_decision.completed_depth != d4::kCandidateDepth ||
d4_decision.action < 0) {
throw std::runtime_error("exact fair-D4 root did not complete");
}
result.d4_action = d4_decision.action;
result.d4_q = d4_decision.root_values;
result.d4_immediate_score = d4_decision.root_expected_scores;
result.d4_work = d4_decision.work;
result.d4_nodes = d4_decision.nodes;
result.d4_cache_hits = d4_decision.cache_hits;
result.d4_cache_entries = d4_decision.cache_entries;
for (int action = 0; action < kBoardSize; ++action) {
const bool legal = isLegal(source.board, action);
if (legal != std::isfinite(result.d1_q[action]) ||
legal != std::isfinite(result.d4_q[action])) {
throw std::runtime_error("exact search legal/Q mismatch");
}
}
if (!isLegal(source.board, result.d1_action) ||
!isLegal(source.board, result.d4_action)) {
throw std::runtime_error("exact search selected illegal action");
}
return result;
}
struct AuditRoot {
PanelRecord panel{};
ExactSearch search{};
int occupied = 0;
int maximum_height = 0;
std::array<int, kBoardSize> heights{};
};
void deriveGeometry(AuditRoot& root) {
root.heights = cfpi::detail::columnHeights(root.panel.state.board);
for (const std::uint8_t cell : root.panel.state.board) {
root.occupied += cell != kEmpty;
}
root.maximum_height =
*std::max_element(root.heights.begin(), root.heights.end());
}
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};
std::atomic<std::size_t> 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, [&]() {
while (true) {
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");
}
deriveGeometry(root);
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 << "terminal-panel D4 audit " << done << '/'
<< panels.size() << '\n';
}
}
}));
}
for (auto& worker : workers) worker.get();
return result;
}
std::array<double, kBoardSize> teacherScores(const AuditRoot& root) {
std::array<double, kBoardSize> result{};
result.fill(-std::numeric_limits<double>::infinity());
for (int action = 0; action < kBoardSize; ++action) {
if (root.panel.actions[action].legal) {
result[action] = root.panel.actions[action].mean_score;
}
}
return result;
}
std::array<double, kBoardSize> teacherMoves(const AuditRoot& root) {
std::array<double, kBoardSize> result{};
result.fill(-std::numeric_limits<double>::infinity());
for (int action = 0; action < kBoardSize; ++action) {
if (root.panel.actions[action].legal) {
result[action] = root.panel.actions[action].mean_moves;
}
}
return result;
}
int bestAction(const std::array<double, kBoardSize>& values,
const std::array<PanelAction, kBoardSize>& actions) {
int best = -1;
for (const int action : kActionOrder) {
if (!actions[action].legal) continue;
if (best < 0 || values[action] > values[best]) best = action;
}
return best;
}
struct Ranking {
std::uint64_t roots = 0;
double top1_credit = 0.0;
double top2_credit = 0.0;
std::uint64_t pairs = 0;
double pairwise_credit = 0.0;
double normalized_regret = 0.0;
double sum_x = 0.0;
double sum_y = 0.0;
double sum_xx = 0.0;
double sum_yy = 0.0;
double sum_xy = 0.0;
};
void observeRanking(Ranking& result,
const std::array<double, kBoardSize>& prediction,
const std::array<double, kBoardSize>& target,
const std::array<PanelAction, kBoardSize>& actions,
int selected) {
double target_minimum = std::numeric_limits<double>::infinity();
double target_maximum = -std::numeric_limits<double>::infinity();
std::vector<int> predicted_order;
for (const int action : kActionOrder) {
if (!actions[action].legal) continue;
if (!std::isfinite(prediction[action]) || !std::isfinite(target[action])) {
throw std::runtime_error("non-finite ranking value");
}
predicted_order.push_back(action);
target_minimum = std::min(target_minimum, target[action]);
target_maximum = std::max(target_maximum, target[action]);
}
if (predicted_order.empty() || !actions[selected].legal) {
throw std::runtime_error("empty ranking root");
}
std::stable_sort(predicted_order.begin(), predicted_order.end(),
[&](int first, int second) {
return prediction[first] > prediction[second];
});
result.top1_credit +=
target[selected] + kTieTolerance >= target_maximum;
const int top_count = std::min<int>(2, predicted_order.size());
bool top_two = false;
for (int rank = 0; rank < top_count; ++rank) {
top_two = top_two ||
target[predicted_order[rank]] + kTieTolerance >= target_maximum;
}
result.top2_credit += top_two;
const double range = target_maximum - target_minimum;
if (range > kTieTolerance) {
result.normalized_regret +=
(target_maximum - target[selected]) / range;
}
for (int first = 0; first < kBoardSize; ++first) {
if (!actions[first].legal) continue;
for (int second = first + 1; second < kBoardSize; ++second) {
if (!actions[second].legal) continue;
const double x = prediction[first] - prediction[second];
const double y = target[first] - target[second];
result.sum_x += x;
result.sum_y += y;
result.sum_xx += x * x;
result.sum_yy += y * y;
result.sum_xy += x * y;
if (std::abs(x) <= kTieTolerance || std::abs(y) <= kTieTolerance) {
result.pairwise_credit += 0.5;
} else {
result.pairwise_credit += (x > 0.0) == (y > 0.0);
}
++result.pairs;
}
}
++result.roots;
}
double pearson(const Ranking& value) {
if (value.pairs < 2) return 0.0;
const double count = static_cast<double>(value.pairs);
const double covariance = value.sum_xy - value.sum_x * value.sum_y / count;
const double x_variance = value.sum_xx - value.sum_x * value.sum_x / count;
const double y_variance = value.sum_yy - value.sum_y * value.sum_y / count;
const double denominator =
std::sqrt(std::max(0.0, x_variance) * std::max(0.0, y_variance));
return denominator > 0.0 ? covariance / denominator : 0.0;
}
void writeRanking(std::ostream& output, const Ranking& value) {
const double roots = static_cast<double>(std::max<std::uint64_t>(1, value.roots));
const double pairs = static_cast<double>(std::max<std::uint64_t>(1, value.pairs));
output << std::setprecision(12)
<< "{\"roots\":" << value.roots
<< ",\"top1WithTies\":" << value.top1_credit / roots
<< ",\"top2ContainsTeacherTop\":" << value.top2_credit / roots
<< ",\"pairwiseAccuracy\":" << value.pairwise_credit / pairs
<< ",\"normalizedRegret\":" << value.normalized_regret / roots
<< ",\"pairDifferencePearson\":" << pearson(value)
<< ",\"pairCount\":" << value.pairs << '}';
}
struct Summary {
std::uint64_t roots = 0;
Ranking d1_score{};
Ranking d4_score{};
Ranking d1_moves{};
Ranking d4_moves{};
Ranking d1_vs_d4{};
std::uint64_t d1_d4_agreements = 0;
std::uint64_t d4_differences = 0;
std::uint64_t deployment_d1 = 0;
std::uint64_t deployment_d4 = 0;
std::uint64_t deployment_teacher_top = 0;
std::uint64_t d4_score_better = 0;
std::uint64_t d4_moves_nonworse = 0;
std::uint64_t d4_mean_pareto_support = 0;
std::uint64_t d4_confident99_support = 0;
double d4_score_delta = 0.0;
double d4_move_delta = 0.0;
double d4_clear_delta = 0.0;
double d4_reveal_delta = 0.0;
};
void observeSummary(Summary& result, const AuditRoot& root) {
const auto scores = teacherScores(root);
const auto moves = teacherMoves(root);
observeRanking(result.d1_score, root.search.d1_q, scores,
root.panel.actions, root.search.d1_action);
observeRanking(result.d4_score, root.search.d4_q, scores,
root.panel.actions, root.search.d4_action);
observeRanking(result.d1_moves, root.search.d1_q, moves,
root.panel.actions, root.search.d1_action);
observeRanking(result.d4_moves, root.search.d4_q, moves,
root.panel.actions, root.search.d4_action);
observeRanking(result.d1_vs_d4, root.search.d1_q, root.search.d4_q,
root.panel.actions, root.search.d1_action);
const int teacher_top = bestAction(scores, root.panel.actions);
result.deployment_d1 +=
root.panel.deployment_action == root.search.d1_action;
result.deployment_d4 +=
root.panel.deployment_action == root.search.d4_action;
result.deployment_teacher_top +=
root.panel.deployment_action == teacher_top;
if (root.search.d1_action == root.search.d4_action) {
++result.d1_d4_agreements;
} else {
++result.d4_differences;
const PanelAction& first = root.panel.actions[root.search.d1_action];
const PanelAction& fourth = root.panel.actions[root.search.d4_action];
const double score_delta = fourth.mean_score - first.mean_score;
const double move_delta = fourth.mean_moves - first.mean_moves;
const bool score_better = score_delta > kTieTolerance;
const bool moves_nonworse = move_delta >= -kTieTolerance;
const bool cutoff_nonworse =
fourth.surviving_cutoffs <= first.surviving_cutoffs;
result.d4_score_better += score_better;
result.d4_moves_nonworse += moves_nonworse;
result.d4_mean_pareto_support +=
score_better && moves_nonworse && cutoff_nonworse;
result.d4_confident99_support +=
fourth.score_lcb99 > 0.0 && fourth.move_lcb99 >= 0.0 &&
cutoff_nonworse;
result.d4_score_delta += score_delta;
result.d4_move_delta += move_delta;
result.d4_clear_delta += fourth.mean_clears - first.mean_clears;
result.d4_reveal_delta += fourth.mean_reveals - first.mean_reveals;
}
++result.roots;
}
void writeSummary(std::ostream& output, const Summary& value) {
const double roots = static_cast<double>(std::max<std::uint64_t>(1, value.roots));
const double differences =
static_cast<double>(std::max<std::uint64_t>(1, value.d4_differences));
output << "{\"roots\":" << value.roots << ",\"scoreTeacher\":{\"d1\":";
writeRanking(output, value.d1_score);
output << ",\"d4\":";
writeRanking(output, value.d4_score);
output << "},\"survivalTeacher\":{\"d1\":";
writeRanking(output, value.d1_moves);
output << ",\"d4\":";
writeRanking(output, value.d4_moves);
output << "},\"d1RankingVsD4Q\":";
writeRanking(output, value.d1_vs_d4);
output << ",\"actionRelationships\":{\"d1D4Agreements\":"
<< value.d1_d4_agreements << ",\"d4Differences\":"
<< value.d4_differences << ",\"d4DifferenceRate\":"
<< value.d4_differences / roots << ",\"deploymentMatchesD1\":"
<< value.deployment_d1 << ",\"deploymentMatchesD4\":"
<< value.deployment_d4 << ",\"deploymentMatchesTeacherTop\":"
<< value.deployment_teacher_top
<< "},\"d4AlternativeRisk\":{\"scoreBetter\":"
<< value.d4_score_better << ",\"movesNonworse\":"
<< value.d4_moves_nonworse << ",\"meanParetoSupport\":"
<< value.d4_mean_pareto_support
<< ",\"confident99Support\":" << value.d4_confident99_support
<< ",\"scoreBetterRate\":"
<< value.d4_score_better / differences
<< ",\"meanParetoSupportRate\":"
<< value.d4_mean_pareto_support / differences
<< ",\"meanScoreDelta\":" << value.d4_score_delta / differences
<< ",\"meanMoveDelta\":" << value.d4_move_delta / differences
<< ",\"meanClearDelta\":" << value.d4_clear_delta / differences
<< ",\"meanRevealDelta\":" << value.d4_reveal_delta / differences
<< "}}";
}
std::string occupancyBand(int occupied) {
if (occupied <= 14) return "07-14";
if (occupied <= 21) return "15-21";
if (occupied <= 28) return "22-28";
if (occupied <= 35) return "29-35";
return "36-49";
}
std::string gamePhase(int move_index) {
if (move_index < 20) return "early-00-19";
if (move_index < 50) return "middle-20-49";
return "late-50-plus";
}
using SummaryMap = std::map<std::string, Summary>;
struct Stratified {
Summary overall{};
SummaryMap origin_game;
SummaryMap rise_phase;
SummaryMap game_phase;
SummaryMap occupancy;
SummaryMap maximum_height;
SummaryMap d4_differs;
};
Stratified stratify(const std::vector<AuditRoot>& roots) {
Stratified result;
for (const AuditRoot& root : roots) {
const auto add = [&](Summary& value) { observeSummary(value, root); };
add(result.overall);
add(result.origin_game[hex64(root.panel.origin_game)]);
add(result.rise_phase[std::to_string(root.panel.state.moves_remaining)]);
add(result.game_phase[gamePhase(root.panel.move_index)]);
add(result.occupancy[occupancyBand(root.occupied)]);
add(result.maximum_height[std::to_string(root.maximum_height)]);
add(result.d4_differs[root.search.d1_action == root.search.d4_action
? "same"
: "different"]);
}
return result;
}
void writeSummaryMap(std::ostream& output, const SummaryMap& values) {
output << '[';
bool first = true;
for (const auto& [name, summary] : values) {
if (!first) output << ',';
first = false;
output << "{\"stratum\":\"" << jsonEscape(name) << "\",\"metrics\":";
writeSummary(output, summary);
output << '}';
}
output << ']';
}
constexpr int kGateFeatures = 7;
constexpr int kGateParameters = kGateFeatures + 1;
constexpr double kGateRidge = 0.5;
constexpr int kGateNewtonIterations = 30;
constexpr std::array<std::string_view, kGateFeatures> kGateFeatureNames{{
"d4NormalizedAdvantageOverD1",
"d1NormalizedPenaltyForD4",
"occupancyFraction",
"maximumHeightFraction",
"riseMovesRemainingFraction",
"d4MinusD1ColumnHeight",
"d4MinusD1CenterDistance",
}};
double finiteRange(const std::array<double, kBoardSize>& values,
const std::array<PanelAction, kBoardSize>& actions) {
double minimum = std::numeric_limits<double>::infinity();
double maximum = -std::numeric_limits<double>::infinity();
for (int action = 0; action < kBoardSize; ++action) {
if (!actions[action].legal) continue;
minimum = std::min(minimum, values[action]);
maximum = std::max(maximum, values[action]);
}
return std::max(1.0, maximum - minimum);
}
struct GateExample {
std::size_t root_index = 0;
std::uint32_t game = 0;
std::array<double, kGateFeatures> features{};
bool pareto_support = false;
bool confident99_support = false;
bool score_support = false;
double score_delta = 0.0;
double move_delta = 0.0;
};
GateExample gateExample(const AuditRoot& root, std::size_t root_index) {
if (root.search.d1_action == root.search.d4_action) {
throw std::invalid_argument("gate example requires a D4 alternative");
}
GateExample result;
result.root_index = root_index;
result.game = root.panel.origin_game;
const int first_action = root.search.d1_action;
const int fourth_action = root.search.d4_action;
const PanelAction& first = root.panel.actions[first_action];
const PanelAction& fourth = root.panel.actions[fourth_action];
const double d4_range = finiteRange(root.search.d4_q, root.panel.actions);
const double d1_range = finiteRange(root.search.d1_q, root.panel.actions);
result.features = {{
(root.search.d4_q[fourth_action] - root.search.d4_q[first_action]) /
d4_range,
(root.search.d1_q[fourth_action] - root.search.d1_q[first_action]) /
d1_range,
static_cast<double>(root.occupied) / kCellCount,
static_cast<double>(root.maximum_height) / kBoardSize,
static_cast<double>(root.panel.state.moves_remaining) / kMovesPerLevel,
static_cast<double>(root.heights[fourth_action] -
root.heights[first_action]) /
kBoardSize,
static_cast<double>(std::abs(fourth_action - kBoardSize / 2) -
std::abs(first_action - kBoardSize / 2)) /
(kBoardSize / 2),
}};
result.score_delta = fourth.mean_score - first.mean_score;
result.move_delta = fourth.mean_moves - first.mean_moves;
const bool cutoff_nonworse =
fourth.surviving_cutoffs <= first.surviving_cutoffs;
result.score_support = result.score_delta > kTieTolerance;
result.pareto_support = result.score_support &&
result.move_delta >= -kTieTolerance && cutoff_nonworse;
result.confident99_support = fourth.score_lcb99 > 0.0 &&
fourth.move_lcb99 >= 0.0 && cutoff_nonworse;
return result;
}
struct GateModel {
std::array<double, kGateFeatures> mean{};
std::array<double, kGateFeatures> scale{};
std::array<double, kGateParameters> beta{};
};
double sigmoid(double value) {
if (value >= 0.0) {
const double exponential = std::exp(-value);
return 1.0 / (1.0 + exponential);
}
const double exponential = std::exp(value);
return exponential / (1.0 + exponential);
}
std::array<double, kGateParameters> designRow(
const GateExample& example, const GateModel& model) {
std::array<double, kGateParameters> result{};
result[0] = 1.0;
for (int feature = 0; feature < kGateFeatures; ++feature) {
result[feature + 1] =
(example.features[feature] - model.mean[feature]) /
model.scale[feature];
}
return result;
}
std::array<double, kGateParameters> solveLinear(
std::array<std::array<double, kGateParameters>, kGateParameters> matrix,
std::array<double, kGateParameters> vector) {
for (int pivot = 0; pivot < kGateParameters; ++pivot) {
int best = pivot;
for (int row = pivot + 1; row < kGateParameters; ++row) {
if (std::abs(matrix[row][pivot]) > std::abs(matrix[best][pivot])) {
best = row;
}
}
if (std::abs(matrix[best][pivot]) < 1.0e-12) {
throw std::runtime_error("singular gate fit");
}
std::swap(matrix[pivot], matrix[best]);
std::swap(vector[pivot], vector[best]);
const double denominator = matrix[pivot][pivot];
for (int column = pivot; column < kGateParameters; ++column) {
matrix[pivot][column] /= denominator;
}
vector[pivot] /= denominator;
for (int row = 0; row < kGateParameters; ++row) {
if (row == pivot) continue;
const double factor = matrix[row][pivot];
for (int column = pivot; column < kGateParameters; ++column) {
matrix[row][column] -= factor * matrix[pivot][column];
}
vector[row] -= factor * vector[pivot];
}
}
return vector;
}
GateModel fitGate(const std::vector<GateExample>& examples,
std::uint32_t excluded_game) {
GateModel result;
int count = 0;
int positives = 0;
for (const GateExample& example : examples) {
if (example.game == excluded_game) continue;
++count;
positives += example.pareto_support;
for (int feature = 0; feature < kGateFeatures; ++feature) {
result.mean[feature] += example.features[feature];
}
}
if (count < kGateParameters || positives == 0 || positives == count) {
throw std::runtime_error("degenerate whole-game gate training fold");
}
for (double& value : result.mean) value /= count;
for (const GateExample& example : examples) {
if (example.game == excluded_game) continue;
for (int feature = 0; feature < kGateFeatures; ++feature) {
const double centered = example.features[feature] - result.mean[feature];
result.scale[feature] += centered * centered;
}
}
for (double& value : result.scale) {
value = std::max(1.0e-6, std::sqrt(value / count));
}
const double positive_weight = static_cast<double>(count) / (2.0 * positives);
const double negative_weight =
static_cast<double>(count) / (2.0 * (count - positives));
for (int iteration = 0; iteration < kGateNewtonIterations; ++iteration) {
std::array<double, kGateParameters> gradient{};
std::array<std::array<double, kGateParameters>, kGateParameters> hessian{};
for (const GateExample& example : examples) {
if (example.game == excluded_game) continue;
const auto row = designRow(example, result);
const double score = std::inner_product(
row.begin(), row.end(), result.beta.begin(), 0.0);
const double probability = sigmoid(score);
const double target = example.pareto_support ? 1.0 : 0.0;
const double weight = example.pareto_support
? positive_weight
: negative_weight;
for (int first = 0; first < kGateParameters; ++first) {
gradient[first] += weight * (probability - target) * row[first];
for (int second = 0; second < kGateParameters; ++second) {
hessian[first][second] += weight * probability *
(1.0 - probability) * row[first] * row[second];
}
}
}
for (int parameter = 1; parameter < kGateParameters; ++parameter) {
gradient[parameter] += kGateRidge * result.beta[parameter];
hessian[parameter][parameter] += kGateRidge;
}
hessian[0][0] += 1.0e-8;
const auto step = solveLinear(hessian, gradient);
double maximum_step = 0.0;
for (int parameter = 0; parameter < kGateParameters; ++parameter) {
result.beta[parameter] -= step[parameter];
maximum_step = std::max(maximum_step, std::abs(step[parameter]));
}
if (maximum_step < 1.0e-9) break;
}
return result;
}
double predictGate(const GateExample& example, const GateModel& model) {
const auto row = designRow(example, model);
const double score =
std::inner_product(row.begin(), row.end(), model.beta.begin(), 0.0);
return sigmoid(score);
}
struct BinaryMetrics {
int examples = 0;
int positives = 0;
int predicted_positive = 0;
int true_positive = 0;
int true_negative = 0;
int false_positive = 0;
int false_negative = 0;
double brier = 0.0;
double auc = 0.0;
};
struct ChoiceMetrics {
int roots = 0;
double top1 = 0.0;
double normalized_regret = 0.0;
double score_delta_vs_d1 = 0.0;
double move_delta_vs_d1 = 0.0;
double clear_delta_vs_d1 = 0.0;
double reveal_delta_vs_d1 = 0.0;
};
void observeChoice(ChoiceMetrics& result, const AuditRoot& root, int selected) {
const auto targets = teacherScores(root);
const int teacher = bestAction(targets, root.panel.actions);
double minimum = std::numeric_limits<double>::infinity();
double maximum = -std::numeric_limits<double>::infinity();
for (int action = 0; action < kBoardSize; ++action) {
if (!root.panel.actions[action].legal) continue;
minimum = std::min(minimum, targets[action]);
maximum = std::max(maximum, targets[action]);
}
result.top1 += targets[selected] + kTieTolerance >= targets[teacher];
if (maximum - minimum > kTieTolerance) {
result.normalized_regret +=
(maximum - targets[selected]) / (maximum - minimum);
}
const PanelAction& baseline = root.panel.actions[root.search.d1_action];
const PanelAction& choice = root.panel.actions[selected];
result.score_delta_vs_d1 += choice.mean_score - baseline.mean_score;
result.move_delta_vs_d1 += choice.mean_moves - baseline.mean_moves;
result.clear_delta_vs_d1 += choice.mean_clears - baseline.mean_clears;
result.reveal_delta_vs_d1 += choice.mean_reveals - baseline.mean_reveals;
++result.roots;
}
void writeChoice(std::ostream& output, const ChoiceMetrics& value) {
const double count = static_cast<double>(std::max(1, value.roots));
output << "{\"roots\":" << value.roots
<< ",\"teacherTop1\":" << value.top1 / count
<< ",\"normalizedRegret\":" << value.normalized_regret / count
<< ",\"meanScoreDeltaVsD1\":" << value.score_delta_vs_d1 / count
<< ",\"meanMoveDeltaVsD1\":" << value.move_delta_vs_d1 / count
<< ",\"meanClearDeltaVsD1\":" << value.clear_delta_vs_d1 / count
<< ",\"meanRevealDeltaVsD1\":" << value.reveal_delta_vs_d1 / count
<< '}';
}
struct FoldModel {
std::uint32_t heldout_game = 0;
GateModel model{};
};
struct GateAudit {
std::vector<GateExample> examples;
std::vector<double> probabilities;
std::vector<FoldModel> folds;
BinaryMetrics classification{};
double score_pairwise_accuracy = 0.0;
double always_d4_score_pairwise_accuracy = 0.0;
ChoiceMetrics d1_choice{};
ChoiceMetrics d4_choice{};
ChoiceMetrics gate_choice{};
bool reduces_d4_regret = false;
bool adds_score_pairwise_signal = false;
};
GateAudit auditGate(const std::vector<AuditRoot>& roots) {
GateAudit result;
for (std::size_t index = 0; index < roots.size(); ++index) {
if (roots[index].search.d1_action != roots[index].search.d4_action) {
result.examples.push_back(gateExample(roots[index], index));
}
}
result.probabilities.resize(result.examples.size());
std::set<std::uint32_t> games;
for (const GateExample& example : result.examples) games.insert(example.game);
if (games.size() != kExpectedGames) {
throw std::runtime_error("D4 alternatives did not span every origin game");
}
for (const std::uint32_t game : games) {
FoldModel fold{game, fitGate(result.examples, game)};
for (std::size_t index = 0; index < result.examples.size(); ++index) {
if (result.examples[index].game == game) {
result.probabilities[index] =
predictGate(result.examples[index], fold.model);
}
}
result.folds.push_back(std::move(fold));
}
std::vector<double> positive_probabilities;
std::vector<double> negative_probabilities;
double score_pairwise_credit = 0.0;
double d4_score_pairwise_credit = 0.0;
for (std::size_t index = 0; index < result.examples.size(); ++index) {
const GateExample& example = result.examples[index];
const AuditRoot& root = roots[example.root_index];
const double probability = result.probabilities[index];
const bool selected_d4 = probability >= 0.5;
const bool target = example.pareto_support;
++result.classification.examples;
result.classification.positives += target;
result.classification.predicted_positive += selected_d4;
result.classification.true_positive += selected_d4 && target;
result.classification.true_negative += !selected_d4 && !target;
result.classification.false_positive += selected_d4 && !target;
result.classification.false_negative += !selected_d4 && target;
result.classification.brier +=
(probability - static_cast<double>(target)) *
(probability - static_cast<double>(target));
(target ? positive_probabilities : negative_probabilities)
.push_back(probability);
if (std::abs(example.score_delta) <= kTieTolerance) {
score_pairwise_credit += 0.5;
d4_score_pairwise_credit += 0.5;
} else {
score_pairwise_credit +=
selected_d4 == (example.score_delta > 0.0);
d4_score_pairwise_credit += example.score_delta > 0.0;
}
observeChoice(result.d1_choice, root, root.search.d1_action);
observeChoice(result.d4_choice, root, root.search.d4_action);
observeChoice(result.gate_choice, root,
selected_d4 ? root.search.d4_action
: root.search.d1_action);
}
double auc_credit = 0.0;
for (const double positive : positive_probabilities) {
for (const double negative : negative_probabilities) {
if (positive > negative) auc_credit += 1.0;
else if (positive == negative) auc_credit += 0.5;
}
}
const double auc_pairs = static_cast<double>(positive_probabilities.size()) *
negative_probabilities.size();
result.classification.auc = auc_pairs > 0.0 ? auc_credit / auc_pairs : 0.0;
result.classification.brier /=
std::max(1, result.classification.examples);
result.score_pairwise_accuracy =
score_pairwise_credit / std::max<std::size_t>(1, result.examples.size());
result.always_d4_score_pairwise_accuracy =
d4_score_pairwise_credit /
std::max<std::size_t>(1, result.examples.size());
result.reduces_d4_regret =
result.gate_choice.normalized_regret <
result.d4_choice.normalized_regret - kTieTolerance;
result.adds_score_pairwise_signal =
result.score_pairwise_accuracy >
result.always_d4_score_pairwise_accuracy + kTieTolerance;
return result;
}
void writeGateModel(std::ostream& output, const FoldModel& fold) {
output << "{\"heldoutOriginGame\":\"" << hex64(fold.heldout_game)
<< "\",\"standardizationMean\":[";
for (int feature = 0; feature < kGateFeatures; ++feature) {
if (feature) output << ',';
output << fold.model.mean[feature];
}
output << "],\"standardizationScale\":[";
for (int feature = 0; feature < kGateFeatures; ++feature) {
if (feature) output << ',';
output << fold.model.scale[feature];
}
output << "],\"standardizedCoefficients\":[";
for (int parameter = 0; parameter < kGateParameters; ++parameter) {
if (parameter) output << ',';
output << fold.model.beta[parameter];
}
output << "]}";
}
void writeBinary(std::ostream& output, const BinaryMetrics& value) {
const double positives = std::max(1, value.positives);
const double negatives = std::max(1, value.examples - value.positives);
const double predicted = std::max(1, value.predicted_positive);
const double count = std::max(1, value.examples);
const double sensitivity = value.true_positive / positives;
const double specificity = value.true_negative / negatives;
output << "{\"examples\":" << value.examples
<< ",\"positives\":" << value.positives
<< ",\"predictedPositive\":" << value.predicted_positive
<< ",\"truePositive\":" << value.true_positive
<< ",\"trueNegative\":" << value.true_negative
<< ",\"falsePositive\":" << value.false_positive
<< ",\"falseNegative\":" << value.false_negative
<< ",\"accuracy\":"
<< (value.true_positive + value.true_negative) / count
<< ",\"balancedAccuracy\":" << 0.5 * (sensitivity + specificity)
<< ",\"precision\":" << value.true_positive / predicted
<< ",\"recall\":" << sensitivity
<< ",\"specificity\":" << specificity
<< ",\"brier\":" << value.brier << ",\"auc\":" << value.auc
<< '}';
}
struct WorkSummary {
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 maximum_d4_cache_entries = 0;
};
WorkSummary summarizeWork(const std::vector<AuditRoot>& roots) {
WorkSummary result;
for (const AuditRoot& root : roots) {
result.d1_work += root.search.d1_work;
result.d4_work += root.search.d4_work;
result.d4_nodes += root.search.d4_nodes;
result.d4_cache_hits += root.search.d4_cache_hits;
result.maximum_d4_cache_entries =
std::max(result.maximum_d4_cache_entries,
root.search.d4_cache_entries);
}
return result;
}
bool rawSignalBeyondD4(const Stratified& stratified) {
const Ranking& d4_score = stratified.overall.d4_score;
return d4_score.roots > 0 &&
d4_score.top1_credit / d4_score.roots < 0.95 &&
d4_score.pairwise_credit / d4_score.pairs < 0.95 &&
d4_score.normalized_regret / d4_score.roots > 0.01;
}
bool learnableGateSignal(const GateAudit& gate) {
const BinaryMetrics& metrics = gate.classification;
const double positive = std::max(1, metrics.positives);
const double negative = std::max(1, metrics.examples - metrics.positives);
const double balanced = 0.5 *
(metrics.true_positive / positive + metrics.true_negative / negative);
return balanced > 0.55 && gate.classification.auc > 0.55 &&
gate.adds_score_pairwise_signal && gate.reduces_d4_regret;
}
void writeArtifact(const Options& options, const Stratified& stratified,
const GateAudit& gate, const WorkSummary& work,
double wall_seconds) {
std::ofstream output(options.output, std::ios::trunc);
if (!output) throw std::runtime_error("could not write audit artifact");
output << std::setprecision(12)
<< "{\n \"experiment\":\"terminal-h200-vs-exact-fair-d4-offline-signal-audit\",\n"
<< " \"scope\":{\"offlineOnly\":true,\"gameplaySeedsOpened\":0,"
"\"newPanelsGenerated\":0,\"policyPerformanceClaimed\":false,"
"\"inputRecords\":" << kExpectedRecords
<< ",\"originGames\":" << kExpectedGames << "},\n"
<< " \"input\":{\"path\":\"" << jsonEscape(options.input)
<< "\",\"sha256\":\"" << options.input_sha256
<< "\",\"recordType\":\"deployment-panel-export-replay\","
"\"horizon\":200,\"scenariosPerAction\":255,"
"\"teacherTarget\":\"meanScoreReturn\","
"\"riskTargets\":[\"meanSurvivedMoves\",\"survivingCutoffs\","
"\"paired99LCB\"]},\n"
<< " \"dependencies\":{\"engineSha256\":\"" << kEngineSha256
<< "\",\"fairD1Sha256\":\"" << kD1SourceSha256
<< "\",\"fairD4Sha256\":\"" << kD4SourceSha256 << "\"},\n"
<< " \"definitions\":{"
"\"top1WithTies\":\"selected action has maximum stored target within 1e-9\","
"\"pairwiseAccuracy\":\"equal-weight legal sibling-pair sign concordance; ties score one half\","
"\"normalizedRegret\":\"(teacher maximum - teacher value of selected action)/(teacher maximum - teacher minimum), averaged by root\","
"\"meanParetoSupport\":\"D4 action has higher h200 mean score return, nonlower mean survival, and no additional surviving cutoff versus D1\","
"\"confident99Support\":\"stored paired score LCB is positive, move LCB nonnegative, and no additional surviving cutoff\","
"\"risePhase\":\"public movesRemaining in the five-drop rise cycle\","
"\"gamePhase\":\"provenance-only diagnostic move bands; never a gate feature\"},\n"
<< " \"overall\":";
writeSummary(output, stratified.overall);
output << ",\n \"strata\":{\"originGame\":";
writeSummaryMap(output, stratified.origin_game);
output << ",\"risePhase\":";
writeSummaryMap(output, stratified.rise_phase);
output << ",\"gamePhase\":";
writeSummaryMap(output, stratified.game_phase);
output << ",\"occupancy\":";
writeSummaryMap(output, stratified.occupancy);
output << ",\"maximumHeight\":";
writeSummaryMap(output, stratified.maximum_height);
output << ",\"d4DiffersFromD1\":";
writeSummaryMap(output, stratified.d4_differs);
output << "},\n \"wholeGameLeaveOneOutRiskGate\":{"
<< "\"diagnosticOnly\":true,\"target\":\"meanParetoSupport\","
"\"threshold\":0.5,\"ridge\":" << kGateRidge
<< ",\"features\":[";
for (int feature = 0; feature < kGateFeatures; ++feature) {
if (feature) output << ',';
output << '"' << kGateFeatureNames[feature] << '"';
}
output << "],\"classification\":";
writeBinary(output, gate.classification);
output << ",\"scorePairwiseAccuracy\":" << gate.score_pairwise_accuracy
<< ",\"alwaysD4ScorePairwiseAccuracy\":"
<< gate.always_d4_score_pairwise_accuracy
<< ",\"addsScorePairwiseSignal\":"
<< (gate.adds_score_pairwise_signal ? "true" : "false")
<< ",\"reducesD4Regret\":"
<< (gate.reduces_d4_regret ? "true" : "false")
<< ",\"choiceDiagnostics\":{\"d1\":";
writeChoice(output, gate.d1_choice);
output << ",\"alwaysD4\":";
writeChoice(output, gate.d4_choice);
output << ",\"gate\":";
writeChoice(output, gate.gate_choice);
output << "},\"foldModels\":[";
for (std::size_t fold = 0; fold < gate.folds.size(); ++fold) {
if (fold) output << ',';
writeGateModel(output, gate.folds[fold]);
}
output << "]},\n \"signalAssessment\":{\"rawLabelsDifferMateriallyFromD4\":"
<< (rawSignalBeyondD4(stratified) ? "true" : "false")
<< ",\"incrementalSignalLearnableByTinyWholeGameGate\":"
<< (learnableGateSignal(gate) ? "true" : "false")
<< ",\"interpretation\":\"Raw disagreement establishes information not present in D4 ranking; only leakage-free whole-game gate improvement establishes that this tiny public model can recover it.\"},\n"
<< " \"work\":{\"d1Work\":" << work.d1_work
<< ",\"d4Work\":" << work.d4_work
<< ",\"d4Nodes\":" << work.d4_nodes
<< ",\"d4CacheHits\":" << work.d4_cache_hits
<< ",\"maximumD4CacheEntries\":"
<< work.maximum_d4_cache_entries << "},\n"
<< " \"resources\":{\"wallSeconds\":" << wall_seconds
<< ",\"peakRssBytes\":" << peakRssBytes()
<< ",\"rssLimitBytes\":" << kRssLimitBytes << "}\n}\n";
output.close();
if (!output) throw std::runtime_error("audit artifact write failed");
}
void writeReadme(const Options& options, const Stratified& stratified,
const GateAudit& gate, const WorkSummary& work,
double wall_seconds) {
std::ofstream output(options.readme, std::ios::trunc);
if (!output) throw std::runtime_error("could not write audit README snippet");
const Ranking& d1_score = stratified.overall.d1_score;
const Ranking& d4_score = stratified.overall.d4_score;
const auto rate = [](double numerator, std::uint64_t denominator) {
return numerator / std::max<std::uint64_t>(1, denominator);
};
const BinaryMetrics& binary = gate.classification;
const double balanced = 0.5 *
(binary.true_positive / static_cast<double>(std::max(1, binary.positives)) +
binary.true_negative /
static_cast<double>(std::max(1, binary.examples - binary.positives)));
output << std::fixed << std::setprecision(6)
<< "## Burned h200 panel vs exact fair-D4 audit\n\n"
<< "This is an offline diagnostic over all " << kExpectedRecords
<< " stored roots from " << kExpectedGames
<< " whole origin games. It opened no gameplay seeds, generated no new "
"panels, and does not estimate policy score. The primary teacher is "
"the stored h200/255-scenario `meanScoreReturn`; survived moves and "
"paired risk statistics are reported separately.\n\n"
<< "- D1 vs h200: top-1 "
<< rate(d1_score.top1_credit, d1_score.roots) << ", pairwise "
<< rate(d1_score.pairwise_credit, d1_score.pairs) << ", regret "
<< rate(d1_score.normalized_regret, d1_score.roots) << ".\n"
<< "- D4 vs h200: top-1 "
<< rate(d4_score.top1_credit, d4_score.roots) << ", pairwise "
<< rate(d4_score.pairwise_credit, d4_score.pairs) << ", regret "
<< rate(d4_score.normalized_regret, d4_score.roots) << ".\n"
<< "- D4 differed from D1 at "
<< stratified.overall.d4_differences << " roots; h200 mean-Pareto "
"supported the D4 alternative at "
<< stratified.overall.d4_mean_pareto_support << " of them, with "
<< stratified.overall.d4_confident99_support
<< " meeting the paired 99% support definition.\n"
<< "- The whole-origin-game leave-one-out tiny risk gate reached balanced "
"accuracy " << balanced << ", AUC " << binary.auc
<< ", and D1-vs-D4 score-pair accuracy "
<< gate.score_pairwise_accuracy << " versus "
<< gate.always_d4_score_pairwise_accuracy << " for always taking D4.\n"
<< "- Raw incremental h200 signal beyond D4: **"
<< (rawSignalBeyondD4(stratified) ? "yes" : "no")
<< "**. Recoverable incremental signal with this tiny leakage-free gate: **"
<< (learnableGateSignal(gate) ? "yes" : "no") << "**.\n\n"
<< "See `" << options.output
<< "` for whole-game, rise-phase, game-phase, occupancy, maximum-height, "
"and D4-vs-D1-disagreement strata. Exact-search work was "
<< work.d4_work << " D4 units; runtime was " << wall_seconds
<< " seconds and peak RSS was " << peakRssBytes() << " bytes.\n";
output.close();
if (!output) throw std::runtime_error("audit README write failed");
}
void expect(bool condition, std::string_view message) {
if (!condition) throw std::runtime_error(std::string(message));
}
bool selfTest(std::ostream& output) {
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;
const PublicState reflected = mirror(fixture);
expect(publicHash(fixture) == publicHash(reflected),
"public hash reflection self-test failed");
const ExactSearch direct = exactSearch(fixture);
const ExactSearch mirrored = exactSearch(reflected);
expect(direct.d1_action == kBoardSize - 1 - mirrored.d1_action &&
direct.d4_action == kBoardSize - 1 - mirrored.d4_action,
"exact action reflection self-test failed");
for (int action = 0; action < kBoardSize; ++action) {
const int other = kBoardSize - 1 - action;
expect(direct.d1_q[action] == mirrored.d1_q[other] &&
direct.d4_q[action] == mirrored.d4_q[other],
"exact Q reflection self-test failed");
}
State metadata = materialize(fixture);
metadata.score = 9'999'999;
metadata.level = 777;
metadata.moves_played = 888;
expect(publicState(metadata) == fixture && materialize(fixture).score == 0 &&
materialize(fixture).level == 1 &&
materialize(fixture).moves_played == 0,
"public-state/metadata self-test failed");
std::ostringstream synthetic;
synthetic << "{\"recordType\":\"deployment-panel-export-replay\","
<< "\"provenance\":{\"screenSeed\":" << kExpectedGameStart
<< ",\"moveIndex\":0,\"canonicalPublicHash\":\""
<< hex64(publicHash(fixture)) << "\",\"tapeSeed\":1},"
<< "\"modelInput\":{\"board\":\"" << board
<< "\",\"nextDisc\":6,\"movesRemaining\":3,"
"\"terminal\":false},\"excludedFromModelInput\":[],"
"\"gate\":\"ultra\",\"fairD1Action\":"
<< direct.d1_action << ",\"selectedAction\":"
<< direct.d1_action << ",\"switched\":false,\"actions\":[";
for (int action = 0; action < kBoardSize; ++action) {
if (action) synthetic << ',';
synthetic << "{\"action\":" << action
<< ",\"meanScoreReturn\":" << 1000 + action
<< ",\"meanSurvivedMoves\":" << 10 + action
<< ",\"meanNumberedClears\":" << action
<< ",\"meanCoversRevealed\":" << action / 2.0
<< ",\"survivingCutoffs\":0,\"pairedVsFairD1\":{"
"\"score\":{\"lowerOneSided99\":"
<< action << "},\"moves\":{\"lowerOneSided99\":"
<< action << "},\"materialDownsides\":0,"
"\"materialDownsideUpper99\":0}}";
}
synthetic << "]}";
const PanelRecord parsed = parsePanel(synthetic.str());
expect(parsed.state == fixture && parsed.stored_d1_action == direct.d1_action &&
parsed.actions[6].mean_score == 1006.0,
"panel parser self-test failed");
Ranking ranking;
std::array<double, kBoardSize> prediction{{0, 1, 2, 3, 4, 5, 6}};
std::array<double, kBoardSize> target{{0, 1, 2, 3, 4, 5, 6}};
observeRanking(ranking, prediction, target, parsed.actions, 6);
expect(ranking.roots == 1 && ranking.top1_credit == 1.0 &&
ranking.pairwise_credit == ranking.pairs &&
ranking.normalized_regret == 0.0,
"ranking metric self-test failed");
std::vector<GateExample> gate_examples;
for (int index = 0; index < 12; ++index) {
GateExample example;
example.game = static_cast<std::uint32_t>(index % 3);
example.features[0] = index / 11.0;
example.features[1] = -example.features[0];
example.features[2] = 0.25 + 0.01 * index;
example.features[3] = 0.5;
example.features[4] = (index % 5 + 1) / 5.0;
example.features[5] = (index % 3 - 1) / 7.0;
example.features[6] = (index % 2) / 3.0;
example.pareto_support = index >= 6;
gate_examples.push_back(example);
}
const GateModel first = fitGate(gate_examples, 99);
const GateModel second = fitGate(gate_examples, 99);
expect(first.beta == second.beta &&
predictGate(gate_examples.front(), first) <
predictGate(gate_examples.back(), first),
"deterministic risk-gate self-test failed");
enforceRss();
output << std::setprecision(12)
<< "TERMINAL_PANEL_D4_SIGNAL_AUDIT_SELF_TEST {\"passed\":true,"
<< "\"offlineOnly\":true,\"gameplaySeedLanes\":0,"
<< "\"parser\":true,\"publicStateOnly\":true,"
<< "\"metadataBlind\":true,\"reflection\":true,"
<< "\"exactD1\":true,\"exactD4\":true,"
<< "\"rankingMetrics\":true,\"wholeGameGate\":true,"
<< "\"peakRssBytes\":" << peakRssBytes() << "}\n";
return true;
}
int run(const Options& options, std::ostream& output) {
const Deadline deadline;
const std::vector<PanelRecord> panels = loadPanels(options);
output << "TERMINAL_PANEL_D4_SIGNAL_INPUT {\"records\":"
<< panels.size() << ",\"games\":" << kExpectedGames
<< ",\"sha256\":\"" << options.input_sha256
<< "\",\"gameplaySeedsOpened\":0}\n" << std::flush;
const std::vector<AuditRoot> roots =
evaluateAll(panels, options.threads, deadline);
const Stratified stratified = stratify(roots);
const GateAudit gate = auditGate(roots);
const WorkSummary work = summarizeWork(roots);
deadline.check();
enforceRss();
const double wall_seconds = deadline.seconds();
writeArtifact(options, stratified, gate, work, wall_seconds);
writeReadme(options, stratified, gate, work, wall_seconds);
output << std::setprecision(12)
<< "TERMINAL_PANEL_D4_SIGNAL_RESULT {\"records\":" << roots.size()
<< ",\"d4Differences\":" << stratified.overall.d4_differences
<< ",\"d4MeanParetoSupport\":"
<< stratified.overall.d4_mean_pareto_support
<< ",\"rawSignalBeyondD4\":"
<< (rawSignalBeyondD4(stratified) ? "true" : "false")
<< ",\"tinyGateLearnableSignal\":"
<< (learnableGateSignal(gate) ? "true" : "false")
<< ",\"wallSeconds\":" << wall_seconds
<< ",\"peakRssBytes\":" << peakRssBytes()
<< ",\"artifact\":\"" << jsonEscape(options.output)
<< "\",\"readme\":\"" << jsonEscape(options.readme) << "\"}\n";
return 0;
}
} // namespace drop7::terminal_panel_d4_signal_audit
int main(int argc, char** argv) {
try {
if (argc >= 2 && std::string_view(argv[1]) == "--self-test") {
return drop7::terminal_panel_d4_signal_audit::selfTest(std::cout)
? EXIT_SUCCESS
: EXIT_FAILURE;
}
if (argc >= 2 && std::string_view(argv[1]) == "--run") {
const auto options =
drop7::terminal_panel_d4_signal_audit::parseOptions(argc, argv, 2);
return drop7::terminal_panel_d4_signal_audit::run(options, std::cout);
}
std::cerr << "usage: drop7_terminal_panel_d4_signal_audit "
"--self-test | --run [--input PATH] [--input-sha256 HEX] "
"[--output PATH] [--readme PATH] [--threads 1..4]\n";
return 2;
} catch (const std::exception& error) {
std::cerr << "error: " << error.what() << '\n';
return 1;
}
}