Merge 2d02ea46a1
into f5df517145
commit
7ee3ae29c6
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue