b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
- // Enemy rooks checks
- rookChecks = b1 & safe & attackedBy[Them][ROOK];
- if (rookChecks)
- kingDanger += more_than_one(rookChecks) ? RookSafeCheck * 3/2
- : RookSafeCheck;
- else
- unsafeChecks |= b1 & attackedBy[Them][ROOK];
-
- // Enemy queen safe checks: we count them only if they are from squares from
- // which we can't give a rook check, because rook checks are more valuable.
- queenChecks = (b1 | b2)
- & attackedBy[Them][QUEEN]
- & safe
- & ~attackedBy[Us][QUEEN]
- & ~rookChecks;
- if (queenChecks)
- kingDanger += more_than_one(queenChecks) ? QueenSafeCheck * 3/2
- : QueenSafeCheck;
-
- // Enemy bishops checks: we count them only if they are from squares from
- // which we can't give a queen check, because queen checks are more valuable.
- bishopChecks = b2
- & attackedBy[Them][BISHOP]
- & safe
- & ~queenChecks;
- if (bishopChecks)
- kingDanger += more_than_one(bishopChecks) ? BishopSafeCheck * 3/2
- : BishopSafeCheck;
- else
- unsafeChecks |= b2 & attackedBy[Them][BISHOP];
+ std::function <Bitboard (Color, PieceType)> get_attacks = [this](Color c, PieceType pt) {
+ return attackedBy[c][pt] | (pos.piece_drops() && pos.count_in_hand(c, pt) ? pos.drop_region(c, pt) & ~pos.pieces() : Bitboard(0));
+ };
+ for (PieceType pt : pos.piece_types())
+ {
+ switch (pt)
+ {
+ case QUEEN:
+ // Enemy queen safe checks: we count them only if they are from squares from
+ // which we can't give a rook check, because rook checks are more valuable.
+ queenChecks = (b1 | b2)
+ & get_attacks(Them, QUEEN)
+ & pos.board_bb()
+ & safe
+ & ~attackedBy[Us][QUEEN]
+ & ~(b1 & attackedBy[Them][ROOK]);
+
+ if (queenChecks)
- kingDanger += QueenSafeCheck;
++ kingDanger += more_than_one(queenChecks) ? QueenSafeCheck * 3/2
++ : QueenSafeCheck;
+ break;
+ case ROOK:
+ case BISHOP:
+ case KNIGHT:
+ knightChecks = attacks_bb(Us, pt, ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)) & get_attacks(Them, pt) & pos.board_bb();
+ if (knightChecks & safe)
- kingDanger += pt == ROOK ? RookSafeCheck
++ kingDanger += (pt == ROOK ? RookSafeCheck
+ : pt == BISHOP ? BishopSafeCheck
- : KnightSafeCheck;
++ : KnightSafeCheck) * (more_than_one(knightChecks & safe) ? 3 : 2) / 2;
+ else
+ unsafeChecks |= knightChecks;
+ break;
+ case PAWN:
+ if (pos.piece_drops() && pos.count_in_hand(Them, pt))
+ {
+ pawnChecks = attacks_bb(Us, pt, ksq, pos.pieces()) & ~pos.pieces() & pos.board_bb();
+ if (pawnChecks & safe)
+ kingDanger += OtherSafeCheck;
+ else
+ unsafeChecks |= pawnChecks;
+ }
+ break;
+ case SHOGI_PAWN:
+ case KING:
+ break;
+ default:
+ otherChecks = attacks_bb(Us, pt, ksq, pos.pieces()) & get_attacks(Them, pt) & pos.board_bb();
+ if (otherChecks & safe)
- kingDanger += OtherSafeCheck;
++ kingDanger += OtherSafeCheck * (more_than_one(otherChecks & safe) ? 3 : 2) / 2;
+ else
+ unsafeChecks |= otherChecks;
+ }
+ }
- // Enemy knights checks
- knightChecks = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
- if (knightChecks & safe)
- kingDanger += more_than_one(knightChecks & safe) ? KnightSafeCheck * 3/2
- : KnightSafeCheck;
- else
- unsafeChecks |= knightChecks;
+ // Virtual piece drops
+ if (pos.two_boards() && pos.piece_drops())
+ {
+ for (PieceType pt : pos.piece_types())
+ if (!pos.count_in_hand(Them, pt) && (attacks_bb(Us, pt, ksq, pos.pieces()) & safe & pos.drop_region(Them, pt) & ~pos.pieces()))
+ {
+ kingDanger += OtherSafeCheck * 500 / (500 + PieceValue[MG][pt]);
+ // Presumably a mate threat
+ if (!(attackedBy[Us][KING] & ~(attackedBy[Them][ALL_PIECES] | pos.pieces(Us))))
+ kingDanger += 2000;
+ }
+ }
+
+ if (pos.check_counting())
+ kingDanger += kingDanger * 7 / (3 + pos.checks_remaining(Them));
+
+ Square s = file_of(ksq) == FILE_A ? ksq + EAST : file_of(ksq) == pos.max_file() ? ksq + WEST : ksq;
+ Bitboard kingFlank = pos.max_file() == FILE_H ? KingFlank[file_of(ksq)] : file_bb(s) | adjacent_files_bb(s);
// Find the squares that opponent attacks in our king flank, the squares
// which they attack twice in that flank, and the squares that we defend.
int sf = me->scale_factor(pos, strongSide);
// If scale is not already specific, scale down the endgame via general heuristics
- if (sf == SCALE_FACTOR_NORMAL)
+ if (sf == SCALE_FACTOR_NORMAL && !pos.captures_to_hand())
{
- if ( pos.opposite_bishops()
- && pos.non_pawn_material() == 2 * BishopValueMg)
- sf = 22;
+ if (pos.opposite_bishops())
+ {
+ if ( pos.non_pawn_material(WHITE) == BishopValueMg
+ && pos.non_pawn_material(BLACK) == BishopValueMg)
+ sf = 22;
+ else
+ sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide);
+ }
else
- sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * (pos.count<PAWN>(strongSide) + pos.count<SOLDIER>(strongSide)));
- sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide));
++ sf = std::min(sf, 36 + 7 * (pos.count<PAWN>(strongSide) + pos.count<SOLDIER>(strongSide)));
sf = std::max(0, sf - (pos.rule50_count() - 12) / 4);
}
// In endgame we like to bring our king near our closest pawn
Bitboard pawns = pos.pieces(Us, PAWN);
- int minPawnDist = pawns ? 8 : 0;
+ int minPawnDist = 6;
- if (pawns & PseudoAttacks[KING][ksq])
+ if (pawns & PseudoAttacks[Us][KING][ksq])
minPawnDist = 1;
else while (pawns)
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns)));
if (!rootNode)
{
+ Value variantResult;
+ if (pos.is_game_end(variantResult, ss->ply))
+ return variantResult;
+
// Step 2. Check for aborted search and immediate draw
if ( Threads.stop.load(std::memory_order_relaxed)
- || pos.is_draw(ss->ply)
|| ss->ply >= MAX_PLY)
- return (ss->ply >= MAX_PLY && !inCheck) ? evaluate(pos)
+ return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos)
: value_draw(pos.this_thread());
// Step 3. Mate distance pruning. Even if we mate at the next move our score
probCutCount++;
ss->currentMove = move;
- ss->continuationHistory = &thisThread->continuationHistory[inCheck]
+ ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
[captureOrPromotion]
- [pos.moved_piece(move)]
+ [history_slot(pos.moved_piece(move))]
[to_sq(move)];
pos.do_move(move, st);
// Futility pruning: parent node (~5 Elo)
if ( lmrDepth < 6
- && !inCheck
+ && !ss->inCheck
- && ss->staticEval + 235 + 172 * lmrDepth <= alpha
- && (*contHist[0])[movedPiece][to_sq(move)]
- + (*contHist[1])[movedPiece][to_sq(move)]
- + (*contHist[3])[movedPiece][to_sq(move)] < 27400)
+ && !( pos.extinction_value() == -VALUE_MATE
+ && pos.extinction_piece_types().find(ALL_PIECES) == pos.extinction_piece_types().end())
+ && ss->staticEval + (235 + 172 * lmrDepth) * (1 + pos.check_counting()) <= alpha
+ && (*contHist[0])[history_slot(movedPiece)][to_sq(move)]
+ + (*contHist[1])[history_slot(movedPiece)][to_sq(move)]
+ + (*contHist[3])[history_slot(movedPiece)][to_sq(move)] < 27400)
continue;
// Prune moves with negative SEE (~20 Elo)
&& captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0)
continue;
+ // Futility pruning for captures
+ if ( !givesCheck
+ && lmrDepth < 6
+ && !ss->inCheck
+ && ss->staticEval + 270 + 384 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha)
+ continue;
+
// See based pruning
- if (!pos.see_ge(move, Value(-194) * depth)) // (~25 Elo)
+ if (!pos.see_ge(move, Value(-194 - 120 * pos.captures_to_hand()) * depth)) // (~25 Elo)
continue;
}
}
// Update the current move (this must be done after singular extension search)
ss->currentMove = move;
- ss->continuationHistory = &thisThread->continuationHistory[inCheck]
+ ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
[captureOrPromotion]
- [movedPiece]
+ [history_slot(movedPiece)]
[to_sq(move)];
// Step 15. Make the move
if (!moveCount)
bestValue = excludedMove ? alpha
- : inCheck ? pos.checkmate_value(ss->ply) : pos.stalemate_value(ss->ply);
- : ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW;
++ : ss->inCheck ? pos.checkmate_value(ss->ply) : pos.stalemate_value(ss->ply);
else if (bestMove)
update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq,
Thread* thisThread = pos.this_thread();
(ss+1)->ply = ss->ply + 1;
bestMove = MOVE_NONE;
- inCheck = pos.checkers();
+ ss->inCheck = pos.checkers();
moveCount = 0;
- // Check for an immediate draw or maximum ply reached
- if ( pos.is_draw(ss->ply)
- || ss->ply >= MAX_PLY)
- return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) : VALUE_DRAW;
+ Value gameResult;
+ if (pos.is_game_end(gameResult, ss->ply))
+ return gameResult;
+
+ // Check for maximum ply reached
+ if (ss->ply >= MAX_PLY)
- return !inCheck ? evaluate(pos) : VALUE_DRAW;
++ return !ss->inCheck ? evaluate(pos) : VALUE_DRAW;
assert(0 <= ss->ply && ss->ply < MAX_PLY);
moveCount++;
// Futility pruning
- if ( !inCheck
+ if ( !ss->inCheck
&& !givesCheck
+ && !( pos.extinction_value() == -VALUE_MATE
+ && pos.piece_on(to_sq(move))
+ && pos.extinction_piece_types().find(type_of(pos.piece_on(to_sq(move)))) != pos.extinction_piece_types().end())
&& futilityBase > -VALUE_KNOWN_WIN
&& !pos.advanced_pawn_push(move))
{
}
ss->currentMove = move;
- ss->continuationHistory = &thisThread->continuationHistory[inCheck]
+ ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
[captureOrPromotion]
- [pos.moved_piece(move)]
+ [history_slot(pos.moved_piece(move))]
[to_sq(move)];
// Make and search the move
// All legal moves have been searched. A special case: If we're in check
// and no legal moves were found, it is checkmate.
- if (inCheck && bestValue == -VALUE_INFINITE)
+ if (ss->inCheck && bestValue == -VALUE_INFINITE)
- return mated_in(ss->ply); // Plies to mate from the root
+ return pos.checkmate_value(ss->ply); // Plies to mate from the root
tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit,
bestValue >= beta ? BOUND_LOWER :
void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) {
for (int i : {1, 2, 4, 6})
+ {
+ if (ss->inCheck && i > 2)
+ break;
if (is_ok((ss-i)->currentMove))
- (*(ss-i)->continuationHistory)[pc][to] << bonus;
+ (*(ss-i)->continuationHistory)[history_slot(pc)][to] << bonus;
+ }
}