diff --git a/cpp/command/gtp.cpp b/cpp/command/gtp.cpp index ef990ad7a..ac6161e99 100644 --- a/cpp/command/gtp.cpp +++ b/cpp/command/gtp.cpp @@ -348,6 +348,10 @@ struct GTPEngine { double genmoveTimeSum; + ClockTimer gameTimer; + double gameGenmoveTimeSum; + double friendlyMaxVisitsFactor; + GTPEngine( const string& modelFile, SearchParams initialParams, Rules initialRules, bool assumeMultiBlackHandicap, bool prevtEncore, @@ -357,7 +361,8 @@ struct GTPEngine { double genmoveWRN, double analysisWRN, bool genmoveAntiMir, bool analysisAntiMir, Player persp, int pvLen, - std::unique_ptr&& pbTable + std::unique_ptr&& pbTable, + double friendlyMaxVisitsFact ) :nnModelFile(modelFile), assumeMultipleStartingBlackMovesAreHandicap(assumeMultiBlackHandicap), @@ -387,7 +392,9 @@ struct GTPEngine { avoidMYTDaggerHack(avoidDagger), patternBonusTable(std::move(pbTable)), perspective(persp), - genmoveTimeSum(0.0) + genmoveTimeSum(0.0), + gameGenmoveTimeSum(0.0), + friendlyMaxVisitsFactor(friendlyMaxVisitsFact) { } @@ -406,7 +413,8 @@ struct GTPEngine { } void clearStatsForNewGame() { - //Currently nothing + gameTimer.reset(); + gameGenmoveTimeSum = 0.0; } //Specify -1 for the sizes for a default @@ -861,6 +869,19 @@ struct GTPEngine { double searchFactor = PlayUtils::getSearchFactor(searchFactorWhenWinningThreshold,searchFactorWhenWinning,params,recentWinLossValues,pla); lastSearchFactor = searchFactor; + //Adjust to opponent's playing speed + if (friendlyMaxVisitsFactor > 0.0) { + double opponentMoveCount = round(moveHistory.size() / 2.0); + if (opponentMoveCount > 0) { + double timeSpentOpponent = (gameTimer.getSeconds() - gameGenmoveTimeSum); + params.maxVisits = lround(timeSpentOpponent / opponentMoveCount * friendlyMaxVisitsFactor); + } else { + // KataGo plays the very first move of the game, take 1s + params.maxVisits = lround(friendlyMaxVisitsFactor); + } + bot->setParams(params); + } + Loc moveLoc; bot->setAvoidMoveUntilByLoc(args.avoidMoveUntilByLocBlack,args.avoidMoveUntilByLocWhite); if(args.analyzing) { @@ -914,6 +935,7 @@ struct GTPEngine { //output of various things. double timeTaken = timer.getSeconds(); genmoveTimeSum += timeTaken; + gameGenmoveTimeSum += timeTaken; //Chatting and logging ---------------------------- @@ -1550,6 +1572,7 @@ int MainCmds::gtp(int argc, const char* const* argv) { const double normalAvoidRepeatedPatternUtility = initialParams.avoidRepeatedPatternUtility; const double handicapAvoidRepeatedPatternUtility = (cfg.contains("avoidRepeatedPatternUtility") || cfg.contains("avoidRepeatedPatternUtility0")) ? initialParams.avoidRepeatedPatternUtility : 0.005; + const double friendlyMaxVisitsFactor = cfg.contains("friendlyMaxVisitsFactor") ? cfg.getDouble("friendlyMaxVisitsFactor", 0.0, 1e10) : 0.0; const int defaultBoardXSize = cfg.contains("defaultBoardXSize") ? cfg.getInt("defaultBoardXSize",2,Board::MAX_LEN) : @@ -1591,12 +1614,13 @@ int MainCmds::gtp(int argc, const char* const* argv) { genmoveWideRootNoise,analysisWideRootNoise, genmoveAntiMirror,analysisAntiMirror, perspective,analysisPVLen, - std::move(patternBonusTable) + std::move(patternBonusTable), + friendlyMaxVisitsFactor ); engine->setOrResetBoardSize(cfg,logger,seedRand,defaultBoardXSize,defaultBoardYSize); //If nobody specified any time limit in any way, then assume a relatively fast time control - if(!cfg.contains("maxPlayouts") && !cfg.contains("maxVisits") && !cfg.contains("maxTime")) { + if(!cfg.contains("maxPlayouts") && !cfg.contains("maxVisits") && !cfg.contains("maxTime") && !cfg.contains("friendlyMaxVisitsFactor")) { double mainTime = 1.0; double byoYomiTime = 5.0; int byoYomiPeriods = 5; diff --git a/cpp/configs/gtp_example.cfg b/cpp/configs/gtp_example.cfg index 0772198ba..85a8ad548 100644 --- a/cpp/configs/gtp_example.cfg +++ b/cpp/configs/gtp_example.cfg @@ -206,6 +206,11 @@ maxVisits = 500 # If provided, cap search time at this many seconds. # maxTime = 10 +# If provided, adapt maxVisits to averageOpponentTimePerMove * friendlyMaxVisitsFactor. (The static maxVisits setting will be ignored.) +# Setting this to 150% of the visits/s you normally get on your hardware makes KataGo play approximately equally fast as the opponent. +# Useful if you're running a ranked bot on a go server, and you want to be friendly if the opponent is also playing fast. +# friendlyMaxVisitsFactor = 500 + # Ponder on the opponent's turn? ponderingEnabled = false maxTimePondering = 60 # Maximum time to ponder, in seconds. Comment out to make unlimited. diff --git a/cpp/program/gtpconfig.cpp b/cpp/program/gtpconfig.cpp index 20a56cabc..53559f914 100644 --- a/cpp/program/gtpconfig.cpp +++ b/cpp/program/gtpconfig.cpp @@ -125,6 +125,11 @@ resignConsecTurns = 3 # If provided, cap search time at this many seconds. $$MAX_TIME +# If provided, adapt maxVisits to averageOpponentTimePerMove * friendlyMaxVisitsFactor. (The static maxVisits setting will be ignored.) +# Setting this to 150% of the visits/s you normally get on your hardware makes KataGo play approximately equally fast as the opponent. +# Useful if you're running a ranked bot on a go server, and you want to be friendly if the opponent is also playing fast. +# friendlyMaxVisitsFactor = 500 + # Ponder on the opponent's turn? $$PONDERING # Note: you can also set "maxVisitsPondering" or "maxPlayoutsPondering" too.