Skip to content

Commit 83ecfa7

Browse files
Rocky640snicolet
authored andcommitted
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might indulge some tricks to make it faster. Another pull request #2469 showed some speedup by removing templates, this version uses Ronald de Man (@syzygy1) SEE implementation which also unrolls the for loop by suppressing the min_attacker() helper function and exits as soon as the last swap is conclusive. See Ronald de Man version there: https://github.com/syzygy1/Cfish/blob/master/src/position.c Patch testes against pull request #2469: LLR: 2.95 (-2.94,2.94) {-1.00,3.00} Total: 19365 W: 3771 L: 3634 D: 11960 Ptnml(0-2): 241, 1984, 5099, 2092, 255 http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3 And since we are using new SPRT statistics, and that both pull requests finished with less than 20000 games I also tested against master as a speed-up: LLR: 2.99 (-2.94,2.94) {-1.00,3.00} Total: 18878 W: 3674 L: 3539 D: 11665 Ptnml(0-2): 193, 1999, 4966, 2019, 250 http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745 Non functional change
1 parent 44f56e0 commit 83ecfa7

File tree

1 file changed

+65
-81
lines changed

1 file changed

+65
-81
lines changed

src/position.cpp

Lines changed: 65 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -50,41 +50,6 @@ const string PieceToChar(" PNBRQK pnbrqk");
5050

5151
constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
5252
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
53-
54-
// min_attacker() is a helper function used by see_ge() to locate the least
55-
// valuable attacker for the side to move, remove the attacker we just found
56-
// from the bitboards and scan for new X-ray attacks behind it.
57-
58-
template<PieceType Pt>
59-
PieceType min_attacker(const Bitboard* byTypeBB, Square to, Bitboard stmAttackers,
60-
Bitboard& occupied, Bitboard& attackers) {
61-
62-
Bitboard b = stmAttackers & byTypeBB[Pt];
63-
if (!b)
64-
return min_attacker<PieceType(Pt + 1)>(byTypeBB, to, stmAttackers, occupied, attackers);
65-
66-
occupied ^= lsb(b); // Remove the attacker from occupied
67-
68-
// Add any X-ray attack behind the just removed piece. For instance with
69-
// rooks in a8 and a7 attacking a1, after removing a7 we add rook in a8.
70-
// Note that new added attackers can be of any color.
71-
if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN)
72-
attackers |= attacks_bb<BISHOP>(to, occupied) & (byTypeBB[BISHOP] | byTypeBB[QUEEN]);
73-
74-
if (Pt == ROOK || Pt == QUEEN)
75-
attackers |= attacks_bb<ROOK>(to, occupied) & (byTypeBB[ROOK] | byTypeBB[QUEEN]);
76-
77-
// X-ray may add already processed pieces because byTypeBB[] is constant: in
78-
// the rook example, now attackers contains _again_ rook in a7, so remove it.
79-
attackers &= occupied;
80-
return Pt;
81-
}
82-
83-
template<>
84-
PieceType min_attacker<KING>(const Bitboard*, Square, Bitboard, Bitboard&, Bitboard&) {
85-
return KING; // No need to update bitboards: it is the last cycle
86-
}
87-
8853
} // namespace
8954

9055

@@ -1052,77 +1017,96 @@ bool Position::see_ge(Move m, Value threshold) const {
10521017
if (type_of(m) != NORMAL)
10531018
return VALUE_ZERO >= threshold;
10541019

1055-
Bitboard stmAttackers;
10561020
Square from = from_sq(m), to = to_sq(m);
1057-
PieceType nextVictim = type_of(piece_on(from));
1058-
Color us = color_of(piece_on(from));
1059-
Color stm = ~us; // First consider opponent's move
1060-
Value balance; // Values of the pieces taken by us minus opponent's ones
1061-
1062-
// The opponent may be able to recapture so this is the best result
1063-
// we can hope for.
1064-
balance = PieceValue[MG][piece_on(to)] - threshold;
10651021

1066-
if (balance < VALUE_ZERO)
1022+
int swap = PieceValue[MG][piece_on(to)] - threshold;
1023+
if (swap < 0)
10671024
return false;
10681025

1069-
// Now assume the worst possible result: that the opponent can
1070-
// capture our piece for free.
1071-
balance -= PieceValue[MG][nextVictim];
1072-
1073-
// If it is enough (like in PxQ) then return immediately. Note that
1074-
// in case nextVictim == KING we always return here, this is ok
1075-
// if the given move is legal.
1076-
if (balance >= VALUE_ZERO)
1026+
swap = PieceValue[MG][piece_on(from)] - swap;
1027+
if (swap <= 0)
10771028
return true;
10781029

1079-
// Find all attackers to the destination square, with the moving piece
1080-
// removed, but possibly an X-ray attacker added behind it.
1081-
Bitboard occupied = pieces() ^ from ^ to;
1082-
Bitboard attackers = attackers_to(to, occupied) & occupied;
1030+
Bitboard occ = pieces() ^ from ^ to;
1031+
Color stm = color_of(piece_on(from));
1032+
Bitboard attackers = attackers_to(to, occ);
1033+
Bitboard stmAttackers, bb;
1034+
int res = 1;
10831035

10841036
while (true)
10851037
{
1086-
stmAttackers = attackers & pieces(stm);
1038+
stm = ~stm;
1039+
attackers &= occ;
1040+
1041+
// If stm has no more attackers then give up: stm loses
1042+
if (!(stmAttackers = attackers & pieces(stm)))
1043+
break;
10871044

10881045
// Don't allow pinned pieces to attack (except the king) as long as
1089-
// any pinners are on their original square.
1090-
if (st->pinners[~stm] & occupied)
1046+
// there are pinners on their original square.
1047+
if (st->pinners[~stm] & occ)
10911048
stmAttackers &= ~st->blockersForKing[stm];
10921049

1093-
// If stm has no more attackers then give up: stm loses
10941050
if (!stmAttackers)
10951051
break;
10961052

1053+
res ^= 1;
1054+
10971055
// Locate and remove the next least valuable attacker, and add to
1098-
// the bitboard 'attackers' the possibly X-ray attackers behind it.
1099-
nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
1056+
// the bitboard 'attackers' any X-ray attackers behind it.
1057+
if ((bb = stmAttackers & pieces(PAWN)))
1058+
{
1059+
if ((swap = PawnValueMg - swap) < res)
1060+
break;
11001061

1101-
stm = ~stm; // Switch side to move
1062+
occ ^= lsb(bb);
1063+
attackers |= attacks_bb<BISHOP>(to, occ) & pieces(BISHOP, QUEEN);
1064+
}
11021065

1103-
// Negamax the balance with alpha = balance, beta = balance+1 and
1104-
// add nextVictim's value.
1105-
//
1106-
// (balance, balance+1) -> (-balance-1, -balance)
1107-
//
1108-
assert(balance < VALUE_ZERO);
1066+
else if ((bb = stmAttackers & pieces(KNIGHT)))
1067+
{
1068+
if ((swap = KnightValueMg - swap) < res)
1069+
break;
11091070

1110-
balance = -balance - 1 - PieceValue[MG][nextVictim];
1071+
occ ^= lsb(bb);
1072+
}
11111073

1112-
// If balance is still non-negative after giving away nextVictim then we
1113-
// win. The only thing to be careful about it is that we should revert
1114-
// stm if we captured with the king when the opponent still has attackers.
1115-
if (balance >= VALUE_ZERO)
1074+
else if ((bb = stmAttackers & pieces(BISHOP)))
11161075
{
1117-
if (nextVictim == KING && (attackers & pieces(stm)))
1118-
stm = ~stm;
1119-
break;
1076+
if ((swap = BishopValueMg - swap) < res)
1077+
break;
1078+
1079+
occ ^= lsb(bb);
1080+
attackers |= attacks_bb<BISHOP>(to, occ) & pieces(BISHOP, QUEEN);
1081+
}
1082+
1083+
else if ((bb = stmAttackers & pieces(ROOK)))
1084+
{
1085+
if ((swap = RookValueMg - swap) < res)
1086+
break;
1087+
1088+
occ ^= lsb(bb);
1089+
attackers |= attacks_bb<ROOK>(to, occ) & pieces(ROOK, QUEEN);
1090+
}
1091+
1092+
else if ((bb = stmAttackers & pieces(QUEEN)))
1093+
{
1094+
if ((swap = QueenValueMg - swap) < res)
1095+
break;
1096+
1097+
occ ^= lsb(bb);
1098+
attackers |= (attacks_bb<BISHOP>(to, occ) & pieces(BISHOP, QUEEN))
1099+
| (attacks_bb<ROOK >(to, occ) & pieces(ROOK , QUEEN));
11201100
}
1121-
assert(nextVictim != KING);
1101+
1102+
else // KING
1103+
// If we "capture" with the king but opponent still has attackers,
1104+
// reverse the result.
1105+
return (attackers & ~pieces(stm)) ? res ^ 1 : res;
11221106
}
1123-
return us != stm; // We break the above loop when stm loses
1124-
}
11251107

1108+
return res;
1109+
}
11261110

11271111
/// Position::is_draw() tests whether the position is drawn by 50-move rule
11281112
/// or by repetition. It does not detect stalemates.

0 commit comments

Comments
 (0)