1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-05-02 09:39:36 +00:00

Merge branch 'master' into clusterMergeMaster3

This commit is contained in:
Joost VandeVondele 2019-01-09 21:52:30 +01:00
commit 21819b7bf8
22 changed files with 181 additions and 165 deletions

View file

@ -63,7 +63,7 @@ script:
#
# Valgrind
#
- export CXXFLAGS=-O1
- export CXXFLAGS="-O1 -fno-inline"
- if [ -x "$(command -v valgrind )" ]; then make clean && make -j2 ARCH=x86-64 debug=yes optimize=no build > /dev/null && ../tests/instrumented.sh --valgrind; fi
- if [ -x "$(command -v valgrind )" ]; then ../tests/instrumented.sh --valgrind-thread; fi
#

View file

@ -86,7 +86,9 @@ Currently, Stockfish has the following UCI options:
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
the .rtbz files on a regular HD.
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.
* #### SyzygyProbeDepth
Minimum remaining search depth for which a position is probed. Set this option

View file

@ -184,8 +184,8 @@ namespace {
// init_magics() computes all rook and bishop attacks at startup. Magic
// bitboards are used to look up attacks of sliding pieces. As a reference see
// chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
// use the so called "fancy" approach.
// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so
// called "fancy" approach.
void init_magics(Bitboard table[], Magic magics[], Direction directions[]) {

View file

@ -239,9 +239,9 @@ void signals_poll() {
}
void save(Thread* thread, TTEntry* tte,
Key k, Value v, Bound b, Depth d, Move m, Value ev) {
Key k, Value v, bool PvHit, Bound b, Depth d, Move m, Value ev) {
tte->save(k, v, b, d, m, ev);
tte->save(k, v, PvHit, b, d, m, ev);
if (d > 3 * ONE_PLY)
{
@ -291,7 +291,7 @@ void save(Thread* thread, TTEntry* tte,
bool found;
TTEntry* replace_tte;
replace_tte = TT.probe(e.first, found);
replace_tte->save(e.first, e.second.value(), e.second.bound(), e.second.depth(),
replace_tte->save(e.first, e.second.value(), e.second.pv_hit(), e.second.bound(), e.second.depth(),
e.second.move(), e.second.eval());
}
}

View file

@ -73,7 +73,7 @@ bool getline(std::istream& input, std::string& str);
int size();
int rank();
inline bool is_root() { return rank() == 0; }
void save(Thread* thread, TTEntry* tte, Key k, Value v, Bound b, Depth d, Move m, Value ev);
void save(Thread* thread, TTEntry* tte, Key k, Value v, bool PvHit, Bound b, Depth d, Move m, Value ev);
void pick_moves(MoveInfo& mi, std::string& PVLine);
void ttRecvBuff_resize(size_t nThreads);
uint64_t nodes_searched();
@ -90,7 +90,7 @@ inline bool getline(std::istream& input, std::string& str) { return static_cast<
constexpr int size() { return 1; }
constexpr int rank() { return 0; }
constexpr bool is_root() { return true; }
inline void save(Thread*, TTEntry* tte, Key k, Value v, Bound b, Depth d, Move m, Value ev) { tte->save(k, v, b, d, m, ev); }
inline void save(Thread*, TTEntry* tte, Key k, Value v, bool PvHit, Bound b, Depth d, Move m, Value ev) { tte->save(k, v, PvHit, b, d, m, ev); }
inline void pick_moves(MoveInfo&, std::string&) { }
inline void ttRecvBuff_resize(size_t) { }
uint64_t nodes_searched();

View file

@ -131,7 +131,7 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
Square loserKSq = pos.square<KING>(weakSide);
Square bishopSq = pos.square<BISHOP>(strongSide);
// If our Bishop does not attack A1/H8, we flip the enemy king square
// If our Bishop does not attack A1/H8, we flip the enemy king square
// to drive to opposite corners (A8/H1).
Value result = VALUE_KNOWN_WIN
@ -724,6 +724,9 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
template<>
ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, KnightValueMg, 1));
assert(verify_material(pos, weakSide, BishopValueMg, 0));
Square pawnSq = pos.square<PAWN>(strongSide);
Square bishopSq = pos.square<BISHOP>(weakSide);
Square weakKingSq = pos.square<KING>(weakSide);

View file

@ -213,7 +213,7 @@ namespace {
// 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. It is set to 0 when king safety evaluation is skipped.
// and h6.
Bitboard kingRing[COLOR_NB];
// kingAttackersCount[color] is the number of pieces of the given color
@ -505,7 +505,7 @@ namespace {
Score score = SCORE_ZERO;
// Non-pawn enemies
nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(Them, PAWN);
nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(PAWN);
// Squares strongly protected by the enemy, either because they defend the
// square with a pawn, or because they defend the square twice and we don't.
@ -717,7 +717,8 @@ namespace {
behind |= (Us == WHITE ? behind >> 16 : behind << 16);
int bonus = popcount(safe) + popcount(behind & safe);
int weight = pos.count<ALL_PIECES>(Us) - 2 * pe->open_files();
int weight = pos.count<ALL_PIECES>(Us)
- 2 * popcount(pe->semiopenFiles[WHITE] & pe->semiopenFiles[BLACK]);
Score score = make_score(bonus * weight * weight / 16, 0);

View file

@ -25,47 +25,6 @@
namespace {
template<Color Us, CastlingSide Cs, bool Checks, bool Chess960>
ExtMove* generate_castling(const Position& pos, ExtMove* moveList) {
constexpr CastlingRight Cr = Us | Cs;
constexpr bool KingSide = (Cs == KING_SIDE);
if (pos.castling_impeded(Cr) || !pos.can_castle(Cr))
return moveList;
// After castling, the rook and king final positions are the same in Chess960
// as they would be in standard chess.
Square kfrom = pos.square<KING>(Us);
Square rfrom = pos.castling_rook_square(Cr);
Square kto = relative_square(Us, KingSide ? SQ_G1 : SQ_C1);
Bitboard enemies = pos.pieces(~Us);
assert(!pos.checkers());
const Direction step = Chess960 ? kto > kfrom ? WEST : EAST
: KingSide ? WEST : EAST;
for (Square s = kto; s != kfrom; s += step)
if (pos.attackers_to(s) & enemies)
return moveList;
// Because we generate only legal castling moves we need to verify that
// when moving the castling rook we do not discover some hidden checker.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
if (Chess960 && (attacks_bb<ROOK>(kto, pos.pieces() ^ rfrom) & pos.pieces(~Us, ROOK, QUEEN)))
return moveList;
Move m = make<CASTLING>(kfrom, rfrom);
if (Checks && !pos.gives_check(m))
return moveList;
*moveList++ = m;
return moveList;
}
template<GenType Type, Direction D>
ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
@ -93,10 +52,8 @@ namespace {
template<Color Us, GenType Type>
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
// Compute our parametrized parameters at compile time, named according to
// the point of view of white side.
// Compute some compile time parameters relative to the white side
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
@ -161,7 +118,7 @@ namespace {
}
// Promotions and underpromotions
if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB)))
if (pawnsOn7)
{
if (Type == CAPTURES)
emptySquares = ~pos.pieces();
@ -262,7 +219,9 @@ namespace {
template<Color Us, GenType Type>
ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) {
constexpr bool Checks = Type == QUIET_CHECKS;
constexpr CastlingRight OO = Us | KING_SIDE;
constexpr CastlingRight OOO = Us | QUEEN_SIDE;
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target);
@ -276,19 +235,14 @@ namespace {
Bitboard b = pos.attacks_from<KING>(ksq) & target;
while (b)
*moveList++ = make_move(ksq, pop_lsb(&b));
}
if (Type != CAPTURES && Type != EVASIONS && pos.castling_rights(Us))
{
if (pos.is_chess960())
if (Type != CAPTURES && pos.can_castle(CastlingRight(OO | OOO)))
{
moveList = generate_castling<Us, KING_SIDE, Checks, true>(pos, moveList);
moveList = generate_castling<Us, QUEEN_SIDE, Checks, true>(pos, moveList);
}
else
{
moveList = generate_castling<Us, KING_SIDE, Checks, false>(pos, moveList);
moveList = generate_castling<Us, QUEEN_SIDE, Checks, false>(pos, moveList);
if (!pos.castling_impeded(OO) && pos.can_castle(OO))
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OO));
if (!pos.castling_impeded(OOO) && pos.can_castle(OOO))
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OOO));
}
}

View file

@ -31,9 +31,6 @@ namespace {
QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK
};
// Helper filter used with select()
const auto Any = [](){ return true; };
// partial_insertion_sort() sorts moves in descending order up to and including
// a given limit. The order of moves smaller than the limit is left unspecified.
void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
@ -225,7 +222,7 @@ top:
/* fallthrough */
case BAD_CAPTURE:
return select<Next>(Any);
return select<Next>([](){ return true; });
case EVASION_INIT:
cur = moves;
@ -236,7 +233,7 @@ top:
/* fallthrough */
case EVASION:
return select<Best>(Any);
return select<Best>([](){ return true; });
case PROBCUT:
return select<Best>([&](){ return pos.see_ge(move, threshold); });
@ -261,7 +258,7 @@ top:
/* fallthrough */
case QCHECK:
return select<Next>(Any);
return select<Next>([](){ return true; });
}
assert(false);

View file

@ -85,11 +85,11 @@ enum StatsParams { NOT_USED = 0 };
/// ButterflyHistory records how often quiet moves have been successful or
/// unsuccessful during the current search, and is used for reduction and move
/// ordering decisions. It uses 2 tables (one for each color) indexed by
/// the move's from and to squares, see chessprogramming.wikispaces.com/Butterfly+Boards
/// 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;
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
/// move, see chessprogramming.wikispaces.com/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]

View file

@ -115,7 +115,7 @@ namespace {
// not attacked more times than defended.
if ( !(stoppers ^ lever ^ leverPush)
&& popcount(support) >= popcount(lever) - 1
&& popcount(phalanx) >= popcount(leverPush))
&& popcount(phalanx) >= popcount(leverPush))
e->passedPawns[Us] |= s;
else if ( stoppers == SquareBB[s + Up]
@ -185,7 +185,6 @@ Entry* probe(const Position& pos) {
e->key = key;
e->scores[WHITE] = evaluate<WHITE>(pos, e);
e->scores[BLACK] = evaluate<BLACK>(pos, e);
e->openFiles = popcount(e->semiopenFiles[WHITE] & e->semiopenFiles[BLACK]);
e->asymmetry = popcount( (e->passedPawns[WHITE] | e->passedPawns[BLACK])
| (e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]));

View file

@ -39,7 +39,6 @@ struct Entry {
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
int weak_unopposed(Color c) const { return weakUnopposed[c]; }
int pawn_asymmetry() const { return asymmetry; }
int open_files() const { return openFiles; }
int semiopen_file(Color c, File f) const {
return semiopenFiles[c] & (1 << f);
@ -73,7 +72,6 @@ struct Entry {
int semiopenFiles[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
int asymmetry;
int openFiles;
};
typedef HashTable<Entry, 16384> Table;

View file

@ -183,7 +183,7 @@ void Position::init() {
{
std::swap(cuckoo[i], key);
std::swap(cuckooMove[i], move);
if (move == 0) // Arrived at empty slot ?
if (move == MOVE_NONE) // Arrived at empty slot?
break;
i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot
}
@ -465,16 +465,16 @@ const string Position::fen() const {
ss << (sideToMove == WHITE ? " w " : " b ");
if (can_castle(WHITE_OO))
ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | KING_SIDE))) : 'K');
ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OO ))) : 'K');
if (can_castle(WHITE_OOO))
ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | QUEEN_SIDE))) : 'Q');
ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OOO))) : 'Q');
if (can_castle(BLACK_OO))
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | KING_SIDE))) : 'k');
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OO ))) : 'k');
if (can_castle(BLACK_OOO))
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q');
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OOO))) : 'q');
if (!can_castle(ANY_CASTLING))
ss << '-';
@ -540,6 +540,7 @@ bool Position::legal(Move m) const {
Color us = sideToMove;
Square from = from_sq(m);
Square to = to_sq(m);
assert(color_of(moved_piece(m)) == us);
assert(piece_on(square<KING>(us)) == make_piece(us, KING));
@ -550,7 +551,6 @@ bool Position::legal(Move m) const {
if (type_of(m) == ENPASSANT)
{
Square ksq = square<KING>(us);
Square to = to_sq(m);
Square capsq = to - pawn_push(us);
Bitboard occupied = (pieces() ^ from ^ capsq) | to;
@ -563,16 +563,35 @@ bool Position::legal(Move m) const {
&& !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
}
// If the moving piece is a king, check whether the destination
// square is attacked by the opponent. Castling moves are checked
// for legality during move generation.
// Castling moves generation does not check if the castling path is clear of
// enemy attacks, it is delayed at a later time: now!
if (type_of(m) == CASTLING)
{
// After castling, the rook and king final positions are the same in
// Chess960 as they would be in standard chess.
to = relative_square(us, to > from ? SQ_G1 : SQ_C1);
Direction step = to > from ? WEST : EAST;
for (Square s = to; s != from; s += step)
if (attackers_to(s) & pieces(~us))
return false;
// In case of Chess960, verify that when moving the castling rook we do
// not discover some hidden checker.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
return !chess960
|| !(attacks_bb<ROOK>(to, pieces() ^ to_sq(m)) & pieces(~us, ROOK, QUEEN));
}
// If the moving piece is a king, check whether the destination square is
// attacked by the opponent.
if (type_of(piece_on(from)) == KING)
return type_of(m) == CASTLING || !(attackers_to(to_sq(m)) & pieces(~us));
return !(attackers_to(to) & pieces(~us));
// A non-king move is legal if and only if it is not pinned or it
// is moving along the ray towards or away from the king.
return !(blockers_for_king(us) & from)
|| aligned(from, to_sq(m), square<KING>(us));
|| aligned(from, to, square<KING>(us));
}

View file

@ -265,7 +265,7 @@ inline bool Position::can_castle(CastlingRight cr) const {
}
inline int Position::castling_rights(Color c) const {
return st->castlingRights & ((WHITE_OO | WHITE_OOO) << (2 * c));
return st->castlingRights & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING);
}
inline bool Position::castling_impeded(CastlingRight cr) const {

View file

@ -90,15 +90,15 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
}
};
constexpr Score PBonus[RANK_NB][FILE_NB] =
{ // Pawn
{ S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },
{ S( 0,-11), S( -3, -4), S( 13, -1), S( 19, -4), S( 16, 17), S( 13, 7), S( 4, 4), S( -4,-13) },
{ S(-16, -8), S(-12, -6), S( 20, -3), S( 21, 0), S( 25,-11), S( 29, 3), S( 0, 0), S(-27, -1) },
{ S(-11, 3), S(-17, 6), S( 11,-10), S( 21, 1), S( 32, -6), S( 19,-11), S( -5, 0), S(-14, -2) },
{ S( 4, 13), S( 6, 7), S( -8, 3), S( 3, -5), S( 8,-15), S( -2, -1), S(-19, 9), S( -5, 13) },
{ S( -5, 25), S(-19, 20), S( 7, 16), S( 8, 12), S( -7, 21), S( -2, 3), S(-10, -4), S(-16, 15) },
{ S(-10, 6), S( 9, -5), S( -7, 16), S(-12, 27), S( -7, 15), S( -8, 11), S( 16, -7), S( -8, 4) }
constexpr Score PBonus[RANK_NB][FILE_NB] =
{ // Pawn (asymmetric distribution)
{ },
{ S( 0,-11), S( -3,-4), S(13, -1), S( 19, -4), S(16, 17), S(13, 7), S( 4, 4), S( -4,-13) },
{ S(-16, -8), S(-12,-6), S(20, -3), S( 21, 0), S(25,-11), S(29, 3), S( 0, 0), S(-27, -1) },
{ S(-11, 3), S(-17, 6), S(11,-10), S( 21, 1), S(32, -6), S(19,-11), S( -5, 0), S(-14, -2) },
{ S( 4, 13), S( 6, 7), S(-8, 3), S( 3, -5), S( 8,-15), S(-2, -1), S(-19, 9), S( -5, 13) },
{ S( -5, 25), S(-19,20), S( 7, 16), S( 8, 12), S(-7, 21), S(-2, 3), S(-10, -4), S(-16, 15) },
{ S(-10, 6), S( 9,-5), S(-7, 16), S(-12, 27), S(-7, 15), S(-8, 11), S( 16, -7), S( -8, 4) }
};
#undef S

View file

@ -114,8 +114,8 @@ namespace {
Value value_from_tt(Value v, int ply);
void update_pv(Move* pv, Move move, Move* childPv);
void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus);
void update_quiet_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, int bonus);
void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCnt, int bonus);
void update_quiet_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietCount, int bonus);
void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCount, int bonus);
inline bool gives_check(const Position& pos, Move move) {
Color us = pos.side_to_move();
@ -529,32 +529,32 @@ void Thread::search() {
if ( Limits.use_time_management()
&& !Threads.stop
&& !Threads.stopOnPonderhit)
{
double fallingEval = (306 + 119 * failedLow + 6 * (mainThread->previousScore - bestValue)) / 581.0;
fallingEval = std::max(0.5, std::min(1.5, fallingEval));
// If the bestMove is stable over several iterations, reduce time accordingly
timeReduction = 1.0;
for (int i : {3, 4, 5})
if (lastBestMoveDepth * i < completedDepth)
timeReduction *= 1.25;
// Use part of the gained time from a previous stable move for the current move
double bestMoveInstability = 1.0 + mainThread->bestMoveChanges;
bestMoveInstability *= std::pow(mainThread->previousTimeReduction, 0.528) / timeReduction;
// Stop the search if we have only one legal move, or if available time elapsed
if ( rootMoves.size() == 1
|| Time.elapsed() > Time.optimum() * bestMoveInstability * fallingEval)
{
double fallingEval = (306 + 119 * failedLow + 6 * (mainThread->previousScore - bestValue)) / 581.0;
fallingEval = std::max(0.5, std::min(1.5, fallingEval));
// If the bestMove is stable over several iterations, reduce time accordingly
timeReduction = 1.0;
for (int i : {3, 4, 5})
if (lastBestMoveDepth * i < completedDepth)
timeReduction *= 1.25;
// Use part of the gained time from a previous stable move for the current move
double bestMoveInstability = 1.0 + mainThread->bestMoveChanges;
bestMoveInstability *= std::pow(mainThread->previousTimeReduction, 0.528) / timeReduction;
// Stop the search if we have only one legal move, or if available time elapsed
if ( rootMoves.size() == 1
|| Time.elapsed() > Time.optimum() * bestMoveInstability * fallingEval)
{
// If we are allowed to ponder do not stop the search now but
// keep pondering until the GUI sends "ponderhit" or "stop".
if (Threads.ponder)
Threads.stopOnPonderhit = true;
else
Threads.stop = true;
}
// If we are allowed to ponder do not stop the search now but
// keep pondering until the GUI sends "ponderhit" or "stop".
if (Threads.ponder)
Threads.stopOnPonderhit = true;
else
Threads.stop = true;
}
}
}
if (!mainThread)
@ -608,7 +608,7 @@ namespace {
Move ttMove, move, excludedMove, bestMove;
Depth extension, newDepth;
Value bestValue, value, ttValue, eval, maxValue, pureStaticEval;
bool ttHit, inCheck, givesCheck, improving;
bool ttHit, pvHit, inCheck, givesCheck, improving;
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture, pvExact;
Piece movedPiece;
int moveCount, captureCount, quietCount;
@ -674,6 +674,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;
pvHit = ttHit ? tte->pv_hit() : false;
// At non-PV nodes we check for an early TT cutoff
if ( !PvNode
@ -707,6 +708,11 @@ namespace {
return ttValue;
}
if ( depth > 6 * ONE_PLY
&& !excludedMove
&& PvNode)
pvHit = true;
// Step 5. Tablebases probe
if (!rootNode && TB::Cardinality)
{
@ -741,7 +747,7 @@ namespace {
|| (b == BOUND_LOWER ? value >= beta : value <= alpha))
{
Cluster::save(thisThread, tte,
posKey, value_to_tt(value, ss->ply), b,
posKey, value_to_tt(value, ss->ply), pvHit, b,
std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY),
MOVE_NONE, VALUE_NONE);
@ -793,7 +799,7 @@ namespace {
ss->staticEval = eval = pureStaticEval = -(ss-1)->staticEval + 2 * Eval::Tempo;
Cluster::save(thisThread, tte,
posKey, VALUE_NONE, BOUND_NONE, DEPTH_NONE, MOVE_NONE,
posKey, VALUE_NONE, pvHit, BOUND_NONE, DEPTH_NONE, MOVE_NONE,
pureStaticEval);
}
@ -909,6 +915,7 @@ namespace {
tte = TT.probe(posKey, ttHit);
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
ttMove = ttHit ? tte->move() : MOVE_NONE;
pvHit = ttHit ? tte->pv_hit() : false;
}
moves_loop: // When in check, search starts from here
@ -977,13 +984,21 @@ moves_loop: // When in check, search starts from here
&& tte->depth() >= depth - 3 * ONE_PLY
&& pos.legal(move))
{
Value reducedBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE);
Value singularBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE);
ss->excludedMove = move;
value = search<NonPV>(pos, ss, reducedBeta - 1, reducedBeta, depth / 2, cutNode);
value = search<NonPV>(pos, ss, singularBeta - 1, singularBeta, depth / 2, cutNode);
ss->excludedMove = MOVE_NONE;
if (value < reducedBeta)
if (value < singularBeta)
extension = ONE_PLY;
// 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;
}
else if ( givesCheck // Check extension (~2 Elo)
&& pos.see_ge(move))
@ -1061,6 +1076,10 @@ moves_loop: // When in check, search starts from here
{
Depth r = reduction<PvNode>(improving, depth, moveCount);
// Decrease reduction if position is or has been on the PV
if (pvHit && !PvNode)
r -= ONE_PLY;
// Decrease reduction if opponent's move count is high (~10 Elo)
if ((ss-1)->moveCount > 15)
r -= ONE_PLY;
@ -1244,7 +1263,7 @@ moves_loop: // When in check, search starts from here
if (!excludedMove)
Cluster::save(thisThread, tte,
posKey, value_to_tt(bestValue, ss->ply),
posKey, value_to_tt(bestValue, ss->ply), pvHit,
bestValue >= beta ? BOUND_LOWER :
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
depth, bestMove, pureStaticEval);
@ -1274,7 +1293,7 @@ moves_loop: // When in check, search starts from here
Move ttMove, move, bestMove;
Depth ttDepth;
Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha;
bool ttHit, inCheck, givesCheck, evasionPrunable;
bool ttHit, pvHit, inCheck, givesCheck, evasionPrunable;
int moveCount;
if (PvNode)
@ -1308,6 +1327,7 @@ moves_loop: // When in check, search starts from here
tte = TT.probe(posKey, ttHit);
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
ttMove = ttHit ? tte->move() : MOVE_NONE;
pvHit = ttHit ? tte->pv_hit() : false;
if ( !PvNode
&& ttHit
@ -1346,7 +1366,7 @@ moves_loop: // When in check, search starts from here
{
if (!ttHit)
Cluster::save(thisThread, tte,
posKey, value_to_tt(bestValue, ss->ply), BOUND_LOWER,
posKey, value_to_tt(bestValue, ss->ply), pvHit, BOUND_LOWER,
DEPTH_NONE, MOVE_NONE, ss->staticEval);
return bestValue;
@ -1458,7 +1478,7 @@ moves_loop: // When in check, search starts from here
return mated_in(ss->ply); // Plies to mate from the root
Cluster::save(thisThread, tte,
posKey, value_to_tt(bestValue, ss->ply),
posKey, value_to_tt(bestValue, ss->ply), pvHit,
bestValue >= beta ? BOUND_LOWER :
PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
ttDepth, bestMove, ss->staticEval);
@ -1518,7 +1538,7 @@ moves_loop: // When in check, search starts from here
// update_capture_stats() updates move sorting heuristics when a new capture best move is found
void update_capture_stats(const Position& pos, Move move,
Move* captures, int captureCnt, int bonus) {
Move* captures, int captureCount, int bonus) {
CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory;
Piece moved_piece = pos.moved_piece(move);
@ -1528,7 +1548,7 @@ moves_loop: // When in check, search starts from here
captureHistory[moved_piece][to_sq(move)][captured] << bonus;
// Decrease all the other played capture moves
for (int i = 0; i < captureCnt; ++i)
for (int i = 0; i < captureCount; ++i)
{
moved_piece = pos.moved_piece(captures[i]);
captured = type_of(pos.piece_on(to_sq(captures[i])));
@ -1540,7 +1560,7 @@ moves_loop: // When in check, search starts from here
// update_quiet_stats() updates move sorting heuristics when a new quiet best move is found
void update_quiet_stats(const Position& pos, Stack* ss, Move move,
Move* quiets, int quietsCnt, int bonus) {
Move* quiets, int quietCount, int bonus) {
if (ss->killers[0] != move)
{
@ -1560,7 +1580,7 @@ moves_loop: // When in check, search starts from here
}
// Decrease all the other played quiet moves
for (int i = 0; i < quietsCnt; ++i)
for (int i = 0; i < quietCount; ++i)
{
thisThread->mainHistory[us][from_to(quiets[i])] << -bonus;
update_continuation_histories(ss, pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus);
@ -1706,7 +1726,7 @@ bool RootMove::extract_ponder_from_tt(Position& pos) {
assert(pv.size() == 1);
if (!pv[0])
if (pv[0] == MOVE_NONE)
return false;
pos.do_move(pv[0], st);

View file

@ -215,14 +215,22 @@ public:
return *baseAddress = nullptr, nullptr;
fstat(fd, &statbuf);
if (statbuf.st_size % 64 != 16)
{
std::cerr << "Corrupt tablebase file " << fname << std::endl;
exit(EXIT_FAILURE);
}
*mapping = statbuf.st_size;
*baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
madvise(*baseAddress, statbuf.st_size, MADV_RANDOM);
::close(fd);
if (*baseAddress == MAP_FAILED) {
if (*baseAddress == MAP_FAILED)
{
std::cerr << "Could not mmap() " << fname << std::endl;
exit(1);
exit(EXIT_FAILURE);
}
#else
// Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored.
@ -234,21 +242,30 @@ public:
DWORD size_high;
DWORD size_low = GetFileSize(fd, &size_high);
if (size_low % 64 != 16)
{
std::cerr << "Corrupt tablebase file " << fname << std::endl;
exit(EXIT_FAILURE);
}
HANDLE mmap = CreateFileMapping(fd, nullptr, PAGE_READONLY, size_high, size_low, nullptr);
CloseHandle(fd);
if (!mmap) {
if (!mmap)
{
std::cerr << "CreateFileMapping() failed" << std::endl;
exit(1);
exit(EXIT_FAILURE);
}
*mapping = (uint64_t)mmap;
*baseAddress = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0);
if (!*baseAddress) {
if (!*baseAddress)
{
std::cerr << "MapViewOfFile() failed, name = " << fname
<< ", error = " << GetLastError() << std::endl;
exit(1);
exit(EXIT_FAILURE);
}
#endif
uint8_t* data = (uint8_t*)*baseAddress;
@ -256,7 +273,8 @@ public:
constexpr uint8_t Magics[][4] = { { 0xD7, 0x66, 0x0C, 0xA5 },
{ 0x71, 0xE8, 0x23, 0x5D } };
if (memcmp(data, Magics[type == WDL], 4)) {
if (memcmp(data, Magics[type == WDL], 4))
{
std::cerr << "Corrupted table in file " << fname << std::endl;
unmap(*baseAddress, *mapping);
return *baseAddress = nullptr, nullptr;
@ -417,7 +435,7 @@ class TBTables {
}
}
std::cerr << "TB hash table size too low!" << std::endl;
exit(1);
exit(EXIT_FAILURE);
}
public:

View file

@ -31,7 +31,7 @@
TranspositionTable TT; // Our global transposition table
/// TTEntry::save saves a TTEntry
void TTEntry::save(Key k, Value v, Bound b, Depth d, Move m, Value ev) {
void TTEntry::save(Key k, Value v, bool PvNode, Bound b, Depth d, Move m, Value ev) {
assert(d / ONE_PLY * ONE_PLY == d);
@ -47,7 +47,7 @@ void TTEntry::save(Key k, Value v, Bound b, Depth d, Move m, Value ev) {
key16 = (uint16_t)(k >> 48);
value16 = (int16_t)v;
eval16 = (int16_t)ev;
genBound8 = (uint8_t)(TT.generation8 | b);
genBound8 = (uint8_t)(TT.generation8 | PvNode << 2 | b);
depth8 = (int8_t)(d / ONE_PLY);
}
}
@ -122,7 +122,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
for (int i = 0; i < ClusterSize; ++i)
if (!tte[i].key16 || tte[i].key16 == key16)
{
tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh
tte[i].genBound8 = uint8_t(generation8 | tte[i].pv_hit() << 2 | tte[i].bound()); // Refresh
return found = (bool)tte[i].key16, &tte[i];
}
@ -131,11 +131,11 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
TTEntry* replace = tte;
for (int i = 1; i < ClusterSize; ++i)
// Due to our packed storage format for generation and its cyclic
// nature we add 259 (256 is the modulus plus 3 to keep the lowest
// nature we add 263 (263 is the modulus plus 7 to keep the lowest
// two bound bits from affecting the result) to calculate the entry
// age correctly even after generation8 overflows into the next cycle.
if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2
> tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2)
if ( replace->depth8 - ((263 + generation8 - replace->genBound8) & 0xF8)
> tte[i].depth8 - ((263 + generation8 - tte[i].genBound8) & 0xF8))
replace = &tte[i];
return found = false, replace;

View file

@ -35,7 +35,8 @@ namespace Cluster {
/// move 16 bit
/// value 16 bit
/// eval value 16 bit
/// generation 6 bit
/// generation 5 bit
/// PvNode 1 bit
/// bound type 2 bit
/// depth 8 bit
@ -45,8 +46,9 @@ struct TTEntry {
Value value() const { return (Value)value16; }
Value eval() const { return (Value)eval16; }
Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)); }
bool pv_hit() const { return (bool)(genBound8 & 0x4); }
Bound bound() const { return (Bound)(genBound8 & 0x3); }
void save(Key k, Value v, Bound b, Depth d, Move m, Value ev);
void save(Key k, Value v, bool PvNode, Bound b, Depth d, Move m, Value ev);
private:
friend class TranspositionTable;
@ -84,7 +86,7 @@ class TranspositionTable {
public:
~TranspositionTable() { free(mem); }
void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound
void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound
TTEntry* probe(const Key key, bool& found) const;
int hashfull() const;
void resize(size_t mbSize);

View file

@ -141,7 +141,9 @@ enum CastlingRight {
WHITE_OOO = WHITE_OO << 1,
BLACK_OO = WHITE_OO << 2,
BLACK_OOO = WHITE_OO << 3,
ANY_CASTLING = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO,
WHITE_CASTLING = WHITE_OO | WHITE_OOO,
BLACK_CASTLING = BLACK_OO | BLACK_OOO,
ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING,
CASTLING_RIGHT_NB = 16
};

View file

@ -28,14 +28,14 @@ case $1 in
echo "sanitizer-undefined testing started"
prefix='!'
exeprefix=''
postfix='2>&1 | grep "runtime error:"'
postfix='2>&1 | grep -A50 "runtime error:"'
threads="1"
;;
--sanitizer-thread)
echo "sanitizer-thread testing started"
prefix='!'
exeprefix=''
postfix='2>&1 | grep "WARNING: ThreadSanitizer:"'
postfix='2>&1 | grep -A50 "WARNING: ThreadSanitizer:"'
threads="2"
cat << EOF > tsan.supp
@ -45,6 +45,7 @@ race:TTEntry::bound
race:TTEntry::save
race:TTEntry::value
race:TTEntry::eval
race:TTEntry::pv_hit
race:TranspositionTable::probe
race:TranspositionTable::hashfull

View file

@ -1,5 +1,5 @@
#!/bin/bash
# verify perft numbers (positions from https://chessprogramming.wikispaces.com/Perft+Results)
# verify perft numbers (positions from www.chessprogramming.org/Perft_Results)
error()
{