mirror of
https://github.com/sockspls/badfish
synced 2025-04-30 00:33:09 +00:00
Verified SEE pruning for capturing and checking moves.
Patch analyzes field after SEE exchanges concluded with a recapture by the opponent: if opponent Queen/Rook/King results under attack after the exchanges, we consider the move sharp and don't prune it. Important note: By accident I forgot to adjust 'occupied' when the king takes part in the exchanges. As result of this a move is considered sharp too, when opponent king apparently can evade check by recapturing. Surprisingly this seems contribute to patch's strength. STC: https://tests.stockfishchess.org/tests/view/640b16132644b62c33947397 LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 116400 W: 31239 L: 30817 D: 54344 Ptnml(0-2): 350, 12742, 31618, 13116, 374 LTC: https://tests.stockfishchess.org/tests/view/640c88092644b62c3394c1c5 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 177600 W: 47988 L: 47421 D: 82191 Ptnml(0-2): 62, 16905, 54317, 17436, 80 closes https://github.com/official-stockfish/Stockfish/pull/4453 bench: 5012145
This commit is contained in:
parent
02e4697055
commit
24b37e4586
5 changed files with 37 additions and 17 deletions
|
@ -95,7 +95,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece
|
|||
|
||||
stage = PROBCUT_TT + !(ttm && pos.capture_stage(ttm)
|
||||
&& pos.pseudo_legal(ttm)
|
||||
&& pos.see_ge(ttm, threshold));
|
||||
&& pos.see_ge(ttm, occupied, threshold));
|
||||
}
|
||||
|
||||
/// MovePicker::score() assigns a numerical value to each move in a list, used
|
||||
|
@ -197,7 +197,7 @@ top:
|
|||
|
||||
case GOOD_CAPTURE:
|
||||
if (select<Next>([&](){
|
||||
return pos.see_ge(*cur, Value(-cur->value)) ?
|
||||
return pos.see_ge(*cur, occupied, Value(-cur->value)) ?
|
||||
// Move losing capture to endBadCaptures to be tried later
|
||||
true : (*endBadCaptures++ = *cur, false); }))
|
||||
return *(cur - 1);
|
||||
|
@ -264,7 +264,7 @@ top:
|
|||
return select<Best>([](){ return true; });
|
||||
|
||||
case PROBCUT:
|
||||
return select<Next>([&](){ return pos.see_ge(*cur, threshold); });
|
||||
return select<Next>([&](){ return pos.see_ge(*cur, occupied, threshold); });
|
||||
|
||||
case QCAPTURE:
|
||||
if (select<Next>([&](){ return depth > DEPTH_QS_RECAPTURES
|
||||
|
|
|
@ -150,6 +150,7 @@ private:
|
|||
Value threshold;
|
||||
Depth depth;
|
||||
ExtMove moves[MAX_MOVES];
|
||||
Bitboard occupied;
|
||||
};
|
||||
|
||||
} // namespace Stockfish
|
||||
|
|
|
@ -1062,7 +1062,7 @@ Key Position::key_after(Move m) const {
|
|||
/// SEE value of move is greater or equal to the given threshold. We'll use an
|
||||
/// algorithm similar to alpha-beta pruning with a null window.
|
||||
|
||||
bool Position::see_ge(Move m, Value threshold) const {
|
||||
bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const {
|
||||
|
||||
assert(is_ok(m));
|
||||
|
||||
|
@ -1081,7 +1081,7 @@ bool Position::see_ge(Move m, Value threshold) const {
|
|||
return true;
|
||||
|
||||
assert(color_of(piece_on(from)) == sideToMove);
|
||||
Bitboard occupied = pieces() ^ from ^ to;
|
||||
occupied = pieces() ^ from ^ to; // xoring to is important for pinned piece logic
|
||||
Color stm = sideToMove;
|
||||
Bitboard attackers = attackers_to(to, occupied);
|
||||
Bitboard stmAttackers, bb;
|
||||
|
@ -1112,45 +1112,44 @@ bool Position::see_ge(Move m, Value threshold) const {
|
|||
// the bitboard 'attackers' any X-ray attackers behind it.
|
||||
if ((bb = stmAttackers & pieces(PAWN)))
|
||||
{
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
if ((swap = PawnValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
|
||||
}
|
||||
|
||||
else if ((bb = stmAttackers & pieces(KNIGHT)))
|
||||
{
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
if ((swap = KnightValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
}
|
||||
|
||||
else if ((bb = stmAttackers & pieces(BISHOP)))
|
||||
{
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
if ((swap = BishopValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
|
||||
}
|
||||
|
||||
else if ((bb = stmAttackers & pieces(ROOK)))
|
||||
{
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
if ((swap = RookValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
attackers |= attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN);
|
||||
}
|
||||
|
||||
else if ((bb = stmAttackers & pieces(QUEEN)))
|
||||
{
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
if ((swap = QueenValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
|
||||
| (attacks_bb<ROOK >(to, occupied) & pieces(ROOK , QUEEN));
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@ public:
|
|||
void undo_null_move();
|
||||
|
||||
// Static Exchange Evaluation
|
||||
bool see_ge(Move m, Value threshold = VALUE_ZERO) const;
|
||||
bool see_ge(Move m, Bitboard& occupied, Value threshold = VALUE_ZERO) const;
|
||||
|
||||
// Accessing hash keys
|
||||
Key key() const;
|
||||
|
|
|
@ -1019,9 +1019,27 @@ moves_loop: // When in check, search starts here
|
|||
+ captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha)
|
||||
continue;
|
||||
|
||||
Bitboard occupied;
|
||||
// SEE based pruning (~11 Elo)
|
||||
if (!pos.see_ge(move, Value(-206) * depth))
|
||||
continue;
|
||||
if (!pos.see_ge(move, occupied, Value(-206) * depth))
|
||||
{
|
||||
if (depth < 2 - capture)
|
||||
continue;
|
||||
// don't prune move if a heavy enemy piece (KQR) is under attack after the exchanges
|
||||
Bitboard leftEnemies = (pos.pieces(~us, QUEEN, ROOK) | pos.pieces(~us, KING)) & occupied;
|
||||
Bitboard attacks = 0;
|
||||
occupied |= to_sq(move);
|
||||
while (leftEnemies && !attacks)
|
||||
{
|
||||
Square sq = pop_lsb(leftEnemies);
|
||||
attacks |= pos.attackers_to(sq, occupied) & pos.pieces(us) & occupied;
|
||||
// exclude Queen/Rook(s) which were already threatened before SEE
|
||||
if (attacks && (sq != pos.square<KING>(~us) && (pos.attackers_to(sq, pos.pieces()) & pos.pieces(us))))
|
||||
attacks = 0;
|
||||
}
|
||||
if (!attacks)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1047,8 +1065,9 @@ moves_loop: // When in check, search starts here
|
|||
|
||||
lmrDepth = std::max(lmrDepth, 0);
|
||||
|
||||
Bitboard occupied;
|
||||
// Prune moves with negative SEE (~4 Elo)
|
||||
if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth)))
|
||||
if (!pos.see_ge(move, occupied, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth)))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -1533,6 +1552,7 @@ moves_loop: // When in check, search starts here
|
|||
prevSq);
|
||||
|
||||
int quietCheckEvasions = 0;
|
||||
Bitboard occupied;
|
||||
|
||||
// Step 5. Loop through all pseudo-legal moves until no moves remain
|
||||
// or a beta cutoff occurs.
|
||||
|
@ -1569,7 +1589,7 @@ moves_loop: // When in check, search starts here
|
|||
continue;
|
||||
}
|
||||
|
||||
if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1))
|
||||
if (futilityBase <= alpha && !pos.see_ge(move, occupied, VALUE_ZERO + 1))
|
||||
{
|
||||
bestValue = std::max(bestValue, futilityBase);
|
||||
continue;
|
||||
|
@ -1588,7 +1608,7 @@ moves_loop: // When in check, search starts here
|
|||
continue;
|
||||
|
||||
// Do not search moves with bad enough SEE values (~5 Elo)
|
||||
if (!pos.see_ge(move, Value(-110)))
|
||||
if (!pos.see_ge(move, occupied, Value(-110)))
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue