[](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 features two evaluation functions, the classical
-evaluation based on handcrafted terms, and the NNUE evaluation based on
-efficiently updateable neural networks. The classical evaluation runs efficiently
-on most 64bit CPU architectures, while the NNUE evaluation benefits strongly from the
-vector intrinsics available on modern CPUs (avx2 or similar).
+derived from Glaurung 2.1. Stockfish is not a complete chess program and requires a
+UCI-compatible graphical user interface (GUI) (e.g. XBoard with PolyGlot, Scid,
+Cute Chess, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order
+to be used comfortably. Read the documentation for your GUI of choice for information
+about how to use Stockfish with it.
-Stockfish is not a complete chess program and requires a
-UCI-compatible GUI (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, Arena,
-Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably.
-Read the documentation for your GUI of choice for information about how to use
-Stockfish with it.
+The Stockfish engine features two evaluation functions for chess, the classical
+evaluation based on handcrafted terms, and the NNUE evaluation based on efficiently
+updateable neural networks. The classical evaluation runs efficiently on most 64bit
+CPU architectures, while the NNUE evaluation benefits strongly from the vector
+intrinsics available on modern CPUs (avx2 or similar).
## Files
* src, a subdirectory containing the full source code, including a Makefile
that can be used to compile Stockfish on Unix-like systems.
-To use the NNUE evaluation an additional data file with neural network parameters
-needs to be downloaded. The filename for the default set can be found as the default
-value of the `EvalFile` UCI option, with the format
-`nn-[SHA256 first 12 digits].nnue` (e.g. nn-c157e0a5755b.nnue). This file can be downloaded from
+ * a file with the .nnue extension, storing the neural network for the NNUE
+ evaluation.
+
+Note: to use the NNUE evaluation, the additional data file with neural network parameters
+needs to be downloaded. The filename for the default net can be found as the default
+value of the `EvalFile` UCI option, with the format `nn-[SHA256 first 12 digits].nnue`
+(for instance, `nn-c157e0a5755b.nnue`). This file can be downloaded from
```
https://tests.stockfishchess.org/api/nn/[filename]
```
The name of the file of the NNUE evaluation parameters. Depending on the GUI the
filename should include the full path to the folder/directory that contains the file.
- * #### Contempt
- A positive value for contempt favors middle game positions and avoids draws,
- effective for the classical evaluation only.
-
- * #### Analysis Contempt
- 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.
-
* #### UCI_AnalyseMode
An option handled by your GUI.
Limit Syzygy tablebase probing to positions with at most this many pieces left
(including kings and pawns).
+ * #### Contempt
+ A positive value for contempt favors middle game positions and avoids draws,
+ effective for the classical evaluation only.
+
+ * #### Analysis Contempt
+ 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.
+
* #### Move Overhead
Assume a time delay of x ms due to network and GUI overheads. This is useful to
avoid losses on time in those cases.
* #### Debug Log File
Write all communication to and from the engine into a text file.
-## Classical and NNUE evaluation
+## A note on classical and NNUE evaluation
Both approaches assign a value to a position that is used in alpha-beta (PVS) search
to find the best move. The classical evaluation computes this value as a function
cd src
make help
make build ARCH=x86-64-modern
+ make net
```
When not using the Makefile to compile (for instance with Microsoft MSVC) you
be found by typing the following commands in a console:
```
- ./stockfish
- compiler
+ ./stockfish compiler
```
## Understanding the code base and participating in the project
endif
endif
-ifeq ($(KERNEL),Darwin)
+ifeq ($(KERNEL),Darwin)
CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.14
LDFLAGS += -arch $(arch) -mmacosx-version-min=10.14
XCRUN = xcrun
Bitboard BishopTable[0x1480]; // To store bishop attacks
void init_magics(PieceType pt, Bitboard table[], Magic magics[]);
+
+}
+
+
+/// safe_destination() returns the bitboard of target square for the given step
+/// from the given square. If the step is off the board, returns empty bitboard.
+
+inline Bitboard safe_destination(Square s, int step) {
+ Square to = Square(s + step);
+ return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
}
Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
- for(Direction d : (pt == ROOK ? RookDirections : BishopDirections))
+ for (Direction d : (pt == ROOK ? RookDirections : BishopDirections))
{
Square s = sq;
while(safe_destination(s, d) && !(occupied & s))
inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); }
-/// safe_destination() returns the bitboard of target square for the given step
-/// from the given square. If the step is off the board, returns empty bitboard.
-
-inline Bitboard safe_destination(Square s, int step)
-{
- Square to = Square(s + step);
- return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
-}
-
-
/// attacks_bb(Square) returns the pseudo attacks of the give piece type
/// assuming an empty board.
attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
// Init our king safety tables
- Square s = make_square(Utility::clamp(file_of(ksq), FILE_B, FILE_G),
- Utility::clamp(rank_of(ksq), RANK_2, RANK_7));
+ Square s = make_square(std::clamp(file_of(ksq), FILE_B, FILE_G),
+ std::clamp(rank_of(ksq), RANK_2, RANK_7));
kingRing[Us] = attacks_bb<KING>(s) | s;
kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
Square blockSq = s + Up;
// Adjust bonus based on the king's proximity
- bonus += make_score(0, ( (king_proximity(Them, blockSq) * 19) / 4
- - king_proximity(Us, blockSq) * 2) * w);
+ bonus += make_score(0, ( king_proximity(Them, blockSq) * 19 / 4
+ - king_proximity(Us, blockSq) * 2) * w);
// If blockSq is not the queening square then consider also a second push
if (r != RANK_7)
// Evaluation::space() computes a space evaluation for a given side, aiming to improve game
- // play in the opening. It is based on the number of safe squares on the 4 central files
+ // play in the opening. It is based on the number of safe squares on the four central files
// on ranks 2 to 4. Completely safe squares behind a friendly pawn are counted twice.
// Finally, the space bonus is multiplied by a weight which decreases according to occupancy.
// Now apply the bonus: note that we find the attacking side by extracting the
// sign of the midgame or endgame values, and that we carefully cap the bonus
// so that the midgame and endgame scores do not change sign after the bonus.
- int u = ((mg > 0) - (mg < 0)) * Utility::clamp(complexity + 50, -abs(mg), 0);
+ int u = ((mg > 0) - (mg < 0)) * std::clamp(complexity + 50, -abs(mg), 0);
int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg));
mg += u;
// Damp down the evaluation linearly when shuffling
v = v * (100 - pos.rule50_count()) / 100;
- // Guarantee evalution outside of TB range
- v = Utility::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
+ // Guarantee evaluation does not hit the tablebase range
+ v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
return v;
}
v = pos.side_to_move() == WHITE ? v : -v;
ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n";
-
return ss.str();
}
Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK);
- Value npm = Utility::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
+ Value npm = std::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
// Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
#endif
-/// Wrappers for systems where the c++17 implementation doesn't guarantee the availability of aligned_alloc.
-/// Memory allocated with std_aligned_alloc must be freed with std_aligned_free.
-///
+
+/// std_aligned_alloc() is our wrapper for systems where the c++17 implementation
+/// does not guarantee the availability of aligned_alloc(). Memory allocated with
+/// std_aligned_alloc() must be freed with std_aligned_free().
void* std_aligned_alloc(size_t alignment, size_t size) {
+
#if defined(POSIXALIGNEDALLOC)
- void *pointer;
- if(posix_memalign(&pointer, alignment, size) == 0)
- return pointer;
- return nullptr;
+ void *mem;
+ return posix_memalign(&mem, alignment, size) ? nullptr : mem;
#elif defined(_WIN32)
return _mm_malloc(size, alignment);
#else
}
void std_aligned_free(void* ptr) {
+
#if defined(POSIXALIGNEDALLOC)
free(ptr);
#elif defined(_WIN32)
#endif
}
-/// aligned_ttmem_alloc() will return suitably aligned memory, and if possible use large pages.
+/// aligned_ttmem_alloc() will return suitably aligned memory, if possible using large pages.
/// The returned pointer is the aligned one, while the mem argument is the one that needs
/// to be passed to free. With c++17 some of this functionality could be simplified.
#define sync_cout std::cout << IO_LOCK
#define sync_endl std::endl << IO_UNLOCK
-namespace Utility {
-
-/// Clamp a value between lo and hi. Available in c++17.
-template<class T> constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
- return v < lo ? lo : v > hi ? hi : v;
-}
-
-}
/// xorshift64star Pseudo-Random Number Generator
/// This class is based on original code written and dedicated
*moveList++ = make_move(ksq, pop_lsb(&b));
if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING))
- for(CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } )
+ for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } )
if (!pos.castling_impeded(cr) && pos.can_castle(cr))
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(cr));
}
--endMoves;
++stage;
- /* fallthrough */
+ [[fallthrough]];
case REFUTATION:
if (select<Next>([&](){ return *cur != MOVE_NONE
&& pos.pseudo_legal(*cur); }))
return *(cur - 1);
++stage;
- /* fallthrough */
+ [[fallthrough]];
case QUIET_INIT:
if (!skipQuiets)
}
++stage;
- /* fallthrough */
+ [[fallthrough]];
case QUIET:
if ( !skipQuiets
endMoves = endBadCaptures;
++stage;
- /* fallthrough */
+ [[fallthrough]];
case BAD_CAPTURE:
return select<Next>([](){ return true; });
score<EVASIONS>();
++stage;
- /* fallthrough */
+ [[fallthrough]];
case EVASION:
return select<Best>([](){ return true; });
return MOVE_NONE;
++stage;
- /* fallthrough */
+ [[fallthrough]];
case QCHECK_INIT:
cur = moves;
endMoves = generate<QUIET_CHECKS>(pos, cur);
++stage;
- /* fallthrough */
+ [[fallthrough]];
case QCHECK:
return select<Next>([](){ return true; });
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
typedef Stats<int16_t, 10692, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
-/// At higher depths LowPlyHistory records successful quiet moves near the root and quiet
-/// moves which are/were in the PV (ttPv)
-/// It is cleared with each new search and filled during iterative deepening
+/// At higher depths LowPlyHistory records successful quiet moves near the root
+/// and quiet moves which are/were in the PV (ttPv). It is cleared with each new
+/// search and filled during iterative deepening.
constexpr int MAX_LPH = 4;
typedef Stats<int16_t, 10692, MAX_LPH, int(SQUARE_NB) * int(SQUARE_NB)> LowPlyHistory;
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
-/// move, see www.chessprogramming.org/Countermove_Heuristic
+/// move, see www.chessprogramming.org/Countermove_Heuristic
typedef Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB> CounterMoveHistory;
/// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
Score bonus = make_score(5, 5);
- File center = Utility::clamp(file_of(ksq), FILE_B, FILE_G);
+ File center = std::clamp(file_of(ksq), FILE_B, FILE_G);
for (File f = File(center - 1); f <= File(center + 1); ++f)
{
b = ourPawns & file_bb(f);
// Don't allow pinned pieces to attack (except the king) as long as
// there are pinners on their original square.
- if (st->pinners[~stm] & occupied)
- stmAttackers &= ~st->blockersForKing[stm];
+ if (pinners(~stm) & occupied)
+ stmAttackers &= ~blockers_for_king(stm);
if (!stmAttackers)
break;
Bitboard checkers() const;
Bitboard blockers_for_king(Color c) const;
Bitboard check_squares(PieceType pt) const;
+ Bitboard pinners(Color c) const;
bool is_discovery_check_on_king(Color c, Move m) const;
// Attacks to/from a given square
return st->blockersForKing[c];
}
+inline Bitboard Position::pinners(Color c) const {
+ return st->pinners[c];
+}
+
inline Bitboard Position::check_squares(PieceType pt) const {
return st->checkSquares[pt];
}
// for match (TC 60+0.6) results spanning a wide range of k values.
PRNG rng(now());
double floatLevel = Options["UCI_LimitStrength"] ?
- Utility::clamp(std::pow((Options["UCI_Elo"] - 1346.6) / 143.4, 1 / 0.806), 0.0, 20.0) :
+ std::clamp(std::pow((Options["UCI_Elo"] - 1346.6) / 143.4, 1 / 0.806), 0.0, 20.0) :
double(Options["Skill Level"]);
int intLevel = int(floatLevel) +
((floatLevel - int(floatLevel)) * 1024 > rng.rand<unsigned>() % 1024 ? 1 : 0);
{
double fallingEval = (318 + 6 * (mainThread->bestPreviousScore - bestValue)
+ 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 825.0;
- fallingEval = Utility::clamp(fallingEval, 0.5, 1.5);
+ fallingEval = std::clamp(fallingEval, 0.5, 1.5);
// If the bestMove is stable over several iterations, reduce time accordingly
timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.92 : 0.95;
&& eval <= alpha - RazorMargin)
return qsearch<NT>(pos, ss, alpha, beta);
- improving = (ss-2)->staticEval == VALUE_NONE ? (ss->staticEval > (ss-4)->staticEval
- || (ss-4)->staticEval == VALUE_NONE) : ss->staticEval > (ss-2)->staticEval;
+ improving = (ss-2)->staticEval == VALUE_NONE
+ ? ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE
+ : ss->staticEval > (ss-2)->staticEval;
// Step 8. Futility pruning: child node (~50 Elo)
if ( !PvNode
// there and in further interactions with transposition table cutoff depth is set to depth - 3
// because probCut search has depth set to depth - 4 but we also do a move before it
// so effective depth is equal to depth - 3
- && !( ttHit
- && tte->depth() >= depth - 3
+ && !( ttHit
+ && tte->depth() >= depth - 3
&& ttValue != VALUE_NONE
&& ttValue < probCutBeta))
{
r++;
}
- Depth d = Utility::clamp(newDepth - r, 1, newDepth);
+ Depth d = std::clamp(newDepth - r, 1, newDepth);
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
TimePoint slowMover = TimePoint(Options["Slow Mover"]);
TimePoint npmsec = TimePoint(Options["nodestime"]);
- // opt_scale is a percentage of available time to use for the current move.
- // max_scale is a multiplier applied to optimumTime.
- double opt_scale, max_scale;
+ // optScale is a percentage of available time to use for the current move.
+ // maxScale is a multiplier applied to optimumTime.
+ double optScale, maxScale;
// If we have to play in 'nodes as time' mode, then convert from time
// to nodes, and use resulting values in time management formulas.
// game time for the current move, so also cap to 20% of available game time.
if (limits.movestogo == 0)
{
- opt_scale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0,
+ optScale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0,
0.2 * limits.time[us] / double(timeLeft));
- max_scale = std::min(7.0, 4.0 + ply / 12.0);
+ maxScale = std::min(7.0, 4.0 + ply / 12.0);
}
// x moves in y seconds (+ z increment)
else
{
- opt_scale = std::min((0.8 + ply / 128.0) / mtg,
+ optScale = std::min((0.8 + ply / 128.0) / mtg,
0.8 * limits.time[us] / double(timeLeft));
- max_scale = std::min(6.3, 1.5 + 0.11 * mtg);
+ maxScale = std::min(6.3, 1.5 + 0.11 * mtg);
}
// Never use more than 80% of the available time for this move
- optimumTime = TimePoint(opt_scale * timeLeft);
- maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, max_scale * optimumTime));
+ optimumTime = TimePoint(optScale * timeLeft);
+ maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime));
if (Options["Ponder"])
optimumTime += optimumTime / 4;
double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3];
// Transform eval to centipawns with limited range
- double x = Utility::clamp(double(100 * v) / PawnValueEg, -1000.0, 1000.0);
+ double x = std::clamp(double(100 * v) / PawnValueEg, -1000.0, 1000.0);
// Return win rate in per mille (rounded to nearest)
return int(0.5 + 1000 / (1 + std::exp((a - x) / b)));