## Overview
[](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
derived from Glaurung 2.1. It is not a complete chess program and requires a
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
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
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.
### 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
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.
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");
+ 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
};
-/// 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().
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);
const EndgameBase<T>* probe(Key key) {
return map<T>().count(key) ? map<T>()[key].get() : nullptr;
}
-
- void init();
}
#endif // #ifndef ENDGAME_H_INCLUDED
// 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
// Early exit if score is high
Value v = (mg_value(score) + eg_value(score)) / 2;
- if (abs(v) > (LazyThreshold + pos.non_pawn_material() / 64))
+ if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64)
return pos.side_to_move() == WHITE ? v : -v;
// Main evaluation begins here
Bitboards::init();
Position::init();
Bitbases::init();
- Search::init();
Endgames::init();
+ Search::init();
Threads.set(Options["Threads"]);
Search::clear(); // After threads are up
Square kingSquares[COLOR_NB];
Score kingSafety[COLOR_NB];
int castlingRights[COLOR_NB];
- int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
};
typedef HashTable<Entry, 16384> Table;
if (type_of(pc) == PAWN)
si->pawnKey ^= Zobrist::psq[pc][s];
- else if (type_of(pc) != PAWN && type_of(pc) != KING)
+ else if (type_of(pc) != KING)
si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc];
}
// 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)
{
if (ply > i)
return true;
- // For nodes before or at the root, check that the move is a repetition one
- // 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.
+ // 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;
void Search::init() {
for (int i = 1; i < MAX_MOVES; ++i)
- Reductions[i] = int(22.9 * std::log(i));
+ Reductions[i] = int(22.9 * std::log(i));
}
minScore = std::min(minScore, th->rootMoves[0].score);
// Vote according to score and depth, and select the best thread
- int64_t bestVote = 0;
for (Thread* th : Threads)
{
votes[th->rootMoves[0].pv[0]] +=
- (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
+ (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
- 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;
- }
}
}
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;
// 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.
- if (rootNode)
- (ss + 4)->statScore = 0;
- else
- (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
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
moveCountPruning = false;
ttCapture = ttMove && pos.capture_or_promotion(ttMove);
- int singularExtensionLMRmultiplier = 0;
// Step 12. Loop through all pseudo-legal moves until no moves remain
// or a beta cutoff occurs.
ss->excludedMove = MOVE_NONE;
if (value < singularBeta)
- {
+ {
extension = ONE_PLY;
- singularExtensionLMRmultiplier++;
+ singularLMR++;
+
if (value < singularBeta - std::min(3 * depth / ONE_PLY, 39))
- singularExtensionLMRmultiplier++;
- }
+ singularLMR++;
+ }
// Multi-cut pruning
// Our ttMove is assumed to fail high, and now we failed high also on a reduced
// 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 -= singularExtensionLMRmultiplier * ONE_PLY;
+ r -= singularLMR * ONE_PLY;
if (!captureOrPromotion)
{
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);
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)));
if (dtz_available || rootMoves[0].tbScore <= VALUE_DRAW)
Cardinality = 0;
}
- else
- {
- // Assign the same rank to all moves
- for (auto& m : rootMoves)
- m.tbRank = 0;
- }
}
Value score = -VALUE_INFINITE;
Value previousScore = -VALUE_INFINITE;
int selDepth = 0;
- int tbRank;
+ int tbRank = 0;
Value tbScore;
std::vector<Move> pv;
};
// Overwrite less valuable entries
if ( (k >> 48) != key16
- || d / ONE_PLY + 10 > depth8
+ ||(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);
- assert((d - DEPTH_NONE) / ONE_PLY >= 0);
- depth8 = (uint8_t)((d - DEPTH_NONE) / ONE_PLY);
+ depth8 = (uint8_t)((d - DEPTH_OFFSET) / ONE_PLY);
}
}
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_NONE; }
+ 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);
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");