1
0
Fork 0
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:
Joost VandeVondele 2019-07-01 16:36:58 +02:00
commit 0fd0e4e849
27 changed files with 344 additions and 293 deletions

View file

@ -1,7 +1,7 @@
## Overview
[![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish)
[![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish)
[![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master)
[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
@ -34,11 +34,11 @@ Currently, Stockfish has the following UCI options:
A positive value for contempt favors middle game positions and avoids draws.
* #### 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.
* #### 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.
* #### Hash
@ -58,18 +58,18 @@ Currently, Stockfish has the following UCI options:
Lower the Skill Level in order to make Stockfish play weaker.
* #### 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.
* #### Minimum Thinking Time
Search for at least x ms per move.
Search for at least x ms per move.
* #### 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.
* #### 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.
* #### UCI_Chess960
@ -79,13 +79,13 @@ Currently, Stockfish has the following UCI options:
An option handled by your GUI.
* #### SyzygyPath
Path to the folders/directories storing the Syzygy tablebase files. Multiple
directories are to be separated by ";" on Windows and by ":" on Unix-based
Path to the folders/directories storing the Syzygy tablebase files. Multiple
directories are to be separated by ";" on Windows and by ":" on Unix-based
operating systems. Do not use spaces around the ";" or ":".
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
of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will
lead to engine crashes.
@ -177,7 +177,7 @@ community effort. There are a few ways to help contribute to its growth.
### Donating hardware
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).
### Improving the code
@ -193,7 +193,7 @@ generic rather than being focused on Stockfish's precise implementation.
Nevertheless, a helpful resource.
* 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).
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.

View file

@ -139,9 +139,9 @@ vector<string> setup_bench(const Position& current, istream& is) {
file.close();
}
list.emplace_back("ucinewgame");
list.emplace_back("setoption name Threads value " + threads);
list.emplace_back("setoption name Hash value " + ttSize);
list.emplace_back("ucinewgame");
for (const string& fen : fens)
if (fen.find("setoption") != string::npos)

View file

@ -27,7 +27,8 @@
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
// Each uint32_t stores results of 32 positions, one per bit

View file

@ -18,8 +18,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <bitset>
#include <algorithm>
#include <bitset>
#include "bitboard.h"
#include "misc.h"
@ -27,16 +27,10 @@
uint8_t PopCnt16[1 << 16];
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
Bitboard SquareBB[SQUARE_NB];
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard PseudoAttacks[PIECE_TYPE_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 BishopMagics[SQUARE_NB];

View file

@ -65,14 +65,19 @@ constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
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 SquareDistance[SQUARE_NB][SQUARE_NB];
extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[PIECE_TYPE_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
@ -148,6 +153,7 @@ inline Bitboard file_bb(Square s) {
template<Direction D>
constexpr Bitboard shift(Bitboard b) {
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 == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7
: 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 of the given one.
inline Bitboard adjacent_files_bb(File f) {
return shift<EAST>(file_bb(f)) | shift<WEST>(file_bb(f));
inline Bitboard adjacent_files_bb(Square s) {
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.
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.
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
/// most/least advanced bit relative to the given color.
/// frontmost_sq() returns the most advanced square for the given color
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

View file

@ -82,6 +82,34 @@ 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
/// 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
@ -337,7 +365,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
&& pos.count<PAWN>(weakSide) >= 1)
{
// 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 weakKingSq = pos.square<KING>(weakSide);

View file

@ -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
/// 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 Map = std::map<Key, Ptr<T>>;
extern std::pair<Map<Value>, Map<ScaleFactor>> maps;
void init();
template<typename T>
Map<T>& map() {
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));
}
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>
const EndgameBase<T>* probe(Key key) {
return map<T>().count(key) ? map<T>()[key].get() : nullptr;
}
};
}
#endif // #ifndef ENDGAME_H_INCLUDED

View file

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include <cstring> // For std::memset
#include <iomanip>
@ -73,7 +74,7 @@ using namespace Trace;
namespace {
// Threshold for lazy and space evaluation
constexpr Value LazyThreshold = Value(1500);
constexpr Value LazyThreshold = Value(1400);
constexpr Value SpaceThreshold = Value(12222);
// KingAttackWeights[PieceType] contains king attack weights by piece type
@ -132,6 +133,7 @@ namespace {
};
// Assorted bonuses and penalties
constexpr Score AttacksOnSpaceArea = S( 4, 0);
constexpr Score BishopPawns = S( 3, 7);
constexpr Score CorneredBishop = S( 50, 50);
constexpr Score FlankAttacks = S( 8, 0);
@ -140,7 +142,7 @@ namespace {
constexpr Score KnightOnQueen = S( 16, 12);
constexpr Score LongDiagonalBishop = S( 45, 0);
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 RestrictedPiece = S( 7, 7);
constexpr Score RookOnPawn = S( 10, 32);
@ -151,7 +153,6 @@ namespace {
constexpr Score ThreatBySafePawn = S(173, 94);
constexpr Score TrappedRook = S( 47, 4);
constexpr Score WeakQueen = S( 49, 15);
constexpr Score WeakUnopposedPawn = S( 12, 23);
#undef S
@ -186,15 +187,12 @@ namespace {
// is also calculated is ALL_PIECES.
Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
// attackedBy2[color] are the squares attacked by 2 pieces of a given color,
// possibly via x-ray or by one pawn and one piece. Diagonal x-ray through
// pawn or squares attacked by 2 pawns are not explicitly added.
// attackedBy2[color] are the squares attacked by at least 2 units of a given
// color, including x-rays. But diagonal x-rays through pawns are not computed.
Bitboard attackedBy2[COLOR_NB];
// kingRing[color] are the squares adjacent to the king, plus (only for a
// king on its first rank) the squares two ranks in front. For instance,
// if black's king is on g8, kingRing[BLACK] is f8, h8, f7, g7, h7, f6, g6
// and h6.
// kingRing[color] are the squares adjacent to the king plus some other
// very near squares, depending on king position.
Bitboard kingRing[COLOR_NB];
// 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][PAWN] = pe->pawn_attacks(Us);
attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
attackedBy2[Us] = (attackedBy[Us][KING] & attackedBy[Us][PAWN])
| dblAttackByPawn;
attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
// Init our king safety tables
kingRing[Us] = attackedBy[Us][KING];
@ -306,14 +303,12 @@ namespace {
if (Pt == BISHOP || Pt == KNIGHT)
{
// 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)
score += Outpost * (Pt == KNIGHT ? 4 : 2)
* (1 + bool(attackedBy[Us][PAWN] & s));
score += Outpost * (Pt == KNIGHT ? 2 : 1);
else if (bb &= b & ~pos.pieces(Us))
score += Outpost * (Pt == KNIGHT ? 2 : 1)
* (1 + bool(attackedBy[Us][PAWN] & bb));
else if (bb & b & ~pos.pieces(Us))
score += Outpost / (Pt == KNIGHT ? 1 : 2);
// Knight and Bishop bonus for being right behind a pawn
if (shift<Down>(pos.pieces(PAWN)) & s)
@ -358,8 +353,8 @@ namespace {
score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
// Bonus for rook on an open or semi-open file
if (pos.semiopen_file(Us, file_of(s)))
score += RookOnFile[bool(pos.semiopen_file(Them, file_of(s)))];
if (pos.is_on_semiopen_file(Us, s))
score += RookOnFile[bool(pos.is_on_semiopen_file(Them, s))];
// Penalty when trapped by the king, even more if the king cannot castle
else if (mob <= 3)
@ -558,10 +553,6 @@ namespace {
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
b = shift<Up>(pos.pieces(Us, PAWN)) & ~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 (pos.empty(blockSq))
{
// If there is a rook or queen attacking/defending the pawn from behind,
// consider all the squaresToQueen. Otherwise consider only the squares
// in the pawn's path attacked or occupied by the enemy.
defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s);
defendedSquares = squaresToQueen = forward_file_bb(Us, s);
unsafeSquares = passed_pawn_span(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))
defendedSquares &= attackedBy[Us][ALL_PIECES];
@ -658,21 +647,21 @@ namespace {
if (!(pos.pieces(Them) & bb))
unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
// If there aren't any enemy attacks, assign a big bonus. Otherwise
// assign a smaller bonus if the block square isn't attacked.
int k = !unsafeSquares ? 20 : !(unsafeSquares & blockSq) ? 9 : 0;
// If there are no enemy attacks on passed pawn span, assign a big bonus.
// Otherwise assign a smaller bonus if the path to queen is not attacked
// 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.
// Otherwise assign a smaller bonus if the block square is defended.
if (defendedSquares == squaresToQueen)
k += 6;
else if (defendedSquares & blockSq)
k += 4;
// Assign a larger bonus if the block square is defended.
if (defendedSquares & blockSq)
k += 5;
bonus += make_score(k * w, k * w);
}
} // rank > RANK_3
} // r > RANK_3
// Scale down bonus for candidate passers which need more than one
// 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
Bitboard behind = pos.pieces(Us, PAWN);
behind |= shift<Down>(behind);
behind |= shift<Down>(shift<Down>(behind));
behind |= shift<Down+Down>(behind);
int bonus = popcount(safe) + popcount(behind & safe);
int weight = pos.count<ALL_PIECES>(Us)
- (16 - pos.count<PAWN>()) / 4;
int weight = pos.count<ALL_PIECES>(Us) - 1;
Score score = make_score(bonus * weight * weight / 16, 0);
score -= AttacksOnSpaceArea * popcount(attackedBy[Them][ALL_PIECES] & behind & safe);
if (T)
Trace::add(SPACE, Us, score);
@ -777,8 +766,7 @@ namespace {
if (sf == SCALE_FACTOR_NORMAL)
{
if ( pos.opposite_bishops()
&& pos.non_pawn_material(WHITE) == BishopValueMg
&& pos.non_pawn_material(BLACK) == BishopValueMg)
&& pos.non_pawn_material() == 2 * BishopValueMg)
sf = 16 + 4 * pe->passed_count();
else
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
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;
// Main evaluation begins here

View file

@ -26,6 +26,7 @@
#include "thread.h"
#include "tt.h"
#include "uci.h"
#include "endgame.h"
#include "syzygy/tbprobe.h"
namespace PSQT {
@ -43,6 +44,7 @@ int main(int argc, char* argv[]) {
Bitboards::init();
Position::init();
Bitbases::init();
Endgames::init();
Search::init();
Threads.set(Options["Threads"]);
Search::clear(); // After threads are up

View file

@ -137,7 +137,7 @@ Entry* probe(const Position& pos) {
// Let's look if we have a specialized evaluation function for this particular
// material configuration. Firstly we look for a fixed configuration one, then
// 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;
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
// 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)
{

View file

@ -145,7 +145,7 @@ const string engine_info(bool to_uci) {
/// 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 c, bool b) { if (c) dbg_hit_on(b); }
@ -210,12 +210,6 @@ void prefetch(void* addr) {
#endif
void prefetch2(void* addr) {
prefetch(addr);
prefetch((uint8_t*)addr + 64);
}
namespace WinProcGroup {
#ifndef _WIN32

View file

@ -31,7 +31,6 @@
const std::string engine_info(bool to_uci = false);
void prefetch(void* addr);
void prefetch2(void* addr);
void start_logger(const std::string& fname);
void dbg_hit_on(bool b);

View file

@ -200,11 +200,15 @@ top:
/* fallthrough */
case QUIET_INIT:
cur = endBadCaptures;
endMoves = generate<QUIETS>(pos, cur);
if (!skipQuiets)
{
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;
/* fallthrough */

View file

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include "bitboard.h"
@ -34,9 +35,11 @@ namespace {
constexpr Score Backward = S( 9, 24);
constexpr Score Doubled = S(11, 56);
constexpr Score Isolated = S( 5, 15);
constexpr Score WeakUnopposed = S( 13, 27);
constexpr Score Attacked2Unsupported = S( 0, 20);
// 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].
// 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].
// RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn
// 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] = {
{ V( 89), V(107), V(123), V(93), V(57), V( 45), V( 51) },
{ 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(-10), V(-14), V( 90), V(15), V( 2), V( -7), V(-16) }
{ 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( 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) }
};
#undef S
@ -76,16 +80,20 @@ namespace {
Bitboard ourPawns = pos.pieces( Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0;
e->kingSquares[Us] = SQ_NONE;
e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns);
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0;
e->kingSquares[Us] = SQ_NONE;
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
while ((s = *pl++) != SQ_NONE)
{
assert(pos.piece_on(s) == make_piece(Us, PAWN));
File f = file_of(s);
Rank r = relative_rank(Us, s);
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
@ -96,7 +104,7 @@ namespace {
lever = theirPawns & PawnAttacks[Us][s];
leverPush = theirPawns & PawnAttacks[Us][s + Up];
doubled = ourPawns & (s - Up);
neighbours = ourPawns & adjacent_files_bb(f);
neighbours = ourPawns & adjacent_files_bb(s);
phalanx = neighbours & rank_bb(s);
support = neighbours & rank_bb(s - Up);
@ -109,9 +117,8 @@ namespace {
// full attack info to evaluate them. Include also not passed pawns
// which could become passed after one or two pawn pushes when are
// not attacked more times than defended.
if ( !(stoppers ^ lever ^ leverPush)
&& (support || !more_than_one(lever))
&& popcount(phalanx) >= popcount(leverPush))
if ( !(stoppers ^ lever) ||
(!(stoppers ^ leverPush) && popcount(phalanx) >= popcount(leverPush)))
e->passedPawns[Us] |= s;
else if (stoppers == square_bb(s + Up) && r >= RANK_5)
@ -125,15 +132,16 @@ namespace {
// Score this pawn
if (support | phalanx)
{
int v = (phalanx ? 3 : 2) * Connected[r];
v = 17 * popcount(support) + (v >> (opposed + 1));
int v = Connected[r] * (phalanx ? 3 : 2) / (opposed ? 2 : 1)
+ 17 * popcount(support);
score += make_score(v, v * (r - 2) / 4);
}
else if (!neighbours)
score -= Isolated, e->weakUnopposed[Us] += !opposed;
score -= Isolated + WeakUnopposed * int(!opposed);
else if (backward)
score -= Backward, e->weakUnopposed[Us] += !opposed;
score -= Backward + WeakUnopposed * int(!opposed);
if (doubled && !support)
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.
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 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 ourPawns = b & pos.pieces(Us);
Bitboard theirPawns = b & pos.pieces(Them);
Value safety = (shift<Down>(theirPawns) & (FileABB | FileHBB) & BlockRanks & ksq) ?
Value(374) : Value(5);
Value bonus[] = { Value(5), Value(5) };
File center = clamp(file_of(ksq), FILE_B, FILE_G);
for (File f = File(center - 1); f <= File(center + 1); ++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);
Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
int d = std::min(f, ~f);
safety += ShelterStrength[d][ourRank];
safety -= (ourRank && (ourRank == theirRank - 1)) ? 66 * (theirRank == RANK_3)
: UnblockedStorm[d][theirRank];
bonus[MG] += ShelterStrength[d][ourRank];
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)
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 (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))
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

View file

@ -37,8 +37,7 @@ struct Entry {
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[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>
Score king_safety(const Position& pos) {
@ -50,7 +49,7 @@ struct Entry {
Score do_king_safety(const Position& pos);
template<Color Us>
Value evaluate_shelter(const Position& pos, Square ksq);
void evaluate_shelter(const Position& pos, Square ksq, Score& shelter);
Key key;
Score scores[COLOR_NB];
@ -59,12 +58,10 @@ struct Entry {
Bitboard pawnAttacksSpan[COLOR_NB];
Square kingSquares[COLOR_NB];
Score kingSafety[COLOR_NB];
int weakUnopposed[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);

View file

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include <cstddef> // For offsetof()
#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 rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
& ~(square_bb(kfrom) | rfrom);
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
& ~(square_bb(kfrom) | rfrom);
}
@ -382,6 +383,12 @@ void Position::set_state(StateInfo* si) const {
Square s = pop_lsb(&b);
Piece pc = piece_on(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)
@ -392,20 +399,9 @@ void Position::set_state(StateInfo* si) const {
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)
{
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)
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
Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK))
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
Bitboard occupancy = pieces() & ~snipers;
Bitboard occupancy = pieces() ^ 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
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
prefetch2(thisThread->pawnsTable[st->pawnKey]);
// Reset rule 50 draw counter
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
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());
}
@ -994,6 +1008,8 @@ void Position::do_null_move(StateInfo& newSt) {
set_check_info(st);
st->repetition = 0;
assert(pos_is_ok());
}
@ -1117,24 +1133,10 @@ bool Position::is_draw(int ply) const {
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
return true;
int end = std::min(st->rule50, st->pliesFromNull);
if (end < 4)
return false;
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 a draw score if a position repeats once earlier but strictly
// after the root, or repeats twice before or at the root.
if (st->repetition && st->repetition < ply)
return true;
return false;
}
@ -1146,26 +1148,15 @@ bool Position::is_draw(int ply) const {
bool Position::has_repeated() const {
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 (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);
if (stc->repetition)
return true;
stc = stc->previous;
}
return false;
}
@ -1198,22 +1189,19 @@ bool Position::has_game_cycle(int ply) const {
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)
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
StateInfo* next_stp = stp;
for (int k = i + 2; k <= end; k += 2)
{
next_stp = next_stp->previous->previous;
if (next_stp->key == stp->key)
return true;
}
if (stp->repetition)
return true;
}
}
}

View file

@ -46,6 +46,7 @@ struct StateInfo {
Square epSquare;
// Not copied when making a move (will be recomputed anyhow)
int repetition;
Key key;
Bitboard checkersBB;
Piece capturedPiece;
@ -95,7 +96,7 @@ public:
template<PieceType Pt> int count() const;
template<PieceType Pt> const Square* squares(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
int castling_rights(Color c) const;
@ -262,8 +263,8 @@ inline Square Position::ep_square() const {
return st->epSquare;
}
inline int Position::semiopen_file(Color c, File f) const {
return !(pieces(c, PAWN) & file_bb(f));
inline bool Position::is_on_semiopen_file(Color c, Square s) const {
return !(pieces(c, PAWN) & file_bb(s));
}
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 {
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 {

View file

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstring> // For std::memset
@ -70,9 +71,9 @@ namespace {
// Reductions lookup table, initialized at startup
int Reductions[MAX_MOVES]; // [depth or moveNumber]
template <bool PvNode> Depth reduction(bool i, Depth d, int mn) {
int r = Reductions[d / ONE_PLY] * Reductions[mn] / 1024;
return ((r + 512) / 1024 + (!i && r > 1024) - PvNode) * ONE_PLY;
Depth reduction(bool i, Depth d, int mn) {
int r = Reductions[d / ONE_PLY] * Reductions[mn];
return ((r + 512) / 1024 + (!i && r > 1024)) * ONE_PLY;
}
constexpr int futility_move_count(bool improving, int depth) {
@ -102,6 +103,48 @@ namespace {
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>
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
@ -149,7 +192,7 @@ namespace {
void Search::init() {
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)
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)
{
int64_t s = th->rootMoves[0].score - minScore + 1;
votes[th->rootMoves[0].pv[0]] += 200 + s * s * int(th->completedDepth);
}
votes[th->rootMoves[0].pv[0]] +=
(th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
// Select best thread
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]];
if (votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])
bestThread = th;
}
}
}
@ -326,8 +363,13 @@ void Thread::search() {
bestValue = delta = alpha = -VALUE_INFINITE;
beta = VALUE_INFINITE;
size_t multiPV = Options["MultiPV"];
Skill skill(Options["Skill Level"]);
multiPV = Options["MultiPV"];
// 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
// use behind the scenes to retrieve a set of possible moves.
@ -439,17 +481,14 @@ void Thread::search() {
beta = (alpha + beta) / 2;
alpha = std::max(bestValue - delta, -VALUE_INFINITE);
failedHighCnt = 0;
if (mainThread)
{
failedHighCnt = 0;
mainThread->stopOnPonderhit = false;
}
}
else if (bestValue >= beta)
{
beta = std::min(bestValue + delta, VALUE_INFINITE);
if (mainThread)
++failedHighCnt;
++failedHighCnt;
}
else
break;
@ -579,13 +618,13 @@ namespace {
bool ttHit, ttPv, inCheck, givesCheck, improving;
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture;
Piece movedPiece;
int moveCount, captureCount, quietCount;
int moveCount, captureCount, quietCount, singularLMR;
// Step 1. Initialize node
Thread* thisThread = pos.this_thread();
inCheck = pos.checkers();
Color us = pos.side_to_move();
moveCount = captureCount = quietCount = ss->moveCount = 0;
moveCount = captureCount = quietCount = singularLMR = ss->moveCount = 0;
bestValue = -VALUE_INFINITE;
maxValue = VALUE_INFINITE;
@ -630,7 +669,10 @@ namespace {
// starts with statScore = 0. Later grandchildren start with the last calculated
// statScore of the previous grandchild. This influences the reduction rules in
// 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
// 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;
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
: ttHit ? tte->move() : MOVE_NONE;
ttPv = (ttHit && tte->is_pv()) || (PvNode && depth > 4 * ONE_PLY);
// 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;
ttPv = PvNode || (ttHit && tte->is_pv());
// At non-PV nodes we check for an early TT cutoff
if ( !PvNode
@ -901,6 +934,9 @@ moves_loop: // When in check, search starts from here
moveCountPruning = false;
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
// or a beta cutoff occurs.
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
<< " currmove " << UCI::move(move, pos.is_chess960())
<< " 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)
(ss+1)->pv = nullptr;
@ -943,7 +985,7 @@ moves_loop: // When in check, search starts from here
&& move == ttMove
&& !rootNode
&& !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
&& (tte->bound() & BOUND_LOWER)
&& tte->depth() >= depth - 3 * ONE_PLY
@ -956,15 +998,22 @@ moves_loop: // When in check, search starts from here
ss->excludedMove = MOVE_NONE;
if (value < singularBeta)
{
extension = ONE_PLY;
singularLMR++;
if (value < singularBeta - std::min(3 * depth / ONE_PLY, 39))
singularLMR++;
}
// Multi-cut pruning
// 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,
// that is multiple moves fail high, and we can prune the whole subtree by returning
// the hard beta bound.
else if (cutNode && singularBeta > beta)
return beta;
// that multiple moves fail high, and we can prune the whole subtree by returning
// a soft bound.
else if ( eval >= beta
&& singularBeta >= beta)
return singularBeta;
}
// Check extension (~2 Elo)
@ -979,9 +1028,8 @@ moves_loop: // When in check, search starts from here
// Shuffle extension
else if ( PvNode
&& pos.rule50_count() > 18
&& ss->ply > 18
&& 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;
// Passed pawn extension
@ -1003,14 +1051,14 @@ moves_loop: // When in check, search starts from here
if ( !captureOrPromotion
&& !givesCheck
&& !pos.advanced_pawn_push(move))
&& (!pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg))
{
// Move count based pruning (~30 Elo)
if (moveCountPruning)
continue;
// 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;
// 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)))
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;
}
@ -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
// re-searched at full depth.
if ( depth >= 3 * ONE_PLY
&& moveCount > 1
&& (!captureOrPromotion || moveCountPruning))
&& moveCount > 1 + 3 * rootNode
&& ( !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
if (ttPv)
r -= ONE_PLY;
r -= 2 * ONE_PLY;
// Decrease reduction if opponent's move count is high (~10 Elo)
if ((ss-1)->moveCount > 15)
r -= ONE_PLY;
// Decrease reduction if move has been singularly extended
r -= singularLMR * ONE_PLY;
if (!captureOrPromotion)
{
// 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;
}
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);
@ -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
// search function with depth zero, or recursively with depth less than ONE_PLY.
// qsearch() is the quiescence search function, which is called by the main search
// function with zero depth, or recursively with further decreasing depth per call.
template <NodeType NT>
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
if ( (!inCheck || evasionPrunable)
&& (!givesCheck || !(pos.blockers_for_king(~pos.side_to_move()) & from_sq(move)))
&& !pos.see_ge(move))
continue;
@ -1519,7 +1578,7 @@ moves_loop: // When in check, search starts from here
void update_capture_stats(const Position& pos, Move move,
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);
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
{
// Assign the same rank to all moves
// Clean up if root_probe() and root_probe_wdl() have failed
for (auto& m : rootMoves)
m.tbRank = 0;
}

View file

@ -70,7 +70,7 @@ struct RootMove {
Value score = -VALUE_INFINITE;
Value previousScore = -VALUE_INFINITE;
int selDepth = 0;
int tbRank;
int tbRank = 0;
Value tbScore;
std::vector<Move> pv;
};

View file

@ -20,6 +20,7 @@
#include <cassert>
#include <algorithm> // For std::count
#include "movegen.h"
#include "search.h"
#include "thread.h"
@ -193,7 +194,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
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->rootMoves = rootMoves;
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);

View file

@ -60,8 +60,7 @@ public:
Pawns::Table pawnsTable;
Material::Table materialTable;
Endgames endgames;
size_t pvIdx, pvLast;
size_t pvIdx, multiPV, pvLast, shuffleExts;
int selDepth, nmpMinPly;
Color nmpColor;
std::atomic<uint64_t> nodes, tbHits, TTsaves, bestMoveChanges;

View file

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cfloat>
#include <cmath>

View file

@ -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
if ( (k >> 48) != key16
|| d / ONE_PLY > depth8 - 4
||(d - DEPTH_OFFSET) / ONE_PLY > depth8 - 4
|| b == BOUND_EXACT)
{
assert((d - DEPTH_OFFSET) / ONE_PLY >= 0);
key16 = (uint16_t)(k >> 48);
value16 = (int16_t)v;
eval16 = (int16_t)ev;
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
depth8 = (int8_t)(d / ONE_PLY);
depth8 = (uint8_t)((d - DEPTH_OFFSET) / ONE_PLY);
}
}

View file

@ -45,7 +45,7 @@ struct TTEntry {
Move move() const { return (Move )move16; }
Value value() const { return (Value)value16; }
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); }
Bound bound() const { return (Bound)(genBound8 & 0x3); }
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 eval16;
uint8_t genBound8;
int8_t depth8;
uint8_t depth8;
};

View file

@ -101,7 +101,7 @@ typedef uint64_t Key;
typedef uint64_t Bitboard;
constexpr int MAX_MOVES = 256;
constexpr int MAX_PLY = 128;
constexpr int MAX_PLY = 246;
/// A move needs 16 bits to be stored
///
@ -213,8 +213,9 @@ enum Depth : int {
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
DEPTH_NONE = -6 * ONE_PLY,
DEPTH_MAX = MAX_PLY * ONE_PLY
DEPTH_NONE = -6 * 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");
@ -290,7 +291,6 @@ inline T& operator--(T& d) { return d = T(int(d) - 1); }
#define ENABLE_FULL_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*(T d, int i) { return T(int(d) * i); } \
constexpr T operator/(T d, int i) { return T(int(d) / i); } \

View file

@ -166,7 +166,7 @@ namespace {
}
else if (token == "setoption") setoption(is);
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'

View file

@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include <ostream>
#include <sstream>