Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Syntaxic sugar for Position::see_ge(move, threshold) #5529

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/movepick.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, int th, const CapturePieceTo
assert(!pos.checkers());

stage = PROBCUT_TT
+ !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm) && pos.see_ge(ttm, threshold));
+ !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm) && pos.see(ttm) >= threshold);
}

// Assigns a numerical value to each move in a list, used for sorting.
Expand Down Expand Up @@ -238,7 +238,7 @@ Move MovePicker::next_move(bool skipQuiets) {
case GOOD_CAPTURE :
if (select<Next>([&]() {
// Move losing capture to endBadCaptures to be tried later
return pos.see_ge(*cur, -cur->value / 18) ? true
return pos.see(*cur) >= (-cur->value / 18) ? true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing PR!
Now one question I wanted to ask for so long, is that for such moves, can't we save the see value into the move struct such that we reuse it when we want to prune?, the code in search looks like redundant work to me, IIUC that should be a (nice) speedup.

Copy link
Member Author

@snicolet snicolet Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, there is no function in master (nor in this PR) to calculate the SEE value of a move, we can only compare that value to a lower bound via see_ge()

: (*endBadCaptures++ = *cur, false);
}))
return *(cur - 1);
Expand Down Expand Up @@ -305,7 +305,7 @@ Move MovePicker::next_move(bool skipQuiets) {
return select<Best>([]() { return true; });

case PROBCUT :
return select<Next>([&]() { return pos.see_ge(*cur, threshold); });
return select<Next>([&]() { return pos.see(*cur) >= threshold; });

case QCAPTURE :
return select<Next>([]() { return true; });
Expand Down
6 changes: 3 additions & 3 deletions src/position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1022,9 +1022,9 @@ Key Position::key_after(Move m) const {
}


// Tests if the SEE (Static Exchange Evaluation)
// 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.
// Tests if the SEE (Static Exchange Evaluation) value of a move is
// greater or equal to the given threshold. We will use an algorithm
// similar to alpha-beta pruning with a null window.
bool Position::see_ge(Move m, int threshold) const {

assert(m.is_ok());
Expand Down
17 changes: 17 additions & 0 deletions src/position.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <iosfwd>
#include <memory>
#include <string>
#include <utility>

#include "bitboard.h"
#include "nnue/nnue_accumulator.h"
Expand All @@ -32,6 +33,7 @@

namespace Stockfish {

class Position;
class TranspositionTable;

// StateInfo struct stores information needed to restore a Position object to
Expand Down Expand Up @@ -73,6 +75,12 @@ struct StateInfo {
using StateListPtr = std::unique_ptr<std::deque<StateInfo>>;


/// A private type used to get a nice syntax for SEE comparisons. Never use this
/// type directly or store a value into a variable of this type, instead use the
/// syntax "if (pos.see(move) >= threshold) ..." and similar for other comparisons.
using SEE = std::pair<const Position&, Move>;


// Position class stores information regarding the board representation as
// pieces, side to move, hash keys, castling info, etc. Important methods are
// do_move() and undo_move(), used by the search to update node info when
Expand Down Expand Up @@ -144,6 +152,7 @@ class Position {

// Static Exchange Evaluation
bool see_ge(Move m, int threshold = 0) const;
inline const SEE see(Move m) const { return SEE(*this, m); }

// Accessing hash keys
Key key() const;
Expand Down Expand Up @@ -359,6 +368,14 @@ inline void Position::do_move(Move m, StateInfo& newSt) { do_move(m, newSt, give

inline StateInfo* Position::state() const { return st; }

// Syntactic sugar around Position::see_ge(), so that we can use the more readable
// pos.see(move) >= threshold instead of pos.see_ge(move, threshold), and similar
// readable code for the three other comparison operators.
inline bool operator>=(const SEE& s, Value threshold) { return s.first.see_ge(s.second, threshold);}
inline bool operator> (const SEE& s, Value threshold) { return (s >= threshold + 1); }
inline bool operator< (const SEE& s, Value threshold) { return !(s >= threshold); }
inline bool operator<=(const SEE& s, Value threshold) { return !(s >= threshold + 1); }

} // namespace Stockfish

#endif // #ifndef POSITION_H_INCLUDED
10 changes: 5 additions & 5 deletions src/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ Value Search::Worker::search(

// SEE based pruning for captures and checks (~11 Elo)
int seeHist = std::clamp(captHist / 32, -182 * depth, 166 * depth);
if (!pos.see_ge(move, -168 * depth - seeHist))
if (pos.see(move) < -168 * depth - seeHist)
continue;
}
else
Expand Down Expand Up @@ -1019,7 +1019,7 @@ Value Search::Worker::search(
lmrDepth = std::max(lmrDepth, 0);

// Prune moves with negative SEE (~4 Elo)
if (!pos.see_ge(move, -24 * lmrDepth * lmrDepth))
if (pos.see(move) < -24 * lmrDepth * lmrDepth)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, there is no function in master (nor in this PR) to calculate the SEE value of a move, we can only compare that value to an upper bound or a lower bound via see_ge()

@snicolet I'm referring to here, aren't we calling the function again on the move, if we are passing move to the function aren't we calculating the result for such move in this position.. is this call not redundant to the ones in the movepicker?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we save the result S. and reuse it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not a function, this is just syntaxic sugar...

pos.see(move) < A     

is strictly equivalent to

!pos.see_ge(move, A)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I see so it's like alpha-beta?
So you are saying that the value is not exact for some reason,
is it not possible to match bounds at least, just something like the saved bounds in TT, where we at least match windows/thresholds?
&& (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER)))

Copy link
Contributor

@peregrineshahin peregrineshahin Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i.e. if a see_ge value fails high over +3000, is calculating see_ge of threshold -90000 relevant?

continue;
}
}
Expand Down Expand Up @@ -1566,15 +1566,15 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)

// If static eval is much lower than alpha and move is
// not winning material, we can prune this move. (~2 Elo)
if (futilityBase <= alpha && !pos.see_ge(move, 1))
if (futilityBase <= alpha && pos.see(move) <= 0)
{
bestValue = std::max(bestValue, futilityBase);
continue;
}

// If static exchange evaluation is much worse than what
// is needed to not fall below alpha, we can prune this move.
if (futilityBase > alpha && !pos.see_ge(move, (alpha - futilityBase) * 4))
if (futilityBase > alpha && pos.see(move) < (alpha - futilityBase) * 4)
{
bestValue = alpha;
continue;
Expand All @@ -1591,7 +1591,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
continue;

// Do not search moves with bad enough SEE values (~5 Elo)
if (!pos.see_ge(move, -83))
if (pos.see(move) < -83)
continue;
}

Expand Down
Loading