1
0
Fork 0
pull/3777/merge
xefoci7612 2021-11-15 11:21:31 -06:00 committed by GitHub
commit 7ee3ae29c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 22 additions and 19 deletions

View File

@ -21,6 +21,7 @@
#include <cmath>
#include <cstring> // For std::memset
#include <iostream>
#include <random>
#include <sstream>
#include "evaluate.h"
@ -124,11 +125,9 @@ namespace {
level = double(skill_level);
}
bool enabled() const { return level < 20.0; }
bool time_to_pick(Depth depth) const { return depth == 1 + int(level); }
Move pick_best(size_t multiPV);
Move pick_move(const RootMoves& rootMoves, size_t multiPV);
double level;
Move best = MOVE_NONE;
};
template <NodeType nodeType>
@ -325,7 +324,7 @@ void Thread::search() {
// When playing with strength handicap enable MultiPV search that we will
// use behind the scenes to retrieve a set of possible moves.
if (skill.enabled())
multiPV = std::max(multiPV, (size_t)4);
multiPV = std::max(multiPV, (size_t)5);
multiPV = std::min(multiPV, rootMoves.size());
@ -468,10 +467,6 @@ void Thread::search() {
if (!mainThread)
continue;
// If skill level is enabled and time is up, pick a sub-optimal best move
if (skill.enabled() && skill.time_to_pick(rootDepth))
skill.pick_best(multiPV);
// Do we have time for the next iteration? Can we stop searching now?
if ( Limits.use_time_management()
&& !Threads.stop
@ -529,8 +524,10 @@ void Thread::search() {
// If skill level is enabled, swap best PV line with the sub-optimal one
if (skill.enabled())
std::swap(rootMoves[0], *std::find(rootMoves.begin(), rootMoves.end(),
skill.best ? skill.best : skill.pick_best(multiPV)));
{
auto move = std::find(rootMoves.begin(), rootMoves.end(), skill.pick_move(rootMoves, multiPV));
std::swap(rootMoves[0], *move);
}
}
@ -1768,25 +1765,31 @@ moves_loop: // When in check, search starts here
// When playing with strength handicap, choose best move among a set of RootMoves
// using a statistical rule dependent on 'level'. Idea by Heinz van Saanen.
Move Skill::pick_best(size_t multiPV) {
const RootMoves& rootMoves = Threads.main()->rootMoves;
static PRNG rng(now()); // PRNG sequence should be non-deterministic
Move Skill::pick_move(const RootMoves& rootMoves, size_t multiPV) {
// RootMoves are already sorted by score in descending order
Move best = MOVE_NONE;
int maxScore = -VALUE_INFINITE;
Value topScore = rootMoves[0].score;
int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValueMg);
int maxScore = -VALUE_INFINITE;
double weakness = 120 - 2 * level;
double weakness = 130 - 2 * level;
// Use a normal distribution to not spread too much the random values, so
// that moves quality remains consistent with the set skill level.
float mean = delta * weakness / 2;
float stddev = mean * 0.25;
std::default_random_engine rng;
std::normal_distribution<float> normal(mean, stddev);
// Choose best move. For each move score we add two terms, both dependent on
// weakness. One is deterministic and bigger for weaker levels, and one is
// random. Then we choose the move with the resulting highest score.
// random with a normal probability distribution. Then we choose the move with
// the resulting highest score.
for (size_t i = 0; i < multiPV; ++i)
{
// This is our magic formula
int push = int(( weakness * int(topScore - rootMoves[i].score)
+ delta * (rng.rand<unsigned>() % int(weakness))) / 128);
int diff_to_top = topScore - rootMoves[i].score;
int push = int((weakness * diff_to_top + normal(rng)) / 128);
if (rootMoves[i].score + push >= maxScore)
{