mirror of
https://github.com/HChaZZY/Stockfish.git
synced 2025-12-20 00:56:39 +08:00
Optimisation of Position::see and Position::see_sign
Stephane's patch removes the only usage of Position::see, where the returned value isn't immediately compared with a value. So I replaced this function by its optimised and more specific version see_ge. This function also supersedes the function Position::see_sign. bool Position::see_ge(Move m, Value v) const; This function tests if the SEE of a move is greater or equal than a given value. We use forward iteration on captures instread of backward one, therefore we don't need the swapList array. Also we stop as soon as we have enough information to obtain the result, avoiding unnecessary calls to the min_attacker function. Speed tests (Windows 7), 20 runs for each engine: Test engine: mean 866648, st. dev. 5964 Base engine: mean 846751, st. dev. 22846 Speedup: 1.023 Speed test by Stephane Nicolet Fishtest STC test: LLR: 2.96 (-2.94,2.94) [0.00,5.00] Total: 26040 W: 4675 L: 4442 D: 16923 http://tests.stockfishchess.org/tests/view/57f648990ebc59038170fa03 No functional change.
This commit is contained in:
committed by
Marco Costalba
parent
1e586288ca
commit
073eed590e
133
src/position.cpp
133
src/position.cpp
@@ -955,102 +955,83 @@ Key Position::key_after(Move m) const {
|
||||
}
|
||||
|
||||
|
||||
/// Position::see() is a static exchange evaluator: It tries to estimate the
|
||||
/// material gain or loss resulting from a move.
|
||||
/// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the
|
||||
/// SEE value of move is greater or equal to the given value. We'll use an
|
||||
/// algorithm similar to alpha-beta pruning with a null window.
|
||||
|
||||
Value Position::see_sign(Move m) const {
|
||||
bool Position::see_ge(Move m, Value v) const {
|
||||
|
||||
assert(is_ok(m));
|
||||
|
||||
// Early return if SEE cannot be negative because captured piece value
|
||||
// is not less then capturing one. Note that king moves always return
|
||||
// here because king midgame value is set to 0.
|
||||
if (PieceValue[MG][moved_piece(m)] <= PieceValue[MG][piece_on(to_sq(m))])
|
||||
return VALUE_KNOWN_WIN;
|
||||
|
||||
return see(m);
|
||||
}
|
||||
|
||||
Value Position::see(Move m) const {
|
||||
|
||||
Square from, to;
|
||||
Bitboard occupied, attackers, stmAttackers;
|
||||
Value swapList[32];
|
||||
int slIndex = 1;
|
||||
PieceType nextVictim;
|
||||
Color stm;
|
||||
|
||||
assert(is_ok(m));
|
||||
|
||||
from = from_sq(m);
|
||||
to = to_sq(m);
|
||||
swapList[0] = PieceValue[MG][piece_on(to)];
|
||||
stm = color_of(piece_on(from));
|
||||
occupied = pieces() ^ from;
|
||||
|
||||
// Castling moves are implemented as king capturing the rook so cannot
|
||||
// be handled correctly. Simply return VALUE_ZERO that is always correct
|
||||
// unless in the rare case the rook ends up under attack.
|
||||
// Castling moves are implemented as king capturing the rook so cannot be
|
||||
// handled correctly. Simply assume the SEE value is VALUE_ZERO that is always
|
||||
// correct unless in the rare case the rook ends up under attack.
|
||||
if (type_of(m) == CASTLING)
|
||||
return VALUE_ZERO;
|
||||
return VALUE_ZERO >= v;
|
||||
|
||||
Square from = from_sq(m), to = to_sq(m);
|
||||
PieceType nextVictim = type_of(piece_on(from));
|
||||
Color stm = ~color_of(piece_on(from)); // First consider opponent's move
|
||||
Value balance; // Values of the pieces taken by us minus opponent's ones
|
||||
Bitboard occupied, stmAttackers;
|
||||
|
||||
if (type_of(m) == ENPASSANT)
|
||||
{
|
||||
occupied ^= to - pawn_push(stm); // Remove the captured pawn
|
||||
swapList[0] = PieceValue[MG][PAWN];
|
||||
occupied = SquareBB[to - pawn_push(~stm)]; // Remove the captured pawn
|
||||
balance = PieceValue[MG][PAWN];
|
||||
}
|
||||
else
|
||||
{
|
||||
balance = PieceValue[MG][piece_on(to)];
|
||||
occupied = 0;
|
||||
}
|
||||
|
||||
// Find all attackers to the destination square, with the moving piece
|
||||
// removed, but possibly an X-ray attacker added behind it.
|
||||
attackers = attackers_to(to, occupied) & occupied;
|
||||
if (balance < v)
|
||||
return false;
|
||||
|
||||
// If the opponent has no attackers we are finished
|
||||
stm = ~stm;
|
||||
stmAttackers = attackers & pieces(stm);
|
||||
occupied ^= to; // For the case when captured piece is a pinner
|
||||
if (nextVictim == KING)
|
||||
return true;
|
||||
|
||||
// Don't allow pinned pieces to attack pieces except the king as long all
|
||||
// pinners are on their original square.
|
||||
if (!(st->pinnersForKing[stm] & ~occupied))
|
||||
stmAttackers &= ~st->blockersForKing[stm];
|
||||
balance -= PieceValue[MG][nextVictim];
|
||||
|
||||
if (!stmAttackers)
|
||||
return swapList[0];
|
||||
if (balance >= v)
|
||||
return true;
|
||||
|
||||
// The destination square is defended, which makes things rather more
|
||||
// difficult to compute. We proceed by building up a "swap list" containing
|
||||
// the material gain or loss at each stop in a sequence of captures to the
|
||||
// destination square, where the sides alternately capture, and always
|
||||
// capture with the least valuable piece. After each capture, we look for
|
||||
// new X-ray attacks from behind the capturing piece.
|
||||
nextVictim = type_of(piece_on(from));
|
||||
bool relativeStm = true; // True if the opponent is to move
|
||||
occupied ^= pieces() ^ from ^ to;
|
||||
|
||||
do {
|
||||
assert(slIndex < 32);
|
||||
// Find all attackers to the destination square, with the moving piece removed,
|
||||
// but possibly an X-ray attacker added behind it.
|
||||
Bitboard attackers = attackers_to(to, occupied) & occupied;
|
||||
|
||||
// Add the new entry to the swap list
|
||||
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][nextVictim];
|
||||
while (true)
|
||||
{
|
||||
stmAttackers = attackers & pieces(stm);
|
||||
|
||||
// Don't allow pinned pieces to attack pieces except the king as long all
|
||||
// pinners are on their original square.
|
||||
if (!(st->pinnersForKing[stm] & ~occupied))
|
||||
stmAttackers &= ~st->blockersForKing[stm];
|
||||
|
||||
if (!stmAttackers)
|
||||
return relativeStm;
|
||||
|
||||
// Locate and remove the next least valuable attacker
|
||||
nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
|
||||
|
||||
if (nextVictim == KING)
|
||||
return relativeStm == bool(attackers & pieces(~stm));
|
||||
|
||||
balance += relativeStm ? PieceValue[MG][nextVictim]
|
||||
: -PieceValue[MG][nextVictim];
|
||||
|
||||
relativeStm = !relativeStm;
|
||||
|
||||
if (relativeStm == (balance >= v))
|
||||
return relativeStm;
|
||||
|
||||
stm = ~stm;
|
||||
stmAttackers = attackers & pieces(stm);
|
||||
|
||||
// Don't allow pinned pieces to attack pieces except the king
|
||||
if ( nextVictim != KING
|
||||
&& !(st->pinnersForKing[stm] & ~occupied))
|
||||
stmAttackers &= ~st->blockersForKing[stm];
|
||||
|
||||
++slIndex;
|
||||
|
||||
} while (stmAttackers && (nextVictim != KING || (--slIndex, false))); // Stop before a king capture
|
||||
|
||||
// Having built the swap list, we negamax through it to find the best
|
||||
// achievable score from the point of view of the side to move.
|
||||
while (--slIndex)
|
||||
swapList[slIndex - 1] = std::min(-swapList[slIndex], swapList[slIndex - 1]);
|
||||
|
||||
return swapList[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user