1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-05-01 09:13:08 +00:00

Merge branch 'master' into sf-nnue-update

This commit is contained in:
joergoster 2020-07-11 12:03:17 +02:00
commit db0615eed9
11 changed files with 177 additions and 134 deletions

View file

@ -75,9 +75,6 @@ Currently, Stockfish has the following UCI options:
Assume a time delay of x ms due to network and GUI overheads. This is useful to Assume a time delay of x ms due to network and GUI overheads. This is useful to
avoid losses on time in those cases. avoid losses on time in those cases.
* #### Minimum Thinking Time
Search for at least x ms per move.
* #### Slow Mover * #### Slow Mover
Lower values will make Stockfish take less time in games, higher values will Lower values will make Stockfish take less time in games, higher values will
make it think longer. make it think longer.

View file

@ -88,7 +88,7 @@ const vector<string> Defaults = {
// Chess 960 // Chess 960
"setoption name UCI_Chess960 value true", "setoption name UCI_Chess960 value true",
"bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w KQkq - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6", "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w HFhf - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6",
"setoption name UCI_Chess960 value false" "setoption name UCI_Chess960 value false"
}; };

View file

@ -124,12 +124,19 @@ inline Bitboard operator&(Square s, Bitboard b) { return b & s; }
inline Bitboard operator|(Square s, Bitboard b) { return b | s; } inline Bitboard operator|(Square s, Bitboard b) { return b | s; }
inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; } inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; }
inline Bitboard operator|(Square s, Square s2) { return square_bb(s) | s2; } inline Bitboard operator|(Square s1, Square s2) { return square_bb(s1) | s2; }
constexpr bool more_than_one(Bitboard b) { constexpr bool more_than_one(Bitboard b) {
return b & (b - 1); return b & (b - 1);
} }
/// Counts the occupation of the bitboard depending on the occupation of SQ_A1
/// as in `b & (1ULL << SQ_A1) ? more_than_two(b) : more_than_one(b)`
constexpr bool conditional_more_than_two(Bitboard b) {
return b & (b - 1) & (b - 2);
}
constexpr bool opposite_colors(Square s1, Square s2) { constexpr bool opposite_colors(Square s1, Square s2) {
return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1; return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1;
} }
@ -138,19 +145,19 @@ constexpr bool opposite_colors(Square s1, Square s2) {
/// rank_bb() and file_bb() return a bitboard representing all the squares on /// rank_bb() and file_bb() return a bitboard representing all the squares on
/// the given file or rank. /// the given file or rank.
inline Bitboard rank_bb(Rank r) { constexpr Bitboard rank_bb(Rank r) {
return Rank1BB << (8 * r); return Rank1BB << (8 * r);
} }
inline Bitboard rank_bb(Square s) { constexpr Bitboard rank_bb(Square s) {
return rank_bb(rank_of(s)); return rank_bb(rank_of(s));
} }
inline Bitboard file_bb(File f) { constexpr Bitboard file_bb(File f) {
return FileABB << f; return FileABB << f;
} }
inline Bitboard file_bb(Square s) { constexpr Bitboard file_bb(Square s) {
return file_bb(file_of(s)); return file_bb(file_of(s));
} }
@ -195,16 +202,16 @@ constexpr Bitboard pawn_double_attacks_bb(Bitboard b) {
/// adjacent_files_bb() returns a bitboard representing all the squares on the /// adjacent_files_bb() returns a bitboard representing all the squares on the
/// adjacent files of the given one. /// adjacent files of a given square.
inline Bitboard adjacent_files_bb(Square s) { constexpr Bitboard adjacent_files_bb(Square s) {
return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s)); return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
} }
/// line_bb(Square, Square) returns a bitboard representing an entire line, /// line_bb() returns a bitboard representing an entire line (from board edge
/// from board edge to board edge, that intersects the given squares. If the /// to board edge) that intersects the two given squares. If the given squares
/// given squares are not on a same file/rank/diagonal, returns 0. For instance, /// are not on a same file/rank/diagonal, the function returns 0. For instance,
/// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal. /// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal.
inline Bitboard line_bb(Square s1, Square s2) { inline Bitboard line_bb(Square s1, Square s2) {
@ -215,8 +222,8 @@ inline Bitboard line_bb(Square s1, Square s2) {
/// between_bb() returns a bitboard representing squares that are linearly /// between_bb() returns a bitboard representing squares that are linearly
/// between the given squares (excluding the given squares). If the given /// between the two given squares (excluding the given squares). If the given
/// squares are not on a same file/rank/diagonal, return 0. For instance, /// squares are not on a same file/rank/diagonal, we return 0. For instance,
/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5 and E6. /// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5 and E6.
inline Bitboard between_bb(Square s1, Square s2) { inline Bitboard between_bb(Square s1, Square s2) {
@ -229,7 +236,7 @@ inline Bitboard between_bb(Square s1, Square s2) {
/// in front of the given one, from the point of view of the given color. For instance, /// in front of the given one, from the point of view of the given color. For instance,
/// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2. /// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2.
inline Bitboard forward_ranks_bb(Color c, Square s) { constexpr Bitboard forward_ranks_bb(Color c, Square s) {
return c == WHITE ? ~Rank1BB << 8 * relative_rank(WHITE, s) return c == WHITE ? ~Rank1BB << 8 * relative_rank(WHITE, s)
: ~Rank8BB >> 8 * relative_rank(BLACK, s); : ~Rank8BB >> 8 * relative_rank(BLACK, s);
} }
@ -238,7 +245,7 @@ inline Bitboard forward_ranks_bb(Color c, Square s) {
/// forward_file_bb() returns a bitboard representing all the squares along the /// forward_file_bb() returns a bitboard representing all the squares along the
/// line in front of the given one, from the point of view of the given color. /// line in front of the given one, from the point of view of the given color.
inline Bitboard forward_file_bb(Color c, Square s) { constexpr Bitboard forward_file_bb(Color c, Square s) {
return forward_ranks_bb(c, s) & file_bb(s); return forward_ranks_bb(c, s) & file_bb(s);
} }
@ -247,7 +254,7 @@ inline Bitboard forward_file_bb(Color c, Square s) {
/// be attacked by a pawn of the given color when it moves along its file, starting /// be attacked by a pawn of the given color when it moves along its file, starting
/// from the given square. /// from the given square.
inline Bitboard pawn_attack_span(Color c, Square s) { constexpr Bitboard pawn_attack_span(Color c, Square s) {
return forward_ranks_bb(c, s) & adjacent_files_bb(s); return forward_ranks_bb(c, s) & adjacent_files_bb(s);
} }
@ -255,7 +262,7 @@ inline Bitboard pawn_attack_span(Color c, Square s) {
/// passed_pawn_span() returns a bitboard which can be used to test if a pawn of /// passed_pawn_span() returns a bitboard which can be used to test if a pawn of
/// the given color and on the given square is a passed pawn. /// the given color and on the given square is a passed pawn.
inline Bitboard passed_pawn_span(Color c, Square s) { constexpr Bitboard passed_pawn_span(Color c, Square s) {
return pawn_attack_span(c, s) | forward_file_bb(c, s); return pawn_attack_span(c, s) | forward_file_bb(c, s);
} }

View file

@ -181,15 +181,15 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, strongSide, RookValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Square strongKing = relative_square(strongSide, pos.square<KING>(strongSide)); Square strongKing = pos.square<KING>(strongSide);
Square weakKing = relative_square(strongSide, pos.square<KING>(weakSide)); Square weakKing = pos.square<KING>(weakSide);
Square strongRook = relative_square(strongSide, pos.square<ROOK>(strongSide)); Square strongRook = pos.square<ROOK>(strongSide);
Square weakPawn = relative_square(strongSide, pos.square<PAWN>(weakSide)); Square weakPawn = pos.square<PAWN>(weakSide);
Square queeningSquare = make_square(file_of(weakPawn), RANK_1); Square queeningSquare = make_square(file_of(weakPawn), relative_rank(weakSide, RANK_8));
Value result; Value result;
// If the stronger side's king is in front of the pawn, it's a win // If the stronger side's king is in front of the pawn, it's a win
if (forward_file_bb(WHITE, strongKing) & weakPawn) if (forward_file_bb(strongSide, strongKing) & weakPawn)
result = RookValueEg - distance(strongKing, weakPawn); result = RookValueEg - distance(strongKing, weakPawn);
// If the weaker side's king is too far from the pawn and the rook, // If the weaker side's king is too far from the pawn and the rook,
@ -200,15 +200,15 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
// If the pawn is far advanced and supported by the defending king, // If the pawn is far advanced and supported by the defending king,
// the position is drawish // the position is drawish
else if ( rank_of(weakKing) <= RANK_3 else if ( relative_rank(strongSide, weakKing) <= RANK_3
&& distance(weakKing, weakPawn) == 1 && distance(weakKing, weakPawn) == 1
&& rank_of(strongKing) >= RANK_4 && relative_rank(strongSide, strongKing) >= RANK_4
&& distance(strongKing, weakPawn) > 2 + (pos.side_to_move() == strongSide)) && distance(strongKing, weakPawn) > 2 + (pos.side_to_move() == strongSide))
result = Value(80) - 8 * distance(strongKing, weakPawn); result = Value(80) - 8 * distance(strongKing, weakPawn);
else else
result = Value(200) - 8 * ( distance(strongKing, weakPawn + SOUTH) result = Value(200) - 8 * ( distance(strongKing, weakPawn + pawn_push(weakSide))
- distance(weakKing, weakPawn + SOUTH) - distance(weakKing, weakPawn + pawn_push(weakSide))
- distance(weakPawn, queeningSquare)); - distance(weakPawn, queeningSquare));
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;

View file

@ -82,11 +82,11 @@ namespace {
// KingAttackWeights[PieceType] contains king attack weights by piece type // KingAttackWeights[PieceType] contains king attack weights by piece type
constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
// Penalties for enemy's safe checks // SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type,
constexpr int QueenSafeCheck = 772; // higher if multiple safe checks are possible for that piece type.
constexpr int RookSafeCheck = 1084; constexpr int SafeCheck[][2] = {
constexpr int BishopSafeCheck = 645; {}, {}, {792, 1283}, {645, 967}, {1084, 1897}, {772, 1119}
constexpr int KnightSafeCheck = 792; };
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
@ -108,6 +108,18 @@ namespace {
S(110,182), S(114,182), S(114,192), S(116,219) } S(110,182), S(114,182), S(114,192), S(116,219) }
}; };
// KingProtector[knight/bishop] contains penalty for each distance unit to own king
constexpr Score KingProtector[] = { S(8, 9), S(6, 9) };
// Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a
// pawn protected square on rank 4 to 6 which is also safe from a pawn attack.
constexpr Score Outpost[] = { S(56, 36), S(30, 23) };
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
constexpr Score PassedRank[RANK_NB] = {
S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
};
// RookOnFile[semiopen/open] contains bonuses for each rook when there is // RookOnFile[semiopen/open] contains bonuses for each rook when there is
// no (friendly) pawn on the rook file. // no (friendly) pawn on the rook file.
constexpr Score RookOnFile[] = { S(19, 7), S(48, 29) }; constexpr Score RookOnFile[] = { S(19, 7), S(48, 29) };
@ -123,23 +135,15 @@ namespace {
S(0, 0), S(3, 46), S(37, 68), S(42, 60), S(0, 38), S(58, 41) S(0, 0), S(3, 46), S(37, 68), S(42, 60), S(0, 38), S(58, 41)
}; };
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
constexpr Score PassedRank[RANK_NB] = {
S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
};
// Assorted bonuses and penalties // Assorted bonuses and penalties
constexpr Score BishopKingProtector = S( 6, 9); constexpr Score BadOutpost = S( -7, 36);
constexpr Score BishopOnKingRing = S( 24, 0); constexpr Score BishopOnKingRing = S( 24, 0);
constexpr Score BishopOutpost = S( 30, 23);
constexpr Score BishopPawns = S( 3, 7); constexpr Score BishopPawns = S( 3, 7);
constexpr Score BishopXRayPawns = S( 4, 5); constexpr Score BishopXRayPawns = S( 4, 5);
constexpr Score CorneredBishop = S( 50, 50); constexpr Score CorneredBishop = S( 50, 50);
constexpr Score FlankAttacks = S( 8, 0); constexpr Score FlankAttacks = S( 8, 0);
constexpr Score Hanging = S( 69, 36); constexpr Score Hanging = S( 69, 36);
constexpr Score KnightKingProtector = S( 8, 9);
constexpr Score KnightOnQueen = S( 16, 11); constexpr Score KnightOnQueen = S( 16, 11);
constexpr Score KnightOutpost = S( 56, 36);
constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score LongDiagonalBishop = S( 45, 0);
constexpr Score MinorBehindPawn = S( 18, 3); constexpr Score MinorBehindPawn = S( 18, 3);
constexpr Score PassedFile = S( 11, 8); constexpr Score PassedFile = S( 11, 8);
@ -309,8 +313,14 @@ namespace {
{ {
// Bonus if piece is on an outpost square or can reach one // Bonus if piece is on an outpost square or can reach one
bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them); bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them);
if (bb & s) if ( Pt == KNIGHT
score += (Pt == KNIGHT) ? KnightOutpost : BishopOutpost; && bb & s & ~CenterFiles
&& !(b & pos.pieces(Them) & ~pos.pieces(PAWN))
&& !conditional_more_than_two(
pos.pieces(Them) & ~pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide)))
score += BadOutpost;
else if (bb & s)
score += Outpost[Pt == BISHOP];
else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
score += ReachableOutpost; score += ReachableOutpost;
@ -319,8 +329,7 @@ namespace {
score += MinorBehindPawn; score += MinorBehindPawn;
// Penalty if the piece is far from the king // Penalty if the piece is far from the king
score -= (Pt == KNIGHT ? KnightKingProtector score -= KingProtector[Pt == BISHOP] * distance(pos.square<KING>(Us), s);
: BishopKingProtector) * distance(pos.square<KING>(Us), s);
if (Pt == BISHOP) if (Pt == BISHOP)
{ {
@ -422,41 +431,33 @@ namespace {
b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)); b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
// Enemy rooks checks // Enemy rooks checks
rookChecks = b1 & safe & attackedBy[Them][ROOK]; rookChecks = b1 & attackedBy[Them][ROOK] & safe;
if (rookChecks) if (rookChecks)
kingDanger += more_than_one(rookChecks) ? RookSafeCheck * 175/100 kingDanger += SafeCheck[ROOK][more_than_one(rookChecks)];
: RookSafeCheck;
else else
unsafeChecks |= b1 & attackedBy[Them][ROOK]; unsafeChecks |= b1 & attackedBy[Them][ROOK];
// Enemy queen safe checks: we count them only if they are from squares from // Enemy queen safe checks: count them only if the checks are from squares from
// which we can't give a rook check, because rook checks are more valuable. // which opponent cannot give a rook check, because rook checks are more valuable.
queenChecks = (b1 | b2) queenChecks = (b1 | b2) & attackedBy[Them][QUEEN] & safe
& attackedBy[Them][QUEEN] & ~(attackedBy[Us][QUEEN] | rookChecks);
& safe
& ~attackedBy[Us][QUEEN]
& ~rookChecks;
if (queenChecks) if (queenChecks)
kingDanger += more_than_one(queenChecks) ? QueenSafeCheck * 145/100 kingDanger += SafeCheck[QUEEN][more_than_one(queenChecks)];
: QueenSafeCheck;
// Enemy bishops checks: we count them only if they are from squares from // Enemy bishops checks: count them only if they are from squares from which
// which we can't give a queen check, because queen checks are more valuable. // opponent cannot give a queen check, because queen checks are more valuable.
bishopChecks = b2 bishopChecks = b2 & attackedBy[Them][BISHOP] & safe
& attackedBy[Them][BISHOP]
& safe
& ~queenChecks; & ~queenChecks;
if (bishopChecks) if (bishopChecks)
kingDanger += more_than_one(bishopChecks) ? BishopSafeCheck * 3/2 kingDanger += SafeCheck[BISHOP][more_than_one(bishopChecks)];
: BishopSafeCheck;
else else
unsafeChecks |= b2 & attackedBy[Them][BISHOP]; unsafeChecks |= b2 & attackedBy[Them][BISHOP];
// Enemy knights checks // Enemy knights checks
knightChecks = attacks_bb<KNIGHT>(ksq) & attackedBy[Them][KNIGHT]; knightChecks = attacks_bb<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
if (knightChecks & safe) if (knightChecks & safe)
kingDanger += more_than_one(knightChecks & safe) ? KnightSafeCheck * 162/100 kingDanger += SafeCheck[KNIGHT][more_than_one(knightChecks & safe)];
: KnightSafeCheck;
else else
unsafeChecks |= knightChecks; unsafeChecks |= knightChecks;
@ -727,9 +728,9 @@ namespace {
} }
// Evaluation::winnable() adjusts the mg and eg score components based on the // Evaluation::winnable() adjusts the midgame and endgame score components, based on
// known attacking/defending status of the players. A single value is derived // the known attacking/defending status of the players. The final value is derived
// by interpolation from the mg and eg values and returned. // by interpolation from the midgame and endgame values.
template<Tracing T> template<Tracing T>
Value Evaluation<T>::winnable(Score score) const { Value Evaluation<T>::winnable(Score score) const {
@ -769,11 +770,10 @@ namespace {
eg += v; eg += v;
// Compute the scale factor for the winning side // Compute the scale factor for the winning side
Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK; Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
int sf = me->scale_factor(pos, strongSide); int sf = me->scale_factor(pos, strongSide);
// If scale is not already specific, scale down the endgame via general heuristics // If scale factor is not already specific, scale down via general heuristics
if (sf == SCALE_FACTOR_NORMAL) if (sf == SCALE_FACTOR_NORMAL)
{ {
if (pos.opposite_bishops()) if (pos.opposite_bishops())
@ -784,6 +784,15 @@ namespace {
else else
sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide); sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide);
} }
else if ( pos.non_pawn_material(WHITE) == RookValueMg
&& pos.non_pawn_material(BLACK) == RookValueMg
&& pos.count<PAWN>(strongSide) - pos.count<PAWN>(~strongSide) <= 1
&& bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN))
&& (attackedBy[~strongSide][KING] & pos.pieces(~strongSide, PAWN)))
sf = 36;
else if (pos.count<QUEEN>() == 1)
sf = 37 + 3 * (pos.count<QUEEN>(WHITE) == 1 ? pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK)
: pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE));
else else
sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide)); sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide));
} }

View file

@ -29,22 +29,20 @@ namespace {
ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{
*moveList++ = make<PROMOTION>(to - D, to, QUEEN); *moveList++ = make<PROMOTION>(to - D, to, QUEEN);
if (attacks_bb<KNIGHT>(to) & ksq)
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
}
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
{ {
*moveList++ = make<PROMOTION>(to - D, to, ROOK); *moveList++ = make<PROMOTION>(to - D, to, ROOK);
*moveList++ = make<PROMOTION>(to - D, to, BISHOP); *moveList++ = make<PROMOTION>(to - D, to, BISHOP);
if (!(attacks_bb<KNIGHT>(to) & ksq))
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT); *moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
} }
// Knight promotion is the only promotion that can give a direct check
// that's not already included in the queen promotion.
if (Type == QUIET_CHECKS && (attacks_bb<KNIGHT>(to) & ksq))
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
else
(void)ksq; // Silence a warning under MSVC
return moveList; return moveList;
} }
@ -263,8 +261,8 @@ namespace {
} // namespace } // namespace
/// <CAPTURES> Generates all pseudo-legal captures and queen promotions /// <CAPTURES> Generates all pseudo-legal captures plus queen and checking knight promotions
/// <QUIETS> Generates all pseudo-legal non-captures and underpromotions /// <QUIETS> Generates all pseudo-legal non-captures and underpromotions(except checking knight)
/// <NON_EVASIONS> Generates all pseudo-legal captures and non-captures /// <NON_EVASIONS> Generates all pseudo-legal captures and non-captures
/// ///
/// Returns a pointer to the end of the move list. /// Returns a pointer to the end of the move list.
@ -287,8 +285,8 @@ template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*); template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight /// generate<QUIET_CHECKS> generates all pseudo-legal non-captures.
/// underpromotions that give check. Returns a pointer to the end of the move list. /// Returns a pointer to the end of the move list.
template<> template<>
ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) { ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {

View file

@ -38,7 +38,12 @@ namespace {
constexpr Score WeakLever = S( 0, 56); constexpr Score WeakLever = S( 0, 56);
constexpr Score WeakUnopposed = S(13, 27); constexpr Score WeakUnopposed = S(13, 27);
constexpr Score BlockedStorm[RANK_NB] = {S( 0, 0), S( 0, 0), S( 76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2)}; // Bonus for blocked pawns at 5th or 6th rank
constexpr Score BlockedPawn[2] = { S(-11, -4), S(-3, 4) };
constexpr Score BlockedStorm[RANK_NB] = {
S(0, 0), S(0, 0), S(76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2)
};
// Connected pawn bonus // Connected pawn bonus
constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 };
@ -143,7 +148,7 @@ namespace {
// Score this pawn // Score this pawn
if (support | phalanx) if (support | phalanx)
{ {
int v = Connected[r] * (4 + 2 * bool(phalanx) - 2 * bool(opposed) - bool(blocked)) / 2 int v = Connected[r] * (2 + bool(phalanx) - bool(opposed))
+ 21 * popcount(support); + 21 * popcount(support);
score += make_score(v, v * (r - 2) / 4); score += make_score(v, v * (r - 2) / 4);
@ -167,6 +172,9 @@ namespace {
if (!support) if (!support)
score -= Doubled * doubled score -= Doubled * doubled
+ WeakLever * more_than_one(lever); + WeakLever * more_than_one(lever);
if (blocked && r > RANK_4)
score += BlockedPawn[r-4];
} }
return score; return score;

View file

@ -119,15 +119,7 @@ void Position::init() {
Zobrist::enpassant[f] = rng.rand<Key>(); Zobrist::enpassant[f] = rng.rand<Key>();
for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr) for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr)
{ Zobrist::castling[cr] = rng.rand<Key>();
Zobrist::castling[cr] = 0;
Bitboard b = cr;
while (b)
{
Key k = Zobrist::castling[1ULL << pop_lsb(&b)];
Zobrist::castling[cr] ^= k ? k : rng.rand<Key>();
}
}
Zobrist::side = rng.rand<Key>(); Zobrist::side = rng.rand<Key>();
Zobrist::noPawns = rng.rand<Key>(); Zobrist::noPawns = rng.rand<Key>();
@ -186,9 +178,9 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
4) En passant target square (in algebraic notation). If there's no en passant 4) En passant target square (in algebraic notation). If there's no en passant
target square, this is "-". If a pawn has just made a 2-square move, this target square, this is "-". If a pawn has just made a 2-square move, this
is the position "behind" the pawn. This is recorded only if there is a pawn is the position "behind" the pawn. Following X-FEN standard, this is recorded only
in position to make an en passant capture, and if there really is a pawn if there is a pawn in position to make an en passant capture, and if there really
that might have advanced two squares. is a pawn that might have advanced two squares.
5) Halfmove clock. This is the number of halfmoves since the last pawn advance 5) Halfmove clock. This is the number of halfmoves since the last pawn advance
or capture. This is used to determine if a draw can be claimed under the or capture. This is used to determine if a draw can be claimed under the
@ -278,17 +270,25 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
set_castling_right(c, rsq); set_castling_right(c, rsq);
} }
// 4. En passant square. Ignore if no pawn capture is possible // 4. En passant square.
// Ignore if square is invalid or not on side to move relative rank 6.
bool enpassant = false;
if ( ((ss >> col) && (col >= 'a' && col <= 'h')) if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
&& ((ss >> row) && (row == '3' || row == '6'))) && ((ss >> row) && (row == (sideToMove == WHITE ? '6' : '3'))))
{ {
st->epSquare = make_square(File(col - 'a'), Rank(row - '1')); st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN)) // En passant square will be considered only if
|| !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))) // a) side to move have a pawn threatening epSquare
st->epSquare = SQ_NONE; // b) there is an enemy pawn in front of epSquare
// c) there is no piece on epSquare or behind epSquare
enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
&& (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
&& !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
} }
else
if (!enpassant)
st->epSquare = SQ_NONE; st->epSquare = SQ_NONE;
// 5-6. Halfmove clock and fullmove number // 5-6. Halfmove clock and fullmove number
@ -851,9 +851,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// Update castling rights if needed // Update castling rights if needed
if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to])) if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to]))
{ {
int cr = castlingRightsMask[from] | castlingRightsMask[to]; k ^= Zobrist::castling[st->castlingRights];
k ^= Zobrist::castling[st->castlingRights & cr]; st->castlingRights &= ~(castlingRightsMask[from] | castlingRightsMask[to]);
st->castlingRights &= ~cr; k ^= Zobrist::castling[st->castlingRights];
} }
// Move the piece. The tricky Chess960 castling is handled earlier // Move the piece. The tricky Chess960 castling is handled earlier

View file

@ -92,7 +92,7 @@ constexpr Score PBonus[RANK_NB][FILE_NB] =
{ S( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) }, { S( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) },
{ S( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) }, { S( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) },
{ S( -4, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S( -8, -9) }, { S( -4, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S( -8, -9) },
{ S( 13, 9), S( 0, 4), S(-13, 3), S( 1,-12), S( 11,-12), S( -2, -6), S(-13, 13), S( 5, 8) }, { S( 13, 10), S( 0, 5), S(-13, 4), S( 1, -5), S( 11, -5), S( -2, -5), S(-13, 14), S( 5, 9) },
{ S( 5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S( -8, 13) }, { S( 5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S( -8, 13) },
{ S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) } { S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) }
}; };

View file

@ -263,10 +263,10 @@ void MainThread::search() {
Thread* bestThread = this; Thread* bestThread = this;
if (int(Options["MultiPV"]) == 1 && if ( int(Options["MultiPV"]) == 1
!Limits.depth && && !Limits.depth
!(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) && && !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"]))
rootMoves[0].pv[0] != MOVE_NONE) && rootMoves[0].pv[0] != MOVE_NONE)
bestThread = Threads.get_best_thread(); bestThread = Threads.get_best_thread();
bestPreviousScore = bestThread->rootMoves[0].score; bestPreviousScore = bestThread->rootMoves[0].score;
@ -596,7 +596,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; Value bestValue, value, ttValue, eval, maxValue, probcutBeta;
bool ttHit, ttPv, formerPv, givesCheck, improving, didLMR, priorCapture; bool ttHit, ttPv, formerPv, givesCheck, improving, didLMR, priorCapture;
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, bool captureOrPromotion, doFullDepthSearch, moveCountPruning,
ttCapture, singularQuietLMR; ttCapture, singularQuietLMR;
@ -662,7 +662,7 @@ namespace {
// search to overwrite a previous full search TT value, so we use a different // search to overwrite a previous full search TT value, so we use a different
// position key in case of an excluded move. // position key in case of an excluded move.
excludedMove = ss->excludedMove; excludedMove = ss->excludedMove;
posKey = pos.key() ^ (Key(excludedMove) << 48); // Isn't a very good hash posKey = excludedMove == MOVE_NONE ? pos.key() : pos.key() ^ make_key(excludedMove);
tte = TT.probe(posKey, ttHit); tte = TT.probe(posKey, ttHit);
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
@ -670,7 +670,11 @@ namespace {
ttPv = PvNode || (ttHit && tte->is_pv()); ttPv = PvNode || (ttHit && tte->is_pv());
formerPv = ttPv && !PvNode; formerPv = ttPv && !PvNode;
if (ttPv && depth > 12 && ss->ply - 1 < MAX_LPH && !priorCapture && is_ok((ss-1)->currentMove)) if ( ttPv
&& depth > 12
&& ss->ply - 1 < MAX_LPH
&& !priorCapture
&& is_ok((ss-1)->currentMove))
thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5);
// thisThread->ttHitAverage can be used to approximate the running average of ttHit // thisThread->ttHitAverage can be used to approximate the running average of ttHit
@ -867,23 +871,33 @@ namespace {
} }
} }
probcutBeta = beta + 176 - 49 * improving;
// Step 10. ProbCut (~10 Elo) // Step 10. ProbCut (~10 Elo)
// If we have a good enough capture and a reduced search returns a value // If we have a good enough capture and a reduced search returns a value
// much above beta, we can (almost) safely prune the previous move. // much above beta, we can (almost) safely prune the previous move.
if ( !PvNode if ( !PvNode
&& depth > 4 && depth > 4
&& abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY
&& !( ttHit
&& tte->depth() >= depth - 3
&& ttValue != VALUE_NONE
&& ttValue < probcutBeta))
{ {
Value raisedBeta = beta + 176 - 49 * improving; if ( ttHit
assert(raisedBeta < VALUE_INFINITE); && tte->depth() >= depth - 3
MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory); && ttValue != VALUE_NONE
&& ttValue >= probcutBeta
&& ttMove
&& pos.capture_or_promotion(ttMove))
return probcutBeta;
assert(probcutBeta < VALUE_INFINITE);
MovePicker mp(pos, ttMove, probcutBeta - ss->staticEval, &captureHistory);
int probCutCount = 0; int probCutCount = 0;
while ( (move = mp.next_move()) != MOVE_NONE while ( (move = mp.next_move()) != MOVE_NONE
&& probCutCount < 2 + 2 * cutNode && probCutCount < 2 + 2 * cutNode)
&& !( move == ttMove
&& tte->depth() >= depth - 4
&& ttValue < raisedBeta))
if (move != excludedMove && pos.legal(move)) if (move != excludedMove && pos.legal(move))
{ {
assert(pos.capture_or_promotion(move)); assert(pos.capture_or_promotion(move));
@ -901,18 +915,23 @@ namespace {
pos.do_move(move, st); pos.do_move(move, st);
// Perform a preliminary qsearch to verify that the move holds // Perform a preliminary qsearch to verify that the move holds
value = -qsearch<NonPV>(pos, ss+1, -raisedBeta, -raisedBeta+1); value = -qsearch<NonPV>(pos, ss+1, -probcutBeta, -probcutBeta+1);
// If the qsearch held, perform the regular search // If the qsearch held, perform the regular search
if (value >= raisedBeta) if (value >= probcutBeta)
value = -search<NonPV>(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4, !cutNode); value = -search<NonPV>(pos, ss+1, -probcutBeta, -probcutBeta+1, depth - 4, !cutNode);
pos.undo_move(move); pos.undo_move(move);
if (value >= raisedBeta) if (value >= probcutBeta)
{
tte->save(posKey, value_to_tt(value, ss->ply), ttPv,
BOUND_LOWER,
depth - 3, move, ss->staticEval);
return value; return value;
} }
} }
}
// Step 11. Internal iterative deepening (~1 Elo) // Step 11. Internal iterative deepening (~1 Elo)
if (depth >= 7 && !ttMove) if (depth >= 7 && !ttMove)
@ -1486,8 +1505,8 @@ moves_loop: // When in check, search starts from here
// Initialize a MovePicker object for the current position, and prepare // Initialize a MovePicker object for the current position, and prepare
// to search the moves. Because the depth is <= 0 here, only captures, // to search the moves. Because the depth is <= 0 here, only captures,
// queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will // queen and checking knight promotions, and other checks(only if depth >= DEPTH_QS_CHECKS)
// be generated. // will be generated.
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory,
&thisThread->captureHistory, &thisThread->captureHistory,
contHist, contHist,

View file

@ -501,6 +501,11 @@ inline PieceNumber& operator--(PieceNumber& d) { return d = PieceNumber(int8_t(d
constexpr bool is_ok(PieceNumber pn) { return pn < PIECE_NUMBER_NB; } constexpr bool is_ok(PieceNumber pn) { return pn < PIECE_NUMBER_NB; }
#endif // defined(EVAL_NNUE) || defined(EVAL_LEARN) #endif // defined(EVAL_NNUE) || defined(EVAL_LEARN)
/// Based on a congruential pseudo random number generator
constexpr Key make_key(uint64_t seed) {
return seed * 6364136223846793005ULL + 1442695040888963407ULL;
}
#endif // #ifndef TYPES_H_INCLUDED #endif // #ifndef TYPES_H_INCLUDED
#include "tune.h" // Global visibility to tuning setup #include "tune.h" // Global visibility to tuning setup