language: cpp
sudo: required
-dist: trusty
+dist: xenial
matrix:
include:
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
- packages: ['g++-7', 'g++-7-multilib', 'g++-multilib', 'valgrind', 'expect', 'curl']
+ packages: ['g++-8', 'g++-8-multilib', 'g++-multilib', 'valgrind', 'expect', 'curl']
env:
- - COMPILER=g++-7
+ - COMPILER=g++-8
- COMP=gcc
- os: linux
compiler: clang
addons:
apt:
- sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-5.0']
- packages: ['clang-5.0', 'llvm-5.0-dev', 'g++-multilib', 'valgrind', 'expect', 'curl']
+ sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-xenial-6.0']
+ packages: ['clang-6.0', 'llvm-6.0-dev', 'g++-multilib', 'valgrind', 'expect', 'curl']
env:
- - COMPILER=clang++-5.0
+ - COMPILER=clang++-6.0
- COMP=clang
- - LDFLAGS=-fuse-ld=gold
+ - LDFLAGS=-fuse-ld=lld
- os: osx
compiler: gcc
- os: osx
compiler: clang
env:
- - COMPILER=clang++ V='Apple LLVM 6.0' # Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn)
+ - COMPILER=clang++ V='Apple LLVM 9.4.1' # Apple LLVM version 9.1.0 (clang-902.0.39.2)
- COMP=clang
branches:
#
# Sanitizer
#
- # Use g++-7 as a proxy for having sanitizers, might need revision as they become available for more recent versions of clang/gcc
- - if [[ "$COMPILER" == "g++-7" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi
- - if [[ "$COMPILER" == "g++-7" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi
+ # Use g++-8 as a proxy for having sanitizers, might need revision as they become available for more recent versions of clang/gcc
+ - if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi
+ - if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi
-# List of authors for Stockfish, updated just after version 9
+# List of authors for Stockfish, updated for version 10
Tord Romstad (romstad)
Marco Costalba (mcostalba)
Joona Kiiski (zamar)
Gary Linscott (glinscott)
-absimaldata
+Aditya (absimaldata)
Ajith Chandy Jose (ajithcj)
Alain Savard (Rocky640)
Alexander Kure
Ben Koshy (BKSpurgeon)
Bill Henry (VoyagerOne)
braich
+Bojun Guo (noobpwnftw)
Brian Sheppard (SapphireBrand)
Bryan Cross (crossbr)
-Bujun Guo (noobpwnftw)
Chris Cain (ceebo)
Dan Schmidt
Daniel Dugovic (ddugovic)
Daylen Yang (daylen)
DiscanX
Eelco de Groot
-ElbertoOne
erbsenzaehler
Ernesto Gatti
Fabian Beuke (madnight)
Ivan Ivec (IIvec)
Jacques B. (Timshel)
Jan Ondruš (hxim)
+Jared Kish (Kurtbusch)
Jarrod Torriero (DU-jdto)
Jean-Francois Romang (jromang)
Jerry Donald Watson (jerrydonaldwatson)
Jonathan Calovski (Mysseno)
+Jonathan D. (SFisGOD)
Joost VandeVondele (vondele)
Jörg Oster (joergoster)
Joseph Ellis (jhellis3)
kinderchocolate
Kiran Panditrao (Krgp)
Kojirion
-Leonardo Ljubičić (GM)
+Leonardo Ljubičić (ICCF World Champion)
Leonid Pechenik (lp--)
Linus Arver
loco-loco
+Lub van den Berg (ElbertoOne)
Luca Brivio (lucabrivio)
Lucas Braesch (lucasart)
Lyudmil Antonov (lantonov)
Michel Van den Bergh (vdbergh)
Miguel Lahoz (miguel-l)
Mikael Bäckman (mbootsector)
-Mike Whiteley (protonspring)
+Michael Whiteley (protonspring)
Miroslav Fontán (Hexik)
Moez Jellouli (MJZ1977)
Mohammed Li (tthsqe12)
Ronald de Man (syzygy1)
Ryan Schmitt
Ryan Takker
+Sebastian Buchwald (UniQP)
Sergei Antonov (saproj)
sf-x
shane31
- appveyor
# Operating system (build VM template)
-os: Visual Studio 2015
+os: Visual Studio 2017
# Build platform, i.e. x86, x64, AnyCPU. This setting is optional.
platform:
$b = git log HEAD | sls "\b[Bb]ench[ :]+[0-9]{7}" | select -first 1
$bench = $b -match '\D+(\d+)' | % { $matches[1] }
Write-Host "Reference bench:" $bench
- $g = "Visual Studio 14 2015"
+ $g = "Visual Studio 15 2017"
If (${env:PLATFORM} -eq 'x64') { $g = $g + ' Win64' }
cmake -G "${g}" .
Write-Host "Generated files for: " $g
$(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $<
#LDFLAGS += -Wl,-Bstatic -Wl,-\( -lprotobuf -lgrpc++_unsecure -lgrpc_unsecure -lgrpc -lz -Wl,-\) -Wl,-Bdynamic -ldl
-LDFLAGS += /usr/lib/x86_64-linux-gnu/libprotobuf.a /usr/lib/libgrpc++_unsecure.a /usr/lib/libgrpc_unsecure.a /usr/lib/libgrpc.a /usr/lib/x86_64-linux-gnu/libcares.a -ldl -lz
+LDFLAGS += /usr/lib/x86_64-linux-gnu/libprotobuf.a /usr/lib/x86_64-linux-gnu/libgrpc++_unsecure.a /usr/lib/x86_64-linux-gnu/libgrpc_unsecure.a /usr/lib/x86_64-linux-gnu/libgrpc.a /usr/lib/x86_64-linux-gnu/libcares.a -ldl -lz
+#LDFLAGS += /usr/lib/x86_64-linux-gnu/libprotobuf.a /usr/lib/libgrpc++_unsecure.a /usr/lib/libgrpc_unsecure.a /usr/lib/libgrpc.a /usr/lib/x86_64-linux-gnu/libcares.a -ldl -lz
client: $(CLIOBJS)
$(CXX) -o $@ $(CLIOBJS) $(LDFLAGS)
return b & (b - 1);
}
+inline bool opposite_colors(Square s1, Square s2) {
+ return bool(DarkSquares & s1) != bool(DarkSquares & s2);
+}
+
/// rank_bb() and file_bb() return a bitboard representing all the squares on
/// the given file or rank.
}
+/// double_pawn_attacks_bb() returns the pawn attacks for the given color
+/// from the squares in the given bitboard.
+
+template<Color C>
+constexpr Bitboard double_pawn_attacks_bb(Bitboard b) {
+ return C == WHITE ? shift<NORTH_WEST>(b) & shift<NORTH_EAST>(b)
+ : shift<SOUTH_WEST>(b) & shift<SOUTH_EAST>(b);
+}
+
+
/// adjacent_files_bb() returns a bitboard representing all the squares on the
/// adjacent files of the given one.
// Table used to drive the king towards a corner square of the
// right color in KBN vs K endgames.
constexpr int PushToCorners[SQUARE_NB] = {
- 200, 190, 180, 170, 160, 150, 140, 130,
- 190, 180, 170, 160, 150, 140, 130, 140,
- 180, 170, 155, 140, 140, 125, 140, 150,
- 170, 160, 140, 120, 110, 140, 150, 160,
- 160, 150, 140, 110, 120, 140, 160, 170,
- 150, 140, 125, 140, 140, 155, 170, 180,
- 140, 130, 140, 150, 160, 170, 180, 190,
- 130, 140, 150, 160, 170, 180, 190, 200
+ 6400, 6080, 5760, 5440, 5120, 4800, 4480, 4160,
+ 6080, 5760, 5440, 5120, 4800, 4480, 4160, 4480,
+ 5760, 5440, 4960, 4480, 4480, 4000, 4480, 4800,
+ 5440, 5120, 4480, 3840, 3520, 4480, 4800, 5120,
+ 5120, 4800, 4480, 3520, 3840, 4480, 5120, 5440,
+ 4800, 4480, 4000, 4480, 4480, 4960, 5440, 5760,
+ 4480, 4160, 4480, 4800, 5120, 5440, 5760, 6080,
+ 4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400
};
// Tables used to drive a piece towards or away from another piece
/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
-/// defending king towards a corner square of the right color.
+/// defending king towards a corner square that our bishop attacks.
template<>
Value Endgame<KBNK>::operator()(const Position& pos) const {
Square loserKSq = pos.square<KING>(weakSide);
Square bishopSq = pos.square<BISHOP>(strongSide);
- // kbnk_mate_table() tries to drive toward corners A1 or H8. If we have a
- // bishop that cannot reach the above squares, we flip the kings in order
- // to drive the enemy toward corners A8 or H1.
- if (opposite_colors(bishopSq, SQ_A1))
- {
- winnerKSq = ~winnerKSq;
- loserKSq = ~loserKSq;
- }
+ // 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
+ PushClose[distance(winnerKSq, loserKSq)]
- + PushToCorners[loserKSq];
+ + PushToCorners[opposite_colors(bishopSq, SQ_A1) ? ~loserKSq : loserKSq];
+ assert(abs(result) < VALUE_MATE_IN_MAX_PLY);
return strongSide == pos.side_to_move() ? result : -result;
}
};
// Assorted bonuses and penalties
- constexpr Score BishopPawns = S( 3, 8);
- constexpr Score CloseEnemies = S( 7, 0);
+ constexpr Score BishopPawns = S( 3, 7);
+ constexpr Score CloseEnemies = S( 8, 0);
constexpr Score CorneredBishop = S( 50, 50);
- constexpr Score Hanging = S( 62, 34);
- constexpr Score KingProtector = S( 6, 7);
- constexpr Score KnightOnQueen = S( 20, 12);
- constexpr Score LongDiagonalBishop = S( 44, 0);
- constexpr Score MinorBehindPawn = S( 16, 0);
- constexpr Score Overload = S( 12, 6);
- constexpr Score PawnlessFlank = S( 18, 94);
- constexpr Score RestrictedPiece = S( 7, 6);
- constexpr Score RookOnPawn = S( 10, 28);
- constexpr Score SliderOnQueen = S( 49, 21);
- constexpr Score ThreatByKing = S( 21, 84);
- constexpr Score ThreatByPawnPush = S( 48, 42);
- constexpr Score ThreatByRank = S( 14, 3);
- constexpr Score ThreatBySafePawn = S(169, 99);
- constexpr Score TrappedRook = S( 98, 5);
- constexpr Score WeakQueen = S( 51, 10);
- constexpr Score WeakUnopposedPawn = S( 14, 20);
+ constexpr Score Hanging = S( 69, 36);
+ constexpr Score KingProtector = S( 7, 8);
+ constexpr Score KnightOnQueen = S( 16, 12);
+ constexpr Score LongDiagonalBishop = S( 45, 0);
+ constexpr Score MinorBehindPawn = S( 18, 3);
+ constexpr Score PawnlessFlank = S( 17, 95);
+ constexpr Score RestrictedPiece = S( 7, 7);
+ constexpr Score RookOnPawn = S( 10, 32);
+ constexpr Score SliderOnQueen = S( 59, 18);
+ constexpr Score ThreatByKing = S( 24, 89);
+ constexpr Score ThreatByPawnPush = S( 48, 39);
+ constexpr Score ThreatByRank = S( 13, 0);
+ constexpr Score ThreatBySafePawn = S(173, 94);
+ constexpr Score TrappedRook = S( 96, 4);
+ constexpr Score WeakQueen = S( 49, 15);
+ constexpr Score WeakUnopposedPawn = S( 12, 23);
#undef S
kingRing[Us] |= shift<EAST>(kingRing[Us]);
kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
+ kingRing[Us] &= ~double_pawn_attacks_bb<Us>(pos.pieces(Us, PAWN));
kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
}
}
{
File kf = file_of(pos.square<KING>(Us));
if ((kf < FILE_E) == (file_of(s) < kf))
- score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us));
+ score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.castling_rights(Us));
}
}
Score score = SCORE_ZERO;
// Non-pawn enemies
- nonPawnEnemies = pos.pieces(Them) ^ pos.pieces(Them, PAWN);
+ nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(Them, 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.
if (weak & attackedBy[Us][KING])
score += ThreatByKing;
- score += Hanging * popcount(weak & ~attackedBy[Them][ALL_PIECES]);
-
- b = weak & nonPawnEnemies & attackedBy[Them][ALL_PIECES];
- score += Overload * popcount(b);
+ b = ~attackedBy[Them][ALL_PIECES]
+ | (nonPawnEnemies & attackedBy2[Us]);
+ score += Hanging * popcount(weak & b);
}
// Bonus for restricting their piece moves
restricted = attackedBy[Them][ALL_PIECES]
- & ~attackedBy[Them][PAWN]
- & ~attackedBy2[Them]
+ & ~stronglyProtected
& attackedBy[Us][ALL_PIECES];
score += RestrictedPiece * popcount(restricted);
namespace Eval {
-constexpr Value Tempo = Value(20); // Must be visible to search
+constexpr Value Tempo = Value(28); // Must be visible to search
std::string trace(const Position& pos);
Key key;
const EndgameBase<Value>* evaluationFunction;
const EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
- // side (e.g. KPKP, KBPsKs)
+ // side (e.g. KPKP, KBPsK)
int16_t value;
uint8_t factor[COLOR_NB];
Phase gamePhase;
return -1;
}
- while (ptr->Size > 0 && byteOffset + ptr->Size <= returnLength)
+ while (byteOffset < returnLength)
{
if (ptr->Relationship == RelationNumaNode)
nodes++;
threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1;
}
+ assert(ptr->Size);
byteOffset += ptr->Size;
ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size);
}
ExtMove* generate_castling(const Position& pos, ExtMove* moveList) {
constexpr CastlingRight Cr = Us | Cs;
- constexpr bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO);
+ constexpr bool KingSide = (Cs == KING_SIDE);
if (pos.castling_impeded(Cr) || !pos.can_castle(Cr))
return moveList;
*moveList++ = make_move(ksq, pop_lsb(&b));
}
- if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
+ if (Type != CAPTURES && Type != EVASIONS && pos.castling_rights(Us))
{
if (pos.is_chess960())
{
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
- Bitboard b, neighbours, stoppers, doubled, supported, phalanx;
+ Bitboard b, neighbours, stoppers, doubled, support, phalanx;
Bitboard lever, leverPush;
Square s;
bool opposed, backward;
doubled = ourPawns & (s - Up);
neighbours = ourPawns & adjacent_files_bb(f);
phalanx = neighbours & rank_bb(s);
- supported = neighbours & rank_bb(s - Up);
+ support = neighbours & rank_bb(s - Up);
// A pawn is backward when it is behind all pawns of the same color
// on the adjacent files and cannot be safely advanced.
// which could become passed after one or two pawn pushes when are
// not attacked more times than defended.
if ( !(stoppers ^ lever ^ leverPush)
- && popcount(supported) >= popcount(lever) - 1
+ && popcount(support) >= popcount(lever) - 1
&& popcount(phalanx) >= popcount(leverPush))
e->passedPawns[Us] |= s;
else if ( stoppers == SquareBB[s + Up]
&& relative_rank(Us, s) >= RANK_5)
{
- b = shift<Up>(supported) & ~theirPawns;
+ b = shift<Up>(support) & ~theirPawns;
while (b)
if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)]))
e->passedPawns[Us] |= s;
}
// Score this pawn
- if (supported | phalanx)
- score += Connected[opposed][bool(phalanx)][popcount(supported)][relative_rank(Us, s)];
+ if (support | phalanx)
+ score += Connected[opposed][bool(phalanx)][popcount(support)][relative_rank(Us, s)];
else if (!neighbours)
score -= Isolated, e->weakUnopposed[Us] += !opposed;
else if (backward)
score -= Backward, e->weakUnopposed[Us] += !opposed;
- if (doubled && !supported)
+ if (doubled && !support)
score -= Doubled;
}
for (File f = File(center - 1); f <= File(center + 1); ++f)
{
b = ourPawns & file_bb(f);
- int ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : 0;
+ Rank ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
b = theirPawns & file_bb(f);
- int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;
+ Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
int d = std::min(f, ~f);
safety += ShelterStrength[d][ourRank];
Square ksq = pos.square<KING>(Us);
kingSquares[Us] = ksq;
- castlingRights[Us] = pos.can_castle(Us);
+ castlingRights[Us] = pos.castling_rights(Us);
int minKingPawnDistance = 0;
Bitboard pawns = pos.pieces(Us, PAWN);
template<Color Us>
Score king_safety(const Position& pos) {
- return kingSquares[Us] == pos.square<KING>(Us) && castlingRights[Us] == pos.can_castle(Us)
+ return kingSquares[Us] == pos.square<KING>(Us) && castlingRights[Us] == pos.castling_rights(Us)
? kingSafety[Us] : (kingSafety[Us] = do_king_safety<Us>(pos));
}
if (can_castle(BLACK_OOO))
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q');
- if (!can_castle(WHITE) && !can_castle(BLACK))
+ if (!can_castle(ANY_CASTLING))
ss << '-';
ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ")
template<PieceType Pt> Square square(Color c) const;
// Castling
- int can_castle(Color c) const;
- int can_castle(CastlingRight cr) const;
+ int castling_rights(Color c) const;
+ bool can_castle(CastlingRight cr) const;
bool castling_impeded(CastlingRight cr) const;
Square castling_rook_square(CastlingRight cr) const;
return st->epSquare;
}
-inline int Position::can_castle(CastlingRight cr) const {
+inline bool Position::can_castle(CastlingRight cr) const {
return st->castlingRights & cr;
}
-inline int Position::can_castle(Color c) const {
+inline int Position::castling_rights(Color c) const {
return st->castlingRights & ((WHITE_OO | WHITE_OOO) << (2 * c));
}
// second half of the files.
constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ },
- { // Pawn
- { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },
- { S(-11,-3), S( 7, -1), S( 7, 7), S(17, 2) },
- { S(-16,-2), S( -3, 2), S( 23, 6), S(23,-1) },
- { S(-14, 7), S( -7, -4), S( 20,-8), S(24, 2) },
- { S( -5,13), S( -2, 10), S( -1,-1), S(12,-8) },
- { S(-11,16), S(-12, 6), S( -2, 1), S( 4,16) },
- { S( -2, 1), S( 20,-12), S(-10, 6), S(-2,25) }
- },
+ { },
{ // Knight
{ S(-169,-105), S(-96,-74), S(-80,-46), S(-79,-18) },
{ S( -79, -70), S(-39,-56), S(-24,-15), S( -9, 6) },
{ S(-200, -98), S(-80,-89), S(-53,-53), S(-32,-16) }
},
{ // Bishop
- { S(-49,-58), S(- 7,-31), S(-10,-37), S(-34,-19) },
- { S(-24,-34), S( 9, -9), S( 15,-14), S( 1, 4) },
- { S( -9,-23), S( 22, 0), S( -3, -3), S( 12, 16) },
- { S( 4,-26), S( 9, -3), S( 18, -5), S( 40, 16) },
- { S( -8,-26), S( 27, -4), S( 13, -7), S( 30, 14) },
- { S(-17,-24), S( 14, -2), S( -6, 0), S( 6, 13) },
- { S(-19,-34), S(-13,-10), S( 7,-12), S(-11, 6) },
- { S(-47,-55), S( -7,-32), S(-17,-36), S(-29,-17) }
+ { S(-44,-63), S( -4,-30), S(-11,-35), S(-28, -8) },
+ { S(-18,-38), S( 7,-13), S( 14,-14), S( 3, 0) },
+ { S( -8,-18), S( 24, 0), S( -3, -7), S( 15, 13) },
+ { S( 1,-26), S( 8, -3), S( 26, 1), S( 37, 16) },
+ { S( -7,-24), S( 30, -6), S( 23,-10), S( 28, 17) },
+ { S(-17,-26), S( 4, 2), S( -1, 1), S( 8, 16) },
+ { S(-21,-34), S(-19,-18), S( 10, -7), S( -6, 9) },
+ { S(-48,-51), S( -3,-40), S(-12,-39), S(-25,-20) }
},
{ // Rook
- { S(-24, 0), S(-15, 3), S( -8, 0), S( 0, 3) },
- { S(-18,-7), S( -5,-5), S( -1,-5), S( 1,-1) },
- { S(-19, 6), S(-10,-7), S( 1, 3), S( 0, 3) },
- { S(-21, 0), S( -7, 4), S( -4,-2), S(-4, 1) },
- { S(-21,-7), S(-12, 5), S( -1,-5), S( 4,-7) },
- { S(-23, 3), S(-10, 2), S( 1,-1), S( 6, 3) },
- { S(-11,-1), S( 8, 7), S( 9,11), S(12,-1) },
- { S(-25, 6), S(-18, 4), S(-11, 6), S( 2, 2) }
+ { S(-24, -2), S(-13,-6), S( -7, -3), S( 2,-2) },
+ { S(-18,-10), S(-10,-7), S( -5, 1), S( 9, 0) },
+ { S(-21, 10), S( -7,-4), S( 3, 2), S(-1,-2) },
+ { S(-13, -5), S( -5, 2), S( -4, -8), S(-6, 8) },
+ { S(-24, -8), S(-12, 5), S( -1, 4), S( 6,-9) },
+ { S(-24, 3), S( -4,-2), S( 4,-10), S(10, 7) },
+ { S( -8, 1), S( 6, 2), S( 10, 17), S(12,-8) },
+ { S(-22, 12), S(-24,-6), S( -6, 13), S( 4, 7) }
},
{ // Queen
{ S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) },
}
};
+constexpr Score PBonus[RANK_NB][FILE_NB] =\r
+ { // Pawn\r
+ { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },\r
+ { S( 0,-11), S( -3, -4), S( 13, -1), S( 19, -4), S( 16, 17), S( 13, 7), S( 4, 4), S( -4,-13) },\r
+ { S(-16, -8), S(-12, -6), S( 20, -3), S( 21, 0), S( 25,-11), S( 29, 3), S( 0, 0), S(-27, -1) },\r
+ { S(-11, 3), S(-17, 6), S( 11,-10), S( 21, 1), S( 32, -6), S( 19,-11), S( -5, 0), S(-14, -2) },\r
+ { S( 4, 13), S( 6, 7), S( -8, 3), S( 3, -5), S( 8,-15), S( -2, -1), S(-19, 9), S( -5, 13) },\r
+ { S( -5, 25), S(-19, 20), S( 7, 16), S( 8, 12), S( -7, 21), S( -2, 3), S(-10, -4), S(-16, 15) },\r
+ { S(-10, 6), S( 9, -5), S( -7, 16), S(-12, 27), S( -7, 15), S( -8, 11), S( 16, -7), S( -8, 4) }\r
+ };
+
#undef S
Score psq[PIECE_NB][SQUARE_NB];
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
File f = std::min(file_of(s), ~file_of(s));
- psq[ pc][ s] = score + Bonus[pc][rank_of(s)][f];
+ psq[ pc][ s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)]
+ : Bonus[pc][rank_of(s)][f]);
psq[~pc][~s] = -psq[pc][s];
}
}
&& !Skill(Options["Skill Level"]).enabled()
&& rootMoves[0].pv[0] != MOVE_NONE)
{
- std::map<Move, int> votes;
+ std::map<Move, int64_t> votes;
Value minScore = this->rootMoves[0].score;
// Find out minimum score and reset votes for moves which can be voted
}
// Vote according to score and depth
+ auto square = [](int64_t x) { return x * x; };
for (Thread* th : Threads)
- votes[th->rootMoves[0].pv[0]] += int(th->rootMoves[0].score - minScore)
- + int(th->completedDepth);
+ votes[th->rootMoves[0].pv[0]] += 200 + (square(th->rootMoves[0].score - minScore + 1)
+ * int64_t(th->completedDepth));
// Select best thread
- int bestVote = votes[this->rootMoves[0].pv[0]];
+ int64_t bestVote = votes[this->rootMoves[0].pv[0]];
for (Thread* th : Threads)
{
if (votes[th->rootMoves[0].pv[0]] > bestVote)
void Thread::search() {
- Stack stack[MAX_PLY+7], *ss = stack+4; // To reference from (ss-4) to (ss+2)
+ // To allow access to (ss-5) up to (ss+2), the stack must be oversized.
+ // The former is needed to allow update_continuation_histories(ss-1, ...),
+ // which accesses its argument at ss-4, also near the root.
+ // The latter is needed for statScores and killer initialization.
+ Stack stack[MAX_PLY+8], *ss = stack+5;
+ Move pv[MAX_PLY+1];
Value bestValue, alpha, beta, delta;
Move lastBestMove = MOVE_NONE;
Depth lastBestMoveDepth = DEPTH_ZERO;
Color us = rootPos.side_to_move();
bool failedLow;
- std::memset(ss-4, 0, 7 * sizeof(Stack));
- for (int i = 4; i > 0; i--)
+ std::memset(ss-5, 0, 8 * sizeof(Stack));
+ for (int i = 5; i > 0; i--)
(ss-i)->continuationHistory = &this->continuationHistory[NO_PIECE][0]; // Use as sentinel
+ ss->pv = pv;
bestValue = delta = alpha = -VALUE_INFINITE;
beta = VALUE_INFINITE;
{
beta = std::min(bestValue + delta, VALUE_INFINITE);
if (mainThread)
- ++failedHighCnt;
+ ++failedHighCnt;
}
else
break;
&& !Threads.stop
&& !Threads.stopOnPonderhit)
{
- const int F[] = { failedLow,
- bestValue - mainThread->previousScore };
-
- int improvingFactor = std::max(246, std::min(832, 306 + 119 * F[0] - 6 * F[1]));
+ 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;
// Stop the search if we have only one legal move, or if available time elapsed
if ( rootMoves.size() == 1
- || Time.elapsed() > Time.optimum() * bestMoveInstability * improvingFactor / 581)
+ || 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 (!pos.capture_or_promotion(ttMove))
update_quiet_stats(pos, ss, ttMove, nullptr, 0, stat_bonus(depth));
- // Extra penalty for a quiet TT move in previous ply when it gets refuted
- if ((ss-1)->moveCount == 1 && !pos.captured_piece())
- update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY));
+ // Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted
+ if ( ((ss-1)->moveCount == 1 || (ss-1)->currentMove == (ss-1)->killers[0])
+ && !pos.captured_piece())
+ update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY));
}
// Penalty for a quiet ttMove that fails low
else if (!pos.capture_or_promotion(ttMove))
}
// Step 7. Razoring (~2 Elo)
- if ( depth < 2 * ONE_PLY
- && eval <= alpha - RazorMargin)
+ if ( !rootNode // The required rootNode PV handling is not available in qsearch
+ && depth < 2 * ONE_PLY
+ && eval <= alpha - RazorMargin)
return qsearch<NT>(pos, ss, alpha, beta);
improving = ss->staticEval >= (ss-2)->staticEval
|| (ss-2)->staticEval == VALUE_NONE;
// Step 8. Futility pruning: child node (~30 Elo)
- if ( !rootNode
+ if ( !PvNode
&& depth < 7 * ONE_PLY
&& eval - futility_margin(depth, improving) >= beta
&& eval < VALUE_KNOWN_WIN) // Do not return unproven wins
&& depth >= 5 * ONE_PLY
&& abs(beta) < VALUE_MATE_IN_MAX_PLY)
{
- Value rbeta = std::min(beta + 216 - 48 * improving, VALUE_INFINITE);
- MovePicker mp(pos, ttMove, rbeta - ss->staticEval, &thisThread->captureHistory);
+ Value raisedBeta = std::min(beta + 216 - 48 * improving, VALUE_INFINITE);
+ MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &thisThread->captureHistory);
int probCutCount = 0;
while ( (move = mp.next_move()) != MOVE_NONE
pos.do_move(move, st);
// Perform a preliminary qsearch to verify that the move holds
- value = -qsearch<NonPV>(pos, ss+1, -rbeta, -rbeta+1);
+ value = -qsearch<NonPV>(pos, ss+1, -raisedBeta, -raisedBeta+1);
// If the qsearch held perform the regular search
- if (value >= rbeta)
- value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, depth - 4 * ONE_PLY, !cutNode);
+ if (value >= raisedBeta)
+ value = -search<NonPV>(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4 * ONE_PLY, !cutNode);
pos.undo_move(move);
- if (value >= rbeta)
+ if (value >= raisedBeta)
return value;
}
}
if ( depth >= 8 * ONE_PLY
&& move == ttMove
&& !rootNode
- && !excludedMove // Recursive singular search is not allowed
+ && !excludedMove // Avoid recursive singular search
&& ttValue != VALUE_NONE
&& (tte->bound() & BOUND_LOWER)
&& tte->depth() >= depth - 3 * ONE_PLY
&& pos.legal(move))
{
- Value rBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE);
+ Value reducedBeta = std::max(ttValue - 2 * depth / ONE_PLY, -VALUE_MATE);
ss->excludedMove = move;
- value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode);
+ value = search<NonPV>(pos, ss, reducedBeta - 1, reducedBeta, depth / 2, cutNode);
ss->excludedMove = MOVE_NONE;
- if (value < rBeta)
+ if (value < reducedBeta)
extension = ONE_PLY;
}
else if ( givesCheck // Check extension (~2 Elo)
&& pos.see_ge(move))
extension = ONE_PLY;
- else if ( pos.can_castle(us) // Extension for king moves that change castling rights
- && type_of(movedPiece) == KING)
+ // Extension if castling
+ else if (type_of(move) == CASTLING)
extension = ONE_PLY;
// Calculate new depth for this move
{
if ( !captureOrPromotion
&& !givesCheck
- && (!pos.advanced_pawn_push(move) || pos.non_pawn_material() >= Value(5000)))
+ && !pos.advanced_pawn_push(move))
{
// Move count based pruning (~30 Elo)
if (moveCountPruning)
int lmrDepth = std::max(newDepth - reduction<PvNode>(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY;
// Countermoves based pruning (~20 Elo)
- if ( lmrDepth < 3 + ((ss-1)->statScore > 0)
+ if ( lmrDepth < 3 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1)
&& (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold
&& (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold)
continue;
update_capture_stats(pos, bestMove, capturesSearched, captureCount, stat_bonus(depth + ONE_PLY));
- // Extra penalty for a quiet TT move in previous ply when it gets refuted
- if ((ss-1)->moveCount == 1 && !pos.captured_piece())
- update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY));
+ // Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted
+ if ( ((ss-1)->moveCount == 1 || ((ss-1)->currentMove == (ss-1)->killers[0]))
+ && !pos.captured_piece())
+ update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY));
+
}
// Bonus for prior countermove that caused the fail low
else if ( (depth >= 3 * ONE_PLY || PvNode)
- && !pos.captured_piece()
- && is_ok((ss-1)->currentMove))
+ && !pos.captured_piece())
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth));
if (PvNode)
if (value > alpha)
{
+ bestMove = move;
+
if (PvNode) // Update pv even in fail-high case
update_pv(ss->pv, move, (ss+1)->pv);
if (PvNode && value < beta) // Update alpha here!
- {
alpha = value;
- bestMove = move;
- }
- else // Fail high
- {
- tte->save(posKey, value_to_tt(value, ss->ply), BOUND_LOWER,
- ttDepth, move, ss->staticEval);
-
- return value;
- }
+ else
+ break; // Fail high
}
}
}
return mated_in(ss->ply); // Plies to mate from the root
tte->save(posKey, value_to_tt(bestValue, ss->ply),
- PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
+ bestValue >= beta ? BOUND_LOWER :
+ PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
ttDepth, bestMove, ss->staticEval);
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
exit(1);
}
#else
+ // Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored.
HANDLE fd = CreateFile(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr);
if (fd == INVALID_HANDLE_VALUE)
return *baseAddress = nullptr, nullptr;
static Mutex mutex;
- // Use 'aquire' to avoid a thread reads 'ready' == true while another is
- // still working, this could happen due to compiler reordering.
+ // Use 'acquire' to avoid a thread reading 'ready' == true while
+ // another is still working. (compiler reordering may cause this).
if (e.ready.load(std::memory_order_acquire))
- return e.baseAddress; // Could be nullptr if file does not exsist
+ return e.baseAddress; // Could be nullptr if file does not exist
std::unique_lock<Mutex> lk(mutex);
continue; // First on diagonal, second above
else if (!off_A1H8(s1) && !off_A1H8(s2))
- bothOnDiagonal.push_back(std::make_pair(idx, s2));
+ bothOnDiagonal.emplace_back(idx, s2);
else
MapKK[idx][s2] = code++;
}
/// ThreadPool::set() creates/destroys threads to match the requested number.
-/// Created and launched threads will go immediately to sleep in idle_loop.
+/// Created and launched threads will immediately go to sleep in idle_loop.
/// Upon resizing, threads are recreated to allow for binding if necessary.
void ThreadPool::set(size_t requested) {
while (size() < requested)
push_back(new Thread(size()));
clear();
- }
- // Reallocate the hash with the new threadpool size
- TT.resize(Options["Hash"]);
+ // Reallocate the hash with the new threadpool size
+ TT.resize(Options["Hash"]);
+ }
}
/// ThreadPool::clear() sets threadPool data to initial values.
#include "bitboard.h"
#include "misc.h"
+#include "thread.h"
#include "tt.h"
#include "uci.h"
void TranspositionTable::resize(size_t mbSize) {
+ Threads.main()->wait_for_search_finished();
+
clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
free(mem);
std::vector<std::thread> threads;
- for (size_t idx = 0; idx < Options["Threads"]; idx++)
+ for (size_t idx = 0; idx < Options["Threads"]; ++idx)
{
- threads.push_back(std::thread([this, idx]() {
+ threads.emplace_back([this, idx]() {
// Thread binding gives faster search on systems with a first-touch policy
if (Options["Threads"] > 8)
stride : clusterCount - start;
std::memset(&table[start], 0, len * sizeof(Cluster));
- }));
+ });
}
for (std::thread& th: threads)
int TranspositionTable::hashfull() const {
int cnt = 0;
- for (int i = 0; i < 1000 / ClusterSize; i++)
- {
- const TTEntry* tte = &table[i].entry[0];
- for (int j = 0; j < ClusterSize; j++)
- if ((tte[j].genBound8 & 0xFC) == generation8)
- cnt++;
- }
- return cnt;
+ for (int i = 0; i < 1000 / ClusterSize; ++i)
+ for (int j = 0; j < ClusterSize; ++j)
+ cnt += (table[i].entry[j].genBound8 & 0xFC) == generation8;
+
+ return cnt * 1000 / (ClusterSize * (1000 / ClusterSize));
}
return relative_rank(c, rank_of(s));
}
-inline bool opposite_colors(Square s1, Square s2) {
- int s = int(s1) ^ int(s2);
- return ((s >> 3) ^ s) & 1;
-}
-
constexpr Direction pawn_push(Color c) {
return c == WHITE ? NORTH : SOUTH;
}