diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 8079b783..13735e3d 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -24,19 +24,13 @@ #include "misc.h" uint8_t PopCnt16[1 << 16]; -int SquareDistance[SQUARE_NB][SQUARE_NB]; +int8_t SquareDistance[SQUARE_NB][SQUARE_NB]; Bitboard SquareBB[SQUARE_NB]; -Bitboard FileBB[FILE_NB]; -Bitboard RankBB[RANK_NB]; -Bitboard AdjacentFilesBB[FILE_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 ForwardFileBB[COLOR_NB][SQUARE_NB]; -Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; -Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; @@ -91,25 +85,8 @@ void Bitboards::init() { for (Square s = SQ_A1; s <= SQ_H8; ++s) SquareBB[s] = (1ULL << s); - for (File f = FILE_A; f <= FILE_H; ++f) - FileBB[f] = f > FILE_A ? FileBB[f - 1] << 1 : FileABB; - - for (Rank r = RANK_1; r <= RANK_8; ++r) - RankBB[r] = r > RANK_1 ? RankBB[r - 1] << 8 : Rank1BB; - - for (File f = FILE_A; f <= FILE_H; ++f) - AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0); - for (Rank r = RANK_1; r < RANK_8; ++r) - ForwardRanksBB[WHITE][r] = ~(ForwardRanksBB[BLACK][r + 1] = ForwardRanksBB[BLACK][r] | RankBB[r]); - - for (Color c = WHITE; c <= BLACK; ++c) - for (Square s = SQ_A1; s <= SQ_H8; ++s) - { - ForwardFileBB [c][s] = ForwardRanksBB[c][rank_of(s)] & FileBB[file_of(s)]; - PawnAttackSpan[c][s] = ForwardRanksBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)]; - PassedPawnMask[c][s] = ForwardFileBB [c][s] | PawnAttackSpan[c][s]; - } + ForwardRanksBB[WHITE][r] = ~(ForwardRanksBB[BLACK][r + 1] = ForwardRanksBB[BLACK][r] | rank_bb(r)); for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) diff --git a/src/bitboard.h b/src/bitboard.h index f8440a23..cf52b39d 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -60,19 +60,13 @@ constexpr Bitboard Rank6BB = Rank1BB << (8 * 5); constexpr Bitboard Rank7BB = Rank1BB << (8 * 6); constexpr Bitboard Rank8BB = Rank1BB << (8 * 7); -extern int SquareDistance[SQUARE_NB][SQUARE_NB]; +extern int8_t SquareDistance[SQUARE_NB][SQUARE_NB]; extern Bitboard SquareBB[SQUARE_NB]; -extern Bitboard FileBB[FILE_NB]; -extern Bitboard RankBB[RANK_NB]; -extern Bitboard AdjacentFilesBB[FILE_NB]; extern Bitboard ForwardRanksBB[COLOR_NB][RANK_NB]; extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard DistanceRingBB[SQUARE_NB][8]; -extern Bitboard ForwardFileBB[COLOR_NB][SQUARE_NB]; -extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; -extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; @@ -143,23 +137,23 @@ inline bool opposite_colors(Square s1, Square s2) { /// the given file or rank. inline Bitboard rank_bb(Rank r) { - return RankBB[r]; + return Rank1BB << (8 * r); } inline Bitboard rank_bb(Square s) { - return RankBB[rank_of(s)]; + return rank_bb(rank_of(s)); } inline Bitboard file_bb(File f) { - return FileBB[f]; + return FileABB << f; } inline Bitboard file_bb(Square s) { - return FileBB[file_of(s)]; + return file_bb(file_of(s)); } -/// shift() moves a bitboard one step along direction D (mainly for pawns) +/// shift() moves a bitboard one step along direction D template constexpr Bitboard shift(Bitboard b) { @@ -171,8 +165,8 @@ constexpr Bitboard shift(Bitboard b) { } -/// pawn_attacks_bb() returns the pawn attacks for the given color from the -/// squares in the given bitboard. +/// pawn_attacks_bb() returns the squares attacked by pawns of the given color +/// from the squares in the given bitboard. template constexpr Bitboard pawn_attacks_bb(Bitboard b) { @@ -181,11 +175,11 @@ constexpr Bitboard pawn_attacks_bb(Bitboard b) { } -/// double_pawn_attacks_bb() returns the pawn attacks for the given color -/// from the squares in the given bitboard. +/// pawn_double_attacks_bb() returns the squares doubly attacked by pawns of the +/// given color from the squares in the given bitboard. template -constexpr Bitboard double_pawn_attacks_bb(Bitboard b) { +constexpr Bitboard pawn_double_attacks_bb(Bitboard b) { return C == WHITE ? shift(b) & shift(b) : shift(b) & shift(b); } @@ -195,10 +189,9 @@ constexpr Bitboard double_pawn_attacks_bb(Bitboard b) { /// adjacent files of the given one. inline Bitboard adjacent_files_bb(File f) { - return AdjacentFilesBB[f]; + return shift(file_bb(f)) | shift(file_bb(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 @@ -223,26 +216,24 @@ inline Bitboard forward_ranks_bb(Color c, Square s) { /// ForwardFileBB[c][s] = forward_ranks_bb(c, s) & file_bb(s) inline Bitboard forward_file_bb(Color c, Square s) { - return ForwardFileBB[c][s]; + return ForwardRanksBB[c][rank_of(s)] & file_bb(s); } /// pawn_attack_span() returns a bitboard representing all the squares that can be /// attacked by a pawn of the given color when it moves along its file, starting /// from the given square: -/// PawnAttackSpan[c][s] = forward_ranks_bb(c, s) & adjacent_files_bb(file_of(s)); inline Bitboard pawn_attack_span(Color c, Square s) { - return PawnAttackSpan[c][s]; + return forward_ranks_bb(c, s) & adjacent_files_bb(file_of(s)); } /// passed_pawn_mask() returns a bitboard mask which can be used to test if a /// pawn of the given color and on the given square is a passed pawn: -/// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_file_bb(c, s) inline Bitboard passed_pawn_mask(Color c, Square s) { - return PassedPawnMask[c][s]; + return pawn_attack_span(c, s) | forward_file_bb(c, s); } diff --git a/src/cluster.cpp b/src/cluster.cpp index ea287676..85a00a54 100644 --- a/src/cluster.cpp +++ b/src/cluster.cpp @@ -356,7 +356,7 @@ void save(Thread* thread, TTEntry* tte, bool found; TTEntry* replace_tte; replace_tte = TT.probe(e.first, found); - replace_tte->save(e.first, e.second.value(), e.second.pv_hit(), e.second.bound(), e.second.depth(), + replace_tte->save(e.first, e.second.value(), e.second.is_pv(), e.second.bound(), e.second.depth(), e.second.move(), e.second.eval()); } } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 725ea49c..98082c4c 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -93,8 +93,8 @@ namespace { // Penalties for enemy's safe checks constexpr int QueenSafeCheck = 780; - constexpr int RookSafeCheck = 880; - constexpr int BishopSafeCheck = 435; + constexpr int RookSafeCheck = 1080; + constexpr int BishopSafeCheck = 635; constexpr int KnightSafeCheck = 790; #define S(mg, eg) make_score(mg, eg) @@ -117,14 +117,6 @@ namespace { S(106,184), S(109,191), S(113,206), S(116,212) } }; - // Outpost[knight/bishop][supported by pawn] contains bonuses for minor - // pieces if they occupy or can reach an outpost square, bigger if that - // square is supported by a pawn. - constexpr Score Outpost[][2] = { - { S(22, 6), S(36,12) }, // Knight - { S( 9, 2), S(15, 5) } // Bishop - }; - // RookOnFile[semiopen/open] contains bonuses for each rook when there is // no (friendly) pawn on the rook file. constexpr Score RookOnFile[] = { S(18, 7), S(44, 20) }; @@ -153,8 +145,8 @@ namespace { // Assorted bonuses and penalties constexpr Score BishopPawns = S( 3, 7); - constexpr Score CloseEnemies = S( 8, 0); constexpr Score CorneredBishop = S( 50, 50); + constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); constexpr Score KingProtector = S( 7, 8); constexpr Score KnightOnQueen = S( 16, 12); @@ -168,9 +160,10 @@ namespace { constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score ThreatByRank = S( 13, 0); constexpr Score ThreatBySafePawn = S(173, 94); - constexpr Score TrappedRook = S( 96, 4); + constexpr Score TrappedRook = S( 47, 4); constexpr Score WeakQueen = S( 49, 15); constexpr Score WeakUnopposedPawn = S( 12, 23); + constexpr Score Outpost = S( 9, 3); #undef S @@ -245,33 +238,37 @@ namespace { constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB: Rank7BB | Rank6BB); + const Square ksq = pos.square(Us); + // Find our pawns that are blocked or on the first two ranks Bitboard b = pos.pieces(Us, PAWN) & (shift(pos.pieces()) | LowRanks); - // Squares occupied by those pawns, by our king or queen, or controlled by enemy pawns - // are excluded from the mobility area. + // Squares occupied by those pawns, by our king or queen or controlled by + // enemy pawns are excluded from the mobility area. mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them)); - // Initialise attackedBy bitboards for kings and pawns - attackedBy[Us][KING] = pos.attacks_from(pos.square(Us)); + // Initialize attackedBy[] for king and pawns + attackedBy[Us][KING] = pos.attacks_from(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]; // Init our king safety tables kingRing[Us] = attackedBy[Us][KING]; - if (relative_rank(Us, pos.square(Us)) == RANK_1) + if (relative_rank(Us, ksq) == RANK_1) kingRing[Us] |= shift(kingRing[Us]); - if (file_of(pos.square(Us)) == FILE_H) + if (file_of(ksq) == FILE_H) kingRing[Us] |= shift(kingRing[Us]); - else if (file_of(pos.square(Us)) == FILE_A) + else if (file_of(ksq) == FILE_A) kingRing[Us] |= shift(kingRing[Us]); kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); - kingRing[Us] &= ~double_pawn_attacks_bb(pos.pieces(Us, PAWN)); kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; + + // Remove from kingRing[] the squares defended by two pawns + kingRing[Us] &= ~pawn_double_attacks_bb(pos.pieces(Us, PAWN)); } @@ -286,12 +283,11 @@ namespace { const Square* pl = pos.squares(Us); Bitboard b, bb; - Square s; Score score = SCORE_ZERO; attackedBy[Us][Pt] = 0; - while ((s = *pl++) != SQ_NONE) + for (Square s = *pl; s != SQ_NONE; s = *++pl) { // Find attacked squares, including x-ray attacks for bishops and rooks b = Pt == BISHOP ? attacks_bb(s, pos.pieces() ^ pos.pieces(QUEEN)) @@ -321,10 +317,12 @@ namespace { // Bonus if piece is on an outpost square or can reach one bb = OutpostRanks & ~pe->pawn_attacks_span(Them); if (bb & s) - score += Outpost[Pt == BISHOP][bool(attackedBy[Us][PAWN] & s)] * 2; + score += Outpost * (Pt == KNIGHT ? 4 : 2) + * (1 + bool(attackedBy[Us][PAWN] & s)); else if (bb &= b & ~pos.pieces(Us)) - score += Outpost[Pt == BISHOP][bool(attackedBy[Us][PAWN] & bb)]; + score += Outpost * (Pt == KNIGHT ? 2 : 1) + * (1 + bool(attackedBy[Us][PAWN] & bb)); // Knight and Bishop bonus for being right behind a pawn if (shift(pos.pieces(PAWN)) & s) @@ -377,7 +375,7 @@ namespace { { File kf = file_of(pos.square(Us)); if ((kf < FILE_E) == (file_of(s) < kf)) - score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.castling_rights(Us)); + score -= TrappedRook * (1 + !pos.castling_rights(Us)); } } @@ -404,23 +402,12 @@ namespace { constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB); - const Square ksq = pos.square(Us); - Bitboard kingFlank, weak, b, b1, b2, safe, unsafeChecks; - - // King shelter and enemy pawns storm - Score score = pe->king_safety(pos); - - // Find the squares that opponent attacks in our king flank, and the squares - // which are attacked twice in that flank. - kingFlank = KingFlank[file_of(ksq)]; - b1 = attackedBy[Them][ALL_PIECES] & kingFlank & Camp; - b2 = b1 & attackedBy2[Them]; - - int tropism = popcount(b1) + popcount(b2); - - // Main king safety evaluation + Bitboard weak, b, b1, b2, safe, unsafeChecks = 0; int kingDanger = 0; - unsafeChecks = 0; + const Square ksq = pos.square(Us); + + // Init the score with king shelter and enemy pawns storm + Score score = pe->king_safety(pos); // Attacked squares defended at most once by our queen or king weak = attackedBy[Them][ALL_PIECES] @@ -434,27 +421,42 @@ namespace { b1 = attacks_bb(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)); b2 = attacks_bb(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)); - // Enemy queen safe checks - if ((b1 | b2) & attackedBy[Them][QUEEN] & safe & ~attackedBy[Us][QUEEN]) - kingDanger += QueenSafeCheck; - - b1 &= attackedBy[Them][ROOK]; - b2 &= attackedBy[Them][BISHOP]; - // Enemy rooks checks - if (b1 & safe) + Bitboard RookCheck = b1 + & safe + & attackedBy[Them][ROOK]; + + if (RookCheck) kingDanger += RookSafeCheck; else - unsafeChecks |= b1; + unsafeChecks |= b1 & attackedBy[Them][ROOK]; - // Enemy bishops checks - if (b2 & safe) + // Enemy queen safe checks: we count them only if they are from squares from + // which we can't give a rook check, because rook checks are more valuable. + Bitboard QueenCheck = (b1 | b2) + & attackedBy[Them][QUEEN] + & safe + & ~attackedBy[Us][QUEEN] + & ~RookCheck; + + if (QueenCheck) + kingDanger += QueenSafeCheck; + + // Enemy bishops checks: we count them only if they are from squares from + // which we can't give a queen check, because queen checks are more valuable. + Bitboard BishopCheck = b2 + & attackedBy[Them][BISHOP] + & safe + & ~QueenCheck; + + if (BishopCheck) kingDanger += BishopSafeCheck; else - unsafeChecks |= b2; + unsafeChecks |= b2 & attackedBy[Them][BISHOP]; // Enemy knights checks b = pos.attacks_from(ksq) & attackedBy[Them][KNIGHT]; + if (b & safe) kingDanger += KnightSafeCheck; else @@ -464,26 +466,34 @@ namespace { // the square is in the attacker's mobility area. unsafeChecks &= mobilityArea[Them]; + // Find the squares that opponent attacks in our king flank, and the squares + // which are attacked twice in that flank. + b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp; + b2 = b1 & attackedBy2[Them]; + + int kingFlankAttacks = popcount(b1) + popcount(b2); + kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] + 69 * kingAttacksCount[Them] + 185 * popcount(kingRing[Us] & weak) + - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) + 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks) - + tropism * tropism / 4 - 873 * !pos.count(Them) - 6 * mg_value(score) / 8 + mg_value(mobility[Them] - mobility[Us]) - - 30; + + 5 * kingFlankAttacks * kingFlankAttacks / 16 + - 25; // Transform the kingDanger units into a Score, and subtract it from the evaluation if (kingDanger > 0) score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16); // Penalty when our king is on a pawnless flank - if (!(pos.pieces(PAWN) & kingFlank)) + if (!(pos.pieces(PAWN) & KingFlank[file_of(ksq)])) score -= PawnlessFlank; - // King tropism bonus, to anticipate slow motion attacks on our king - score -= CloseEnemies * tropism; + // Penalty if king flank is under attack, potentially moving toward the king + score -= FlankAttacks * kingFlankAttacks; if (T) Trace::add(KING, Us, score); @@ -701,7 +711,8 @@ namespace { if (pos.non_pawn_material() < SpaceThreshold) return SCORE_ZERO; - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); + constexpr Color Them = (Us == WHITE ? BLACK : WHITE); + constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); constexpr Bitboard SpaceMask = Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB) : CenterFiles & (Rank7BB | Rank6BB | Rank5BB); @@ -713,8 +724,8 @@ namespace { // Find all squares which are at most three squares behind some friendly pawn Bitboard behind = pos.pieces(Us, PAWN); - behind |= (Us == WHITE ? behind >> 8 : behind << 8); - behind |= (Us == WHITE ? behind >> 16 : behind << 16); + behind |= shift(behind); + behind |= shift(shift(behind)); int bonus = popcount(safe) + popcount(behind & safe); int weight = pos.count(Us) @@ -743,12 +754,12 @@ namespace { && (pos.pieces(PAWN) & KingSide); // Compute the initiative bonus for the attacking side - int complexity = 8 * pe->pawn_asymmetry() - + 12 * pos.count() - + 12 * outflanking - + 16 * pawnsOnBothFlanks - + 48 * !pos.non_pawn_material() - -118 ; + int complexity = 9 * pe->pawn_asymmetry() + + 11 * pos.count() + + 9 * outflanking + + 18 * pawnsOnBothFlanks + + 49 * !pos.non_pawn_material() + -121 ; // 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 @@ -842,7 +853,7 @@ namespace { v = mg_value(score) * int(me->game_phase()) + eg_value(score) * int(PHASE_MIDGAME - me->game_phase()) * sf / SCALE_FACTOR_NORMAL; - v /= int(PHASE_MIDGAME); + v /= PHASE_MIDGAME; // In case of tracing add all remaining individual evaluation terms if (T) diff --git a/src/movegen.cpp b/src/movegen.cpp index 5ed24893..2f7c55c8 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -93,10 +93,10 @@ namespace { // if the pawn is not on the same file as the enemy king, because we // don't generate captures. Note that a possible discovery check // promotion has been already generated amongst the captures. - Bitboard dcCandidates = pos.blockers_for_king(Them); - if (pawnsNotOn7 & dcCandidates) + Bitboard dcCandidateQuiets = pos.blockers_for_king(Them) & pawnsNotOn7; + if (dcCandidateQuiets) { - Bitboard dc1 = shift(pawnsNotOn7 & dcCandidates) & emptySquares & ~file_bb(ksq); + Bitboard dc1 = shift(dcCandidateQuiets) & emptySquares & ~file_bb(ksq); Bitboard dc2 = shift(dc1 & TRank3BB) & emptySquares; b1 |= dc1; diff --git a/src/movepick.cpp b/src/movepick.cpp index d8ab68e7..87c3f6fa 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -76,9 +76,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist assert(d <= DEPTH_ZERO); stage = pos.checkers() ? EVASION_TT : QSEARCH_TT; - ttMove = ttm - && pos.pseudo_legal(ttm) - && (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) ? ttm : MOVE_NONE; + ttMove = ttm + && (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) + && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; stage += (ttMove == MOVE_NONE); } @@ -91,8 +91,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece stage = PROBCUT_TT; ttMove = ttm - && pos.pseudo_legal(ttm) && pos.capture(ttm) + && pos.pseudo_legal(ttm) && pos.see_ge(ttm, threshold) ? ttm : MOVE_NONE; stage += (ttMove == MOVE_NONE); } diff --git a/src/position.cpp b/src/position.cpp index 21eff88c..812eabca 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -498,14 +498,15 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners Bitboard blockers = 0; pinners = 0; - // Snipers are sliders that attack 's' when a piece is removed + // Snipers are sliders that attack 's' when a piece and other snipers are removed Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK)) | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; + Bitboard occupancy = pieces() & ~snipers; while (snipers) { Square sniperSq = pop_lsb(&snipers); - Bitboard b = between_bb(s, sniperSq) & pieces(); + Bitboard b = between_bb(s, sniperSq) & occupancy; if (b && !more_than_one(b)) { @@ -1076,8 +1077,8 @@ bool Position::see_ge(Move m, Value threshold) const { stmAttackers = attackers & pieces(stm); // Don't allow pinned pieces to attack (except the king) as long as - // all pinners are on their original square. - if (!(st->pinners[~stm] & ~occupied)) + // any pinners are on their original square. + if (st->pinners[~stm] & occupied) stmAttackers &= ~st->blockersForKing[stm]; // If stm has no more attackers then give up: stm loses diff --git a/src/psqt.cpp b/src/psqt.cpp index bc3e5392..cba6bb06 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -59,46 +59,46 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { S(-48,-51), S( -3,-40), S(-12,-39), S(-25,-20) } }, { // Rook - { S(-24, -2), S(-13,-6), S( -7, -3), S( 2,-2) }, - { S(-18,-10), S(-10,-7), S( -5, 1), S( 9, 0) }, - { S(-21, 10), S( -7,-4), S( 3, 2), S(-1,-2) }, - { S(-13, -5), S( -5, 2), S( -4, -8), S(-6, 8) }, - { S(-24, -8), S(-12, 5), S( -1, 4), S( 6,-9) }, - { S(-24, 3), S( -4,-2), S( 4,-10), S(10, 7) }, - { S( -8, 1), S( 6, 2), S( 10, 17), S(12,-8) }, - { S(-22, 12), S(-24,-6), S( -6, 13), S( 4, 7) } + { S(-24, -2), S(-13,-6), S(-7, -3), S( 2,-2) }, + { S(-18,-10), S(-10,-7), S(-5, 1), S( 9, 0) }, + { S(-21, 10), S( -7,-4), S( 3, 2), S(-1,-2) }, + { S(-13, -5), S( -5, 2), S(-4, -8), S(-6, 8) }, + { S(-24, -8), S(-12, 5), S(-1, 4), S( 6,-9) }, + { S(-24, 3), S( -4,-2), S( 4,-10), S(10, 7) }, + { S( -8, 1), S( 6, 2), S(10, 17), S(12,-8) }, + { S(-22, 12), S(-24,-6), S(-6, 13), S( 4, 7) } }, { // Queen - { S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) }, - { S( -3,-55), S( 5,-31), S( 8,-22), S(12, -4) }, - { S( -3,-39), S( 6,-18), S(13, -9), S( 7, 3) }, - { S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) }, - { S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) }, - { S( -4,-38), S(10,-18), S( 6,-12), S( 8, 1) }, - { S( -5,-50), S( 6,-27), S(10,-24), S( 8, -8) }, - { S( -2,-75), S(-2,-52), S( 1,-43), S(-2,-36) } + { S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) }, + { S(-3,-55), S( 5,-31), S( 8,-22), S(12, -4) }, + { S(-3,-39), S( 6,-18), S(13, -9), S( 7, 3) }, + { S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) }, + { S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) }, + { S(-4,-38), S(10,-18), S( 6,-12), S( 8, 1) }, + { S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) }, + { S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) } }, { // King { S(272, 0), S(325, 41), S(273, 80), S(190, 93) }, { S(277, 57), S(305, 98), S(241,138), S(183,131) }, { S(198, 86), S(253,138), S(168,165), S(120,173) }, { S(169,103), S(191,152), S(136,168), S(108,169) }, - { S(145, 98), S(176,166), S(112,197), S(69, 194) }, - { S(122, 87), S(159,164), S(85, 174), S(36, 189) }, - { S(87, 40), S(120, 99), S(64, 128), S(25, 141) }, - { S(64, 5), S(87, 60), S(49, 75), S(0, 75) } + { S(145, 98), S(176,166), S(112,197), S( 69,194) }, + { S(122, 87), S(159,164), S( 85,174), S( 36,189) }, + { S( 87, 40), S(120, 99), S( 64,128), S( 25,141) }, + { S( 64, 5), S( 87, 60), S( 49, 75), S( 0, 75) } } }; constexpr Score PBonus[RANK_NB][FILE_NB] = { // Pawn (asymmetric distribution) { }, - { S( 0,-11), S( -3,-4), S(13, -1), S( 19, -4), S(16, 17), S(13, 7), S( 4, 4), S( -4,-13) }, - { S(-16, -8), S(-12,-6), S(20, -3), S( 21, 0), S(25,-11), S(29, 3), S( 0, 0), S(-27, -1) }, - { S(-11, 3), S(-17, 6), S(11,-10), S( 21, 1), S(32, -6), S(19,-11), S( -5, 0), S(-14, -2) }, - { S( 4, 13), S( 6, 7), S(-8, 3), S( 3, -5), S( 8,-15), S(-2, -1), S(-19, 9), S( -5, 13) }, - { S( -5, 25), S(-19,20), S( 7, 16), S( 8, 12), S(-7, 21), S(-2, 3), S(-10, -4), S(-16, 15) }, - { S(-10, 6), S( 9,-5), S(-7, 16), S(-12, 27), S(-7, 15), S(-8, 11), S( 16, -7), S( -8, 4) } + { S( 0,-10), S( -5,-3), S( 10, 7), S( 13,-1), S( 21, 7), S( 17, 6), S( 6, 1), S( -3,-20) }, + { S(-11, -6), S(-10,-6), S( 15,-1), S( 22,-1), S( 26, -1), S( 28, 2), S( 4,-2), S(-24, -5) }, + { S( -9, 4), S(-18,-5), S( 8,-4), S( 22,-5), S( 33, -6), S( 25,-13), S( -4,-3), S(-16, -7) }, + { S( 6, 18), S( -3, 2), S(-10, 2), S( 1,-9), S( 12,-13), S( 6, -8), S(-12,11), S( 1, 9) }, + { S( -6, 25), S( -8,17), S( 5,19), S( 11,29), S(-14, 29), S( 0, 8), S(-12, 4), S(-14, 12) }, + { S(-10, -1), S( 6,-6), S( -5,18), S(-11,22), S( -2, 22), S(-14, 17), S( 12, 2), S( -1, 9) } }; #undef S diff --git a/src/search.cpp b/src/search.cpp index 98ce30a7..e12c6f51 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -230,10 +230,9 @@ void MainThread::search() { // Threads.stop. However, if we are pondering or in an infinite search, // the UCI protocol states that we shouldn't print the best move before the // GUI sends a "stop" or "ponderhit" command. We therefore simply wait here - // until the GUI sends one of those commands (which also raises Threads.stop). - Threads.stopOnPonderhit = true; + // until the GUI sends one of those commands. - while (!Threads.stop && (Threads.ponder || Limits.infinite)) + while (!Threads.stop && (ponder || Limits.infinite)) { Cluster::signals_poll(); } // Busy wait for a stop or a ponder reset // Stop the threads if not already stopped (also raise the stop if @@ -265,27 +264,23 @@ void MainThread::search() { // Find out minimum score and reset votes for moves which can be voted for (Thread* th: Threads) - { minScore = std::min(minScore, th->rootMoves[0].score); - votes[th->rootMoves[0].pv[0]] = 0; - } // Vote according to score and depth - auto square = [](int64_t x) { return x * x; }; - for (Thread* th : Threads) - votes[th->rootMoves[0].pv[0]] += 200 + (square(th->rootMoves[0].score - minScore + 1) - * int64_t(th->completedDepth)); - - // Select best thread - int64_t bestVote = votes[this->rootMoves[0].pv[0]]; for (Thread* th : Threads) { + int64_t s = th->rootMoves[0].score - minScore + 1; + votes[th->rootMoves[0].pv[0]] += 200 + s * s * int(th->completedDepth); + } + + // Select best thread + auto bestVote = votes[this->rootMoves[0].pv[0]]; + for (Thread* th : Threads) if (votes[th->rootMoves[0].pv[0]] > bestVote) { bestVote = votes[th->rootMoves[0].pv[0]]; bestThread = th; } - } } @@ -482,7 +477,7 @@ void Thread::search() { { failedHighCnt = 0; failedLow = true; - Threads.stopOnPonderhit = false; + mainThread->stopOnPonderhit = false; } } else if (bestValue >= beta) @@ -534,16 +529,13 @@ void Thread::search() { // Do we have time for the next iteration? Can we stop searching now? if ( Limits.use_time_management() && !Threads.stop - && !Threads.stopOnPonderhit) + && !mainThread->stopOnPonderhit) { double fallingEval = (306 + 119 * failedLow + 6 * (mainThread->previousScore - bestValue)) / 581.0; fallingEval = std::max(0.5, std::min(1.5, fallingEval)); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = 1.0; - for (int i : {3, 4, 5}) - if (lastBestMoveDepth * i < completedDepth) - timeReduction *= 1.25; + timeReduction = lastBestMoveDepth + 10 * ONE_PLY < completedDepth ? 1.95 : 1.0; // Use part of the gained time from a previous stable move for the current move double bestMoveInstability = 1.0 + mainThread->bestMoveChanges; @@ -555,8 +547,8 @@ void Thread::search() { { // If we are allowed to ponder do not stop the search now but // keep pondering until the GUI sends "ponderhit" or "stop". - if (Threads.ponder) - Threads.stopOnPonderhit = true; + if (mainThread->ponder) + mainThread->stopOnPonderhit = true; else Threads.stop = true; } @@ -614,8 +606,8 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, pureStaticEval; - bool ttHit, pvHit, inCheck, givesCheck, improving; - bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture, pvExact; + bool ttHit, ttPv, inCheck, givesCheck, improving; + bool captureOrPromotion, doFullDepthSearch, moveCountPruning, skipQuiets, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -680,7 +672,7 @@ namespace { ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ttHit ? tte->move() : MOVE_NONE; - pvHit = ttHit ? tte->pv_hit() : false; + ttPv = (ttHit && tte->is_pv()) || (PvNode && depth > 4 * ONE_PLY); // At non-PV nodes we check for an early TT cutoff if ( !PvNode @@ -714,11 +706,6 @@ namespace { return ttValue; } - if ( depth > 6 * ONE_PLY - && !excludedMove - && PvNode) - pvHit = true; - // Step 5. Tablebases probe if (!rootNode && TB::Cardinality) { @@ -753,7 +740,7 @@ namespace { || (b == BOUND_LOWER ? value >= beta : value <= alpha)) { Cluster::save(thisThread, tte, - posKey, value_to_tt(value, ss->ply), pvHit, b, + posKey, value_to_tt(value, ss->ply), ttPv, b, std::min(DEPTH_MAX - ONE_PLY, depth + 6 * ONE_PLY), MOVE_NONE, VALUE_NONE); @@ -794,9 +781,7 @@ namespace { { if ((ss-1)->currentMove != MOVE_NULL) { - int p = (ss-1)->statScore; - int bonus = p > 0 ? (-p - 2500) / 512 : - p < 0 ? (-p + 2500) / 512 : 0; + int bonus = -(ss-1)->statScore / 512; pureStaticEval = evaluate(pos); ss->staticEval = eval = pureStaticEval + bonus; @@ -805,7 +790,7 @@ namespace { ss->staticEval = eval = pureStaticEval = -(ss-1)->staticEval + 2 * Eval::Tempo; Cluster::save(thisThread, tte, - posKey, VALUE_NONE, pvHit, BOUND_NONE, DEPTH_NONE, MOVE_NONE, + posKey, VALUE_NONE, ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, pureStaticEval); } @@ -921,7 +906,6 @@ namespace { tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; ttMove = ttHit ? tte->move() : MOVE_NONE; - pvHit = ttHit ? tte->pv_hit() : false; } moves_loop: // When in check, search starts from here @@ -938,7 +922,6 @@ moves_loop: // When in check, search starts from here skipQuiets = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); - pvExact = PvNode && ttHit && tte->bound() == BOUND_EXACT; // Step 12. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. @@ -1006,11 +989,13 @@ moves_loop: // When in check, search starts from here else if (cutNode && singularBeta > beta) return beta; } - else if ( givesCheck // Check extension (~2 Elo) - && pos.see_ge(move)) + + // Check extension (~2 Elo) + else if ( givesCheck + && (pos.blockers_for_king(~us) & from_sq(move) || pos.see_ge(move))) extension = ONE_PLY; - // Extension if castling + // Castling extension else if (type_of(move) == CASTLING) extension = ONE_PLY; @@ -1083,7 +1068,7 @@ moves_loop: // When in check, search starts from here Depth r = reduction(improving, depth, moveCount); // Decrease reduction if position is or has been on the PV - if (pvHit && !PvNode) + if (ttPv) r -= ONE_PLY; // Decrease reduction if opponent's move count is high (~10 Elo) @@ -1092,10 +1077,6 @@ moves_loop: // When in check, search starts from here if (!captureOrPromotion) { - // Decrease reduction for exact PV nodes (~0 Elo) - if (pvExact) - r -= ONE_PLY; - // Increase reduction if ttMove is a capture (~0 Elo) if (ttCapture) r += ONE_PLY; @@ -1269,7 +1250,7 @@ moves_loop: // When in check, search starts from here if (!excludedMove) Cluster::save(thisThread, tte, - posKey, value_to_tt(bestValue, ss->ply), pvHit, + posKey, value_to_tt(bestValue, ss->ply), ttPv, bestValue >= beta ? BOUND_LOWER : PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, depth, bestMove, pureStaticEval); @@ -1333,7 +1314,7 @@ moves_loop: // When in check, search starts from here tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; ttMove = ttHit ? tte->move() : MOVE_NONE; - pvHit = ttHit ? tte->pv_hit() : false; + pvHit = ttHit && tte->is_pv(); if ( !PvNode && ttHit @@ -1654,10 +1635,10 @@ void MainThread::check_time() { Cluster::signals_poll(); // We should not stop pondering until told so by the GUI - if (Threads.ponder) + if (ponder) return; - if ( (Limits.use_time_management() && elapsed > Time.maximum() - 10) + if ( (Limits.use_time_management() && (elapsed > Time.maximum() - 10 || stopOnPonderhit)) || (Limits.movetime && elapsed >= Limits.movetime) || (Limits.nodes && Cluster::nodes_searched() >= (uint64_t)Limits.nodes)) Threads.stop = true; diff --git a/src/thread.cpp b/src/thread.cpp index 69a7752f..75c2bf6a 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -165,9 +165,8 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, main()->wait_for_search_finished(); - stopOnPonderhit = stop = false; - - ponder = ponderMode; + main()->stopOnPonderhit = stop = false; + main()->ponder = ponderMode; Search::Limits = limits; Search::RootMoves rootMoves; diff --git a/src/thread.h b/src/thread.h index 6b078603..b3bed46a 100644 --- a/src/thread.h +++ b/src/thread.h @@ -96,6 +96,8 @@ struct MainThread : public Thread { double bestMoveChanges, previousTimeReduction; Value previousScore; int callsCnt; + bool stopOnPonderhit; + std::atomic_bool ponder; }; @@ -114,7 +116,7 @@ struct ThreadPool : public std::vector { uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } uint64_t TT_saves() const { return accumulate(&Thread::TTsaves); } - std::atomic_bool stop, ponder, stopOnPonderhit; + std::atomic_bool stop; private: StateListPtr setupStates; diff --git a/src/tt.cpp b/src/tt.cpp index d05de916..33768ca4 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -30,8 +30,10 @@ TranspositionTable TT; // Our global transposition table -/// TTEntry::save saves a TTEntry -void TTEntry::save(Key k, Value v, bool PvNode, Bound b, Depth d, Move m, Value ev) { +/// TTEntry::save populates the TTEntry with a new node's data, possibly +/// overwriting an old position. Update is not atomic and can be racy. + +void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { assert(d / ONE_PLY * ONE_PLY == d); @@ -47,7 +49,7 @@ void TTEntry::save(Key k, Value v, bool PvNode, Bound b, Depth d, Move m, Value key16 = (uint16_t)(k >> 48); value16 = (int16_t)v; eval16 = (int16_t)ev; - genBound8 = (uint8_t)(TT.generation8 | PvNode << 2 | b); + genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); depth8 = (int8_t)(d / ONE_PLY); } } @@ -122,7 +124,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { for (int i = 0; i < ClusterSize; ++i) if (!tte[i].key16 || tte[i].key16 == key16) { - tte[i].genBound8 = uint8_t(generation8 | tte[i].pv_hit() << 2 | tte[i].bound()); // Refresh + tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & 0x7)); // Refresh return found = (bool)tte[i].key16, &tte[i]; } @@ -131,8 +133,8 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { TTEntry* replace = tte; for (int i = 1; i < ClusterSize; ++i) // Due to our packed storage format for generation and its cyclic - // nature we add 263 (263 is the modulus plus 7 to keep the lowest - // two bound bits from affecting the result) to calculate the entry + // nature we add 263 (256 is the modulus plus 7 to keep the unrelated + // lowest three bits from affecting the result) to calculate the entry // age correctly even after generation8 overflows into the next cycle. if ( replace->depth8 - ((263 + generation8 - replace->genBound8) & 0xF8) > tte[i].depth8 - ((263 + generation8 - tte[i].genBound8) & 0xF8)) @@ -150,7 +152,7 @@ int TranspositionTable::hashfull() const { int cnt = 0; for (int i = 0; i < 1000 / ClusterSize; ++i) for (int j = 0; j < ClusterSize; ++j) - cnt += (table[i].entry[j].genBound8 & 0xFC) == generation8; + cnt += (table[i].entry[j].genBound8 & 0xF8) == generation8; return cnt * 1000 / (ClusterSize * (1000 / ClusterSize)); } diff --git a/src/tt.h b/src/tt.h index 32267c7a..9b9516fa 100644 --- a/src/tt.h +++ b/src/tt.h @@ -36,7 +36,7 @@ namespace Cluster { /// value 16 bit /// eval value 16 bit /// generation 5 bit -/// PvNode 1 bit +/// pv node 1 bit /// bound type 2 bit /// depth 8 bit @@ -46,9 +46,9 @@ struct TTEntry { Value value() const { return (Value)value16; } Value eval() const { return (Value)eval16; } Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)); } - bool pv_hit() const { return (bool)(genBound8 & 0x4); } + bool is_pv() const { return (bool)(genBound8 & 0x4); } Bound bound() const { return (Bound)(genBound8 & 0x3); } - void save(Key k, Value v, bool PvNode, Bound b, Depth d, Move m, Value ev); + void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev); private: friend class TranspositionTable; diff --git a/src/types.h b/src/types.h index 8e27606c..a7415519 100644 --- a/src/types.h +++ b/src/types.h @@ -141,9 +141,11 @@ enum CastlingRight { WHITE_OOO = WHITE_OO << 1, BLACK_OO = WHITE_OO << 2, BLACK_OOO = WHITE_OO << 3, + WHITE_CASTLING = WHITE_OO | WHITE_OOO, BLACK_CASTLING = BLACK_OO | BLACK_OOO, - ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING, + ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING, + CASTLING_RIGHT_NB = 16 }; diff --git a/src/uci.cpp b/src/uci.cpp index 82c9db2e..7318b758 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -210,18 +210,16 @@ void UCI::loop(int argc, char* argv[]) { token.clear(); // Avoid a stale if getline() returns empty or blank line is >> skipws >> token; + if ( token == "quit" + || token == "stop") + Threads.stop = true; + // The GUI sends 'ponderhit' to tell us the user has played the expected move. // So 'ponderhit' will be sent if we were told to ponder on the same move the // user has played. We should continue searching but switch from pondering to - // normal search. In case Threads.stopOnPonderhit is set we are waiting for - // 'ponderhit' to stop the search, for instance if max search depth is reached. - if ( token == "quit" - || token == "stop" - || (token == "ponderhit" && Threads.stopOnPonderhit)) - Threads.stop = true; - + // normal search. else if (token == "ponderhit") - Threads.ponder = false; // Switch to normal search + Threads.main()->ponder = false; // Switch to normal search else if (token == "uci" && Cluster::is_root()) sync_cout << "id name " << engine_info(true) diff --git a/tests/instrumented.sh b/tests/instrumented.sh index 5156e02f..ae6d5c4b 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -45,7 +45,7 @@ race:TTEntry::bound race:TTEntry::save race:TTEntry::value race:TTEntry::eval -race:TTEntry::pv_hit +race:TTEntry::is_pv race:TranspositionTable::probe race:TranspositionTable::hashfull