Merge commit '74a8fc060465a822f0c047f908d5fb07ebc6ad96' into cluster

This is the last commit before there are more conflicts.
This commit is contained in:
Steinar H. Gunderson
2025-12-25 15:16:24 +01:00
20 changed files with 224 additions and 66 deletions
+5 -1
View File
@@ -26,6 +26,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Download required linux packages
if: runner.os == 'Linux'
@@ -91,4 +92,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }}
path: .
path: |
.
!.git
!.output
+1
View File
@@ -19,6 +19,7 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
persist-credentials: false
- name: Run clang-format style check
uses: jidicula/clang-format-action@f62da5e3d3a2d88ff364771d9d938773a618ab5e # @v4.11.0
+2
View File
@@ -30,6 +30,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
+6 -1
View File
@@ -25,6 +25,8 @@ jobs:
shell: ${{ matrix.config.shell }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install fixed GCC on Linux
if: runner.os == 'Linux'
@@ -86,4 +88,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }}
path: .
path: |
.
!.git
!.output
+43
View File
@@ -0,0 +1,43 @@
# This workflow will play games with a debug enabled SF using the PR
name: Games
on:
workflow_call:
jobs:
Matetrack:
name: Games
runs-on: ubuntu-22.04
steps:
- name: Checkout SF repo
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
path: Stockfish
persist-credentials: false
- name: build debug enabled version of SF
working-directory: Stockfish/src
run: make -j build debug=yes
- name: Checkout fast-chess repo
uses: actions/checkout@v4
with:
repository: Disservin/fast-chess
path: fast-chess
ref: d54af1910d5479c669dc731f1f54f9108a251951
persist-credentials: false
- name: fast-chess build
working-directory: fast-chess
run: make -j
- name: Run games
working-directory: fast-chess
run: |
./fast-chess -rounds 4 -games 2 -repeat -concurrency 4 -openings file=app/tests/data/openings.epd format=epd order=random -srand $RANDOM\
-engine name=sf1 cmd=/home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish\
-engine name=sf2 cmd=/home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish\
-ratinginterval 1 -report penta=true -each proto=uci tc=4+0.04 -log file=fast.log | tee fast.out
cat fast.log
! grep "Assertion" fast.log > /dev/null
! grep "disconnect" fast.out > /dev/null
+2
View File
@@ -14,6 +14,7 @@ jobs:
uses: actions/checkout@v4
with:
path: Stockfish
persist-credentials: false
- name: Checkout include-what-you-use
uses: actions/checkout@v4
@@ -21,6 +22,7 @@ jobs:
repository: include-what-you-use/include-what-you-use
ref: f25caa280dc3277c4086ec345ad279a2463fea0f
path: include-what-you-use
persist-credentials: false
- name: Download required linux packages
run: |
+2
View File
@@ -13,6 +13,7 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}
path: Stockfish
persist-credentials: false
- name: build SF
working-directory: Stockfish/src
@@ -24,6 +25,7 @@ jobs:
repository: vondele/matetrack
path: matetrack
ref: 20287a1a145f30a166b7ef251eddb611e4e44fbf
persist-credentials: false
- name: matetrack install deps
working-directory: matetrack
+2
View File
@@ -40,6 +40,8 @@ jobs:
shell: ${{ matrix.config.shell }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Download required linux packages
run: |
+16
View File
@@ -15,8 +15,12 @@ jobs:
Prerelease:
if: github.repository == 'official-stockfish/Stockfish' && (github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag'))
runs-on: ubuntu-latest
permissions:
contents: write # For deleting/creating a prerelease
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
# returns null if no pre-release exists
- name: Get Commit SHA of Latest Pre-release
@@ -66,6 +70,8 @@ jobs:
arm_matrix: ${{ steps.set-arm-matrix.outputs.arm_matrix }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- id: set-matrix
run: |
TASKS=$(echo $(cat .github/ci/matrix.json) )
@@ -92,15 +98,25 @@ jobs:
uses: ./.github/workflows/tests.yml
Matetrack:
uses: ./.github/workflows/matetrack.yml
Games:
uses: ./.github/workflows/games.yml
Binaries:
if: github.repository == 'official-stockfish/Stockfish'
needs: [Matrix, Prerelease, Compilation]
uses: ./.github/workflows/upload_binaries.yml
with:
matrix: ${{ needs.Matrix.outputs.matrix }}
permissions:
contents: write # For deleting/creating a (pre)release
secrets:
token: ${{ secrets.GITHUB_TOKEN }}
ARM_Binaries:
if: github.repository == 'official-stockfish/Stockfish'
needs: [Matrix, Prerelease, ARMCompilation]
uses: ./.github/workflows/upload_binaries.yml
with:
matrix: ${{ needs.Matrix.outputs.arm_matrix }}
permissions:
contents: write # For deleting/creating a (pre)release
secrets:
token: ${{ secrets.GITHUB_TOKEN }}
+1
View File
@@ -106,6 +106,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Download required linux packages
if: runner.os == 'Linux'
+7
View File
@@ -5,6 +5,9 @@ on:
matrix:
type: string
required: true
secrets:
token:
required: true
jobs:
Artifacts:
@@ -25,6 +28,8 @@ jobs:
shell: ${{ matrix.config.shell }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Download artifact from compilation
uses: actions/download-artifact@v4
@@ -78,6 +83,7 @@ jobs:
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
with:
files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }}
token: ${{ secrets.token }}
- name: Get last commit sha
id: last_commit
@@ -104,3 +110,4 @@ jobs:
tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }}
prerelease: true
files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }}
token: ${{ secrets.token }}
+10 -6
View File
@@ -300,16 +300,20 @@ std::string Engine::get_numa_config_as_string() const {
std::string Engine::numa_config_information_as_string() const {
auto cfgStr = get_numa_config_as_string();
return "Available Processors: " + cfgStr;
return "Available processors: " + cfgStr;
}
std::string Engine::thread_binding_information_as_string() const {
auto boundThreadsByNode = get_bound_thread_count_by_numa_node();
if (boundThreadsByNode.empty())
return "";
auto boundThreadsByNode = get_bound_thread_count_by_numa_node();
std::stringstream ss;
ss << "NUMA Node Thread Binding: ";
size_t threadsSize = threads.size();
ss << "Using " << threadsSize << (threadsSize > 1 ? " threads" : " thread");
if (boundThreadsByNode.empty())
return ss.str();
ss << " with NUMA node thread binding: ";
bool isFirst = true;
+32 -5
View File
@@ -281,13 +281,21 @@ template<size_t N>
struct DebugInfo {
std::atomic<int64_t> data[N] = {0};
constexpr inline std::atomic<int64_t>& operator[](int index) { return data[index]; }
constexpr std::atomic<int64_t>& operator[](int index) { return data[index]; }
};
DebugInfo<2> hit[MaxDebugSlots];
DebugInfo<2> mean[MaxDebugSlots];
DebugInfo<3> stdev[MaxDebugSlots];
DebugInfo<6> correl[MaxDebugSlots];
struct DebugExtremes: public DebugInfo<3> {
DebugExtremes() {
data[1] = std::numeric_limits<int64_t>::min();
data[2] = std::numeric_limits<int64_t>::max();
}
};
DebugInfo<2> hit[MaxDebugSlots];
DebugInfo<2> mean[MaxDebugSlots];
DebugInfo<3> stdev[MaxDebugSlots];
DebugInfo<6> correl[MaxDebugSlots];
DebugExtremes extremes[MaxDebugSlots];
} // namespace
@@ -311,6 +319,18 @@ void dbg_stdev_of(int64_t value, int slot) {
stdev[slot][2] += value * value;
}
void dbg_extremes_of(int64_t value, int slot) {
++extremes[slot][0];
int64_t current_max = extremes[slot][1].load();
while (current_max < value && !extremes[slot][1].compare_exchange_weak(current_max, value))
{}
int64_t current_min = extremes[slot][2].load();
while (current_min > value && !extremes[slot][2].compare_exchange_weak(current_min, value))
{}
}
void dbg_correl_of(int64_t value1, int64_t value2, int slot) {
++correl[slot][0];
@@ -345,6 +365,13 @@ void dbg_print() {
std::cerr << "Stdev #" << i << ": Total " << n << " Stdev " << r << std::endl;
}
for (int i = 0; i < MaxDebugSlots; ++i)
if ((n = extremes[i][0]))
{
std::cerr << "Extremity #" << i << ": Total " << n << " Min " << extremes[i][2]
<< " Max " << extremes[i][1] << std::endl;
}
for (int i = 0; i < MaxDebugSlots; ++i)
if ((n = correl[i][0]))
{
+2
View File
@@ -67,6 +67,8 @@ std::optional<std::string> read_file_to_string(const std::string& path);
void dbg_hit_on(bool cond, int slot = 0);
void dbg_mean_of(int64_t value, int slot = 0);
void dbg_stdev_of(int64_t value, int slot = 0);
void dbg_extremes_of(int64_t value, int slot);
void dbg_correl_of(int64_t value1, int64_t value2, int slot = 0);
void dbg_print();
+10 -10
View File
@@ -1156,9 +1156,9 @@ bool Position::has_repeated() const {
}
// Tests if the position has a move which draws by repetition,
// or an earlier position has a move that directly reaches the current position.
bool Position::has_game_cycle(int ply) const {
// Tests if the position has a move which draws by repetition.
// This function accurately matches the outcome of is_draw() over all legal moves.
bool Position::upcoming_repetition(int ply) const {
int j;
@@ -1169,10 +1169,16 @@ bool Position::has_game_cycle(int ply) const {
Key originalKey = st->key;
StateInfo* stp = st->previous;
Key other = originalKey ^ stp->key ^ Zobrist::side;
for (int i = 3; i <= end; i += 2)
{
stp = stp->previous->previous;
stp = stp->previous;
other ^= stp->key ^ stp->previous->key ^ Zobrist::side;
stp = stp->previous;
if (other != 0)
continue;
Key moveKey = originalKey ^ stp->key;
if ((j = H1(moveKey), cuckoo[j] == moveKey) || (j = H2(moveKey), cuckoo[j] == moveKey))
@@ -1188,12 +1194,6 @@ bool Position::has_game_cycle(int ply) const {
// For nodes before or at the root, check that the move is a
// repetition rather than a move to the current position.
// In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in
// the same location, so we have to select which square to check.
if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move())
continue;
// For repetitions before or at the root, require one more
if (stp->repetition)
return true;
}
+1 -1
View File
@@ -156,7 +156,7 @@ class Position {
int game_ply() const;
bool is_chess960() const;
bool is_draw(int ply) const;
bool has_game_cycle(int ply) const;
bool upcoming_repetition(int ply) const;
bool has_repeated() const;
int rule50_count() const;
Value non_pawn_material(Color c) const;
+68 -32
View File
@@ -63,7 +63,7 @@ static constexpr double EvalLevel[10] = {0.981, 0.956, 0.895, 0.949, 0.913,
Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) {
Value futilityMult = 109 - 40 * noTtCutNode;
Value improvingDeduction = 59 * improving * futilityMult / 32;
Value worseningDeduction = 328 * oppWorsening * futilityMult / 1024;
Value worseningDeduction = oppWorsening * futilityMult / 3;
return futilityMult * d - improvingDeduction - worseningDeduction;
}
@@ -550,7 +550,7 @@ void Search::Worker::iterative_deepening() {
void Search::Worker::clear() {
counterMoves.fill(Move::none());
mainHistory.fill(0);
captureHistory.fill(0);
captureHistory.fill(-700);
pawnHistory.fill(-1193);
correctionHistory.fill(0);
@@ -582,9 +582,8 @@ Value Search::Worker::search(
// Limit the depth if extensions made it too large
depth = std::min(depth, MAX_PLY - 1);
// Check if we have an upcoming move that draws by repetition, or
// if the opponent had an alternative move earlier to this position.
if (!rootNode && alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply))
// Check if we have an upcoming move that draws by repetition.
if (!rootNode && alpha < VALUE_DRAW && pos.upcoming_repetition(ss->ply))
{
alpha = value_draw(this->nodes);
if (alpha >= beta)
@@ -800,8 +799,7 @@ Value Search::Worker::search(
// Use static evaluation difference to improve quiet move ordering (~9 Elo)
if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture)
{
int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1590, 1371);
bonus = bonus > 0 ? 2 * bonus : bonus / 2;
int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1590, 1371) + 800;
thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus;
if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION)
thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq]
@@ -835,8 +833,9 @@ Value Search::Worker::search(
&& eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening)
- (ss - 1)->statScore / 263
>= beta
&& eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttData.move || ttCapture))
return beta > VALUE_TB_LOSS_IN_MAX_PLY ? beta + (eval - beta) / 3 : eval;
&& eval >= beta && (!ttData.move || ttCapture) && beta > VALUE_TB_LOSS_IN_MAX_PLY
&& eval < VALUE_TB_WIN_IN_MAX_PLY)
return beta + (eval - beta) / 3;
// Step 9. Null move search with verification search (~35 Elo)
if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 14369
@@ -880,9 +879,12 @@ Value Search::Worker::search(
}
// Step 10. Internal iterative reductions (~9 Elo)
// For PV nodes without a ttMove, we decrease depth by 3.
// For PV nodes without a ttMove, we decrease depth.
// Additionally, if the current position is found in the TT
// and the stored depth in the TT is greater than or equal to
// current search depth, we decrease search depth even further.
if (PvNode && !ttData.move)
depth -= 3;
depth -= 3 + (ss->ttHit && ttData.depth >= depth);
// Use qsearch if depth <= 0.
if (depth <= 0)
@@ -909,12 +911,19 @@ Value Search::Worker::search(
assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta);
MovePicker mp(pos, ttData.move, probCutBeta - ss->staticEval, &thisThread->captureHistory);
Move probcutCapturesSearched[32];
int probcutCaptureCount = 0;
Piece captured;
while ((move = mp.next_move()) != Move::none())
if (move != excludedMove && pos.legal(move))
{
assert(pos.capture_stage(move));
movedPiece = pos.moved_piece(move);
captured = pos.piece_on(move.to_sq());
// Prefetch the TT entry for the resulting position
prefetch(tt.first_entry(pos.key_after(move)));
@@ -938,6 +947,19 @@ Value Search::Worker::search(
if (value >= probCutBeta)
{
thisThread->captureHistory[movedPiece][move.to_sq()][type_of(captured)]
<< stat_bonus(depth - 2);
for (int i = 0; i < probcutCaptureCount; i++)
{
movedPiece = pos.moved_piece(probcutCapturesSearched[i]);
captured = pos.piece_on(probcutCapturesSearched[i].to_sq());
thisThread->captureHistory[movedPiece][probcutCapturesSearched[i].to_sq()]
[type_of(captured)]
<< -stat_malus(depth - 3);
}
// Save ProbCut data into transposition table
Distributed::save(tt, threads, thisThread, ttWriter, posKey,
value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3,
@@ -945,6 +967,9 @@ Value Search::Worker::search(
return std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY ? value - (probCutBeta - beta)
: value;
}
if (probcutCaptureCount < 32)
probcutCapturesSearched[probcutCaptureCount++] = move;
}
Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable);
@@ -954,9 +979,8 @@ moves_loop: // When in check, search starts here
// Step 12. A small Probcut idea, when we are in check (~4 Elo)
probCutBeta = beta + 388;
if (ss->inCheck && !PvNode && ttCapture && (ttData.bound & BOUND_LOWER)
&& ttData.depth >= depth - 4 && ttData.value >= probCutBeta
&& std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY
if (ss->inCheck && (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4
&& ttData.value >= probCutBeta && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY
&& std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY)
return probCutBeta;
@@ -1042,7 +1066,7 @@ moves_loop: // When in check, search starts here
// Futility pruning for captures (~2 Elo)
if (!givesCheck && lmrDepth < 7 && !ss->inCheck)
{
Value futilityValue = ss->staticEval + 287 + 248 * lmrDepth
Value futilityValue = ss->staticEval + 294 + 246 * lmrDepth
+ PieceValue[capturedPiece] + captHist / 7;
if (futilityValue <= alpha)
continue;
@@ -1050,7 +1074,7 @@ moves_loop: // When in check, search starts here
// SEE based pruning for captures and checks (~11 Elo)
int seeHist = std::clamp(captHist / 32, -180 * depth, 163 * depth);
if (!pos.see_ge(move, -160 * depth - seeHist))
if (!pos.see_ge(move, -163 * depth - seeHist))
continue;
}
else
@@ -1061,15 +1085,15 @@ moves_loop: // When in check, search starts here
+ thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()];
// Continuation history based pruning (~2 Elo)
if (lmrDepth < 6 && history < -4151 * depth)
if (lmrDepth < 6 && history < -3899 * depth)
continue;
history += 2 * thisThread->mainHistory[us][move.from_to()];
lmrDepth += history / 3678;
lmrDepth += history / 4040;
Value futilityValue =
ss->staticEval + (bestValue < ss->staticEval - 51 ? 138 : 54) + 140 * lmrDepth;
ss->staticEval + (bestValue < ss->staticEval - 51 ? 135 : 56) + 140 * lmrDepth;
// Futility pruning: parent node (~13 Elo)
if (!ss->inCheck && lmrDepth < 12 && futilityValue <= alpha)
@@ -1135,8 +1159,8 @@ moves_loop: // When in check, search starts here
// and if after excluding the ttMove with a reduced search we fail high over the original beta,
// we assume this expected cut-node is not singular (multiple moves fail high),
// and we can prune the whole subtree by returning a softbound.
else if (singularBeta >= beta)
return singularBeta;
else if (value >= beta && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY)
return value;
// Negative extensions
// If other moves failed high over (ttValue - margin) without the ttMove on a reduced search,
@@ -1185,7 +1209,8 @@ moves_loop: // When in check, search starts here
// Decrease reduction if position is or has been on the PV (~7 Elo)
if (ss->ttPv)
r -= 1 + (ttData.value > alpha) + (ttData.depth >= depth);
r -= 1 + (ttData.value > alpha) + (ttData.depth >= depth)
- (PvNode && ttData.value < alpha && ttData.depth >= depth);
// Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC)
if (PvNode)
@@ -1204,7 +1229,7 @@ moves_loop: // When in check, search starts here
// Increase reduction if next ply has a lot of fail high (~5 Elo)
if ((ss + 1)->cutoffCnt > 3)
r++;
r += 1 + !(PvNode || cutNode);
// For first picked move (ttMove) reduce reduction
// but never allow it to go below 0 (~3 Elo)
@@ -1293,7 +1318,7 @@ moves_loop: // When in check, search starts here
rm.effort += nodes - nodeCount;
rm.averageScore =
rm.averageScore != -VALUE_INFINITE ? (2 * value + rm.averageScore) / 3 : value;
rm.averageScore != -VALUE_INFINITE ? (value + rm.averageScore) / 2 : value;
// PV move or new best move?
if (moveCount == 1 || value > alpha)
@@ -1333,11 +1358,17 @@ moves_loop: // When in check, search starts here
rm.score = -VALUE_INFINITE;
}
if (value > bestValue)
// In case we have an alternative move equal in eval to the current bestmove,
// promote it to bestmove by pretending it just exceeds alpha (but not beta).
int inc = (value == bestValue && (int(nodes) & 15) == 0
&& ss->ply + 2 + ss->ply / 32 >= thisThread->rootDepth
&& std::abs(value) + 1 < VALUE_TB_WIN_IN_MAX_PLY);
if (value + inc > bestValue)
{
bestValue = value;
if (value > alpha)
if (value + inc > alpha)
{
bestMove = move;
@@ -1396,10 +1427,16 @@ moves_loop: // When in check, search starts here
// Bonus for prior countermove that caused the fail low
else if (!priorCapture && prevSq != SQ_NONE)
{
int bonus = (113 * (depth > 5) + 118 * (PvNode || cutNode)
+ 191 * ((ss - 1)->statScore < -14396) + 119 * ((ss - 1)->moveCount > 8)
int bonus = (113 * (depth > 5) + 118 * (PvNode || cutNode) + 119 * ((ss - 1)->moveCount > 8)
+ 64 * (!ss->inCheck && bestValue <= ss->staticEval - 107)
+ 147 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 75));
// proportional to "how much damage we have to undo"
if ((ss - 1)->statScore < -8000)
bonus += std::clamp(-(ss - 1)->statScore / 100, 0, 250);
update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq,
stat_bonus(depth) * bonus / 100);
thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()]
@@ -1461,9 +1498,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta,
assert(PvNode || (alpha == beta - 1));
assert(depth <= 0);
// Check if we have an upcoming move that draws by repetition, or if
// the opponent had an alternative move earlier to this position. (~1 Elo)
if (alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply))
// Check if we have an upcoming move that draws by repetition. (~1 Elo)
if (alpha < VALUE_DRAW && pos.upcoming_repetition(ss->ply))
{
alpha = value_draw(this->nodes);
if (alpha >= beta)
@@ -1633,7 +1669,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta,
// 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) * 2 - 30))
if (futilityBase > alpha && !pos.see_ge(move, (alpha - futilityBase) * 4))
{
bestValue = alpha;
continue;
+3 -3
View File
@@ -40,10 +40,10 @@ namespace Stockfish {
class OptionsMap;
using Value = int;
// Sometimes we don't want to actually bind the threads, but the recipent still
// Sometimes we don't want to actually bind the threads, but the recipient still
// needs to think it runs on *some* NUMA node, such that it can access structures
// that rely on NUMA node knowledge. This class encapsulates this optional process
// such that the recipent does not need to know whether the binding happened or not.
// such that the recipient does not need to know whether the binding happened or not.
class OptionalThreadToNumaNodeBinder {
public:
OptionalThreadToNumaNodeBinder(NumaIndex n) :
@@ -88,7 +88,7 @@ class Thread {
// this name is no longer correct. However, this class (and ThreadPool)
// require further work to make them properly generic while maintaining
// appropriate specificity regarding search, from the point of view of an
// outside user, so renaming of this function in left for whenever that happens.
// outside user, so renaming of this function is left for whenever that happens.
void wait_for_search_finished();
size_t id() const { return idx; }
+6 -4
View File
@@ -55,6 +55,7 @@ struct TTEntry {
Bound(genBound8 & 0x3), bool(genBound8 & 0x4)};
}
bool is_occupied() const;
void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev, uint8_t generation8);
// The returned age is a multiple of TranspositionTable::GENERATION_DELTA
uint8_t relative_age(const uint8_t generation8) const;
@@ -84,7 +85,8 @@ static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF;
// DEPTH_ENTRY_OFFSET exists because 1) we use `bool(depth8)` as the occupancy check, but
// 2) we need to store negative depths for QS. (`depth8` is the only field with "spare bits":
// we sacrifice the ability to store depths greater than 1<<8 less the offset, as asserted below.)
// we sacrifice the ability to store depths greater than 1<<8 less the offset, as asserted in `save`.)
bool TTEntry::is_occupied() const { return bool(depth8); }
// Populates the TTEntry with a new node's data, possibly
// overwriting an old position. The update is not atomic and can be racy.
@@ -196,7 +198,7 @@ int TranspositionTable::hashfull() const {
int cnt = 0;
for (int i = 0; i < 1000; ++i)
for (int j = 0; j < ClusterSize; ++j)
cnt += table[i].entry[j].depth8
cnt += table[i].entry[j].is_occupied()
&& (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8;
return cnt / ClusterSize;
@@ -227,7 +229,7 @@ std::tuple<bool, TTData, TTWriter> TranspositionTable::probe(const Key key) cons
if (tte[i].key16 == key16)
// This gap is the main place for read races.
// After `read()` completes that copy is final, but may be self-inconsistent.
return {bool(tte[i].depth8), tte[i].read(), TTWriter(&tte[i])};
return {tte[i].is_occupied(), tte[i].read(), TTWriter(&tte[i])};
// Find an entry to be replaced according to the replacement strategy
TTEntry* replace = tte;
@@ -236,7 +238,7 @@ std::tuple<bool, TTData, TTWriter> TranspositionTable::probe(const Key key) cons
> tte[i].depth8 - tte[i].relative_age(generation8) * 2)
replace = &tte[i];
return {false, replace->read(), TTWriter(replace)};
return {false, TTData(), TTWriter(replace)};
}
+5 -3
View File
@@ -110,16 +110,18 @@ void UCIEngine::loop() {
sync_cout << "id name " << engine_info(true) << "\n"
<< engine.get_options() << sync_endl;
print_info_string(engine.numa_config_information_as_string());
print_info_string(engine.thread_binding_information_as_string());
sync_cout << "uciok" << sync_endl;
}
else if (token == "setoption")
setoption(is);
else if (token == "go")
{
// send info strings after the go command is sent for old GUIs and python-chess
print_info_string(engine.numa_config_information_as_string());
print_info_string(engine.thread_binding_information_as_string());
go(is);
}
else if (token == "position")
position(is);
else if (token == "ucinewgame")