mirror of
https://github.com/sockspls/badfish
synced 2025-04-30 00:33:09 +00:00
Merge branch 'master' into clusterMergeMaster6
This commit is contained in:
commit
0fd0e4e849
27 changed files with 344 additions and 293 deletions
28
Readme.md
28
Readme.md
|
@ -1,7 +1,7 @@
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
[](https://travis-ci.org/official-stockfish/Stockfish)
|
[](https://travis-ci.org/official-stockfish/Stockfish)
|
||||||
[](https://ci.appveyor.com/project/mcostalba/stockfish)
|
[](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master)
|
||||||
|
|
||||||
[Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine
|
[Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine
|
||||||
derived from Glaurung 2.1. It is not a complete chess program and requires a
|
derived from Glaurung 2.1. It is not a complete chess program and requires a
|
||||||
|
@ -34,11 +34,11 @@ Currently, Stockfish has the following UCI options:
|
||||||
A positive value for contempt favors middle game positions and avoids draws.
|
A positive value for contempt favors middle game positions and avoids draws.
|
||||||
|
|
||||||
* #### Analysis Contempt
|
* #### Analysis Contempt
|
||||||
By default, contempt is set to prefer the side to move. Set this option to "White"
|
By default, contempt is set to prefer the side to move. Set this option to "White"
|
||||||
or "Black" to analyse with contempt for that side, or "Off" to disable contempt.
|
or "Black" to analyse with contempt for that side, or "Off" to disable contempt.
|
||||||
|
|
||||||
* #### Threads
|
* #### Threads
|
||||||
The number of CPU threads used for searching a position. For best performance, set
|
The number of CPU threads used for searching a position. For best performance, set
|
||||||
this equal to the number of CPU cores available.
|
this equal to the number of CPU cores available.
|
||||||
|
|
||||||
* #### Hash
|
* #### Hash
|
||||||
|
@ -58,18 +58,18 @@ Currently, Stockfish has the following UCI options:
|
||||||
Lower the Skill Level in order to make Stockfish play weaker.
|
Lower the Skill Level in order to make Stockfish play weaker.
|
||||||
|
|
||||||
* #### Move Overhead
|
* #### Move Overhead
|
||||||
Assume a time delay of x ms due to network and GUI overheads. This is useful to
|
Assume a time delay of x ms due to network and GUI overheads. This is useful to
|
||||||
avoid losses on time in those cases.
|
avoid losses on time in those cases.
|
||||||
|
|
||||||
* #### Minimum Thinking Time
|
* #### Minimum Thinking Time
|
||||||
Search for at least x ms per move.
|
Search for at least x ms per move.
|
||||||
|
|
||||||
* #### Slow Mover
|
* #### Slow Mover
|
||||||
Lower values will make Stockfish take less time in games, higher values will
|
Lower values will make Stockfish take less time in games, higher values will
|
||||||
make it think longer.
|
make it think longer.
|
||||||
|
|
||||||
* #### nodestime
|
* #### nodestime
|
||||||
Tells the engine to use nodes searched instead of wall time to account for
|
Tells the engine to use nodes searched instead of wall time to account for
|
||||||
elapsed time. Useful for engine testing.
|
elapsed time. Useful for engine testing.
|
||||||
|
|
||||||
* #### UCI_Chess960
|
* #### UCI_Chess960
|
||||||
|
@ -79,13 +79,13 @@ Currently, Stockfish has the following UCI options:
|
||||||
An option handled by your GUI.
|
An option handled by your GUI.
|
||||||
|
|
||||||
* #### SyzygyPath
|
* #### SyzygyPath
|
||||||
Path to the folders/directories storing the Syzygy tablebase files. Multiple
|
Path to the folders/directories storing the Syzygy tablebase files. Multiple
|
||||||
directories are to be separated by ";" on Windows and by ":" on Unix-based
|
directories are to be separated by ";" on Windows and by ":" on Unix-based
|
||||||
operating systems. Do not use spaces around the ";" or ":".
|
operating systems. Do not use spaces around the ";" or ":".
|
||||||
|
|
||||||
Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6`
|
Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6`
|
||||||
|
|
||||||
It is recommended to store .rtbw files on an SSD. There is no loss in storing
|
It is recommended to store .rtbw files on an SSD. There is no loss in storing
|
||||||
the .rtbz files on a regular HD. It is recommended to verify all md5 checksums
|
the .rtbz files on a regular HD. It is recommended to verify all md5 checksums
|
||||||
of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will
|
of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will
|
||||||
lead to engine crashes.
|
lead to engine crashes.
|
||||||
|
@ -177,7 +177,7 @@ community effort. There are a few ways to help contribute to its growth.
|
||||||
### Donating hardware
|
### Donating hardware
|
||||||
|
|
||||||
Improving Stockfish requires a massive amount of testing. You can donate
|
Improving Stockfish requires a massive amount of testing. You can donate
|
||||||
your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker)
|
your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker)
|
||||||
and view the current tests on [Fishtest](http://tests.stockfishchess.org/tests).
|
and view the current tests on [Fishtest](http://tests.stockfishchess.org/tests).
|
||||||
|
|
||||||
### Improving the code
|
### Improving the code
|
||||||
|
@ -193,7 +193,7 @@ generic rather than being focused on Stockfish's precise implementation.
|
||||||
Nevertheless, a helpful resource.
|
Nevertheless, a helpful resource.
|
||||||
|
|
||||||
* The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish).
|
* The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish).
|
||||||
Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking)
|
Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking)
|
||||||
group and engine testing is done on [Fishtest](http://tests.stockfishchess.org/tests).
|
group and engine testing is done on [Fishtest](http://tests.stockfishchess.org/tests).
|
||||||
If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test)
|
If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test)
|
||||||
first, where the basics of Stockfish development are explained.
|
first, where the basics of Stockfish development are explained.
|
||||||
|
|
|
@ -139,9 +139,9 @@ vector<string> setup_bench(const Position& current, istream& is) {
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
list.emplace_back("ucinewgame");
|
|
||||||
list.emplace_back("setoption name Threads value " + threads);
|
list.emplace_back("setoption name Threads value " + threads);
|
||||||
list.emplace_back("setoption name Hash value " + ttSize);
|
list.emplace_back("setoption name Hash value " + ttSize);
|
||||||
|
list.emplace_back("ucinewgame");
|
||||||
|
|
||||||
for (const string& fen : fens)
|
for (const string& fen : fens)
|
||||||
if (fen.find("setoption") != string::npos)
|
if (fen.find("setoption") != string::npos)
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// There are 24 possible pawn squares: the first 4 files and ranks from 2 to 7
|
// There are 24 possible pawn squares: files A to D and ranks from 2 to 7.
|
||||||
|
// Positions with the pawn on files E to H will be mirrored before probing.
|
||||||
constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608
|
constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608
|
||||||
|
|
||||||
// Each uint32_t stores results of 32 positions, one per bit
|
// Each uint32_t stores results of 32 positions, one per bit
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <bitset>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <bitset>
|
||||||
|
|
||||||
#include "bitboard.h"
|
#include "bitboard.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
@ -27,16 +27,10 @@
|
||||||
uint8_t PopCnt16[1 << 16];
|
uint8_t PopCnt16[1 << 16];
|
||||||
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
|
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||||
|
|
||||||
|
Bitboard SquareBB[SQUARE_NB];
|
||||||
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
||||||
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||||
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||||
Bitboard SquareBB[SQUARE_NB];
|
|
||||||
|
|
||||||
Bitboard KingFlank[FILE_NB] = {
|
|
||||||
QueenSide ^ FileDBB, QueenSide, QueenSide,
|
|
||||||
CenterFiles, CenterFiles,
|
|
||||||
KingSide, KingSide, KingSide ^ FileEBB
|
|
||||||
};
|
|
||||||
|
|
||||||
Magic RookMagics[SQUARE_NB];
|
Magic RookMagics[SQUARE_NB];
|
||||||
Magic BishopMagics[SQUARE_NB];
|
Magic BishopMagics[SQUARE_NB];
|
||||||
|
|
|
@ -65,14 +65,19 @@ constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
|
||||||
constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
|
constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
|
||||||
constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);
|
constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);
|
||||||
|
|
||||||
|
constexpr Bitboard KingFlank[FILE_NB] = {
|
||||||
|
QueenSide ^ FileDBB, QueenSide, QueenSide,
|
||||||
|
CenterFiles, CenterFiles,
|
||||||
|
KingSide, KingSide, KingSide ^ FileEBB
|
||||||
|
};
|
||||||
|
|
||||||
extern uint8_t PopCnt16[1 << 16];
|
extern uint8_t PopCnt16[1 << 16];
|
||||||
extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
|
extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||||
|
|
||||||
|
extern Bitboard SquareBB[SQUARE_NB];
|
||||||
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
||||||
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||||
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||||
extern Bitboard KingFlank[FILE_NB];
|
|
||||||
extern Bitboard SquareBB[SQUARE_NB];
|
|
||||||
|
|
||||||
|
|
||||||
/// Magic holds all magic bitboards relevant data for a single square
|
/// Magic holds all magic bitboards relevant data for a single square
|
||||||
|
@ -148,6 +153,7 @@ inline Bitboard file_bb(Square s) {
|
||||||
template<Direction D>
|
template<Direction D>
|
||||||
constexpr Bitboard shift(Bitboard b) {
|
constexpr Bitboard shift(Bitboard b) {
|
||||||
return D == NORTH ? b << 8 : D == SOUTH ? b >> 8
|
return D == NORTH ? b << 8 : D == SOUTH ? b >> 8
|
||||||
|
: D == NORTH+NORTH? b <<16 : D == SOUTH+SOUTH? b >>16
|
||||||
: D == EAST ? (b & ~FileHBB) << 1 : D == WEST ? (b & ~FileABB) >> 1
|
: D == EAST ? (b & ~FileHBB) << 1 : D == WEST ? (b & ~FileABB) >> 1
|
||||||
: D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7
|
: D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7
|
||||||
: D == SOUTH_EAST ? (b & ~FileHBB) >> 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9
|
: D == SOUTH_EAST ? (b & ~FileHBB) >> 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9
|
||||||
|
@ -178,8 +184,8 @@ constexpr Bitboard pawn_double_attacks_bb(Bitboard b) {
|
||||||
/// adjacent_files_bb() returns a bitboard representing all the squares on the
|
/// adjacent_files_bb() returns a bitboard representing all the squares on the
|
||||||
/// adjacent files of the given one.
|
/// adjacent files of the given one.
|
||||||
|
|
||||||
inline Bitboard adjacent_files_bb(File f) {
|
inline Bitboard adjacent_files_bb(Square s) {
|
||||||
return shift<EAST>(file_bb(f)) | shift<WEST>(file_bb(f));
|
return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -215,7 +221,7 @@ inline Bitboard forward_file_bb(Color c, Square s) {
|
||||||
/// starting from the given square.
|
/// starting from the given square.
|
||||||
|
|
||||||
inline Bitboard pawn_attack_span(Color c, Square s) {
|
inline Bitboard pawn_attack_span(Color c, Square s) {
|
||||||
return forward_ranks_bb(c, s) & adjacent_files_bb(file_of(s));
|
return forward_ranks_bb(c, s) & adjacent_files_bb(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -223,7 +229,7 @@ inline Bitboard pawn_attack_span(Color c, Square s) {
|
||||||
/// the given color and on the given square is a passed pawn.
|
/// the given color and on the given square is a passed pawn.
|
||||||
|
|
||||||
inline Bitboard passed_pawn_span(Color c, Square s) {
|
inline Bitboard passed_pawn_span(Color c, Square s) {
|
||||||
return forward_ranks_bb(c, s) & (adjacent_files_bb(file_of(s)) | file_bb(s));
|
return forward_ranks_bb(c, s) & (adjacent_files_bb(s) | file_bb(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -370,10 +376,7 @@ inline Square pop_lsb(Bitboard* b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// frontmost_sq() and backmost_sq() return the square corresponding to the
|
/// frontmost_sq() returns the most advanced square for the given color
|
||||||
/// most/least advanced bit relative to the given color.
|
|
||||||
|
|
||||||
inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); }
|
inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); }
|
||||||
inline Square backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); }
|
|
||||||
|
|
||||||
#endif // #ifndef BITBOARD_H_INCLUDED
|
#endif // #ifndef BITBOARD_H_INCLUDED
|
||||||
|
|
|
@ -82,6 +82,34 @@ namespace {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
namespace Endgames {
|
||||||
|
|
||||||
|
std::pair<Map<Value>, Map<ScaleFactor>> maps;
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
|
||||||
|
add<KPK>("KPK");
|
||||||
|
add<KNNK>("KNNK");
|
||||||
|
add<KBNK>("KBNK");
|
||||||
|
add<KRKP>("KRKP");
|
||||||
|
add<KRKB>("KRKB");
|
||||||
|
add<KRKN>("KRKN");
|
||||||
|
add<KQKP>("KQKP");
|
||||||
|
add<KQKR>("KQKR");
|
||||||
|
add<KNNKP>("KNNKP");
|
||||||
|
|
||||||
|
add<KNPK>("KNPK");
|
||||||
|
add<KNPKB>("KNPKB");
|
||||||
|
add<KRPKR>("KRPKR");
|
||||||
|
add<KRPKB>("KRPKB");
|
||||||
|
add<KBPKB>("KBPKB");
|
||||||
|
add<KBPKN>("KBPKN");
|
||||||
|
add<KBPPKB>("KBPPKB");
|
||||||
|
add<KRPPKRP>("KRPPKRP");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Mate with KX vs K. This function is used to evaluate positions with
|
/// Mate with KX vs K. This function is used to evaluate positions with
|
||||||
/// king and plenty of material vs a lone king. It simply gives the
|
/// king and plenty of material vs a lone king. It simply gives the
|
||||||
/// attacking side a bonus for driving the defending king towards the edge
|
/// attacking side a bonus for driving the defending king towards the edge
|
||||||
|
@ -337,7 +365,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
|
||||||
&& pos.count<PAWN>(weakSide) >= 1)
|
&& pos.count<PAWN>(weakSide) >= 1)
|
||||||
{
|
{
|
||||||
// Get weakSide pawn that is closest to the home rank
|
// Get weakSide pawn that is closest to the home rank
|
||||||
Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN));
|
Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN));
|
||||||
|
|
||||||
Square strongKingSq = pos.square<KING>(strongSide);
|
Square strongKingSq = pos.square<KING>(strongSide);
|
||||||
Square weakKingSq = pos.square<KING>(weakSide);
|
Square weakKingSq = pos.square<KING>(weakSide);
|
||||||
|
|
|
@ -91,15 +91,19 @@ struct Endgame : public EndgameBase<T> {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// The Endgames class stores the pointers to endgame evaluation and scaling
|
/// The Endgames namespace handles the pointers to endgame evaluation and scaling
|
||||||
/// base objects in two std::map. We use polymorphism to invoke the actual
|
/// base objects in two std::map. We use polymorphism to invoke the actual
|
||||||
/// endgame function by calling its virtual operator().
|
/// endgame function by calling its virtual operator().
|
||||||
|
|
||||||
class Endgames {
|
namespace Endgames {
|
||||||
|
|
||||||
template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>;
|
template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>;
|
||||||
template<typename T> using Map = std::map<Key, Ptr<T>>;
|
template<typename T> using Map = std::map<Key, Ptr<T>>;
|
||||||
|
|
||||||
|
extern std::pair<Map<Value>, Map<ScaleFactor>> maps;
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Map<T>& map() {
|
Map<T>& map() {
|
||||||
return std::get<std::is_same<T, ScaleFactor>::value>(maps);
|
return std::get<std::is_same<T, ScaleFactor>::value>(maps);
|
||||||
|
@ -113,35 +117,10 @@ class Endgames {
|
||||||
map<T>()[Position().set(code, BLACK, &st).material_key()] = Ptr<T>(new Endgame<E>(BLACK));
|
map<T>()[Position().set(code, BLACK, &st).material_key()] = Ptr<T>(new Endgame<E>(BLACK));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Map<Value>, Map<ScaleFactor>> maps;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Endgames() {
|
|
||||||
|
|
||||||
add<KPK>("KPK");
|
|
||||||
add<KNNK>("KNNK");
|
|
||||||
add<KBNK>("KBNK");
|
|
||||||
add<KRKP>("KRKP");
|
|
||||||
add<KRKB>("KRKB");
|
|
||||||
add<KRKN>("KRKN");
|
|
||||||
add<KQKP>("KQKP");
|
|
||||||
add<KQKR>("KQKR");
|
|
||||||
add<KNNKP>("KNNKP");
|
|
||||||
|
|
||||||
add<KNPK>("KNPK");
|
|
||||||
add<KNPKB>("KNPKB");
|
|
||||||
add<KRPKR>("KRPKR");
|
|
||||||
add<KRPKB>("KRPKB");
|
|
||||||
add<KBPKB>("KBPKB");
|
|
||||||
add<KBPKN>("KBPKN");
|
|
||||||
add<KBPPKB>("KBPPKB");
|
|
||||||
add<KRPPKRP>("KRPPKRP");
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
const EndgameBase<T>* probe(Key key) {
|
const EndgameBase<T>* probe(Key key) {
|
||||||
return map<T>().count(key) ? map<T>()[key].get() : nullptr;
|
return map<T>().count(key) ? map<T>()[key].get() : nullptr;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
#endif // #ifndef ENDGAME_H_INCLUDED
|
#endif // #ifndef ENDGAME_H_INCLUDED
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring> // For std::memset
|
#include <cstring> // For std::memset
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
@ -73,7 +74,7 @@ using namespace Trace;
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Threshold for lazy and space evaluation
|
// Threshold for lazy and space evaluation
|
||||||
constexpr Value LazyThreshold = Value(1500);
|
constexpr Value LazyThreshold = Value(1400);
|
||||||
constexpr Value SpaceThreshold = Value(12222);
|
constexpr Value SpaceThreshold = Value(12222);
|
||||||
|
|
||||||
// KingAttackWeights[PieceType] contains king attack weights by piece type
|
// KingAttackWeights[PieceType] contains king attack weights by piece type
|
||||||
|
@ -132,6 +133,7 @@ namespace {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Assorted bonuses and penalties
|
// Assorted bonuses and penalties
|
||||||
|
constexpr Score AttacksOnSpaceArea = S( 4, 0);
|
||||||
constexpr Score BishopPawns = S( 3, 7);
|
constexpr Score BishopPawns = S( 3, 7);
|
||||||
constexpr Score CorneredBishop = S( 50, 50);
|
constexpr Score CorneredBishop = S( 50, 50);
|
||||||
constexpr Score FlankAttacks = S( 8, 0);
|
constexpr Score FlankAttacks = S( 8, 0);
|
||||||
|
@ -140,7 +142,7 @@ namespace {
|
||||||
constexpr Score KnightOnQueen = S( 16, 12);
|
constexpr Score KnightOnQueen = S( 16, 12);
|
||||||
constexpr Score LongDiagonalBishop = S( 45, 0);
|
constexpr Score LongDiagonalBishop = S( 45, 0);
|
||||||
constexpr Score MinorBehindPawn = S( 18, 3);
|
constexpr Score MinorBehindPawn = S( 18, 3);
|
||||||
constexpr Score Outpost = S( 9, 3);
|
constexpr Score Outpost = S( 36, 12);
|
||||||
constexpr Score PawnlessFlank = S( 17, 95);
|
constexpr Score PawnlessFlank = S( 17, 95);
|
||||||
constexpr Score RestrictedPiece = S( 7, 7);
|
constexpr Score RestrictedPiece = S( 7, 7);
|
||||||
constexpr Score RookOnPawn = S( 10, 32);
|
constexpr Score RookOnPawn = S( 10, 32);
|
||||||
|
@ -151,7 +153,6 @@ namespace {
|
||||||
constexpr Score ThreatBySafePawn = S(173, 94);
|
constexpr Score ThreatBySafePawn = S(173, 94);
|
||||||
constexpr Score TrappedRook = S( 47, 4);
|
constexpr Score TrappedRook = S( 47, 4);
|
||||||
constexpr Score WeakQueen = S( 49, 15);
|
constexpr Score WeakQueen = S( 49, 15);
|
||||||
constexpr Score WeakUnopposedPawn = S( 12, 23);
|
|
||||||
|
|
||||||
#undef S
|
#undef S
|
||||||
|
|
||||||
|
@ -186,15 +187,12 @@ namespace {
|
||||||
// is also calculated is ALL_PIECES.
|
// is also calculated is ALL_PIECES.
|
||||||
Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
|
Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
|
||||||
|
|
||||||
// attackedBy2[color] are the squares attacked by 2 pieces of a given color,
|
// attackedBy2[color] are the squares attacked by at least 2 units of a given
|
||||||
// possibly via x-ray or by one pawn and one piece. Diagonal x-ray through
|
// color, including x-rays. But diagonal x-rays through pawns are not computed.
|
||||||
// pawn or squares attacked by 2 pawns are not explicitly added.
|
|
||||||
Bitboard attackedBy2[COLOR_NB];
|
Bitboard attackedBy2[COLOR_NB];
|
||||||
|
|
||||||
// kingRing[color] are the squares adjacent to the king, plus (only for a
|
// kingRing[color] are the squares adjacent to the king plus some other
|
||||||
// king on its first rank) the squares two ranks in front. For instance,
|
// very near squares, depending on king position.
|
||||||
// if black's king is on g8, kingRing[BLACK] is f8, h8, f7, g7, h7, f6, g6
|
|
||||||
// and h6.
|
|
||||||
Bitboard kingRing[COLOR_NB];
|
Bitboard kingRing[COLOR_NB];
|
||||||
|
|
||||||
// kingAttackersCount[color] is the number of pieces of the given color
|
// kingAttackersCount[color] is the number of pieces of the given color
|
||||||
|
@ -241,8 +239,7 @@ namespace {
|
||||||
attackedBy[Us][KING] = pos.attacks_from<KING>(ksq);
|
attackedBy[Us][KING] = pos.attacks_from<KING>(ksq);
|
||||||
attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
|
attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
|
||||||
attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
|
attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
|
||||||
attackedBy2[Us] = (attackedBy[Us][KING] & attackedBy[Us][PAWN])
|
attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
|
||||||
| dblAttackByPawn;
|
|
||||||
|
|
||||||
// Init our king safety tables
|
// Init our king safety tables
|
||||||
kingRing[Us] = attackedBy[Us][KING];
|
kingRing[Us] = attackedBy[Us][KING];
|
||||||
|
@ -306,14 +303,12 @@ namespace {
|
||||||
if (Pt == BISHOP || Pt == KNIGHT)
|
if (Pt == BISHOP || Pt == KNIGHT)
|
||||||
{
|
{
|
||||||
// Bonus if piece is on an outpost square or can reach one
|
// Bonus if piece is on an outpost square or can reach one
|
||||||
bb = OutpostRanks & ~pe->pawn_attacks_span(Them);
|
bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them);
|
||||||
if (bb & s)
|
if (bb & s)
|
||||||
score += Outpost * (Pt == KNIGHT ? 4 : 2)
|
score += Outpost * (Pt == KNIGHT ? 2 : 1);
|
||||||
* (1 + bool(attackedBy[Us][PAWN] & s));
|
|
||||||
|
|
||||||
else if (bb &= b & ~pos.pieces(Us))
|
else if (bb & b & ~pos.pieces(Us))
|
||||||
score += Outpost * (Pt == KNIGHT ? 2 : 1)
|
score += Outpost / (Pt == KNIGHT ? 1 : 2);
|
||||||
* (1 + bool(attackedBy[Us][PAWN] & bb));
|
|
||||||
|
|
||||||
// Knight and Bishop bonus for being right behind a pawn
|
// Knight and Bishop bonus for being right behind a pawn
|
||||||
if (shift<Down>(pos.pieces(PAWN)) & s)
|
if (shift<Down>(pos.pieces(PAWN)) & s)
|
||||||
|
@ -358,8 +353,8 @@ namespace {
|
||||||
score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
|
score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
|
||||||
|
|
||||||
// Bonus for rook on an open or semi-open file
|
// Bonus for rook on an open or semi-open file
|
||||||
if (pos.semiopen_file(Us, file_of(s)))
|
if (pos.is_on_semiopen_file(Us, s))
|
||||||
score += RookOnFile[bool(pos.semiopen_file(Them, file_of(s)))];
|
score += RookOnFile[bool(pos.is_on_semiopen_file(Them, s))];
|
||||||
|
|
||||||
// Penalty when trapped by the king, even more if the king cannot castle
|
// Penalty when trapped by the king, even more if the king cannot castle
|
||||||
else if (mob <= 3)
|
else if (mob <= 3)
|
||||||
|
@ -558,10 +553,6 @@ namespace {
|
||||||
|
|
||||||
score += RestrictedPiece * popcount(b);
|
score += RestrictedPiece * popcount(b);
|
||||||
|
|
||||||
// Bonus for enemy unopposed weak pawns
|
|
||||||
if (pos.pieces(Us, ROOK, QUEEN))
|
|
||||||
score += WeakUnopposedPawn * pe->weak_unopposed(Them);
|
|
||||||
|
|
||||||
// Find squares where our pawns can push on the next move
|
// Find squares where our pawns can push on the next move
|
||||||
b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces();
|
b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces();
|
||||||
b |= shift<Up>(b & TRank3BB) & ~pos.pieces();
|
b |= shift<Up>(b & TRank3BB) & ~pos.pieces();
|
||||||
|
@ -645,12 +636,10 @@ namespace {
|
||||||
// If the pawn is free to advance, then increase the bonus
|
// If the pawn is free to advance, then increase the bonus
|
||||||
if (pos.empty(blockSq))
|
if (pos.empty(blockSq))
|
||||||
{
|
{
|
||||||
// If there is a rook or queen attacking/defending the pawn from behind,
|
defendedSquares = squaresToQueen = forward_file_bb(Us, s);
|
||||||
// consider all the squaresToQueen. Otherwise consider only the squares
|
unsafeSquares = passed_pawn_span(Us, s);
|
||||||
// in the pawn's path attacked or occupied by the enemy.
|
|
||||||
defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s);
|
|
||||||
|
|
||||||
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s);
|
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
|
||||||
|
|
||||||
if (!(pos.pieces(Us) & bb))
|
if (!(pos.pieces(Us) & bb))
|
||||||
defendedSquares &= attackedBy[Us][ALL_PIECES];
|
defendedSquares &= attackedBy[Us][ALL_PIECES];
|
||||||
|
@ -658,21 +647,21 @@ namespace {
|
||||||
if (!(pos.pieces(Them) & bb))
|
if (!(pos.pieces(Them) & bb))
|
||||||
unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
|
unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
|
||||||
|
|
||||||
// If there aren't any enemy attacks, assign a big bonus. Otherwise
|
// If there are no enemy attacks on passed pawn span, assign a big bonus.
|
||||||
// assign a smaller bonus if the block square isn't attacked.
|
// Otherwise assign a smaller bonus if the path to queen is not attacked
|
||||||
int k = !unsafeSquares ? 20 : !(unsafeSquares & blockSq) ? 9 : 0;
|
// and even smaller bonus if it is attacked but block square is not.
|
||||||
|
int k = !unsafeSquares ? 35 :
|
||||||
|
!(unsafeSquares & squaresToQueen) ? 20 :
|
||||||
|
!(unsafeSquares & blockSq) ? 9 :
|
||||||
|
0 ;
|
||||||
|
|
||||||
// If the path to the queen is fully defended, assign a big bonus.
|
// Assign a larger bonus if the block square is defended.
|
||||||
// Otherwise assign a smaller bonus if the block square is defended.
|
if (defendedSquares & blockSq)
|
||||||
if (defendedSquares == squaresToQueen)
|
k += 5;
|
||||||
k += 6;
|
|
||||||
|
|
||||||
else if (defendedSquares & blockSq)
|
|
||||||
k += 4;
|
|
||||||
|
|
||||||
bonus += make_score(k * w, k * w);
|
bonus += make_score(k * w, k * w);
|
||||||
}
|
}
|
||||||
} // rank > RANK_3
|
} // r > RANK_3
|
||||||
|
|
||||||
// Scale down bonus for candidate passers which need more than one
|
// Scale down bonus for candidate passers which need more than one
|
||||||
// pawn push to become passed, or have a pawn in front of them.
|
// pawn push to become passed, or have a pawn in front of them.
|
||||||
|
@ -717,14 +706,14 @@ namespace {
|
||||||
// Find all squares which are at most three squares behind some friendly pawn
|
// Find all squares which are at most three squares behind some friendly pawn
|
||||||
Bitboard behind = pos.pieces(Us, PAWN);
|
Bitboard behind = pos.pieces(Us, PAWN);
|
||||||
behind |= shift<Down>(behind);
|
behind |= shift<Down>(behind);
|
||||||
behind |= shift<Down>(shift<Down>(behind));
|
behind |= shift<Down+Down>(behind);
|
||||||
|
|
||||||
int bonus = popcount(safe) + popcount(behind & safe);
|
int bonus = popcount(safe) + popcount(behind & safe);
|
||||||
int weight = pos.count<ALL_PIECES>(Us)
|
int weight = pos.count<ALL_PIECES>(Us) - 1;
|
||||||
- (16 - pos.count<PAWN>()) / 4;
|
|
||||||
|
|
||||||
Score score = make_score(bonus * weight * weight / 16, 0);
|
Score score = make_score(bonus * weight * weight / 16, 0);
|
||||||
|
|
||||||
|
score -= AttacksOnSpaceArea * popcount(attackedBy[Them][ALL_PIECES] & behind & safe);
|
||||||
|
|
||||||
if (T)
|
if (T)
|
||||||
Trace::add(SPACE, Us, score);
|
Trace::add(SPACE, Us, score);
|
||||||
|
|
||||||
|
@ -777,8 +766,7 @@ namespace {
|
||||||
if (sf == SCALE_FACTOR_NORMAL)
|
if (sf == SCALE_FACTOR_NORMAL)
|
||||||
{
|
{
|
||||||
if ( pos.opposite_bishops()
|
if ( pos.opposite_bishops()
|
||||||
&& pos.non_pawn_material(WHITE) == BishopValueMg
|
&& pos.non_pawn_material() == 2 * BishopValueMg)
|
||||||
&& pos.non_pawn_material(BLACK) == BishopValueMg)
|
|
||||||
sf = 16 + 4 * pe->passed_count();
|
sf = 16 + 4 * pe->passed_count();
|
||||||
else
|
else
|
||||||
sf = std::min(40 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide), sf);
|
sf = std::min(40 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide), sf);
|
||||||
|
@ -817,7 +805,7 @@ namespace {
|
||||||
|
|
||||||
// Early exit if score is high
|
// Early exit if score is high
|
||||||
Value v = (mg_value(score) + eg_value(score)) / 2;
|
Value v = (mg_value(score) + eg_value(score)) / 2;
|
||||||
if (abs(v) > LazyThreshold)
|
if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64)
|
||||||
return pos.side_to_move() == WHITE ? v : -v;
|
return pos.side_to_move() == WHITE ? v : -v;
|
||||||
|
|
||||||
// Main evaluation begins here
|
// Main evaluation begins here
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
#include "tt.h"
|
#include "tt.h"
|
||||||
#include "uci.h"
|
#include "uci.h"
|
||||||
|
#include "endgame.h"
|
||||||
#include "syzygy/tbprobe.h"
|
#include "syzygy/tbprobe.h"
|
||||||
|
|
||||||
namespace PSQT {
|
namespace PSQT {
|
||||||
|
@ -43,6 +44,7 @@ int main(int argc, char* argv[]) {
|
||||||
Bitboards::init();
|
Bitboards::init();
|
||||||
Position::init();
|
Position::init();
|
||||||
Bitbases::init();
|
Bitbases::init();
|
||||||
|
Endgames::init();
|
||||||
Search::init();
|
Search::init();
|
||||||
Threads.set(Options["Threads"]);
|
Threads.set(Options["Threads"]);
|
||||||
Search::clear(); // After threads are up
|
Search::clear(); // After threads are up
|
||||||
|
|
|
@ -137,7 +137,7 @@ Entry* probe(const Position& pos) {
|
||||||
// Let's look if we have a specialized evaluation function for this particular
|
// Let's look if we have a specialized evaluation function for this particular
|
||||||
// material configuration. Firstly we look for a fixed configuration one, then
|
// material configuration. Firstly we look for a fixed configuration one, then
|
||||||
// for a generic one if the previous search failed.
|
// for a generic one if the previous search failed.
|
||||||
if ((e->evaluationFunction = pos.this_thread()->endgames.probe<Value>(key)) != nullptr)
|
if ((e->evaluationFunction = Endgames::probe<Value>(key)) != nullptr)
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
for (Color c = WHITE; c <= BLACK; ++c)
|
for (Color c = WHITE; c <= BLACK; ++c)
|
||||||
|
@ -149,7 +149,7 @@ Entry* probe(const Position& pos) {
|
||||||
|
|
||||||
// OK, we didn't find any special evaluation function for the current material
|
// OK, we didn't find any special evaluation function for the current material
|
||||||
// configuration. Is there a suitable specialized scaling function?
|
// configuration. Is there a suitable specialized scaling function?
|
||||||
const auto* sf = pos.this_thread()->endgames.probe<ScaleFactor>(key);
|
const auto* sf = Endgames::probe<ScaleFactor>(key);
|
||||||
|
|
||||||
if (sf)
|
if (sf)
|
||||||
{
|
{
|
||||||
|
|
|
@ -145,7 +145,7 @@ const string engine_info(bool to_uci) {
|
||||||
|
|
||||||
|
|
||||||
/// Debug functions used mainly to collect run-time statistics
|
/// Debug functions used mainly to collect run-time statistics
|
||||||
static int64_t hits[2], means[2];
|
static std::atomic<int64_t> hits[2], means[2];
|
||||||
|
|
||||||
void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
|
void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
|
||||||
void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); }
|
void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); }
|
||||||
|
@ -210,12 +210,6 @@ void prefetch(void* addr) {
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void prefetch2(void* addr) {
|
|
||||||
|
|
||||||
prefetch(addr);
|
|
||||||
prefetch((uint8_t*)addr + 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace WinProcGroup {
|
namespace WinProcGroup {
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
|
|
||||||
const std::string engine_info(bool to_uci = false);
|
const std::string engine_info(bool to_uci = false);
|
||||||
void prefetch(void* addr);
|
void prefetch(void* addr);
|
||||||
void prefetch2(void* addr);
|
|
||||||
void start_logger(const std::string& fname);
|
void start_logger(const std::string& fname);
|
||||||
|
|
||||||
void dbg_hit_on(bool b);
|
void dbg_hit_on(bool b);
|
||||||
|
|
|
@ -200,11 +200,15 @@ top:
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
|
|
||||||
case QUIET_INIT:
|
case QUIET_INIT:
|
||||||
cur = endBadCaptures;
|
if (!skipQuiets)
|
||||||
endMoves = generate<QUIETS>(pos, cur);
|
{
|
||||||
|
cur = endBadCaptures;
|
||||||
|
endMoves = generate<QUIETS>(pos, cur);
|
||||||
|
|
||||||
|
score<QUIETS>();
|
||||||
|
partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY);
|
||||||
|
}
|
||||||
|
|
||||||
score<QUIETS>();
|
|
||||||
partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY);
|
|
||||||
++stage;
|
++stage;
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include "bitboard.h"
|
#include "bitboard.h"
|
||||||
|
@ -34,9 +35,11 @@ namespace {
|
||||||
constexpr Score Backward = S( 9, 24);
|
constexpr Score Backward = S( 9, 24);
|
||||||
constexpr Score Doubled = S(11, 56);
|
constexpr Score Doubled = S(11, 56);
|
||||||
constexpr Score Isolated = S( 5, 15);
|
constexpr Score Isolated = S( 5, 15);
|
||||||
|
constexpr Score WeakUnopposed = S( 13, 27);
|
||||||
|
constexpr Score Attacked2Unsupported = S( 0, 20);
|
||||||
|
|
||||||
// Connected pawn bonus
|
// Connected pawn bonus
|
||||||
constexpr int Connected[RANK_NB] = { 0, 13, 17, 24, 59, 96, 171 };
|
constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 };
|
||||||
|
|
||||||
// Strength of pawn shelter for our king by [distance from edge][rank].
|
// Strength of pawn shelter for our king by [distance from edge][rank].
|
||||||
// RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king.
|
// RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king.
|
||||||
|
@ -50,11 +53,12 @@ namespace {
|
||||||
// Danger of enemy pawns moving toward our king by [distance from edge][rank].
|
// Danger of enemy pawns moving toward our king by [distance from edge][rank].
|
||||||
// RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn
|
// RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn
|
||||||
// is behind our king.
|
// is behind our king.
|
||||||
|
// [0][1-2] accommodate opponent pawn on edge (likely blocked by our king)
|
||||||
constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = {
|
constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = {
|
||||||
{ V( 89), V(107), V(123), V(93), V(57), V( 45), V( 51) },
|
{ V( 89), V(-285), V(-185), V(93), V(57), V( 45), V( 51) },
|
||||||
{ V( 44), V(-18), V(123), V(46), V(39), V( -7), V( 23) },
|
{ V( 44), V( -18), V( 123), V(46), V(39), V( -7), V( 23) },
|
||||||
{ V( 4), V( 52), V(162), V(37), V( 7), V(-14), V( -2) },
|
{ V( 4), V( 52), V( 162), V(37), V( 7), V(-14), V( -2) },
|
||||||
{ V(-10), V(-14), V( 90), V(15), V( 2), V( -7), V(-16) }
|
{ V(-10), V( -14), V( 90), V(15), V( 2), V( -7), V(-16) }
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef S
|
#undef S
|
||||||
|
@ -76,16 +80,20 @@ namespace {
|
||||||
Bitboard ourPawns = pos.pieces( Us, PAWN);
|
Bitboard ourPawns = pos.pieces( Us, PAWN);
|
||||||
Bitboard theirPawns = pos.pieces(Them, PAWN);
|
Bitboard theirPawns = pos.pieces(Them, PAWN);
|
||||||
|
|
||||||
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0;
|
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0;
|
||||||
e->kingSquares[Us] = SQ_NONE;
|
e->kingSquares[Us] = SQ_NONE;
|
||||||
e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns);
|
e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns);
|
||||||
|
|
||||||
|
// Unsupported enemy pawns attacked twice by us
|
||||||
|
score += Attacked2Unsupported * popcount( theirPawns
|
||||||
|
& pawn_double_attacks_bb<Us>(ourPawns)
|
||||||
|
& ~pawn_attacks_bb<Them>(theirPawns));
|
||||||
|
|
||||||
// Loop through all pawns of the current color and score each pawn
|
// Loop through all pawns of the current color and score each pawn
|
||||||
while ((s = *pl++) != SQ_NONE)
|
while ((s = *pl++) != SQ_NONE)
|
||||||
{
|
{
|
||||||
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
||||||
|
|
||||||
File f = file_of(s);
|
|
||||||
Rank r = relative_rank(Us, s);
|
Rank r = relative_rank(Us, s);
|
||||||
|
|
||||||
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
|
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
|
||||||
|
@ -96,7 +104,7 @@ namespace {
|
||||||
lever = theirPawns & PawnAttacks[Us][s];
|
lever = theirPawns & PawnAttacks[Us][s];
|
||||||
leverPush = theirPawns & PawnAttacks[Us][s + Up];
|
leverPush = theirPawns & PawnAttacks[Us][s + Up];
|
||||||
doubled = ourPawns & (s - Up);
|
doubled = ourPawns & (s - Up);
|
||||||
neighbours = ourPawns & adjacent_files_bb(f);
|
neighbours = ourPawns & adjacent_files_bb(s);
|
||||||
phalanx = neighbours & rank_bb(s);
|
phalanx = neighbours & rank_bb(s);
|
||||||
support = neighbours & rank_bb(s - Up);
|
support = neighbours & rank_bb(s - Up);
|
||||||
|
|
||||||
|
@ -109,9 +117,8 @@ namespace {
|
||||||
// full attack info to evaluate them. Include also not passed pawns
|
// full attack info to evaluate them. Include also not passed pawns
|
||||||
// which could become passed after one or two pawn pushes when are
|
// which could become passed after one or two pawn pushes when are
|
||||||
// not attacked more times than defended.
|
// not attacked more times than defended.
|
||||||
if ( !(stoppers ^ lever ^ leverPush)
|
if ( !(stoppers ^ lever) ||
|
||||||
&& (support || !more_than_one(lever))
|
(!(stoppers ^ leverPush) && popcount(phalanx) >= popcount(leverPush)))
|
||||||
&& popcount(phalanx) >= popcount(leverPush))
|
|
||||||
e->passedPawns[Us] |= s;
|
e->passedPawns[Us] |= s;
|
||||||
|
|
||||||
else if (stoppers == square_bb(s + Up) && r >= RANK_5)
|
else if (stoppers == square_bb(s + Up) && r >= RANK_5)
|
||||||
|
@ -125,15 +132,16 @@ namespace {
|
||||||
// Score this pawn
|
// Score this pawn
|
||||||
if (support | phalanx)
|
if (support | phalanx)
|
||||||
{
|
{
|
||||||
int v = (phalanx ? 3 : 2) * Connected[r];
|
int v = Connected[r] * (phalanx ? 3 : 2) / (opposed ? 2 : 1)
|
||||||
v = 17 * popcount(support) + (v >> (opposed + 1));
|
+ 17 * popcount(support);
|
||||||
|
|
||||||
score += make_score(v, v * (r - 2) / 4);
|
score += make_score(v, v * (r - 2) / 4);
|
||||||
}
|
}
|
||||||
else if (!neighbours)
|
else if (!neighbours)
|
||||||
score -= Isolated, e->weakUnopposed[Us] += !opposed;
|
score -= Isolated + WeakUnopposed * int(!opposed);
|
||||||
|
|
||||||
else if (backward)
|
else if (backward)
|
||||||
score -= Backward, e->weakUnopposed[Us] += !opposed;
|
score -= Backward + WeakUnopposed * int(!opposed);
|
||||||
|
|
||||||
if (doubled && !support)
|
if (doubled && !support)
|
||||||
score -= Doubled;
|
score -= Doubled;
|
||||||
|
@ -171,35 +179,36 @@ Entry* probe(const Position& pos) {
|
||||||
/// penalty for a king, looking at the king file and the two closest files.
|
/// penalty for a king, looking at the king file and the two closest files.
|
||||||
|
|
||||||
template<Color Us>
|
template<Color Us>
|
||||||
Value Entry::evaluate_shelter(const Position& pos, Square ksq) {
|
void Entry::evaluate_shelter(const Position& pos, Square ksq, Score& shelter) {
|
||||||
|
|
||||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||||
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
|
|
||||||
constexpr Bitboard BlockRanks = (Us == WHITE ? Rank1BB | Rank2BB : Rank8BB | Rank7BB);
|
|
||||||
|
|
||||||
Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
|
Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
|
||||||
Bitboard ourPawns = b & pos.pieces(Us);
|
Bitboard ourPawns = b & pos.pieces(Us);
|
||||||
Bitboard theirPawns = b & pos.pieces(Them);
|
Bitboard theirPawns = b & pos.pieces(Them);
|
||||||
|
|
||||||
Value safety = (shift<Down>(theirPawns) & (FileABB | FileHBB) & BlockRanks & ksq) ?
|
Value bonus[] = { Value(5), Value(5) };
|
||||||
Value(374) : Value(5);
|
|
||||||
|
|
||||||
File center = clamp(file_of(ksq), FILE_B, FILE_G);
|
File center = clamp(file_of(ksq), FILE_B, FILE_G);
|
||||||
for (File f = File(center - 1); f <= File(center + 1); ++f)
|
for (File f = File(center - 1); f <= File(center + 1); ++f)
|
||||||
{
|
{
|
||||||
b = ourPawns & file_bb(f);
|
b = ourPawns & file_bb(f);
|
||||||
Rank ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
|
Rank ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
|
||||||
|
|
||||||
b = theirPawns & file_bb(f);
|
b = theirPawns & file_bb(f);
|
||||||
Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
|
Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
|
||||||
|
|
||||||
int d = std::min(f, ~f);
|
int d = std::min(f, ~f);
|
||||||
safety += ShelterStrength[d][ourRank];
|
bonus[MG] += ShelterStrength[d][ourRank];
|
||||||
safety -= (ourRank && (ourRank == theirRank - 1)) ? 66 * (theirRank == RANK_3)
|
|
||||||
: UnblockedStorm[d][theirRank];
|
if (ourRank && (ourRank == theirRank - 1))
|
||||||
|
bonus[MG] -= 82 * (theirRank == RANK_3), bonus[EG] -= 82 * (theirRank == RANK_3);
|
||||||
|
else
|
||||||
|
bonus[MG] -= UnblockedStorm[d][theirRank];
|
||||||
}
|
}
|
||||||
|
|
||||||
return safety;
|
if (bonus[MG] > mg_value(shelter))
|
||||||
|
shelter = make_score(bonus[MG], bonus[EG]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -222,16 +231,17 @@ Score Entry::do_king_safety(const Position& pos) {
|
||||||
else while (pawns)
|
else while (pawns)
|
||||||
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns)));
|
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns)));
|
||||||
|
|
||||||
Value bonus = evaluate_shelter<Us>(pos, ksq);
|
Score shelter = make_score(-VALUE_INFINITE, VALUE_ZERO);
|
||||||
|
evaluate_shelter<Us>(pos, ksq, shelter);
|
||||||
|
|
||||||
// If we can castle use the bonus after the castling if it is bigger
|
// If we can castle use the bonus after the castling if it is bigger
|
||||||
if (pos.can_castle(Us | KING_SIDE))
|
if (pos.can_castle(Us | KING_SIDE))
|
||||||
bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)));
|
evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1), shelter);
|
||||||
|
|
||||||
if (pos.can_castle(Us | QUEEN_SIDE))
|
if (pos.can_castle(Us | QUEEN_SIDE))
|
||||||
bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)));
|
evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1), shelter);
|
||||||
|
|
||||||
return make_score(bonus, -16 * minPawnDist);
|
return shelter - make_score(VALUE_ZERO, 16 * minPawnDist);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit template instantiation
|
// Explicit template instantiation
|
||||||
|
|
|
@ -37,8 +37,7 @@ struct Entry {
|
||||||
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
|
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
|
||||||
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
|
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
|
||||||
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
|
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
|
||||||
int weak_unopposed(Color c) const { return weakUnopposed[c]; }
|
int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); }
|
||||||
int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); };
|
|
||||||
|
|
||||||
template<Color Us>
|
template<Color Us>
|
||||||
Score king_safety(const Position& pos) {
|
Score king_safety(const Position& pos) {
|
||||||
|
@ -50,7 +49,7 @@ struct Entry {
|
||||||
Score do_king_safety(const Position& pos);
|
Score do_king_safety(const Position& pos);
|
||||||
|
|
||||||
template<Color Us>
|
template<Color Us>
|
||||||
Value evaluate_shelter(const Position& pos, Square ksq);
|
void evaluate_shelter(const Position& pos, Square ksq, Score& shelter);
|
||||||
|
|
||||||
Key key;
|
Key key;
|
||||||
Score scores[COLOR_NB];
|
Score scores[COLOR_NB];
|
||||||
|
@ -59,12 +58,10 @@ struct Entry {
|
||||||
Bitboard pawnAttacksSpan[COLOR_NB];
|
Bitboard pawnAttacksSpan[COLOR_NB];
|
||||||
Square kingSquares[COLOR_NB];
|
Square kingSquares[COLOR_NB];
|
||||||
Score kingSafety[COLOR_NB];
|
Score kingSafety[COLOR_NB];
|
||||||
int weakUnopposed[COLOR_NB];
|
|
||||||
int castlingRights[COLOR_NB];
|
int castlingRights[COLOR_NB];
|
||||||
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef HashTable<Entry, 16384> Table;
|
typedef HashTable<Entry, 131072> Table;
|
||||||
|
|
||||||
Entry* probe(const Position& pos);
|
Entry* probe(const Position& pos);
|
||||||
|
|
||||||
|
|
110
src/position.cpp
110
src/position.cpp
|
@ -18,6 +18,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstddef> // For offsetof()
|
#include <cstddef> // For offsetof()
|
||||||
#include <cstring> // For std::memset, std::memcmp
|
#include <cstring> // For std::memset, std::memcmp
|
||||||
|
@ -340,8 +341,8 @@ void Position::set_castling_right(Color c, Square rfrom) {
|
||||||
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
|
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
|
||||||
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
|
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
|
||||||
|
|
||||||
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
|
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
|
||||||
& ~(square_bb(kfrom) | rfrom);
|
& ~(square_bb(kfrom) | rfrom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -382,6 +383,12 @@ void Position::set_state(StateInfo* si) const {
|
||||||
Square s = pop_lsb(&b);
|
Square s = pop_lsb(&b);
|
||||||
Piece pc = piece_on(s);
|
Piece pc = piece_on(s);
|
||||||
si->key ^= Zobrist::psq[pc][s];
|
si->key ^= Zobrist::psq[pc][s];
|
||||||
|
|
||||||
|
if (type_of(pc) == PAWN)
|
||||||
|
si->pawnKey ^= Zobrist::psq[pc][s];
|
||||||
|
|
||||||
|
else if (type_of(pc) != KING)
|
||||||
|
si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (si->epSquare != SQ_NONE)
|
if (si->epSquare != SQ_NONE)
|
||||||
|
@ -392,20 +399,9 @@ void Position::set_state(StateInfo* si) const {
|
||||||
|
|
||||||
si->key ^= Zobrist::castling[si->castlingRights];
|
si->key ^= Zobrist::castling[si->castlingRights];
|
||||||
|
|
||||||
for (Bitboard b = pieces(PAWN); b; )
|
|
||||||
{
|
|
||||||
Square s = pop_lsb(&b);
|
|
||||||
si->pawnKey ^= Zobrist::psq[piece_on(s)][s];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Piece pc : Pieces)
|
for (Piece pc : Pieces)
|
||||||
{
|
|
||||||
if (type_of(pc) != PAWN && type_of(pc) != KING)
|
|
||||||
si->nonPawnMaterial[color_of(pc)] += pieceCount[pc] * PieceValue[MG][pc];
|
|
||||||
|
|
||||||
for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
|
for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
|
||||||
si->materialKey ^= Zobrist::psq[pc][cnt];
|
si->materialKey ^= Zobrist::psq[pc][cnt];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -495,7 +491,7 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners
|
||||||
// Snipers are sliders that attack 's' when a piece and other snipers are removed
|
// Snipers are sliders that attack 's' when a piece and other snipers are removed
|
||||||
Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK))
|
Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK))
|
||||||
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
|
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
|
||||||
Bitboard occupancy = pieces() & ~snipers;
|
Bitboard occupancy = pieces() ^ snipers;
|
||||||
|
|
||||||
while (snipers)
|
while (snipers)
|
||||||
{
|
{
|
||||||
|
@ -859,7 +855,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||||
|
|
||||||
// Update pawn hash key and prefetch access to pawnsTable
|
// Update pawn hash key and prefetch access to pawnsTable
|
||||||
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
|
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
|
||||||
prefetch2(thisThread->pawnsTable[st->pawnKey]);
|
|
||||||
|
|
||||||
// Reset rule 50 draw counter
|
// Reset rule 50 draw counter
|
||||||
st->rule50 = 0;
|
st->rule50 = 0;
|
||||||
|
@ -879,6 +874,25 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||||
// Update king attacks used for fast check detection
|
// Update king attacks used for fast check detection
|
||||||
set_check_info(st);
|
set_check_info(st);
|
||||||
|
|
||||||
|
// Calculate the repetition info. It is the ply distance from the previous
|
||||||
|
// occurrence of the same position, negative in the 3-fold case, or zero
|
||||||
|
// if the position was not repeated.
|
||||||
|
st->repetition = 0;
|
||||||
|
int end = std::min(st->rule50, st->pliesFromNull);
|
||||||
|
if (end >= 4)
|
||||||
|
{
|
||||||
|
StateInfo* stp = st->previous->previous;
|
||||||
|
for (int i=4; i <= end; i += 2)
|
||||||
|
{
|
||||||
|
stp = stp->previous->previous;
|
||||||
|
if (stp->key == st->key)
|
||||||
|
{
|
||||||
|
st->repetition = stp->repetition ? -i : i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assert(pos_is_ok());
|
assert(pos_is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -994,6 +1008,8 @@ void Position::do_null_move(StateInfo& newSt) {
|
||||||
|
|
||||||
set_check_info(st);
|
set_check_info(st);
|
||||||
|
|
||||||
|
st->repetition = 0;
|
||||||
|
|
||||||
assert(pos_is_ok());
|
assert(pos_is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1117,24 +1133,10 @@ bool Position::is_draw(int ply) const {
|
||||||
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
|
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
int end = std::min(st->rule50, st->pliesFromNull);
|
// Return a draw score if a position repeats once earlier but strictly
|
||||||
|
// after the root, or repeats twice before or at the root.
|
||||||
if (end < 4)
|
if (st->repetition && st->repetition < ply)
|
||||||
return false;
|
return true;
|
||||||
|
|
||||||
StateInfo* stp = st->previous->previous;
|
|
||||||
int cnt = 0;
|
|
||||||
|
|
||||||
for (int i = 4; i <= end; i += 2)
|
|
||||||
{
|
|
||||||
stp = stp->previous->previous;
|
|
||||||
|
|
||||||
// Return a draw score if a position repeats once earlier but strictly
|
|
||||||
// after the root, or repeats twice before or at the root.
|
|
||||||
if ( stp->key == st->key
|
|
||||||
&& ++cnt + (ply > i) == 2)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1146,26 +1148,15 @@ bool Position::is_draw(int ply) const {
|
||||||
bool Position::has_repeated() const {
|
bool Position::has_repeated() const {
|
||||||
|
|
||||||
StateInfo* stc = st;
|
StateInfo* stc = st;
|
||||||
while (true)
|
int end = std::min(st->rule50, st->pliesFromNull);
|
||||||
|
while (end-- >= 4)
|
||||||
{
|
{
|
||||||
int i = 4, end = std::min(stc->rule50, stc->pliesFromNull);
|
if (stc->repetition)
|
||||||
|
return true;
|
||||||
if (end < i)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
StateInfo* stp = stc->previous->previous;
|
|
||||||
|
|
||||||
do {
|
|
||||||
stp = stp->previous->previous;
|
|
||||||
|
|
||||||
if (stp->key == stc->key)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
i += 2;
|
|
||||||
} while (i <= end);
|
|
||||||
|
|
||||||
stc = stc->previous;
|
stc = stc->previous;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1198,22 +1189,19 @@ bool Position::has_game_cycle(int ply) const {
|
||||||
|
|
||||||
if (!(between_bb(s1, s2) & pieces()))
|
if (!(between_bb(s1, s2) & pieces()))
|
||||||
{
|
{
|
||||||
// In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in the same
|
|
||||||
// location. We select the legal one by reversing the move variable if necessary.
|
|
||||||
if (empty(s1))
|
|
||||||
move = make_move(s2, s1);
|
|
||||||
|
|
||||||
if (ply > i)
|
if (ply > i)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// For nodes before or at the root, check that the move is a
|
||||||
|
// repetition rather than a move to the current position.
|
||||||
|
// In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in
|
||||||
|
// the same location, so we have to select which square to check.
|
||||||
|
if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move())
|
||||||
|
continue;
|
||||||
|
|
||||||
// For repetitions before or at the root, require one more
|
// For repetitions before or at the root, require one more
|
||||||
StateInfo* next_stp = stp;
|
if (stp->repetition)
|
||||||
for (int k = i + 2; k <= end; k += 2)
|
return true;
|
||||||
{
|
|
||||||
next_stp = next_stp->previous->previous;
|
|
||||||
if (next_stp->key == stp->key)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ struct StateInfo {
|
||||||
Square epSquare;
|
Square epSquare;
|
||||||
|
|
||||||
// Not copied when making a move (will be recomputed anyhow)
|
// Not copied when making a move (will be recomputed anyhow)
|
||||||
|
int repetition;
|
||||||
Key key;
|
Key key;
|
||||||
Bitboard checkersBB;
|
Bitboard checkersBB;
|
||||||
Piece capturedPiece;
|
Piece capturedPiece;
|
||||||
|
@ -95,7 +96,7 @@ public:
|
||||||
template<PieceType Pt> int count() const;
|
template<PieceType Pt> int count() const;
|
||||||
template<PieceType Pt> const Square* squares(Color c) const;
|
template<PieceType Pt> const Square* squares(Color c) const;
|
||||||
template<PieceType Pt> Square square(Color c) const;
|
template<PieceType Pt> Square square(Color c) const;
|
||||||
int semiopen_file(Color c, File f) const;
|
bool is_on_semiopen_file(Color c, Square s) const;
|
||||||
|
|
||||||
// Castling
|
// Castling
|
||||||
int castling_rights(Color c) const;
|
int castling_rights(Color c) const;
|
||||||
|
@ -262,8 +263,8 @@ inline Square Position::ep_square() const {
|
||||||
return st->epSquare;
|
return st->epSquare;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int Position::semiopen_file(Color c, File f) const {
|
inline bool Position::is_on_semiopen_file(Color c, Square s) const {
|
||||||
return !(pieces(c, PAWN) & file_bb(f));
|
return !(pieces(c, PAWN) & file_bb(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Position::can_castle(CastlingRight cr) const {
|
inline bool Position::can_castle(CastlingRight cr) const {
|
||||||
|
@ -321,7 +322,7 @@ inline bool Position::pawn_passed(Color c, Square s) const {
|
||||||
|
|
||||||
inline bool Position::advanced_pawn_push(Move m) const {
|
inline bool Position::advanced_pawn_push(Move m) const {
|
||||||
return type_of(moved_piece(m)) == PAWN
|
return type_of(moved_piece(m)) == PAWN
|
||||||
&& relative_rank(sideToMove, from_sq(m)) > RANK_4;
|
&& relative_rank(sideToMove, to_sq(m)) > RANK_5;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
|
inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
|
||||||
|
|
167
src/search.cpp
167
src/search.cpp
|
@ -18,6 +18,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring> // For std::memset
|
#include <cstring> // For std::memset
|
||||||
|
@ -70,9 +71,9 @@ namespace {
|
||||||
// Reductions lookup table, initialized at startup
|
// Reductions lookup table, initialized at startup
|
||||||
int Reductions[MAX_MOVES]; // [depth or moveNumber]
|
int Reductions[MAX_MOVES]; // [depth or moveNumber]
|
||||||
|
|
||||||
template <bool PvNode> Depth reduction(bool i, Depth d, int mn) {
|
Depth reduction(bool i, Depth d, int mn) {
|
||||||
int r = Reductions[d / ONE_PLY] * Reductions[mn] / 1024;
|
int r = Reductions[d / ONE_PLY] * Reductions[mn];
|
||||||
return ((r + 512) / 1024 + (!i && r > 1024) - PvNode) * ONE_PLY;
|
return ((r + 512) / 1024 + (!i && r > 1024)) * ONE_PLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int futility_move_count(bool improving, int depth) {
|
constexpr int futility_move_count(bool improving, int depth) {
|
||||||
|
@ -102,6 +103,48 @@ namespace {
|
||||||
Move best = MOVE_NONE;
|
Move best = MOVE_NONE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Breadcrumbs are used to mark nodes as being searched by a given thread.
|
||||||
|
struct Breadcrumb {
|
||||||
|
std::atomic<Thread*> thread;
|
||||||
|
std::atomic<Key> key;
|
||||||
|
};
|
||||||
|
std::array<Breadcrumb, 1024> breadcrumbs;
|
||||||
|
|
||||||
|
// ThreadHolding keeps track of which thread left breadcrumbs at the given node for potential reductions.
|
||||||
|
// A free node will be marked upon entering the moves loop, and unmarked upon leaving that loop, by the ctor/dtor of this struct.
|
||||||
|
struct ThreadHolding {
|
||||||
|
explicit ThreadHolding(Thread* thisThread, Key posKey, int ply) {
|
||||||
|
location = ply < 8 ? &breadcrumbs[posKey & (breadcrumbs.size() - 1)] : nullptr;
|
||||||
|
otherThread = false;
|
||||||
|
owning = false;
|
||||||
|
if (location)
|
||||||
|
{
|
||||||
|
// see if another already marked this location, if not, mark it ourselves.
|
||||||
|
Thread* tmp = (*location).thread.load(std::memory_order_relaxed);
|
||||||
|
if (tmp == nullptr)
|
||||||
|
{
|
||||||
|
(*location).thread.store(thisThread, std::memory_order_relaxed);
|
||||||
|
(*location).key.store(posKey, std::memory_order_relaxed);
|
||||||
|
owning = true;
|
||||||
|
}
|
||||||
|
else if ( tmp != thisThread
|
||||||
|
&& (*location).key.load(std::memory_order_relaxed) == posKey)
|
||||||
|
otherThread = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~ThreadHolding() {
|
||||||
|
if (owning) // free the marked location.
|
||||||
|
(*location).thread.store(nullptr, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool marked() { return otherThread; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Breadcrumb* location;
|
||||||
|
bool otherThread, owning;
|
||||||
|
};
|
||||||
|
|
||||||
template <NodeType NT>
|
template <NodeType NT>
|
||||||
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
|
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
|
||||||
|
|
||||||
|
@ -149,7 +192,7 @@ namespace {
|
||||||
void Search::init() {
|
void Search::init() {
|
||||||
|
|
||||||
for (int i = 1; i < MAX_MOVES; ++i)
|
for (int i = 1; i < MAX_MOVES; ++i)
|
||||||
Reductions[i] = int(1024 * std::log(i) / std::sqrt(1.95));
|
Reductions[i] = int(22.9 * std::log(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -244,21 +287,15 @@ void MainThread::search() {
|
||||||
for (Thread* th: Threads)
|
for (Thread* th: Threads)
|
||||||
minScore = std::min(minScore, th->rootMoves[0].score);
|
minScore = std::min(minScore, th->rootMoves[0].score);
|
||||||
|
|
||||||
// Vote according to score and depth
|
// Vote according to score and depth, and select the best thread
|
||||||
for (Thread* th : Threads)
|
for (Thread* th : Threads)
|
||||||
{
|
{
|
||||||
int64_t s = th->rootMoves[0].score - minScore + 1;
|
votes[th->rootMoves[0].pv[0]] +=
|
||||||
votes[th->rootMoves[0].pv[0]] += 200 + s * s * int(th->completedDepth);
|
(th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
|
||||||
}
|
|
||||||
|
|
||||||
// Select best thread
|
if (votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])
|
||||||
auto bestVote = votes[this->rootMoves[0].pv[0]];
|
|
||||||
for (Thread* th : Threads)
|
|
||||||
if (votes[th->rootMoves[0].pv[0]] > bestVote)
|
|
||||||
{
|
|
||||||
bestVote = votes[th->rootMoves[0].pv[0]];
|
|
||||||
bestThread = th;
|
bestThread = th;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -326,8 +363,13 @@ void Thread::search() {
|
||||||
bestValue = delta = alpha = -VALUE_INFINITE;
|
bestValue = delta = alpha = -VALUE_INFINITE;
|
||||||
beta = VALUE_INFINITE;
|
beta = VALUE_INFINITE;
|
||||||
|
|
||||||
size_t multiPV = Options["MultiPV"];
|
multiPV = Options["MultiPV"];
|
||||||
Skill skill(Options["Skill Level"]);
|
// Pick integer skill levels, but non-deterministically round up or down
|
||||||
|
// such that the average integer skill corresponds to the input floating point one.
|
||||||
|
PRNG rng(now());
|
||||||
|
int intLevel = int(Options["Skill Level"]) +
|
||||||
|
((Options["Skill Level"] - int(Options["Skill Level"])) * 1024 > rng.rand<unsigned>() % 1024 ? 1 : 0);
|
||||||
|
Skill skill(intLevel);
|
||||||
|
|
||||||
// When playing with strength handicap enable MultiPV search that we will
|
// When playing with strength handicap enable MultiPV search that we will
|
||||||
// use behind the scenes to retrieve a set of possible moves.
|
// use behind the scenes to retrieve a set of possible moves.
|
||||||
|
@ -439,17 +481,14 @@ void Thread::search() {
|
||||||
beta = (alpha + beta) / 2;
|
beta = (alpha + beta) / 2;
|
||||||
alpha = std::max(bestValue - delta, -VALUE_INFINITE);
|
alpha = std::max(bestValue - delta, -VALUE_INFINITE);
|
||||||
|
|
||||||
|
failedHighCnt = 0;
|
||||||
if (mainThread)
|
if (mainThread)
|
||||||
{
|
|
||||||
failedHighCnt = 0;
|
|
||||||
mainThread->stopOnPonderhit = false;
|
mainThread->stopOnPonderhit = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (bestValue >= beta)
|
else if (bestValue >= beta)
|
||||||
{
|
{
|
||||||
beta = std::min(bestValue + delta, VALUE_INFINITE);
|
beta = std::min(bestValue + delta, VALUE_INFINITE);
|
||||||
if (mainThread)
|
++failedHighCnt;
|
||||||
++failedHighCnt;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
@ -579,13 +618,13 @@ namespace {
|
||||||
bool ttHit, ttPv, inCheck, givesCheck, improving;
|
bool ttHit, ttPv, inCheck, givesCheck, improving;
|
||||||
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture;
|
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture;
|
||||||
Piece movedPiece;
|
Piece movedPiece;
|
||||||
int moveCount, captureCount, quietCount;
|
int moveCount, captureCount, quietCount, singularLMR;
|
||||||
|
|
||||||
// Step 1. Initialize node
|
// Step 1. Initialize node
|
||||||
Thread* thisThread = pos.this_thread();
|
Thread* thisThread = pos.this_thread();
|
||||||
inCheck = pos.checkers();
|
inCheck = pos.checkers();
|
||||||
Color us = pos.side_to_move();
|
Color us = pos.side_to_move();
|
||||||
moveCount = captureCount = quietCount = ss->moveCount = 0;
|
moveCount = captureCount = quietCount = singularLMR = ss->moveCount = 0;
|
||||||
bestValue = -VALUE_INFINITE;
|
bestValue = -VALUE_INFINITE;
|
||||||
maxValue = VALUE_INFINITE;
|
maxValue = VALUE_INFINITE;
|
||||||
|
|
||||||
|
@ -630,7 +669,10 @@ namespace {
|
||||||
// starts with statScore = 0. Later grandchildren start with the last calculated
|
// starts with statScore = 0. Later grandchildren start with the last calculated
|
||||||
// statScore of the previous grandchild. This influences the reduction rules in
|
// statScore of the previous grandchild. This influences the reduction rules in
|
||||||
// LMR which are based on the statScore of parent position.
|
// LMR which are based on the statScore of parent position.
|
||||||
(ss+2)->statScore = 0;
|
if (rootNode)
|
||||||
|
(ss + 4)->statScore = 0;
|
||||||
|
else
|
||||||
|
(ss + 2)->statScore = 0;
|
||||||
|
|
||||||
// Step 4. Transposition table lookup. We don't want the score of a partial
|
// Step 4. Transposition table lookup. We don't want the score of a partial
|
||||||
// search to overwrite a previous full search TT value, so we use a different
|
// search to overwrite a previous full search TT value, so we use a different
|
||||||
|
@ -641,16 +683,7 @@ namespace {
|
||||||
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
|
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
|
||||||
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
|
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
|
||||||
: ttHit ? tte->move() : MOVE_NONE;
|
: ttHit ? tte->move() : MOVE_NONE;
|
||||||
ttPv = (ttHit && tte->is_pv()) || (PvNode && depth > 4 * ONE_PLY);
|
ttPv = PvNode || (ttHit && tte->is_pv());
|
||||||
|
|
||||||
// If position has been searched at higher depths and we are shuffling,
|
|
||||||
// return value_draw.
|
|
||||||
if ( pos.rule50_count() > 36 - 6 * (pos.count<ALL_PIECES>() > 14)
|
|
||||||
&& ss->ply > 36 - 6 * (pos.count<ALL_PIECES>() > 14)
|
|
||||||
&& ttHit
|
|
||||||
&& tte->depth() > depth
|
|
||||||
&& pos.count<PAWN>() > 0)
|
|
||||||
return VALUE_DRAW;
|
|
||||||
|
|
||||||
// At non-PV nodes we check for an early TT cutoff
|
// At non-PV nodes we check for an early TT cutoff
|
||||||
if ( !PvNode
|
if ( !PvNode
|
||||||
|
@ -901,6 +934,9 @@ moves_loop: // When in check, search starts from here
|
||||||
moveCountPruning = false;
|
moveCountPruning = false;
|
||||||
ttCapture = ttMove && pos.capture_or_promotion(ttMove);
|
ttCapture = ttMove && pos.capture_or_promotion(ttMove);
|
||||||
|
|
||||||
|
// Mark this node as being searched.
|
||||||
|
ThreadHolding th(thisThread, posKey, ss->ply);
|
||||||
|
|
||||||
// Step 12. Loop through all pseudo-legal moves until no moves remain
|
// Step 12. Loop through all pseudo-legal moves until no moves remain
|
||||||
// or a beta cutoff occurs.
|
// or a beta cutoff occurs.
|
||||||
while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE)
|
while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE)
|
||||||
|
@ -924,6 +960,12 @@ moves_loop: // When in check, search starts from here
|
||||||
sync_cout << "info depth " << depth / ONE_PLY
|
sync_cout << "info depth " << depth / ONE_PLY
|
||||||
<< " currmove " << UCI::move(move, pos.is_chess960())
|
<< " currmove " << UCI::move(move, pos.is_chess960())
|
||||||
<< " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl;
|
<< " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl;
|
||||||
|
|
||||||
|
// In MultiPV mode also skip moves which will be searched later as PV moves
|
||||||
|
if (rootNode && std::count(thisThread->rootMoves.begin() + thisThread->pvIdx + 1,
|
||||||
|
thisThread->rootMoves.begin() + thisThread->multiPV, move))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (PvNode)
|
if (PvNode)
|
||||||
(ss+1)->pv = nullptr;
|
(ss+1)->pv = nullptr;
|
||||||
|
|
||||||
|
@ -943,7 +985,7 @@ moves_loop: // When in check, search starts from here
|
||||||
&& move == ttMove
|
&& move == ttMove
|
||||||
&& !rootNode
|
&& !rootNode
|
||||||
&& !excludedMove // Avoid recursive singular search
|
&& !excludedMove // Avoid recursive singular search
|
||||||
/* && ttValue != VALUE_NONE Already implicit in the next condition */
|
/* && ttValue != VALUE_NONE Already implicit in the next condition */
|
||||||
&& abs(ttValue) < VALUE_KNOWN_WIN
|
&& abs(ttValue) < VALUE_KNOWN_WIN
|
||||||
&& (tte->bound() & BOUND_LOWER)
|
&& (tte->bound() & BOUND_LOWER)
|
||||||
&& tte->depth() >= depth - 3 * ONE_PLY
|
&& tte->depth() >= depth - 3 * ONE_PLY
|
||||||
|
@ -956,15 +998,22 @@ moves_loop: // When in check, search starts from here
|
||||||
ss->excludedMove = MOVE_NONE;
|
ss->excludedMove = MOVE_NONE;
|
||||||
|
|
||||||
if (value < singularBeta)
|
if (value < singularBeta)
|
||||||
|
{
|
||||||
extension = ONE_PLY;
|
extension = ONE_PLY;
|
||||||
|
singularLMR++;
|
||||||
|
|
||||||
|
if (value < singularBeta - std::min(3 * depth / ONE_PLY, 39))
|
||||||
|
singularLMR++;
|
||||||
|
}
|
||||||
|
|
||||||
// Multi-cut pruning
|
// Multi-cut pruning
|
||||||
// Our ttMove is assumed to fail high, and now we failed high also on a reduced
|
// Our ttMove is assumed to fail high, and now we failed high also on a reduced
|
||||||
// search without the ttMove. So we assume this expected Cut-node is not singular,
|
// search without the ttMove. So we assume this expected Cut-node is not singular,
|
||||||
// that is multiple moves fail high, and we can prune the whole subtree by returning
|
// that multiple moves fail high, and we can prune the whole subtree by returning
|
||||||
// the hard beta bound.
|
// a soft bound.
|
||||||
else if (cutNode && singularBeta > beta)
|
else if ( eval >= beta
|
||||||
return beta;
|
&& singularBeta >= beta)
|
||||||
|
return singularBeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check extension (~2 Elo)
|
// Check extension (~2 Elo)
|
||||||
|
@ -979,9 +1028,8 @@ moves_loop: // When in check, search starts from here
|
||||||
// Shuffle extension
|
// Shuffle extension
|
||||||
else if ( PvNode
|
else if ( PvNode
|
||||||
&& pos.rule50_count() > 18
|
&& pos.rule50_count() > 18
|
||||||
&& ss->ply > 18
|
|
||||||
&& depth < 3 * ONE_PLY
|
&& depth < 3 * ONE_PLY
|
||||||
&& ss->ply < 3 * thisThread->rootDepth / ONE_PLY) // To avoid infinite loops
|
&& ++thisThread->shuffleExts < thisThread->nodes.load(std::memory_order_relaxed) / 4) // To avoid too many extensions
|
||||||
extension = ONE_PLY;
|
extension = ONE_PLY;
|
||||||
|
|
||||||
// Passed pawn extension
|
// Passed pawn extension
|
||||||
|
@ -1003,14 +1051,14 @@ moves_loop: // When in check, search starts from here
|
||||||
|
|
||||||
if ( !captureOrPromotion
|
if ( !captureOrPromotion
|
||||||
&& !givesCheck
|
&& !givesCheck
|
||||||
&& !pos.advanced_pawn_push(move))
|
&& (!pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg))
|
||||||
{
|
{
|
||||||
// Move count based pruning (~30 Elo)
|
// Move count based pruning (~30 Elo)
|
||||||
if (moveCountPruning)
|
if (moveCountPruning)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Reduced depth of the next LMR search
|
// Reduced depth of the next LMR search
|
||||||
int lmrDepth = std::max(newDepth - reduction<PvNode>(improving, depth, moveCount), DEPTH_ZERO);
|
int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), DEPTH_ZERO);
|
||||||
lmrDepth /= ONE_PLY;
|
lmrDepth /= ONE_PLY;
|
||||||
|
|
||||||
// Countermoves based pruning (~20 Elo)
|
// Countermoves based pruning (~20 Elo)
|
||||||
|
@ -1029,7 +1077,8 @@ moves_loop: // When in check, search starts from here
|
||||||
if (!pos.see_ge(move, Value(-29 * lmrDepth * lmrDepth)))
|
if (!pos.see_ge(move, Value(-29 * lmrDepth * lmrDepth)))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (!pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) // (~20 Elo)
|
else if ((!givesCheck || !extension)
|
||||||
|
&& !pos.see_ge(move, -PawnValueEg * (depth / ONE_PLY))) // (~20 Elo)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1053,19 +1102,28 @@ moves_loop: // When in check, search starts from here
|
||||||
// Step 16. Reduced depth search (LMR). If the move fails high it will be
|
// Step 16. Reduced depth search (LMR). If the move fails high it will be
|
||||||
// re-searched at full depth.
|
// re-searched at full depth.
|
||||||
if ( depth >= 3 * ONE_PLY
|
if ( depth >= 3 * ONE_PLY
|
||||||
&& moveCount > 1
|
&& moveCount > 1 + 3 * rootNode
|
||||||
&& (!captureOrPromotion || moveCountPruning))
|
&& ( !captureOrPromotion
|
||||||
|
|| moveCountPruning
|
||||||
|
|| ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha))
|
||||||
{
|
{
|
||||||
Depth r = reduction<PvNode>(improving, depth, moveCount);
|
Depth r = reduction(improving, depth, moveCount);
|
||||||
|
|
||||||
|
// Reduction if other threads are searching this position.
|
||||||
|
if (th.marked())
|
||||||
|
r += ONE_PLY;
|
||||||
|
|
||||||
// Decrease reduction if position is or has been on the PV
|
// Decrease reduction if position is or has been on the PV
|
||||||
if (ttPv)
|
if (ttPv)
|
||||||
r -= ONE_PLY;
|
r -= 2 * ONE_PLY;
|
||||||
|
|
||||||
// Decrease reduction if opponent's move count is high (~10 Elo)
|
// Decrease reduction if opponent's move count is high (~10 Elo)
|
||||||
if ((ss-1)->moveCount > 15)
|
if ((ss-1)->moveCount > 15)
|
||||||
r -= ONE_PLY;
|
r -= ONE_PLY;
|
||||||
|
|
||||||
|
// Decrease reduction if move has been singularly extended
|
||||||
|
r -= singularLMR * ONE_PLY;
|
||||||
|
|
||||||
if (!captureOrPromotion)
|
if (!captureOrPromotion)
|
||||||
{
|
{
|
||||||
// Increase reduction if ttMove is a capture (~0 Elo)
|
// Increase reduction if ttMove is a capture (~0 Elo)
|
||||||
|
@ -1100,7 +1158,7 @@ moves_loop: // When in check, search starts from here
|
||||||
r -= ss->statScore / 20000 * ONE_PLY;
|
r -= ss->statScore / 20000 * ONE_PLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
Depth d = std::max(newDepth - std::max(r, DEPTH_ZERO), ONE_PLY);
|
Depth d = clamp(newDepth - r, ONE_PLY, newDepth);
|
||||||
|
|
||||||
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
|
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
|
||||||
|
|
||||||
|
@ -1252,8 +1310,8 @@ moves_loop: // When in check, search starts from here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// qsearch() is the quiescence search function, which is called by the main
|
// qsearch() is the quiescence search function, which is called by the main search
|
||||||
// search function with depth zero, or recursively with depth less than ONE_PLY.
|
// function with zero depth, or recursively with further decreasing depth per call.
|
||||||
template <NodeType NT>
|
template <NodeType NT>
|
||||||
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
|
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
|
||||||
|
|
||||||
|
@ -1408,6 +1466,7 @@ moves_loop: // When in check, search starts from here
|
||||||
|
|
||||||
// Don't search moves with negative SEE values
|
// Don't search moves with negative SEE values
|
||||||
if ( (!inCheck || evasionPrunable)
|
if ( (!inCheck || evasionPrunable)
|
||||||
|
&& (!givesCheck || !(pos.blockers_for_king(~pos.side_to_move()) & from_sq(move)))
|
||||||
&& !pos.see_ge(move))
|
&& !pos.see_ge(move))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1519,7 +1578,7 @@ moves_loop: // When in check, search starts from here
|
||||||
void update_capture_stats(const Position& pos, Move move,
|
void update_capture_stats(const Position& pos, Move move,
|
||||||
Move* captures, int captureCount, int bonus) {
|
Move* captures, int captureCount, int bonus) {
|
||||||
|
|
||||||
CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory;
|
CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory;
|
||||||
Piece moved_piece = pos.moved_piece(move);
|
Piece moved_piece = pos.moved_piece(move);
|
||||||
PieceType captured = type_of(pos.piece_on(to_sq(move)));
|
PieceType captured = type_of(pos.piece_on(to_sq(move)));
|
||||||
|
|
||||||
|
@ -1763,7 +1822,7 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) {
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Assign the same rank to all moves
|
// Clean up if root_probe() and root_probe_wdl() have failed
|
||||||
for (auto& m : rootMoves)
|
for (auto& m : rootMoves)
|
||||||
m.tbRank = 0;
|
m.tbRank = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ struct RootMove {
|
||||||
Value score = -VALUE_INFINITE;
|
Value score = -VALUE_INFINITE;
|
||||||
Value previousScore = -VALUE_INFINITE;
|
Value previousScore = -VALUE_INFINITE;
|
||||||
int selDepth = 0;
|
int selDepth = 0;
|
||||||
int tbRank;
|
int tbRank = 0;
|
||||||
Value tbScore;
|
Value tbScore;
|
||||||
std::vector<Move> pv;
|
std::vector<Move> pv;
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <algorithm> // For std::count
|
||||||
#include "movegen.h"
|
#include "movegen.h"
|
||||||
#include "search.h"
|
#include "search.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
|
@ -193,7 +194,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
|
||||||
|
|
||||||
for (Thread* th : *this)
|
for (Thread* th : *this)
|
||||||
{
|
{
|
||||||
th->nodes = th->tbHits = th->TTsaves = th->nmpMinPly = 0;
|
th->shuffleExts = th->nodes = th->tbHits = th->TTsaves = th->nmpMinPly = 0;
|
||||||
th->rootDepth = th->completedDepth = DEPTH_ZERO;
|
th->rootDepth = th->completedDepth = DEPTH_ZERO;
|
||||||
th->rootMoves = rootMoves;
|
th->rootMoves = rootMoves;
|
||||||
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
|
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
|
||||||
|
|
|
@ -60,8 +60,7 @@ public:
|
||||||
|
|
||||||
Pawns::Table pawnsTable;
|
Pawns::Table pawnsTable;
|
||||||
Material::Table materialTable;
|
Material::Table materialTable;
|
||||||
Endgames endgames;
|
size_t pvIdx, multiPV, pvLast, shuffleExts;
|
||||||
size_t pvIdx, pvLast;
|
|
||||||
int selDepth, nmpMinPly;
|
int selDepth, nmpMinPly;
|
||||||
Color nmpColor;
|
Color nmpColor;
|
||||||
std::atomic<uint64_t> nodes, tbHits, TTsaves, bestMoveChanges;
|
std::atomic<uint64_t> nodes, tbHits, TTsaves, bestMoveChanges;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
|
|
@ -43,14 +43,16 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev)
|
||||||
|
|
||||||
// Overwrite less valuable entries
|
// Overwrite less valuable entries
|
||||||
if ( (k >> 48) != key16
|
if ( (k >> 48) != key16
|
||||||
|| d / ONE_PLY > depth8 - 4
|
||(d - DEPTH_OFFSET) / ONE_PLY > depth8 - 4
|
||||||
|| b == BOUND_EXACT)
|
|| b == BOUND_EXACT)
|
||||||
{
|
{
|
||||||
|
assert((d - DEPTH_OFFSET) / ONE_PLY >= 0);
|
||||||
|
|
||||||
key16 = (uint16_t)(k >> 48);
|
key16 = (uint16_t)(k >> 48);
|
||||||
value16 = (int16_t)v;
|
value16 = (int16_t)v;
|
||||||
eval16 = (int16_t)ev;
|
eval16 = (int16_t)ev;
|
||||||
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
|
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
|
||||||
depth8 = (int8_t)(d / ONE_PLY);
|
depth8 = (uint8_t)((d - DEPTH_OFFSET) / ONE_PLY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
src/tt.h
4
src/tt.h
|
@ -45,7 +45,7 @@ struct TTEntry {
|
||||||
Move move() const { return (Move )move16; }
|
Move move() const { return (Move )move16; }
|
||||||
Value value() const { return (Value)value16; }
|
Value value() const { return (Value)value16; }
|
||||||
Value eval() const { return (Value)eval16; }
|
Value eval() const { return (Value)eval16; }
|
||||||
Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)); }
|
Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)) + DEPTH_OFFSET; }
|
||||||
bool is_pv() const { return (bool)(genBound8 & 0x4); }
|
bool is_pv() const { return (bool)(genBound8 & 0x4); }
|
||||||
Bound bound() const { return (Bound)(genBound8 & 0x3); }
|
Bound bound() const { return (Bound)(genBound8 & 0x3); }
|
||||||
void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev);
|
void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev);
|
||||||
|
@ -59,7 +59,7 @@ private:
|
||||||
int16_t value16;
|
int16_t value16;
|
||||||
int16_t eval16;
|
int16_t eval16;
|
||||||
uint8_t genBound8;
|
uint8_t genBound8;
|
||||||
int8_t depth8;
|
uint8_t depth8;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ typedef uint64_t Key;
|
||||||
typedef uint64_t Bitboard;
|
typedef uint64_t Bitboard;
|
||||||
|
|
||||||
constexpr int MAX_MOVES = 256;
|
constexpr int MAX_MOVES = 256;
|
||||||
constexpr int MAX_PLY = 128;
|
constexpr int MAX_PLY = 246;
|
||||||
|
|
||||||
/// A move needs 16 bits to be stored
|
/// A move needs 16 bits to be stored
|
||||||
///
|
///
|
||||||
|
@ -213,8 +213,9 @@ enum Depth : int {
|
||||||
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
|
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
|
||||||
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
|
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
|
||||||
|
|
||||||
DEPTH_NONE = -6 * ONE_PLY,
|
DEPTH_NONE = -6 * ONE_PLY,
|
||||||
DEPTH_MAX = MAX_PLY * ONE_PLY
|
DEPTH_OFFSET = DEPTH_NONE,
|
||||||
|
DEPTH_MAX = MAX_PLY * ONE_PLY
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
|
static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
|
||||||
|
@ -290,7 +291,6 @@ inline T& operator--(T& d) { return d = T(int(d) - 1); }
|
||||||
|
|
||||||
#define ENABLE_FULL_OPERATORS_ON(T) \
|
#define ENABLE_FULL_OPERATORS_ON(T) \
|
||||||
ENABLE_BASE_OPERATORS_ON(T) \
|
ENABLE_BASE_OPERATORS_ON(T) \
|
||||||
ENABLE_INCR_OPERATORS_ON(T) \
|
|
||||||
constexpr T operator*(int i, T d) { return T(i * int(d)); } \
|
constexpr T operator*(int i, T d) { return T(i * int(d)); } \
|
||||||
constexpr T operator*(T d, int i) { return T(int(d) * i); } \
|
constexpr T operator*(T d, int i) { return T(int(d) * i); } \
|
||||||
constexpr T operator/(T d, int i) { return T(int(d) / i); } \
|
constexpr T operator/(T d, int i) { return T(int(d) / i); } \
|
||||||
|
|
|
@ -166,7 +166,7 @@ namespace {
|
||||||
}
|
}
|
||||||
else if (token == "setoption") setoption(is);
|
else if (token == "setoption") setoption(is);
|
||||||
else if (token == "position") position(pos, is, states);
|
else if (token == "position") position(pos, is, states);
|
||||||
else if (token == "ucinewgame") Search::clear();
|
else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take some while
|
||||||
}
|
}
|
||||||
|
|
||||||
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
|
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
Loading…
Add table
Reference in a new issue