}
bool pv_flag = false;
const auto score_move = [&stats, &bestMove, &pv_flag](Move move) -> U32 {
const auto score_move = [&bestMove, &pv_flag](Move move) -> U32 {
if (move == bestMove) return 30000;
if (stats.ply && stats.follow_pv) {
stats.follow_pv = 0;
if (stats.pv_table[0][stats.ply] == move) {
if (ply && follow_pv) {
follow_pv = 0;
if (pv_table[0][ply] == move) {
pv_flag = true;
return 20000;
}
}
const piece::Type type = move.piece().type;
if (move.is_capture()) return piece::score(type, move.piece_capture().type) + 10000;
if (stats.killer[0][stats.ply] == move) return 9000;
if (stats.killer[1][stats.ply] == move) return 8000;
return stats.history[to_underlying(type)][to_underlying(move.target())];
if (killer[0][ply] == move) return 9000;
if (killer[1][ply] == move) return 8000;
return history[to_underlying(type)][to_underlying(move.target())];
};
stats.follow_pv = pv_flag;
follow_pv = pv_flag;
int legal_moves = 0;
int searched = 0;
for (const auto [move, _] : MoveList(stats.board, score_move)) {
if (!stats_move_make(stats, copy, move, 0)) continue;
for (const auto [move, _] : MoveList(board, score_move)) {
if (!stats_move_make(copy, move, 0)) continue;
legal_moves++;
// futility pruning
if (futility && searched && !move.is_capture() && !move.is_promote() && !stats.board.is_check()) {
stats_move_unmake(stats, copy);
if (futility && searched && !move.is_capture() && !move.is_promote() && !board.is_check()) {
stats_move_unmake(copy);
continue;
}
if (!searched) {
score = -negamax(stats, -beta, -alpha, depth - 1, true);
score = -negamax(-beta, -alpha, depth - 1, true);
} else {
// Late Move Reduction
if (!pv_node && searched >= FULL_DEPTH && depth >= REDUCTION_LIMIT && !isCheck &&
!move.is_capture() && !move.is_promote() &&
(move.source() != stats.killer[0][stats.ply].source() ||
move.target() != stats.killer[0][stats.ply].target()) &&
(move.source() != stats.killer[1][stats.ply].source() ||
move.target() != stats.killer[1][stats.ply].target())) {
score = -negamax(stats, -alpha - 1, -alpha, depth - 2, true);
(move.source() != killer[0][ply].source() || move.target() != killer[0][ply].target()) &&
(move.source() != killer[1][ply].source() || move.target() != killer[1][ply].target())) {
score = -negamax(-alpha - 1, -alpha, depth - 2, true);
} else
score = alpha + 1;
// found better move
// Principal Variation Search
if (score > alpha) {
score = -negamax(stats, -alpha - 1, -alpha, depth - 1, true);
score = -negamax(-alpha - 1, -alpha, depth - 1, true);
// if fail research
if ((score > alpha) && (score < beta))
score = -negamax(stats, -beta, -alpha, depth - 1, true);
if ((score > alpha) && (score < beta)) score = -negamax(-beta, -alpha, depth - 1, true);
}
}
stats_move_unmake(stats, copy);
stats_move_unmake(copy);
searched++;
if (score > alpha) {
if (!move.is_capture()) {
stats.history[move.piece().index][to_underlying(move.target())] += depth;
history[move.piece().index][to_underlying(move.target())] += depth;
}
alpha = score;
flag = HasheFlag::Exact;
bestMove = move;
stats_pv_store(stats, move);
stats_pv_store(move);
if (score >= beta) {
stats.ttable.write(stats, bestMove, beta, depth, HasheFlag::Beta);
ttable.write(board, ply, bestMove, beta, depth, HasheFlag::Beta);
if (!move.is_capture()) {
stats.killer[1][stats.ply] = stats.killer[0][stats.ply];
stats.killer[0][stats.ply] = move;
killer[1][ply] = killer[0][ply];
killer[0][ply] = move;
}
return beta;