#include "movepick.h"
#include <cassert>
#include <limits>
#include <utility>
#include "bitboard.h"
#include "misc.h"
#include "position.h"
namespace Stockfish {
namespace {
enum Stages {
MAIN_TT,
CAPTURE_INIT,
GOOD_CAPTURE,
QUIET_INIT,
GOOD_QUIET,
BAD_CAPTURE,
BAD_QUIET,
EVASION_TT,
EVASION_INIT,
EVASION,
PROBCUT_TT,
PROBCUT_INIT,
PROBCUT,
QSEARCH_TT,
QCAPTURE_INIT,
QCAPTURE
};
void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
for (ExtMove *sortedEnd = begin, *p = begin + 1; p < end; ++p)
if (p->value >= limit)
{
ExtMove tmp = *p, *q;
*p = *++sortedEnd;
for (q = sortedEnd; q != begin && *(q - 1) < tmp; --q)
*q = *(q - 1);
*q = tmp;
}
}
}
MovePicker::MovePicker(const Position& p,
Move ttm,
Depth d,
const ButterflyHistory* mh,
const LowPlyHistory* lph,
const CapturePieceToHistory* cph,
const PieceToHistory** ch,
const PawnHistory* ph,
int pl) :
pos(p),
mainHistory(mh),
lowPlyHistory(lph),
captureHistory(cph),
continuationHistory(ch),
pawnHistory(ph),
ttMove(ttm),
depth(d),
ply(pl) {
if (pos.checkers())
stage = EVASION_TT + !(ttm && pos.pseudo_legal(ttm));
else
stage = (depth > 0 ? MAIN_TT : QSEARCH_TT) + !(ttm && pos.pseudo_legal(ttm));
}
MovePicker::MovePicker(const Position& p, Move ttm, int th, const CapturePieceToHistory* cph) :
pos(p),
captureHistory(cph),
ttMove(ttm),
threshold(th) {
assert(!pos.checkers());
stage = PROBCUT_TT
+ !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm) && pos.see_ge(ttm, threshold));
}
template<GenType Type>
ExtMove* MovePicker::score(MoveList<Type>& ml) {
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
Color us = pos.side_to_move();
[[maybe_unused]] Bitboard threatByLesser[KING + 1];
if constexpr (Type == QUIETS)
{
threatByLesser[PAWN] = 0;
threatByLesser[KNIGHT] = threatByLesser[BISHOP] = pos.attacks_by<PAWN>(~us);
threatByLesser[ROOK] =
pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatByLesser[KNIGHT];
threatByLesser[QUEEN] = pos.attacks_by<ROOK>(~us) | threatByLesser[ROOK];
threatByLesser[KING] = pos.attacks_by<QUEEN>(~us) | threatByLesser[QUEEN];
}
ExtMove* it = cur;
for (auto move : ml)
{
ExtMove& m = *it++;
m = move;
const Square from = m.from_sq();
const Square to = m.to_sq();
const Piece pc = pos.moved_piece(m);
const PieceType pt = type_of(pc);
const Piece capturedPiece = pos.piece_on(to);
if constexpr (Type == CAPTURES)
m.value = (*captureHistory)[pc][to][type_of(capturedPiece)]
+ 7 * int(PieceValue[capturedPiece]) + 1024 * bool(pos.check_squares(pt) & to);
else if constexpr (Type == QUIETS)
{
m.value = 2 * (*mainHistory)[us][m.from_to()];
m.value += 2 * (*pawnHistory)[pawn_history_index(pos)][pc][to];
m.value += (*continuationHistory[0])[pc][to];
m.value += (*continuationHistory[1])[pc][to];
m.value += (*continuationHistory[2])[pc][to];
m.value += (*continuationHistory[3])[pc][to];
m.value += (*continuationHistory[5])[pc][to];
m.value += (bool(pos.check_squares(pt) & to) && pos.see_ge(m, -75)) * 16384;
static constexpr int bonus[KING + 1] = {0, 0, 144, 144, 256, 517, 10000};
int v = threatByLesser[pt] & to ? -95 : 100 * bool(threatByLesser[pt] & from);
m.value += bonus[pt] * v;
if (ply < LOW_PLY_HISTORY_SIZE)
m.value += 8 * (*lowPlyHistory)[ply][m.from_to()] / (1 + ply);
}
else
{
if (pos.capture_stage(m))
m.value = PieceValue[capturedPiece] + (1 << 28);
else
{
m.value = (*mainHistory)[us][m.from_to()] + (*continuationHistory[0])[pc][to];
if (ply < LOW_PLY_HISTORY_SIZE)
m.value += 2 * (*lowPlyHistory)[ply][m.from_to()] / (1 + ply);
}
}
}
return it;
}
template<typename Pred>
Move MovePicker::select(Pred filter) {
for (; cur < endCur; ++cur)
if (*cur != ttMove && filter())
return *cur++;
return Move::none();
}
Move MovePicker::next_move() {
constexpr int goodQuietThreshold = -14000;
top:
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 : {
MoveList<CAPTURES> ml(pos);
cur = endBadCaptures = moves;
endCur = endCaptures = score<CAPTURES>(ml);
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
++stage;
goto top;
}
case GOOD_CAPTURE :
if (select([&]() {
if (pos.see_ge(*cur, -cur->value / 18))
return true;
std::swap(*endBadCaptures++, *cur);
return false;
}))
return *(cur - 1);
++stage;
[[fallthrough]];
case QUIET_INIT :
if (!skipQuiets)
{
MoveList<QUIETS> ml(pos);
endCur = endGenerated = score<QUIETS>(ml);
partial_insertion_sort(cur, endCur, -3560 * depth);
}
++stage;
[[fallthrough]];
case GOOD_QUIET :
if (!skipQuiets && select([&]() { return cur->value > goodQuietThreshold; }))
return *(cur - 1);
cur = moves;
endCur = endBadCaptures;
++stage;
[[fallthrough]];
case BAD_CAPTURE :
if (select([]() { return true; }))
return *(cur - 1);
cur = endCaptures;
endCur = endGenerated;
++stage;
[[fallthrough]];
case BAD_QUIET :
if (!skipQuiets)
return select([&]() { return cur->value <= goodQuietThreshold; });
return Move::none();
case EVASION_INIT : {
MoveList<EVASIONS> ml(pos);
cur = moves;
endCur = endGenerated = score<EVASIONS>(ml);
partial_insertion_sort(cur, endCur, std::numeric_limits<int>::min());
++stage;
[[fallthrough]];
}
case EVASION :
case QCAPTURE :
return select([]() { return true; });
case PROBCUT :
return select([&]() { return pos.see_ge(*cur, threshold); });
}
assert(false);
return Move::none();
}
void MovePicker::skip_quiet_moves() { skipQuiets = true; }
}