diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 946a81cec..950435f30 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -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 diff --git a/AUTHORS b/AUTHORS index ddc53ec02..f6468c561 100644 --- a/AUTHORS +++ b/AUTHORS @@ -47,6 +47,7 @@ Bryan Cross (crossbr) candirufish Carlos Esparza Sánchez (ces42) Chess13234 +Chris Bao (sscg13) Chris Cain (ceebo) Ciekce clefrks diff --git a/scripts/.gitattributes b/scripts/.gitattributes new file mode 100644 index 000000000..dfdb8b771 --- /dev/null +++ b/scripts/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf diff --git a/src/Makefile b/src/Makefile index 1c9ba6aae..3127f9449 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 35ad3c180..a9f70f10d 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -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 diff --git a/src/benchmark.h b/src/benchmark.h index eb3a52d89..d6bdc275d 100644 --- a/src/benchmark.h +++ b/src/benchmark.h @@ -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 diff --git a/src/bitboard.cpp b/src/bitboard.cpp index deda6da2a..8798c5701 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -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 diff --git a/src/bitboard.h b/src/bitboard.h index c4bf18b53..6f9cca0bd 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -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 diff --git a/src/engine.cpp b/src/engine.cpp index ad3c1b0da..aba0ae993 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -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 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 states = StateListPtr(new std::deque(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 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(); } } diff --git a/src/engine.h b/src/engine.h index 257826935..d26844f4c 100644 --- a/src/engine.h +++ b/src/engine.h @@ -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; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index bc86a7420..4fce86e3a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -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() + pos.non_pawn_material(); + int material = 535 * pos.count() + pos.non_pawn_material(); int v = (nnue * (77777 + material) + optimism * (7777 + material)) / 77777; // Damp down the evaluation linearly when shuffling diff --git a/src/evaluate.h b/src/evaluate.h index 4604321d3..aad358321 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -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 diff --git a/src/history.h b/src/history.h index 17ab3481c..15095cd0b 100644 --- a/src/history.h +++ b/src/history.h @@ -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 #include // 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 class StatsEntry { + static_assert(std::is_arithmetic::value, "Not an arithmetic type"); + static_assert(D <= std::numeric_limits::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::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 -struct Stats: public std::array, Size> { - using stats = Stats; - - void fill(const T& v) { - - // For standard-layout 'this' points to the first struct member - assert(std::is_standard_layout_v); - - using entry = StatsEntry; - entry* p = reinterpret_cast(this); - std::fill(p, p + sizeof(*this) / sizeof(entry), v); - } -}; - -template -struct Stats: public std::array, Size> {}; - -// In stats table, D=0 means that the template parameter is not used -enum StatsParams { - NOT_USED = 0 -}; enum StatsType { NoCaptures, Captures }; +template +using Stats = MultiArray, 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; +using ButterflyHistory = Stats; // LowPlyHistory is adressed by play and move's from and to squares, used // to improve move ordering near the root -using LowPlyHistory = Stats; +using LowPlyHistory = + Stats; // CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] -using CapturePieceToHistory = Stats; +using CapturePieceToHistory = Stats; // PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] -using PieceToHistory = Stats; +using PieceToHistory = Stats; // 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; +using ContinuationHistory = MultiArray; // PawnHistory is addressed by the pawn structure and a move's [piece][to] -using PawnHistory = Stats; +using PawnHistory = Stats; // 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 struct CorrHistTypedef { - using type = Stats; + using type = Stats; }; template<> struct CorrHistTypedef { - using type = Stats; + using type = Stats; }; template<> struct CorrHistTypedef { - using type = Stats::type, NOT_USED, PIECE_NB, SQUARE_NB>; + using type = MultiArray::type, PIECE_NB, SQUARE_NB>; }; +} + template -using CorrectionHistory = typename CorrHistTypedef::type; +using CorrectionHistory = typename Detail::CorrHistTypedef::type; } // namespace Stockfish diff --git a/src/main.cpp b/src/main.cpp index 724f779a2..f0e0b8d05 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 diff --git a/src/memory.cpp b/src/memory.cpp index 47c901b4e..92cb650f2 100644 --- a/src/memory.cpp +++ b/src/memory.cpp @@ -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 diff --git a/src/memory.h b/src/memory.h index eaf0261aa..4dc232878 100644 --- a/src/memory.h +++ b/src/memory.h @@ -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 diff --git a/src/misc.cpp b/src/misc.cpp index 10c86b7a6..06a8c624a 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -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 #include +#include #include #include #include @@ -287,7 +289,10 @@ template struct DebugInfo { std::atomic data[N] = {0}; - constexpr std::atomic& operator[](int index) { return data[index]; } + [[nodiscard]] constexpr std::atomic& 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, MaxDebugSlots> hit; +std::array, MaxDebugSlots> mean; +std::array, MaxDebugSlots> stdev; +std::array, MaxDebugSlots> correl; +std::array 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() { diff --git a/src/misc.h b/src/misc.h index 21093769b..81c7b17fe 100644 --- a/src/misc.h +++ b/src/misc.h @@ -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 +#include #include #include #include @@ -142,6 +143,92 @@ class ValueList { }; +template +class MultiArray; + +namespace Detail { + +template +struct MultiArrayHelper { + using ChildType = MultiArray; +}; + +template +struct MultiArrayHelper { + using ChildType = T; +}; + +} + +// MultiArray is a generic N-dimensional array. +// The template parameters (Size and Sizes) encode the dimensions of the array. +template +class MultiArray { + using ChildType = typename Detail::MultiArrayHelper::ChildType; + using ArrayType = std::array; + 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 + void fill(const U& v) { + static_assert(std::is_assignable_v, "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& 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). diff --git a/src/movegen.cpp b/src/movegen.cpp index 69b8fe6ae..8653a828f 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -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 diff --git a/src/movegen.h b/src/movegen.h index f067f8808..7c6cceb7c 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -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 diff --git a/src/movepick.cpp b/src/movepick.cpp index 96f031717..844861694 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -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 #include #include #include "bitboard.h" +#include "misc.h" #include "position.h" namespace Stockfish { diff --git a/src/movepick.h b/src/movepick.h index ab4e832fd..71078bdcf 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -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 diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 71782a7b7..5bb0296e2 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -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 diff --git a/src/nnue/features/half_ka_v2_hm.h b/src/nnue/features/half_ka_v2_hm.h index 963497047..ca940c54e 100644 --- a/src/nnue/features/half_ka_v2_hm.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -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 diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 59a6149f0..f5c640fb9 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -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 diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index 0ac557aba..cbeb507f0 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -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 diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 2ee378ad8..2ad5a86a1 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -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 diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h index 55cb7df14..70ca68a0c 100644 --- a/src/nnue/layers/simd.h +++ b/src/nnue/layers/simd.h @@ -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 diff --git a/src/nnue/layers/sqr_clipped_relu.h b/src/nnue/layers/sqr_clipped_relu.h index 9c20df9d6..d14f1e0ab 100644 --- a/src/nnue/layers/sqr_clipped_relu.h +++ b/src/nnue/layers/sqr_clipped_relu.h @@ -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 diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index c5924183d..6923e5a07 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -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 -bool write_parameters(std::ostream& stream, const T& reference) { +bool write_parameters(std::ostream& stream, T& reference) { write_little_endian(stream, T::get_hash_value()); return reference.write_parameters(stream); diff --git a/src/nnue/network.h b/src/nnue/network.h index 95253595a..f99fa1182 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -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 diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index b92901e4a..0d3d94135 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -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 diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 7f73f87fd..0c9f097dc 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -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 diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 4bc3408f1..f21a8dec7 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -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 diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index eb16f20f2..c450d6456 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -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 +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 -static constexpr int BestRegisterCount() { - #define RegisterSize sizeof(SIMDRegisterType) - #define LaneSize sizeof(LaneType) + template + 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(); + static constexpr int NumPsqtRegs = + BestRegisterCount(); + + 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(); - static constexpr int NumPsqtRegs = - BestRegisterCount(); - - 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; 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(const_cast(&biases[0])); + uint64_t* b = reinterpret_cast(&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(const_cast(&weights[j * HalfDimensions])); + uint64_t* w = reinterpret_cast(&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(&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(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(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(&(computed->*accPtr).accumulation[Perspective][0]); - auto accOut = reinterpret_cast(&(next->*accPtr).accumulation[Perspective][0]); + auto* accOut = reinterpret_cast(&(next->*accPtr).accumulation[Perspective][0]); const IndexType offsetR0 = HalfDimensions * removed[0]; - auto columnR0 = reinterpret_cast(&weights[offsetR0]); + auto* columnR0 = reinterpret_cast(&weights[offsetR0]); const IndexType offsetA = HalfDimensions * added[0]; - auto columnA = reinterpret_cast(&weights[offsetA]); + auto* columnA = reinterpret_cast(&weights[offsetA]); if (removed.size() == 1) { @@ -521,22 +523,22 @@ class FeatureTransformer { else { const IndexType offsetR1 = HalfDimensions * removed[1]; - auto columnR1 = reinterpret_cast(&weights[offsetR1]); + auto* columnR1 = reinterpret_cast(&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( + auto* accPsqtIn = reinterpret_cast( &(computed->*accPtr).psqtAccumulation[Perspective][0]); - auto accPsqtOut = + auto* accPsqtOut = reinterpret_cast(&(next->*accPtr).psqtAccumulation[Perspective][0]); const IndexType offsetPsqtR0 = PSQTBuckets * removed[0]; - auto columnPsqtR0 = reinterpret_cast(&psqtWeights[offsetPsqtR0]); + auto* columnPsqtR0 = reinterpret_cast(&psqtWeights[offsetPsqtR0]); const IndexType offsetPsqtA = PSQTBuckets * added[0]; - auto columnPsqtA = reinterpret_cast(&psqtWeights[offsetPsqtA]); + auto* columnPsqtA = reinterpret_cast(&psqtWeights[offsetPsqtA]); if (removed.size() == 1) { @@ -548,7 +550,8 @@ class FeatureTransformer { else { const IndexType offsetPsqtR1 = PSQTBuckets * removed[1]; - auto columnPsqtR1 = reinterpret_cast(&psqtWeights[offsetPsqtR1]); + auto* columnPsqtR1 = + reinterpret_cast(&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( - &(computed->*accPtr).accumulation[Perspective][i * TileHeight]); - for (IndexType j = 0; j < NumRegs; ++j) + auto* accTileIn = reinterpret_cast( + &(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(&weights[offset]); - for (IndexType j = 0; j < NumRegs; ++j) + const IndexType offset = HalfDimensions * index + i * Tiling::TileHeight; + auto* column = reinterpret_cast(&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(&weights[offset]); - for (IndexType j = 0; j < NumRegs; ++j) + const IndexType offset = HalfDimensions * index + i * Tiling::TileHeight; + auto* column = reinterpret_cast(&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( - &(next->*accPtr).accumulation[Perspective][i * TileHeight]); - for (IndexType j = 0; j < NumRegs; ++j) + auto* accTileOut = reinterpret_cast( + &(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( - &(computed->*accPtr).psqtAccumulation[Perspective][i * PsqtTileHeight]); - for (std::size_t j = 0; j < NumPsqtRegs; ++j) + auto* accTilePsqtIn = reinterpret_cast( + &(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(&psqtWeights[offset]); - for (std::size_t j = 0; j < NumPsqtRegs; ++j) + const IndexType offset = PSQTBuckets * index + i * Tiling::PsqtTileHeight; + auto* columnPsqt = reinterpret_cast(&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(&psqtWeights[offset]); - for (std::size_t j = 0; j < NumPsqtRegs; ++j) + const IndexType offset = PSQTBuckets * index + i * Tiling::PsqtTileHeight; + auto* columnPsqt = reinterpret_cast(&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( - &(next->*accPtr).psqtAccumulation[Perspective][i * PsqtTileHeight]); - for (std::size_t j = 0; j < NumPsqtRegs; ++j) + auto* accTilePsqtOut = reinterpret_cast( + &(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(&accumulator.accumulation[Perspective][j * TileHeight]); - auto entryTile = reinterpret_cast(&entry.accumulation[j * TileHeight]); + auto* accTile = reinterpret_cast( + &accumulator.accumulation[Perspective][j * Tiling::TileHeight]); + auto* entryTile = reinterpret_cast(&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(&weights[offsetR]); + const IndexType offsetR = HalfDimensions * indexR + j * Tiling::TileHeight; + auto* columnR = reinterpret_cast(&weights[offsetR]); IndexType indexA = added[i]; - const IndexType offsetA = HalfDimensions * indexA + j * TileHeight; - auto columnA = reinterpret_cast(&weights[offsetA]); + const IndexType offsetA = HalfDimensions * indexA + j * Tiling::TileHeight; + auto* columnA = reinterpret_cast(&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(&weights[offset]); + const IndexType offset = HalfDimensions * index + j * Tiling::TileHeight; + auto* column = reinterpret_cast(&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(&weights[offset]); + const IndexType offset = HalfDimensions * index + j * Tiling::TileHeight; + auto* column = reinterpret_cast(&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( - &accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); - auto entryTilePsqt = - reinterpret_cast(&entry.psqtAccumulation[j * PsqtTileHeight]); + auto* accTilePsqt = reinterpret_cast( + &accumulator.psqtAccumulation[Perspective][j * Tiling::PsqtTileHeight]); + auto* entryTilePsqt = + reinterpret_cast(&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(&psqtWeights[offset]); + const IndexType offset = PSQTBuckets * index + j * Tiling::PsqtTileHeight; + auto* columnPsqt = reinterpret_cast(&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(&psqtWeights[offset]); + const IndexType offset = PSQTBuckets * index + j * Tiling::PsqtTileHeight; + auto* columnPsqt = reinterpret_cast(&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]); } diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index a2bece21d..1e2690503 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -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 diff --git a/src/nnue/nnue_misc.h b/src/nnue/nnue_misc.h index 27a93f884..a7647f846 100644 --- a/src/nnue/nnue_misc.h +++ b/src/nnue/nnue_misc.h @@ -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 diff --git a/src/numa.h b/src/numa.h index 1063721e3..5cadbc726 100644 --- a/src/numa.h +++ b/src/numa.h @@ -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 diff --git a/src/perft.h b/src/perft.h index fa21c67dc..545d5aec8 100644 --- a/src/perft.h +++ b/src/perft.h @@ -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 diff --git a/src/position.cpp b/src/position.cpp index 1b1c0269f..60b7d7d3f 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -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(k); } diff --git a/src/position.h b/src/position.h index 888612da7..7dc83f251 100644 --- a/src/position.h +++ b/src/position.h @@ -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 diff --git a/src/score.cpp b/src/score.cpp index 179796d20..561bc23c4 100644 --- a/src/score.cpp +++ b/src/score.cpp @@ -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 diff --git a/src/score.h b/src/score.h index 2eb40f7e0..eda90af35 100644 --- a/src/score.h +++ b/src/score.h @@ -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 diff --git a/src/search.cpp b/src/search.cpp index ab85d5520..a140e09d1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -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(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(pos)]; const auto bnpcv = w.nonPawnCorrectionHistory[BLACK][us][non_pawn_index(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(pos, ss, alpha, beta); + { + constexpr auto nt = PvNode ? PV : NonPV; + return qsearch(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(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(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(pos, ss + 1, -(alpha + 1), -alpha, d, true); @@ -1267,8 +1264,8 @@ moves_loop: // When in check, search starts here value = -search(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 diff --git a/src/search.h b/src/search.h index af2a72928..b82e94b1c 100644 --- a/src/search.h +++ b/src/search.h @@ -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; diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 806211578..f1e484a44 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -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 diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index 75a185857..c34338fe3 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -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 diff --git a/src/thread.cpp b/src/thread.cpp index 5e4f9abc9..d6f33472b 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -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 diff --git a/src/thread.h b/src/thread.h index 780ef79fa..b971e0b41 100644 --- a/src/thread.h +++ b/src/thread.h @@ -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 diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index 1d9a834f6..fb4b2ec97 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -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 diff --git a/src/timeman.cpp b/src/timeman.cpp index 9de70fdc6..d0b0d0a94 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -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 diff --git a/src/timeman.h b/src/timeman.h index 016103470..1175e39c5 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -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 diff --git a/src/tt.cpp b/src/tt.cpp index 75689562d..50f5ca45a 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -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 diff --git a/src/tt.h b/src/tt.h index b6bc87571..bbc11a845 100644 --- a/src/tt.h +++ b/src/tt.h @@ -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 diff --git a/src/tune.cpp b/src/tune.cpp index dfcd34689..aff96c8cb 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -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 diff --git a/src/tune.h b/src/tune.h index ed4738cdc..4dab6bf45 100644 --- a/src/tune.h +++ b/src/tune.h @@ -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 diff --git a/src/types.h b/src/types.h index 564446011..6465dfd6b 100644 --- a/src/types.h +++ b/src/types.h @@ -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 diff --git a/src/uci.cpp b/src/uci.cpp index a9722d53d..5eb547112 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -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 diff --git a/src/uci.h b/src/uci.h index 6adf74cb8..5c1c07f7b 100644 --- a/src/uci.h +++ b/src/uci.h @@ -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 diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 0427c90e1..b2f141eba 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -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 diff --git a/src/ucioption.h b/src/ucioption.h index a47cc98de..c9f6787d3 100644 --- a/src/ucioption.h +++ b/src/ucioption.h @@ -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 diff --git a/tests/.gitattributes b/tests/.gitattributes new file mode 100644 index 000000000..dfdb8b771 --- /dev/null +++ b/tests/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf diff --git a/tests/testing.py b/tests/testing.py index d51ca89ac..bc1f6b15b 100644 --- a/tests/testing.py +++ b/tests/testing.py @@ -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)