From: Marco Costalba Date: Sun, 27 Mar 2011 10:23:29 +0000 (+0100) Subject: Add "Skill level functionality X-Git-Url: https://git.sesse.net/?p=stockfish;a=commitdiff_plain;h=41fe70d7031993d1802659d01dd0710477bffcab Add "Skill level functionality It is now possible to adjust skill level of Stockfish from 10 (full strength) to 0. Skill adjustment is done in such a way that is CPU speed and time control largely independent, at least at low skills. It means that given a skill we have same play level on a mobile phone and on a super OCTAL CPU, at 1' per game or at 180'. At skill 9 strength is that of an average engine, I have used Crafty 20.14 to tune and we are more or less there. At skill 0 engine is pretty weak but still shows a realistic play. When skill is not used we don't have any impact on the regular code. Idea to use MultiPV is from Heinz van Saanen, implementation and formulas by me. No functional change. Signed-off-by: Marco Costalba --- diff --git a/src/search.cpp b/src/search.cpp index c393b75b..a2424ddc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -243,7 +243,7 @@ namespace { RootMoveList Rml; // MultiPV mode - int MultiPV; + int MultiPV, UCIMultiPV; // Time management variables int SearchStartTime, MaxNodes, MaxDepth, ExactMaxTime; @@ -255,6 +255,10 @@ namespace { bool UseLogFile; std::ofstream LogFile; + // Skill level adjustment + int SkillLevel; + RKISS RK; + // Multi-threads manager object ThreadsManager ThreadsMgr; @@ -503,11 +507,16 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[ PawnEndgameExtension[0] = Options["Pawn Endgame Extension (non-PV nodes)"].value(); MateThreatExtension[1] = Options["Mate Threat Extension (PV nodes)"].value(); MateThreatExtension[0] = Options["Mate Threat Extension (non-PV nodes)"].value(); - MultiPV = Options["MultiPV"].value(); + UCIMultiPV = Options["MultiPV"].value(); + SkillLevel = Options["Skill level"].value(); UseLogFile = Options["Use Search Log"].value(); read_evaluation_uci_options(pos.side_to_move()); + // Do we have to play with skill handicap? In this case enable MultiPV that + // we will use behind the scenes to retrieve a set of possible moves. + MultiPV = (SkillLevel < 10 ? Max(UCIMultiPV, 4) : UCIMultiPV); + // Set the number of active threads ThreadsMgr.read_uci_options(); init_eval(ThreadsMgr.active_threads()); @@ -687,11 +696,12 @@ namespace { // Collect info about search result bestMove = Rml[0].pv[0]; + *ponderMove = Rml[0].pv[1]; bestValues[depth] = value; bestMoveChanges[depth] = Rml.bestMoveChanges; // Send PV line to GUI and to log file - for (int i = 0; i < Min(MultiPV, (int)Rml.size()); i++) + for (int i = 0; i < Min(UCIMultiPV, (int)Rml.size()); i++) cout << Rml[i].pv_info_to_uci(pos, depth, alpha, beta, i) << endl; if (UseLogFile) @@ -746,7 +756,47 @@ namespace { } } - *ponderMove = Rml[0].pv[1]; + // When playing with strength handicap choose best move among the MultiPV + // set using a statistical rule dependent on SkillLevel. + if (SkillLevel < 10) + { + assert(MultiPV > 1); + + // Rml list is already sorted by pv_score in descending order + int s; + int max_s = -VALUE_INFINITE; + int size = Min(MultiPV, (int)Rml.size()); + int max = Rml[0].pv_score; + int var = Min(max - Rml[size - 1].pv_score, PawnValueMidgame); + int wk = 128 - 8 * SkillLevel; + + // PRNG sequence should be non deterministic + for (int i = abs(get_system_time() % 50); i > 0; i--) + RK.rand(); + + // Choose best move. For each move's score we add two terms both dependent + // on wk, one deterministic and bigger for weaker moves, and one random, + // then we choose the move with the resulting highest score. + for (int i = 0; i < size; i++) + { + s = Rml[i].pv_score; + + // Don't allow crazy blunders even at very low skills + if (i > 0 && Rml[i-1].pv_score > s + EasyMoveMargin) + break; + + // This is our magical formula + s += ((max - s) * wk + var * (RK.rand() % wk)) / 128; + + if (s > max_s) + { + max_s = s; + bestMove = Rml[i].pv[0]; + *ponderMove = Rml[i].pv[1]; + } + } + } + return bestMove; } diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 487778e5..cd7dc739 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -98,6 +98,7 @@ void init_uci_options() { Options["Ponder"] = Option(true); Options["OwnBook"] = Option(true); Options["MultiPV"] = Option(1, 1, 500); + Options["Skill level"] = Option(10, 0, 10); Options["Emergency Move Horizon"] = Option(40, 0, 50); Options["Emergency Base Time"] = Option(200, 0, 30000); Options["Emergency Move Time"] = Option(70, 0, 5000);