From 67a91d5554b98e2e70566f8f68266e104d563b6b Mon Sep 17 00:00:00 2001 From: 22nsuk <32119488+22nsuk@users.noreply.github.com> Date: Mon, 26 Oct 2026 15:45:56 +0900 Subject: [PATCH] Fix White winner for unscored SGF training results --- cpp/command/runtests.cpp | 1 + cpp/command/writetrainingdata.cpp | 48 ++++++++++++++++++++++++++----- cpp/tests/tests.h | 3 ++ 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/cpp/command/runtests.cpp b/cpp/command/runtests.cpp index e7dfa0c90c..0f909f7d93 100644 --- a/cpp/command/runtests.cpp +++ b/cpp/command/runtests.cpp @@ -52,6 +52,7 @@ int MainCmds::runtests(const vector& args) { Tests::runBoardStressTest(); Tests::runSgfTests(); + Tests::runTrainingDataResultTests(); Tests::runBasicSymmetryTests(); Tests::runBoardSymmetryTests(); Tests::runSymmetryDifferenceTests(); diff --git a/cpp/command/writetrainingdata.cpp b/cpp/command/writetrainingdata.cpp index 70119884a0..fcaca4a262 100644 --- a/cpp/command/writetrainingdata.cpp +++ b/cpp/command/writetrainingdata.cpp @@ -13,6 +13,7 @@ #include "../program/play.h" #include "../command/commandline.h" #include "../core/test.h" +#include "../tests/tests.h" #include "../main.h" #include @@ -20,6 +21,14 @@ using namespace std; +// Called after normalizing the result; other result forms have their own handling. +static bool parseSgfResultWithoutScore(const string& s, Player& winner) { + if(s != "b+" && s != "w+") + return false; + winner = s == "b+" ? P_BLACK : P_WHITE; + return true; +} + static ValueTargets makeForcedWinnerValueTarget(Player winner) { ValueTargets targets; if(winner == C_EMPTY) { @@ -41,6 +50,37 @@ static ValueTargets makeForcedWinnerValueTarget(Player winner) { return targets; } +void Tests::runTrainingDataResultTests() { + cout << "Running training data result tests" << endl; + struct TestCase { + const char* result; + Player winner; + }; + const TestCase cases[] = { + {"B+",P_BLACK}, {"W+",P_WHITE}, {"b+",P_BLACK}, {"w+",P_WHITE}, + {" B+ ",P_BLACK}, {" w+ ",P_WHITE} + }; + for(const TestCase& testCase: cases) { + std::unique_ptr sgf = Sgf::parse(string("(;GM[1]FF[4]SZ[9]RE[") + testCase.result + "])"); + string result = Global::trim(Global::toLower(sgf->getRootPropertyWithDefault("RE", ""))); + Player winner = C_EMPTY; + testAssert(parseSgfResultWithoutScore(result,winner)); + testAssert(winner == testCase.winner); + ValueTargets targets = makeForcedWinnerValueTarget(winner); + // Training values are stored from White's perspective before per-row conversion. + testAssert(targets.win == (testCase.winner == P_WHITE ? 1.0f : 0.0f)); + testAssert(targets.loss == (testCase.winner == P_BLACK ? 1.0f : 0.0f)); + testAssert(targets.noResult == 0.0f); + testAssert(targets.score == (testCase.winner == P_WHITE ? 0.5f : -0.5f)); + testAssert(!targets.hasLead); + } + for(const char* result: {"", "?", "draw", "b+r", "w+t", "w+3.5"}) { + Player winner = C_EMPTY; + testAssert(!parseSgfResultWithoutScore(result,winner)); + testAssert(winner == C_EMPTY); + } +} + static void getNNEval( const Board& board, const BoardHistory& hist, @@ -1575,15 +1615,9 @@ int MainCmds::writetrainingdata(const vector& args) { return; } } - else if(s == "b+") { - sgfGameEnded = true; - sgfGameEndedButRecordIncomplete = true; - sgfGameWinner = P_BLACK; - } - else if(s == "w+") { + else if(parseSgfResultWithoutScore(s,sgfGameWinner)) { sgfGameEnded = true; sgfGameEndedButRecordIncomplete = true; - sgfGameWinner = P_BLACK; } else { logger.write("Game ended with unknown result " + fileName + " result " + sgfResult); diff --git a/cpp/tests/tests.h b/cpp/tests/tests.h index 90c28f663b..fcf6da6bbc 100644 --- a/cpp/tests/tests.h +++ b/cpp/tests/tests.h @@ -40,6 +40,9 @@ namespace Tests { void runSgfTests(); void runSgfFileTests(); + //command/writetrainingdata.cpp + void runTrainingDataResultTests(); + //testnninputs.cpp void runNNInputsV3V4Tests(); void runExcludeTerritoryAtariNNInputsTests();