1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-07-12 12:09:14 +00:00

Merge branch 'master' into clusterMergeMaster5

This commit is contained in:
Joost VandeVondele 2019-05-01 08:23:22 +02:00
commit 85327828c9
21 changed files with 186 additions and 231 deletions

View file

@ -55,6 +55,9 @@ script:
- make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref - make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref
- make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref - make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref
- make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref - make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref
# Verify bench number is ONE_PLY independent by doubling its value
- sed -i'.bak' 's/.*\(ONE_PLY = [0-9]*\),.*/\1 * 2,/g' types.h
- make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref - make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref
# #
# Check perft and reproducible search # Check perft and reproducible search

View file

@ -490,7 +490,7 @@ config-sanity:
@echo "Testing config sanity. If this fails, try 'make help' ..." @echo "Testing config sanity. If this fails, try 'make help' ..."
@echo "" @echo ""
@test "$(debug)" = "yes" || test "$(debug)" = "no" @test "$(debug)" = "yes" || test "$(debug)" = "no"
@test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "no" @test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "address" || test "$(sanitize)" = "no"
@test "$(optimize)" = "yes" || test "$(optimize)" = "no" @test "$(optimize)" = "yes" || test "$(optimize)" = "no"
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "armv7" test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "armv7"

View file

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

View file

@ -18,8 +18,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <bitset> #include <bitset>
#include <algorithm>
#include "bitboard.h" #include "bitboard.h"
#include "misc.h" #include "misc.h"
@ -27,13 +27,10 @@
uint8_t PopCnt16[1 << 16]; uint8_t PopCnt16[1 << 16];
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
Bitboard SquareBB[SQUARE_NB];
Bitboard ForwardRanksBB[COLOR_NB][RANK_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard LineBB[SQUARE_NB][SQUARE_NB]; Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard DistanceRingBB[SQUARE_NB][8];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
Bitboard SquareBB[SQUARE_NB];
Bitboard KingFlank[FILE_NB] = { Bitboard KingFlank[FILE_NB] = {
QueenSide ^ FileDBB, QueenSide, QueenSide, QueenSide ^ FileDBB, QueenSide, QueenSide,
@ -85,10 +82,7 @@ void Bitboards::init() {
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
{
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2)); SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
DistanceRingBB[s1][SquareDistance[s1][s2]] |= s2;
}
int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } }; int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } };
@ -122,10 +116,7 @@ void Bitboards::init() {
for (PieceType pt : { BISHOP, ROOK }) for (PieceType pt : { BISHOP, ROOK })
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
if (PseudoAttacks[pt][s1] & s2) if (PseudoAttacks[pt][s1] & s2)
{
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
BetweenBB[s1][s2] = attacks_bb(pt, s1, SquareBB[s2]) & attacks_bb(pt, s2, SquareBB[s1]);
}
} }
} }

View file

@ -68,13 +68,11 @@ constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);
extern uint8_t PopCnt16[1 << 16]; extern uint8_t PopCnt16[1 << 16];
extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
extern Bitboard DistanceRingBB[SQUARE_NB][8];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
extern Bitboard KingFlank[FILE_NB]; extern Bitboard KingFlank[FILE_NB];
extern Bitboard SquareBB[SQUARE_NB];
/// Magic holds all magic bitboards relevant data for a single square /// Magic holds all magic bitboards relevant data for a single square
@ -102,34 +100,19 @@ struct Magic {
extern Magic RookMagics[SQUARE_NB]; extern Magic RookMagics[SQUARE_NB];
extern Magic BishopMagics[SQUARE_NB]; extern Magic BishopMagics[SQUARE_NB];
inline Bitboard square_bb(Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return SquareBB[s];
}
/// Overloads of bitwise operators between a Bitboard and a Square for testing /// Overloads of bitwise operators between a Bitboard and a Square for testing
/// whether a given bit is set in a bitboard, and for setting and clearing bits. /// whether a given bit is set in a bitboard, and for setting and clearing bits.
inline Bitboard operator&(Bitboard b, Square s) { inline Bitboard operator&( Bitboard b, Square s) { return b & square_bb(s); }
assert(s >= SQ_A1 && s <= SQ_H8); inline Bitboard operator|( Bitboard b, Square s) { return b | square_bb(s); }
return b & SquareBB[s]; inline Bitboard operator^( Bitboard b, Square s) { return b ^ square_bb(s); }
} inline Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); }
inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }
inline Bitboard operator|(Bitboard b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b | SquareBB[s];
}
inline Bitboard operator^(Bitboard b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b ^ SquareBB[s];
}
inline Bitboard& operator|=(Bitboard& b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b |= SquareBB[s];
}
inline Bitboard& operator^=(Bitboard& b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b ^= SquareBB[s];
}
constexpr bool more_than_one(Bitboard b) { constexpr bool more_than_one(Bitboard b) {
return b & (b - 1); return b & (b - 1);
@ -200,13 +183,12 @@ inline Bitboard adjacent_files_bb(File f) {
} }
/// between_bb() returns a bitboard representing all the squares between the two /// between_bb() returns squares that are linearly between the given squares
/// given ones. For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with /// If the given squares are not on a same file/rank/diagonal, return 0.
/// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank,
/// file or diagonal, 0 is returned.
inline Bitboard between_bb(Square s1, Square s2) { inline Bitboard between_bb(Square s1, Square s2) {
return BetweenBB[s1][s2]; return LineBB[s1][s2] & ( (AllSquares << (s1 + (s1 < s2)))
^(AllSquares << (s2 + !(s1 < s2))));
} }
@ -254,15 +236,16 @@ inline bool aligned(Square s1, Square s2, Square s3) {
/// distance() functions return the distance between x and y, defined as the /// distance() functions return the distance between x and y, defined as the
/// number of steps for a king in x to reach y. Works with squares, ranks, files. /// number of steps for a king in x to reach y.
template<typename T> inline int distance(T x, T y) { return std::abs(x - y); } template<typename T1 = Square> inline int distance(Square x, Square y);
template<> inline int distance<File>(Square x, Square y) { return std::abs(file_of(x) - file_of(y)); }
template<> inline int distance<Rank>(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); }
template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; } template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; }
template<typename T1, typename T2> inline int distance(T2 x, T2 y); template<class T> constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
template<> inline int distance<File>(Square x, Square y) { return distance(file_of(x), file_of(y)); } return v < lo ? lo : v > hi ? hi : v;
template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_of(x), rank_of(y)); } }
/// attacks_bb() returns a bitboard representing all the squares attacked by a /// attacks_bb() returns a bitboard representing all the squares attacked by a
/// piece of type Pt (bishop or rook) placed on 's'. /// piece of type Pt (bishop or rook) placed on 's'.

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include "bitboard.h" #include "bitboard.h"
@ -77,10 +76,7 @@ namespace {
if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E) if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E)
sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1 sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1
if (strongSide == BLACK) return strongSide == WHITE ? sq : ~sq;
sq = ~sq;
return sq;
} }
} // namespace } // namespace
@ -286,18 +282,18 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
} }
/// KNN vs KP. Simply push the opposing king to the corner. /// KNN vs KP. Simply push the opposing king to the corner
template<> template<>
Value Endgame<KNNKP>::operator()(const Position& pos) const { Value Endgame<KNNKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0)); assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Value result = 2 * KnightValueEg Value result = 2 * KnightValueEg
- PawnValueEg - PawnValueEg
+ PushToEdges[pos.square<KING>(weakSide)]; + PushToEdges[pos.square<KING>(weakSide)];
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
} }
@ -639,8 +635,6 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
Square ksq = pos.square<KING>(weakSide); Square ksq = pos.square<KING>(weakSide);
Square psq1 = pos.squares<PAWN>(strongSide)[0]; Square psq1 = pos.squares<PAWN>(strongSide)[0];
Square psq2 = pos.squares<PAWN>(strongSide)[1]; Square psq2 = pos.squares<PAWN>(strongSide)[1];
Rank r1 = rank_of(psq1);
Rank r2 = rank_of(psq2);
Square blockSq1, blockSq2; Square blockSq1, blockSq2;
if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2)) if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2))
@ -674,7 +668,7 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
&& opposite_colors(ksq, wbsq) && opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2 && ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP)) || (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
|| distance(r1, r2) >= 2)) || distance<Rank>(psq1, psq2) >= 2))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2 else if ( ksq == blockSq2

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include <cstring> // For std::memset #include <cstring> // For std::memset
#include <iomanip> #include <iomanip>
@ -229,6 +228,8 @@ namespace {
const Square ksq = pos.square<KING>(Us); const Square ksq = pos.square<KING>(Us);
Bitboard dblAttackByPawn = pawn_double_attacks_bb<Us>(pos.pieces(Us, PAWN));
// Find our pawns that are blocked or on the first two ranks // Find our pawns that are blocked or on the first two ranks
Bitboard b = pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | LowRanks); Bitboard b = pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | LowRanks);
@ -240,7 +241,8 @@ namespace {
attackedBy[Us][KING] = pos.attacks_from<KING>(ksq); attackedBy[Us][KING] = pos.attacks_from<KING>(ksq);
attackedBy[Us][PAWN] = pe->pawn_attacks(Us); attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN]; attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
attackedBy2[Us] = attackedBy[Us][KING] & attackedBy[Us][PAWN]; attackedBy2[Us] = (attackedBy[Us][KING] & attackedBy[Us][PAWN])
| dblAttackByPawn;
// Init our king safety tables // Init our king safety tables
kingRing[Us] = attackedBy[Us][KING]; kingRing[Us] = attackedBy[Us][KING];
@ -257,7 +259,7 @@ namespace {
kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
// Remove from kingRing[] the squares defended by two pawns // Remove from kingRing[] the squares defended by two pawns
kingRing[Us] &= ~pawn_double_attacks_bb<Us>(pos.pieces(Us, PAWN)); kingRing[Us] &= ~dblAttackByPawn;
} }
@ -326,7 +328,7 @@ namespace {
// bishop, bigger when the center files are blocked with pawns. // bishop, bigger when the center files are blocked with pawns.
Bitboard blocked = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces()); Bitboard blocked = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces());
score -= BishopPawns * pe->pawns_on_same_color_squares(Us, s) score -= BishopPawns * pos.pawns_on_same_color_squares(Us, s)
* (1 + popcount(blocked & CenterFiles)); * (1 + popcount(blocked & CenterFiles));
// Bonus for bishop on a long diagonal which can "see" both center squares // Bonus for bishop on a long diagonal which can "see" both center squares
@ -356,8 +358,8 @@ namespace {
score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]); score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
// Bonus for rook on an open or semi-open file // Bonus for rook on an open or semi-open file
if (pe->semiopen_file(Us, file_of(s))) if (pos.semiopen_file(Us, file_of(s)))
score += RookOnFile[bool(pe->semiopen_file(Them, file_of(s)))]; score += RookOnFile[bool(pos.semiopen_file(Them, file_of(s)))];
// Penalty when trapped by the king, even more if the king cannot castle // Penalty when trapped by the king, even more if the king cannot castle
else if (mob <= 3) else if (mob <= 3)
@ -465,15 +467,16 @@ namespace {
+ 69 * kingAttacksCount[Them] + 69 * kingAttacksCount[Them]
+ 185 * popcount(kingRing[Us] & weak) + 185 * popcount(kingRing[Us] & weak)
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
- 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING])
+ 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks) + 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks)
- 873 * !pos.count<QUEEN>(Them) - 873 * !pos.count<QUEEN>(Them)
- 6 * mg_value(score) / 8 - 6 * mg_value(score) / 8
+ mg_value(mobility[Them] - mobility[Us]) + mg_value(mobility[Them] - mobility[Us])
+ 5 * kingFlankAttacks * kingFlankAttacks / 16 + 5 * kingFlankAttacks * kingFlankAttacks / 16
- 25; - 7;
// Transform the kingDanger units into a Score, and subtract it from the evaluation // Transform the kingDanger units into a Score, and subtract it from the evaluation
if (kingDanger > 0) if (kingDanger > 100)
score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16); score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);
// Penalty when our king is on a pawnless flank // Penalty when our king is on a pawnless flank
@ -718,7 +721,7 @@ namespace {
int bonus = popcount(safe) + popcount(behind & safe); int bonus = popcount(safe) + popcount(behind & safe);
int weight = pos.count<ALL_PIECES>(Us) int weight = pos.count<ALL_PIECES>(Us)
- 2 * popcount(pe->semiopenFiles[WHITE] & pe->semiopenFiles[BLACK]); - (16 - pos.count<PAWN>()) / 4;
Score score = make_score(bonus * weight * weight / 16, 0); Score score = make_score(bonus * weight * weight / 16, 0);
@ -743,12 +746,12 @@ namespace {
&& (pos.pieces(PAWN) & KingSide); && (pos.pieces(PAWN) & KingSide);
// Compute the initiative bonus for the attacking side // Compute the initiative bonus for the attacking side
int complexity = 9 * pe->pawn_asymmetry() int complexity = 9 * pe->passed_count()
+ 11 * pos.count<PAWN>() + 11 * pos.count<PAWN>()
+ 9 * outflanking + 9 * outflanking
+ 18 * pawnsOnBothFlanks + 18 * pawnsOnBothFlanks
+ 49 * !pos.non_pawn_material() + 49 * !pos.non_pawn_material()
-121 ; -103 ;
// Now apply the bonus: note that we find the attacking side by extracting // Now apply the bonus: note that we find the attacking side by extracting
// the sign of the endgame value, and that we carefully cap the bonus so // the sign of the endgame value, and that we carefully cap the bonus so
@ -776,7 +779,7 @@ namespace {
if ( pos.opposite_bishops() if ( pos.opposite_bishops()
&& pos.non_pawn_material(WHITE) == BishopValueMg && pos.non_pawn_material(WHITE) == BishopValueMg
&& pos.non_pawn_material(BLACK) == BishopValueMg) && pos.non_pawn_material(BLACK) == BishopValueMg)
sf = 8 + 4 * pe->pawn_asymmetry(); sf = 16 + 4 * pe->passed_count();
else else
sf = std::min(40 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide), sf); sf = std::min(40 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide), sf);

View file

@ -44,7 +44,6 @@ int main(int argc, char* argv[]) {
Position::init(); Position::init();
Bitbases::init(); Bitbases::init();
Search::init(); Search::init();
Pawns::init();
Threads.set(Options["Threads"]); Threads.set(Options["Threads"]);
Search::clear(); // After threads are up Search::clear(); // After threads are up

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm> // For std::min
#include <cassert> #include <cassert>
#include <cstring> // For std::memset #include <cstring> // For std::memset
@ -70,14 +69,12 @@ namespace {
bool is_KBPsK(const Position& pos, Color us) { bool is_KBPsK(const Position& pos, Color us) {
return pos.non_pawn_material(us) == BishopValueMg return pos.non_pawn_material(us) == BishopValueMg
&& pos.count<BISHOP>(us) == 1
&& pos.count<PAWN >(us) >= 1; && pos.count<PAWN >(us) >= 1;
} }
bool is_KQKRPs(const Position& pos, Color us) { bool is_KQKRPs(const Position& pos, Color us) {
return !pos.count<PAWN>(us) return !pos.count<PAWN>(us)
&& pos.non_pawn_material(us) == QueenValueMg && pos.non_pawn_material(us) == QueenValueMg
&& pos.count<QUEEN>(us) == 1
&& pos.count<ROOK>(~us) == 1 && pos.count<ROOK>(~us) == 1
&& pos.count<PAWN>(~us) >= 1; && pos.count<PAWN>(~us) >= 1;
} }
@ -132,7 +129,7 @@ Entry* probe(const Position& pos) {
Value npm_w = pos.non_pawn_material(WHITE); Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK); Value npm_b = pos.non_pawn_material(BLACK);
Value npm = std::max(EndgameLimit, std::min(npm_w + npm_b, MidgameLimit)); Value npm = clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
// Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME] // Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit)); e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
@ -152,9 +149,9 @@ Entry* probe(const Position& pos) {
// OK, we didn't find any special evaluation function for the current material // OK, we didn't find any special evaluation function for the current material
// configuration. Is there a suitable specialized scaling function? // configuration. Is there a suitable specialized scaling function?
const EndgameBase<ScaleFactor>* sf; const auto* sf = pos.this_thread()->endgames.probe<ScaleFactor>(key);
if ((sf = pos.this_thread()->endgames.probe<ScaleFactor>(key)) != nullptr) if (sf)
{ {
e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned
return e; return e;

View file

@ -53,7 +53,7 @@ struct HashTable {
Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; } Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; }
private: private:
std::vector<Entry> table = std::vector<Entry>(Size); std::vector<Entry> table = std::vector<Entry>(Size); // Allocate on the heap
}; };

View file

@ -139,12 +139,12 @@ Move MovePicker::select(Pred filter) {
if (T == Best) if (T == Best)
std::swap(*cur, *std::max_element(cur, endMoves)); std::swap(*cur, *std::max_element(cur, endMoves));
move = *cur++; if (*cur != ttMove && filter())
return *cur++;
if (move != ttMove && filter()) cur++;
return move;
} }
return move = MOVE_NONE; return MOVE_NONE;
} }
/// MovePicker::next_move() is the most important method of the MovePicker class. It /// MovePicker::next_move() is the most important method of the MovePicker class. It
@ -174,10 +174,10 @@ top:
case GOOD_CAPTURE: case GOOD_CAPTURE:
if (select<Best>([&](){ if (select<Best>([&](){
return pos.see_ge(move, Value(-55 * (cur-1)->value / 1024)) ? return pos.see_ge(*cur, Value(-55 * cur->value / 1024)) ?
// Move losing capture to endBadCaptures to be tried later // Move losing capture to endBadCaptures to be tried later
true : (*endBadCaptures++ = move, false); })) true : (*endBadCaptures++ = *cur, false); }))
return move; return *(cur - 1);
// Prepare the pointers to loop over the refutations array // Prepare the pointers to loop over the refutations array
cur = std::begin(refutations); cur = std::begin(refutations);
@ -192,10 +192,10 @@ top:
/* fallthrough */ /* fallthrough */
case REFUTATION: case REFUTATION:
if (select<Next>([&](){ return move != MOVE_NONE if (select<Next>([&](){ return *cur != MOVE_NONE
&& !pos.capture(move) && !pos.capture(*cur)
&& pos.pseudo_legal(move); })) && pos.pseudo_legal(*cur); }))
return move; return *(cur - 1);
++stage; ++stage;
/* fallthrough */ /* fallthrough */
@ -210,10 +210,10 @@ top:
case QUIET: case QUIET:
if ( !skipQuiets if ( !skipQuiets
&& select<Next>([&](){return move != refutations[0] && select<Next>([&](){return *cur != refutations[0].move
&& move != refutations[1] && *cur != refutations[1].move
&& move != refutations[2];})) && *cur != refutations[2].move;}))
return move; return *(cur - 1);
// Prepare the pointers to loop over the bad captures // Prepare the pointers to loop over the bad captures
cur = moves; cur = moves;
@ -237,12 +237,12 @@ top:
return select<Best>([](){ return true; }); return select<Best>([](){ return true; });
case PROBCUT: case PROBCUT:
return select<Best>([&](){ return pos.see_ge(move, threshold); }); return select<Best>([&](){ return pos.see_ge(*cur, threshold); });
case QCAPTURE: case QCAPTURE:
if (select<Best>([&](){ return depth > DEPTH_QS_RECAPTURES if (select<Best>([&](){ return depth > DEPTH_QS_RECAPTURES
|| to_sq(move) == recaptureSquare; })) || to_sq(*cur) == recaptureSquare; }))
return move; return *(cur - 1);
// If we did not find any move and we do not try checks, we have finished // If we did not find any move and we do not try checks, we have finished
if (depth != DEPTH_QS_CHECKS) if (depth != DEPTH_QS_CHECKS)

View file

@ -142,7 +142,6 @@ private:
Move ttMove; Move ttMove;
ExtMove refutations[3], *cur, *endMoves, *endBadCaptures; ExtMove refutations[3], *cur, *endMoves, *endBadCaptures;
int stage; int stage;
Move move;
Square recaptureSquare; Square recaptureSquare;
Value threshold; Value threshold;
Depth depth; Depth depth;

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include "bitboard.h" #include "bitboard.h"
@ -36,8 +35,8 @@ namespace {
constexpr Score Doubled = S(11, 56); constexpr Score Doubled = S(11, 56);
constexpr Score Isolated = S( 5, 15); constexpr Score Isolated = S( 5, 15);
// Connected pawn bonus by opposed, phalanx, #support and rank // Connected pawn bonus
Score Connected[2][2][3][RANK_NB]; constexpr int Connected[RANK_NB] = { 0, 13, 17, 24, 59, 96, 171 };
// Strength of pawn shelter for our king by [distance from edge][rank]. // Strength of pawn shelter for our king by [distance from edge][rank].
// RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king.
@ -78,11 +77,8 @@ namespace {
Bitboard theirPawns = pos.pieces(Them, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0; e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0;
e->semiopenFiles[Us] = 0xFF;
e->kingSquares[Us] = SQ_NONE; e->kingSquares[Us] = SQ_NONE;
e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns); e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns);
e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & DarkSquares);
e->pawnsOnSquares[Us][WHITE] = pos.count<PAWN>(Us) - e->pawnsOnSquares[Us][BLACK];
// Loop through all pawns of the current color and score each pawn // Loop through all pawns of the current color and score each pawn
while ((s = *pl++) != SQ_NONE) while ((s = *pl++) != SQ_NONE)
@ -90,8 +86,8 @@ namespace {
assert(pos.piece_on(s) == make_piece(Us, PAWN)); assert(pos.piece_on(s) == make_piece(Us, PAWN));
File f = file_of(s); File f = file_of(s);
Rank r = relative_rank(Us, s);
e->semiopenFiles[Us] &= ~(1 << f);
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
// Flag the pawn // Flag the pawn
@ -114,12 +110,11 @@ namespace {
// which could become passed after one or two pawn pushes when are // which could become passed after one or two pawn pushes when are
// not attacked more times than defended. // not attacked more times than defended.
if ( !(stoppers ^ lever ^ leverPush) if ( !(stoppers ^ lever ^ leverPush)
&& popcount(support) >= popcount(lever) - 1 && (support || !more_than_one(lever))
&& popcount(phalanx) >= popcount(leverPush)) && popcount(phalanx) >= popcount(leverPush))
e->passedPawns[Us] |= s; e->passedPawns[Us] |= s;
else if ( stoppers == SquareBB[s + Up] else if (stoppers == square_bb(s + Up) && r >= RANK_5)
&& relative_rank(Us, s) >= RANK_5)
{ {
b = shift<Up>(support) & ~theirPawns; b = shift<Up>(support) & ~theirPawns;
while (b) while (b)
@ -129,8 +124,11 @@ namespace {
// Score this pawn // Score this pawn
if (support | phalanx) if (support | phalanx)
score += Connected[opposed][bool(phalanx)][popcount(support)][relative_rank(Us, s)]; {
int v = (phalanx ? 3 : 2) * Connected[r];
v = 17 * popcount(support) + (v >> (opposed + 1));
score += make_score(v, v * (r - 2) / 4);
}
else if (!neighbours) else if (!neighbours)
score -= Isolated, e->weakUnopposed[Us] += !opposed; score -= Isolated, e->weakUnopposed[Us] += !opposed;
@ -148,27 +146,6 @@ namespace {
namespace Pawns { namespace Pawns {
/// Pawns::init() initializes some tables needed by evaluation. Instead of using
/// hard-coded tables, when makes sense, we prefer to calculate them with a formula
/// to reduce independent parameters and to allow easier tuning and better insight.
void init() {
static constexpr int Seed[RANK_NB] = { 0, 13, 24, 18, 65, 100, 175, 330 };
for (int opposed = 0; opposed <= 1; ++opposed)
for (int phalanx = 0; phalanx <= 1; ++phalanx)
for (int support = 0; support <= 2; ++support)
for (Rank r = RANK_2; r < RANK_8; ++r)
{
int v = 17 * support;
v += (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed;
Connected[opposed][phalanx][support][r] = make_score(v, v * (r - 2) / 4);
}
}
/// Pawns::probe() looks up the current position's pawns configuration in /// Pawns::probe() looks up the current position's pawns configuration in
/// the pawns hash table. It returns a pointer to the Entry if the position /// the pawns hash table. It returns a pointer to the Entry if the position
/// is found. Otherwise a new Entry is computed and stored there, so we don't /// is found. Otherwise a new Entry is computed and stored there, so we don't
@ -185,8 +162,6 @@ Entry* probe(const Position& pos) {
e->key = key; e->key = key;
e->scores[WHITE] = evaluate<WHITE>(pos, e); e->scores[WHITE] = evaluate<WHITE>(pos, e);
e->scores[BLACK] = evaluate<BLACK>(pos, e); e->scores[BLACK] = evaluate<BLACK>(pos, e);
e->asymmetry = popcount( (e->passedPawns[WHITE] | e->passedPawns[BLACK])
| (e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]));
return e; return e;
} }
@ -209,7 +184,7 @@ Value Entry::evaluate_shelter(const Position& pos, Square ksq) {
Value safety = (shift<Down>(theirPawns) & (FileABB | FileHBB) & BlockRanks & ksq) ? Value safety = (shift<Down>(theirPawns) & (FileABB | FileHBB) & BlockRanks & ksq) ?
Value(374) : Value(5); Value(374) : Value(5);
File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq))); File center = clamp(file_of(ksq), FILE_B, FILE_G);
for (File f = File(center - 1); f <= File(center + 1); ++f) for (File f = File(center - 1); f <= File(center + 1); ++f)
{ {
b = ourPawns & file_bb(f); b = ourPawns & file_bb(f);
@ -237,11 +212,15 @@ Score Entry::do_king_safety(const Position& pos) {
Square ksq = pos.square<KING>(Us); Square ksq = pos.square<KING>(Us);
kingSquares[Us] = ksq; kingSquares[Us] = ksq;
castlingRights[Us] = pos.castling_rights(Us); castlingRights[Us] = pos.castling_rights(Us);
int minKingPawnDistance = 0;
Bitboard pawns = pos.pieces(Us, PAWN); Bitboard pawns = pos.pieces(Us, PAWN);
if (pawns) int minPawnDist = pawns ? 8 : 0;
while (!(DistanceRingBB[ksq][++minKingPawnDistance] & pawns)) {}
if (pawns & PseudoAttacks[KING][ksq])
minPawnDist = 1;
else while (pawns)
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns)));
Value bonus = evaluate_shelter<Us>(pos, ksq); Value bonus = evaluate_shelter<Us>(pos, ksq);
@ -252,7 +231,7 @@ Score Entry::do_king_safety(const Position& pos) {
if (pos.can_castle(Us | QUEEN_SIDE)) if (pos.can_castle(Us | QUEEN_SIDE))
bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1))); bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)));
return make_score(bonus, -16 * minKingPawnDistance); return make_score(bonus, -16 * minPawnDist);
} }
// Explicit template instantiation // Explicit template instantiation

View file

@ -38,15 +38,7 @@ struct Entry {
Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
int weak_unopposed(Color c) const { return weakUnopposed[c]; } int weak_unopposed(Color c) const { return weakUnopposed[c]; }
int pawn_asymmetry() const { return asymmetry; } int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); };
int semiopen_file(Color c, File f) const {
return semiopenFiles[c] & (1 << f);
}
int pawns_on_same_color_squares(Color c, Square s) const {
return pawnsOnSquares[c][bool(DarkSquares & s)];
}
template<Color Us> template<Color Us>
Score king_safety(const Position& pos) { Score king_safety(const Position& pos) {
@ -69,14 +61,11 @@ struct Entry {
Score kingSafety[COLOR_NB]; Score kingSafety[COLOR_NB];
int weakUnopposed[COLOR_NB]; int weakUnopposed[COLOR_NB];
int castlingRights[COLOR_NB]; int castlingRights[COLOR_NB];
int semiopenFiles[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares] int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
int asymmetry;
}; };
typedef HashTable<Entry, 16384> Table; typedef HashTable<Entry, 16384> Table;
void init();
Entry* probe(const Position& pos); Entry* probe(const Position& pos);
} // namespace Pawns } // namespace Pawns

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include <cstddef> // For offsetof() #include <cstddef> // For offsetof()
#include <cstring> // For std::memset, std::memcmp #include <cstring> // For std::memset, std::memcmp
@ -341,13 +340,8 @@ void Position::set_castling_right(Color c, Square rfrom) {
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1); Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1); Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); ++s) castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
if (s != kfrom && s != rfrom) & ~(square_bb(kfrom) | rfrom);
castlingPath[cr] |= s;
for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); ++s)
if (s != kfrom && s != rfrom)
castlingPath[cr] |= s;
} }
@ -629,7 +623,7 @@ bool Position::pseudo_legal(const Move m) const {
{ {
// We have already handled promotion moves, so destination // We have already handled promotion moves, so destination
// cannot be on the 8th/1st rank. // cannot be on the 8th/1st rank.
if (rank_of(to) == relative_rank(us, RANK_8)) if ((Rank8BB | Rank1BB) & to)
return false; return false;
if ( !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture if ( !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture

View file

@ -95,6 +95,7 @@ public:
template<PieceType Pt> int count() const; template<PieceType Pt> int count() const;
template<PieceType Pt> const Square* squares(Color c) const; template<PieceType Pt> const Square* squares(Color c) const;
template<PieceType Pt> Square square(Color c) const; template<PieceType Pt> Square square(Color c) const;
int semiopen_file(Color c, File f) const;
// Castling // Castling
int castling_rights(Color c) const; int castling_rights(Color c) const;
@ -128,6 +129,7 @@ public:
// Piece specific // Piece specific
bool pawn_passed(Color c, Square s) const; bool pawn_passed(Color c, Square s) const;
bool opposite_bishops() const; bool opposite_bishops() const;
int pawns_on_same_color_squares(Color c, Square s) const;
// Doing and undoing moves // Doing and undoing moves
void do_move(Move m, StateInfo& newSt); void do_move(Move m, StateInfo& newSt);
@ -260,6 +262,10 @@ inline Square Position::ep_square() const {
return st->epSquare; return st->epSquare;
} }
inline int Position::semiopen_file(Color c, File f) const {
return !(pieces(c, PAWN) & file_bb(f));
}
inline bool Position::can_castle(CastlingRight cr) const { inline bool Position::can_castle(CastlingRight cr) const {
return st->castlingRights & cr; return st->castlingRights & cr;
} }
@ -318,6 +324,10 @@ inline bool Position::advanced_pawn_push(Move m) const {
&& relative_rank(sideToMove, from_sq(m)) > RANK_4; && relative_rank(sideToMove, from_sq(m)) > RANK_4;
} }
inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
return popcount(pieces(c, PAWN) & ((DarkSquares & s) ? DarkSquares : ~DarkSquares));
}
inline Key Position::key() const { inline Key Position::key() const {
return st->key; return st->key;
} }
@ -413,7 +423,7 @@ inline void Position::move_piece(Piece pc, Square from, Square to) {
// index[from] is not updated and becomes stale. This works as long as index[] // index[from] is not updated and becomes stale. This works as long as index[]
// is accessed just by known occupied squares. // is accessed just by known occupied squares.
Bitboard fromTo = SquareBB[from] ^ SquareBB[to]; Bitboard fromTo = square_bb(from) | square_bb(to);
byTypeBB[ALL_PIECES] ^= fromTo; byTypeBB[ALL_PIECES] ^= fromTo;
byTypeBB[type_of(pc)] ^= fromTo; byTypeBB[type_of(pc)] ^= fromTo;
byColorBB[color_of(pc)] ^= fromTo; byColorBB[color_of(pc)] ^= fromTo;

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
#include <cstring> // For std::memset #include <cstring> // For std::memset
@ -69,10 +68,10 @@ namespace {
} }
// Reductions lookup table, initialized at startup // Reductions lookup table, initialized at startup
int Reductions[64]; // [depth or moveNumber] int Reductions[MAX_MOVES]; // [depth or moveNumber]
template <bool PvNode> Depth reduction(bool i, Depth d, int mn) { template <bool PvNode> Depth reduction(bool i, Depth d, int mn) {
int r = Reductions[std::min(d / ONE_PLY, 63)] * Reductions[std::min(mn, 63)] / 1024; int r = Reductions[d / ONE_PLY] * Reductions[mn] / 1024;
return ((r + 512) / 1024 + (!i && r > 1024) - PvNode) * ONE_PLY; return ((r + 512) / 1024 + (!i && r > 1024) - PvNode) * ONE_PLY;
} }
@ -86,11 +85,10 @@ namespace {
return d > 17 ? 0 : 29 * d * d + 138 * d - 134; return d > 17 ? 0 : 29 * d * d + 138 * d - 134;
} }
// Add a small random component to draw evaluations to keep search dynamic // Add a small random component to draw evaluations to avoid 3fold-blindness
// and to avoid 3fold-blindness.
Value value_draw(Depth depth, Thread* thisThread) { Value value_draw(Depth depth, Thread* thisThread) {
return depth < 4 ? VALUE_DRAW return depth < 4 * ONE_PLY ? VALUE_DRAW
: VALUE_DRAW + Value(2 * (thisThread->nodes.load(std::memory_order_relaxed) % 2) - 1); : VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1);
} }
// Skill structure is used to implement strength limit // Skill structure is used to implement strength limit
@ -117,13 +115,6 @@ namespace {
void update_quiet_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietCount, 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); 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();
return type_of(move) == NORMAL && !(pos.blockers_for_king(~us) & pos.pieces(us))
? pos.check_squares(type_of(pos.moved_piece(move))) & to_sq(move)
: pos.gives_check(move);
}
// perft() is our utility to verify move generation. All the leaf nodes up // perft() is our utility to verify move generation. All the leaf nodes up
// to the given depth are generated and counted, and the sum is returned. // to the given depth are generated and counted, and the sum is returned.
template<bool Root> template<bool Root>
@ -157,7 +148,7 @@ namespace {
void Search::init() { void Search::init() {
for (int i = 1; i < 64; ++i) for (int i = 1; i < MAX_MOVES; ++i)
Reductions[i] = int(1024 * std::log(i) / std::sqrt(1.95)); Reductions[i] = int(1024 * std::log(i) / std::sqrt(1.95));
} }
@ -171,12 +162,12 @@ void Search::clear() {
Time.availableNodes = 0; Time.availableNodes = 0;
TT.clear(); TT.clear();
Threads.clear(); Threads.clear();
Tablebases::init(Options["SyzygyPath"]); // Free up mapped files Tablebases::init(Options["SyzygyPath"]); // Free mapped files
} }
/// MainThread::search() is called by the main thread when the program receives /// MainThread::search() is started when the program receives the UCI 'go'
/// the UCI 'go' command. It searches from the root position and outputs the "bestmove". /// command. It searches from the root position and outputs the "bestmove".
void MainThread::search() { void MainThread::search() {
@ -203,8 +194,11 @@ void MainThread::search() {
else else
{ {
for (Thread* th : Threads) for (Thread* th : Threads)
{
th->bestMoveChanges = 0;
if (th != this) if (th != this)
th->start_searching(); th->start_searching();
}
Thread::search(); // Let's start searching! Thread::search(); // Let's start searching!
} }
@ -235,8 +229,9 @@ void MainThread::search() {
if (Limits.npmsec) if (Limits.npmsec)
Time.availableNodes += Limits.inc[us] - Cluster::nodes_searched(); Time.availableNodes += Limits.inc[us] - Cluster::nodes_searched();
// Check if there are threads with a better score than main thread
Thread* bestThread = this; Thread* bestThread = this;
// Check if there are threads with a better score than main thread
if ( Options["MultiPV"] == 1 if ( Options["MultiPV"] == 1
&& !Limits.depth && !Limits.depth
&& !Skill(Options["Skill Level"]).enabled() && !Skill(Options["Skill Level"]).enabled()
@ -310,9 +305,9 @@ void MainThread::search() {
void Thread::search() { void Thread::search() {
// To allow access to (ss-5) up to (ss+2), the stack must be oversized. // To allow access to (ss-7) up to (ss+2), the stack must be oversized.
// The former is needed to allow update_continuation_histories(ss-1, ...), // The former is needed to allow update_continuation_histories(ss-1, ...),
// which accesses its argument at ss-4, also near the root. // which accesses its argument at ss-6, also near the root.
// The latter is needed for statScores and killer initialization. // The latter is needed for statScores and killer initialization.
Stack stack[MAX_PLY+10], *ss = stack+7; Stack stack[MAX_PLY+10], *ss = stack+7;
Move pv[MAX_PLY+1]; Move pv[MAX_PLY+1];
@ -320,7 +315,7 @@ void Thread::search() {
Move lastBestMove = MOVE_NONE; Move lastBestMove = MOVE_NONE;
Depth lastBestMoveDepth = DEPTH_ZERO; Depth lastBestMoveDepth = DEPTH_ZERO;
MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr);
double timeReduction = 1.0; double timeReduction = 1, totBestMoveChanges = 0;
Color us = rootPos.side_to_move(); Color us = rootPos.side_to_move();
std::memset(ss-7, 0, 10 * sizeof(Stack)); std::memset(ss-7, 0, 10 * sizeof(Stack));
@ -331,9 +326,6 @@ void Thread::search() {
bestValue = delta = alpha = -VALUE_INFINITE; bestValue = delta = alpha = -VALUE_INFINITE;
beta = VALUE_INFINITE; beta = VALUE_INFINITE;
if (mainThread)
mainThread->bestMoveChanges = 0;
size_t multiPV = Options["MultiPV"]; size_t multiPV = Options["MultiPV"];
Skill skill(Options["Skill Level"]); Skill skill(Options["Skill Level"]);
@ -354,7 +346,7 @@ void Thread::search() {
: Options["Analysis Contempt"] == "Black" && us == WHITE ? -ct : Options["Analysis Contempt"] == "Black" && us == WHITE ? -ct
: ct; : ct;
// In evaluate.cpp the evaluation is from the white point of view // Evaluation score is from the white point of view
contempt = (us == WHITE ? make_score(ct, ct / 2) contempt = (us == WHITE ? make_score(ct, ct / 2)
: -make_score(ct, ct / 2)); : -make_score(ct, ct / 2));
@ -365,7 +357,7 @@ void Thread::search() {
{ {
// Age out PV variability metric // Age out PV variability metric
if (mainThread) if (mainThread)
mainThread->bestMoveChanges *= 0.517; totBestMoveChanges /= 2;
// Save the last iteration's scores before first PV line is searched and // Save the last iteration's scores before first PV line is searched and
@ -504,15 +496,20 @@ void Thread::search() {
&& !Threads.stop && !Threads.stop
&& !mainThread->stopOnPonderhit) && !mainThread->stopOnPonderhit)
{ {
double fallingEval = (306 + 9 * (mainThread->previousScore - bestValue)) / 581.0; double fallingEval = (314 + 9 * (mainThread->previousScore - bestValue)) / 581.0;
fallingEval = std::max(0.5, std::min(1.5, fallingEval)); fallingEval = clamp(fallingEval, 0.5, 1.5);
// If the bestMove is stable over several iterations, reduce time accordingly // If the bestMove is stable over several iterations, reduce time accordingly
timeReduction = lastBestMoveDepth + 10 * ONE_PLY < completedDepth ? 1.95 : 1.0; timeReduction = lastBestMoveDepth + 10 * ONE_PLY < completedDepth ? 1.95 : 1.0;
double reduction = std::pow(mainThread->previousTimeReduction, 0.528) / timeReduction; double reduction = std::pow(mainThread->previousTimeReduction, 0.528) / timeReduction;
// Use part of the gained time from a previous stable move for the current move // Use part of the gained time from a previous stable move for the current move
double bestMoveInstability = 1.0 + mainThread->bestMoveChanges; for (Thread* th : Threads)
{
totBestMoveChanges += th->bestMoveChanges;
th->bestMoveChanges = 0;
}
double bestMoveInstability = 1 + totBestMoveChanges / Threads.size();
// Stop the search if we have only one legal move, or if available time elapsed // Stop the search if we have only one legal move, or if available time elapsed
if ( rootMoves.size() == 1 if ( rootMoves.size() == 1
@ -578,7 +575,7 @@ namespace {
Key posKey; Key posKey;
Move ttMove, move, excludedMove, bestMove; Move ttMove, move, excludedMove, bestMove;
Depth extension, newDepth; Depth extension, newDepth;
Value bestValue, value, ttValue, eval, maxValue, pureStaticEval; Value bestValue, value, ttValue, eval, maxValue;
bool ttHit, ttPv, inCheck, givesCheck, improving; bool ttHit, ttPv, inCheck, givesCheck, improving;
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture;
Piece movedPiece; Piece movedPiece;
@ -624,8 +621,7 @@ namespace {
assert(0 <= ss->ply && ss->ply < MAX_PLY); assert(0 <= ss->ply && ss->ply < MAX_PLY);
(ss+1)->ply = ss->ply + 1; (ss+1)->ply = ss->ply + 1;
ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; (ss+1)->excludedMove = bestMove = MOVE_NONE;
ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0];
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
Square prevSq = to_sq((ss-1)->currentMove); Square prevSq = to_sq((ss-1)->currentMove);
@ -647,6 +643,15 @@ namespace {
: ttHit ? tte->move() : MOVE_NONE; : ttHit ? tte->move() : MOVE_NONE;
ttPv = (ttHit && tte->is_pv()) || (PvNode && depth > 4 * ONE_PLY); ttPv = (ttHit && tte->is_pv()) || (PvNode && depth > 4 * ONE_PLY);
// If position has been searched at higher depths and we are shuffling,
// return value_draw.
if ( pos.rule50_count() > 36 - 6 * (pos.count<ALL_PIECES>() > 14)
&& ss->ply > 36 - 6 * (pos.count<ALL_PIECES>() > 14)
&& ttHit
&& tte->depth() > depth
&& pos.count<PAWN>() > 0)
return VALUE_DRAW;
// At non-PV nodes we check for an early TT cutoff // At non-PV nodes we check for an early TT cutoff
if ( !PvNode if ( !PvNode
&& ttHit && ttHit
@ -663,9 +668,8 @@ namespace {
if (!pos.capture_or_promotion(ttMove)) if (!pos.capture_or_promotion(ttMove))
update_quiet_stats(pos, ss, ttMove, nullptr, 0, stat_bonus(depth)); update_quiet_stats(pos, ss, ttMove, nullptr, 0, stat_bonus(depth));
// Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted // Extra penalty for early quiet moves of the previous ply
if ( ((ss-1)->moveCount == 1 || (ss-1)->currentMove == (ss-1)->killers[0]) if ((ss-1)->moveCount <= 2 && !pos.captured_piece())
&& !pos.captured_piece())
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY)); update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY));
} }
// Penalty for a quiet ttMove that fails low // Penalty for a quiet ttMove that fails low
@ -734,16 +738,16 @@ namespace {
// Step 6. Static evaluation of the position // Step 6. Static evaluation of the position
if (inCheck) if (inCheck)
{ {
ss->staticEval = eval = pureStaticEval = VALUE_NONE; ss->staticEval = eval = VALUE_NONE;
improving = false; improving = false;
goto moves_loop; // Skip early pruning when in check goto moves_loop; // Skip early pruning when in check
} }
else if (ttHit) else if (ttHit)
{ {
// Never assume anything on values stored in TT // Never assume anything on values stored in TT
ss->staticEval = eval = pureStaticEval = tte->eval(); ss->staticEval = eval = tte->eval();
if (eval == VALUE_NONE) if (eval == VALUE_NONE)
ss->staticEval = eval = pureStaticEval = evaluate(pos); ss->staticEval = eval = evaluate(pos);
// Can ttValue be used as a better position evaluation? // Can ttValue be used as a better position evaluation?
if ( ttValue != VALUE_NONE if ( ttValue != VALUE_NONE
@ -756,15 +760,14 @@ namespace {
{ {
int bonus = -(ss-1)->statScore / 512; int bonus = -(ss-1)->statScore / 512;
pureStaticEval = evaluate(pos); ss->staticEval = eval = evaluate(pos) + bonus;
ss->staticEval = eval = pureStaticEval + bonus;
} }
else else
ss->staticEval = eval = pureStaticEval = -(ss-1)->staticEval + 2 * Eval::Tempo; ss->staticEval = eval = -(ss-1)->staticEval + 2 * Eval::Tempo;
Cluster::save(thisThread, tte, Cluster::save(thisThread, tte,
posKey, VALUE_NONE, ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, posKey, VALUE_NONE, ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE,
pureStaticEval); eval);
} }
// Step 7. Razoring (~2 Elo) // Step 7. Razoring (~2 Elo)
@ -788,7 +791,7 @@ namespace {
&& (ss-1)->currentMove != MOVE_NULL && (ss-1)->currentMove != MOVE_NULL
&& (ss-1)->statScore < 23200 && (ss-1)->statScore < 23200
&& eval >= beta && eval >= beta
&& pureStaticEval >= beta - 36 * depth / ONE_PLY + 225 && ss->staticEval >= beta - 36 * depth / ONE_PLY + 225
&& !excludedMove && !excludedMove
&& pos.non_pawn_material(us) && pos.non_pawn_material(us)
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
@ -820,7 +823,7 @@ namespace {
// Do verification search at high depths, with null move pruning disabled // Do verification search at high depths, with null move pruning disabled
// for us, until ply exceeds nmpMinPly. // for us, until ply exceeds nmpMinPly.
thisThread->nmpMinPly = ss->ply + 3 * (depth-R) / 4; thisThread->nmpMinPly = ss->ply + 3 * (depth-R) / (4 * ONE_PLY);
thisThread->nmpColor = us; thisThread->nmpColor = us;
Value v = search<NonPV>(pos, ss, beta-1, beta, depth-R, false); Value v = search<NonPV>(pos, ss, beta-1, beta, depth-R, false);
@ -871,8 +874,7 @@ namespace {
} }
// Step 11. Internal iterative deepening (~2 Elo) // Step 11. Internal iterative deepening (~2 Elo)
if ( depth >= 8 * ONE_PLY if (depth >= 8 * ONE_PLY && !ttMove)
&& !ttMove)
{ {
search<NT>(pos, ss, alpha, beta, depth - 7 * ONE_PLY, cutNode); search<NT>(pos, ss, alpha, beta, depth - 7 * ONE_PLY, cutNode);
@ -886,6 +888,7 @@ moves_loop: // When in check, search starts from here
const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory,
nullptr, (ss-4)->continuationHistory, nullptr, (ss-4)->continuationHistory,
nullptr, (ss-6)->continuationHistory }; nullptr, (ss-6)->continuationHistory };
Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]; Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq];
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory,
@ -893,8 +896,8 @@ moves_loop: // When in check, search starts from here
contHist, contHist,
countermove, countermove,
ss->killers); ss->killers);
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
moveCountPruning = false; moveCountPruning = false;
ttCapture = ttMove && pos.capture_or_promotion(ttMove); ttCapture = ttMove && pos.capture_or_promotion(ttMove);
@ -927,7 +930,7 @@ moves_loop: // When in check, search starts from here
extension = DEPTH_ZERO; extension = DEPTH_ZERO;
captureOrPromotion = pos.capture_or_promotion(move); captureOrPromotion = pos.capture_or_promotion(move);
movedPiece = pos.moved_piece(move); movedPiece = pos.moved_piece(move);
givesCheck = gives_check(pos, move); givesCheck = pos.gives_check(move);
// Step 13. Extensions (~70 Elo) // Step 13. Extensions (~70 Elo)
@ -947,8 +950,9 @@ moves_loop: // When in check, search starts from here
&& pos.legal(move)) && pos.legal(move))
{ {
Value singularBeta = ttValue - 2 * depth / ONE_PLY; Value singularBeta = ttValue - 2 * depth / ONE_PLY;
Depth halfDepth = depth / (2 * ONE_PLY) * ONE_PLY; // ONE_PLY invariant
ss->excludedMove = move; ss->excludedMove = move;
value = search<NonPV>(pos, ss, singularBeta - 1, singularBeta, depth / 2, cutNode); value = search<NonPV>(pos, ss, singularBeta - 1, singularBeta, halfDepth, cutNode);
ss->excludedMove = MOVE_NONE; ss->excludedMove = MOVE_NONE;
if (value < singularBeta) if (value < singularBeta)
@ -972,6 +976,20 @@ moves_loop: // When in check, search starts from here
else if (type_of(move) == CASTLING) else if (type_of(move) == CASTLING)
extension = ONE_PLY; extension = ONE_PLY;
// Shuffle extension
else if ( PvNode
&& pos.rule50_count() > 18
&& ss->ply > 18
&& depth < 3 * ONE_PLY
&& ss->ply < 3 * thisThread->rootDepth / ONE_PLY) // To avoid infinite loops
extension = ONE_PLY;
// Passed pawn extension
else if ( move == ss->killers[0]
&& pos.advanced_pawn_push(move)
&& pos.pawn_passed(us, to_sq(move)))
extension = ONE_PLY;
// Calculate new depth for this move // Calculate new depth for this move
newDepth = depth - ONE_PLY + extension; newDepth = depth - ONE_PLY + extension;
@ -981,7 +999,7 @@ moves_loop: // When in check, search starts from here
&& bestValue > VALUE_MATED_IN_MAX_PLY) && bestValue > VALUE_MATED_IN_MAX_PLY)
{ {
// Skip quiet moves if movecount exceeds our FutilityMoveCount threshold // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold
moveCountPruning = moveCount >= futility_move_count(improving,depth / ONE_PLY); moveCountPruning = moveCount >= futility_move_count(improving, depth / ONE_PLY);
if ( !captureOrPromotion if ( !captureOrPromotion
&& !givesCheck && !givesCheck
@ -992,7 +1010,8 @@ moves_loop: // When in check, search starts from here
continue; continue;
// Reduced depth of the next LMR search // Reduced depth of the next LMR search
int lmrDepth = std::max(newDepth - reduction<PvNode>(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY; int lmrDepth = std::max(newDepth - reduction<PvNode>(improving, depth, moveCount), DEPTH_ZERO);
lmrDepth /= ONE_PLY;
// Countermoves based pruning (~20 Elo) // Countermoves based pruning (~20 Elo)
if ( lmrDepth < 3 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1) if ( lmrDepth < 3 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1)
@ -1137,8 +1156,8 @@ moves_loop: // When in check, search starts from here
// We record how often the best move has been changed in each // We record how often the best move has been changed in each
// iteration. This information is used for time management: When // iteration. This information is used for time management: When
// the best move changes frequently, we allocate some more time. // the best move changes frequently, we allocate some more time.
if (moveCount > 1 && thisThread == Threads.main()) if (moveCount > 1)
++static_cast<MainThread*>(thisThread)->bestMoveChanges; ++thisThread->bestMoveChanges;
} }
else else
// All other moves but the PV are set to the lowest value: this // All other moves but the PV are set to the lowest value: this
@ -1225,7 +1244,7 @@ moves_loop: // When in check, search starts from here
posKey, value_to_tt(bestValue, ss->ply), ttPv, posKey, value_to_tt(bestValue, ss->ply), ttPv,
bestValue >= beta ? BOUND_LOWER : bestValue >= beta ? BOUND_LOWER :
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
depth, bestMove, pureStaticEval); depth, bestMove, ss->staticEval);
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
@ -1264,8 +1283,7 @@ moves_loop: // When in check, search starts from here
Thread* thisThread = pos.this_thread(); Thread* thisThread = pos.this_thread();
(ss+1)->ply = ss->ply + 1; (ss+1)->ply = ss->ply + 1;
ss->currentMove = bestMove = MOVE_NONE; bestMove = MOVE_NONE;
ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0];
inCheck = pos.checkers(); inCheck = pos.checkers();
moveCount = 0; moveCount = 0;
@ -1355,7 +1373,7 @@ moves_loop: // When in check, search starts from here
{ {
assert(is_ok(move)); assert(is_ok(move));
givesCheck = gives_check(pos, move); givesCheck = pos.gives_check(move);
moveCount++; moveCount++;

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm> // For std::count
#include <cassert> #include <cassert>
#include "movegen.h" #include "movegen.h"
@ -32,7 +31,7 @@ ThreadPool Threads; // Global object
/// Thread constructor launches the thread and waits until it goes to sleep /// Thread constructor launches the thread and waits until it goes to sleep
/// in idle_loop(). Note that 'searching' and 'exit' should be alredy set. /// in idle_loop(). Note that 'searching' and 'exit' should be already set.
Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) { Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) {

View file

@ -64,7 +64,7 @@ public:
size_t pvIdx, pvLast; size_t pvIdx, pvLast;
int selDepth, nmpMinPly; int selDepth, nmpMinPly;
Color nmpColor; Color nmpColor;
std::atomic<uint64_t> nodes, tbHits, TTsaves; std::atomic<uint64_t> nodes, tbHits, TTsaves, bestMoveChanges;
Position rootPos; Position rootPos;
Search::RootMoves rootMoves; Search::RootMoves rootMoves;
@ -93,7 +93,7 @@ struct MainThread : public Thread {
void search() override; void search() override;
void check_time(); void check_time();
double bestMoveChanges, previousTimeReduction; double previousTimeReduction;
Value previousScore; Value previousScore;
int callsCnt; int callsCnt;
bool stopOnPonderhit; bool stopOnPonderhit;

View file

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

View file

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