1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-04-30 08:43:09 +00:00

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 <mcostalba@gmail.com>
This commit is contained in:
Marco Costalba 2011-03-27 11:23:29 +01:00
parent d372f2e39a
commit 41fe70d703
2 changed files with 55 additions and 4 deletions

View file

@ -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<Depth>();
MateThreatExtension[1] = Options["Mate Threat Extension (PV nodes)"].value<Depth>();
MateThreatExtension[0] = Options["Mate Threat Extension (non-PV nodes)"].value<Depth>();
MultiPV = Options["MultiPV"].value<int>();
UCIMultiPV = Options["MultiPV"].value<int>();
SkillLevel = Options["Skill level"].value<int>();
UseLogFile = Options["Use Search Log"].value<bool>();
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<unsigned>();
// 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<unsigned>() % wk)) / 128;
if (s > max_s)
{
max_s = s;
bestMove = Rml[i].pv[0];
*ponderMove = Rml[i].pv[1];
}
}
}
return bestMove;
}

View file

@ -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);