├── .gitignore ├── src ├── FishWrap.h ├── StockfishObjC.h ├── StockfishObjC.m ├── incbin │ ├── UNLICENCE │ └── incbin.h ├── nnue │ ├── nnue_accumulator.h │ ├── nnue_architecture.h │ ├── features │ │ ├── features_common.h │ │ ├── index_list.h │ │ ├── half_kp.h │ │ ├── feature_set.h │ │ └── half_kp.cpp │ ├── evaluate_nnue.h │ ├── architectures │ │ └── halfkp_256x2-32-32.h │ ├── layers │ │ ├── input_slice.h │ │ └── clipped_relu.h │ ├── nnue_common.h │ └── evaluate_nnue.cpp ├── main.cpp ├── evaluate.h ├── timeman.h ├── movegen.h ├── thread_win32_osx.h ├── pawns.h ├── syzygy │ └── tbprobe.h ├── uci.h ├── material.h ├── tt.h ├── search.h ├── timeman.cpp ├── endgame.h ├── thread.h ├── tune.cpp ├── FishWrap.mm ├── misc.h ├── psqt.cpp ├── tt.cpp ├── bitbase.cpp ├── movepick.h ├── ucioption.cpp ├── benchmark.cpp ├── tune.h ├── bitboard.cpp ├── thread.cpp ├── material.cpp ├── movepick.cpp ├── pawns.cpp └── movegen.cpp ├── stockfish.xcodeproj └── project.xcworkspace │ └── contents.xcworkspacedata └── AUTHORS /.gitignore: -------------------------------------------------------------------------------- 1 | project.pbxproj/ 2 | xcuserdata 3 | .DS_Store 4 | /*.xcodeproj 5 | -------------------------------------------------------------------------------- /src/FishWrap.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface FishWrap : NSObject 4 | - (NSString *)sendUCICommand:(NSString *)uci; 5 | -(instancetype)initWithTalksTo:(NSObject *)talksTo; 6 | @end 7 | 8 | -------------------------------------------------------------------------------- /stockfish.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/StockfishObjC.h: -------------------------------------------------------------------------------- 1 | // 2 | // StockfishObjC.h 3 | // stockfish 4 | // 5 | // Created by Douglas Pedley on 12/27/20. 6 | // 7 | 8 | #import 9 | 10 | // These came from the Makefile. 11 | #define NNUE_EMBEDDING_OFF 12 | #define IS_64BIT 13 | #define USE_PTHREADS 14 | @protocol TalksToFish 15 | -(void)stockfishBestMove:(NSString *)message; 16 | -(void)stockfishInfo:(NSString *)infoMessage; 17 | -(void)stockfishError:(NSString *)errorMessage; 18 | @end 19 | 20 | @interface Stockfish : NSObject 21 | -(void)setPositionAndEvaluate:(NSString *)position time:(NSTimeInterval)timeInterval; 22 | -(instancetype)initWithTalksTo:(NSObject *)talksTo; 23 | @end 24 | -------------------------------------------------------------------------------- /src/StockfishObjC.m: -------------------------------------------------------------------------------- 1 | // 2 | // stockfish.m 3 | // stockfish 4 | // 5 | // Created by Douglas Pedley on 12/27/20. 6 | // 7 | 8 | #import "StockfishObjC.h" 9 | #import "FishWrap.h" 10 | 11 | 12 | @interface Stockfish () 13 | @property (nonatomic, strong) FishWrap *fishWrap; 14 | @end 15 | 16 | @implementation Stockfish 17 | 18 | -(instancetype)initWithTalksTo:(NSObject *)talksTo { 19 | self = [super init]; 20 | if (self) { 21 | self.fishWrap = [[FishWrap alloc] initWithTalksTo:talksTo]; 22 | } 23 | return self; 24 | } 25 | 26 | -(void)setPositionAndEvaluate:(NSString *)position time:(NSTimeInterval)timeInterval { 27 | NSString *go = @"go depth 16"; //[NSString stringWithFormat:@"go movetime %d", (int)(timeInterval * 1000)]; 28 | [self.fishWrap sendUCICommand:position]; 29 | [self.fishWrap sendUCICommand:go]; 30 | } 31 | 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /src/incbin/UNLICENCE: -------------------------------------------------------------------------------- 1 | The file "incbin.h" is free and unencumbered software released into 2 | the public domain by Dale Weiler, see: 3 | 4 | 5 | Anyone is free to copy, modify, publish, use, compile, sell, or 6 | distribute this software, either in source code form or as a compiled 7 | binary, for any purpose, commercial or non-commercial, and by any 8 | means. 9 | 10 | In jurisdictions that recognize copyright laws, the author or authors 11 | of this software dedicate any and all copyright interest in the 12 | software to the public domain. We make this dedication for the benefit 13 | of the public at large and to the detriment of our heirs and 14 | successors. We intend this dedication to be an overt act of 15 | relinquishment in perpetuity of all present and future rights to this 16 | software under copyright law. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | For more information, please refer to 27 | -------------------------------------------------------------------------------- /src/nnue/nnue_accumulator.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // Class for difference calculation of NNUE evaluation function 20 | 21 | #ifndef NNUE_ACCUMULATOR_H_INCLUDED 22 | #define NNUE_ACCUMULATOR_H_INCLUDED 23 | 24 | #include "nnue_architecture.h" 25 | 26 | namespace Eval::NNUE { 27 | 28 | // The accumulator of a StateInfo without parent is set to the INIT state 29 | enum AccumulatorState { EMPTY, COMPUTED, INIT }; 30 | 31 | // Class that holds the result of affine transformation of input features 32 | struct alignas(kCacheLineSize) Accumulator { 33 | std::int16_t 34 | accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions]; 35 | AccumulatorState state[2]; 36 | }; 37 | 38 | } // namespace Eval::NNUE 39 | 40 | #endif // NNUE_ACCUMULATOR_H_INCLUDED 41 | -------------------------------------------------------------------------------- /src/nnue/nnue_architecture.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // Input features and network structure used in NNUE evaluation function 20 | 21 | #ifndef NNUE_ARCHITECTURE_H_INCLUDED 22 | #define NNUE_ARCHITECTURE_H_INCLUDED 23 | 24 | // Defines the network structure 25 | #include "architectures/halfkp_256x2-32-32.h" 26 | 27 | namespace Eval::NNUE { 28 | 29 | static_assert(kTransformedFeatureDimensions % kMaxSimdWidth == 0, ""); 30 | static_assert(Network::kOutputDimensions == 1, ""); 31 | static_assert(std::is_same::value, ""); 32 | 33 | // Trigger for full calculation instead of difference calculation 34 | constexpr auto kRefreshTriggers = RawFeatures::kRefreshTriggers; 35 | 36 | } // namespace Eval::NNUE 37 | 38 | #endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED 39 | -------------------------------------------------------------------------------- /src/nnue/features/features_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | //Common header of input features of NNUE evaluation function 20 | 21 | #ifndef NNUE_FEATURES_COMMON_H_INCLUDED 22 | #define NNUE_FEATURES_COMMON_H_INCLUDED 23 | 24 | #include "../../evaluate.h" 25 | #include "../nnue_common.h" 26 | 27 | namespace Eval::NNUE::Features { 28 | 29 | class IndexList; 30 | 31 | template 32 | class FeatureSet; 33 | 34 | // Trigger to perform full calculations instead of difference only 35 | enum class TriggerEvent { 36 | kFriendKingMoved // calculate full evaluation when own king moves 37 | }; 38 | 39 | enum class Side { 40 | kFriend // side to move 41 | }; 42 | 43 | } // namespace Eval::NNUE::Features 44 | 45 | #endif // #ifndef NNUE_FEATURES_COMMON_H_INCLUDED 46 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include "bitboard.h" 22 | #include "endgame.h" 23 | #include "position.h" 24 | #include "search.h" 25 | #include "thread.h" 26 | #include "tt.h" 27 | #include "uci.h" 28 | #include "syzygy/tbprobe.h" 29 | 30 | namespace PSQT { 31 | void init(); 32 | } 33 | 34 | int main(int argc, char* argv[]) { 35 | 36 | std::cout << engine_info() << std::endl; 37 | 38 | CommandLine::init(argc, argv); 39 | UCI::init(Options); 40 | Tune::init(); 41 | PSQT::init(); 42 | Bitboards::init(); 43 | Position::init(); 44 | Bitbases::init(); 45 | Endgames::init(); 46 | Threads.set(size_t(Options["Threads"])); 47 | Search::clear(); // After threads are up 48 | Eval::NNUE::init(); 49 | 50 | UCI::loop(argc, argv); 51 | 52 | Threads.set(0); 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /src/evaluate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef EVALUATE_H_INCLUDED 20 | #define EVALUATE_H_INCLUDED 21 | 22 | #include 23 | 24 | #include "types.h" 25 | 26 | class Position; 27 | 28 | namespace Eval { 29 | 30 | std::string trace(const Position& pos); 31 | Value evaluate(const Position& pos); 32 | 33 | extern bool useNNUE; 34 | extern std::string eval_file_loaded; 35 | 36 | // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue 37 | // for the build process (profile-build and fishtest) to work. Do not change the 38 | // name of the macro, as it is used in the Makefile. 39 | #define EvalFileDefaultName "nn-62ef826d1a6d.nnue" 40 | 41 | namespace NNUE { 42 | 43 | Value evaluate(const Position& pos); 44 | bool load_eval(std::string name, std::istream& stream); 45 | void init(); 46 | void verify(); 47 | 48 | } // namespace NNUE 49 | 50 | } // namespace Eval 51 | 52 | #endif // #ifndef EVALUATE_H_INCLUDED 53 | -------------------------------------------------------------------------------- /src/timeman.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef TIMEMAN_H_INCLUDED 20 | #define TIMEMAN_H_INCLUDED 21 | 22 | #include "misc.h" 23 | #include "search.h" 24 | #include "thread.h" 25 | 26 | /// The TimeManagement class computes the optimal time to think depending on 27 | /// the maximum available time, the game move number and other parameters. 28 | 29 | class TimeManagement { 30 | public: 31 | void init(Search::LimitsType& limits, Color us, int ply); 32 | TimePoint optimum() const { return optimumTime; } 33 | TimePoint maximum() const { return maximumTime; } 34 | TimePoint elapsed() const { return Search::Limits.npmsec ? 35 | TimePoint(Threads.nodes_searched()) : now() - startTime; } 36 | 37 | int64_t availableNodes; // When in 'nodes as time' mode 38 | 39 | private: 40 | TimePoint startTime; 41 | TimePoint optimumTime; 42 | TimePoint maximumTime; 43 | }; 44 | 45 | extern TimeManagement Time; 46 | 47 | #endif // #ifndef TIMEMAN_H_INCLUDED 48 | -------------------------------------------------------------------------------- /src/nnue/evaluate_nnue.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // header used in NNUE evaluation function 20 | 21 | #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED 22 | #define NNUE_EVALUATE_NNUE_H_INCLUDED 23 | 24 | #include "nnue_feature_transformer.h" 25 | 26 | #include 27 | 28 | namespace Eval::NNUE { 29 | 30 | // Hash value of evaluation function structure 31 | constexpr std::uint32_t kHashValue = 32 | FeatureTransformer::GetHashValue() ^ Network::GetHashValue(); 33 | 34 | // Deleter for automating release of memory area 35 | template 36 | struct AlignedDeleter { 37 | void operator()(T* ptr) const { 38 | ptr->~T(); 39 | std_aligned_free(ptr); 40 | } 41 | }; 42 | 43 | template 44 | struct LargePageDeleter { 45 | void operator()(T* ptr) const { 46 | ptr->~T(); 47 | aligned_large_pages_free(ptr); 48 | } 49 | }; 50 | 51 | template 52 | using AlignedPtr = std::unique_ptr>; 53 | 54 | template 55 | using LargePagePtr = std::unique_ptr>; 56 | 57 | } // namespace Eval::NNUE 58 | 59 | #endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED 60 | -------------------------------------------------------------------------------- /src/nnue/architectures/halfkp_256x2-32-32.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // Definition of input features and network structure used in NNUE evaluation function 20 | 21 | #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED 22 | #define NNUE_HALFKP_256X2_32_32_H_INCLUDED 23 | 24 | #include "../features/feature_set.h" 25 | #include "../features/half_kp.h" 26 | 27 | #include "../layers/input_slice.h" 28 | #include "../layers/affine_transform.h" 29 | #include "../layers/clipped_relu.h" 30 | 31 | namespace Eval::NNUE { 32 | 33 | // Input features used in evaluation function 34 | using RawFeatures = Features::FeatureSet< 35 | Features::HalfKP>; 36 | 37 | // Number of input feature dimensions after conversion 38 | constexpr IndexType kTransformedFeatureDimensions = 256; 39 | 40 | namespace Layers { 41 | 42 | // Define network structure 43 | using InputLayer = InputSlice; 44 | using HiddenLayer1 = ClippedReLU>; 45 | using HiddenLayer2 = ClippedReLU>; 46 | using OutputLayer = AffineTransform; 47 | 48 | } // namespace Layers 49 | 50 | using Network = Layers::OutputLayer; 51 | 52 | } // namespace Eval::NNUE 53 | 54 | #endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED 55 | -------------------------------------------------------------------------------- /src/movegen.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef MOVEGEN_H_INCLUDED 20 | #define MOVEGEN_H_INCLUDED 21 | 22 | #include 23 | 24 | #include "types.h" 25 | 26 | class Position; 27 | 28 | enum GenType { 29 | CAPTURES, 30 | QUIETS, 31 | QUIET_CHECKS, 32 | EVASIONS, 33 | NON_EVASIONS, 34 | LEGAL 35 | }; 36 | 37 | struct ExtMove { 38 | Move move; 39 | int value; 40 | 41 | operator Move() const { return move; } 42 | void operator=(Move m) { move = m; } 43 | 44 | // Inhibit unwanted implicit conversions to Move 45 | // with an ambiguity that yields to a compile error. 46 | operator float() const = delete; 47 | }; 48 | 49 | inline bool operator<(const ExtMove& f, const ExtMove& s) { 50 | return f.value < s.value; 51 | } 52 | 53 | template 54 | ExtMove* generate(const Position& pos, ExtMove* moveList); 55 | 56 | /// The MoveList struct is a simple wrapper around generate(). It sometimes comes 57 | /// in handy to use this class instead of the low level generate() function. 58 | template 59 | struct MoveList { 60 | 61 | explicit MoveList(const Position& pos) : last(generate(pos, moveList)) {} 62 | const ExtMove* begin() const { return moveList; } 63 | const ExtMove* end() const { return last; } 64 | size_t size() const { return last - moveList; } 65 | bool contains(Move move) const { 66 | return std::find(begin(), end(), move) != end(); 67 | } 68 | 69 | private: 70 | ExtMove moveList[MAX_MOVES], *last; 71 | }; 72 | 73 | #endif // #ifndef MOVEGEN_H_INCLUDED 74 | -------------------------------------------------------------------------------- /src/nnue/layers/input_slice.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // NNUE evaluation function layer InputSlice definition 20 | 21 | #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED 22 | #define NNUE_LAYERS_INPUT_SLICE_H_INCLUDED 23 | 24 | #include "../nnue_common.h" 25 | 26 | namespace Eval::NNUE::Layers { 27 | 28 | // Input layer 29 | template 30 | class InputSlice { 31 | public: 32 | // Need to maintain alignment 33 | static_assert(Offset % kMaxSimdWidth == 0, ""); 34 | 35 | // Output type 36 | using OutputType = TransformedFeatureType; 37 | 38 | // Output dimensionality 39 | static constexpr IndexType kOutputDimensions = OutputDimensions; 40 | 41 | // Size of forward propagation buffer used from the input layer to this layer 42 | static constexpr std::size_t kBufferSize = 0; 43 | 44 | // Hash value embedded in the evaluation file 45 | static constexpr std::uint32_t GetHashValue() { 46 | std::uint32_t hash_value = 0xEC42E90Du; 47 | hash_value ^= kOutputDimensions ^ (Offset << 10); 48 | return hash_value; 49 | } 50 | 51 | // Read network parameters 52 | bool ReadParameters(std::istream& /*stream*/) { 53 | return true; 54 | } 55 | 56 | // Forward propagation 57 | const OutputType* Propagate( 58 | const TransformedFeatureType* transformed_features, 59 | char* /*buffer*/) const { 60 | return transformed_features + Offset; 61 | } 62 | 63 | private: 64 | }; 65 | 66 | } // namespace Layers 67 | 68 | #endif // #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED 69 | -------------------------------------------------------------------------------- /src/nnue/features/index_list.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // Definition of index list of input features 20 | 21 | #ifndef NNUE_FEATURES_INDEX_LIST_H_INCLUDED 22 | #define NNUE_FEATURES_INDEX_LIST_H_INCLUDED 23 | 24 | #include "../../position.h" 25 | #include "../nnue_architecture.h" 26 | 27 | namespace Eval::NNUE::Features { 28 | 29 | // Class template used for feature index list 30 | template 31 | class ValueList { 32 | 33 | public: 34 | std::size_t size() const { return size_; } 35 | void resize(std::size_t size) { size_ = size; } 36 | void push_back(const T& value) { values_[size_++] = value; } 37 | T& operator[](std::size_t index) { return values_[index]; } 38 | T* begin() { return values_; } 39 | T* end() { return values_ + size_; } 40 | const T& operator[](std::size_t index) const { return values_[index]; } 41 | const T* begin() const { return values_; } 42 | const T* end() const { return values_ + size_; } 43 | 44 | void swap(ValueList& other) { 45 | const std::size_t max_size = std::max(size_, other.size_); 46 | for (std::size_t i = 0; i < max_size; ++i) { 47 | std::swap(values_[i], other.values_[i]); 48 | } 49 | std::swap(size_, other.size_); 50 | } 51 | 52 | private: 53 | T values_[MaxSize]; 54 | std::size_t size_ = 0; 55 | }; 56 | 57 | //Type of feature index list 58 | class IndexList 59 | : public ValueList { 60 | }; 61 | 62 | } // namespace Eval::NNUE::Features 63 | 64 | #endif // NNUE_FEATURES_INDEX_LIST_H_INCLUDED 65 | -------------------------------------------------------------------------------- /src/thread_win32_osx.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef THREAD_WIN32_OSX_H_INCLUDED 20 | #define THREAD_WIN32_OSX_H_INCLUDED 21 | 22 | #include 23 | 24 | /// On OSX threads other than the main thread are created with a reduced stack 25 | /// size of 512KB by default, this is too low for deep searches, which require 26 | /// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE. 27 | /// The implementation calls pthread_create() with the stack size parameter 28 | /// equal to the linux 8MB default, on platforms that support it. 29 | 30 | #if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS) 31 | 32 | #include 33 | 34 | static const size_t TH_STACK_SIZE = 8 * 1024 * 1024; 35 | 36 | template > 37 | void* start_routine(void* ptr) 38 | { 39 | P* p = reinterpret_cast(ptr); 40 | (p->first->*(p->second))(); // Call member function pointer 41 | delete p; 42 | return NULL; 43 | } 44 | 45 | class NativeThread { 46 | 47 | pthread_t thread; 48 | 49 | public: 50 | template> 51 | explicit NativeThread(void(T::*fun)(), T* obj) { 52 | pthread_attr_t attr_storage, *attr = &attr_storage; 53 | pthread_attr_init(attr); 54 | pthread_attr_setstacksize(attr, TH_STACK_SIZE); 55 | pthread_create(&thread, attr, start_routine, new P(obj, fun)); 56 | } 57 | void join() { pthread_join(thread, NULL); } 58 | }; 59 | 60 | #else // Default case: use STL classes 61 | 62 | typedef std::thread NativeThread; 63 | 64 | #endif 65 | 66 | #endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED 67 | -------------------------------------------------------------------------------- /src/pawns.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef PAWNS_H_INCLUDED 20 | #define PAWNS_H_INCLUDED 21 | 22 | #include "misc.h" 23 | #include "position.h" 24 | #include "types.h" 25 | 26 | namespace Pawns { 27 | 28 | /// Pawns::Entry contains various information about a pawn structure. A lookup 29 | /// to the pawn hash table (performed by calling the probe function) returns a 30 | /// pointer to an Entry object. 31 | 32 | struct Entry { 33 | 34 | Score pawn_score(Color c) const { return scores[c]; } 35 | Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } 36 | Bitboard passed_pawns(Color c) const { return passedPawns[c]; } 37 | Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } 38 | int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); } 39 | int blocked_count() const { return blockedCount; } 40 | 41 | template 42 | Score king_safety(const Position& pos) { 43 | return kingSquares[Us] == pos.square(Us) && castlingRights[Us] == pos.castling_rights(Us) 44 | ? kingSafety[Us] : (kingSafety[Us] = do_king_safety(pos)); 45 | } 46 | 47 | template 48 | Score do_king_safety(const Position& pos); 49 | 50 | template 51 | Score evaluate_shelter(const Position& pos, Square ksq) const; 52 | 53 | Key key; 54 | Score scores[COLOR_NB]; 55 | Bitboard passedPawns[COLOR_NB]; 56 | Bitboard pawnAttacks[COLOR_NB]; 57 | Bitboard pawnAttacksSpan[COLOR_NB]; 58 | Square kingSquares[COLOR_NB]; 59 | Score kingSafety[COLOR_NB]; 60 | int castlingRights[COLOR_NB]; 61 | int blockedCount; 62 | }; 63 | 64 | typedef HashTable Table; 65 | 66 | Entry* probe(const Position& pos); 67 | 68 | } // namespace Pawns 69 | 70 | #endif // #ifndef PAWNS_H_INCLUDED 71 | -------------------------------------------------------------------------------- /src/nnue/features/half_kp.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | //Definition of input features HalfKP of NNUE evaluation function 20 | 21 | #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED 22 | #define NNUE_FEATURES_HALF_KP_H_INCLUDED 23 | 24 | #include "../../evaluate.h" 25 | #include "features_common.h" 26 | 27 | namespace Eval::NNUE::Features { 28 | 29 | // Feature HalfKP: Combination of the position of own king 30 | // and the position of pieces other than kings 31 | template 32 | class HalfKP { 33 | 34 | public: 35 | // Feature name 36 | static constexpr const char* kName = "HalfKP(Friend)"; 37 | // Hash value embedded in the evaluation file 38 | static constexpr std::uint32_t kHashValue = 39 | 0x5D69D5B9u ^ (AssociatedKing == Side::kFriend); 40 | // Number of feature dimensions 41 | static constexpr IndexType kDimensions = 42 | static_cast(SQUARE_NB) * static_cast(PS_END); 43 | // Maximum number of simultaneously active features 44 | static constexpr IndexType kMaxActiveDimensions = 30; // Kings don't count 45 | // Trigger for full calculation instead of difference calculation 46 | static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved; 47 | 48 | // Get a list of indices for active features 49 | static void AppendActiveIndices(const Position& pos, Color perspective, 50 | IndexList* active); 51 | 52 | // Get a list of indices for recently changed features 53 | static void AppendChangedIndices(const Position& pos, const DirtyPiece& dp, Color perspective, 54 | IndexList* removed, IndexList* added); 55 | }; 56 | 57 | } // namespace Eval::NNUE::Features 58 | 59 | #endif // #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED 60 | -------------------------------------------------------------------------------- /src/nnue/features/feature_set.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // A class template that represents the input feature set of the NNUE evaluation function 20 | 21 | #ifndef NNUE_FEATURE_SET_H_INCLUDED 22 | #define NNUE_FEATURE_SET_H_INCLUDED 23 | 24 | #include "features_common.h" 25 | #include 26 | 27 | namespace Eval::NNUE::Features { 28 | 29 | // Class template that represents a list of values 30 | template 31 | struct CompileTimeList; 32 | 33 | template 34 | struct CompileTimeList { 35 | static constexpr bool Contains(T value) { 36 | return value == First || CompileTimeList::Contains(value); 37 | } 38 | static constexpr std::array 39 | kValues = {{First, Remaining...}}; 40 | }; 41 | 42 | // Base class of feature set 43 | template 44 | class FeatureSetBase { 45 | 46 | }; 47 | 48 | // Class template that represents the feature set 49 | template 50 | class FeatureSet : public FeatureSetBase> { 51 | 52 | public: 53 | // Hash value embedded in the evaluation file 54 | static constexpr std::uint32_t kHashValue = FeatureType::kHashValue; 55 | // Number of feature dimensions 56 | static constexpr IndexType kDimensions = FeatureType::kDimensions; 57 | // Maximum number of simultaneously active features 58 | static constexpr IndexType kMaxActiveDimensions = 59 | FeatureType::kMaxActiveDimensions; 60 | // Trigger for full calculation instead of difference calculation 61 | using SortedTriggerSet = 62 | CompileTimeList; 63 | static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues; 64 | 65 | }; 66 | 67 | } // namespace Eval::NNUE::Features 68 | 69 | #endif // #ifndef NNUE_FEATURE_SET_H_INCLUDED 70 | -------------------------------------------------------------------------------- /src/syzygy/tbprobe.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef TBPROBE_H 20 | #define TBPROBE_H 21 | 22 | #include 23 | 24 | #include "../search.h" 25 | 26 | namespace Tablebases { 27 | 28 | enum WDLScore { 29 | WDLLoss = -2, // Loss 30 | WDLBlessedLoss = -1, // Loss, but draw under 50-move rule 31 | WDLDraw = 0, // Draw 32 | WDLCursedWin = 1, // Win, but draw under 50-move rule 33 | WDLWin = 2, // Win 34 | 35 | WDLScoreNone = -1000 36 | }; 37 | 38 | // Possible states after a probing operation 39 | enum ProbeState { 40 | FAIL = 0, // Probe failed (missing file table) 41 | OK = 1, // Probe succesful 42 | CHANGE_STM = -1, // DTZ should check the other side 43 | ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move) 44 | }; 45 | 46 | extern int MaxCardinality; 47 | 48 | void init(const std::string& paths); 49 | WDLScore probe_wdl(Position& pos, ProbeState* result); 50 | int probe_dtz(Position& pos, ProbeState* result); 51 | bool root_probe(Position& pos, Search::RootMoves& rootMoves); 52 | bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves); 53 | void rank_root_moves(Position& pos, Search::RootMoves& rootMoves); 54 | 55 | inline std::ostream& operator<<(std::ostream& os, const WDLScore v) { 56 | 57 | os << (v == WDLLoss ? "Loss" : 58 | v == WDLBlessedLoss ? "Blessed loss" : 59 | v == WDLDraw ? "Draw" : 60 | v == WDLCursedWin ? "Cursed win" : 61 | v == WDLWin ? "Win" : "None"); 62 | 63 | return os; 64 | } 65 | 66 | inline std::ostream& operator<<(std::ostream& os, const ProbeState v) { 67 | 68 | os << (v == FAIL ? "Failed" : 69 | v == OK ? "Success" : 70 | v == CHANGE_STM ? "Probed opponent side" : 71 | v == ZEROING_BEST_MOVE ? "Best move zeroes DTZ" : "None"); 72 | 73 | return os; 74 | } 75 | 76 | } 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/nnue/features/half_kp.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | //Definition of input features HalfKP of NNUE evaluation function 20 | 21 | #include "half_kp.h" 22 | #include "index_list.h" 23 | 24 | namespace Eval::NNUE::Features { 25 | 26 | // Orient a square according to perspective (rotates by 180 for black) 27 | inline Square orient(Color perspective, Square s) { 28 | return Square(int(s) ^ (bool(perspective) * 63)); 29 | } 30 | 31 | // Index of a feature for a given king position and another piece on some square 32 | inline IndexType make_index(Color perspective, Square s, Piece pc, Square ksq) { 33 | return IndexType(orient(perspective, s) + kpp_board_index[perspective][pc] + PS_END * ksq); 34 | } 35 | 36 | // Get a list of indices for active features 37 | template 38 | void HalfKP::AppendActiveIndices( 39 | const Position& pos, Color perspective, IndexList* active) { 40 | 41 | Square ksq = orient(perspective, pos.square(perspective)); 42 | Bitboard bb = pos.pieces() & ~pos.pieces(KING); 43 | while (bb) { 44 | Square s = pop_lsb(&bb); 45 | active->push_back(make_index(perspective, s, pos.piece_on(s), ksq)); 46 | } 47 | } 48 | 49 | // Get a list of indices for recently changed features 50 | template 51 | void HalfKP::AppendChangedIndices( 52 | const Position& pos, const DirtyPiece& dp, Color perspective, 53 | IndexList* removed, IndexList* added) { 54 | 55 | Square ksq = orient(perspective, pos.square(perspective)); 56 | for (int i = 0; i < dp.dirty_num; ++i) { 57 | Piece pc = dp.piece[i]; 58 | if (type_of(pc) == KING) continue; 59 | if (dp.from[i] != SQ_NONE) 60 | removed->push_back(make_index(perspective, dp.from[i], pc, ksq)); 61 | if (dp.to[i] != SQ_NONE) 62 | added->push_back(make_index(perspective, dp.to[i], pc, ksq)); 63 | } 64 | } 65 | 66 | template class HalfKP; 67 | 68 | } // namespace Eval::NNUE::Features 69 | -------------------------------------------------------------------------------- /src/uci.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef UCI_H_INCLUDED 20 | #define UCI_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "types.h" 27 | 28 | class Position; 29 | struct StateInfo; 30 | 31 | namespace UCI { 32 | 33 | class Option; 34 | 35 | /// Custom comparator because UCI options should be case insensitive 36 | struct CaseInsensitiveLess { 37 | bool operator() (const std::string&, const std::string&) const; 38 | }; 39 | 40 | /// Our options container is actually a std::map 41 | typedef std::map OptionsMap; 42 | 43 | /// Option class implements an option as defined by UCI protocol 44 | class Option { 45 | 46 | typedef void (*OnChange)(const Option&); 47 | 48 | public: 49 | Option(OnChange = nullptr); 50 | Option(bool v, OnChange = nullptr); 51 | Option(const char* v, OnChange = nullptr); 52 | Option(double v, int minv, int maxv, OnChange = nullptr); 53 | Option(const char* v, const char* cur, OnChange = nullptr); 54 | 55 | Option& operator=(const std::string&); 56 | void operator<<(const Option&); 57 | operator double() const; 58 | operator std::string() const; 59 | bool operator==(const char*) const; 60 | 61 | private: 62 | friend std::ostream& operator<<(std::ostream&, const OptionsMap&); 63 | 64 | std::string defaultValue, currentValue, type; 65 | int min, max; 66 | size_t idx; 67 | OnChange on_change; 68 | }; 69 | 70 | //typedef std::unique_ptr> StateListPtr 71 | 72 | void init(OptionsMap&); 73 | void loop(int argc, char* argv[]); 74 | void processCommand(std::string token, std::string cmd, 75 | Position& pos, 76 | std::unique_ptr>& states); 77 | 78 | std::string value(Value v); 79 | std::string square(Square s); 80 | std::string move(Move m, bool chess960); 81 | std::string pv(const Position& pos, Depth depth, Value alpha, Value beta); 82 | std::string wdl(Value v, int ply); 83 | Move to_move(const Position& pos, std::string& str); 84 | 85 | } // namespace UCI 86 | 87 | extern UCI::OptionsMap Options; 88 | 89 | #endif // #ifndef UCI_H_INCLUDED 90 | -------------------------------------------------------------------------------- /src/material.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef MATERIAL_H_INCLUDED 20 | #define MATERIAL_H_INCLUDED 21 | 22 | #include "endgame.h" 23 | #include "misc.h" 24 | #include "position.h" 25 | #include "types.h" 26 | 27 | namespace Material { 28 | 29 | /// Material::Entry contains various information about a material configuration. 30 | /// It contains a material imbalance evaluation, a function pointer to a special 31 | /// endgame evaluation function (which in most cases is NULL, meaning that the 32 | /// standard evaluation function will be used), and scale factors. 33 | /// 34 | /// The scale factors are used to scale the evaluation score up or down. For 35 | /// instance, in KRB vs KR endgames, the score is scaled down by a factor of 4, 36 | /// which will result in scores of absolute value less than one pawn. 37 | 38 | struct Entry { 39 | 40 | Score imbalance() const { return score; } 41 | Phase game_phase() const { return (Phase)gamePhase; } 42 | bool specialized_eval_exists() const { return evaluationFunction != nullptr; } 43 | Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); } 44 | 45 | // scale_factor() takes a position and a color as input and returns a scale factor 46 | // for the given color. We have to provide the position in addition to the color 47 | // because the scale factor may also be a function which should be applied to 48 | // the position. For instance, in KBP vs K endgames, the scaling function looks 49 | // for rook pawns and wrong-colored bishops. 50 | ScaleFactor scale_factor(const Position& pos, Color c) const { 51 | ScaleFactor sf = scalingFunction[c] ? (*scalingFunction[c])(pos) 52 | : SCALE_FACTOR_NONE; 53 | return sf != SCALE_FACTOR_NONE ? sf : ScaleFactor(factor[c]); 54 | } 55 | 56 | Key key; 57 | const EndgameBase* evaluationFunction; 58 | const EndgameBase* scalingFunction[COLOR_NB]; // Could be one for each 59 | // side (e.g. KPKP, KBPsK) 60 | Score score; 61 | int16_t gamePhase; 62 | uint8_t factor[COLOR_NB]; 63 | }; 64 | 65 | typedef HashTable Table; 66 | 67 | Entry* probe(const Position& pos); 68 | 69 | } // namespace Material 70 | 71 | #endif // #ifndef MATERIAL_H_INCLUDED 72 | -------------------------------------------------------------------------------- /src/tt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef TT_H_INCLUDED 20 | #define TT_H_INCLUDED 21 | 22 | #include "misc.h" 23 | #include "types.h" 24 | 25 | /// TTEntry struct is the 10 bytes transposition table entry, defined as below: 26 | /// 27 | /// key 16 bit 28 | /// depth 8 bit 29 | /// generation 5 bit 30 | /// pv node 1 bit 31 | /// bound type 2 bit 32 | /// move 16 bit 33 | /// value 16 bit 34 | /// eval value 16 bit 35 | 36 | struct TTEntry { 37 | 38 | Move move() const { return (Move )move16; } 39 | Value value() const { return (Value)value16; } 40 | Value eval() const { return (Value)eval16; } 41 | Depth depth() const { return (Depth)depth8 + DEPTH_OFFSET; } 42 | bool is_pv() const { return (bool)(genBound8 & 0x4); } 43 | Bound bound() const { return (Bound)(genBound8 & 0x3); } 44 | void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev); 45 | 46 | private: 47 | friend class TranspositionTable; 48 | 49 | uint16_t key16; 50 | uint8_t depth8; 51 | uint8_t genBound8; 52 | uint16_t move16; 53 | int16_t value16; 54 | int16_t eval16; 55 | }; 56 | 57 | 58 | /// A TranspositionTable is an array of Cluster, of size clusterCount. Each 59 | /// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry 60 | /// contains information on exactly one position. The size of a Cluster should 61 | /// divide the size of a cache line for best performance, as the cacheline is 62 | /// prefetched when possible. 63 | 64 | class TranspositionTable { 65 | 66 | static constexpr int ClusterSize = 3; 67 | 68 | struct Cluster { 69 | TTEntry entry[ClusterSize]; 70 | char padding[2]; // Pad to 32 bytes 71 | }; 72 | 73 | static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size"); 74 | 75 | public: 76 | ~TranspositionTable() { aligned_large_pages_free(table); } 77 | void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound 78 | TTEntry* probe(const Key key, bool& found) const; 79 | int hashfull() const; 80 | void resize(size_t mbSize); 81 | void clear(); 82 | 83 | TTEntry* first_entry(const Key key) const { 84 | return &table[mul_hi64(key, clusterCount)].entry[0]; 85 | } 86 | 87 | private: 88 | friend struct TTEntry; 89 | 90 | size_t clusterCount; 91 | Cluster* table; 92 | uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 93 | }; 94 | 95 | extern TranspositionTable TT; 96 | 97 | #endif // #ifndef TT_H_INCLUDED 98 | -------------------------------------------------------------------------------- /src/search.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef SEARCH_H_INCLUDED 20 | #define SEARCH_H_INCLUDED 21 | 22 | #include 23 | 24 | #include "misc.h" 25 | #include "movepick.h" 26 | #include "types.h" 27 | 28 | class Position; 29 | 30 | namespace Search { 31 | 32 | /// Threshold used for countermoves based pruning 33 | constexpr int CounterMovePruneThreshold = 0; 34 | 35 | 36 | /// Stack struct keeps track of the information we need to remember from nodes 37 | /// shallower and deeper in the tree during the search. Each search thread has 38 | /// its own array of Stack objects, indexed by the current ply. 39 | 40 | struct Stack { 41 | Move* pv; 42 | PieceToHistory* continuationHistory; 43 | int ply; 44 | Move currentMove; 45 | Move excludedMove; 46 | Move killers[2]; 47 | Value staticEval; 48 | int statScore; 49 | int moveCount; 50 | bool inCheck; 51 | bool ttPv; 52 | bool ttHit; 53 | }; 54 | 55 | 56 | /// RootMove struct is used for moves at the root of the tree. For each root move 57 | /// we store a score and a PV (really a refutation in the case of moves which 58 | /// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves. 59 | 60 | struct RootMove { 61 | 62 | explicit RootMove(Move m) : pv(1, m) {} 63 | bool extract_ponder_from_tt(Position& pos); 64 | bool operator==(const Move& m) const { return pv[0] == m; } 65 | bool operator<(const RootMove& m) const { // Sort in descending order 66 | return m.score != score ? m.score < score 67 | : m.previousScore < previousScore; 68 | } 69 | 70 | Value score = -VALUE_INFINITE; 71 | Value previousScore = -VALUE_INFINITE; 72 | int selDepth = 0; 73 | int tbRank = 0; 74 | Value tbScore; 75 | std::vector pv; 76 | }; 77 | 78 | typedef std::vector RootMoves; 79 | 80 | 81 | /// LimitsType struct stores information sent by GUI about available time to 82 | /// search the current move, maximum depth/time, or if we are in analysis mode. 83 | 84 | struct LimitsType { 85 | 86 | LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC 87 | time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0); 88 | movestogo = depth = mate = perft = infinite = 0; 89 | nodes = 0; 90 | } 91 | 92 | bool use_time_management() const { 93 | return time[WHITE] || time[BLACK]; 94 | } 95 | 96 | std::vector searchmoves; 97 | TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime; 98 | int movestogo, depth, mate, perft, infinite; 99 | int64_t nodes; 100 | }; 101 | 102 | extern LimitsType Limits; 103 | 104 | void init(); 105 | void clear(); 106 | 107 | } // namespace Search 108 | 109 | #endif // #ifndef SEARCH_H_INCLUDED 110 | -------------------------------------------------------------------------------- /src/timeman.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "search.h" 24 | #include "timeman.h" 25 | #include "uci.h" 26 | 27 | TimeManagement Time; // Our global time management object 28 | 29 | 30 | /// TimeManagement::init() is called at the beginning of the search and calculates 31 | /// the bounds of time allowed for the current game ply. We currently support: 32 | // 1) x basetime (+ z increment) 33 | // 2) x moves in y seconds (+ z increment) 34 | 35 | void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { 36 | 37 | TimePoint moveOverhead = TimePoint(Options["Move Overhead"]); 38 | TimePoint slowMover = TimePoint(Options["Slow Mover"]); 39 | TimePoint npmsec = TimePoint(Options["nodestime"]); 40 | 41 | // optScale is a percentage of available time to use for the current move. 42 | // maxScale is a multiplier applied to optimumTime. 43 | double optScale, maxScale; 44 | 45 | // If we have to play in 'nodes as time' mode, then convert from time 46 | // to nodes, and use resulting values in time management formulas. 47 | // WARNING: to avoid time losses, the given npmsec (nodes per millisecond) 48 | // must be much lower than the real engine speed. 49 | if (npmsec) 50 | { 51 | if (!availableNodes) // Only once at game start 52 | availableNodes = npmsec * limits.time[us]; // Time is in msec 53 | 54 | // Convert from milliseconds to nodes 55 | limits.time[us] = TimePoint(availableNodes); 56 | limits.inc[us] *= npmsec; 57 | limits.npmsec = npmsec; 58 | } 59 | 60 | startTime = limits.startTime; 61 | 62 | // Maximum move horizon of 50 moves 63 | int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; 64 | 65 | // Make sure timeLeft is > 0 since we may use it as a divisor 66 | TimePoint timeLeft = std::max(TimePoint(1), 67 | limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); 68 | 69 | // A user may scale time usage by setting UCI option "Slow Mover" 70 | // Default is 100 and changing this value will probably lose elo. 71 | timeLeft = slowMover * timeLeft / 100; 72 | 73 | // x basetime (+ z increment) 74 | // If there is a healthy increment, timeLeft can exceed actual available 75 | // game time for the current move, so also cap to 20% of available game time. 76 | if (limits.movestogo == 0) 77 | { 78 | optScale = std::min(0.0084 + std::pow(ply + 3.0, 0.5) * 0.0042, 79 | 0.2 * limits.time[us] / double(timeLeft)); 80 | maxScale = std::min(7.0, 4.0 + ply / 12.0); 81 | } 82 | 83 | // x moves in y seconds (+ z increment) 84 | else 85 | { 86 | optScale = std::min((0.8 + ply / 128.0) / mtg, 87 | 0.8 * limits.time[us] / double(timeLeft)); 88 | maxScale = std::min(6.3, 1.5 + 0.11 * mtg); 89 | } 90 | 91 | // Never use more than 80% of the available time for this move 92 | optimumTime = TimePoint(optScale * timeLeft); 93 | maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)); 94 | 95 | if (Options["Ponder"]) 96 | optimumTime += optimumTime / 4; 97 | } 98 | -------------------------------------------------------------------------------- /src/endgame.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef ENDGAME_H_INCLUDED 20 | #define ENDGAME_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "position.h" 29 | #include "types.h" 30 | 31 | 32 | /// EndgameCode lists all supported endgame functions by corresponding codes 33 | 34 | enum EndgameCode { 35 | 36 | EVALUATION_FUNCTIONS, 37 | KNNK, // KNN vs K 38 | KNNKP, // KNN vs KP 39 | KXK, // Generic "mate lone king" eval 40 | KBNK, // KBN vs K 41 | KPK, // KP vs K 42 | KRKP, // KR vs KP 43 | KRKB, // KR vs KB 44 | KRKN, // KR vs KN 45 | KQKP, // KQ vs KP 46 | KQKR, // KQ vs KR 47 | 48 | SCALING_FUNCTIONS, 49 | KBPsK, // KB and pawns vs K 50 | KQKRPs, // KQ vs KR and pawns 51 | KRPKR, // KRP vs KR 52 | KRPKB, // KRP vs KB 53 | KRPPKRP, // KRPP vs KRP 54 | KPsK, // K and pawns vs K 55 | KBPKB, // KBP vs KB 56 | KBPPKB, // KBPP vs KB 57 | KBPKN, // KBP vs KN 58 | KPKP // KP vs KP 59 | }; 60 | 61 | 62 | /// Endgame functions can be of two types depending on whether they return a 63 | /// Value or a ScaleFactor. 64 | 65 | template using 66 | eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type; 67 | 68 | 69 | /// Base and derived functors for endgame evaluation and scaling functions 70 | 71 | template 72 | struct EndgameBase { 73 | 74 | explicit EndgameBase(Color c) : strongSide(c), weakSide(~c) {} 75 | virtual ~EndgameBase() = default; 76 | virtual T operator()(const Position&) const = 0; 77 | 78 | const Color strongSide, weakSide; 79 | }; 80 | 81 | 82 | template> 83 | struct Endgame : public EndgameBase { 84 | 85 | explicit Endgame(Color c) : EndgameBase(c) {} 86 | T operator()(const Position&) const override; 87 | }; 88 | 89 | 90 | /// The Endgames namespace handles the pointers to endgame evaluation and scaling 91 | /// base objects in two std::map. We use polymorphism to invoke the actual 92 | /// endgame function by calling its virtual operator(). 93 | 94 | namespace Endgames { 95 | 96 | template using Ptr = std::unique_ptr>; 97 | template using Map = std::unordered_map>; 98 | 99 | extern std::pair, Map> maps; 100 | 101 | void init(); 102 | 103 | template 104 | Map& map() { 105 | return std::get::value>(maps); 106 | } 107 | 108 | template> 109 | void add(const std::string& code) { 110 | 111 | StateInfo st; 112 | map()[Position().set(code, WHITE, &st).material_key()] = Ptr(new Endgame(WHITE)); 113 | map()[Position().set(code, BLACK, &st).material_key()] = Ptr(new Endgame(BLACK)); 114 | } 115 | 116 | template 117 | const EndgameBase* probe(Key key) { 118 | auto it = map().find(key); 119 | return it != map().end() ? it->second.get() : nullptr; 120 | } 121 | } 122 | 123 | #endif // #ifndef ENDGAME_H_INCLUDED 124 | -------------------------------------------------------------------------------- /src/thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef THREAD_H_INCLUDED 20 | #define THREAD_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "material.h" 29 | #include "movepick.h" 30 | #include "pawns.h" 31 | #include "position.h" 32 | #include "search.h" 33 | #include "thread_win32_osx.h" 34 | 35 | 36 | /// Thread class keeps together all the thread-related stuff. We use 37 | /// per-thread pawn and material hash tables so that once we get a 38 | /// pointer to an entry its life time is unlimited and we don't have 39 | /// to care about someone changing the entry under our feet. 40 | 41 | class Thread { 42 | 43 | std::mutex mutex; 44 | std::condition_variable cv; 45 | size_t idx; 46 | bool exit = false, searching = true; // Set before starting std::thread 47 | NativeThread stdThread; 48 | 49 | public: 50 | explicit Thread(size_t); 51 | virtual ~Thread(); 52 | virtual void search(); 53 | void clear(); 54 | void idle_loop(); 55 | void start_searching(); 56 | void wait_for_search_finished(); 57 | 58 | Pawns::Table pawnsTable; 59 | Material::Table materialTable; 60 | size_t pvIdx, pvLast; 61 | uint64_t ttHitAverage; 62 | int selDepth, nmpMinPly; 63 | Color nmpColor; 64 | std::atomic nodes, tbHits, bestMoveChanges; 65 | 66 | Position rootPos; 67 | StateInfo rootState; 68 | Search::RootMoves rootMoves; 69 | Depth rootDepth, completedDepth; 70 | CounterMoveHistory counterMoves; 71 | ButterflyHistory mainHistory; 72 | LowPlyHistory lowPlyHistory; 73 | CapturePieceToHistory captureHistory; 74 | ContinuationHistory continuationHistory[2][2]; 75 | Score contempt; 76 | int failedHighCnt; 77 | }; 78 | 79 | 80 | /// MainThread is a derived class specific for main thread 81 | 82 | struct MainThread : public Thread { 83 | 84 | using Thread::Thread; 85 | 86 | void search() override; 87 | void check_time(); 88 | 89 | double previousTimeReduction; 90 | Value bestPreviousScore; 91 | Value iterValue[4]; 92 | int callsCnt; 93 | bool stopOnPonderhit; 94 | std::atomic_bool ponder; 95 | }; 96 | 97 | 98 | /// ThreadPool struct handles all the threads-related stuff like init, starting, 99 | /// parking and, most importantly, launching a thread. All the access to threads 100 | /// is done through this class. 101 | 102 | struct ThreadPool : public std::vector { 103 | 104 | void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false); 105 | void clear(); 106 | void set(size_t); 107 | 108 | MainThread* main() const { return static_cast(front()); } 109 | uint64_t nodes_searched() const { return accumulate(&Thread::nodes); } 110 | uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } 111 | Thread* get_best_thread() const; 112 | void start_searching(); 113 | void wait_for_search_finished() const; 114 | 115 | std::atomic_bool stop, increaseDepth; 116 | 117 | private: 118 | StateListPtr setupStates; 119 | 120 | uint64_t accumulate(std::atomic Thread::* member) const { 121 | 122 | uint64_t sum = 0; 123 | for (Thread* th : *this) 124 | sum += (th->*member).load(std::memory_order_relaxed); 125 | return sum; 126 | } 127 | }; 128 | 129 | extern ThreadPool Threads; 130 | 131 | #endif // #ifndef THREAD_H_INCLUDED 132 | -------------------------------------------------------------------------------- /src/nnue/nnue_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // Constants used in NNUE evaluation function 20 | 21 | #ifndef NNUE_COMMON_H_INCLUDED 22 | #define NNUE_COMMON_H_INCLUDED 23 | 24 | #include 25 | #include 26 | 27 | #if defined(USE_AVX2) 28 | #include 29 | 30 | #elif defined(USE_SSE41) 31 | #include 32 | 33 | #elif defined(USE_SSSE3) 34 | #include 35 | 36 | #elif defined(USE_SSE2) 37 | #include 38 | 39 | #elif defined(USE_MMX) 40 | #include 41 | 42 | #elif defined(USE_NEON) 43 | #include 44 | #endif 45 | 46 | namespace Eval::NNUE { 47 | 48 | // Version of the evaluation file 49 | constexpr std::uint32_t kVersion = 0x7AF32F16u; 50 | 51 | // Constant used in evaluation value calculation 52 | constexpr int FV_SCALE = 16; 53 | constexpr int kWeightScaleBits = 6; 54 | 55 | // Size of cache line (in bytes) 56 | constexpr std::size_t kCacheLineSize = 64; 57 | 58 | // SIMD width (in bytes) 59 | #if defined(USE_AVX2) 60 | constexpr std::size_t kSimdWidth = 32; 61 | 62 | #elif defined(USE_SSE2) 63 | constexpr std::size_t kSimdWidth = 16; 64 | 65 | #elif defined(USE_MMX) 66 | constexpr std::size_t kSimdWidth = 8; 67 | 68 | #elif defined(USE_NEON) 69 | constexpr std::size_t kSimdWidth = 16; 70 | #endif 71 | 72 | constexpr std::size_t kMaxSimdWidth = 32; 73 | 74 | // unique number for each piece type on each square 75 | enum { 76 | PS_NONE = 0, 77 | PS_W_PAWN = 1, 78 | PS_B_PAWN = 1 * SQUARE_NB + 1, 79 | PS_W_KNIGHT = 2 * SQUARE_NB + 1, 80 | PS_B_KNIGHT = 3 * SQUARE_NB + 1, 81 | PS_W_BISHOP = 4 * SQUARE_NB + 1, 82 | PS_B_BISHOP = 5 * SQUARE_NB + 1, 83 | PS_W_ROOK = 6 * SQUARE_NB + 1, 84 | PS_B_ROOK = 7 * SQUARE_NB + 1, 85 | PS_W_QUEEN = 8 * SQUARE_NB + 1, 86 | PS_B_QUEEN = 9 * SQUARE_NB + 1, 87 | PS_W_KING = 10 * SQUARE_NB + 1, 88 | PS_END = PS_W_KING, // pieces without kings (pawns included) 89 | PS_B_KING = 11 * SQUARE_NB + 1, 90 | PS_END2 = 12 * SQUARE_NB + 1 91 | }; 92 | 93 | constexpr uint32_t kpp_board_index[COLOR_NB][PIECE_NB] = { 94 | // convention: W - us, B - them 95 | // viewed from other side, W and B are reversed 96 | { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_KING, PS_NONE, 97 | PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_KING, PS_NONE }, 98 | { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_KING, PS_NONE, 99 | PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_KING, PS_NONE } 100 | }; 101 | 102 | // Type of input feature after conversion 103 | using TransformedFeatureType = std::uint8_t; 104 | using IndexType = std::uint32_t; 105 | 106 | // Round n up to be a multiple of base 107 | template 108 | constexpr IntType CeilToMultiple(IntType n, IntType base) { 109 | return (n + base - 1) / base * base; 110 | } 111 | 112 | // read_little_endian() is our utility to read an integer (signed or unsigned, any size) 113 | // from a stream in little-endian order. We swap the byte order after the read if 114 | // necessary to return a result with the byte ordering of the compiling machine. 115 | template 116 | inline IntType read_little_endian(std::istream& stream) { 117 | 118 | IntType result; 119 | std::uint8_t u[sizeof(IntType)]; 120 | typename std::make_unsigned::type v = 0; 121 | 122 | stream.read(reinterpret_cast(u), sizeof(IntType)); 123 | for (std::size_t i = 0; i < sizeof(IntType); ++i) 124 | v = (v << 8) | u[sizeof(IntType) - i - 1]; 125 | 126 | std::memcpy(&result, &v, sizeof(IntType)); 127 | return result; 128 | } 129 | 130 | } // namespace Eval::NNUE 131 | 132 | #endif // #ifndef NNUE_COMMON_H_INCLUDED 133 | -------------------------------------------------------------------------------- /src/tune.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "types.h" 24 | #include "misc.h" 25 | #include "uci.h" 26 | 27 | using std::string; 28 | 29 | bool Tune::update_on_last; 30 | const UCI::Option* LastOption = nullptr; 31 | BoolConditions Conditions; 32 | static std::map TuneResults; 33 | 34 | string Tune::next(string& names, bool pop) { 35 | 36 | string name; 37 | 38 | do { 39 | string token = names.substr(0, names.find(',')); 40 | 41 | if (pop) 42 | names.erase(0, token.size() + 1); 43 | 44 | std::stringstream ws(token); 45 | name += (ws >> token, token); // Remove trailing whitespace 46 | 47 | } while ( std::count(name.begin(), name.end(), '(') 48 | - std::count(name.begin(), name.end(), ')')); 49 | 50 | return name; 51 | } 52 | 53 | static void on_tune(const UCI::Option& o) { 54 | 55 | if (!Tune::update_on_last || LastOption == &o) 56 | Tune::read_options(); 57 | } 58 | 59 | static void make_option(const string& n, int v, const SetRange& r) { 60 | 61 | // Do not generate option when there is nothing to tune (ie. min = max) 62 | if (r(v).first == r(v).second) 63 | return; 64 | 65 | if (TuneResults.count(n)) 66 | v = TuneResults[n]; 67 | 68 | Options[n] << UCI::Option(v, r(v).first, r(v).second, on_tune); 69 | LastOption = &Options[n]; 70 | 71 | // Print formatted parameters, ready to be copy-pasted in Fishtest 72 | std::cout << n << "," 73 | << v << "," 74 | << r(v).first << "," << r(v).second << "," 75 | << (r(v).second - r(v).first) / 20.0 << "," 76 | << "0.0020" 77 | << std::endl; 78 | } 79 | 80 | template<> void Tune::Entry::init_option() { make_option(name, value, range); } 81 | 82 | template<> void Tune::Entry::read_option() { 83 | if (Options.count(name)) 84 | value = int(Options[name]); 85 | } 86 | 87 | template<> void Tune::Entry::init_option() { make_option(name, value, range); } 88 | 89 | template<> void Tune::Entry::read_option() { 90 | if (Options.count(name)) 91 | value = Value(int(Options[name])); 92 | } 93 | 94 | template<> void Tune::Entry::init_option() { 95 | make_option("m" + name, mg_value(value), range); 96 | make_option("e" + name, eg_value(value), range); 97 | } 98 | 99 | template<> void Tune::Entry::read_option() { 100 | if (Options.count("m" + name)) 101 | value = make_score(int(Options["m" + name]), eg_value(value)); 102 | 103 | if (Options.count("e" + name)) 104 | value = make_score(mg_value(value), int(Options["e" + name])); 105 | } 106 | 107 | // Instead of a variable here we have a PostUpdate function: just call it 108 | template<> void Tune::Entry::init_option() {} 109 | template<> void Tune::Entry::read_option() { value(); } 110 | 111 | 112 | // Set binary conditions according to a probability that depends 113 | // on the corresponding parameter value. 114 | 115 | void BoolConditions::set() { 116 | 117 | static PRNG rng(now()); 118 | static bool startup = true; // To workaround fishtest bench 119 | 120 | for (size_t i = 0; i < binary.size(); i++) 121 | binary[i] = !startup && (values[i] + int(rng.rand() % variance) > threshold); 122 | 123 | startup = false; 124 | 125 | for (size_t i = 0; i < binary.size(); i++) 126 | sync_cout << binary[i] << sync_endl; 127 | } 128 | 129 | 130 | // Init options with tuning session results instead of default values. Useful to 131 | // get correct bench signature after a tuning session or to test tuned values. 132 | // Just copy fishtest tuning results in a result.txt file and extract the 133 | // values with: 134 | // 135 | // cat results.txt | sed 's/^param: \([^,]*\), best: \([^,]*\).*/ TuneResults["\1"] = int(round(\2));/' 136 | // 137 | // Then paste the output below, as the function body 138 | 139 | #include 140 | 141 | void Tune::read_results() { 142 | 143 | /* ...insert your values here... */ 144 | } 145 | -------------------------------------------------------------------------------- /src/FishWrap.mm: -------------------------------------------------------------------------------- 1 | // 2 | // FishWrap.m 3 | // stockfish 4 | // 5 | // Created by Douglas Pedley on 12/28/20. 6 | // 7 | 8 | #import 9 | #import "FishWrap.h" 10 | #include 11 | #include "bitboard.h" 12 | #include "endgame.h" 13 | #include "position.h" 14 | #include "search.h" 15 | #include "thread.h" 16 | #include "tt.h" 17 | #include "uci.h" 18 | #include "syzygy/tbprobe.h" 19 | #include 20 | #include "StockfishObjC.h" 21 | 22 | namespace PSQT { 23 | void init(); 24 | } 25 | const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; 26 | std::stringstream fishOut; 27 | 28 | @interface FishLoop : NSOperation 29 | @property (nonatomic, strong) NSMutableArray *commands; 30 | @property (nonatomic, weak) NSObject *communicationDelegate; 31 | @end 32 | 33 | @implementation FishLoop 34 | 35 | - (instancetype)initWithTalksTo:(NSObject *)talksTo { 36 | self = [super init]; 37 | if (self) { 38 | self.commands = [@[] mutableCopy]; 39 | if ([talksTo conformsToProtocol:@protocol(TalksToFish)]) { 40 | self.communicationDelegate = (NSObject *)talksTo; 41 | } 42 | } 43 | return self; 44 | } 45 | 46 | - (void)appendCommand:(NSString *)command { 47 | @synchronized (self.commands) { 48 | [self.commands addObject:command]; 49 | } 50 | } 51 | 52 | - (BOOL)isBestMove:(NSString *)message { 53 | if (message==nil || message.length == 0) { 54 | return NO; 55 | } 56 | if (message.length > 9 && [[message substringToIndex:9] isEqualToString:@"bestmove "]) { 57 | return YES; 58 | } 59 | return NO; 60 | } 61 | 62 | - (BOOL)isInfo:(NSString *)message { 63 | if (message==nil || message.length == 0) { 64 | return NO; 65 | } 66 | if (message.length > 5 && [[message substringToIndex:5] isEqualToString:@"info "]) { 67 | return YES; 68 | } 69 | return NO; 70 | } 71 | 72 | - (void)startAquaman { 73 | [NSThread detachNewThreadWithBlock:^{ 74 | do { 75 | std::string glub = fishOut.str(); 76 | fishOut.clear(); 77 | fishOut.str(std::string()); 78 | if (glub.size() > 0) { 79 | NSString *messageLines = [NSString stringWithUTF8String:glub.c_str()]; 80 | NSArray *messages = [messageLines componentsSeparatedByString:@"\n"]; 81 | for (int i=0; i0) { 89 | [self.communicationDelegate stockfishError:message]; 90 | } 91 | } 92 | } 93 | [NSThread sleepForTimeInterval:0.05]; 94 | } while (!self.cancelled); 95 | }]; 96 | } 97 | 98 | - (void)main { 99 | Position pos; 100 | std::string token; 101 | StateListPtr states(new std::deque(1)); 102 | Options["Use NNUE"] = false; 103 | // Initialize Stockfish classes 104 | UCI::init(Options); 105 | Tune::init(); 106 | PSQT::init(); 107 | Bitboards::init(); 108 | Position::init(); 109 | Bitbases::init(); 110 | Endgames::init(); 111 | Threads.set(size_t(Options["Threads"])); 112 | Search::clear(); // After threads are up 113 | Eval::NNUE::init(); 114 | pos.set(StartFEN, false, &states->back(), Threads.main()); 115 | [self startAquaman]; 116 | do { 117 | NSString *command = [self.commands firstObject]; 118 | if (command != nil) { 119 | @synchronized (self.commands) { 120 | [self.commands removeObjectAtIndex:0]; 121 | } 122 | // Call UCI 123 | std::string cmd = std::string([command UTF8String]); 124 | UCI::processCommand(token, cmd, pos, states); 125 | } else { 126 | [NSThread sleepForTimeInterval:0.05]; 127 | } 128 | } while (!self.cancelled); 129 | } 130 | 131 | @end 132 | 133 | @interface FishWrap() 134 | @property (nonatomic, strong) FishLoop *fishLoop; 135 | @end 136 | 137 | @implementation FishWrap 138 | - (NSString *)sendUCICommand:(NSString *)uci { 139 | [self.fishLoop appendCommand:uci]; 140 | return @""; 141 | } 142 | 143 | -(instancetype)initWithTalksTo:(NSObject *)talksTo { 144 | self = [super init]; 145 | if (self) { 146 | self.fishLoop = [[FishLoop alloc] initWithTalksTo:talksTo]; 147 | [NSThread detachNewThreadWithBlock:^{ 148 | [self.fishLoop start]; 149 | }]; 150 | } 151 | return self; 152 | } 153 | @end 154 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # List of authors for Stockfish, as of August 4, 2020 2 | 3 | # Founders of the Stockfish project and fishtest infrastructure 4 | Tord Romstad (romstad) 5 | Marco Costalba (mcostalba) 6 | Joona Kiiski (zamar) 7 | Gary Linscott (glinscott) 8 | 9 | # Authors and inventors of NNUE, training, NNUE port 10 | Yu Nasu (ynasu87) 11 | Motohiro Isozaki (yaneurao) 12 | Hisayori Noda (nodchip) 13 | 14 | # all other authors of the code in alphabetical order 15 | Aditya (absimaldata) 16 | Adrian Petrescu (apetresc) 17 | Ajith Chandy Jose (ajithcj) 18 | Alain Savard (Rocky640) 19 | Alayan Feh (Alayan-stk-2) 20 | Alexander Kure 21 | Alexander Pagel (Lolligerhans) 22 | Alfredo Menezes (lonfom169) 23 | Ali AlZhrani (Cooffe) 24 | Andrew Grant (AndyGrant) 25 | Andrey Neporada (nepal) 26 | Andy Duplain 27 | Aram Tumanian (atumanian) 28 | Arjun Temurnikar 29 | Auguste Pop 30 | Balint Pfliegel 31 | Ben Koshy (BKSpurgeon) 32 | Bill Henry (VoyagerOne) 33 | Bojun Guo (noobpwnftw, Nooby) 34 | braich 35 | Brian Sheppard (SapphireBrand, briansheppard-toast) 36 | Bryan Cross (crossbr) 37 | candirufish 38 | Chess13234 39 | Chris Cain (ceebo) 40 | Dale Weiler (graphitemaster) 41 | Dan Schmidt (dfannius) 42 | Daniel Axtens (daxtens) 43 | Daniel Dugovic (ddugovic) 44 | Dariusz Orzechowski (dorzechowski) 45 | David Zar 46 | Daylen Yang (daylen) 47 | Deshawn Mohan-Smith (GoldenRare) 48 | DiscanX 49 | Dominik Schlösser (domschl) 50 | double-beep 51 | Eduardo Cáceres (eduherminio) 52 | Eelco de Groot (KingDefender) 53 | Elvin Liu (solarlight2) 54 | erbsenzaehler 55 | Ernesto Gatti 56 | Linmiao Xu (linrock) 57 | Fabian Beuke (madnight) 58 | Fabian Fichter (ianfab) 59 | Fanael Linithien (Fanael) 60 | fanon 61 | Fauzi Akram Dabat (FauziAkram) 62 | Felix Wittmann 63 | gamander 64 | Gary Heckman (gheckman) 65 | George Sobala (gsobala) 66 | gguliash 67 | Gian-Carlo Pascutto (gcp) 68 | Gontran Lemaire (gonlem) 69 | Goodkov Vasiliy Aleksandrovich (goodkov) 70 | Gregor Cramer 71 | GuardianRM 72 | Günther Demetz (pb00067, pb00068) 73 | Guy Vreuls (gvreuls) 74 | Henri Wiechers 75 | Hiraoka Takuya (HiraokaTakuya) 76 | homoSapiensSapiens 77 | Hongzhi Cheng 78 | Ivan Ivec (IIvec) 79 | Jacques B. (Timshel) 80 | Jan Ondruš (hxim) 81 | Jared Kish (Kurtbusch) 82 | Jarrod Torriero (DU-jdto) 83 | Jean Gauthier (OuaisBla) 84 | Jean-Francois Romang (jromang) 85 | Jekaa 86 | Jerry Donald Watson (jerrydonaldwatson) 87 | jjoshua2 88 | Jonathan Calovski (Mysseno) 89 | Jonathan Buladas Dumale (SFisGOD) 90 | Joost VandeVondele (vondele) 91 | Jörg Oster (joergoster) 92 | Joseph Ellis (jhellis3) 93 | Joseph R. Prostko 94 | jundery 95 | Justin Blanchard (UncombedCoconut) 96 | Kelly Wilson 97 | Ken Takusagawa 98 | kinderchocolate 99 | Kiran Panditrao (Krgp) 100 | Kojirion 101 | Leonardo Ljubičić (ICCF World Champion) 102 | Leonid Pechenik (lp--) 103 | Linus Arver (listx) 104 | loco-loco 105 | Lub van den Berg (ElbertoOne) 106 | Luca Brivio (lucabrivio) 107 | Lucas Braesch (lucasart) 108 | Lyudmil Antonov (lantonov) 109 | Maciej Żenczykowski (zenczykowski) 110 | Malcolm Campbell (xoto10) 111 | Mark Tenzer (31m059) 112 | marotear 113 | Matthew Lai (matthewlai) 114 | Matthew Sullivan (Matt14916) 115 | Maxim Molchanov (Maxim) 116 | Michael An (man) 117 | Michael Byrne (MichaelB7) 118 | Michael Chaly (Vizvezdenec) 119 | Michael Stembera (mstembera) 120 | Michael Whiteley (protonspring) 121 | Michel Van den Bergh (vdbergh) 122 | Miguel Lahoz (miguel-l) 123 | Mikael Bäckman (mbootsector) 124 | Mira 125 | Miroslav Fontán (Hexik) 126 | Moez Jellouli (MJZ1977) 127 | Mohammed Li (tthsqe12) 128 | Nathan Rugg (nmrugg) 129 | Nick Pelling (nickpelling) 130 | Nicklas Persson (NicklasPersson) 131 | Niklas Fiekas (niklasf) 132 | Nikolay Kostov (NikolayIT) 133 | Nguyen Pham (nguyenpham) 134 | Norman Schmidt (FireFather) 135 | notruck 136 | Ondrej Mosnáček (WOnder93) 137 | Oskar Werkelin Ahlin 138 | Pablo Vazquez 139 | Panthee 140 | Pascal Romaret 141 | Pasquale Pigazzini (ppigazzini) 142 | Patrick Jansen (mibere) 143 | pellanda 144 | Peter Zsifkovits (CoffeeOne) 145 | Praveen Kumar Tummala (praveentml) 146 | Rahul Dsilva (silversolver1) 147 | Ralph Stößer (Ralph Stoesser) 148 | Raminder Singh 149 | renouve 150 | Reuven Peleg 151 | Richard Lloyd 152 | Rodrigo Exterckötter Tjäder 153 | Ron Britvich (Britvich) 154 | Ronald de Man (syzygy1, syzygy) 155 | rqs 156 | Ryan Schmitt 157 | Ryan Takker 158 | Sami Kiminki (skiminki) 159 | Sebastian Buchwald (UniQP) 160 | Sergei Antonov (saproj) 161 | Sergei Ivanov (svivanov72) 162 | Sergio Vieri (sergiovieri) 163 | sf-x 164 | Shane Booth (shane31) 165 | Shawn Varghese (xXH4CKST3RXx) 166 | Stefan Geschwentner (locutus2) 167 | Stefano Cardanobile (Stefano80) 168 | Steinar Gunderson (sesse) 169 | Stéphane Nicolet (snicolet) 170 | Thanar2 171 | thaspel 172 | theo77186 173 | Tom Truscott 174 | Tom Vijlbrief (tomtor) 175 | Tomasz Sobczyk (Sopel97) 176 | Torsten Franz (torfranz, tfranzer) 177 | Tracey Emery (basepr1me) 178 | tttak 179 | Unai Corzo (unaiic) 180 | Uri Blass (uriblass) 181 | Vince Negri (cuddlestmonkey) 182 | zz4032 183 | 184 | 185 | # Additionally, we acknowledge the authors and maintainers of fishtest, 186 | # an amazing and essential framework for the development of Stockfish! 187 | # 188 | # https://github.com/glinscott/fishtest/blob/master/AUTHORS 189 | -------------------------------------------------------------------------------- /src/nnue/evaluate_nnue.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // Code for calculating NNUE evaluation function 20 | 21 | #include 22 | #include 23 | 24 | #include "../evaluate.h" 25 | #include "../position.h" 26 | #include "../misc.h" 27 | #include "../uci.h" 28 | #include "../types.h" 29 | 30 | #include "evaluate_nnue.h" 31 | 32 | namespace Eval::NNUE { 33 | 34 | // Input feature converter 35 | LargePagePtr feature_transformer; 36 | 37 | // Evaluation function 38 | AlignedPtr network; 39 | 40 | // Evaluation function file name 41 | std::string fileName; 42 | 43 | namespace Detail { 44 | 45 | // Initialize the evaluation function parameters 46 | template 47 | void Initialize(AlignedPtr& pointer) { 48 | 49 | pointer.reset(reinterpret_cast(std_aligned_alloc(alignof(T), sizeof(T)))); 50 | std::memset(pointer.get(), 0, sizeof(T)); 51 | } 52 | 53 | template 54 | void Initialize(LargePagePtr& pointer) { 55 | 56 | static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); 57 | pointer.reset(reinterpret_cast(aligned_large_pages_alloc(sizeof(T)))); 58 | std::memset(pointer.get(), 0, sizeof(T)); 59 | } 60 | 61 | // Read evaluation function parameters 62 | template 63 | bool ReadParameters(std::istream& stream, T& reference) { 64 | 65 | std::uint32_t header; 66 | header = read_little_endian(stream); 67 | if (!stream || header != T::GetHashValue()) return false; 68 | return reference.ReadParameters(stream); 69 | } 70 | 71 | } // namespace Detail 72 | 73 | // Initialize the evaluation function parameters 74 | void Initialize() { 75 | 76 | Detail::Initialize(feature_transformer); 77 | Detail::Initialize(network); 78 | } 79 | 80 | // Read network header 81 | bool ReadHeader(std::istream& stream, std::uint32_t* hash_value, std::string* architecture) 82 | { 83 | std::uint32_t version, size; 84 | 85 | version = read_little_endian(stream); 86 | *hash_value = read_little_endian(stream); 87 | size = read_little_endian(stream); 88 | if (!stream || version != kVersion) return false; 89 | architecture->resize(size); 90 | stream.read(&(*architecture)[0], size); 91 | return !stream.fail(); 92 | } 93 | 94 | // Read network parameters 95 | bool ReadParameters(std::istream& stream) { 96 | 97 | std::uint32_t hash_value; 98 | std::string architecture; 99 | if (!ReadHeader(stream, &hash_value, &architecture)) return false; 100 | if (hash_value != kHashValue) return false; 101 | if (!Detail::ReadParameters(stream, *feature_transformer)) return false; 102 | if (!Detail::ReadParameters(stream, *network)) return false; 103 | return stream && stream.peek() == std::ios::traits_type::eof(); 104 | } 105 | 106 | // Evaluation function. Perform differential calculation. 107 | Value evaluate(const Position& pos) { 108 | 109 | // We manually align the arrays on the stack because with gcc < 9.3 110 | // overaligning stack variables with alignas() doesn't work correctly. 111 | 112 | constexpr uint64_t alignment = kCacheLineSize; 113 | 114 | #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) 115 | TransformedFeatureType transformed_features_unaligned[ 116 | FeatureTransformer::kBufferSize + alignment / sizeof(TransformedFeatureType)]; 117 | char buffer_unaligned[Network::kBufferSize + alignment]; 118 | 119 | auto* transformed_features = align_ptr_up(&transformed_features_unaligned[0]); 120 | auto* buffer = align_ptr_up(&buffer_unaligned[0]); 121 | #else 122 | alignas(alignment) 123 | TransformedFeatureType transformed_features[FeatureTransformer::kBufferSize]; 124 | alignas(alignment) char buffer[Network::kBufferSize]; 125 | #endif 126 | 127 | ASSERT_ALIGNED(transformed_features, alignment); 128 | ASSERT_ALIGNED(buffer, alignment); 129 | 130 | feature_transformer->Transform(pos, transformed_features); 131 | const auto output = network->Propagate(transformed_features, buffer); 132 | 133 | return static_cast(output[0] / FV_SCALE); 134 | } 135 | 136 | // Load eval, from a file stream or a memory stream 137 | bool load_eval(std::string name, std::istream& stream) { 138 | 139 | Initialize(); 140 | fileName = name; 141 | return ReadParameters(stream); 142 | } 143 | 144 | } // namespace Eval::NNUE 145 | -------------------------------------------------------------------------------- /src/misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef MISC_H_INCLUDED 20 | #define MISC_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "types.h" 30 | 31 | const std::string engine_info(bool to_uci = false); 32 | const std::string compiler_info(); 33 | void prefetch(void* addr); 34 | void start_logger(const std::string& fname); 35 | void* std_aligned_alloc(size_t alignment, size_t size); 36 | void std_aligned_free(void* ptr); 37 | void* aligned_large_pages_alloc(size_t size); // memory aligned by page size, min alignment: 4096 bytes 38 | void aligned_large_pages_free(void* mem); // nop if mem == nullptr 39 | 40 | void dbg_hit_on(bool b); 41 | void dbg_hit_on(bool c, bool b); 42 | void dbg_mean_of(int v); 43 | void dbg_print(); 44 | 45 | typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds 46 | static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits"); 47 | inline TimePoint now() { 48 | return std::chrono::duration_cast 49 | (std::chrono::steady_clock::now().time_since_epoch()).count(); 50 | } 51 | 52 | template 53 | struct HashTable { 54 | Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; } 55 | 56 | private: 57 | std::vector table = std::vector(Size); // Allocate on the heap 58 | }; 59 | 60 | 61 | enum SyncCout { IO_LOCK, IO_UNLOCK }; 62 | std::ostream& operator<<(std::ostream&, SyncCout); 63 | 64 | extern std::stringstream fishOut; 65 | #define sync_cout fishOut 66 | #define sync_endl std::endl 67 | 68 | // `ptr` must point to an array of size at least 69 | // `sizeof(T) * N + alignment` bytes, where `N` is the 70 | // number of elements in the array. 71 | template 72 | T* align_ptr_up(T* ptr) 73 | { 74 | static_assert(alignof(T) < Alignment); 75 | 76 | const uintptr_t ptrint = reinterpret_cast(reinterpret_cast(ptr)); 77 | return reinterpret_cast(reinterpret_cast((ptrint + (Alignment - 1)) / Alignment * Alignment)); 78 | } 79 | 80 | /// xorshift64star Pseudo-Random Number Generator 81 | /// This class is based on original code written and dedicated 82 | /// to the public domain by Sebastiano Vigna (2014). 83 | /// It has the following characteristics: 84 | /// 85 | /// - Outputs 64-bit numbers 86 | /// - Passes Dieharder and SmallCrush test batteries 87 | /// - Does not require warm-up, no zeroland to escape 88 | /// - Internal state is a single 64-bit integer 89 | /// - Period is 2^64 - 1 90 | /// - Speed: 1.60 ns/call (Core i7 @3.40GHz) 91 | /// 92 | /// For further analysis see 93 | /// 94 | 95 | class PRNG { 96 | 97 | uint64_t s; 98 | 99 | uint64_t rand64() { 100 | 101 | s ^= s >> 12, s ^= s << 25, s ^= s >> 27; 102 | return s * 2685821657736338717LL; 103 | } 104 | 105 | public: 106 | PRNG(uint64_t seed) : s(seed) { assert(seed); } 107 | 108 | template T rand() { return T(rand64()); } 109 | 110 | /// Special generator used to fast init magic numbers. 111 | /// Output values only have 1/8th of their bits set on average. 112 | template T sparse_rand() 113 | { return T(rand64() & rand64() & rand64()); } 114 | }; 115 | 116 | inline uint64_t mul_hi64(uint64_t a, uint64_t b) { 117 | #if defined(__GNUC__) && defined(IS_64BIT) 118 | __extension__ typedef unsigned __int128 uint128; 119 | return ((uint128)a * (uint128)b) >> 64; 120 | #else 121 | uint64_t aL = (uint32_t)a, aH = a >> 32; 122 | uint64_t bL = (uint32_t)b, bH = b >> 32; 123 | uint64_t c1 = (aL * bL) >> 32; 124 | uint64_t c2 = aH * bL + c1; 125 | uint64_t c3 = aL * bH + (uint32_t)c2; 126 | return aH * bH + (c2 >> 32) + (c3 >> 32); 127 | #endif 128 | } 129 | 130 | /// Under Windows it is not possible for a process to run on more than one 131 | /// logical processor group. This usually means to be limited to use max 64 132 | /// cores. To overcome this, some special platform specific API should be 133 | /// called to set group affinity for each thread. Original code from Texel by 134 | /// Peter Österlund. 135 | 136 | namespace WinProcGroup { 137 | void bindThisThread(size_t idx); 138 | } 139 | 140 | namespace CommandLine { 141 | void init(int argc, char* argv[]); 142 | 143 | extern std::string binaryDirectory; // path of the executable directory 144 | extern std::string workingDirectory; // path of the working directory 145 | } 146 | 147 | #endif // #ifndef MISC_H_INCLUDED 148 | -------------------------------------------------------------------------------- /src/psqt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include "types.h" 22 | #include "bitboard.h" 23 | 24 | namespace PSQT { 25 | 26 | #define S(mg, eg) make_score(mg, eg) 27 | 28 | // Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece 29 | // type on a given square a (middlegame, endgame) score pair is assigned. Table 30 | // is defined for files A..D and white side: it is symmetric for black side and 31 | // second half of the files. 32 | constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { 33 | { }, 34 | { }, 35 | { // Knight 36 | { S(-175, -96), S(-92,-65), S(-74,-49), S(-73,-21) }, 37 | { S( -77, -67), S(-41,-54), S(-27,-18), S(-15, 8) }, 38 | { S( -61, -40), S(-17,-27), S( 6, -8), S( 12, 29) }, 39 | { S( -35, -35), S( 8, -2), S( 40, 13), S( 49, 28) }, 40 | { S( -34, -45), S( 13,-16), S( 44, 9), S( 51, 39) }, 41 | { S( -9, -51), S( 22,-44), S( 58,-16), S( 53, 17) }, 42 | { S( -67, -69), S(-27,-50), S( 4,-51), S( 37, 12) }, 43 | { S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) } 44 | }, 45 | { // Bishop 46 | { S(-53,-57), S( -5,-30), S( -8,-37), S(-23,-12) }, 47 | { S(-15,-37), S( 8,-13), S( 19,-17), S( 4, 1) }, 48 | { S( -7,-16), S( 21, -1), S( -5, -2), S( 17, 10) }, 49 | { S( -5,-20), S( 11, -6), S( 25, 0), S( 39, 17) }, 50 | { S(-12,-17), S( 29, -1), S( 22,-14), S( 31, 15) }, 51 | { S(-16,-30), S( 6, 6), S( 1, 4), S( 11, 6) }, 52 | { S(-17,-31), S(-14,-20), S( 5, -1), S( 0, 1) }, 53 | { S(-48,-46), S( 1,-42), S(-14,-37), S(-23,-24) } 54 | }, 55 | { // Rook 56 | { S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) }, 57 | { S(-21,-12), S(-13, -9), S( -8, -1), S( 6, -2) }, 58 | { S(-25, 6), S(-11, -8), S( -1, -2), S( 3, -6) }, 59 | { S(-13, -6), S( -5, 1), S( -4, -9), S(-6, 7) }, 60 | { S(-27, -5), S(-15, 8), S( -4, 7), S( 3, -6) }, 61 | { S(-22, 6), S( -2, 1), S( 6, -7), S(12, 10) }, 62 | { S( -2, 4), S( 12, 5), S( 16, 20), S(18, -5) }, 63 | { S(-17, 18), S(-19, 0), S( -1, 19), S( 9, 13) } 64 | }, 65 | { // Queen 66 | { S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) }, 67 | { S(-3,-55), S( 5,-31), S( 8,-22), S(12, -4) }, 68 | { S(-3,-39), S( 6,-18), S(13, -9), S( 7, 3) }, 69 | { S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) }, 70 | { S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) }, 71 | { S(-4,-38), S(10,-18), S( 6,-12), S( 8, 1) }, 72 | { S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) }, 73 | { S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) } 74 | }, 75 | { // King 76 | { S(271, 1), S(327, 45), S(271, 85), S(198, 76) }, 77 | { S(278, 53), S(303,100), S(234,133), S(179,135) }, 78 | { S(195, 88), S(258,130), S(169,169), S(120,175) }, 79 | { S(164,103), S(190,156), S(138,172), S( 98,172) }, 80 | { S(154, 96), S(179,166), S(105,199), S( 70,199) }, 81 | { S(123, 92), S(145,172), S( 81,184), S( 31,191) }, 82 | { S( 88, 47), S(120,121), S( 65,116), S( 33,131) }, 83 | { S( 59, 11), S( 89, 59), S( 45, 73), S( -1, 78) } 84 | } 85 | }; 86 | 87 | constexpr Score PBonus[RANK_NB][FILE_NB] = 88 | { // Pawn (asymmetric distribution) 89 | { }, 90 | { S( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) }, 91 | { S( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) }, 92 | { S( -4, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S( -8, -9) }, 93 | { S( 13, 10), S( 0, 5), S(-13, 4), S( 1, -5), S( 11, -5), S( -2, -5), S(-13, 14), S( 5, 9) }, 94 | { S( 5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S( -8, 13) }, 95 | { S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) } 96 | }; 97 | 98 | #undef S 99 | 100 | Score psq[PIECE_NB][SQUARE_NB]; 101 | 102 | 103 | // PSQT::init() initializes piece-square tables: the white halves of the tables are 104 | // copied from Bonus[] and PBonus[], adding the piece value, then the black halves of 105 | // the tables are initialized by flipping and changing the sign of the white scores. 106 | void init() { 107 | 108 | for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING}) 109 | { 110 | Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); 111 | 112 | for (Square s = SQ_A1; s <= SQ_H8; ++s) 113 | { 114 | File f = File(edge_distance(file_of(s))); 115 | psq[ pc][s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)] 116 | : Bonus[pc][rank_of(s)][f]); 117 | psq[~pc][flip_rank(s)] = -psq[pc][s]; 118 | } 119 | } 120 | } 121 | 122 | } // namespace PSQT 123 | -------------------------------------------------------------------------------- /src/tt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include // For std::memset 20 | #include 21 | #include 22 | 23 | #include "bitboard.h" 24 | #include "misc.h" 25 | #include "thread.h" 26 | #include "tt.h" 27 | #include "uci.h" 28 | 29 | TranspositionTable TT; // Our global transposition table 30 | 31 | /// TTEntry::save() populates the TTEntry with a new node's data, possibly 32 | /// overwriting an old position. Update is not atomic and can be racy. 33 | 34 | void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { 35 | 36 | // Preserve any existing move for the same position 37 | if (m || (uint16_t)k != key16) 38 | move16 = (uint16_t)m; 39 | 40 | // Overwrite less valuable entries (cheapest checks first) 41 | if (b == BOUND_EXACT 42 | || (uint16_t)k != key16 43 | || d - DEPTH_OFFSET > depth8 - 4) 44 | { 45 | assert(d > DEPTH_OFFSET); 46 | assert(d < 256 + DEPTH_OFFSET); 47 | 48 | key16 = (uint16_t)k; 49 | depth8 = (uint8_t)(d - DEPTH_OFFSET); 50 | genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); 51 | value16 = (int16_t)v; 52 | eval16 = (int16_t)ev; 53 | } 54 | } 55 | 56 | 57 | /// TranspositionTable::resize() sets the size of the transposition table, 58 | /// measured in megabytes. Transposition table consists of a power of 2 number 59 | /// of clusters and each cluster consists of ClusterSize number of TTEntry. 60 | 61 | void TranspositionTable::resize(size_t mbSize) { 62 | 63 | Threads.main()->wait_for_search_finished(); 64 | 65 | aligned_large_pages_free(table); 66 | 67 | clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); 68 | 69 | table = static_cast(aligned_large_pages_alloc(clusterCount * sizeof(Cluster))); 70 | if (!table) 71 | { 72 | std::cerr << "Failed to allocate " << mbSize 73 | << "MB for transposition table." << std::endl; 74 | exit(EXIT_FAILURE); 75 | } 76 | 77 | clear(); 78 | } 79 | 80 | 81 | /// TranspositionTable::clear() initializes the entire transposition table to zero, 82 | // in a multi-threaded way. 83 | 84 | void TranspositionTable::clear() { 85 | 86 | std::vector threads; 87 | 88 | for (size_t idx = 0; idx < Options["Threads"]; ++idx) 89 | { 90 | threads.emplace_back([this, idx]() { 91 | 92 | // Thread binding gives faster search on systems with a first-touch policy 93 | if (Options["Threads"] > 8) 94 | WinProcGroup::bindThisThread(idx); 95 | 96 | // Each thread will zero its part of the hash table 97 | const size_t stride = size_t(clusterCount / Options["Threads"]), 98 | start = size_t(stride * idx), 99 | len = idx != Options["Threads"] - 1 ? 100 | stride : clusterCount - start; 101 | 102 | std::memset(&table[start], 0, len * sizeof(Cluster)); 103 | }); 104 | } 105 | 106 | for (std::thread& th : threads) 107 | th.join(); 108 | } 109 | 110 | 111 | /// TranspositionTable::probe() looks up the current position in the transposition 112 | /// table. It returns true and a pointer to the TTEntry if the position is found. 113 | /// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry 114 | /// to be replaced later. The replace value of an entry is calculated as its depth 115 | /// minus 8 times its relative age. TTEntry t1 is considered more valuable than 116 | /// TTEntry t2 if its replace value is greater than that of t2. 117 | 118 | TTEntry* TranspositionTable::probe(const Key key, bool& found) const { 119 | 120 | TTEntry* const tte = first_entry(key); 121 | const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster 122 | 123 | for (int i = 0; i < ClusterSize; ++i) 124 | if (tte[i].key16 == key16 || !tte[i].depth8) 125 | { 126 | tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & 0x7)); // Refresh 127 | 128 | return found = (bool)tte[i].depth8, &tte[i]; 129 | } 130 | 131 | // Find an entry to be replaced according to the replacement strategy 132 | TTEntry* replace = tte; 133 | for (int i = 1; i < ClusterSize; ++i) 134 | // Due to our packed storage format for generation and its cyclic 135 | // nature we add 263 (256 is the modulus plus 7 to keep the unrelated 136 | // lowest three bits from affecting the result) to calculate the entry 137 | // age correctly even after generation8 overflows into the next cycle. 138 | if ( replace->depth8 - ((263 + generation8 - replace->genBound8) & 0xF8) 139 | > tte[i].depth8 - ((263 + generation8 - tte[i].genBound8) & 0xF8)) 140 | replace = &tte[i]; 141 | 142 | return found = false, replace; 143 | } 144 | 145 | 146 | /// TranspositionTable::hashfull() returns an approximation of the hashtable 147 | /// occupation during a search. The hash is x permill full, as per UCI protocol. 148 | 149 | int TranspositionTable::hashfull() const { 150 | 151 | int cnt = 0; 152 | for (int i = 0; i < 1000; ++i) 153 | for (int j = 0; j < ClusterSize; ++j) 154 | cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & 0xF8) == generation8; 155 | 156 | return cnt / ClusterSize; 157 | } 158 | -------------------------------------------------------------------------------- /src/bitbase.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "bitboard.h" 24 | #include "types.h" 25 | 26 | namespace { 27 | 28 | // There are 24 possible pawn squares: files A to D and ranks from 2 to 7. 29 | // Positions with the pawn on files E to H will be mirrored before probing. 30 | constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608 31 | 32 | std::bitset KPKBitbase; 33 | 34 | // A KPK bitbase index is an integer in [0, IndexMax] range 35 | // 36 | // Information is mapped in a way that minimizes the number of iterations: 37 | // 38 | // bit 0- 5: white king square (from SQ_A1 to SQ_H8) 39 | // bit 6-11: black king square (from SQ_A1 to SQ_H8) 40 | // bit 12: side to move (WHITE or BLACK) 41 | // bit 13-14: white pawn file (from FILE_A to FILE_D) 42 | // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2) 43 | unsigned index(Color stm, Square bksq, Square wksq, Square psq) { 44 | return int(wksq) | (bksq << 6) | (stm << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15); 45 | } 46 | 47 | enum Result { 48 | INVALID = 0, 49 | UNKNOWN = 1, 50 | DRAW = 2, 51 | WIN = 4 52 | }; 53 | 54 | Result& operator|=(Result& r, Result v) { return r = Result(r | v); } 55 | 56 | struct KPKPosition { 57 | KPKPosition() = default; 58 | explicit KPKPosition(unsigned idx); 59 | operator Result() const { return result; } 60 | Result classify(const std::vector& db); 61 | 62 | Color stm; 63 | Square ksq[COLOR_NB], psq; 64 | Result result; 65 | }; 66 | 67 | } // namespace 68 | 69 | 70 | bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) { 71 | 72 | assert(file_of(wpsq) <= FILE_D); 73 | 74 | return KPKBitbase[index(stm, bksq, wksq, wpsq)]; 75 | } 76 | 77 | 78 | void Bitbases::init() { 79 | 80 | std::vector db(MAX_INDEX); 81 | unsigned idx, repeat = 1; 82 | 83 | // Initialize db with known win / draw positions 84 | for (idx = 0; idx < MAX_INDEX; ++idx) 85 | db[idx] = KPKPosition(idx); 86 | 87 | // Iterate through the positions until none of the unknown positions can be 88 | // changed to either wins or draws (15 cycles needed). 89 | while (repeat) 90 | for (repeat = idx = 0; idx < MAX_INDEX; ++idx) 91 | repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN); 92 | 93 | // Fill the bitbase with the decisive results 94 | for (idx = 0; idx < MAX_INDEX; ++idx) 95 | if (db[idx] == WIN) 96 | KPKBitbase.set(idx); 97 | } 98 | 99 | 100 | namespace { 101 | 102 | KPKPosition::KPKPosition(unsigned idx) { 103 | 104 | ksq[WHITE] = Square((idx >> 0) & 0x3F); 105 | ksq[BLACK] = Square((idx >> 6) & 0x3F); 106 | stm = Color ((idx >> 12) & 0x01); 107 | psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7))); 108 | 109 | // Invalid if two pieces are on the same square or if a king can be captured 110 | if ( distance(ksq[WHITE], ksq[BLACK]) <= 1 111 | || ksq[WHITE] == psq 112 | || ksq[BLACK] == psq 113 | || (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK]))) 114 | result = INVALID; 115 | 116 | // Win if the pawn can be promoted without getting captured 117 | else if ( stm == WHITE 118 | && rank_of(psq) == RANK_7 119 | && ksq[WHITE] != psq + NORTH 120 | && ( distance(ksq[BLACK], psq + NORTH) > 1 121 | || (distance(ksq[WHITE], psq + NORTH) == 1))) 122 | result = WIN; 123 | 124 | // Draw if it is stalemate or the black king can capture the pawn 125 | else if ( stm == BLACK 126 | && ( !(attacks_bb(ksq[BLACK]) & ~(attacks_bb(ksq[WHITE]) | pawn_attacks_bb(WHITE, psq))) 127 | || (attacks_bb(ksq[BLACK]) & ~attacks_bb(ksq[WHITE]) & psq))) 128 | result = DRAW; 129 | 130 | // Position will be classified later 131 | else 132 | result = UNKNOWN; 133 | } 134 | 135 | Result KPKPosition::classify(const std::vector& db) { 136 | 137 | // White to move: If one move leads to a position classified as WIN, the result 138 | // of the current position is WIN. If all moves lead to positions classified 139 | // as DRAW, the current position is classified as DRAW, otherwise the current 140 | // position is classified as UNKNOWN. 141 | // 142 | // Black to move: If one move leads to a position classified as DRAW, the result 143 | // of the current position is DRAW. If all moves lead to positions classified 144 | // as WIN, the position is classified as WIN, otherwise the current position is 145 | // classified as UNKNOWN. 146 | const Result Good = (stm == WHITE ? WIN : DRAW); 147 | const Result Bad = (stm == WHITE ? DRAW : WIN); 148 | 149 | Result r = INVALID; 150 | Bitboard b = attacks_bb(ksq[stm]); 151 | 152 | while (b) 153 | r |= stm == WHITE ? db[index(BLACK, ksq[BLACK] , pop_lsb(&b), psq)] 154 | : db[index(WHITE, pop_lsb(&b), ksq[WHITE], psq)]; 155 | 156 | if (stm == WHITE) 157 | { 158 | if (rank_of(psq) < RANK_7) // Single push 159 | r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH)]; 160 | 161 | if ( rank_of(psq) == RANK_2 // Double push 162 | && psq + NORTH != ksq[WHITE] 163 | && psq + NORTH != ksq[BLACK]) 164 | r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH + NORTH)]; 165 | } 166 | 167 | return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad; 168 | } 169 | 170 | } // namespace 171 | -------------------------------------------------------------------------------- /src/movepick.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef MOVEPICK_H_INCLUDED 20 | #define MOVEPICK_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "movegen.h" 27 | #include "position.h" 28 | #include "types.h" 29 | 30 | /// StatsEntry stores the stat table value. It is usually a number but could 31 | /// be a move or even a nested history. We use a class instead of naked value 32 | /// to directly call history update operator<<() on the entry so to use stats 33 | /// tables at caller sites as simple multi-dim arrays. 34 | template 35 | class StatsEntry { 36 | 37 | T entry; 38 | 39 | public: 40 | void operator=(const T& v) { entry = v; } 41 | T* operator&() { return &entry; } 42 | T* operator->() { return &entry; } 43 | operator const T&() const { return entry; } 44 | 45 | void operator<<(int bonus) { 46 | assert(abs(bonus) <= D); // Ensure range is [-D, D] 47 | static_assert(D <= std::numeric_limits::max(), "D overflows T"); 48 | 49 | entry += bonus - entry * abs(bonus) / D; 50 | 51 | assert(abs(entry) <= D); 52 | } 53 | }; 54 | 55 | /// Stats is a generic N-dimensional array used to store various statistics. 56 | /// The first template parameter T is the base type of the array, the second 57 | /// template parameter D limits the range of updates in [-D, D] when we update 58 | /// values with the << operator, while the last parameters (Size and Sizes) 59 | /// encode the dimensions of the array. 60 | template 61 | struct Stats : public std::array, Size> 62 | { 63 | typedef Stats stats; 64 | 65 | void fill(const T& v) { 66 | 67 | // For standard-layout 'this' points to first struct member 68 | assert(std::is_standard_layout::value); 69 | 70 | typedef StatsEntry entry; 71 | entry* p = reinterpret_cast(this); 72 | std::fill(p, p + sizeof(*this) / sizeof(entry), v); 73 | } 74 | }; 75 | 76 | template 77 | struct Stats : public std::array, Size> {}; 78 | 79 | /// In stats table, D=0 means that the template parameter is not used 80 | enum StatsParams { NOT_USED = 0 }; 81 | enum StatsType { NoCaptures, Captures }; 82 | 83 | /// ButterflyHistory records how often quiet moves have been successful or 84 | /// unsuccessful during the current search, and is used for reduction and move 85 | /// ordering decisions. It uses 2 tables (one for each color) indexed by 86 | /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards 87 | typedef Stats ButterflyHistory; 88 | 89 | /// At higher depths LowPlyHistory records successful quiet moves near the root 90 | /// and quiet moves which are/were in the PV (ttPv). It is cleared with each new 91 | /// search and filled during iterative deepening. 92 | constexpr int MAX_LPH = 4; 93 | typedef Stats LowPlyHistory; 94 | 95 | /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous 96 | /// move, see www.chessprogramming.org/Countermove_Heuristic 97 | typedef Stats CounterMoveHistory; 98 | 99 | /// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] 100 | typedef Stats CapturePieceToHistory; 101 | 102 | /// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] 103 | typedef Stats PieceToHistory; 104 | 105 | /// ContinuationHistory is the combined history of a given pair of moves, usually 106 | /// the current one given a previous one. The nested history table is based on 107 | /// PieceToHistory instead of ButterflyBoards. 108 | typedef Stats ContinuationHistory; 109 | 110 | 111 | /// MovePicker class is used to pick one pseudo legal move at a time from the 112 | /// current position. The most important method is next_move(), which returns a 113 | /// new pseudo legal move each time it is called, until there are no moves left, 114 | /// when MOVE_NONE is returned. In order to improve the efficiency of the alpha 115 | /// beta algorithm, MovePicker attempts to return the moves which are most likely 116 | /// to get a cut-off first. 117 | class MovePicker { 118 | 119 | enum PickType { Next, Best }; 120 | 121 | public: 122 | MovePicker(const MovePicker&) = delete; 123 | MovePicker& operator=(const MovePicker&) = delete; 124 | MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); 125 | MovePicker(const Position&, Move, Depth, const ButterflyHistory*, 126 | const CapturePieceToHistory*, 127 | const PieceToHistory**, 128 | Square); 129 | MovePicker(const Position&, Move, Depth, const ButterflyHistory*, 130 | const LowPlyHistory*, 131 | const CapturePieceToHistory*, 132 | const PieceToHistory**, 133 | Move, 134 | const Move*, 135 | int); 136 | Move next_move(bool skipQuiets = false); 137 | 138 | private: 139 | template Move select(Pred); 140 | template void score(); 141 | ExtMove* begin() { return cur; } 142 | ExtMove* end() { return endMoves; } 143 | 144 | const Position& pos; 145 | const ButterflyHistory* mainHistory; 146 | const LowPlyHistory* lowPlyHistory; 147 | const CapturePieceToHistory* captureHistory; 148 | const PieceToHistory** continuationHistory; 149 | Move ttMove; 150 | ExtMove refutations[3], *cur, *endMoves, *endBadCaptures; 151 | int stage; 152 | Square recaptureSquare; 153 | Value threshold; 154 | Depth depth; 155 | int ply; 156 | ExtMove moves[MAX_MOVES]; 157 | }; 158 | 159 | #endif // #ifndef MOVEPICK_H_INCLUDED 160 | -------------------------------------------------------------------------------- /src/nnue/layers/clipped_relu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // Definition of layer ClippedReLU of NNUE evaluation function 20 | 21 | #ifndef NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED 22 | #define NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED 23 | 24 | #include "../nnue_common.h" 25 | 26 | namespace Eval::NNUE::Layers { 27 | 28 | // Clipped ReLU 29 | template 30 | class ClippedReLU { 31 | public: 32 | // Input/output type 33 | using InputType = typename PreviousLayer::OutputType; 34 | using OutputType = std::uint8_t; 35 | static_assert(std::is_same::value, ""); 36 | 37 | // Number of input/output dimensions 38 | static constexpr IndexType kInputDimensions = 39 | PreviousLayer::kOutputDimensions; 40 | static constexpr IndexType kOutputDimensions = kInputDimensions; 41 | 42 | // Size of forward propagation buffer used in this layer 43 | static constexpr std::size_t kSelfBufferSize = 44 | CeilToMultiple(kOutputDimensions * sizeof(OutputType), kCacheLineSize); 45 | 46 | // Size of the forward propagation buffer used from the input layer to this layer 47 | static constexpr std::size_t kBufferSize = 48 | PreviousLayer::kBufferSize + kSelfBufferSize; 49 | 50 | // Hash value embedded in the evaluation file 51 | static constexpr std::uint32_t GetHashValue() { 52 | std::uint32_t hash_value = 0x538D24C7u; 53 | hash_value += PreviousLayer::GetHashValue(); 54 | return hash_value; 55 | } 56 | 57 | // Read network parameters 58 | bool ReadParameters(std::istream& stream) { 59 | return previous_layer_.ReadParameters(stream); 60 | } 61 | 62 | // Forward propagation 63 | const OutputType* Propagate( 64 | const TransformedFeatureType* transformed_features, char* buffer) const { 65 | const auto input = previous_layer_.Propagate( 66 | transformed_features, buffer + kSelfBufferSize); 67 | const auto output = reinterpret_cast(buffer); 68 | 69 | #if defined(USE_AVX2) 70 | constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; 71 | const __m256i kZero = _mm256_setzero_si256(); 72 | const __m256i kOffsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); 73 | const auto in = reinterpret_cast(input); 74 | const auto out = reinterpret_cast<__m256i*>(output); 75 | for (IndexType i = 0; i < kNumChunks; ++i) { 76 | const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( 77 | _mm256_load_si256(&in[i * 4 + 0]), 78 | _mm256_load_si256(&in[i * 4 + 1])), kWeightScaleBits); 79 | const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( 80 | _mm256_load_si256(&in[i * 4 + 2]), 81 | _mm256_load_si256(&in[i * 4 + 3])), kWeightScaleBits); 82 | _mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( 83 | _mm256_packs_epi16(words0, words1), kZero), kOffsets)); 84 | } 85 | constexpr IndexType kStart = kNumChunks * kSimdWidth; 86 | 87 | #elif defined(USE_SSE2) 88 | constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; 89 | 90 | #ifdef USE_SSE41 91 | const __m128i kZero = _mm_setzero_si128(); 92 | #else 93 | const __m128i k0x80s = _mm_set1_epi8(-128); 94 | #endif 95 | 96 | const auto in = reinterpret_cast(input); 97 | const auto out = reinterpret_cast<__m128i*>(output); 98 | for (IndexType i = 0; i < kNumChunks; ++i) { 99 | const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32( 100 | _mm_load_si128(&in[i * 4 + 0]), 101 | _mm_load_si128(&in[i * 4 + 1])), kWeightScaleBits); 102 | const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32( 103 | _mm_load_si128(&in[i * 4 + 2]), 104 | _mm_load_si128(&in[i * 4 + 3])), kWeightScaleBits); 105 | const __m128i packedbytes = _mm_packs_epi16(words0, words1); 106 | _mm_store_si128(&out[i], 107 | 108 | #ifdef USE_SSE41 109 | _mm_max_epi8(packedbytes, kZero) 110 | #else 111 | _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) 112 | #endif 113 | 114 | ); 115 | } 116 | constexpr IndexType kStart = kNumChunks * kSimdWidth; 117 | 118 | #elif defined(USE_MMX) 119 | constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; 120 | const __m64 k0x80s = _mm_set1_pi8(-128); 121 | const auto in = reinterpret_cast(input); 122 | const auto out = reinterpret_cast<__m64*>(output); 123 | for (IndexType i = 0; i < kNumChunks; ++i) { 124 | const __m64 words0 = _mm_srai_pi16( 125 | _mm_packs_pi32(in[i * 4 + 0], in[i * 4 + 1]), 126 | kWeightScaleBits); 127 | const __m64 words1 = _mm_srai_pi16( 128 | _mm_packs_pi32(in[i * 4 + 2], in[i * 4 + 3]), 129 | kWeightScaleBits); 130 | const __m64 packedbytes = _mm_packs_pi16(words0, words1); 131 | out[i] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); 132 | } 133 | _mm_empty(); 134 | constexpr IndexType kStart = kNumChunks * kSimdWidth; 135 | 136 | #elif defined(USE_NEON) 137 | constexpr IndexType kNumChunks = kInputDimensions / (kSimdWidth / 2); 138 | const int8x8_t kZero = {0}; 139 | const auto in = reinterpret_cast(input); 140 | const auto out = reinterpret_cast(output); 141 | for (IndexType i = 0; i < kNumChunks; ++i) { 142 | int16x8_t shifted; 143 | const auto pack = reinterpret_cast(&shifted); 144 | pack[0] = vqshrn_n_s32(in[i * 2 + 0], kWeightScaleBits); 145 | pack[1] = vqshrn_n_s32(in[i * 2 + 1], kWeightScaleBits); 146 | out[i] = vmax_s8(vqmovn_s16(shifted), kZero); 147 | } 148 | constexpr IndexType kStart = kNumChunks * (kSimdWidth / 2); 149 | #else 150 | constexpr IndexType kStart = 0; 151 | #endif 152 | 153 | for (IndexType i = kStart; i < kInputDimensions; ++i) { 154 | output[i] = static_cast( 155 | std::max(0, std::min(127, input[i] >> kWeightScaleBits))); 156 | } 157 | return output; 158 | } 159 | 160 | private: 161 | PreviousLayer previous_layer_; 162 | }; 163 | 164 | } // namespace Eval::NNUE::Layers 165 | 166 | #endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED 167 | -------------------------------------------------------------------------------- /src/ucioption.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "evaluate.h" 25 | #include "misc.h" 26 | #include "search.h" 27 | #include "thread.h" 28 | #include "tt.h" 29 | #include "uci.h" 30 | #include "syzygy/tbprobe.h" 31 | 32 | using std::string; 33 | 34 | UCI::OptionsMap Options; // Global object 35 | 36 | namespace UCI { 37 | 38 | /// 'On change' actions, triggered by an option's value change 39 | void on_clear_hash(const Option&) { Search::clear(); } 40 | void on_hash_size(const Option& o) { TT.resize(size_t(o)); } 41 | void on_logger(const Option& o) { start_logger(o); } 42 | void on_threads(const Option& o) { Threads.set(size_t(o)); } 43 | void on_tb_path(const Option& o) { Tablebases::init(o); } 44 | void on_use_NNUE(const Option& ) { Eval::NNUE::init(); } 45 | void on_eval_file(const Option& ) { Eval::NNUE::init(); } 46 | 47 | /// Our case insensitive less() function as required by UCI protocol 48 | bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { 49 | 50 | return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), 51 | [](char c1, char c2) { return tolower(c1) < tolower(c2); }); 52 | } 53 | 54 | 55 | /// UCI::init() initializes the UCI options to their hard-coded default values 56 | 57 | void init(OptionsMap& o) { 58 | 59 | constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; 60 | 61 | o["Debug Log File"] << Option("", on_logger); 62 | o["Contempt"] << Option(24, -100, 100); 63 | o["Analysis Contempt"] << Option("Both var Off var White var Black var Both", "Both"); 64 | o["Threads"] << Option(1, 1, 512, on_threads); 65 | o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); 66 | o["Clear Hash"] << Option(on_clear_hash); 67 | o["Ponder"] << Option(false); 68 | o["MultiPV"] << Option(1, 1, 500); 69 | o["Skill Level"] << Option(20, 0, 20); 70 | o["Move Overhead"] << Option(10, 0, 5000); 71 | o["Slow Mover"] << Option(100, 10, 1000); 72 | o["nodestime"] << Option(0, 0, 10000); 73 | o["UCI_Chess960"] << Option(false); 74 | o["UCI_AnalyseMode"] << Option(false); 75 | o["UCI_LimitStrength"] << Option(false); 76 | o["UCI_Elo"] << Option(1350, 1350, 2850); 77 | o["UCI_ShowWDL"] << Option(false); 78 | o["SyzygyPath"] << Option("", on_tb_path); 79 | o["SyzygyProbeDepth"] << Option(1, 1, 100); 80 | o["Syzygy50MoveRule"] << Option(true); 81 | o["SyzygyProbeLimit"] << Option(7, 0, 7); 82 | o["Use NNUE"] << Option(false, on_use_NNUE); 83 | o["EvalFile"] << Option(EvalFileDefaultName, on_eval_file); 84 | } 85 | 86 | 87 | /// operator<<() is used to print all the options default values in chronological 88 | /// insertion order (the idx field) and in the format defined by the UCI protocol. 89 | 90 | std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { 91 | 92 | for (size_t idx = 0; idx < om.size(); ++idx) 93 | for (const auto& it : om) 94 | if (it.second.idx == idx) 95 | { 96 | const Option& o = it.second; 97 | os << "\noption name " << it.first << " type " << o.type; 98 | 99 | if (o.type == "string" || o.type == "check" || o.type == "combo") 100 | os << " default " << o.defaultValue; 101 | 102 | if (o.type == "spin") 103 | os << " default " << int(stof(o.defaultValue)) 104 | << " min " << o.min 105 | << " max " << o.max; 106 | 107 | break; 108 | } 109 | 110 | return os; 111 | } 112 | 113 | 114 | /// Option class constructors and conversion operators 115 | 116 | Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f) 117 | { defaultValue = currentValue = v; } 118 | 119 | Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), on_change(f) 120 | { defaultValue = currentValue = (v ? "true" : "false"); } 121 | 122 | Option::Option(OnChange f) : type("button"), min(0), max(0), on_change(f) 123 | {} 124 | 125 | Option::Option(double v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f) 126 | { defaultValue = currentValue = std::to_string(v); } 127 | 128 | Option::Option(const char* v, const char* cur, OnChange f) : type("combo"), min(0), max(0), on_change(f) 129 | { defaultValue = v; currentValue = cur; } 130 | 131 | Option::operator double() const { 132 | assert(type == "check" || type == "spin"); 133 | return (type == "spin" ? stof(currentValue) : currentValue == "true"); 134 | } 135 | 136 | Option::operator std::string() const { 137 | assert(type == "string"); 138 | return currentValue; 139 | } 140 | 141 | bool Option::operator==(const char* s) const { 142 | assert(type == "combo"); 143 | return !CaseInsensitiveLess()(currentValue, s) 144 | && !CaseInsensitiveLess()(s, currentValue); 145 | } 146 | 147 | 148 | /// operator<<() inits options and assigns idx in the correct printing order 149 | 150 | void Option::operator<<(const Option& o) { 151 | 152 | static size_t insert_order = 0; 153 | 154 | *this = o; 155 | idx = insert_order++; 156 | } 157 | 158 | 159 | /// operator=() updates currentValue and triggers on_change() action. It's up to 160 | /// the GUI to check for option's limits, but we could receive the new value 161 | /// from the user by console window, so let's check the bounds anyway. 162 | 163 | Option& Option::operator=(const string& v) { 164 | 165 | assert(!type.empty()); 166 | 167 | if ( (type != "button" && v.empty()) 168 | || (type == "check" && v != "true" && v != "false") 169 | || (type == "spin" && (stof(v) < min || stof(v) > max))) 170 | return *this; 171 | 172 | if (type == "combo") 173 | { 174 | OptionsMap comboMap; // To have case insensitive compare 175 | string token; 176 | std::istringstream ss(defaultValue); 177 | while (ss >> token) 178 | comboMap[token] << Option(); 179 | if (!comboMap.count(v) || v == "var") 180 | return *this; 181 | } 182 | 183 | if (type != "button") 184 | currentValue = v; 185 | 186 | if (on_change) 187 | on_change(*this); 188 | 189 | return *this; 190 | } 191 | 192 | } // namespace UCI 193 | -------------------------------------------------------------------------------- /src/benchmark.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "position.h" 25 | 26 | using namespace std; 27 | 28 | namespace { 29 | 30 | const vector Defaults = { 31 | "setoption name UCI_Chess960 value false", 32 | "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 33 | "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10", 34 | "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11", 35 | "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", 36 | "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14 moves d4e6", 37 | "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14 moves g2g4", 38 | "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15", 39 | "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13", 40 | "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16", 41 | "4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17", 42 | "2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11", 43 | "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16", 44 | "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", 45 | "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", 46 | "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", 47 | "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26", 48 | "6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1", 49 | "3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1", 50 | "2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1 moves g5g6 f3e3 g6g5 e3f3", 51 | "8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1", 52 | "7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1", 53 | "8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1", 54 | "8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1", 55 | "8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1", 56 | "8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1", 57 | "5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1", 58 | "6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1", 59 | "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1", 60 | "6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1", 61 | "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1", 62 | "5rk1/q6p/2p3bR/1pPp1rP1/1P1Pp3/P3B1Q1/1K3P2/R7 w - - 93 90", 63 | "4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21", 64 | "r3k2r/3nnpbp/q2pp1p1/p7/Pp1PPPP1/4BNN1/1P5P/R2Q1RK1 w kq - 0 16", 65 | "3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40", 66 | "4k3/3q1r2/1N2r1b1/3ppN2/2nPP3/1B1R2n1/2R1Q3/3K4 w - - 5 1", 67 | 68 | // 5-man positions 69 | "8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate 70 | "8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate 71 | "8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw 72 | 73 | // 6-man positions 74 | "8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate 75 | "8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate 76 | "8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw 77 | 78 | // 7-man positions 79 | "8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw 80 | 81 | // Mate and stalemate positions 82 | "6k1/3b3r/1p1p4/p1n2p2/1PPNpP1q/P3Q1p1/1R1RB1P1/5K2 b - - 0 1", 83 | "r2r1n2/pp2bk2/2p1p2p/3q4/3PN1QP/2P3R1/P4PP1/5RK1 w - - 0 1", 84 | "8/8/8/8/8/6k1/6p1/6K1 w - -", 85 | "7k/7P/6K1/8/3B4/8/8/8 b - -", 86 | 87 | // Chess 960 88 | "setoption name UCI_Chess960 value true", 89 | "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w HFhf - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6", 90 | "setoption name UCI_Chess960 value false" 91 | }; 92 | 93 | } // namespace 94 | 95 | /// setup_bench() builds a list of UCI commands to be run by bench. There 96 | /// are five parameters: TT size in MB, number of search threads that 97 | /// should be used, the limit value spent for each position, a file name 98 | /// where to look for positions in FEN format, the type of the limit: 99 | /// depth, perft, nodes and movetime (in millisecs), and evaluation type 100 | /// mixed (default), classical, NNUE. 101 | /// 102 | /// bench -> search default positions up to depth 13 103 | /// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB) 104 | /// bench 64 4 5000 current movetime -> search current position with 4 threads for 5 sec 105 | /// bench 64 1 100000 default nodes -> search default positions for 100K nodes each 106 | /// bench 16 1 5 default perft -> run a perft 5 on default positions 107 | 108 | vector setup_bench(const Position& current, istream& is) { 109 | 110 | vector fens, list; 111 | string go, token; 112 | 113 | // Assign default values to missing arguments 114 | string ttSize = (is >> token) ? token : "16"; 115 | string threads = (is >> token) ? token : "1"; 116 | string limit = (is >> token) ? token : "13"; 117 | string fenFile = (is >> token) ? token : "default"; 118 | string limitType = (is >> token) ? token : "depth"; 119 | string evalType = (is >> token) ? token : "mixed"; 120 | 121 | go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit; 122 | 123 | if (fenFile == "default") 124 | fens = Defaults; 125 | 126 | else if (fenFile == "current") 127 | fens.push_back(current.fen()); 128 | 129 | else 130 | { 131 | string fen; 132 | ifstream file(fenFile); 133 | 134 | if (!file.is_open()) 135 | { 136 | cerr << "Unable to open file " << fenFile << endl; 137 | exit(EXIT_FAILURE); 138 | } 139 | 140 | while (getline(file, fen)) 141 | if (!fen.empty()) 142 | fens.push_back(fen); 143 | 144 | file.close(); 145 | } 146 | 147 | list.emplace_back("setoption name Threads value " + threads); 148 | list.emplace_back("setoption name Hash value " + ttSize); 149 | list.emplace_back("ucinewgame"); 150 | 151 | size_t posCounter = 0; 152 | 153 | for (const string& fen : fens) 154 | if (fen.find("setoption") != string::npos) 155 | list.emplace_back(fen); 156 | else 157 | { 158 | if (evalType == "classical" || (evalType == "mixed" && posCounter % 2 == 0)) 159 | list.emplace_back("setoption name Use NNUE value false"); 160 | else if (evalType == "NNUE" || (evalType == "mixed" && posCounter % 2 != 0)) 161 | list.emplace_back("setoption name Use NNUE value true"); 162 | list.emplace_back("position fen " + fen); 163 | list.emplace_back(go); 164 | ++posCounter; 165 | } 166 | 167 | list.emplace_back("setoption name Use NNUE value true"); 168 | 169 | return list; 170 | } 171 | -------------------------------------------------------------------------------- /src/tune.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef TUNE_H_INCLUDED 20 | #define TUNE_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | typedef std::pair Range; // Option's min-max values 28 | typedef Range (RangeFun) (int); 29 | 30 | // Default Range function, to calculate Option's min-max values 31 | inline Range default_range(int v) { 32 | return v > 0 ? Range(0, 2 * v) : Range(2 * v, 0); 33 | } 34 | 35 | struct SetRange { 36 | explicit SetRange(RangeFun f) : fun(f) {} 37 | SetRange(int min, int max) : fun(nullptr), range(min, max) {} 38 | Range operator()(int v) const { return fun ? fun(v) : range; } 39 | 40 | RangeFun* fun; 41 | Range range; 42 | }; 43 | 44 | #define SetDefaultRange SetRange(default_range) 45 | 46 | 47 | /// BoolConditions struct is used to tune boolean conditions in the 48 | /// code by toggling them on/off according to a probability that 49 | /// depends on the value of a tuned integer parameter: for high 50 | /// values of the parameter condition is always disabled, for low 51 | /// values is always enabled, otherwise it is enabled with a given 52 | /// probability that depnends on the parameter under tuning. 53 | 54 | struct BoolConditions { 55 | void init(size_t size) { values.resize(size, defaultValue), binary.resize(size, 0); } 56 | void set(); 57 | 58 | std::vector binary, values; 59 | int defaultValue = 465, variance = 40, threshold = 500; 60 | SetRange range = SetRange(0, 1000); 61 | }; 62 | 63 | extern BoolConditions Conditions; 64 | 65 | inline void set_conditions() { Conditions.set(); } 66 | 67 | 68 | /// Tune class implements the 'magic' code that makes the setup of a fishtest 69 | /// tuning session as easy as it can be. Mainly you have just to remove const 70 | /// qualifiers from the variables you want to tune and flag them for tuning, so 71 | /// if you have: 72 | /// 73 | /// const Score myScore = S(10, 15); 74 | /// const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } }; 75 | /// 76 | /// If you have a my_post_update() function to run after values have been updated, 77 | /// and a my_range() function to set custom Option's min-max values, then you just 78 | /// remove the 'const' qualifiers and write somewhere below in the file: 79 | /// 80 | /// TUNE(SetRange(my_range), myScore, myValue, my_post_update); 81 | /// 82 | /// You can also set the range directly, and restore the default at the end 83 | /// 84 | /// TUNE(SetRange(-100, 100), myScore, SetDefaultRange); 85 | /// 86 | /// In case update function is slow and you have many parameters, you can add: 87 | /// 88 | /// UPDATE_ON_LAST(); 89 | /// 90 | /// And the values update, including post update function call, will be done only 91 | /// once, after the engine receives the last UCI option, that is the one defined 92 | /// and created as the last one, so the GUI should send the options in the same 93 | /// order in which have been defined. 94 | 95 | class Tune { 96 | 97 | typedef void (PostUpdate) (); // Post-update function 98 | 99 | Tune() { read_results(); } 100 | Tune(const Tune&) = delete; 101 | void operator=(const Tune&) = delete; 102 | void read_results(); 103 | 104 | static Tune& instance() { static Tune t; return t; } // Singleton 105 | 106 | // Use polymorphism to accomodate Entry of different types in the same vector 107 | struct EntryBase { 108 | virtual ~EntryBase() = default; 109 | virtual void init_option() = 0; 110 | virtual void read_option() = 0; 111 | }; 112 | 113 | template 114 | struct Entry : public EntryBase { 115 | 116 | static_assert(!std::is_const::value, "Parameter cannot be const!"); 117 | 118 | static_assert( std::is_same::value 119 | || std::is_same::value 120 | || std::is_same::value 121 | || std::is_same::value, "Parameter type not supported!"); 122 | 123 | Entry(const std::string& n, T& v, const SetRange& r) : name(n), value(v), range(r) {} 124 | void operator=(const Entry&) = delete; // Because 'value' is a reference 125 | void init_option() override; 126 | void read_option() override; 127 | 128 | std::string name; 129 | T& value; 130 | SetRange range; 131 | }; 132 | 133 | // Our facilty to fill the container, each Entry corresponds to a parameter to tune. 134 | // We use variadic templates to deal with an unspecified number of entries, each one 135 | // of a possible different type. 136 | static std::string next(std::string& names, bool pop = true); 137 | 138 | int add(const SetRange&, std::string&&) { return 0; } 139 | 140 | template 141 | int add(const SetRange& range, std::string&& names, T& value, Args&&... args) { 142 | list.push_back(std::unique_ptr(new Entry(next(names), value, range))); 143 | return add(range, std::move(names), args...); 144 | } 145 | 146 | // Template specialization for arrays: recursively handle multi-dimensional arrays 147 | template 148 | int add(const SetRange& range, std::string&& names, T (&value)[N], Args&&... args) { 149 | for (size_t i = 0; i < N; i++) 150 | add(range, next(names, i == N - 1) + "[" + std::to_string(i) + "]", value[i]); 151 | return add(range, std::move(names), args...); 152 | } 153 | 154 | // Template specialization for SetRange 155 | template 156 | int add(const SetRange&, std::string&& names, SetRange& value, Args&&... args) { 157 | return add(value, (next(names), std::move(names)), args...); 158 | } 159 | 160 | // Template specialization for BoolConditions 161 | template 162 | int add(const SetRange& range, std::string&& names, BoolConditions& cond, Args&&... args) { 163 | for (size_t size = cond.values.size(), i = 0; i < size; i++) 164 | add(cond.range, next(names, i == size - 1) + "_" + std::to_string(i), cond.values[i]); 165 | return add(range, std::move(names), args...); 166 | } 167 | 168 | std::vector> list; 169 | 170 | public: 171 | template 172 | static int add(const std::string& names, Args&&... args) { 173 | return instance().add(SetDefaultRange, names.substr(1, names.size() - 2), args...); // Remove trailing parenthesis 174 | } 175 | static void init() { for (auto& e : instance().list) e->init_option(); read_options(); } // Deferred, due to UCI::Options access 176 | static void read_options() { for (auto& e : instance().list) e->read_option(); } 177 | static bool update_on_last; 178 | }; 179 | 180 | // Some macro magic :-) we define a dummy int variable that compiler initializes calling Tune::add() 181 | #define STRINGIFY(x) #x 182 | #define UNIQUE2(x, y) x ## y 183 | #define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__ 184 | #define TUNE(...) int UNIQUE(p, __LINE__) = Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__) 185 | 186 | #define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true 187 | 188 | // Some macro to tune toggling of boolean conditions 189 | #define CONDITION(x) (Conditions.binary[__COUNTER__] || (x)) 190 | #define TUNE_CONDITIONS() int UNIQUE(c, __LINE__) = (Conditions.init(__COUNTER__), 0); \ 191 | TUNE(Conditions, set_conditions) 192 | 193 | #endif // #ifndef TUNE_H_INCLUDED 194 | -------------------------------------------------------------------------------- /src/bitboard.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "bitboard.h" 23 | #include "misc.h" 24 | 25 | uint8_t PopCnt16[1 << 16]; 26 | uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; 27 | 28 | Bitboard SquareBB[SQUARE_NB]; 29 | Bitboard LineBB[SQUARE_NB][SQUARE_NB]; 30 | Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; 31 | Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; 32 | 33 | Magic RookMagics[SQUARE_NB]; 34 | Magic BishopMagics[SQUARE_NB]; 35 | 36 | namespace { 37 | 38 | Bitboard RookTable[0x19000]; // To store rook attacks 39 | Bitboard BishopTable[0x1480]; // To store bishop attacks 40 | 41 | void init_magics(PieceType pt, Bitboard table[], Magic magics[]); 42 | 43 | } 44 | 45 | 46 | /// safe_destination() returns the bitboard of target square for the given step 47 | /// from the given square. If the step is off the board, returns empty bitboard. 48 | 49 | inline Bitboard safe_destination(Square s, int step) { 50 | Square to = Square(s + step); 51 | return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); 52 | } 53 | 54 | 55 | /// Bitboards::pretty() returns an ASCII representation of a bitboard suitable 56 | /// to be printed to standard output. Useful for debugging. 57 | 58 | const std::string Bitboards::pretty(Bitboard b) { 59 | 60 | std::string s = "+---+---+---+---+---+---+---+---+\n"; 61 | 62 | for (Rank r = RANK_8; r >= RANK_1; --r) 63 | { 64 | for (File f = FILE_A; f <= FILE_H; ++f) 65 | s += b & make_square(f, r) ? "| X " : "| "; 66 | 67 | s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n"; 68 | } 69 | s += " a b c d e f g h\n"; 70 | 71 | return s; 72 | } 73 | 74 | 75 | /// Bitboards::init() initializes various bitboard tables. It is called at 76 | /// startup and relies on global objects to be already zero-initialized. 77 | 78 | void Bitboards::init() { 79 | 80 | for (unsigned i = 0; i < (1 << 16); ++i) 81 | PopCnt16[i] = uint8_t(std::bitset<16>(i).count()); 82 | 83 | for (Square s = SQ_A1; s <= SQ_H8; ++s) 84 | SquareBB[s] = (1ULL << s); 85 | 86 | for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) 87 | for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) 88 | SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); 89 | 90 | init_magics(ROOK, RookTable, RookMagics); 91 | init_magics(BISHOP, BishopTable, BishopMagics); 92 | 93 | for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) 94 | { 95 | PawnAttacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1)); 96 | PawnAttacks[BLACK][s1] = pawn_attacks_bb(square_bb(s1)); 97 | 98 | for (int step : {-9, -8, -7, -1, 1, 7, 8, 9} ) 99 | PseudoAttacks[KING][s1] |= safe_destination(s1, step); 100 | 101 | for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} ) 102 | PseudoAttacks[KNIGHT][s1] |= safe_destination(s1, step); 103 | 104 | PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); 105 | PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0); 106 | 107 | for (PieceType pt : { BISHOP, ROOK }) 108 | for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) 109 | if (PseudoAttacks[pt][s1] & s2) 110 | LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; 111 | } 112 | } 113 | 114 | 115 | namespace { 116 | 117 | Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { 118 | 119 | Bitboard attacks = 0; 120 | Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST}; 121 | Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST}; 122 | 123 | for (Direction d : (pt == ROOK ? RookDirections : BishopDirections)) 124 | { 125 | Square s = sq; 126 | while(safe_destination(s, d) && !(occupied & s)) 127 | attacks |= (s += d); 128 | } 129 | 130 | return attacks; 131 | } 132 | 133 | 134 | // init_magics() computes all rook and bishop attacks at startup. Magic 135 | // bitboards are used to look up attacks of sliding pieces. As a reference see 136 | // www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so 137 | // called "fancy" approach. 138 | 139 | void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { 140 | 141 | // Optimal PRNG seeds to pick the correct magics in the shortest time 142 | int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 }, 143 | { 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } }; 144 | 145 | Bitboard occupancy[4096], reference[4096], edges, b; 146 | int epoch[4096] = {}, cnt = 0, size = 0; 147 | 148 | for (Square s = SQ_A1; s <= SQ_H8; ++s) 149 | { 150 | // Board edges are not considered in the relevant occupancies 151 | edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s)); 152 | 153 | // Given a square 's', the mask is the bitboard of sliding attacks from 154 | // 's' computed on an empty board. The index must be big enough to contain 155 | // all the attacks for each possible subset of the mask and so is 2 power 156 | // the number of 1s of the mask. Hence we deduce the size of the shift to 157 | // apply to the 64 or 32 bits word to get the index. 158 | Magic& m = magics[s]; 159 | m.mask = sliding_attack(pt, s, 0) & ~edges; 160 | m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask); 161 | 162 | // Set the offset for the attacks table of the square. We have individual 163 | // table sizes for each square with "Fancy Magic Bitboards". 164 | m.attacks = s == SQ_A1 ? table : magics[s - 1].attacks + size; 165 | 166 | // Use Carry-Rippler trick to enumerate all subsets of masks[s] and 167 | // store the corresponding sliding attack bitboard in reference[]. 168 | b = size = 0; 169 | do { 170 | occupancy[size] = b; 171 | reference[size] = sliding_attack(pt, s, b); 172 | 173 | if (HasPext) 174 | m.attacks[pext(b, m.mask)] = reference[size]; 175 | 176 | size++; 177 | b = (b - m.mask) & m.mask; 178 | } while (b); 179 | 180 | if (HasPext) 181 | continue; 182 | 183 | PRNG rng(seeds[Is64Bit][rank_of(s)]); 184 | 185 | // Find a magic for square 's' picking up an (almost) random number 186 | // until we find the one that passes the verification test. 187 | for (int i = 0; i < size; ) 188 | { 189 | for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6; ) 190 | m.magic = rng.sparse_rand(); 191 | 192 | // A good magic must map every possible occupancy to an index that 193 | // looks up the correct sliding attack in the attacks[s] database. 194 | // Note that we build up the database for square 's' as a side 195 | // effect of verifying the magic. Keep track of the attempt count 196 | // and save it in epoch[], little speed-up trick to avoid resetting 197 | // m.attacks[] after every failed attempt. 198 | for (++cnt, i = 0; i < size; ++i) 199 | { 200 | unsigned idx = m.index(occupancy[i]); 201 | 202 | if (epoch[idx] < cnt) 203 | { 204 | epoch[idx] = cnt; 205 | m.attacks[idx] = reference[i]; 206 | } 207 | else if (m.attacks[idx] != reference[i]) 208 | break; 209 | } 210 | } 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/thread.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include // For std::count 22 | #include "movegen.h" 23 | #include "search.h" 24 | #include "thread.h" 25 | #include "uci.h" 26 | #include "syzygy/tbprobe.h" 27 | #include "tt.h" 28 | 29 | ThreadPool Threads; // Global object 30 | 31 | 32 | /// Thread constructor launches the thread and waits until it goes to sleep 33 | /// in idle_loop(). Note that 'searching' and 'exit' should be already set. 34 | 35 | Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) { 36 | 37 | wait_for_search_finished(); 38 | } 39 | 40 | 41 | /// Thread destructor wakes up the thread in idle_loop() and waits 42 | /// for its termination. Thread should be already waiting. 43 | 44 | Thread::~Thread() { 45 | 46 | assert(!searching); 47 | 48 | exit = true; 49 | start_searching(); 50 | stdThread.join(); 51 | } 52 | 53 | 54 | /// Thread::clear() reset histories, usually before a new game 55 | 56 | void Thread::clear() { 57 | 58 | counterMoves.fill(MOVE_NONE); 59 | mainHistory.fill(0); 60 | lowPlyHistory.fill(0); 61 | captureHistory.fill(0); 62 | 63 | for (bool inCheck : { false, true }) 64 | for (StatsType c : { NoCaptures, Captures }) 65 | { 66 | for (auto& to : continuationHistory[inCheck][c]) 67 | for (auto& h : to) 68 | h->fill(0); 69 | continuationHistory[inCheck][c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); 70 | } 71 | } 72 | 73 | 74 | /// Thread::start_searching() wakes up the thread that will start the search 75 | 76 | void Thread::start_searching() { 77 | 78 | std::lock_guard lk(mutex); 79 | searching = true; 80 | cv.notify_one(); // Wake up the thread in idle_loop() 81 | } 82 | 83 | 84 | /// Thread::wait_for_search_finished() blocks on the condition variable 85 | /// until the thread has finished searching. 86 | 87 | void Thread::wait_for_search_finished() { 88 | 89 | std::unique_lock lk(mutex); 90 | cv.wait(lk, [&]{ return !searching; }); 91 | } 92 | 93 | 94 | /// Thread::idle_loop() is where the thread is parked, blocked on the 95 | /// condition variable, when it has no work to do. 96 | 97 | void Thread::idle_loop() { 98 | 99 | // If OS already scheduled us on a different group than 0 then don't overwrite 100 | // the choice, eventually we are one of many one-threaded processes running on 101 | // some Windows NUMA hardware, for instance in fishtest. To make it simple, 102 | // just check if running threads are below a threshold, in this case all this 103 | // NUMA machinery is not needed. 104 | if (Options["Threads"] > 8) 105 | WinProcGroup::bindThisThread(idx); 106 | 107 | while (true) 108 | { 109 | std::unique_lock lk(mutex); 110 | searching = false; 111 | cv.notify_one(); // Wake up anyone waiting for search finished 112 | cv.wait(lk, [&]{ return searching; }); 113 | 114 | if (exit) 115 | return; 116 | 117 | lk.unlock(); 118 | 119 | search(); 120 | } 121 | } 122 | 123 | /// ThreadPool::set() creates/destroys threads to match the requested number. 124 | /// Created and launched threads will immediately go to sleep in idle_loop. 125 | /// Upon resizing, threads are recreated to allow for binding if necessary. 126 | 127 | void ThreadPool::set(size_t requested) { 128 | 129 | if (size() > 0) { // destroy any existing thread(s) 130 | main()->wait_for_search_finished(); 131 | 132 | while (size() > 0) 133 | delete back(), pop_back(); 134 | } 135 | 136 | if (requested > 0) { // create new thread(s) 137 | push_back(new MainThread(0)); 138 | 139 | while (size() < requested) 140 | push_back(new Thread(size())); 141 | clear(); 142 | 143 | // Reallocate the hash with the new threadpool size 144 | TT.resize(size_t(Options["Hash"])); 145 | 146 | // Init thread number dependent search params. 147 | Search::init(); 148 | } 149 | } 150 | 151 | 152 | /// ThreadPool::clear() sets threadPool data to initial values 153 | 154 | void ThreadPool::clear() { 155 | 156 | for (Thread* th : *this) 157 | th->clear(); 158 | 159 | main()->callsCnt = 0; 160 | main()->bestPreviousScore = VALUE_INFINITE; 161 | main()->previousTimeReduction = 1.0; 162 | } 163 | 164 | 165 | /// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and 166 | /// returns immediately. Main thread will wake up other threads and start the search. 167 | 168 | void ThreadPool::start_thinking(Position& pos, StateListPtr& states, 169 | const Search::LimitsType& limits, bool ponderMode) { 170 | 171 | main()->wait_for_search_finished(); 172 | 173 | main()->stopOnPonderhit = stop = false; 174 | increaseDepth = true; 175 | main()->ponder = ponderMode; 176 | Search::Limits = limits; 177 | Search::RootMoves rootMoves; 178 | 179 | for (const auto& m : MoveList(pos)) 180 | if ( limits.searchmoves.empty() 181 | || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) 182 | rootMoves.emplace_back(m); 183 | 184 | if (!rootMoves.empty()) 185 | Tablebases::rank_root_moves(pos, rootMoves); 186 | 187 | // After ownership transfer 'states' becomes empty, so if we stop the search 188 | // and call 'go' again without setting a new position states.get() == NULL. 189 | assert(states.get() || setupStates.get()); 190 | 191 | if (states.get()) 192 | setupStates = std::move(states); // Ownership transfer, states is now empty 193 | 194 | // We use Position::set() to set root position across threads. But there are 195 | // some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot 196 | // be deduced from a fen string, so set() clears them and they are set from 197 | // setupStates->back() later. The rootState is per thread, earlier states are shared 198 | // since they are read-only. 199 | for (Thread* th : *this) 200 | { 201 | th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0; 202 | th->rootDepth = th->completedDepth = 0; 203 | th->rootMoves = rootMoves; 204 | th->rootPos.set(pos.fen(), pos.is_chess960(), &th->rootState, th); 205 | th->rootState = setupStates->back(); 206 | } 207 | 208 | main()->start_searching(); 209 | } 210 | 211 | Thread* ThreadPool::get_best_thread() const { 212 | 213 | Thread* bestThread = front(); 214 | std::map votes; 215 | Value minScore = VALUE_NONE; 216 | 217 | // Find minimum score of all threads 218 | for (Thread* th: *this) 219 | minScore = std::min(minScore, th->rootMoves[0].score); 220 | 221 | // Vote according to score and depth, and select the best thread 222 | for (Thread* th : *this) 223 | { 224 | votes[th->rootMoves[0].pv[0]] += 225 | (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); 226 | 227 | if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) 228 | { 229 | // Make sure we pick the shortest mate / TB conversion or stave off mate the longest 230 | if (th->rootMoves[0].score > bestThread->rootMoves[0].score) 231 | bestThread = th; 232 | } 233 | else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY 234 | || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY 235 | && votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])) 236 | bestThread = th; 237 | } 238 | 239 | return bestThread; 240 | } 241 | 242 | 243 | /// Start non-main threads 244 | 245 | void ThreadPool::start_searching() { 246 | 247 | for (Thread* th : *this) 248 | if (th != front()) 249 | th->start_searching(); 250 | } 251 | 252 | 253 | /// Wait for non-main threads 254 | 255 | void ThreadPool::wait_for_search_finished() const { 256 | 257 | for (Thread* th : *this) 258 | if (th != front()) 259 | th->wait_for_search_finished(); 260 | } 261 | -------------------------------------------------------------------------------- /src/material.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include // For std::memset 21 | 22 | #include "material.h" 23 | #include "thread.h" 24 | 25 | using namespace std; 26 | 27 | namespace { 28 | #define S(mg, eg) make_score(mg, eg) 29 | 30 | // Polynomial material imbalance parameters 31 | 32 | constexpr Score QuadraticOurs[][PIECE_TYPE_NB] = { 33 | // OUR PIECES 34 | // pair pawn knight bishop rook queen 35 | {S(1419, 1455) }, // Bishop pair 36 | {S( 101, 28), S( 37, 39) }, // Pawn 37 | {S( 57, 64), S(249, 187), S(-49, -62) }, // Knight OUR PIECES 38 | {S( 0, 0), S(118, 137), S( 10, 27), S( 0, 0) }, // Bishop 39 | {S( -63, -68), S( -5, 3), S(100, 81), S(132, 118), S(-246, -244) }, // Rook 40 | {S(-210, -211), S( 37, 14), S(147, 141), S(161, 105), S(-158, -174), S(-9,-31) } // Queen 41 | }; 42 | 43 | constexpr Score QuadraticTheirs[][PIECE_TYPE_NB] = { 44 | // THEIR PIECES 45 | // pair pawn knight bishop rook queen 46 | { }, // Bishop pair 47 | {S( 33, 30) }, // Pawn 48 | {S( 46, 18), S(106, 84) }, // Knight OUR PIECES 49 | {S( 75, 35), S( 59, 44), S( 60, 15) }, // Bishop 50 | {S( 26, 35), S( 6, 22), S( 38, 39), S(-12, -2) }, // Rook 51 | {S( 97, 93), S(100, 163), S(-58, -91), S(112, 192), S(276, 225) } // Queen 52 | }; 53 | 54 | #undef S 55 | 56 | // Endgame evaluation and scaling functions are accessed directly and not through 57 | // the function maps because they correspond to more than one material hash key. 58 | Endgame EvaluateKXK[] = { Endgame(WHITE), Endgame(BLACK) }; 59 | 60 | Endgame ScaleKBPsK[] = { Endgame(WHITE), Endgame(BLACK) }; 61 | Endgame ScaleKQKRPs[] = { Endgame(WHITE), Endgame(BLACK) }; 62 | Endgame ScaleKPsK[] = { Endgame(WHITE), Endgame(BLACK) }; 63 | Endgame ScaleKPKP[] = { Endgame(WHITE), Endgame(BLACK) }; 64 | 65 | // Helper used to detect a given material distribution 66 | bool is_KXK(const Position& pos, Color us) { 67 | return !more_than_one(pos.pieces(~us)) 68 | && pos.non_pawn_material(us) >= RookValueMg; 69 | } 70 | 71 | bool is_KBPsK(const Position& pos, Color us) { 72 | return pos.non_pawn_material(us) == BishopValueMg 73 | && pos.count(us) >= 1; 74 | } 75 | 76 | bool is_KQKRPs(const Position& pos, Color us) { 77 | return !pos.count(us) 78 | && pos.non_pawn_material(us) == QueenValueMg 79 | && pos.count(~us) == 1 80 | && pos.count(~us) >= 1; 81 | } 82 | 83 | 84 | /// imbalance() calculates the imbalance by comparing the piece count of each 85 | /// piece type for both colors. 86 | 87 | template 88 | Score imbalance(const int pieceCount[][PIECE_TYPE_NB]) { 89 | 90 | constexpr Color Them = ~Us; 91 | 92 | Score bonus = SCORE_ZERO; 93 | 94 | // Second-degree polynomial material imbalance, by Tord Romstad 95 | for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1) 96 | { 97 | if (!pieceCount[Us][pt1]) 98 | continue; 99 | 100 | int v = QuadraticOurs[pt1][pt1] * pieceCount[Us][pt1]; 101 | 102 | for (int pt2 = NO_PIECE_TYPE; pt2 < pt1; ++pt2) 103 | v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2] 104 | + QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2]; 105 | 106 | bonus += pieceCount[Us][pt1] * v; 107 | } 108 | 109 | return bonus; 110 | } 111 | 112 | } // namespace 113 | 114 | namespace Material { 115 | 116 | 117 | /// Material::probe() looks up the current position's material configuration in 118 | /// the material hash table. It returns a pointer to the Entry if the position 119 | /// is found. Otherwise a new Entry is computed and stored there, so we don't 120 | /// have to recompute all when the same material configuration occurs again. 121 | 122 | Entry* probe(const Position& pos) { 123 | 124 | Key key = pos.material_key(); 125 | Entry* e = pos.this_thread()->materialTable[key]; 126 | 127 | if (e->key == key) 128 | return e; 129 | 130 | std::memset(e, 0, sizeof(Entry)); 131 | e->key = key; 132 | e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL; 133 | 134 | Value npm_w = pos.non_pawn_material(WHITE); 135 | Value npm_b = pos.non_pawn_material(BLACK); 136 | Value npm = std::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit); 137 | 138 | // Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME] 139 | e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit)); 140 | 141 | // Let's look if we have a specialized evaluation function for this particular 142 | // material configuration. Firstly we look for a fixed configuration one, then 143 | // for a generic one if the previous search failed. 144 | if ((e->evaluationFunction = Endgames::probe(key)) != nullptr) 145 | return e; 146 | 147 | for (Color c : { WHITE, BLACK }) 148 | if (is_KXK(pos, c)) 149 | { 150 | e->evaluationFunction = &EvaluateKXK[c]; 151 | return e; 152 | } 153 | 154 | // OK, we didn't find any special evaluation function for the current material 155 | // configuration. Is there a suitable specialized scaling function? 156 | const auto* sf = Endgames::probe(key); 157 | 158 | if (sf) 159 | { 160 | e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned 161 | return e; 162 | } 163 | 164 | // We didn't find any specialized scaling function, so fall back on generic 165 | // ones that refer to more than one material distribution. Note that in this 166 | // case we don't return after setting the function. 167 | for (Color c : { WHITE, BLACK }) 168 | { 169 | if (is_KBPsK(pos, c)) 170 | e->scalingFunction[c] = &ScaleKBPsK[c]; 171 | 172 | else if (is_KQKRPs(pos, c)) 173 | e->scalingFunction[c] = &ScaleKQKRPs[c]; 174 | } 175 | 176 | if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board 177 | { 178 | if (!pos.count(BLACK)) 179 | { 180 | assert(pos.count(WHITE) >= 2); 181 | 182 | e->scalingFunction[WHITE] = &ScaleKPsK[WHITE]; 183 | } 184 | else if (!pos.count(WHITE)) 185 | { 186 | assert(pos.count(BLACK) >= 2); 187 | 188 | e->scalingFunction[BLACK] = &ScaleKPsK[BLACK]; 189 | } 190 | else if (pos.count(WHITE) == 1 && pos.count(BLACK) == 1) 191 | { 192 | // This is a special case because we set scaling functions 193 | // for both colors instead of only one. 194 | e->scalingFunction[WHITE] = &ScaleKPKP[WHITE]; 195 | e->scalingFunction[BLACK] = &ScaleKPKP[BLACK]; 196 | } 197 | } 198 | 199 | // Zero or just one pawn makes it difficult to win, even with a small material 200 | // advantage. This catches some trivial draws like KK, KBK and KNK and gives a 201 | // drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN). 202 | if (!pos.count(WHITE) && npm_w - npm_b <= BishopValueMg) 203 | e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW : 204 | npm_b <= BishopValueMg ? 4 : 14); 205 | 206 | if (!pos.count(BLACK) && npm_b - npm_w <= BishopValueMg) 207 | e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW : 208 | npm_w <= BishopValueMg ? 4 : 14); 209 | 210 | // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder 211 | // for the bishop pair "extended piece", which allows us to be more flexible 212 | // in defining bishop pair bonuses. 213 | const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = { 214 | { pos.count(WHITE) > 1, pos.count(WHITE), pos.count(WHITE), 215 | pos.count(WHITE) , pos.count(WHITE), pos.count(WHITE) }, 216 | { pos.count(BLACK) > 1, pos.count(BLACK), pos.count(BLACK), 217 | pos.count(BLACK) , pos.count(BLACK), pos.count(BLACK) } }; 218 | 219 | e->score = (imbalance(pieceCount) - imbalance(pieceCount)) / 16; 220 | return e; 221 | } 222 | 223 | } // namespace Material 224 | -------------------------------------------------------------------------------- /src/movepick.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include "movepick.h" 22 | 23 | namespace { 24 | 25 | enum Stages { 26 | MAIN_TT, CAPTURE_INIT, GOOD_CAPTURE, REFUTATION, QUIET_INIT, QUIET, BAD_CAPTURE, 27 | EVASION_TT, EVASION_INIT, EVASION, 28 | PROBCUT_TT, PROBCUT_INIT, PROBCUT, 29 | QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK 30 | }; 31 | 32 | // partial_insertion_sort() sorts moves in descending order up to and including 33 | // a given limit. The order of moves smaller than the limit is left unspecified. 34 | void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) { 35 | 36 | for (ExtMove *sortedEnd = begin, *p = begin + 1; p < end; ++p) 37 | if (p->value >= limit) 38 | { 39 | ExtMove tmp = *p, *q; 40 | *p = *++sortedEnd; 41 | for (q = sortedEnd; q != begin && *(q - 1) < tmp; --q) 42 | *q = *(q - 1); 43 | *q = tmp; 44 | } 45 | } 46 | 47 | } // namespace 48 | 49 | 50 | /// Constructors of the MovePicker class. As arguments we pass information 51 | /// to help it to return the (presumably) good moves first, to decide which 52 | /// moves to return (in the quiescence search, for instance, we only want to 53 | /// search captures, promotions, and some checks) and how important good move 54 | /// ordering is at the current node. 55 | 56 | /// MovePicker constructor for the main search 57 | MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp, 58 | const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, const Move* killers, int pl) 59 | : pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch), 60 | ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) { 61 | 62 | assert(d > 0); 63 | 64 | stage = (pos.checkers() ? EVASION_TT : MAIN_TT) + 65 | !(ttm && pos.pseudo_legal(ttm)); 66 | } 67 | 68 | /// MovePicker constructor for quiescence search 69 | MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, 70 | const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs) 71 | : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) { 72 | 73 | assert(d <= 0); 74 | 75 | stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + 76 | !( ttm 77 | && (pos.checkers() || depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) 78 | && pos.pseudo_legal(ttm)); 79 | } 80 | 81 | /// MovePicker constructor for ProbCut: we generate captures with SEE greater 82 | /// than or equal to the given threshold. 83 | MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) 84 | : pos(p), captureHistory(cph), ttMove(ttm), threshold(th) { 85 | 86 | assert(!pos.checkers()); 87 | 88 | stage = PROBCUT_TT + !(ttm && pos.capture(ttm) 89 | && pos.pseudo_legal(ttm) 90 | && pos.see_ge(ttm, threshold)); 91 | } 92 | 93 | /// MovePicker::score() assigns a numerical value to each move in a list, used 94 | /// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring 95 | /// captures with a good history. Quiets moves are ordered using the histories. 96 | template 97 | void MovePicker::score() { 98 | 99 | static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); 100 | 101 | for (auto& m : *this) 102 | if (Type == CAPTURES) 103 | m.value = int(PieceValue[MG][pos.piece_on(to_sq(m))]) * 6 104 | + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]; 105 | 106 | else if (Type == QUIETS) 107 | m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] 108 | + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] 109 | + 2 * (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] 110 | + 2 * (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)] 111 | + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] 112 | + (ply < MAX_LPH ? std::min(4, depth / 3) * (*lowPlyHistory)[ply][from_to(m)] : 0); 113 | 114 | else // Type == EVASIONS 115 | { 116 | if (pos.capture(m)) 117 | m.value = PieceValue[MG][pos.piece_on(to_sq(m))] 118 | - Value(type_of(pos.moved_piece(m))); 119 | else 120 | m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] 121 | + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] 122 | - (1 << 28); 123 | } 124 | } 125 | 126 | /// MovePicker::select() returns the next move satisfying a predicate function. 127 | /// It never returns the TT move. 128 | template 129 | Move MovePicker::select(Pred filter) { 130 | 131 | while (cur < endMoves) 132 | { 133 | if (T == Best) 134 | std::swap(*cur, *std::max_element(cur, endMoves)); 135 | 136 | if (*cur != ttMove && filter()) 137 | return *cur++; 138 | 139 | cur++; 140 | } 141 | return MOVE_NONE; 142 | } 143 | 144 | /// MovePicker::next_move() is the most important method of the MovePicker class. It 145 | /// returns a new pseudo legal move every time it is called until there are no more 146 | /// moves left, picking the move with the highest score from a list of generated moves. 147 | Move MovePicker::next_move(bool skipQuiets) { 148 | 149 | top: 150 | switch (stage) { 151 | 152 | case MAIN_TT: 153 | case EVASION_TT: 154 | case QSEARCH_TT: 155 | case PROBCUT_TT: 156 | ++stage; 157 | return ttMove; 158 | 159 | case CAPTURE_INIT: 160 | case PROBCUT_INIT: 161 | case QCAPTURE_INIT: 162 | cur = endBadCaptures = moves; 163 | endMoves = generate(pos, cur); 164 | 165 | score(); 166 | ++stage; 167 | goto top; 168 | 169 | case GOOD_CAPTURE: 170 | if (select([&](){ 171 | return pos.see_ge(*cur, Value(-69 * cur->value / 1024)) ? 172 | // Move losing capture to endBadCaptures to be tried later 173 | true : (*endBadCaptures++ = *cur, false); })) 174 | return *(cur - 1); 175 | 176 | // Prepare the pointers to loop over the refutations array 177 | cur = std::begin(refutations); 178 | endMoves = std::end(refutations); 179 | 180 | // If the countermove is the same as a killer, skip it 181 | if ( refutations[0].move == refutations[2].move 182 | || refutations[1].move == refutations[2].move) 183 | --endMoves; 184 | 185 | ++stage; 186 | [[fallthrough]]; 187 | 188 | case REFUTATION: 189 | if (select([&](){ return *cur != MOVE_NONE 190 | && !pos.capture(*cur) 191 | && pos.pseudo_legal(*cur); })) 192 | return *(cur - 1); 193 | ++stage; 194 | [[fallthrough]]; 195 | 196 | case QUIET_INIT: 197 | if (!skipQuiets) 198 | { 199 | cur = endBadCaptures; 200 | endMoves = generate(pos, cur); 201 | 202 | score(); 203 | partial_insertion_sort(cur, endMoves, -3000 * depth); 204 | } 205 | 206 | ++stage; 207 | [[fallthrough]]; 208 | 209 | case QUIET: 210 | if ( !skipQuiets 211 | && select([&](){return *cur != refutations[0].move 212 | && *cur != refutations[1].move 213 | && *cur != refutations[2].move;})) 214 | return *(cur - 1); 215 | 216 | // Prepare the pointers to loop over the bad captures 217 | cur = moves; 218 | endMoves = endBadCaptures; 219 | 220 | ++stage; 221 | [[fallthrough]]; 222 | 223 | case BAD_CAPTURE: 224 | return select([](){ return true; }); 225 | 226 | case EVASION_INIT: 227 | cur = moves; 228 | endMoves = generate(pos, cur); 229 | 230 | score(); 231 | ++stage; 232 | [[fallthrough]]; 233 | 234 | case EVASION: 235 | return select([](){ return true; }); 236 | 237 | case PROBCUT: 238 | return select([&](){ return pos.see_ge(*cur, threshold); }); 239 | 240 | case QCAPTURE: 241 | if (select([&](){ return depth > DEPTH_QS_RECAPTURES 242 | || to_sq(*cur) == recaptureSquare; })) 243 | return *(cur - 1); 244 | 245 | // If we did not find any move and we do not try checks, we have finished 246 | if (depth != DEPTH_QS_CHECKS) 247 | return MOVE_NONE; 248 | 249 | ++stage; 250 | [[fallthrough]]; 251 | 252 | case QCHECK_INIT: 253 | cur = moves; 254 | endMoves = generate(pos, cur); 255 | 256 | ++stage; 257 | [[fallthrough]]; 258 | 259 | case QCHECK: 260 | return select([](){ return true; }); 261 | } 262 | 263 | assert(false); 264 | return MOVE_NONE; // Silence warning 265 | } 266 | -------------------------------------------------------------------------------- /src/pawns.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "bitboard.h" 23 | #include "pawns.h" 24 | #include "position.h" 25 | #include "thread.h" 26 | 27 | namespace { 28 | 29 | #define V Value 30 | #define S(mg, eg) make_score(mg, eg) 31 | 32 | // Pawn penalties 33 | constexpr Score Backward = S( 8, 25); 34 | constexpr Score Doubled = S(10, 55); 35 | constexpr Score Isolated = S( 3, 15); 36 | constexpr Score WeakLever = S( 3, 55); 37 | constexpr Score WeakUnopposed = S(13, 25); 38 | 39 | // Bonus for blocked pawns at 5th or 6th rank 40 | constexpr Score BlockedPawn[2] = { S(-15, -3), S(-6, 3) }; 41 | 42 | constexpr Score BlockedStorm[RANK_NB] = { 43 | S(0, 0), S(0, 0), S(75, 78), S(-8, 16), S(-6, 10), S(-6, 6), S(0, 2) 44 | }; 45 | 46 | // Connected pawn bonus 47 | constexpr int Connected[RANK_NB] = { 0, 5, 7, 11, 24, 48, 86 }; 48 | 49 | // Strength of pawn shelter for our king by [distance from edge][rank]. 50 | // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. 51 | constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = { 52 | { V( -5), V( 82), V( 92), V( 54), V( 36), V( 22), V( 28) }, 53 | { V(-44), V( 63), V( 33), V(-50), V(-30), V(-12), V( -62) }, 54 | { V(-11), V( 77), V( 22), V( -6), V( 31), V( 8), V( -45) }, 55 | { V(-39), V(-12), V(-29), V(-50), V(-43), V(-68), V(-164) } 56 | }; 57 | 58 | // Danger of enemy pawns moving toward our king by [distance from edge][rank]. 59 | // RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn 60 | // is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn 61 | // on edge, likely blocked by our king. 62 | constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = { 63 | { V( 87), V(-288), V(-168), V( 96), V( 47), V( 44), V( 46) }, 64 | { V( 42), V( -25), V( 120), V( 45), V( 34), V( -9), V( 24) }, 65 | { V( -8), V( 51), V( 167), V( 35), V( -4), V(-16), V(-12) }, 66 | { V(-17), V( -13), V( 100), V( 4), V( 9), V(-16), V(-31) } 67 | }; 68 | 69 | 70 | // KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties 71 | // for king when the king is on a semi-open or open file. 72 | constexpr Score KingOnFile[2][2] = {{ S(-19,12), S(-6, 7) }, 73 | { S( 0, 2), S( 6,-5) }}; 74 | 75 | #undef S 76 | #undef V 77 | 78 | 79 | /// evaluate() calculates a score for the static pawn structure of the given position. 80 | /// We cannot use the location of pieces or king in this function, as the evaluation 81 | /// of the pawn structure will be stored in a small cache for speed reasons, and will 82 | /// be re-used even when the pieces have moved. 83 | 84 | template 85 | Score evaluate(const Position& pos, Pawns::Entry* e) { 86 | 87 | constexpr Color Them = ~Us; 88 | constexpr Direction Up = pawn_push(Us); 89 | 90 | Bitboard neighbours, stoppers, support, phalanx, opposed; 91 | Bitboard lever, leverPush, blocked; 92 | Square s; 93 | bool backward, passed, doubled; 94 | Score score = SCORE_ZERO; 95 | Bitboard b = pos.pieces(Us, PAWN); 96 | 97 | Bitboard ourPawns = pos.pieces( Us, PAWN); 98 | Bitboard theirPawns = pos.pieces(Them, PAWN); 99 | 100 | Bitboard doubleAttackThem = pawn_double_attacks_bb(theirPawns); 101 | 102 | e->passedPawns[Us] = 0; 103 | e->kingSquares[Us] = SQ_NONE; 104 | e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb(ourPawns); 105 | e->blockedCount += popcount(shift(ourPawns) & (theirPawns | doubleAttackThem)); 106 | 107 | // Loop through all pawns of the current color and score each pawn 108 | while (b) { 109 | s = pop_lsb(&b); 110 | 111 | assert(pos.piece_on(s) == make_piece(Us, PAWN)); 112 | 113 | Rank r = relative_rank(Us, s); 114 | 115 | // Flag the pawn 116 | opposed = theirPawns & forward_file_bb(Us, s); 117 | blocked = theirPawns & (s + Up); 118 | stoppers = theirPawns & passed_pawn_span(Us, s); 119 | lever = theirPawns & pawn_attacks_bb(Us, s); 120 | leverPush = theirPawns & pawn_attacks_bb(Us, s + Up); 121 | doubled = ourPawns & (s - Up); 122 | neighbours = ourPawns & adjacent_files_bb(s); 123 | phalanx = neighbours & rank_bb(s); 124 | support = neighbours & rank_bb(s - Up); 125 | 126 | // A pawn is backward when it is behind all pawns of the same color on 127 | // the adjacent files and cannot safely advance. 128 | backward = !(neighbours & forward_ranks_bb(Them, s + Up)) 129 | && (leverPush | blocked); 130 | 131 | // Compute additional span if pawn is not backward nor blocked 132 | if (!backward && !blocked) 133 | e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); 134 | 135 | // A pawn is passed if one of the three following conditions is true: 136 | // (a) there is no stoppers except some levers 137 | // (b) the only stoppers are the leverPush, but we outnumber them 138 | // (c) there is only one front stopper which can be levered. 139 | // (Refined in Evaluation::passed) 140 | passed = !(stoppers ^ lever) 141 | || ( !(stoppers ^ leverPush) 142 | && popcount(phalanx) >= popcount(leverPush)) 143 | || ( stoppers == blocked && r >= RANK_5 144 | && (shift(support) & ~(theirPawns | doubleAttackThem))); 145 | 146 | passed &= !(forward_file_bb(Us, s) & ourPawns); 147 | 148 | // Passed pawns will be properly scored later in evaluation when we have 149 | // full attack info. 150 | if (passed) 151 | e->passedPawns[Us] |= s; 152 | 153 | // Score this pawn 154 | if (support | phalanx) 155 | { 156 | int v = Connected[r] * (2 + bool(phalanx) - bool(opposed)) 157 | + 22 * popcount(support); 158 | 159 | score += make_score(v, v * (r - 2) / 4); 160 | } 161 | 162 | else if (!neighbours) 163 | { 164 | if ( opposed 165 | && (ourPawns & forward_file_bb(Them, s)) 166 | && !(theirPawns & adjacent_files_bb(s))) 167 | score -= Doubled; 168 | else 169 | score -= Isolated 170 | + WeakUnopposed * !opposed; 171 | } 172 | 173 | else if (backward) 174 | score -= Backward 175 | + WeakUnopposed * !opposed; 176 | 177 | if (!support) 178 | score -= Doubled * doubled 179 | + WeakLever * more_than_one(lever); 180 | 181 | if (blocked && r >= RANK_5) 182 | score += BlockedPawn[r - RANK_5]; 183 | } 184 | 185 | return score; 186 | } 187 | 188 | } // namespace 189 | 190 | namespace Pawns { 191 | 192 | 193 | /// Pawns::probe() looks up the current position's pawns configuration in 194 | /// the pawns hash table. It returns a pointer to the Entry if the position 195 | /// is found. Otherwise a new Entry is computed and stored there, so we don't 196 | /// have to recompute all when the same pawns configuration occurs again. 197 | 198 | Entry* probe(const Position& pos) { 199 | 200 | Key key = pos.pawn_key(); 201 | Entry* e = pos.this_thread()->pawnsTable[key]; 202 | 203 | if (e->key == key) 204 | return e; 205 | 206 | e->key = key; 207 | e->blockedCount = 0; 208 | e->scores[WHITE] = evaluate(pos, e); 209 | e->scores[BLACK] = evaluate(pos, e); 210 | 211 | return e; 212 | } 213 | 214 | 215 | /// Entry::evaluate_shelter() calculates the shelter bonus and the storm 216 | /// penalty for a king, looking at the king file and the two closest files. 217 | 218 | template 219 | Score Entry::evaluate_shelter(const Position& pos, Square ksq) const { 220 | 221 | constexpr Color Them = ~Us; 222 | 223 | Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq); 224 | Bitboard ourPawns = b & pos.pieces(Us) & ~pawnAttacks[Them]; 225 | Bitboard theirPawns = b & pos.pieces(Them); 226 | 227 | Score bonus = make_score(5, 5); 228 | 229 | File center = std::clamp(file_of(ksq), FILE_B, FILE_G); 230 | for (File f = File(center - 1); f <= File(center + 1); ++f) 231 | { 232 | b = ourPawns & file_bb(f); 233 | int ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; 234 | 235 | b = theirPawns & file_bb(f); 236 | int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; 237 | 238 | int d = edge_distance(f); 239 | bonus += make_score(ShelterStrength[d][ourRank], 0); 240 | 241 | if (ourRank && (ourRank == theirRank - 1)) 242 | bonus -= BlockedStorm[theirRank]; 243 | else 244 | bonus -= make_score(UnblockedStorm[d][theirRank], 0); 245 | } 246 | 247 | // King On File 248 | bonus -= KingOnFile[pos.is_on_semiopen_file(Us, ksq)][pos.is_on_semiopen_file(Them, ksq)]; 249 | 250 | return bonus; 251 | } 252 | 253 | 254 | /// Entry::do_king_safety() calculates a bonus for king safety. It is called only 255 | /// when king square changes, which is about 20% of total king_safety() calls. 256 | 257 | template 258 | Score Entry::do_king_safety(const Position& pos) { 259 | 260 | Square ksq = pos.square(Us); 261 | kingSquares[Us] = ksq; 262 | castlingRights[Us] = pos.castling_rights(Us); 263 | auto compare = [](Score a, Score b) { return mg_value(a) < mg_value(b); }; 264 | 265 | Score shelter = evaluate_shelter(pos, ksq); 266 | 267 | // If we can castle use the bonus after castling if it is bigger 268 | 269 | if (pos.can_castle(Us & KING_SIDE)) 270 | shelter = std::max(shelter, evaluate_shelter(pos, relative_square(Us, SQ_G1)), compare); 271 | 272 | if (pos.can_castle(Us & QUEEN_SIDE)) 273 | shelter = std::max(shelter, evaluate_shelter(pos, relative_square(Us, SQ_C1)), compare); 274 | 275 | // In endgame we like to bring our king near our closest pawn 276 | Bitboard pawns = pos.pieces(Us, PAWN); 277 | int minPawnDist = 6; 278 | 279 | if (pawns & attacks_bb(ksq)) 280 | minPawnDist = 1; 281 | else while (pawns) 282 | minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns))); 283 | 284 | return shelter - make_score(0, 16 * minPawnDist); 285 | } 286 | 287 | // Explicit template instantiation 288 | template Score Entry::do_king_safety(const Position& pos); 289 | template Score Entry::do_king_safety(const Position& pos); 290 | 291 | } // namespace Pawns 292 | -------------------------------------------------------------------------------- /src/incbin/incbin.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file incbin.h 3 | * @author Dale Weiler 4 | * @brief Utility for including binary files 5 | * 6 | * Facilities for including binary files into the current translation unit and 7 | * making use from them externally in other translation units. 8 | */ 9 | #ifndef INCBIN_HDR 10 | #define INCBIN_HDR 11 | #include 12 | #if defined(__AVX512BW__) || \ 13 | defined(__AVX512CD__) || \ 14 | defined(__AVX512DQ__) || \ 15 | defined(__AVX512ER__) || \ 16 | defined(__AVX512PF__) || \ 17 | defined(__AVX512VL__) || \ 18 | defined(__AVX512F__) 19 | # define INCBIN_ALIGNMENT_INDEX 6 20 | #elif defined(__AVX__) || \ 21 | defined(__AVX2__) 22 | # define INCBIN_ALIGNMENT_INDEX 5 23 | #elif defined(__SSE__) || \ 24 | defined(__SSE2__) || \ 25 | defined(__SSE3__) || \ 26 | defined(__SSSE3__) || \ 27 | defined(__SSE4_1__) || \ 28 | defined(__SSE4_2__) || \ 29 | defined(__neon__) 30 | # define INCBIN_ALIGNMENT_INDEX 4 31 | #elif ULONG_MAX != 0xffffffffu 32 | # define INCBIN_ALIGNMENT_INDEX 3 33 | # else 34 | # define INCBIN_ALIGNMENT_INDEX 2 35 | #endif 36 | 37 | /* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */ 38 | #define INCBIN_ALIGN_SHIFT_0 1 39 | #define INCBIN_ALIGN_SHIFT_1 2 40 | #define INCBIN_ALIGN_SHIFT_2 4 41 | #define INCBIN_ALIGN_SHIFT_3 8 42 | #define INCBIN_ALIGN_SHIFT_4 16 43 | #define INCBIN_ALIGN_SHIFT_5 32 44 | #define INCBIN_ALIGN_SHIFT_6 64 45 | 46 | /* Actual alignment value */ 47 | #define INCBIN_ALIGNMENT \ 48 | INCBIN_CONCATENATE( \ 49 | INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), \ 50 | INCBIN_ALIGNMENT_INDEX) 51 | 52 | /* Stringize */ 53 | #define INCBIN_STR(X) \ 54 | #X 55 | #define INCBIN_STRINGIZE(X) \ 56 | INCBIN_STR(X) 57 | /* Concatenate */ 58 | #define INCBIN_CAT(X, Y) \ 59 | X ## Y 60 | #define INCBIN_CONCATENATE(X, Y) \ 61 | INCBIN_CAT(X, Y) 62 | /* Deferred macro expansion */ 63 | #define INCBIN_EVAL(X) \ 64 | X 65 | #define INCBIN_INVOKE(N, ...) \ 66 | INCBIN_EVAL(N(__VA_ARGS__)) 67 | 68 | /* Green Hills uses a different directive for including binary data */ 69 | #if defined(__ghs__) 70 | # if (__ghs_asm == 2) 71 | # define INCBIN_MACRO ".file" 72 | /* Or consider the ".myrawdata" entry in the ld file */ 73 | # else 74 | # define INCBIN_MACRO "\tINCBIN" 75 | # endif 76 | #else 77 | # define INCBIN_MACRO ".incbin" 78 | #endif 79 | 80 | #ifndef _MSC_VER 81 | # define INCBIN_ALIGN \ 82 | __attribute__((aligned(INCBIN_ALIGNMENT))) 83 | #else 84 | # define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT)) 85 | #endif 86 | 87 | #if defined(__arm__) || /* GNU C and RealView */ \ 88 | defined(__arm) || /* Diab */ \ 89 | defined(_ARM) /* ImageCraft */ 90 | # define INCBIN_ARM 91 | #endif 92 | 93 | #ifdef __GNUC__ 94 | /* Utilize .balign where supported */ 95 | # define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n" 96 | # define INCBIN_ALIGN_BYTE ".balign 1\n" 97 | #elif defined(INCBIN_ARM) 98 | /* 99 | * On arm assemblers, the alignment value is calculated as (1 << n) where `n' is 100 | * the shift count. This is the value passed to `.align' 101 | */ 102 | # define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n" 103 | # define INCBIN_ALIGN_BYTE ".align 0\n" 104 | #else 105 | /* We assume other inline assembler's treat `.align' as `.balign' */ 106 | # define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n" 107 | # define INCBIN_ALIGN_BYTE ".align 1\n" 108 | #endif 109 | 110 | /* INCBIN_CONST is used by incbin.c generated files */ 111 | #if defined(__cplusplus) 112 | # define INCBIN_EXTERNAL extern "C" 113 | # define INCBIN_CONST extern const 114 | #else 115 | # define INCBIN_EXTERNAL extern 116 | # define INCBIN_CONST const 117 | #endif 118 | 119 | /** 120 | * @brief Optionally override the linker section into which data is emitted. 121 | * 122 | * @warning If you use this facility, you'll have to deal with platform-specific linker output 123 | * section naming on your own 124 | * 125 | * Overriding the default linker output section, e.g for esp8266/Arduino: 126 | * @code 127 | * #define INCBIN_OUTPUT_SECTION ".irom.text" 128 | * #include "incbin.h" 129 | * INCBIN(Foo, "foo.txt"); 130 | * // Data is emitted into program memory that never gets copied to RAM 131 | * @endcode 132 | */ 133 | #if !defined(INCBIN_OUTPUT_SECTION) 134 | # if defined(__APPLE__) 135 | # define INCBIN_OUTPUT_SECTION ".const_data" 136 | # else 137 | # define INCBIN_OUTPUT_SECTION ".rodata" 138 | # endif 139 | #endif 140 | 141 | #if defined(__APPLE__) 142 | /* The directives are different for Apple branded compilers */ 143 | # define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n" 144 | # define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n" 145 | # define INCBIN_INT ".long " 146 | # define INCBIN_MANGLE "_" 147 | # define INCBIN_BYTE ".byte " 148 | # define INCBIN_TYPE(...) 149 | #else 150 | # define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n" 151 | # define INCBIN_GLOBAL(NAME) ".global " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n" 152 | # if defined(__ghs__) 153 | # define INCBIN_INT ".word " 154 | # else 155 | # define INCBIN_INT ".int " 156 | # endif 157 | # if defined(__USER_LABEL_PREFIX__) 158 | # define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__) 159 | # else 160 | # define INCBIN_MANGLE "" 161 | # endif 162 | # if defined(INCBIN_ARM) 163 | /* On arm assemblers, `@' is used as a line comment token */ 164 | # define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n" 165 | # elif defined(__MINGW32__) || defined(__MINGW64__) 166 | /* Mingw doesn't support this directive either */ 167 | # define INCBIN_TYPE(NAME) 168 | # else 169 | /* It's safe to use `@' on other architectures */ 170 | # define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n" 171 | # endif 172 | # define INCBIN_BYTE ".byte " 173 | #endif 174 | 175 | /* List of style types used for symbol names */ 176 | #define INCBIN_STYLE_CAMEL 0 177 | #define INCBIN_STYLE_SNAKE 1 178 | 179 | /** 180 | * @brief Specify the prefix to use for symbol names. 181 | * 182 | * By default this is `g', producing symbols of the form: 183 | * @code 184 | * #include "incbin.h" 185 | * INCBIN(Foo, "foo.txt"); 186 | * 187 | * // Now you have the following symbols: 188 | * // const unsigned char gFooData[]; 189 | * // const unsigned char *const gFooEnd; 190 | * // const unsigned int gFooSize; 191 | * @endcode 192 | * 193 | * If however you specify a prefix before including: e.g: 194 | * @code 195 | * #define INCBIN_PREFIX incbin 196 | * #include "incbin.h" 197 | * INCBIN(Foo, "foo.txt"); 198 | * 199 | * // Now you have the following symbols instead: 200 | * // const unsigned char incbinFooData[]; 201 | * // const unsigned char *const incbinFooEnd; 202 | * // const unsigned int incbinFooSize; 203 | * @endcode 204 | */ 205 | #if !defined(INCBIN_PREFIX) 206 | # define INCBIN_PREFIX g 207 | #endif 208 | 209 | /** 210 | * @brief Specify the style used for symbol names. 211 | * 212 | * Possible options are 213 | * - INCBIN_STYLE_CAMEL "CamelCase" 214 | * - INCBIN_STYLE_SNAKE "snake_case" 215 | * 216 | * Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form: 217 | * @code 218 | * #include "incbin.h" 219 | * INCBIN(Foo, "foo.txt"); 220 | * 221 | * // Now you have the following symbols: 222 | * // const unsigned char FooData[]; 223 | * // const unsigned char *const FooEnd; 224 | * // const unsigned int FooSize; 225 | * @endcode 226 | * 227 | * If however you specify a style before including: e.g: 228 | * @code 229 | * #define INCBIN_STYLE INCBIN_STYLE_SNAKE 230 | * #include "incbin.h" 231 | * INCBIN(foo, "foo.txt"); 232 | * 233 | * // Now you have the following symbols: 234 | * // const unsigned char foo_data[]; 235 | * // const unsigned char *const foo_end; 236 | * // const unsigned int foo_size; 237 | * @endcode 238 | */ 239 | #if !defined(INCBIN_STYLE) 240 | # define INCBIN_STYLE INCBIN_STYLE_CAMEL 241 | #endif 242 | 243 | /* Style lookup tables */ 244 | #define INCBIN_STYLE_0_DATA Data 245 | #define INCBIN_STYLE_0_END End 246 | #define INCBIN_STYLE_0_SIZE Size 247 | #define INCBIN_STYLE_1_DATA _data 248 | #define INCBIN_STYLE_1_END _end 249 | #define INCBIN_STYLE_1_SIZE _size 250 | 251 | /* Style lookup: returning identifier */ 252 | #define INCBIN_STYLE_IDENT(TYPE) \ 253 | INCBIN_CONCATENATE( \ 254 | INCBIN_STYLE_, \ 255 | INCBIN_CONCATENATE( \ 256 | INCBIN_EVAL(INCBIN_STYLE), \ 257 | INCBIN_CONCATENATE(_, TYPE))) 258 | 259 | /* Style lookup: returning string literal */ 260 | #define INCBIN_STYLE_STRING(TYPE) \ 261 | INCBIN_STRINGIZE( \ 262 | INCBIN_STYLE_IDENT(TYPE)) \ 263 | 264 | /* Generate the global labels by indirectly invoking the macro with our style 265 | * type and concatenating the name against them. */ 266 | #define INCBIN_GLOBAL_LABELS(NAME, TYPE) \ 267 | INCBIN_INVOKE( \ 268 | INCBIN_GLOBAL, \ 269 | INCBIN_CONCATENATE( \ 270 | NAME, \ 271 | INCBIN_INVOKE( \ 272 | INCBIN_STYLE_IDENT, \ 273 | TYPE))) \ 274 | INCBIN_INVOKE( \ 275 | INCBIN_TYPE, \ 276 | INCBIN_CONCATENATE( \ 277 | NAME, \ 278 | INCBIN_INVOKE( \ 279 | INCBIN_STYLE_IDENT, \ 280 | TYPE))) 281 | 282 | /** 283 | * @brief Externally reference binary data included in another translation unit. 284 | * 285 | * Produces three external symbols that reference the binary data included in 286 | * another translation unit. 287 | * 288 | * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with 289 | * "Data", as well as "End" and "Size" after. An example is provided below. 290 | * 291 | * @param NAME The name given for the binary data 292 | * 293 | * @code 294 | * INCBIN_EXTERN(Foo); 295 | * 296 | * // Now you have the following symbols: 297 | * // extern const unsigned char FooData[]; 298 | * // extern const unsigned char *const FooEnd; 299 | * // extern const unsigned int FooSize; 300 | * @endcode 301 | */ 302 | #define INCBIN_EXTERN(NAME) \ 303 | INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char \ 304 | INCBIN_CONCATENATE( \ 305 | INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \ 306 | INCBIN_STYLE_IDENT(DATA))[]; \ 307 | INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const \ 308 | INCBIN_CONCATENATE( \ 309 | INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \ 310 | INCBIN_STYLE_IDENT(END)); \ 311 | INCBIN_EXTERNAL const unsigned int \ 312 | INCBIN_CONCATENATE( \ 313 | INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \ 314 | INCBIN_STYLE_IDENT(SIZE)) 315 | 316 | /** 317 | * @brief Include a binary file into the current translation unit. 318 | * 319 | * Includes a binary file into the current translation unit, producing three symbols 320 | * for objects that encode the data and size respectively. 321 | * 322 | * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with 323 | * "Data", as well as "End" and "Size" after. An example is provided below. 324 | * 325 | * @param NAME The name to associate with this binary data (as an identifier.) 326 | * @param FILENAME The file to include (as a string literal.) 327 | * 328 | * @code 329 | * INCBIN(Icon, "icon.png"); 330 | * 331 | * // Now you have the following symbols: 332 | * // const unsigned char IconData[]; 333 | * // const unsigned char *const IconEnd; 334 | * // const unsigned int IconSize; 335 | * @endcode 336 | * 337 | * @warning This must be used in global scope 338 | * @warning The identifiers may be different if INCBIN_STYLE is not default 339 | * 340 | * To externally reference the data included by this in another translation unit 341 | * please @see INCBIN_EXTERN. 342 | */ 343 | #ifdef _MSC_VER 344 | #define INCBIN(NAME, FILENAME) \ 345 | INCBIN_EXTERN(NAME) 346 | #else 347 | #define INCBIN(NAME, FILENAME) \ 348 | __asm__(INCBIN_SECTION \ 349 | INCBIN_GLOBAL_LABELS(NAME, DATA) \ 350 | INCBIN_ALIGN_HOST \ 351 | INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \ 352 | INCBIN_MACRO " \"" FILENAME "\"\n" \ 353 | INCBIN_GLOBAL_LABELS(NAME, END) \ 354 | INCBIN_ALIGN_BYTE \ 355 | INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \ 356 | INCBIN_BYTE "1\n" \ 357 | INCBIN_GLOBAL_LABELS(NAME, SIZE) \ 358 | INCBIN_ALIGN_HOST \ 359 | INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE) ":\n" \ 360 | INCBIN_INT INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) " - " \ 361 | INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) "\n" \ 362 | INCBIN_ALIGN_HOST \ 363 | ".text\n" \ 364 | ); \ 365 | INCBIN_EXTERN(NAME) 366 | 367 | #endif 368 | #endif 369 | -------------------------------------------------------------------------------- /src/movegen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include "movegen.h" 22 | #include "position.h" 23 | 24 | namespace { 25 | 26 | template 27 | ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { 28 | 29 | if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) 30 | { 31 | *moveList++ = make(to - D, to, QUEEN); 32 | if (attacks_bb(to) & ksq) 33 | *moveList++ = make(to - D, to, KNIGHT); 34 | } 35 | 36 | if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) 37 | { 38 | *moveList++ = make(to - D, to, ROOK); 39 | *moveList++ = make(to - D, to, BISHOP); 40 | if (!(attacks_bb(to) & ksq)) 41 | *moveList++ = make(to - D, to, KNIGHT); 42 | } 43 | 44 | return moveList; 45 | } 46 | 47 | 48 | template 49 | ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) { 50 | 51 | constexpr Color Them = ~Us; 52 | constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); 53 | constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); 54 | constexpr Direction Up = pawn_push(Us); 55 | constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); 56 | constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); 57 | 58 | const Square ksq = pos.square(Them); 59 | Bitboard emptySquares; 60 | 61 | Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; 62 | Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; 63 | 64 | Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target: 65 | Type == CAPTURES ? target : pos.pieces(Them)); 66 | 67 | // Single and double pawn pushes, no promotions 68 | if (Type != CAPTURES) 69 | { 70 | emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces()); 71 | 72 | Bitboard b1 = shift(pawnsNotOn7) & emptySquares; 73 | Bitboard b2 = shift(b1 & TRank3BB) & emptySquares; 74 | 75 | if (Type == EVASIONS) // Consider only blocking squares 76 | { 77 | b1 &= target; 78 | b2 &= target; 79 | } 80 | 81 | if (Type == QUIET_CHECKS) 82 | { 83 | b1 &= pawn_attacks_bb(Them, ksq); 84 | b2 &= pawn_attacks_bb(Them, ksq); 85 | 86 | // Add pawn pushes which give discovered check. This is possible only 87 | // if the pawn is not on the same file as the enemy king, because we 88 | // don't generate captures. Note that a possible discovery check 89 | // promotion has been already generated amongst the captures. 90 | Bitboard dcCandidateQuiets = pos.blockers_for_king(Them) & pawnsNotOn7; 91 | if (dcCandidateQuiets) 92 | { 93 | Bitboard dc1 = shift(dcCandidateQuiets) & emptySquares & ~file_bb(ksq); 94 | Bitboard dc2 = shift(dc1 & TRank3BB) & emptySquares; 95 | 96 | b1 |= dc1; 97 | b2 |= dc2; 98 | } 99 | } 100 | 101 | while (b1) 102 | { 103 | Square to = pop_lsb(&b1); 104 | *moveList++ = make_move(to - Up, to); 105 | } 106 | 107 | while (b2) 108 | { 109 | Square to = pop_lsb(&b2); 110 | *moveList++ = make_move(to - Up - Up, to); 111 | } 112 | } 113 | 114 | // Promotions and underpromotions 115 | if (pawnsOn7) 116 | { 117 | if (Type == CAPTURES) 118 | emptySquares = ~pos.pieces(); 119 | 120 | if (Type == EVASIONS) 121 | emptySquares &= target; 122 | 123 | Bitboard b1 = shift(pawnsOn7) & enemies; 124 | Bitboard b2 = shift(pawnsOn7) & enemies; 125 | Bitboard b3 = shift(pawnsOn7) & emptySquares; 126 | 127 | while (b1) 128 | moveList = make_promotions(moveList, pop_lsb(&b1), ksq); 129 | 130 | while (b2) 131 | moveList = make_promotions(moveList, pop_lsb(&b2), ksq); 132 | 133 | while (b3) 134 | moveList = make_promotions(moveList, pop_lsb(&b3), ksq); 135 | } 136 | 137 | // Standard and en-passant captures 138 | if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) 139 | { 140 | Bitboard b1 = shift(pawnsNotOn7) & enemies; 141 | Bitboard b2 = shift(pawnsNotOn7) & enemies; 142 | 143 | while (b1) 144 | { 145 | Square to = pop_lsb(&b1); 146 | *moveList++ = make_move(to - UpRight, to); 147 | } 148 | 149 | while (b2) 150 | { 151 | Square to = pop_lsb(&b2); 152 | *moveList++ = make_move(to - UpLeft, to); 153 | } 154 | 155 | if (pos.ep_square() != SQ_NONE) 156 | { 157 | assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6)); 158 | 159 | // An en passant capture can be an evasion only if the checking piece 160 | // is the double pushed pawn and so is in the target. Otherwise this 161 | // is a discovery check and we are forced to do otherwise. 162 | if (Type == EVASIONS && !(target & (pos.ep_square() - Up))) 163 | return moveList; 164 | 165 | b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square()); 166 | 167 | assert(b1); 168 | 169 | while (b1) 170 | *moveList++ = make(pop_lsb(&b1), pos.ep_square()); 171 | } 172 | } 173 | 174 | return moveList; 175 | } 176 | 177 | 178 | template 179 | ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { 180 | 181 | static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); 182 | 183 | Bitboard bb = pos.pieces(Us, Pt); 184 | 185 | while (bb) { 186 | Square from = pop_lsb(&bb); 187 | 188 | if (Checks) 189 | { 190 | if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) 191 | && !(attacks_bb(from) & target & pos.check_squares(Pt))) 192 | continue; 193 | 194 | if (pos.blockers_for_king(~Us) & from) 195 | continue; 196 | } 197 | 198 | Bitboard b = attacks_bb(from, pos.pieces()) & target; 199 | 200 | if (Checks) 201 | b &= pos.check_squares(Pt); 202 | 203 | while (b) 204 | *moveList++ = make_move(from, pop_lsb(&b)); 205 | } 206 | 207 | return moveList; 208 | } 209 | 210 | 211 | template 212 | ExtMove* generate_all(const Position& pos, ExtMove* moveList) { 213 | constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations 214 | Bitboard target; 215 | 216 | switch (Type) 217 | { 218 | case CAPTURES: 219 | target = pos.pieces(~Us); 220 | break; 221 | case QUIETS: 222 | case QUIET_CHECKS: 223 | target = ~pos.pieces(); 224 | break; 225 | case EVASIONS: 226 | { 227 | Square checksq = lsb(pos.checkers()); 228 | target = between_bb(pos.square(Us), checksq) | checksq; 229 | break; 230 | } 231 | case NON_EVASIONS: 232 | target = ~pos.pieces(Us); 233 | break; 234 | default: 235 | static_assert(true, "Unsupported type in generate_all()"); 236 | } 237 | 238 | moveList = generate_pawn_moves(pos, moveList, target); 239 | moveList = generate_moves(pos, moveList, target); 240 | moveList = generate_moves(pos, moveList, target); 241 | moveList = generate_moves(pos, moveList, target); 242 | moveList = generate_moves(pos, moveList, target); 243 | 244 | if (Type != QUIET_CHECKS && Type != EVASIONS) 245 | { 246 | Square ksq = pos.square(Us); 247 | Bitboard b = attacks_bb(ksq) & target; 248 | while (b) 249 | *moveList++ = make_move(ksq, pop_lsb(&b)); 250 | 251 | if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING)) 252 | for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) 253 | if (!pos.castling_impeded(cr) && pos.can_castle(cr)) 254 | *moveList++ = make(ksq, pos.castling_rook_square(cr)); 255 | } 256 | 257 | return moveList; 258 | } 259 | 260 | } // namespace 261 | 262 | 263 | /// Generates all pseudo-legal captures plus queen and checking knight promotions 264 | /// Generates all pseudo-legal non-captures and underpromotions(except checking knight) 265 | /// Generates all pseudo-legal captures and non-captures 266 | /// 267 | /// Returns a pointer to the end of the move list. 268 | 269 | template 270 | ExtMove* generate(const Position& pos, ExtMove* moveList) { 271 | 272 | static_assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS, "Unsupported type in generate()"); 273 | assert(!pos.checkers()); 274 | 275 | Color us = pos.side_to_move(); 276 | 277 | return us == WHITE ? generate_all(pos, moveList) 278 | : generate_all(pos, moveList); 279 | } 280 | 281 | // Explicit template instantiations 282 | template ExtMove* generate(const Position&, ExtMove*); 283 | template ExtMove* generate(const Position&, ExtMove*); 284 | template ExtMove* generate(const Position&, ExtMove*); 285 | 286 | 287 | /// generate generates all pseudo-legal non-captures. 288 | /// Returns a pointer to the end of the move list. 289 | template<> 290 | ExtMove* generate(const Position& pos, ExtMove* moveList) { 291 | 292 | assert(!pos.checkers()); 293 | 294 | Color us = pos.side_to_move(); 295 | Bitboard dc = pos.blockers_for_king(~us) & pos.pieces(us) & ~pos.pieces(PAWN); 296 | 297 | while (dc) 298 | { 299 | Square from = pop_lsb(&dc); 300 | PieceType pt = type_of(pos.piece_on(from)); 301 | 302 | Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces(); 303 | 304 | if (pt == KING) 305 | b &= ~attacks_bb(pos.square(~us)); 306 | 307 | while (b) 308 | *moveList++ = make_move(from, pop_lsb(&b)); 309 | } 310 | 311 | return us == WHITE ? generate_all(pos, moveList) 312 | : generate_all(pos, moveList); 313 | } 314 | 315 | 316 | /// generate generates all pseudo-legal check evasions when the side 317 | /// to move is in check. Returns a pointer to the end of the move list. 318 | template<> 319 | ExtMove* generate(const Position& pos, ExtMove* moveList) { 320 | 321 | assert(pos.checkers()); 322 | 323 | Color us = pos.side_to_move(); 324 | Square ksq = pos.square(us); 325 | Bitboard sliderAttacks = 0; 326 | Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN); 327 | 328 | // Find all the squares attacked by slider checkers. We will remove them from 329 | // the king evasions in order to skip known illegal moves, which avoids any 330 | // useless legality checks later on. 331 | while (sliders) 332 | sliderAttacks |= line_bb(ksq, pop_lsb(&sliders)) & ~pos.checkers(); 333 | 334 | // Generate evasions for king, capture and non capture moves 335 | Bitboard b = attacks_bb(ksq) & ~pos.pieces(us) & ~sliderAttacks; 336 | while (b) 337 | *moveList++ = make_move(ksq, pop_lsb(&b)); 338 | 339 | if (more_than_one(pos.checkers())) 340 | return moveList; // Double check, only a king move can save the day 341 | 342 | // Generate blocking evasions or captures of the checking piece 343 | return us == WHITE ? generate_all(pos, moveList) 344 | : generate_all(pos, moveList); 345 | } 346 | 347 | 348 | /// generate generates all the legal moves in the given position 349 | 350 | template<> 351 | ExtMove* generate(const Position& pos, ExtMove* moveList) { 352 | 353 | Color us = pos.side_to_move(); 354 | Bitboard pinned = pos.blockers_for_king(us) & pos.pieces(us); 355 | Square ksq = pos.square(us); 356 | ExtMove* cur = moveList; 357 | 358 | moveList = pos.checkers() ? generate(pos, moveList) 359 | : generate(pos, moveList); 360 | while (cur != moveList) 361 | if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT) 362 | && !pos.legal(*cur)) 363 | *cur = (--moveList)->move; 364 | else 365 | ++cur; 366 | 367 | return moveList; 368 | } 369 | --------------------------------------------------------------------------------