Cleanup movepicker.
parent
8fc297c506
commit
f5a98be462
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HISTORY_H_INCLUDED
|
||||
#define HISTORY_H_INCLUDED
|
||||
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
/// StatsEntry stores the stat table value. It is usually a number but could
|
||||
/// be a move or even a nested history. We use a class instead of naked value
|
||||
/// to directly call history update operator<<() on the entry so to use stats
|
||||
/// tables at caller sites as simple multi-dim arrays.
|
||||
template<typename T, int D>
|
||||
class StatsEntry {
|
||||
|
||||
T entry;
|
||||
|
||||
public:
|
||||
void operator=(const T& v) { entry = v; }
|
||||
T* operator&() { return &entry; }
|
||||
T* operator->() { return &entry; }
|
||||
operator const T&() const { return entry; }
|
||||
|
||||
void operator<<(int bonus) {
|
||||
assert(abs(bonus) <= D); // Ensure range is [-D, D]
|
||||
static_assert(D <= std::numeric_limits<T>::max(), "D overflows T");
|
||||
|
||||
entry += bonus - entry * abs(bonus) / D;
|
||||
|
||||
assert(abs(entry) <= D);
|
||||
}
|
||||
};
|
||||
|
||||
/// Stats is a generic N-dimensional array used to store various statistics.
|
||||
/// The first template parameter T is the base type of the array, the second
|
||||
/// template parameter D limits the range of updates in [-D, D] when we update
|
||||
/// values with the << operator, while the last parameters (Size and Sizes)
|
||||
/// encode the dimensions of the array.
|
||||
template <typename T, int D, int Size, int... Sizes>
|
||||
struct Stats : public std::array<Stats<T, D, Sizes...>, Size>
|
||||
{
|
||||
using Self = Stats<T, D, Size, Sizes...>;
|
||||
|
||||
void fill(const T& v) {
|
||||
|
||||
// For standard-layout 'this' points to first struct member
|
||||
assert(std::is_standard_layout<Self>::value);
|
||||
|
||||
typedef StatsEntry<T, D> entry;
|
||||
entry* p = reinterpret_cast<entry*>(this);
|
||||
std::fill(p, p + sizeof(*this) / sizeof(entry), v);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, int D, int Size>
|
||||
struct Stats<T, D, Size> : public std::array<StatsEntry<T, D>, Size> {};
|
||||
|
||||
/// In stats table, D=0 means that the template parameter is not used
|
||||
enum StatsParams { NOT_USED = 0 };
|
||||
enum StatsType { NoCaptures, Captures };
|
||||
|
||||
/// ButterflyHistory records how often quiet moves have been successful or
|
||||
/// unsuccessful during the current search, and is used for reduction and move
|
||||
/// ordering decisions. It uses 2 tables (one for each color) indexed by
|
||||
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
|
||||
using ButterflyHistory = Stats< int16_t, 13365, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>;
|
||||
|
||||
/// At higher depths LowPlyHistory records successful quiet moves near the root
|
||||
/// and quiet moves which are/were in the PV (ttPv). It is cleared with each new
|
||||
/// search and filled during iterative deepening.
|
||||
constexpr int MAX_LPH = 4;
|
||||
using LowPlyHistory = Stats< int16_t, 10692, MAX_LPH, int(SQUARE_NB) * int(SQUARE_NB)>;
|
||||
|
||||
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
|
||||
/// move, see www.chessprogramming.org/Countermove_Heuristic
|
||||
using CounterMoveHistory = Stats< Move, NOT_USED, PIECE_NB, SQUARE_NB>;
|
||||
|
||||
/// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
|
||||
using CapturePieceToHistory = Stats< int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB>;
|
||||
|
||||
/// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to]
|
||||
using PieceToHistory = Stats< int16_t, 29952, PIECE_NB, SQUARE_NB>;
|
||||
|
||||
/// ContinuationHistory is the combined history of a given pair of moves, usually
|
||||
/// the current one given a previous one. The nested history table is based on
|
||||
/// PieceToHistory instead of ButterflyBoards.
|
||||
using ContinuationHistory = Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB>;
|
||||
|
||||
}
|
||||
|
||||
#endif // #ifndef HISTORY_H_INCLUDED
|
227
src/movepick.cpp
227
src/movepick.cpp
|
@ -24,13 +24,6 @@ namespace Stockfish {
|
|||
|
||||
namespace {
|
||||
|
||||
enum Stages {
|
||||
MAIN_TT, CAPTURE_INIT, GOOD_CAPTURE, REFUTATION, QUIET_INIT, QUIET, BAD_CAPTURE,
|
||||
EVASION_TT, EVASION_INIT, EVASION,
|
||||
PROBCUT_TT, PROBCUT_INIT, PROBCUT,
|
||||
QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK
|
||||
};
|
||||
|
||||
// partial_insertion_sort() sorts moves in descending order up to and including
|
||||
// a given limit. The order of moves smaller than the limit is left unspecified.
|
||||
void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
|
||||
|
@ -55,74 +48,110 @@ namespace {
|
|||
/// search captures, promotions, and some checks) and how important good move
|
||||
/// ordering is at the current node.
|
||||
|
||||
MovePicker::MovePicker(const Position& p, Move ttm) :
|
||||
pos(p),
|
||||
ttMove(ttm) {
|
||||
}
|
||||
|
||||
/// MovePicker constructor for the main search
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp,
|
||||
const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, const Move* killers, int pl)
|
||||
: pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch),
|
||||
ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) {
|
||||
MovePicker_Main::MovePicker_Main(const Position& p,
|
||||
Move ttm,
|
||||
Depth d,
|
||||
const ButterflyHistory* mh,
|
||||
const LowPlyHistory* lp,
|
||||
const CapturePieceToHistory* cph,
|
||||
const PieceToHistory** ch,
|
||||
Move cm,
|
||||
const Move* killers,
|
||||
int pl) :
|
||||
MovePicker(p, ttm),
|
||||
mainHistory(mh),
|
||||
lowPlyHistory(lp),
|
||||
captureHistory(cph),
|
||||
continuationHistory(ch),
|
||||
refutations{ { killers[0], 0 }, { killers[1], 0 }, { cm, 0 } },
|
||||
depth(d),
|
||||
ply(pl) {
|
||||
|
||||
assert(d > 0);
|
||||
|
||||
stage = (pos.checkers() ? EVASION_TT : MAIN_TT) +
|
||||
!(ttm && pos.pseudo_legal(ttm));
|
||||
const bool evasion = pos.checkers();
|
||||
const bool tt = ttm && pos.pseudo_legal(ttm);
|
||||
|
||||
stage = get_start_stage(evasion, tt);
|
||||
}
|
||||
|
||||
MovePicker_Main::Stages MovePicker_Main::get_start_stage(bool evasion, bool tt) {
|
||||
static constexpr Stages startStages[2][2] = {
|
||||
{ CAPTURE_INIT, MAIN_TT },
|
||||
{ EVASION_INIT, EVASION_TT }
|
||||
};
|
||||
|
||||
return startStages[evasion][tt];
|
||||
}
|
||||
|
||||
/// MovePicker constructor for quiescence search
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
|
||||
const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs)
|
||||
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) {
|
||||
MovePicker_Quiescence::MovePicker_Quiescence(const Position& p,
|
||||
Move ttm,
|
||||
Depth d,
|
||||
const ButterflyHistory* mh,
|
||||
const CapturePieceToHistory* cph,
|
||||
const PieceToHistory** ch,
|
||||
Square rs) :
|
||||
MovePicker(p, ttm),
|
||||
mainHistory(mh),
|
||||
captureHistory(cph),
|
||||
continuationHistory(ch),
|
||||
recaptureSquare(rs),
|
||||
depth(d) {
|
||||
|
||||
assert(d <= 0);
|
||||
|
||||
stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) +
|
||||
!( ttm
|
||||
&& (pos.checkers() || depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare)
|
||||
&& pos.pseudo_legal(ttm));
|
||||
const bool evasion = pos.checkers();
|
||||
const bool tt = ttm
|
||||
&& ( pos.checkers()
|
||||
|| depth > DEPTH_QS_RECAPTURES
|
||||
|| to_sq(ttm) == recaptureSquare)
|
||||
&& pos.pseudo_legal(ttm);
|
||||
|
||||
stage = get_start_stage(evasion, tt);
|
||||
}
|
||||
|
||||
MovePicker_Quiescence::Stages MovePicker_Quiescence::get_start_stage(bool evasion, bool tt) {
|
||||
static constexpr Stages startStages[2][2] = {
|
||||
{ QCAPTURE_INIT, QSEARCH_TT },
|
||||
{ EVASION_INIT, EVASION_TT }
|
||||
};
|
||||
|
||||
return startStages[evasion][tt];
|
||||
}
|
||||
|
||||
/// MovePicker constructor for ProbCut: we generate captures with SEE greater
|
||||
/// than or equal to the given threshold.
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph)
|
||||
: pos(p), captureHistory(cph), ttMove(ttm), threshold(th) {
|
||||
MovePicker_ProbCut::MovePicker_ProbCut(const Position& p,
|
||||
Move ttm,
|
||||
Value th,
|
||||
const CapturePieceToHistory* cph) :
|
||||
MovePicker(p, ttm),
|
||||
captureHistory(cph),
|
||||
threshold(th) {
|
||||
|
||||
assert(!pos.checkers());
|
||||
|
||||
stage = PROBCUT_TT + !(ttm && pos.capture(ttm)
|
||||
&& pos.pseudo_legal(ttm)
|
||||
&& pos.see_ge(ttm, threshold));
|
||||
const bool tt = ttm
|
||||
&& pos.capture(ttm)
|
||||
&& pos.pseudo_legal(ttm)
|
||||
&& pos.see_ge(ttm, threshold);
|
||||
|
||||
stage = get_start_stage(tt);
|
||||
}
|
||||
|
||||
/// MovePicker::score() assigns a numerical value to each move in a list, used
|
||||
/// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring
|
||||
/// captures with a good history. Quiets moves are ordered using the histories.
|
||||
template<GenType Type>
|
||||
void MovePicker::score() {
|
||||
MovePicker_ProbCut::Stages MovePicker_ProbCut::get_start_stage(bool tt) {
|
||||
static constexpr Stages startStages[2] = {
|
||||
PROBCUT_INIT, PROBCUT_TT
|
||||
};
|
||||
|
||||
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
|
||||
|
||||
for (auto& m : *this)
|
||||
if constexpr (Type == CAPTURES)
|
||||
m.value = int(PieceValue[MG][pos.piece_on(to_sq(m))]) * 6
|
||||
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))];
|
||||
|
||||
else if constexpr (Type == QUIETS)
|
||||
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
|
||||
+ 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (ply < MAX_LPH ? std::min(4, depth / 3) * (*lowPlyHistory)[ply][from_to(m)] : 0);
|
||||
|
||||
else // Type == EVASIONS
|
||||
{
|
||||
if (pos.capture(m))
|
||||
m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
|
||||
- Value(type_of(pos.moved_piece(m)));
|
||||
else
|
||||
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
|
||||
+ 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
|
||||
- (1 << 28);
|
||||
}
|
||||
return startStages[tt];
|
||||
}
|
||||
|
||||
/// MovePicker::select() returns the next move satisfying a predicate function.
|
||||
|
@ -143,30 +172,25 @@ Move MovePicker::select(Pred filter) {
|
|||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
/// MovePicker::next_move() is the most important method of the MovePicker class. It
|
||||
/// MovePicker_*::next_move() is the most important method of the MovePicker_* classes. It
|
||||
/// returns a new pseudo-legal move every time it is called until there are no more
|
||||
/// moves left, picking the move with the highest score from a list of generated moves.
|
||||
Move MovePicker::next_move(bool skipQuiets) {
|
||||
|
||||
top:
|
||||
Move MovePicker_Main::next_move(bool skipQuiets) {
|
||||
|
||||
switch (stage) {
|
||||
|
||||
case MAIN_TT:
|
||||
case EVASION_TT:
|
||||
case QSEARCH_TT:
|
||||
case PROBCUT_TT:
|
||||
++stage;
|
||||
return ttMove;
|
||||
|
||||
case CAPTURE_INIT:
|
||||
case PROBCUT_INIT:
|
||||
case QCAPTURE_INIT:
|
||||
cur = endBadCaptures = moves;
|
||||
endMoves = generate<CAPTURES>(pos, cur);
|
||||
|
||||
score<CAPTURES>();
|
||||
score_captures();
|
||||
++stage;
|
||||
goto top;
|
||||
[[fallthrough]];
|
||||
|
||||
case GOOD_CAPTURE:
|
||||
if (select<Best>([&](){
|
||||
|
@ -201,7 +225,7 @@ top:
|
|||
cur = endBadCaptures;
|
||||
endMoves = generate<QUIETS>(pos, cur);
|
||||
|
||||
score<QUIETS>();
|
||||
score_quiets();
|
||||
partial_insertion_sort(cur, endMoves, -3000 * depth);
|
||||
}
|
||||
|
||||
|
@ -225,19 +249,43 @@ top:
|
|||
case BAD_CAPTURE:
|
||||
return select<Next>([](){ return true; });
|
||||
|
||||
|
||||
|
||||
case EVASION_TT:
|
||||
++stage;
|
||||
return ttMove;
|
||||
|
||||
case EVASION_INIT:
|
||||
cur = moves;
|
||||
endMoves = generate<EVASIONS>(pos, cur);
|
||||
|
||||
score<EVASIONS>();
|
||||
score_evasions();
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
|
||||
case EVASION:
|
||||
return select<Best>([](){ return true; });
|
||||
}
|
||||
|
||||
case PROBCUT:
|
||||
return select<Best>([&](){ return pos.see_ge(*cur, threshold); });
|
||||
assert(false);
|
||||
return MOVE_NONE; // Silence warning
|
||||
}
|
||||
|
||||
Move MovePicker_Quiescence::next_move() {
|
||||
|
||||
switch (stage) {
|
||||
|
||||
case QSEARCH_TT:
|
||||
++stage;
|
||||
return ttMove;
|
||||
|
||||
case QCAPTURE_INIT:
|
||||
cur = endBadCaptures = moves;
|
||||
endMoves = generate<CAPTURES>(pos, cur);
|
||||
|
||||
score_captures();
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
|
||||
case QCAPTURE:
|
||||
if (select<Best>([&](){ return depth > DEPTH_QS_RECAPTURES
|
||||
|
@ -260,6 +308,47 @@ top:
|
|||
|
||||
case QCHECK:
|
||||
return select<Next>([](){ return true; });
|
||||
|
||||
|
||||
|
||||
case EVASION_TT:
|
||||
++stage;
|
||||
return ttMove;
|
||||
|
||||
case EVASION_INIT:
|
||||
cur = moves;
|
||||
endMoves = generate<EVASIONS>(pos, cur);
|
||||
|
||||
score_evasions();
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
|
||||
case EVASION:
|
||||
return select<Best>([](){ return true; });
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return MOVE_NONE; // Silence warning
|
||||
}
|
||||
|
||||
Move MovePicker_ProbCut::next_move() {
|
||||
|
||||
switch (stage) {
|
||||
|
||||
case PROBCUT_TT:
|
||||
++stage;
|
||||
return ttMove;
|
||||
|
||||
case PROBCUT_INIT:
|
||||
cur = endBadCaptures = moves;
|
||||
endMoves = generate<CAPTURES>(pos, cur);
|
||||
|
||||
score_captures();
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
|
||||
case PROBCUT:
|
||||
return select<Best>([&](){ return pos.see_ge(*cur, threshold); });
|
||||
}
|
||||
|
||||
assert(false);
|
||||
|
|
236
src/movepick.h
236
src/movepick.h
|
@ -23,139 +23,181 @@
|
|||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "history.h"
|
||||
#include "movegen.h"
|
||||
#include "position.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
/// StatsEntry stores the stat table value. It is usually a number but could
|
||||
/// be a move or even a nested history. We use a class instead of naked value
|
||||
/// to directly call history update operator<<() on the entry so to use stats
|
||||
/// tables at caller sites as simple multi-dim arrays.
|
||||
template<typename T, int D>
|
||||
class StatsEntry {
|
||||
/// MovePicker::score_*() assigns a numerical value to each move in a list, used
|
||||
/// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring
|
||||
/// captures with a good history. Quiets moves are ordered using the histories.
|
||||
|
||||
T entry;
|
||||
template <typename T>
|
||||
inline void score_captures_base(T& self) {
|
||||
for (auto& m : self)
|
||||
m.value = int(PieceValue[MG][self.pos.piece_on(to_sq(m))]) * 6
|
||||
+ (*self.captureHistory)[self.pos.moved_piece(m)][to_sq(m)][type_of(self.pos.piece_on(to_sq(m)))];
|
||||
}
|
||||
|
||||
public:
|
||||
void operator=(const T& v) { entry = v; }
|
||||
T* operator&() { return &entry; }
|
||||
T* operator->() { return &entry; }
|
||||
operator const T&() const { return entry; }
|
||||
template <typename T>
|
||||
inline void score_evasions_base(T& self) {
|
||||
for (auto& m : self)
|
||||
if (self.pos.capture(m))
|
||||
m.value = PieceValue[MG][self.pos.piece_on(to_sq(m))]
|
||||
- Value(type_of(self.pos.moved_piece(m)));
|
||||
else
|
||||
m.value = (*self.mainHistory)[self.pos.side_to_move()][from_to(m)]
|
||||
+ 2 * (*self.continuationHistory[0])[self.pos.moved_piece(m)][to_sq(m)]
|
||||
- (1 << 28);
|
||||
}
|
||||
|
||||
void operator<<(int bonus) {
|
||||
assert(abs(bonus) <= D); // Ensure range is [-D, D]
|
||||
static_assert(D <= std::numeric_limits<T>::max(), "D overflows T");
|
||||
|
||||
entry += bonus - entry * abs(bonus) / D;
|
||||
|
||||
assert(abs(entry) <= D);
|
||||
}
|
||||
};
|
||||
|
||||
/// Stats is a generic N-dimensional array used to store various statistics.
|
||||
/// The first template parameter T is the base type of the array, the second
|
||||
/// template parameter D limits the range of updates in [-D, D] when we update
|
||||
/// values with the << operator, while the last parameters (Size and Sizes)
|
||||
/// encode the dimensions of the array.
|
||||
template <typename T, int D, int Size, int... Sizes>
|
||||
struct Stats : public std::array<Stats<T, D, Sizes...>, Size>
|
||||
{
|
||||
typedef Stats<T, D, Size, Sizes...> stats;
|
||||
|
||||
void fill(const T& v) {
|
||||
|
||||
// For standard-layout 'this' points to first struct member
|
||||
assert(std::is_standard_layout<stats>::value);
|
||||
|
||||
typedef StatsEntry<T, D> entry;
|
||||
entry* p = reinterpret_cast<entry*>(this);
|
||||
std::fill(p, p + sizeof(*this) / sizeof(entry), v);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, int D, int Size>
|
||||
struct Stats<T, D, Size> : public std::array<StatsEntry<T, D>, Size> {};
|
||||
|
||||
/// In stats table, D=0 means that the template parameter is not used
|
||||
enum StatsParams { NOT_USED = 0 };
|
||||
enum StatsType { NoCaptures, Captures };
|
||||
|
||||
/// ButterflyHistory records how often quiet moves have been successful or
|
||||
/// unsuccessful during the current search, and is used for reduction and move
|
||||
/// ordering decisions. It uses 2 tables (one for each color) indexed by
|
||||
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
|
||||
typedef Stats<int16_t, 13365, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
|
||||
|
||||
/// At higher depths LowPlyHistory records successful quiet moves near the root
|
||||
/// and quiet moves which are/were in the PV (ttPv). It is cleared with each new
|
||||
/// search and filled during iterative deepening.
|
||||
constexpr int MAX_LPH = 4;
|
||||
typedef Stats<int16_t, 10692, MAX_LPH, int(SQUARE_NB) * int(SQUARE_NB)> LowPlyHistory;
|
||||
|
||||
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
|
||||
/// move, see www.chessprogramming.org/Countermove_Heuristic
|
||||
typedef Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB> CounterMoveHistory;
|
||||
|
||||
/// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
|
||||
typedef Stats<int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB> CapturePieceToHistory;
|
||||
|
||||
/// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to]
|
||||
typedef Stats<int16_t, 29952, PIECE_NB, SQUARE_NB> PieceToHistory;
|
||||
|
||||
/// ContinuationHistory is the combined history of a given pair of moves, usually
|
||||
/// the current one given a previous one. The nested history table is based on
|
||||
/// PieceToHistory instead of ButterflyBoards.
|
||||
typedef Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB> ContinuationHistory;
|
||||
template <typename T>
|
||||
inline void score_quiets_base(T& self) {
|
||||
for (auto& m : self)
|
||||
m.value = (*self.mainHistory)[self.pos.side_to_move()][from_to(m)]
|
||||
+ 2 * (*self.continuationHistory[0])[self.pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*self.continuationHistory[1])[self.pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*self.continuationHistory[3])[self.pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*self.continuationHistory[5])[self.pos.moved_piece(m)][to_sq(m)]
|
||||
+ (self.ply < MAX_LPH ? std::min(4, self.depth / 3) * (*self.lowPlyHistory)[self.ply][from_to(m)] : 0);
|
||||
}
|
||||
|
||||
|
||||
/// MovePicker class is used to pick one pseudo-legal move at a time from the
|
||||
/// MovePicker* classes are used to pick one pseudo-legal move at a time from the
|
||||
/// current position. The most important method is next_move(), which returns a
|
||||
/// new pseudo-legal move each time it is called, until there are no moves left,
|
||||
/// when MOVE_NONE is returned. In order to improve the efficiency of the
|
||||
/// alpha-beta algorithm, MovePicker attempts to return the moves which are most
|
||||
/// alpha-beta algorithm, Move pickers attempts to return the moves which are most
|
||||
/// likely to get a cut-off first.
|
||||
/// Different move pickers are needed in different places in search, sometimes
|
||||
/// excluding certain moves, or rating them differently. Each MovePicker*
|
||||
/// class represents one such use case.
|
||||
|
||||
class MovePicker {
|
||||
|
||||
protected:
|
||||
enum PickType { Next, Best };
|
||||
|
||||
public:
|
||||
MovePicker(const MovePicker&) = delete;
|
||||
MovePicker(MovePicker&&) = delete;
|
||||
MovePicker& operator=(const MovePicker&) = delete;
|
||||
MovePicker(const Position&, Move, Value, const CapturePieceToHistory*);
|
||||
MovePicker(const Position&, Move, Depth, const ButterflyHistory*,
|
||||
const CapturePieceToHistory*,
|
||||
const PieceToHistory**,
|
||||
Square);
|
||||
MovePicker(const Position&, Move, Depth, const ButterflyHistory*,
|
||||
const LowPlyHistory*,
|
||||
const CapturePieceToHistory*,
|
||||
const PieceToHistory**,
|
||||
Move,
|
||||
const Move*,
|
||||
int);
|
||||
Move next_move(bool skipQuiets = false);
|
||||
MovePicker& operator=(MovePicker&&) = delete;
|
||||
|
||||
MovePicker(const Position&, Move);
|
||||
|
||||
private:
|
||||
template<PickType T, typename Pred> Move select(Pred);
|
||||
template<GenType> void score();
|
||||
|
||||
ExtMove* begin() { return cur; }
|
||||
ExtMove* end() { return endMoves; }
|
||||
|
||||
const Position& pos;
|
||||
Move ttMove;
|
||||
ExtMove moves[MAX_MOVES];
|
||||
ExtMove* cur;
|
||||
ExtMove* endMoves;
|
||||
};
|
||||
|
||||
class MovePicker_Main : public MovePicker {
|
||||
private:
|
||||
enum Stages {
|
||||
MAIN_TT, CAPTURE_INIT, GOOD_CAPTURE, REFUTATION, QUIET_INIT, QUIET, BAD_CAPTURE,
|
||||
EVASION_TT, EVASION_INIT, EVASION
|
||||
};
|
||||
|
||||
public:
|
||||
MovePicker_Main(const Position&,
|
||||
Move,
|
||||
Depth,
|
||||
const ButterflyHistory*,
|
||||
const LowPlyHistory*,
|
||||
const CapturePieceToHistory*,
|
||||
const PieceToHistory**,
|
||||
Move,
|
||||
const Move*,
|
||||
int);
|
||||
|
||||
Move next_move(bool skipQuiets = false);
|
||||
|
||||
private:
|
||||
const ButterflyHistory* mainHistory;
|
||||
const LowPlyHistory* lowPlyHistory;
|
||||
const CapturePieceToHistory* captureHistory;
|
||||
const PieceToHistory** continuationHistory;
|
||||
Move ttMove;
|
||||
ExtMove refutations[3], *cur, *endMoves, *endBadCaptures;
|
||||
ExtMove refutations[3];
|
||||
ExtMove* endBadCaptures;
|
||||
int stage;
|
||||
Square recaptureSquare;
|
||||
Value threshold;
|
||||
Depth depth;
|
||||
int ply;
|
||||
ExtMove moves[MAX_MOVES];
|
||||
|
||||
Stages get_start_stage(bool evastion, bool tt);
|
||||
|
||||
auto score_captures() { return score_captures_base(*this); }
|
||||
auto score_evasions() { return score_evasions_base(*this); }
|
||||
auto score_quiets() { return score_quiets_base(*this); }
|
||||
template <typename T> friend void score_captures_base(T&);
|
||||
template <typename T> friend void score_evasions_base(T&);
|
||||
template <typename T> friend void score_quiets_base (T&);
|
||||
};
|
||||
|
||||
class MovePicker_Quiescence : public MovePicker {
|
||||
private:
|
||||
enum Stages {
|
||||
QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK,
|
||||
EVASION_TT, EVASION_INIT, EVASION
|
||||
};
|
||||
|
||||
public:
|
||||
MovePicker_Quiescence(const Position&,
|
||||
Move,
|
||||
Depth,
|
||||
const ButterflyHistory*,
|
||||
const CapturePieceToHistory*,
|
||||
const PieceToHistory**,
|
||||
Square);
|
||||
|
||||
Move next_move();
|
||||
|
||||
private:
|
||||
const ButterflyHistory* mainHistory;
|
||||
const CapturePieceToHistory* captureHistory;
|
||||
const PieceToHistory** continuationHistory;
|
||||
ExtMove* endBadCaptures;
|
||||
int stage;
|
||||
Square recaptureSquare;
|
||||
Depth depth;
|
||||
|
||||
Stages get_start_stage(bool evastion, bool tt);
|
||||
|
||||
auto score_captures() { return score_captures_base(*this); }
|
||||
auto score_evasions() { return score_evasions_base(*this); }
|
||||
template <typename T> friend void score_captures_base(T&);
|
||||
template <typename T> friend void score_evasions_base(T&);
|
||||
};
|
||||
|
||||
class MovePicker_ProbCut : public MovePicker {
|
||||
private:
|
||||
enum Stages {
|
||||
PROBCUT_TT, PROBCUT_INIT, PROBCUT
|
||||
};
|
||||
|
||||
public:
|
||||
MovePicker_ProbCut(const Position&, Move, Value, const CapturePieceToHistory*);
|
||||
Move next_move();
|
||||
|
||||
private:
|
||||
const CapturePieceToHistory* captureHistory;
|
||||
ExtMove* endBadCaptures;
|
||||
int stage;
|
||||
Value threshold;
|
||||
|
||||
Stages get_start_stage(bool tt);
|
||||
|
||||
auto score_captures() { return score_captures_base(*this); }
|
||||
template <typename T> friend void score_captures_base(T&);
|
||||
};
|
||||
|
||||
} // namespace Stockfish
|
||||
|
|
|
@ -854,7 +854,7 @@ namespace {
|
|||
{
|
||||
assert(probCutBeta < VALUE_INFINITE);
|
||||
|
||||
MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory);
|
||||
MovePicker_ProbCut mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory);
|
||||
int probCutCount = 0;
|
||||
bool ttPv = ss->ttPv;
|
||||
ss->ttPv = false;
|
||||
|
@ -932,13 +932,16 @@ moves_loop: // When in check, search starts from here
|
|||
|
||||
Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq];
|
||||
|
||||
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory,
|
||||
&thisThread->lowPlyHistory,
|
||||
&captureHistory,
|
||||
contHist,
|
||||
countermove,
|
||||
ss->killers,
|
||||
ss->ply);
|
||||
MovePicker_Main mp(pos,
|
||||
ttMove,
|
||||
depth,
|
||||
&thisThread->mainHistory,
|
||||
&thisThread->lowPlyHistory,
|
||||
&captureHistory,
|
||||
contHist,
|
||||
countermove,
|
||||
ss->killers,
|
||||
ss->ply);
|
||||
|
||||
value = bestValue;
|
||||
singularQuietLMR = moveCountPruning = false;
|
||||
|
@ -1463,10 +1466,13 @@ moves_loop: // When in check, search starts from here
|
|||
// to search the moves. Because the depth is <= 0 here, only captures,
|
||||
// queen promotions, and other checks (only if depth >= DEPTH_QS_CHECKS)
|
||||
// will be generated.
|
||||
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory,
|
||||
&thisThread->captureHistory,
|
||||
contHist,
|
||||
to_sq((ss-1)->currentMove));
|
||||
MovePicker_Quiescence mp(pos,
|
||||
ttMove,
|
||||
depth,
|
||||
&thisThread->mainHistory,
|
||||
&thisThread->captureHistory,
|
||||
contHist,
|
||||
to_sq((ss-1)->currentMove));
|
||||
|
||||
// Loop through the moves until no moves remain or a beta cutoff occurs
|
||||
while ((move = mp.next_move()) != MOVE_NONE)
|
||||
|
|
Loading…
Reference in New Issue