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-32 optimize=no debug=yes 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
|
||||
#
|
||||
# Check perft and reproducible search
|
||||
|
|
|
@ -490,7 +490,7 @@ config-sanity:
|
|||
@echo "Testing config sanity. If this fails, try 'make help' ..."
|
||||
@echo ""
|
||||
@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 "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <algorithm>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "misc.h"
|
||||
|
@ -27,13 +27,10 @@
|
|||
uint8_t PopCnt16[1 << 16];
|
||||
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 DistanceRingBB[SQUARE_NB][8];
|
||||
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||
Bitboard SquareBB[SQUARE_NB];
|
||||
|
||||
Bitboard KingFlank[FILE_NB] = {
|
||||
QueenSide ^ FileDBB, QueenSide, QueenSide,
|
||||
|
@ -85,10 +82,7 @@ void Bitboards::init() {
|
|||
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++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 } };
|
||||
|
||||
|
@ -122,10 +116,7 @@ void Bitboards::init() {
|
|||
for (PieceType pt : { BISHOP, ROOK })
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||
if (PseudoAttacks[pt][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 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 DistanceRingBB[SQUARE_NB][8];
|
||||
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||
extern Bitboard KingFlank[FILE_NB];
|
||||
extern Bitboard SquareBB[SQUARE_NB];
|
||||
|
||||
|
||||
/// Magic holds all magic bitboards relevant data for a single square
|
||||
|
@ -102,34 +100,19 @@ struct Magic {
|
|||
extern Magic RookMagics[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
|
||||
/// whether a given bit is set in a bitboard, and for setting and clearing bits.
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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) { 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) { return b |= square_bb(s); }
|
||||
inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }
|
||||
|
||||
constexpr bool more_than_one(Bitboard b) {
|
||||
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
|
||||
/// given ones. For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with
|
||||
/// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank,
|
||||
/// file or diagonal, 0 is returned.
|
||||
/// between_bb() returns squares that are linearly between the given squares
|
||||
/// If the given squares are not on a same file/rank/diagonal, return 0.
|
||||
|
||||
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
|
||||
/// 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<typename T1, typename T2> inline int distance(T2 x, T2 y);
|
||||
template<> inline int distance<File>(Square x, Square y) { return distance(file_of(x), file_of(y)); }
|
||||
template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_of(x), rank_of(y)); }
|
||||
|
||||
template<class T> constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
|
||||
return v < lo ? lo : v > hi ? hi : v;
|
||||
}
|
||||
|
||||
/// attacks_bb() returns a bitboard representing all the squares attacked by a
|
||||
/// 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/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "bitboard.h"
|
||||
|
@ -77,10 +76,7 @@ namespace {
|
|||
if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E)
|
||||
sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1
|
||||
|
||||
if (strongSide == BLACK)
|
||||
sq = ~sq;
|
||||
|
||||
return sq;
|
||||
return strongSide == WHITE ? sq : ~sq;
|
||||
}
|
||||
|
||||
} // 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<>
|
||||
Value Endgame<KNNKP>::operator()(const Position& pos) const {
|
||||
|
||||
assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0));
|
||||
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
|
||||
assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0));
|
||||
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
|
||||
|
||||
Value result = 2 * KnightValueEg
|
||||
- PawnValueEg
|
||||
+ PushToEdges[pos.square<KING>(weakSide)];
|
||||
Value result = 2 * KnightValueEg
|
||||
- PawnValueEg
|
||||
+ 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 psq1 = pos.squares<PAWN>(strongSide)[0];
|
||||
Square psq2 = pos.squares<PAWN>(strongSide)[1];
|
||||
Rank r1 = rank_of(psq1);
|
||||
Rank r2 = rank_of(psq2);
|
||||
Square blockSq1, blockSq2;
|
||||
|
||||
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)
|
||||
&& ( bbsq == blockSq2
|
||||
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
|
||||
|| distance(r1, r2) >= 2))
|
||||
|| distance<Rank>(psq1, psq2) >= 2))
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
||||
else if ( ksq == blockSq2
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring> // For std::memset
|
||||
#include <iomanip>
|
||||
|
@ -229,6 +228,8 @@ namespace {
|
|||
|
||||
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
|
||||
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][PAWN] = pe->pawn_attacks(Us);
|
||||
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
|
||||
kingRing[Us] = attackedBy[Us][KING];
|
||||
|
@ -257,7 +259,7 @@ namespace {
|
|||
kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
|
||||
|
||||
// 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.
|
||||
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));
|
||||
|
||||
// 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]);
|
||||
|
||||
// Bonus for rook on an open or semi-open file
|
||||
if (pe->semiopen_file(Us, file_of(s)))
|
||||
score += RookOnFile[bool(pe->semiopen_file(Them, file_of(s)))];
|
||||
if (pos.semiopen_file(Us, 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
|
||||
else if (mob <= 3)
|
||||
|
@ -465,15 +467,16 @@ namespace {
|
|||
+ 69 * kingAttacksCount[Them]
|
||||
+ 185 * popcount(kingRing[Us] & weak)
|
||||
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
|
||||
- 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING])
|
||||
+ 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks)
|
||||
- 873 * !pos.count<QUEEN>(Them)
|
||||
- 6 * mg_value(score) / 8
|
||||
+ mg_value(mobility[Them] - mobility[Us])
|
||||
+ 5 * kingFlankAttacks * kingFlankAttacks / 16
|
||||
- 25;
|
||||
- 7;
|
||||
|
||||
// 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);
|
||||
|
||||
// Penalty when our king is on a pawnless flank
|
||||
|
@ -718,7 +721,7 @@ namespace {
|
|||
|
||||
int bonus = popcount(safe) + popcount(behind & safe);
|
||||
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);
|
||||
|
||||
|
@ -743,12 +746,12 @@ namespace {
|
|||
&& (pos.pieces(PAWN) & KingSide);
|
||||
|
||||
// Compute the initiative bonus for the attacking side
|
||||
int complexity = 9 * pe->pawn_asymmetry()
|
||||
int complexity = 9 * pe->passed_count()
|
||||
+ 11 * pos.count<PAWN>()
|
||||
+ 9 * outflanking
|
||||
+ 18 * pawnsOnBothFlanks
|
||||
+ 49 * !pos.non_pawn_material()
|
||||
-121 ;
|
||||
-103 ;
|
||||
|
||||
// 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
|
||||
|
@ -776,7 +779,7 @@ namespace {
|
|||
if ( pos.opposite_bishops()
|
||||
&& pos.non_pawn_material(WHITE) == BishopValueMg
|
||||
&& pos.non_pawn_material(BLACK) == BishopValueMg)
|
||||
sf = 8 + 4 * pe->pawn_asymmetry();
|
||||
sf = 16 + 4 * pe->passed_count();
|
||||
else
|
||||
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();
|
||||
Bitbases::init();
|
||||
Search::init();
|
||||
Pawns::init();
|
||||
Threads.set(Options["Threads"]);
|
||||
Search::clear(); // After threads are up
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm> // For std::min
|
||||
#include <cassert>
|
||||
#include <cstring> // For std::memset
|
||||
|
||||
|
@ -70,14 +69,12 @@ namespace {
|
|||
|
||||
bool is_KBPsK(const Position& pos, Color us) {
|
||||
return pos.non_pawn_material(us) == BishopValueMg
|
||||
&& pos.count<BISHOP>(us) == 1
|
||||
&& pos.count<PAWN >(us) >= 1;
|
||||
}
|
||||
|
||||
bool is_KQKRPs(const Position& pos, Color us) {
|
||||
return !pos.count<PAWN>(us)
|
||||
&& pos.non_pawn_material(us) == QueenValueMg
|
||||
&& pos.count<QUEEN>(us) == 1
|
||||
&& pos.count<ROOK>(~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_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]
|
||||
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
|
||||
// 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
|
||||
return e;
|
||||
|
|
|
@ -53,7 +53,7 @@ struct HashTable {
|
|||
Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; }
|
||||
|
||||
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)
|
||||
std::swap(*cur, *std::max_element(cur, endMoves));
|
||||
|
||||
move = *cur++;
|
||||
if (*cur != ttMove && filter())
|
||||
return *cur++;
|
||||
|
||||
if (move != ttMove && filter())
|
||||
return move;
|
||||
cur++;
|
||||
}
|
||||
return move = MOVE_NONE;
|
||||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
/// MovePicker::next_move() is the most important method of the MovePicker class. It
|
||||
|
@ -174,10 +174,10 @@ top:
|
|||
|
||||
case GOOD_CAPTURE:
|
||||
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
|
||||
true : (*endBadCaptures++ = move, false); }))
|
||||
return move;
|
||||
true : (*endBadCaptures++ = *cur, false); }))
|
||||
return *(cur - 1);
|
||||
|
||||
// Prepare the pointers to loop over the refutations array
|
||||
cur = std::begin(refutations);
|
||||
|
@ -192,10 +192,10 @@ top:
|
|||
/* fallthrough */
|
||||
|
||||
case REFUTATION:
|
||||
if (select<Next>([&](){ return move != MOVE_NONE
|
||||
&& !pos.capture(move)
|
||||
&& pos.pseudo_legal(move); }))
|
||||
return move;
|
||||
if (select<Next>([&](){ return *cur != MOVE_NONE
|
||||
&& !pos.capture(*cur)
|
||||
&& pos.pseudo_legal(*cur); }))
|
||||
return *(cur - 1);
|
||||
++stage;
|
||||
/* fallthrough */
|
||||
|
||||
|
@ -210,10 +210,10 @@ top:
|
|||
|
||||
case QUIET:
|
||||
if ( !skipQuiets
|
||||
&& select<Next>([&](){return move != refutations[0]
|
||||
&& move != refutations[1]
|
||||
&& move != refutations[2];}))
|
||||
return move;
|
||||
&& select<Next>([&](){return *cur != refutations[0].move
|
||||
&& *cur != refutations[1].move
|
||||
&& *cur != refutations[2].move;}))
|
||||
return *(cur - 1);
|
||||
|
||||
// Prepare the pointers to loop over the bad captures
|
||||
cur = moves;
|
||||
|
@ -237,12 +237,12 @@ top:
|
|||
return select<Best>([](){ return true; });
|
||||
|
||||
case PROBCUT:
|
||||
return select<Best>([&](){ return pos.see_ge(move, threshold); });
|
||||
return select<Best>([&](){ return pos.see_ge(*cur, threshold); });
|
||||
|
||||
case QCAPTURE:
|
||||
if (select<Best>([&](){ return depth > DEPTH_QS_RECAPTURES
|
||||
|| to_sq(move) == recaptureSquare; }))
|
||||
return move;
|
||||
|| to_sq(*cur) == recaptureSquare; }))
|
||||
return *(cur - 1);
|
||||
|
||||
// If we did not find any move and we do not try checks, we have finished
|
||||
if (depth != DEPTH_QS_CHECKS)
|
||||
|
|
|
@ -142,7 +142,6 @@ private:
|
|||
Move ttMove;
|
||||
ExtMove refutations[3], *cur, *endMoves, *endBadCaptures;
|
||||
int stage;
|
||||
Move move;
|
||||
Square recaptureSquare;
|
||||
Value threshold;
|
||||
Depth depth;
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "bitboard.h"
|
||||
|
@ -36,8 +35,8 @@ namespace {
|
|||
constexpr Score Doubled = S(11, 56);
|
||||
constexpr Score Isolated = S( 5, 15);
|
||||
|
||||
// Connected pawn bonus by opposed, phalanx, #support and rank
|
||||
Score Connected[2][2][3][RANK_NB];
|
||||
// Connected pawn bonus
|
||||
constexpr int Connected[RANK_NB] = { 0, 13, 17, 24, 59, 96, 171 };
|
||||
|
||||
// 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.
|
||||
|
@ -78,11 +77,8 @@ namespace {
|
|||
Bitboard theirPawns = pos.pieces(Them, PAWN);
|
||||
|
||||
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0;
|
||||
e->semiopenFiles[Us] = 0xFF;
|
||||
e->kingSquares[Us] = SQ_NONE;
|
||||
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
|
||||
while ((s = *pl++) != SQ_NONE)
|
||||
|
@ -90,8 +86,8 @@ namespace {
|
|||
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
||||
|
||||
File f = file_of(s);
|
||||
Rank r = relative_rank(Us, s);
|
||||
|
||||
e->semiopenFiles[Us] &= ~(1 << f);
|
||||
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
|
||||
|
||||
// Flag the pawn
|
||||
|
@ -114,12 +110,11 @@ namespace {
|
|||
// which could become passed after one or two pawn pushes when are
|
||||
// not attacked more times than defended.
|
||||
if ( !(stoppers ^ lever ^ leverPush)
|
||||
&& popcount(support) >= popcount(lever) - 1
|
||||
&& (support || !more_than_one(lever))
|
||||
&& popcount(phalanx) >= popcount(leverPush))
|
||||
e->passedPawns[Us] |= s;
|
||||
|
||||
else if ( stoppers == SquareBB[s + Up]
|
||||
&& relative_rank(Us, s) >= RANK_5)
|
||||
else if (stoppers == square_bb(s + Up) && r >= RANK_5)
|
||||
{
|
||||
b = shift<Up>(support) & ~theirPawns;
|
||||
while (b)
|
||||
|
@ -129,8 +124,11 @@ namespace {
|
|||
|
||||
// Score this pawn
|
||||
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)
|
||||
score -= Isolated, e->weakUnopposed[Us] += !opposed;
|
||||
|
||||
|
@ -148,27 +146,6 @@ namespace {
|
|||
|
||||
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
|
||||
/// 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
|
||||
|
@ -185,8 +162,6 @@ Entry* probe(const Position& pos) {
|
|||
e->key = key;
|
||||
e->scores[WHITE] = evaluate<WHITE>(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;
|
||||
}
|
||||
|
@ -209,7 +184,7 @@ Value Entry::evaluate_shelter(const Position& pos, Square ksq) {
|
|||
Value safety = (shift<Down>(theirPawns) & (FileABB | FileHBB) & BlockRanks & ksq) ?
|
||||
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)
|
||||
{
|
||||
b = ourPawns & file_bb(f);
|
||||
|
@ -237,11 +212,15 @@ Score Entry::do_king_safety(const Position& pos) {
|
|||
Square ksq = pos.square<KING>(Us);
|
||||
kingSquares[Us] = ksq;
|
||||
castlingRights[Us] = pos.castling_rights(Us);
|
||||
int minKingPawnDistance = 0;
|
||||
|
||||
Bitboard pawns = pos.pieces(Us, PAWN);
|
||||
if (pawns)
|
||||
while (!(DistanceRingBB[ksq][++minKingPawnDistance] & pawns)) {}
|
||||
int minPawnDist = pawns ? 8 : 0;
|
||||
|
||||
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);
|
||||
|
||||
|
@ -252,7 +231,7 @@ Score Entry::do_king_safety(const Position& pos) {
|
|||
if (pos.can_castle(Us | QUEEN_SIDE))
|
||||
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
|
||||
|
|
13
src/pawns.h
13
src/pawns.h
|
@ -38,15 +38,7 @@ struct Entry {
|
|||
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
|
||||
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
|
||||
int weak_unopposed(Color c) const { return weakUnopposed[c]; }
|
||||
int pawn_asymmetry() const { return asymmetry; }
|
||||
|
||||
int 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)];
|
||||
}
|
||||
int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); };
|
||||
|
||||
template<Color Us>
|
||||
Score king_safety(const Position& pos) {
|
||||
|
@ -69,14 +61,11 @@ struct Entry {
|
|||
Score kingSafety[COLOR_NB];
|
||||
int weakUnopposed[COLOR_NB];
|
||||
int castlingRights[COLOR_NB];
|
||||
int semiopenFiles[COLOR_NB];
|
||||
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
|
||||
int asymmetry;
|
||||
};
|
||||
|
||||
typedef HashTable<Entry, 16384> Table;
|
||||
|
||||
void init();
|
||||
Entry* probe(const Position& pos);
|
||||
|
||||
} // namespace Pawns
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef> // For offsetof()
|
||||
#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 rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
|
||||
|
||||
for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); ++s)
|
||||
if (s != kfrom && s != 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;
|
||||
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
|
||||
& ~(square_bb(kfrom) | rfrom);
|
||||
}
|
||||
|
||||
|
||||
|
@ -629,7 +623,7 @@ bool Position::pseudo_legal(const Move m) const {
|
|||
{
|
||||
// We have already handled promotion moves, so destination
|
||||
// cannot be on the 8th/1st rank.
|
||||
if (rank_of(to) == relative_rank(us, RANK_8))
|
||||
if ((Rank8BB | Rank1BB) & to)
|
||||
return false;
|
||||
|
||||
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> const Square* squares(Color c) const;
|
||||
template<PieceType Pt> Square square(Color c) const;
|
||||
int semiopen_file(Color c, File f) const;
|
||||
|
||||
// Castling
|
||||
int castling_rights(Color c) const;
|
||||
|
@ -128,6 +129,7 @@ public:
|
|||
// Piece specific
|
||||
bool pawn_passed(Color c, Square s) const;
|
||||
bool opposite_bishops() const;
|
||||
int pawns_on_same_color_squares(Color c, Square s) const;
|
||||
|
||||
// Doing and undoing moves
|
||||
void do_move(Move m, StateInfo& newSt);
|
||||
|
@ -260,6 +262,10 @@ inline Square Position::ep_square() const {
|
|||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
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[]
|
||||
// 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[type_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/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring> // For std::memset
|
||||
|
@ -69,10 +68,10 @@ namespace {
|
|||
}
|
||||
|
||||
// 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -86,11 +85,10 @@ namespace {
|
|||
return d > 17 ? 0 : 29 * d * d + 138 * d - 134;
|
||||
}
|
||||
|
||||
// Add a small random component to draw evaluations to keep search dynamic
|
||||
// and to avoid 3fold-blindness.
|
||||
// Add a small random component to draw evaluations to avoid 3fold-blindness
|
||||
Value value_draw(Depth depth, Thread* thisThread) {
|
||||
return depth < 4 ? VALUE_DRAW
|
||||
: VALUE_DRAW + Value(2 * (thisThread->nodes.load(std::memory_order_relaxed) % 2) - 1);
|
||||
return depth < 4 * ONE_PLY ? VALUE_DRAW
|
||||
: VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1);
|
||||
}
|
||||
|
||||
// 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_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
|
||||
// to the given depth are generated and counted, and the sum is returned.
|
||||
template<bool Root>
|
||||
|
@ -157,7 +148,7 @@ namespace {
|
|||
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -171,12 +162,12 @@ void Search::clear() {
|
|||
Time.availableNodes = 0;
|
||||
TT.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
|
||||
/// the UCI 'go' command. It searches from the root position and outputs the "bestmove".
|
||||
/// MainThread::search() is started when the program receives the UCI 'go'
|
||||
/// command. It searches from the root position and outputs the "bestmove".
|
||||
|
||||
void MainThread::search() {
|
||||
|
||||
|
@ -203,8 +194,11 @@ void MainThread::search() {
|
|||
else
|
||||
{
|
||||
for (Thread* th : Threads)
|
||||
{
|
||||
th->bestMoveChanges = 0;
|
||||
if (th != this)
|
||||
th->start_searching();
|
||||
}
|
||||
|
||||
Thread::search(); // Let's start searching!
|
||||
}
|
||||
|
@ -235,8 +229,9 @@ void MainThread::search() {
|
|||
if (Limits.npmsec)
|
||||
Time.availableNodes += Limits.inc[us] - Cluster::nodes_searched();
|
||||
|
||||
// Check if there are threads with a better score than main thread
|
||||
Thread* bestThread = this;
|
||||
|
||||
// Check if there are threads with a better score than main thread
|
||||
if ( Options["MultiPV"] == 1
|
||||
&& !Limits.depth
|
||||
&& !Skill(Options["Skill Level"]).enabled()
|
||||
|
@ -310,9 +305,9 @@ void MainThread::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, ...),
|
||||
// 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.
|
||||
Stack stack[MAX_PLY+10], *ss = stack+7;
|
||||
Move pv[MAX_PLY+1];
|
||||
|
@ -320,7 +315,7 @@ void Thread::search() {
|
|||
Move lastBestMove = MOVE_NONE;
|
||||
Depth lastBestMoveDepth = DEPTH_ZERO;
|
||||
MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr);
|
||||
double timeReduction = 1.0;
|
||||
double timeReduction = 1, totBestMoveChanges = 0;
|
||||
Color us = rootPos.side_to_move();
|
||||
|
||||
std::memset(ss-7, 0, 10 * sizeof(Stack));
|
||||
|
@ -331,9 +326,6 @@ void Thread::search() {
|
|||
bestValue = delta = alpha = -VALUE_INFINITE;
|
||||
beta = VALUE_INFINITE;
|
||||
|
||||
if (mainThread)
|
||||
mainThread->bestMoveChanges = 0;
|
||||
|
||||
size_t multiPV = Options["MultiPV"];
|
||||
Skill skill(Options["Skill Level"]);
|
||||
|
||||
|
@ -354,7 +346,7 @@ void Thread::search() {
|
|||
: Options["Analysis Contempt"] == "Black" && us == WHITE ? -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)
|
||||
: -make_score(ct, ct / 2));
|
||||
|
||||
|
@ -365,7 +357,7 @@ void Thread::search() {
|
|||
{
|
||||
// Age out PV variability metric
|
||||
if (mainThread)
|
||||
mainThread->bestMoveChanges *= 0.517;
|
||||
totBestMoveChanges /= 2;
|
||||
|
||||
|
||||
// Save the last iteration's scores before first PV line is searched and
|
||||
|
@ -504,15 +496,20 @@ void Thread::search() {
|
|||
&& !Threads.stop
|
||||
&& !mainThread->stopOnPonderhit)
|
||||
{
|
||||
double fallingEval = (306 + 9 * (mainThread->previousScore - bestValue)) / 581.0;
|
||||
fallingEval = std::max(0.5, std::min(1.5, fallingEval));
|
||||
double fallingEval = (314 + 9 * (mainThread->previousScore - bestValue)) / 581.0;
|
||||
fallingEval = clamp(fallingEval, 0.5, 1.5);
|
||||
|
||||
// If the bestMove is stable over several iterations, reduce time accordingly
|
||||
timeReduction = lastBestMoveDepth + 10 * ONE_PLY < completedDepth ? 1.95 : 1.0;
|
||||
double reduction = std::pow(mainThread->previousTimeReduction, 0.528) / timeReduction;
|
||||
|
||||
// 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
|
||||
if ( rootMoves.size() == 1
|
||||
|
@ -578,7 +575,7 @@ namespace {
|
|||
Key posKey;
|
||||
Move ttMove, move, excludedMove, bestMove;
|
||||
Depth extension, newDepth;
|
||||
Value bestValue, value, ttValue, eval, maxValue, pureStaticEval;
|
||||
Value bestValue, value, ttValue, eval, maxValue;
|
||||
bool ttHit, ttPv, inCheck, givesCheck, improving;
|
||||
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture;
|
||||
Piece movedPiece;
|
||||
|
@ -624,8 +621,7 @@ namespace {
|
|||
assert(0 <= ss->ply && ss->ply < MAX_PLY);
|
||||
|
||||
(ss+1)->ply = ss->ply + 1;
|
||||
ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
|
||||
ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0];
|
||||
(ss+1)->excludedMove = bestMove = MOVE_NONE;
|
||||
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
|
||||
Square prevSq = to_sq((ss-1)->currentMove);
|
||||
|
||||
|
@ -647,6 +643,15 @@ namespace {
|
|||
: ttHit ? tte->move() : MOVE_NONE;
|
||||
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
|
||||
if ( !PvNode
|
||||
&& ttHit
|
||||
|
@ -663,9 +668,8 @@ namespace {
|
|||
if (!pos.capture_or_promotion(ttMove))
|
||||
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
|
||||
if ( ((ss-1)->moveCount == 1 || (ss-1)->currentMove == (ss-1)->killers[0])
|
||||
&& !pos.captured_piece())
|
||||
// Extra penalty for early quiet moves of the previous ply
|
||||
if ((ss-1)->moveCount <= 2 && !pos.captured_piece())
|
||||
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY));
|
||||
}
|
||||
// Penalty for a quiet ttMove that fails low
|
||||
|
@ -734,16 +738,16 @@ namespace {
|
|||
// Step 6. Static evaluation of the position
|
||||
if (inCheck)
|
||||
{
|
||||
ss->staticEval = eval = pureStaticEval = VALUE_NONE;
|
||||
ss->staticEval = eval = VALUE_NONE;
|
||||
improving = false;
|
||||
goto moves_loop; // Skip early pruning when in check
|
||||
}
|
||||
else if (ttHit)
|
||||
{
|
||||
// Never assume anything on values stored in TT
|
||||
ss->staticEval = eval = pureStaticEval = tte->eval();
|
||||
ss->staticEval = eval = tte->eval();
|
||||
if (eval == VALUE_NONE)
|
||||
ss->staticEval = eval = pureStaticEval = evaluate(pos);
|
||||
ss->staticEval = eval = evaluate(pos);
|
||||
|
||||
// Can ttValue be used as a better position evaluation?
|
||||
if ( ttValue != VALUE_NONE
|
||||
|
@ -756,15 +760,14 @@ namespace {
|
|||
{
|
||||
int bonus = -(ss-1)->statScore / 512;
|
||||
|
||||
pureStaticEval = evaluate(pos);
|
||||
ss->staticEval = eval = pureStaticEval + bonus;
|
||||
ss->staticEval = eval = evaluate(pos) + bonus;
|
||||
}
|
||||
else
|
||||
ss->staticEval = eval = pureStaticEval = -(ss-1)->staticEval + 2 * Eval::Tempo;
|
||||
ss->staticEval = eval = -(ss-1)->staticEval + 2 * Eval::Tempo;
|
||||
|
||||
Cluster::save(thisThread, tte,
|
||||
posKey, VALUE_NONE, ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE,
|
||||
pureStaticEval);
|
||||
eval);
|
||||
}
|
||||
|
||||
// Step 7. Razoring (~2 Elo)
|
||||
|
@ -788,7 +791,7 @@ namespace {
|
|||
&& (ss-1)->currentMove != MOVE_NULL
|
||||
&& (ss-1)->statScore < 23200
|
||||
&& eval >= beta
|
||||
&& pureStaticEval >= beta - 36 * depth / ONE_PLY + 225
|
||||
&& ss->staticEval >= beta - 36 * depth / ONE_PLY + 225
|
||||
&& !excludedMove
|
||||
&& pos.non_pawn_material(us)
|
||||
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
|
||||
|
@ -820,7 +823,7 @@ namespace {
|
|||
|
||||
// Do verification search at high depths, with null move pruning disabled
|
||||
// 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;
|
||||
|
||||
Value v = search<NonPV>(pos, ss, beta-1, beta, depth-R, false);
|
||||
|
@ -871,8 +874,7 @@ namespace {
|
|||
}
|
||||
|
||||
// Step 11. Internal iterative deepening (~2 Elo)
|
||||
if ( depth >= 8 * ONE_PLY
|
||||
&& !ttMove)
|
||||
if (depth >= 8 * ONE_PLY && !ttMove)
|
||||
{
|
||||
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,
|
||||
nullptr, (ss-4)->continuationHistory,
|
||||
nullptr, (ss-6)->continuationHistory };
|
||||
|
||||
Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq];
|
||||
|
||||
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory,
|
||||
|
@ -893,8 +896,8 @@ moves_loop: // When in check, search starts from here
|
|||
contHist,
|
||||
countermove,
|
||||
ss->killers);
|
||||
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
|
||||
|
||||
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
|
||||
moveCountPruning = false;
|
||||
ttCapture = ttMove && pos.capture_or_promotion(ttMove);
|
||||
|
||||
|
@ -927,7 +930,7 @@ moves_loop: // When in check, search starts from here
|
|||
extension = DEPTH_ZERO;
|
||||
captureOrPromotion = pos.capture_or_promotion(move);
|
||||
movedPiece = pos.moved_piece(move);
|
||||
givesCheck = gives_check(pos, move);
|
||||
givesCheck = pos.gives_check(move);
|
||||
|
||||
// Step 13. Extensions (~70 Elo)
|
||||
|
||||
|
@ -947,8 +950,9 @@ moves_loop: // When in check, search starts from here
|
|||
&& pos.legal(move))
|
||||
{
|
||||
Value singularBeta = ttValue - 2 * depth / ONE_PLY;
|
||||
Depth halfDepth = depth / (2 * ONE_PLY) * ONE_PLY; // ONE_PLY invariant
|
||||
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;
|
||||
|
||||
if (value < singularBeta)
|
||||
|
@ -972,6 +976,20 @@ moves_loop: // When in check, search starts from here
|
|||
else if (type_of(move) == CASTLING)
|
||||
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
|
||||
newDepth = depth - ONE_PLY + extension;
|
||||
|
||||
|
@ -981,7 +999,7 @@ moves_loop: // When in check, search starts from here
|
|||
&& bestValue > VALUE_MATED_IN_MAX_PLY)
|
||||
{
|
||||
// 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
|
||||
&& !givesCheck
|
||||
|
@ -992,7 +1010,8 @@ moves_loop: // When in check, search starts from here
|
|||
continue;
|
||||
|
||||
// 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)
|
||||
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
|
||||
// iteration. This information is used for time management: When
|
||||
// the best move changes frequently, we allocate some more time.
|
||||
if (moveCount > 1 && thisThread == Threads.main())
|
||||
++static_cast<MainThread*>(thisThread)->bestMoveChanges;
|
||||
if (moveCount > 1)
|
||||
++thisThread->bestMoveChanges;
|
||||
}
|
||||
else
|
||||
// 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,
|
||||
bestValue >= beta ? BOUND_LOWER :
|
||||
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
|
||||
depth, bestMove, pureStaticEval);
|
||||
depth, bestMove, ss->staticEval);
|
||||
|
||||
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();
|
||||
(ss+1)->ply = ss->ply + 1;
|
||||
ss->currentMove = bestMove = MOVE_NONE;
|
||||
ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0];
|
||||
bestMove = MOVE_NONE;
|
||||
inCheck = pos.checkers();
|
||||
moveCount = 0;
|
||||
|
||||
|
@ -1355,7 +1373,7 @@ moves_loop: // When in check, search starts from here
|
|||
{
|
||||
assert(is_ok(move));
|
||||
|
||||
givesCheck = gives_check(pos, move);
|
||||
givesCheck = pos.gives_check(move);
|
||||
|
||||
moveCount++;
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm> // For std::count
|
||||
#include <cassert>
|
||||
|
||||
#include "movegen.h"
|
||||
|
@ -32,7 +31,7 @@ ThreadPool Threads; // Global object
|
|||
|
||||
|
||||
/// 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) {
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ public:
|
|||
size_t pvIdx, pvLast;
|
||||
int selDepth, nmpMinPly;
|
||||
Color nmpColor;
|
||||
std::atomic<uint64_t> nodes, tbHits, TTsaves;
|
||||
std::atomic<uint64_t> nodes, tbHits, TTsaves, bestMoveChanges;
|
||||
|
||||
Position rootPos;
|
||||
Search::RootMoves rootMoves;
|
||||
|
@ -93,7 +93,7 @@ struct MainThread : public Thread {
|
|||
void search() override;
|
||||
void check_time();
|
||||
|
||||
double bestMoveChanges, previousTimeReduction;
|
||||
double previousTimeReduction;
|
||||
Value previousScore;
|
||||
int callsCnt;
|
||||
bool stopOnPonderhit;
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
|
Loading…
Add table
Reference in a new issue