void id_loop(Position& pos);
Value value_to_tt(Value v, int ply);
Value value_from_tt(Value v, int ply);
- bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta);
bool allows(const Position& pos, Move first, Move second);
bool refutes(const Position& pos, Move first, Move second);
string uci_pv(const Position& pos, int depth, Value alpha, Value beta);
// Init futility move count array
for (d = 0; d < 32; d++)
{
- FutilityMoveCounts[0][d] = int(3.001 + 0.3 * pow(double(d ), 1.8)) * (d < 5 ? 4 : 3) / 4;
- FutilityMoveCounts[1][d] = int(3.001 + 0.3 * pow(double(d + 0.98), 1.8));
+ FutilityMoveCounts[0][d] = int(3 + 0.3 * pow(double(d ), 1.8)) * 3/4 + (2 < d && d < 5);
+ FutilityMoveCounts[1][d] = int(3 + 0.3 * pow(double(d + 0.98), 1.8));
}
}
Threads.sleepWhileIdle = Options["Idle Threads Sleep"];
// Set best timer interval to avoid lagging under time pressure. Timer is
- // used to check for remaining available thinking time.
+ // used to check for remaining available thinking time. Timer will be started
+ // at the end of first iteration to avoid returning with a random move.
Threads.timer->msec =
Limits.use_time_management() ? std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution)) :
- Limits.nodes ? 2 * TimerResolution
- : 100;
-
- Threads.timer->notify_one(); // Wake up the recurring timer
+ Limits.nodes ? 2 * TimerResolution : 100;
id_loop(RootPos); // Let's start searching !
while (++depth <= MAX_PLY && !Signals.stop && (!Limits.depth || depth <= Limits.depth))
{
// Age out PV variability metric
- BestMoveChanges *= 0.8;
+ BestMoveChanges *= 0.8f;
// Save last iteration's scores before first PV line is searched and all
// the move scores but the (new) PV are set to -VALUE_INFINITE.
assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
}
+ // Wake up the recurring timer after first iteration is finished
+ if (depth == 1)
+ Threads.timer->notify_one();
+
// Sort the PV lines searched so far and update the GUI
std::stable_sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1);
singularExtensionNode = !RootNode
&& !SpNode
- && depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY)
+ && depth >= 8 * ONE_PLY
&& ttMove != MOVE_NONE
&& !excludedMove // Recursive singular search is not allowed
&& (tte->bound() & BOUND_LOWER)
ext = ONE_PLY;
else if (givesCheck && pos.see_sign(move) >= 0)
- ext = ONE_PLY / 2;
+ ext = inCheck || ss->staticEval <= alpha ? ONE_PLY : ONE_PLY / 2;
// Singular extension search. If all moves but one fail low on a search of
// (alpha-s, beta-s), and just one fails high on (alpha, beta), then that move
// Step 15. Reduced depth search (LMR). If the move fails high will be
// re-searched at full depth.
- if ( depth > 3 * ONE_PLY
+ if ( depth >= 3 * ONE_PLY
&& !pvMove
&& !captureOrPromotion
&& move != ttMove
&& pos.see_sign(move) < 0)
continue;
- // Don't search useless checks
- if ( !PvNode
- && !InCheck
- && givesCheck
- && move != ttMove
- && !pos.is_capture_or_promotion(move)
- && ss->staticEval + PawnValueMg / 4 < beta
- && !check_is_dangerous(pos, move, futilityBase, beta))
- continue;
-
// Check for legality only before to do the move
if (!pos.pl_move_is_legal(move, ci.pinned))
continue;
}
- // check_is_dangerous() tests if a checking move can be pruned in qsearch()
-
- bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta)
- {
- Piece pc = pos.piece_moved(move);
- Square from = from_sq(move);
- Square to = to_sq(move);
- Color them = ~pos.side_to_move();
- Square ksq = pos.king_square(them);
- Bitboard enemies = pos.pieces(them);
- Bitboard kingAtt = pos.attacks_from<KING>(ksq);
- Bitboard occ = pos.pieces() ^ from ^ ksq;
- Bitboard oldAtt = pos.attacks_from(pc, from, occ);
- Bitboard newAtt = pos.attacks_from(pc, to, occ);
-
- // Checks which give opponent's king at most one escape square are dangerous
- if (!more_than_one(kingAtt & ~(enemies | newAtt | to)))
- return true;
-
- // Queen contact check is very dangerous
- if (type_of(pc) == QUEEN && (kingAtt & to))
- return true;
-
- // Creating new double threats with checks is dangerous
- Bitboard b = (enemies ^ ksq) & newAtt & ~oldAtt;
- while (b)
- {
- // Note that here we generate illegal "double move"!
- if (futilityBase + PieceValue[EG][pos.piece_on(pop_lsb(&b))] >= beta)
- return true;
- }
-
- return false;
- }
-
-
// allows() tests whether the 'first' move at previous ply somehow makes the
// 'second' move possible, for instance if the moving piece is the same in
// both moves. Normally the second move is the threat (the best move returned
assert(is_ok(first));
assert(is_ok(second));
assert(color_of(pos.piece_on(from_sq(second))) == ~pos.side_to_move());
- assert(color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move());
+ assert(type_of(first) == CASTLE || color_of(pos.piece_on(to_sq(first))) == ~pos.side_to_move());
Square m1from = from_sq(first);
Square m2from = from_sq(second);
Square m2to = to_sq(second);
// The piece is the same or second's destination was vacated by the first move
- if (m1to == m2from || m2to == m1from)
+ // We exclude the trivial case where a sliding piece does in two moves what
+ // it could do in one move: eg. Ra1a2, Ra2a3.
+ if ( m2to == m1from
+ || (m1to == m2from && !squares_aligned(m1from, m2from, m2to)))
return true;
// Second one moves through the square vacated by first one