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:
commit
85327828c9
21 changed files with 186 additions and 231 deletions
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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'.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
13
src/pawns.h
13
src/pawns.h
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
134
src/search.cpp
134
src/search.cpp
|
@ -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++;
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Reference in a new issue