Merge commit 'ea71a088435d4f1e51433c0a321f2afdff7814b1' into cluster

This is the last commit before there are more conflicts.
This commit is contained in:
Steinar H. Gunderson
2025-12-25 19:58:36 +01:00
63 changed files with 417 additions and 328 deletions
+10 -1
View File
@@ -21,19 +21,28 @@ jobs:
sanitizers:
- name: Run with thread sanitizer
make_option: sanitize=thread
cxx_extra_flags: ""
instrumented_option: sanitizer-thread
- name: Run with UB sanitizer
make_option: sanitize=undefined
cxx_extra_flags: ""
instrumented_option: sanitizer-undefined
- name: Run under valgrind
make_option: ""
cxx_extra_flags: ""
instrumented_option: valgrind
- name: Run under valgrind-thread
make_option: ""
cxx_extra_flags: ""
instrumented_option: valgrind-thread
- name: Run non-instrumented
make_option: ""
cxx_extra_flags: ""
instrumented_option: none
- name: Run with glibcxx assertions
make_option: ""
cxx_extra_flags: -D_GLIBCXX_ASSERTIONS
instrumented_option: non
defaults:
run:
working-directory: src
@@ -72,7 +81,7 @@ jobs:
- name: ${{ matrix.sanitizers.name }}
run: |
export CXXFLAGS="-O1 -fno-inline"
export CXXFLAGS="-O1 -fno-inline ${{ matrix.sanitizers.cxx_extra_flags }}"
make clean
make -j4 ARCH=x86-64-sse41-popcnt ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null
python3 ../tests/instrumented.py --${{ matrix.sanitizers.instrumented_option }} ./stockfish
+1
View File
@@ -47,6 +47,7 @@ Bryan Cross (crossbr)
candirufish
Carlos Esparza Sánchez (ces42)
Chess13234
Chris Bao (sscg13)
Chris Cain (ceebo)
Ciekce
clefrks
+1
View File
@@ -0,0 +1 @@
*.sh text eol=lf
+1 -1
View File
@@ -1,5 +1,5 @@
# Stockfish, a UCI chess playing engine derived from Glaurung 2.1
# Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
# Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
#
# Stockfish is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -9
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -58,7 +58,6 @@ Engine::Engine(std::optional<std::string> path) :
NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG),
NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) {
pos.set(StartFEN, false, &states->back());
capSq = SQ_NONE;
options["Debug Log File"] << Option("", [](const Option& o) {
start_logger(o);
@@ -125,7 +124,6 @@ std::uint64_t Engine::perft(const std::string& fen, Depth depth, bool isChess960
void Engine::go(Search::LimitsType& limits) {
assert(limits.perft == 0);
verify_networks();
limits.capSq = capSq;
threads.start_thinking(options, pos, states, limits);
}
@@ -168,7 +166,6 @@ void Engine::set_position(const std::string& fen, const std::vector<std::string>
states = StateListPtr(new std::deque<StateInfo>(1));
pos.set(fen, options["UCI_Chess960"], &states->back());
capSq = SQ_NONE;
for (const auto& move : moves)
{
auto m = UCIEngine::to_move(pos, move);
@@ -178,11 +175,6 @@ void Engine::set_position(const std::string& fen, const std::vector<std::string>
states->emplace_back();
pos.do_move(m, states->back());
capSq = SQ_NONE;
DirtyPiece& dp = states->back().dirtyPiece;
if (dp.dirty_num > 1 && dp.to[1] == SQ_NONE)
capSq = m.to_sq();
}
}
+1 -4
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -39,8 +39,6 @@
namespace Stockfish {
enum Square : int;
class Engine {
public:
using InfoShort = Search::InfoShort;
@@ -116,7 +114,6 @@ class Engine {
Position pos;
StateListPtr states;
Square capSq;
OptionsMap options;
ThreadPool threads;
+2 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -78,7 +78,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks,
optimism += optimism * nnueComplexity / 468;
nnue -= nnue * nnueComplexity / (smallNet ? 20233 : 17879);
int material = (smallNet ? 553 : 532) * pos.count<PAWN>() + pos.non_pawn_material();
int material = 535 * pos.count<PAWN>() + pos.non_pawn_material();
int v = (nnue * (77777 + material) + optimism * (7777 + material)) / 77777;
// Damp down the evaluation linearly when shuffling
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+32 -47
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@
#include <limits>
#include <type_traits> // IWYU pragma: keep
#include "misc.h"
#include "position.h"
namespace Stockfish {
@@ -66,24 +67,27 @@ inline int non_pawn_index(const Position& pos) {
return pos.non_pawn_key(c) & (CORRECTION_HISTORY_SIZE - 1);
}
// StatsEntry stores the stat table value. It is usually a number but could
// be a move or even a nested history. We use a class instead of a naked value
// to directly call history update operator<<() on the entry so to use stats
// tables at caller sites as simple multi-dim arrays.
// StatsEntry is the container of various numerical statistics. We use a class
// instead of a naked value to directly call history update operator<<() on
// the entry. The first template parameter T is the base type of the array,
// and the second template parameter D limits the range of updates in [-D, D]
// when we update values with the << operator
template<typename T, int D>
class StatsEntry {
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
static_assert(D <= std::numeric_limits<T>::max(), "D overflows T");
T entry;
public:
void operator=(const T& v) { entry = v; }
T* operator&() { return &entry; }
T* operator->() { return &entry; }
StatsEntry& operator=(const T& v) {
entry = v;
return *this;
}
operator const T&() const { return entry; }
void operator<<(int bonus) {
static_assert(D <= std::numeric_limits<T>::max(), "D overflows T");
// Make sure that bonus is in range [-D, D]
int clampedBonus = std::clamp(bonus, -D, D);
entry += clampedBonus - entry * std::abs(clampedBonus) / D;
@@ -92,62 +96,39 @@ class StatsEntry {
}
};
// Stats is a generic N-dimensional array used to store various statistics.
// The first template parameter T is the base type of the array, and the second
// template parameter D limits the range of updates in [-D, D] when we update
// values with the << operator, while the last parameters (Size and Sizes)
// encode the dimensions of the array.
template<typename T, int D, int Size, int... Sizes>
struct Stats: public std::array<Stats<T, D, Sizes...>, Size> {
using stats = Stats<T, D, Size, Sizes...>;
void fill(const T& v) {
// For standard-layout 'this' points to the first struct member
assert(std::is_standard_layout_v<stats>);
using entry = StatsEntry<T, D>;
entry* p = reinterpret_cast<entry*>(this);
std::fill(p, p + sizeof(*this) / sizeof(entry), v);
}
};
template<typename T, int D, int Size>
struct Stats<T, D, Size>: public std::array<StatsEntry<T, D>, Size> {};
// In stats table, D=0 means that the template parameter is not used
enum StatsParams {
NOT_USED = 0
};
enum StatsType {
NoCaptures,
Captures
};
template<typename T, int D, std::size_t... Sizes>
using Stats = MultiArray<StatsEntry<T, D>, Sizes...>;
// ButterflyHistory records how often quiet moves have been successful or unsuccessful
// during the current search, and is used for reduction and move ordering decisions.
// It uses 2 tables (one for each color) indexed by the move's from and to squares,
// see https://www.chessprogramming.org/Butterfly_Boards (~11 elo)
using ButterflyHistory = Stats<int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>;
using ButterflyHistory = Stats<std::int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>;
// LowPlyHistory is adressed by play and move's from and to squares, used
// to improve move ordering near the root
using LowPlyHistory = Stats<int16_t, 7183, LOW_PLY_HISTORY_SIZE, int(SQUARE_NB) * int(SQUARE_NB)>;
using LowPlyHistory =
Stats<std::int16_t, 7183, LOW_PLY_HISTORY_SIZE, int(SQUARE_NB) * int(SQUARE_NB)>;
// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
using CapturePieceToHistory = Stats<int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB>;
using CapturePieceToHistory = Stats<std::int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB>;
// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to]
using PieceToHistory = Stats<int16_t, 30000, PIECE_NB, SQUARE_NB>;
using PieceToHistory = Stats<std::int16_t, 30000, PIECE_NB, SQUARE_NB>;
// ContinuationHistory is the combined history of a given pair of moves, usually
// the current one given a previous one. The nested history table is based on
// PieceToHistory instead of ButterflyBoards.
// (~63 elo)
using ContinuationHistory = Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB>;
using ContinuationHistory = MultiArray<PieceToHistory, PIECE_NB, SQUARE_NB>;
// PawnHistory is addressed by the pawn structure and a move's [piece][to]
using PawnHistory = Stats<int16_t, 8192, PAWN_HISTORY_SIZE, PIECE_NB, SQUARE_NB>;
using PawnHistory = Stats<std::int16_t, 8192, PAWN_HISTORY_SIZE, PIECE_NB, SQUARE_NB>;
// Correction histories record differences between the static evaluation of
// positions and their search score. It is used to improve the static evaluation
@@ -162,23 +143,27 @@ enum CorrHistType {
Continuation, // Combined history of move pairs
};
namespace Detail {
template<CorrHistType _>
struct CorrHistTypedef {
using type = Stats<int16_t, CORRECTION_HISTORY_LIMIT, COLOR_NB, CORRECTION_HISTORY_SIZE>;
using type = Stats<std::int16_t, CORRECTION_HISTORY_LIMIT, COLOR_NB, CORRECTION_HISTORY_SIZE>;
};
template<>
struct CorrHistTypedef<PieceTo> {
using type = Stats<int16_t, CORRECTION_HISTORY_LIMIT, PIECE_NB, SQUARE_NB>;
using type = Stats<std::int16_t, CORRECTION_HISTORY_LIMIT, PIECE_NB, SQUARE_NB>;
};
template<>
struct CorrHistTypedef<Continuation> {
using type = Stats<CorrHistTypedef<PieceTo>::type, NOT_USED, PIECE_NB, SQUARE_NB>;
using type = MultiArray<CorrHistTypedef<PieceTo>::type, PIECE_NB, SQUARE_NB>;
};
}
template<CorrHistType T>
using CorrectionHistory = typename CorrHistTypedef<T>::type;
using CorrectionHistory = typename Detail::CorrHistTypedef<T>::type;
} // namespace Stockfish
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+30 -25
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,9 @@
#include "misc.h"
#include <array>
#include <atomic>
#include <cassert>
#include <cctype>
#include <cmath>
#include <cstdlib>
@@ -287,7 +289,10 @@ template<size_t N>
struct DebugInfo {
std::atomic<int64_t> data[N] = {0};
constexpr std::atomic<int64_t>& operator[](int index) { return data[index]; }
[[nodiscard]] constexpr std::atomic<int64_t>& operator[](size_t index) {
assert(index < N);
return data[index];
}
};
struct DebugExtremes: public DebugInfo<3> {
@@ -297,54 +302,54 @@ struct DebugExtremes: public DebugInfo<3> {
}
};
DebugInfo<2> hit[MaxDebugSlots];
DebugInfo<2> mean[MaxDebugSlots];
DebugInfo<3> stdev[MaxDebugSlots];
DebugInfo<6> correl[MaxDebugSlots];
DebugExtremes extremes[MaxDebugSlots];
std::array<DebugInfo<2>, MaxDebugSlots> hit;
std::array<DebugInfo<2>, MaxDebugSlots> mean;
std::array<DebugInfo<3>, MaxDebugSlots> stdev;
std::array<DebugInfo<6>, MaxDebugSlots> correl;
std::array<DebugExtremes, MaxDebugSlots> extremes;
} // namespace
void dbg_hit_on(bool cond, int slot) {
++hit[slot][0];
++hit.at(slot)[0];
if (cond)
++hit[slot][1];
++hit.at(slot)[1];
}
void dbg_mean_of(int64_t value, int slot) {
++mean[slot][0];
mean[slot][1] += value;
++mean.at(slot)[0];
mean.at(slot)[1] += value;
}
void dbg_stdev_of(int64_t value, int slot) {
++stdev[slot][0];
stdev[slot][1] += value;
stdev[slot][2] += value * value;
++stdev.at(slot)[0];
stdev.at(slot)[1] += value;
stdev.at(slot)[2] += value * value;
}
void dbg_extremes_of(int64_t value, int slot) {
++extremes[slot][0];
++extremes.at(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_max = extremes.at(slot)[1].load();
while (current_max < value && !extremes.at(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))
int64_t current_min = extremes.at(slot)[2].load();
while (current_min > value && !extremes.at(slot)[2].compare_exchange_weak(current_min, value))
{}
}
void dbg_correl_of(int64_t value1, int64_t value2, int slot) {
++correl[slot][0];
correl[slot][1] += value1;
correl[slot][2] += value1 * value1;
correl[slot][3] += value2;
correl[slot][4] += value2 * value2;
correl[slot][5] += value1 * value2;
++correl.at(slot)[0];
correl.at(slot)[1] += value1;
correl.at(slot)[2] += value1 * value1;
correl.at(slot)[3] += value2;
correl.at(slot)[4] += value2 * value2;
correl.at(slot)[5] += value1 * value2;
}
void dbg_print() {
+88 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
#define MISC_H_INCLUDED
#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <cstddef>
@@ -142,6 +143,92 @@ class ValueList {
};
template<typename T, std::size_t Size, std::size_t... Sizes>
class MultiArray;
namespace Detail {
template<typename T, std::size_t Size, std::size_t... Sizes>
struct MultiArrayHelper {
using ChildType = MultiArray<T, Sizes...>;
};
template<typename T, std::size_t Size>
struct MultiArrayHelper<T, Size> {
using ChildType = T;
};
}
// MultiArray is a generic N-dimensional array.
// The template parameters (Size and Sizes) encode the dimensions of the array.
template<typename T, std::size_t Size, std::size_t... Sizes>
class MultiArray {
using ChildType = typename Detail::MultiArrayHelper<T, Size, Sizes...>::ChildType;
using ArrayType = std::array<ChildType, Size>;
ArrayType data_;
public:
using value_type = typename ArrayType::value_type;
using size_type = typename ArrayType::size_type;
using difference_type = typename ArrayType::difference_type;
using reference = typename ArrayType::reference;
using const_reference = typename ArrayType::const_reference;
using pointer = typename ArrayType::pointer;
using const_pointer = typename ArrayType::const_pointer;
using iterator = typename ArrayType::iterator;
using const_iterator = typename ArrayType::const_iterator;
using reverse_iterator = typename ArrayType::reverse_iterator;
using const_reverse_iterator = typename ArrayType::const_reverse_iterator;
constexpr auto& at(size_type index) noexcept { return data_.at(index); }
constexpr const auto& at(size_type index) const noexcept { return data_.at(index); }
constexpr auto& operator[](size_type index) noexcept { return data_[index]; }
constexpr const auto& operator[](size_type index) const noexcept { return data_[index]; }
constexpr auto& front() noexcept { return data_.front(); }
constexpr const auto& front() const noexcept { return data_.front(); }
constexpr auto& back() noexcept { return data_.back(); }
constexpr const auto& back() const noexcept { return data_.back(); }
auto* data() { return data_.data(); }
const auto* data() const { return data_.data(); }
constexpr auto begin() noexcept { return data_.begin(); }
constexpr auto end() noexcept { return data_.end(); }
constexpr auto begin() const noexcept { return data_.begin(); }
constexpr auto end() const noexcept { return data_.end(); }
constexpr auto cbegin() const noexcept { return data_.cbegin(); }
constexpr auto cend() const noexcept { return data_.cend(); }
constexpr auto rbegin() noexcept { return data_.rbegin(); }
constexpr auto rend() noexcept { return data_.rend(); }
constexpr auto rbegin() const noexcept { return data_.rbegin(); }
constexpr auto rend() const noexcept { return data_.rend(); }
constexpr auto crbegin() const noexcept { return data_.crbegin(); }
constexpr auto crend() const noexcept { return data_.crend(); }
constexpr bool empty() const noexcept { return data_.empty(); }
constexpr size_type size() const noexcept { return data_.size(); }
constexpr size_type max_size() const noexcept { return data_.max_size(); }
template<typename U>
void fill(const U& v) {
static_assert(std::is_assignable_v<T, U>, "Cannot assign fill value to entry type");
for (auto& ele : data_)
{
if constexpr (sizeof...(Sizes) == 0)
ele = v;
else
ele.fill(v);
}
}
constexpr void swap(MultiArray<T, Size, Sizes...>& other) noexcept { data_.swap(other.data_); }
};
// xorshift64star Pseudo-Random Number Generator
// This class is based on original code written and dedicated
// to the public domain by Sebastiano Vigna (2014).
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+2 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,11 +18,11 @@
#include "movepick.h"
#include <array>
#include <cassert>
#include <limits>
#include "bitboard.h"
#include "misc.h"
#include "position.h"
namespace Stockfish {
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+4 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -39,7 +39,6 @@
#include "nnue_common.h"
#include "nnue_misc.h"
namespace {
// Macro to embed the default efficiently updatable neural network (NNUE) file
// data in the engine binary (using incbin.h, by Dale Weiler).
// This macro invocation will declare the following three variables
@@ -59,6 +58,8 @@ const unsigned char* const gEmbeddedNNUESmallEnd = &gEmbeddedNNUESmallData[1
const unsigned int gEmbeddedNNUESmallSize = 1;
#endif
namespace {
struct EmbeddedNNUE {
EmbeddedNNUE(const unsigned char* embeddedData,
const unsigned char* embeddedEnd,
@@ -101,7 +102,7 @@ bool read_parameters(std::istream& stream, T& reference) {
// Write evaluation function parameters
template<typename T>
bool write_parameters(std::ostream& stream, const T& reference) {
bool write_parameters(std::ostream& stream, T& reference) {
write_little_endian<std::uint32_t>(stream, T::get_hash_value());
return reference.write_parameters(stream);
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+124 -121
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -144,10 +144,10 @@ using psqt_vec_t = int32x4_t;
#endif
// Compute optimal SIMD register count for feature transformer accumulation.
template<IndexType TransformedFeatureWidth, IndexType HalfDimensions>
class SIMDTiling {
#ifdef VECTOR
// Compute optimal SIMD register count for feature transformer accumulation.
// We use __m* types as template arguments, which causes GCC to emit warnings
// about losing some attribute information. This is irrelevant to us as we
// only take their size, so the following pragma are harmless.
@@ -156,33 +156,47 @@ using psqt_vec_t = int32x4_t;
#pragma GCC diagnostic ignored "-Wignored-attributes"
#endif
template<typename SIMDRegisterType, typename LaneType, int NumLanes, int MaxRegisters>
static constexpr int BestRegisterCount() {
#define RegisterSize sizeof(SIMDRegisterType)
#define LaneSize sizeof(LaneType)
template<typename SIMDRegisterType, typename LaneType, int NumLanes, int MaxRegisters>
static constexpr int BestRegisterCount() {
constexpr std::size_t RegisterSize = sizeof(SIMDRegisterType);
constexpr std::size_t LaneSize = sizeof(LaneType);
static_assert(RegisterSize >= LaneSize);
static_assert(MaxRegisters <= NumRegistersSIMD);
static_assert(MaxRegisters > 0);
static_assert(NumRegistersSIMD > 0);
static_assert(RegisterSize % LaneSize == 0);
static_assert((NumLanes * LaneSize) % RegisterSize == 0);
static_assert(RegisterSize >= LaneSize);
static_assert(MaxRegisters <= NumRegistersSIMD);
static_assert(MaxRegisters > 0);
static_assert(NumRegistersSIMD > 0);
static_assert(RegisterSize % LaneSize == 0);
static_assert((NumLanes * LaneSize) % RegisterSize == 0);
const int ideal = (NumLanes * LaneSize) / RegisterSize;
if (ideal <= MaxRegisters)
return ideal;
const int ideal = (NumLanes * LaneSize) / RegisterSize;
if (ideal <= MaxRegisters)
return ideal;
// Look for the largest divisor of the ideal register count that is smaller than MaxRegisters
for (int divisor = MaxRegisters; divisor > 1; --divisor)
if (ideal % divisor == 0)
return divisor;
// Look for the largest divisor of the ideal register count that is smaller than MaxRegisters
for (int divisor = MaxRegisters; divisor > 1; --divisor)
if (ideal % divisor == 0)
return divisor;
return 1;
}
return 1;
}
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
public:
static constexpr int NumRegs =
BestRegisterCount<vec_t, WeightType, TransformedFeatureWidth, NumRegistersSIMD>();
static constexpr int NumPsqtRegs =
BestRegisterCount<psqt_vec_t, PSQTWeightType, PSQTBuckets, NumRegistersSIMD>();
static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2;
static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4;
static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions");
static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets");
#endif
};
// Input feature converter
@@ -194,17 +208,7 @@ class FeatureTransformer {
static constexpr IndexType HalfDimensions = TransformedFeatureDimensions;
private:
#ifdef VECTOR
static constexpr int NumRegs =
BestRegisterCount<vec_t, WeightType, TransformedFeatureDimensions, NumRegistersSIMD>();
static constexpr int NumPsqtRegs =
BestRegisterCount<psqt_vec_t, PSQTWeightType, PSQTBuckets, NumRegistersSIMD>();
static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2;
static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4;
static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions");
static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets");
#endif
using Tiling = SIMDTiling<TransformedFeatureDimensions, HalfDimensions>;
public:
// Output type
@@ -254,21 +258,20 @@ class FeatureTransformer {
#endif
}
void permute_weights([[maybe_unused]] void (*order_fn)(uint64_t*)) const {
void permute_weights([[maybe_unused]] void (*order_fn)(uint64_t*)) {
#if defined(USE_AVX2)
#if defined(USE_AVX512)
constexpr IndexType di = 16;
#else
constexpr IndexType di = 8;
#endif
uint64_t* b = reinterpret_cast<uint64_t*>(const_cast<BiasType*>(&biases[0]));
uint64_t* b = reinterpret_cast<uint64_t*>(&biases[0]);
for (IndexType i = 0; i < HalfDimensions * sizeof(BiasType) / sizeof(uint64_t); i += di)
order_fn(&b[i]);
for (IndexType j = 0; j < InputDimensions; ++j)
{
uint64_t* w =
reinterpret_cast<uint64_t*>(const_cast<WeightType*>(&weights[j * HalfDimensions]));
uint64_t* w = reinterpret_cast<uint64_t*>(&weights[j * HalfDimensions]);
for (IndexType i = 0; i < HalfDimensions * sizeof(WeightType) / sizeof(uint64_t);
i += di)
order_fn(&w[i]);
@@ -276,17 +279,16 @@ class FeatureTransformer {
#endif
}
inline void scale_weights(bool read) const {
inline void scale_weights(bool read) {
for (IndexType j = 0; j < InputDimensions; ++j)
{
WeightType* w = const_cast<WeightType*>(&weights[j * HalfDimensions]);
WeightType* w = &weights[j * HalfDimensions];
for (IndexType i = 0; i < HalfDimensions; ++i)
w[i] = read ? w[i] * 2 : w[i] / 2;
}
BiasType* b = const_cast<BiasType*>(biases);
for (IndexType i = 0; i < HalfDimensions; ++i)
b[i] = read ? b[i] * 2 : b[i] / 2;
biases[i] = read ? biases[i] * 2 : biases[i] / 2;
}
// Read network parameters
@@ -302,7 +304,7 @@ class FeatureTransformer {
}
// Write network parameters
bool write_parameters(std::ostream& stream) const {
bool write_parameters(std::ostream& stream) {
permute_weights(order_packs);
scale_weights(false);
@@ -478,8 +480,8 @@ class FeatureTransformer {
#ifdef VECTOR
// Gcc-10.2 unnecessarily spills AVX2 registers if this array
// is defined in the VECTOR code below, once in each branch.
vec_t acc[NumRegs];
psqt_vec_t psqt[NumPsqtRegs];
vec_t acc[Tiling::NumRegs];
psqt_vec_t psqt[Tiling::NumPsqtRegs];
#endif
const Square ksq = pos.square<KING>(Perspective);
@@ -504,14 +506,14 @@ class FeatureTransformer {
#ifdef VECTOR
if ((removed.size() == 1 || removed.size() == 2) && added.size() == 1)
{
auto accIn =
auto* accIn =
reinterpret_cast<const vec_t*>(&(computed->*accPtr).accumulation[Perspective][0]);
auto accOut = reinterpret_cast<vec_t*>(&(next->*accPtr).accumulation[Perspective][0]);
auto* accOut = reinterpret_cast<vec_t*>(&(next->*accPtr).accumulation[Perspective][0]);
const IndexType offsetR0 = HalfDimensions * removed[0];
auto columnR0 = reinterpret_cast<const vec_t*>(&weights[offsetR0]);
auto* columnR0 = reinterpret_cast<const vec_t*>(&weights[offsetR0]);
const IndexType offsetA = HalfDimensions * added[0];
auto columnA = reinterpret_cast<const vec_t*>(&weights[offsetA]);
auto* columnA = reinterpret_cast<const vec_t*>(&weights[offsetA]);
if (removed.size() == 1)
{
@@ -521,22 +523,22 @@ class FeatureTransformer {
else
{
const IndexType offsetR1 = HalfDimensions * removed[1];
auto columnR1 = reinterpret_cast<const vec_t*>(&weights[offsetR1]);
auto* columnR1 = reinterpret_cast<const vec_t*>(&weights[offsetR1]);
for (IndexType i = 0; i < HalfDimensions * sizeof(WeightType) / sizeof(vec_t); ++i)
accOut[i] = vec_sub_16(vec_add_16(accIn[i], columnA[i]),
vec_add_16(columnR0[i], columnR1[i]));
}
auto accPsqtIn = reinterpret_cast<const psqt_vec_t*>(
auto* accPsqtIn = reinterpret_cast<const psqt_vec_t*>(
&(computed->*accPtr).psqtAccumulation[Perspective][0]);
auto accPsqtOut =
auto* accPsqtOut =
reinterpret_cast<psqt_vec_t*>(&(next->*accPtr).psqtAccumulation[Perspective][0]);
const IndexType offsetPsqtR0 = PSQTBuckets * removed[0];
auto columnPsqtR0 = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtR0]);
auto* columnPsqtR0 = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtR0]);
const IndexType offsetPsqtA = PSQTBuckets * added[0];
auto columnPsqtA = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtA]);
auto* columnPsqtA = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtA]);
if (removed.size() == 1)
{
@@ -548,7 +550,8 @@ class FeatureTransformer {
else
{
const IndexType offsetPsqtR1 = PSQTBuckets * removed[1];
auto columnPsqtR1 = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtR1]);
auto* columnPsqtR1 =
reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtR1]);
for (std::size_t i = 0;
i < PSQTBuckets * sizeof(PSQTWeightType) / sizeof(psqt_vec_t); ++i)
@@ -559,69 +562,69 @@ class FeatureTransformer {
}
else
{
for (IndexType i = 0; i < HalfDimensions / TileHeight; ++i)
for (IndexType i = 0; i < HalfDimensions / Tiling::TileHeight; ++i)
{
// Load accumulator
auto accTileIn = reinterpret_cast<const vec_t*>(
&(computed->*accPtr).accumulation[Perspective][i * TileHeight]);
for (IndexType j = 0; j < NumRegs; ++j)
auto* accTileIn = reinterpret_cast<const vec_t*>(
&(computed->*accPtr).accumulation[Perspective][i * Tiling::TileHeight]);
for (IndexType j = 0; j < Tiling::NumRegs; ++j)
acc[j] = vec_load(&accTileIn[j]);
// Difference calculation for the deactivated features
for (const auto index : removed)
{
const IndexType offset = HalfDimensions * index + i * TileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
for (IndexType j = 0; j < NumRegs; ++j)
const IndexType offset = HalfDimensions * index + i * Tiling::TileHeight;
auto* column = reinterpret_cast<const vec_t*>(&weights[offset]);
for (IndexType j = 0; j < Tiling::NumRegs; ++j)
acc[j] = vec_sub_16(acc[j], column[j]);
}
// Difference calculation for the activated features
for (const auto index : added)
{
const IndexType offset = HalfDimensions * index + i * TileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
for (IndexType j = 0; j < NumRegs; ++j)
const IndexType offset = HalfDimensions * index + i * Tiling::TileHeight;
auto* column = reinterpret_cast<const vec_t*>(&weights[offset]);
for (IndexType j = 0; j < Tiling::NumRegs; ++j)
acc[j] = vec_add_16(acc[j], column[j]);
}
// Store accumulator
auto accTileOut = reinterpret_cast<vec_t*>(
&(next->*accPtr).accumulation[Perspective][i * TileHeight]);
for (IndexType j = 0; j < NumRegs; ++j)
auto* accTileOut = reinterpret_cast<vec_t*>(
&(next->*accPtr).accumulation[Perspective][i * Tiling::TileHeight]);
for (IndexType j = 0; j < Tiling::NumRegs; ++j)
vec_store(&accTileOut[j], acc[j]);
}
for (IndexType i = 0; i < PSQTBuckets / PsqtTileHeight; ++i)
for (IndexType i = 0; i < PSQTBuckets / Tiling::PsqtTileHeight; ++i)
{
// Load accumulator
auto accTilePsqtIn = reinterpret_cast<const psqt_vec_t*>(
&(computed->*accPtr).psqtAccumulation[Perspective][i * PsqtTileHeight]);
for (std::size_t j = 0; j < NumPsqtRegs; ++j)
auto* accTilePsqtIn = reinterpret_cast<const psqt_vec_t*>(
&(computed->*accPtr).psqtAccumulation[Perspective][i * Tiling::PsqtTileHeight]);
for (std::size_t j = 0; j < Tiling::NumPsqtRegs; ++j)
psqt[j] = vec_load_psqt(&accTilePsqtIn[j]);
// Difference calculation for the deactivated features
for (const auto index : removed)
{
const IndexType offset = PSQTBuckets * index + i * PsqtTileHeight;
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
for (std::size_t j = 0; j < NumPsqtRegs; ++j)
const IndexType offset = PSQTBuckets * index + i * Tiling::PsqtTileHeight;
auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
for (std::size_t j = 0; j < Tiling::NumPsqtRegs; ++j)
psqt[j] = vec_sub_psqt_32(psqt[j], columnPsqt[j]);
}
// Difference calculation for the activated features
for (const auto index : added)
{
const IndexType offset = PSQTBuckets * index + i * PsqtTileHeight;
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
for (std::size_t j = 0; j < NumPsqtRegs; ++j)
const IndexType offset = PSQTBuckets * index + i * Tiling::PsqtTileHeight;
auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
for (std::size_t j = 0; j < Tiling::NumPsqtRegs; ++j)
psqt[j] = vec_add_psqt_32(psqt[j], columnPsqt[j]);
}
// Store accumulator
auto accTilePsqtOut = reinterpret_cast<psqt_vec_t*>(
&(next->*accPtr).psqtAccumulation[Perspective][i * PsqtTileHeight]);
for (std::size_t j = 0; j < NumPsqtRegs; ++j)
auto* accTilePsqtOut = reinterpret_cast<psqt_vec_t*>(
&(next->*accPtr).psqtAccumulation[Perspective][i * Tiling::PsqtTileHeight]);
for (std::size_t j = 0; j < Tiling::NumPsqtRegs; ++j)
vec_store_psqt(&accTilePsqtOut[j], psqt[j]);
}
}
@@ -700,88 +703,88 @@ class FeatureTransformer {
accumulator.computed[Perspective] = true;
#ifdef VECTOR
vec_t acc[NumRegs];
psqt_vec_t psqt[NumPsqtRegs];
vec_t acc[Tiling::NumRegs];
psqt_vec_t psqt[Tiling::NumPsqtRegs];
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
for (IndexType j = 0; j < HalfDimensions / Tiling::TileHeight; ++j)
{
auto accTile =
reinterpret_cast<vec_t*>(&accumulator.accumulation[Perspective][j * TileHeight]);
auto entryTile = reinterpret_cast<vec_t*>(&entry.accumulation[j * TileHeight]);
auto* accTile = reinterpret_cast<vec_t*>(
&accumulator.accumulation[Perspective][j * Tiling::TileHeight]);
auto* entryTile = reinterpret_cast<vec_t*>(&entry.accumulation[j * Tiling::TileHeight]);
for (IndexType k = 0; k < NumRegs; ++k)
for (IndexType k = 0; k < Tiling::NumRegs; ++k)
acc[k] = entryTile[k];
int i = 0;
for (; i < int(std::min(removed.size(), added.size())); ++i)
std::size_t i = 0;
for (; i < std::min(removed.size(), added.size()); ++i)
{
IndexType indexR = removed[i];
const IndexType offsetR = HalfDimensions * indexR + j * TileHeight;
auto columnR = reinterpret_cast<const vec_t*>(&weights[offsetR]);
const IndexType offsetR = HalfDimensions * indexR + j * Tiling::TileHeight;
auto* columnR = reinterpret_cast<const vec_t*>(&weights[offsetR]);
IndexType indexA = added[i];
const IndexType offsetA = HalfDimensions * indexA + j * TileHeight;
auto columnA = reinterpret_cast<const vec_t*>(&weights[offsetA]);
const IndexType offsetA = HalfDimensions * indexA + j * Tiling::TileHeight;
auto* columnA = reinterpret_cast<const vec_t*>(&weights[offsetA]);
for (unsigned k = 0; k < NumRegs; ++k)
for (IndexType k = 0; k < Tiling::NumRegs; ++k)
acc[k] = vec_add_16(acc[k], vec_sub_16(columnA[k], columnR[k]));
}
for (; i < int(removed.size()); ++i)
for (; i < removed.size(); ++i)
{
IndexType index = removed[i];
const IndexType offset = HalfDimensions * index + j * TileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
const IndexType offset = HalfDimensions * index + j * Tiling::TileHeight;
auto* column = reinterpret_cast<const vec_t*>(&weights[offset]);
for (unsigned k = 0; k < NumRegs; ++k)
for (IndexType k = 0; k < Tiling::NumRegs; ++k)
acc[k] = vec_sub_16(acc[k], column[k]);
}
for (; i < int(added.size()); ++i)
for (; i < added.size(); ++i)
{
IndexType index = added[i];
const IndexType offset = HalfDimensions * index + j * TileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
const IndexType offset = HalfDimensions * index + j * Tiling::TileHeight;
auto* column = reinterpret_cast<const vec_t*>(&weights[offset]);
for (unsigned k = 0; k < NumRegs; ++k)
for (IndexType k = 0; k < Tiling::NumRegs; ++k)
acc[k] = vec_add_16(acc[k], column[k]);
}
for (IndexType k = 0; k < NumRegs; k++)
for (IndexType k = 0; k < Tiling::NumRegs; k++)
vec_store(&entryTile[k], acc[k]);
for (IndexType k = 0; k < NumRegs; k++)
for (IndexType k = 0; k < Tiling::NumRegs; k++)
vec_store(&accTile[k], acc[k]);
}
for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
for (IndexType j = 0; j < PSQTBuckets / Tiling::PsqtTileHeight; ++j)
{
auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
&accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
auto entryTilePsqt =
reinterpret_cast<psqt_vec_t*>(&entry.psqtAccumulation[j * PsqtTileHeight]);
auto* accTilePsqt = reinterpret_cast<psqt_vec_t*>(
&accumulator.psqtAccumulation[Perspective][j * Tiling::PsqtTileHeight]);
auto* entryTilePsqt =
reinterpret_cast<psqt_vec_t*>(&entry.psqtAccumulation[j * Tiling::PsqtTileHeight]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
for (std::size_t k = 0; k < Tiling::NumPsqtRegs; ++k)
psqt[k] = entryTilePsqt[k];
for (int i = 0; i < int(removed.size()); ++i)
for (std::size_t i = 0; i < removed.size(); ++i)
{
IndexType index = removed[i];
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
const IndexType offset = PSQTBuckets * index + j * Tiling::PsqtTileHeight;
auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
for (std::size_t k = 0; k < Tiling::NumPsqtRegs; ++k)
psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]);
}
for (int i = 0; i < int(added.size()); ++i)
for (std::size_t i = 0; i < added.size(); ++i)
{
IndexType index = added[i];
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
const IndexType offset = PSQTBuckets * index + j * Tiling::PsqtTileHeight;
auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
for (std::size_t k = 0; k < Tiling::NumPsqtRegs; ++k)
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
}
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
for (std::size_t k = 0; k < Tiling::NumPsqtRegs; ++k)
vec_store_psqt(&entryTilePsqt[k], psqt[k]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
for (std::size_t k = 0; k < Tiling::NumPsqtRegs; ++k)
vec_store_psqt(&accTilePsqt[k], psqt[k]);
}
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+3 -6
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1012,7 +1012,7 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ
// Used to do a "null move": it flips
// the side to move without executing any move on the board.
void Position::do_null_move(StateInfo& newSt, TranspositionTable& tt) {
void Position::do_null_move(StateInfo& newSt, const TranspositionTable& tt) {
assert(!checkers());
assert(&newSt != st);
@@ -1071,10 +1071,7 @@ Key Position::key_after(Move m) const {
Piece captured = piece_on(to);
Key k = st->key ^ Zobrist::side;
if (captured)
k ^= Zobrist::psq[captured][to];
k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
k ^= Zobrist::psq[captured][to] ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
return (captured || type_of(pc) == PAWN) ? k : adjust_key50<true>(k);
}
+2 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -143,7 +143,7 @@ class Position {
void do_move(Move m, StateInfo& newSt);
void do_move(Move m, StateInfo& newSt, bool givesCheck);
void undo_move(Move m);
void do_null_move(StateInfo& newSt, TranspositionTable& tt);
void do_null_move(StateInfo& newSt, const TranspositionTable& tt);
void undo_null_move();
// Static Exchange Evaluation
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+51 -58
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -61,7 +61,6 @@ void syzygy_extend_pv(const OptionsMap& options,
Stockfish::Search::RootMove& rootMove,
Value& v);
using Eval::evaluate;
using namespace Search;
namespace {
@@ -79,9 +78,7 @@ constexpr int futility_move_count(bool improving, Depth depth) {
return (3 + depth * depth) / (2 - improving);
}
// Add correctionHistory value to raw staticEval and guarantee evaluation
// does not hit the tablebase range.
Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos, Stack* ss) {
int correction_value(const Worker& w, const Position& pos, Stack* ss) {
const Color us = pos.side_to_move();
const auto m = (ss - 1)->currentMove;
const auto pcv = w.pawnCorrectionHistory[us][pawn_structure_index<Correction>(pos)];
@@ -89,15 +86,17 @@ Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos, St
const auto micv = w.minorPieceCorrectionHistory[us][minor_piece_index(pos)];
const auto wnpcv = w.nonPawnCorrectionHistory[WHITE][us][non_pawn_index<WHITE>(pos)];
const auto bnpcv = w.nonPawnCorrectionHistory[BLACK][us][non_pawn_index<BLACK>(pos)];
int cntcv = 1;
const auto cntcv =
m.is_ok() ? (*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()]
: 0;
if (m.is_ok())
cntcv = int((*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()]);
return (6384 * pcv + 3583 * macv + 6492 * micv + 6725 * (wnpcv + bnpcv) + 5880 * cntcv);
}
const auto cv =
(6384 * pcv + 3583 * macv + 6492 * micv + 6725 * (wnpcv + bnpcv) + cntcv * 5880) / 131072;
v += cv;
return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
// Add correctionHistory value to raw staticEval and guarantee evaluation
// does not hit the tablebase range.
Value to_corrected_static_eval(Value v, const int cv) {
return std::clamp(v + cv / 131072, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
}
// History and stats update bonus, based on depth
@@ -501,10 +500,9 @@ void Search::Worker::iterative_deepening() {
timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.495 : 0.687;
double reduction = (1.48 + mainThread->previousTimeReduction) / (2.17 * timeReduction);
double bestMoveInstability = 1 + 1.88 * totBestMoveChanges / threads.size();
double recapture = limits.capSq == rootMoves[0].pv[0].to_sq() ? 0.955 : 1.005;
double totalTime =
mainThread->tm.optimum() * fallingEval * reduction * bestMoveInstability * recapture;
mainThread->tm.optimum() * fallingEval * reduction * bestMoveInstability;
// Cap used time in case of a single legal move for a better viewer experience
if (rootMoves.size() == 1)
@@ -560,16 +558,16 @@ void Search::Worker::clear() {
for (auto& to : continuationCorrectionHistory)
for (auto& h : to)
h->fill(0);
h.fill(0);
for (bool inCheck : {false, true})
for (StatsType c : {NoCaptures, Captures})
for (auto& to : continuationHistory[inCheck][c])
for (auto& h : to)
h->fill(-427);
h.fill(-427);
for (size_t i = 1; i < reductions.size(); ++i)
reductions[i] = int((19.43 + std::log(size_t(options["Threads"])) / 2) * std::log(i));
reductions[i] = int(19.43 * std::log(i));
refreshTable.clear(networks[numaAccessToken]);
}
@@ -586,7 +584,10 @@ Value Search::Worker::search(
// Dive into quiescence search when the depth reaches zero
if (depth <= 0)
return qsearch<PvNode ? PV : NonPV>(pos, ss, alpha, beta);
{
constexpr auto nt = PvNode ? PV : NonPV;
return qsearch<nt>(pos, ss, alpha, beta);
}
// Limit the depth if extensions made it too large
depth = std::min(depth, MAX_PLY - 1);
@@ -641,10 +642,8 @@ Value Search::Worker::search(
// Step 2. Check for aborted search and immediate draw
if (threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply)
|| ss->ply >= MAX_PLY)
return (ss->ply >= MAX_PLY && !ss->inCheck)
? evaluate(networks[numaAccessToken], pos, refreshTable,
thisThread->optimism[us])
: value_draw(thisThread->nodes);
return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos)
: value_draw(thisThread->nodes);
// Step 3. Mate distance pruning. Even if we mate at the next move our score
// would be at best mate_in(ss->ply + 1), but if alpha is already bigger because
@@ -762,7 +761,8 @@ Value Search::Worker::search(
}
// Step 6. Static evaluation of the position
Value unadjustedStaticEval = VALUE_NONE;
Value unadjustedStaticEval = VALUE_NONE;
const auto correctionValue = correction_value(*thisThread, pos, ss);
if (ss->inCheck)
{
// Skip early pruning when in check
@@ -782,13 +782,11 @@ Value Search::Worker::search(
// Never assume anything about values stored in TT
unadjustedStaticEval = ttData.eval;
if (!is_valid(unadjustedStaticEval))
unadjustedStaticEval =
evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]);
unadjustedStaticEval = evaluate(pos);
else if (PvNode)
Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable);
ss->staticEval = eval =
to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss);
ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, correctionValue);
// ttValue can be used as a better position evaluation (~7 Elo)
if (is_valid(ttData.value)
@@ -797,10 +795,8 @@ Value Search::Worker::search(
}
else
{
unadjustedStaticEval =
evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]);
ss->staticEval = eval =
to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss);
unadjustedStaticEval = evaluate(pos);
ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, correctionValue);
// Static evaluation is saved as it was before adjustment by correction history
Distributed::save(tt, threads, thisThread, ttWriter, posKey, VALUE_NONE, ss->ttPv,
@@ -829,18 +825,16 @@ Value Search::Worker::search(
// Step 7. Razoring (~1 Elo)
// If eval is really low, check with qsearch if we can exceed alpha. If the
// search suggests we cannot exceed alpha, return a speculative fail low.
if (eval < alpha - 469 - 307 * depth * depth)
{
value = qsearch<NonPV>(pos, ss, alpha - 1, alpha);
if (value < alpha && !is_decisive(value))
return value;
}
// For PvNodes, we must have a guard against mates being returned.
if (!PvNode && eval < alpha - 469 - 307 * depth * depth)
return qsearch<NonPV>(pos, ss, alpha - 1, alpha);
// Step 8. Futility pruning: child node (~40 Elo)
// The depth condition is important for mate finding.
if (!ss->ttPv && depth < 14
&& eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening)
- (ss - 1)->statScore / 290
+ (ss->staticEval == eval) * (40 - std::abs(correctionValue) / 131072)
>= beta
&& eval >= beta && (!ttData.move || ttCapture) && !is_loss(beta) && !is_win(eval))
return beta + (eval - beta) / 3;
@@ -968,8 +962,6 @@ Value Search::Worker::search(
return is_decisive(value) ? value : value - (probCutBeta - beta);
}
}
Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable);
}
moves_loop: // When in check, search starts here
@@ -1212,6 +1204,10 @@ moves_loop: // When in check, search starts here
// These reduction adjustments have no proven non-linear scaling
r += 330;
r -= std::abs(correctionValue) / 32768;
// Increase reduction for cut nodes (~4 Elo)
if (cutNode)
r += 2518 - (ttData.depth >= depth && ss->ttPv) * 991;
@@ -1249,7 +1245,8 @@ moves_loop: // When in check, search starts here
// beyond the first move depth.
// To prevent problems when the max value is less than the min value,
// std::clamp has been replaced by a more robust implementation.
Depth d = std::max(1, std::min(newDepth - r / 1024, newDepth + !allNode));
Depth d = std::max(
1, std::min(newDepth - r / 1024, newDepth + !allNode + (PvNode && !bestMove)));
value = -search<NonPV>(pos, ss + 1, -(alpha + 1), -alpha, d, true);
@@ -1267,8 +1264,8 @@ moves_loop: // When in check, search starts here
value = -search<NonPV>(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode);
// Post LMR continuation history updates (~1 Elo)
int bonus = (value >= beta) * stat_bonus(newDepth);
update_continuation_histories(ss, movedPiece, move.to_sq(), bonus * 1427 / 1024);
int bonus = (value >= beta) * 2048;
update_continuation_histories(ss, movedPiece, move.to_sq(), bonus);
}
}
@@ -1460,10 +1457,6 @@ moves_loop: // When in check, search starts here
<< stat_bonus(depth) * 2;
}
// Bonus when search fails low and there is a TT move
else if (ttData.move && !allNode)
thisThread->mainHistory[us][ttData.move.from_to()] << stat_bonus(depth) * 287 / 1024;
if (PvNode)
bestValue = std::min(bestValue, maxValue);
@@ -1563,9 +1556,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
// Step 2. Check for an immediate draw or maximum ply reached
if (pos.is_draw(ss->ply) || ss->ply >= MAX_PLY)
return (ss->ply >= MAX_PLY && !ss->inCheck)
? evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us])
: VALUE_DRAW;
return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) : VALUE_DRAW;
assert(0 <= ss->ply && ss->ply < MAX_PLY);
@@ -1585,7 +1576,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
return ttData.value;
// Step 4. Static evaluation of the position
Value unadjustedStaticEval = VALUE_NONE;
Value unadjustedStaticEval = VALUE_NONE;
const auto correctionValue = correction_value(*thisThread, pos, ss);
if (ss->inCheck)
bestValue = futilityBase = -VALUE_INFINITE;
else
@@ -1595,10 +1587,9 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
// Never assume anything about values stored in TT
unadjustedStaticEval = ttData.eval;
if (!is_valid(unadjustedStaticEval))
unadjustedStaticEval =
evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]);
unadjustedStaticEval = evaluate(pos);
ss->staticEval = bestValue =
to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss);
to_corrected_static_eval(unadjustedStaticEval, correctionValue);
// ttValue can be used as a better position evaluation (~13 Elo)
if (is_valid(ttData.value) && !is_decisive(ttData.value)
@@ -1609,11 +1600,9 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
{
// In case of null move search, use previous static eval with opposite sign
unadjustedStaticEval =
(ss - 1)->currentMove != Move::null()
? evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us])
: -(ss - 1)->staticEval;
(ss - 1)->currentMove != Move::null() ? evaluate(pos) : -(ss - 1)->staticEval;
ss->staticEval = bestValue =
to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss);
to_corrected_static_eval(unadjustedStaticEval, correctionValue);
}
// Stand pat. Return immediately if static value is at least beta
@@ -1768,7 +1757,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) const {
int reductionScale = reductions[d] * reductions[mn];
return (reductionScale + 1304 - delta * 814 / rootDelta) + (!i && reductionScale > 1423) * 1135;
return reductionScale - delta * 814 / rootDelta + !i * reductionScale / 3 + 1304;
}
// elapsed() returns the time elapsed since the search started. If the
@@ -1785,6 +1774,10 @@ TimePoint Search::Worker::elapsed() const {
TimePoint Search::Worker::elapsed_time() const { return main_manager()->tm.elapsed_time(); }
Value Search::Worker::evaluate(const Position& pos) {
return Eval::evaluate(networks[numaAccessToken], pos, refreshTable,
optimism[pos.side_to_move()]);
}
namespace {
// Adjusts a mate or TB score from "plies to mate from the root" to
+3 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -130,7 +130,6 @@ struct LimitsType {
int movestogo, depth, mate, perft, infinite;
uint64_t nodes;
bool ponderMode;
Square capSq;
};
@@ -348,6 +347,8 @@ class Worker {
TimePoint elapsed() const;
TimePoint elapsed_time() const;
Value evaluate(const Position&);
LimitsType limits;
size_t pvIdx, pvLast;
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+1
View File
@@ -0,0 +1 @@
*.sh text eol=lf
+16
View File
@@ -150,6 +150,7 @@ class MiniTestFramework:
self.failed_test_suites = 0
self.passed_tests = 0
self.failed_tests = 0
self.stop_on_failure = True
def has_failed(self) -> bool:
return self.failed_test_suites > 0
@@ -167,6 +168,9 @@ class MiniTestFramework:
self.failed_test_suites += 1
else:
self.passed_test_suites += 1
except Exception as e:
self.failed_test_suites += 1
print(f"\n{RED_COLOR}Error: {e}{RESET_COLOR}")
finally:
os.chdir(original_cwd)
@@ -226,6 +230,10 @@ class MiniTestFramework:
if isinstance(e, AssertionError):
self.__handle_assertion_error(t0, method)
if self.stop_on_failure:
self.__print_buffer_output(buffer)
raise e
fails += 1
finally:
self.__print_buffer_output(buffer)
@@ -286,6 +294,11 @@ class Stockfish:
self.start()
def _check_process_alive(self):
if not self.process or self.process.poll() is not None:
print("\n".join(self.output))
raise RuntimeError("Stockfish process has terminated")
def start(self):
if self.cli:
self.process = subprocess.run(
@@ -314,6 +327,8 @@ class Stockfish:
if not self.process:
raise RuntimeError("Stockfish process is not started")
self._check_process_alive()
self.process.stdin.write(command + "\n")
self.process.stdin.flush()
@@ -355,6 +370,7 @@ class Stockfish:
raise RuntimeError("Stockfish process is not started")
while True:
self._check_process_alive()
line = self.process.stdout.readline().strip()
self.output.append(line)