X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fpawns.cpp;h=c8c2e6d8c243db77d67b30afd109dfd2ea1fff4f;hp=91f8ed752004cb7a48760f71fb0c4d0679617545;hb=471f7a1b5c4da03a712da2ce079034de45b4c35b;hpb=ccdb634b7722a300707b9348ac30ae2c4152da8a diff --git a/src/pawns.cpp b/src/pawns.cpp index 91f8ed75..fb91afcb 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -1,7 +1,8 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2009 Marco Costalba + Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2015-2017 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,400 +18,284 @@ along with this program. If not, see . */ - -//// -//// Includes -//// - +#include #include -#include -#include "bitcount.h" +#include "bitboard.h" #include "pawns.h" #include "position.h" - - -//// -//// Local definitions -//// +#include "thread.h" namespace { - /// Constants and variables + #define V Value + #define S(mg, eg) make_score(mg, eg) - // Doubled pawn penalty by file, middle game - const Value DoubledPawnMidgamePenalty[8] = { - Value(13), Value(20), Value(23), Value(23), - Value(23), Value(23), Value(20), Value(13) - }; + // Isolated pawn penalty by opposed flag + const Score Isolated[2] = { S(45, 40), S(30, 27) }; - // Doubled pawn penalty by file, endgame - const Value DoubledPawnEndgamePenalty[8] = { - Value(43), Value(48), Value(48), Value(48), - Value(48), Value(48), Value(48), Value(43) - }; + // Backward pawn penalty by opposed flag + const Score Backward[2] = { S(56, 33), S(41, 19) }; - // Isolated pawn penalty by file, middle game - const Value IsolatedPawnMidgamePenalty[8] = { - Value(25), Value(36), Value(40), Value(40), - Value(40), Value(40), Value(36), Value(25) - }; + // Unsupported pawn penalty for pawns which are neither isolated or backward + const Score Unsupported = S(17, 8); - // Isolated pawn penalty by file, endgame - const Value IsolatedPawnEndgamePenalty[8] = { - Value(30), Value(35), Value(35), Value(35), - Value(35), Value(35), Value(35), Value(30) - }; + // Connected pawn bonus by opposed, phalanx, twice supported and rank + Score Connected[2][2][2][RANK_NB]; - // Backward pawn penalty by file, middle game - const Value BackwardPawnMidgamePenalty[8] = { - Value(20), Value(29), Value(33), Value(33), - Value(33), Value(33), Value(29), Value(20) - }; + // Doubled pawn penalty + const Score Doubled = S(18, 38); - // Backward pawn penalty by file, endgame - const Value BackwardPawnEndgamePenalty[8] = { - Value(28), Value(31), Value(31), Value(31), - Value(31), Value(31), Value(31), Value(28) + // Lever bonus by rank + const Score Lever[RANK_NB] = { + S( 0, 0), S( 0, 0), S(0, 0), S(0, 0), + S(17, 16), S(33, 32), S(0, 0), S(0, 0) }; - // Pawn chain membership bonus by file, middle game - const Value ChainMidgameBonus[8] = { - Value(11), Value(13), Value(13), Value(14), - Value(14), Value(13), Value(13), Value(11) + // Weakness of our pawn shelter in front of the king by [distance from edge][rank]. + // RANK_1 = 0 is used for files where we have no pawns or our pawn is behind our king. + const Value ShelterWeakness[][RANK_NB] = { + { V(100), V(20), V(10), V(46), V(82), V( 86), V( 98) }, + { V(116), V( 4), V(28), V(87), V(94), V(108), V(104) }, + { V(109), V( 1), V(59), V(87), V(62), V( 91), V(116) }, + { V( 75), V(12), V(43), V(59), V(90), V( 84), V(112) } }; - // Pawn chain membership bonus by file, endgame - const Value ChainEndgameBonus[8] = { - Value(-1), Value(-1), Value(-1), Value(-1), - Value(-1), Value(-1), Value(-1), Value(-1) + // Danger of enemy pawns moving toward our king by [type][distance from edge][rank]. + // For the unopposed and unblocked cases, RANK_1 = 0 is used when opponent has no pawn + // on the given file, or their pawn is behind our king. + const Value StormDanger[][4][RANK_NB] = { + { { V( 0), V(-290), V(-274), V(57), V(41) }, //BlockedByKing + { V( 0), V( 60), V( 144), V(39), V(13) }, + { V( 0), V( 65), V( 141), V(41), V(34) }, + { V( 0), V( 53), V( 127), V(56), V(14) } }, + { { V( 4), V( 73), V( 132), V(46), V(31) }, //Unopposed + { V( 1), V( 64), V( 143), V(26), V(13) }, + { V( 1), V( 47), V( 110), V(44), V(24) }, + { V( 0), V( 72), V( 127), V(50), V(31) } }, + { { V( 0), V( 0), V( 79), V(23), V( 1) }, //BlockedByPawn + { V( 0), V( 0), V( 148), V(27), V( 2) }, + { V( 0), V( 0), V( 161), V(16), V( 1) }, + { V( 0), V( 0), V( 171), V(22), V(15) } }, + { { V(22), V( 45), V( 104), V(62), V( 6) }, //Unblocked + { V(31), V( 30), V( 99), V(39), V(19) }, + { V(23), V( 29), V( 96), V(41), V(15) }, + { V(21), V( 23), V( 116), V(41), V(15) } } }; - // Candidate passed pawn bonus by rank, middle game - const Value CandidateMidgameBonus[8] = { - Value( 0), Value( 6), Value(6), Value(14), - Value(34), Value(83), Value(0), Value( 0) - }; + // Max bonus for king safety. Corresponds to start position with all the pawns + // in front of the king and no enemy pawn on the horizon. + const Value MaxSafetyBonus = V(258); + + #undef S + #undef V + + template + Score evaluate(const Position& pos, Pawns::Entry* e) { + + const Color Them = (Us == WHITE ? BLACK : WHITE); + const Square Up = (Us == WHITE ? NORTH : SOUTH); + const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); + const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); + + Bitboard b, neighbours, stoppers, doubled, supported, phalanx; + Bitboard lever, leverPush, connected; + Square s; + bool opposed, backward; + Score score = SCORE_ZERO; + const Square* pl = pos.squares(Us); + const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)]; + + Bitboard ourPawns = pos.pieces(Us , PAWN); + Bitboard theirPawns = pos.pieces(Them, PAWN); + + e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0; + e->semiopenFiles[Us] = 0xFF; + e->kingSquares[Us] = SQ_NONE; + e->pawnAttacks[Us] = shift(ourPawns) | shift(ourPawns); + e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & DarkSquares); + e->pawnsOnSquares[Us][WHITE] = pos.count(Us) - e->pawnsOnSquares[Us][BLACK]; + + // Loop through all pawns of the current color and score each pawn + while ((s = *pl++) != SQ_NONE) + { + assert(pos.piece_on(s) == make_piece(Us, PAWN)); + + File f = file_of(s); + + e->semiopenFiles[Us] &= ~(1 << f); + e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s); + + // Flag the pawn + opposed = theirPawns & forward_bb(Us, s); + stoppers = theirPawns & passed_pawn_mask(Us, s); + lever = theirPawns & pawnAttacksBB[s]; + leverPush = theirPawns & pawnAttacksBB[s + Up]; + doubled = ourPawns & (s + Up); + neighbours = ourPawns & adjacent_files_bb(f); + phalanx = neighbours & rank_bb(s); + supported = neighbours & rank_bb(s - Up); + connected = supported | phalanx; + + // A pawn is backward when it is behind all pawns of the same color on the + // adjacent files and cannot be safely advanced. + if (!neighbours || lever || relative_rank(Us, s) >= RANK_5) + backward = false; + else + { + // Find the backmost rank with neighbours or stoppers + b = rank_bb(backmost_sq(Us, neighbours | stoppers)); + + // The pawn is backward when it cannot safely progress to that rank: + // either there is a stopper in the way on this rank, or there is a + // stopper on adjacent file which controls the way to that rank. + backward = (b | shift(b & adjacent_files_bb(f))) & stoppers; + + assert(!backward || !(pawn_attack_span(Them, s + Up) & neighbours)); + } + + // Passed pawns will be properly scored in evaluation because we need + // full attack info to evaluate them. Include also not passed pawns + // which could become passed after one or two pawn pushes when are + // not attacked more times than defended. + if ( !(stoppers ^ lever ^ leverPush) + && !(ourPawns & forward_bb(Us, s)) + && popcount(supported) >= popcount(lever) + && popcount(phalanx) >= popcount(leverPush)) + e->passedPawns[Us] |= s; + + // Score this pawn + if (!neighbours) + score -= Isolated[opposed]; + + else if (backward) + score -= Backward[opposed]; + + else if (!supported) + score -= Unsupported; + + if (connected) + score += Connected[opposed][!!phalanx][more_than_one(supported)][relative_rank(Us, s)]; + + if (doubled) + score -= Doubled; + + if (lever) + score += Lever[relative_rank(Us, s)]; + } + + return score; + } - // Candidate passed pawn bonus by rank, endgame - const Value CandidateEndgameBonus[8] = { - Value( 0), Value( 13), Value(13), Value(29), - Value(68), Value(166), Value( 0), Value( 0) - }; +} // namespace - // Pawn storm tables for positions with opposite castling - const int QStormTable[64] = { - 0, 0, 0, 0, 0, 0, 0, 0, - -22,-22,-22,-14,-6, 0, 0, 0, - -6,-10,-10,-10,-6, 0, 0, 0, - 4, 12, 16, 12, 4, 0, 0, 0, - 16, 23, 23, 16, 0, 0, 0, 0, - 23, 31, 31, 23, 0, 0, 0, 0, - 23, 31, 31, 23, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - }; +namespace Pawns { - const int KStormTable[64] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,-10,-19,-28,-33,-33, - 0, 0, 0,-10,-15,-19,-24,-24, - 0, 0, 0, 0, 1, 1, 1, 1, - 0, 0, 0, 0, 1, 10, 19, 19, - 0, 0, 0, 0, 1, 19, 31, 27, - 0, 0, 0, 0, 0, 22, 31, 22, - 0, 0, 0, 0, 0, 0, 0, 0 - }; +/// 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. - // Pawn storm open file bonuses by file - const int16_t KStormOpenFileBonus[8] = { 31, 31, 18, 0, 0, 0, 0, 0 }; - const int16_t QStormOpenFileBonus[8] = { 0, 0, 0, 0, 0, 26, 42, 26 }; +void init() { - // Pawn storm lever bonuses by file - const int StormLeverBonus[8] = { -8, -8, -13, 0, 0, -13, -8, -8 }; + static const int Seed[RANK_NB] = { 0, 8, 19, 13, 71, 94, 169, 324 }; + for (int opposed = 0; opposed <= 1; ++opposed) + for (int phalanx = 0; phalanx <= 1; ++phalanx) + for (int apex = 0; apex <= 1; ++apex) + for (Rank r = RANK_2; r < RANK_8; ++r) + { + int v = (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed; + v += (apex ? v / 2 : 0); + Connected[opposed][phalanx][apex][r] = make_score(v, v * (r-2) / 4); + } } -//// -//// Functions -//// - -/// Constructor +/// 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 +/// have to recompute all when the same pawns configuration occurs again. -PawnInfoTable::PawnInfoTable(unsigned numOfEntries) { +Entry* probe(const Position& pos) { - size = numOfEntries; - entries = new PawnInfo[size]; - if (!entries) - { - std::cerr << "Failed to allocate " << (numOfEntries * sizeof(PawnInfo)) - << " bytes for pawn hash table." << std::endl; - Application::exit_with_failure(); - } -} + Key key = pos.pawn_key(); + Entry* e = pos.this_thread()->pawnsTable[key]; + if (e->key == key) + return e; -/// Destructor - -PawnInfoTable::~PawnInfoTable() { - delete [] entries; + e->key = key; + e->score = evaluate(pos, e) - evaluate(pos, e); + e->asymmetry = popcount(e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]); + e->openFiles = popcount(e->semiopenFiles[WHITE] & e->semiopenFiles[BLACK]); + return e; } -/// PawnInfo::clear() resets to zero the PawnInfo entry. Note that -/// kingSquares[] is initialized to SQ_NONE instead. +/// Entry::shelter_storm() calculates shelter and storm penalties for the file +/// the king is on, as well as the two closest files. -void PawnInfo::clear() { +template +Value Entry::shelter_storm(const Position& pos, Square ksq) { - memset(this, 0, sizeof(PawnInfo)); - kingSquares[WHITE] = kingSquares[BLACK] = SQ_NONE; -} + const Color Them = (Us == WHITE ? BLACK : WHITE); + enum { BlockedByKing, Unopposed, BlockedByPawn, Unblocked }; -/// PawnInfoTable::get_pawn_info() takes a position object as input, computes -/// a PawnInfo object, and returns a pointer to it. The result is also -/// stored in a hash table, so we don't have to recompute everything when -/// the same pawn structure occurs again. + Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq)); + Bitboard ourPawns = b & pos.pieces(Us); + Bitboard theirPawns = b & pos.pieces(Them); + Value safety = MaxSafetyBonus; + File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq))); -PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) { + for (File f = center - File(1); f <= center + File(1); ++f) + { + b = ourPawns & file_bb(f); + Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1; + + b = theirPawns & file_bb(f); + Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; + + safety -= ShelterWeakness[std::min(f, FILE_H - f)][rkUs] + + StormDanger + [f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing : + rkUs == RANK_1 ? Unopposed : + rkThem == rkUs + 1 ? BlockedByPawn : Unblocked] + [std::min(f, FILE_H - f)][rkThem]; + } - assert(pos.is_ok()); + return safety; +} - Key key = pos.get_pawn_key(); - int index = int(key & (size - 1)); - PawnInfo* pi = entries + index; - // If pi->key matches the position's pawn hash key, it means that we - // have analysed this pawn structure before, and we can simply return - // the information we found the last time instead of recomputing it. - if (pi->key == key) - return pi; +/// Entry::do_king_safety() calculates a bonus for king safety. It is called only +/// when king square changes, which is about 20% of total king_safety() calls. - // Clear the PawnInfo object, and set the key - pi->clear(); - pi->key = key; +template +Score Entry::do_king_safety(const Position& pos, Square ksq) { - // Calculate pawn attacks - Bitboard whitePawns = pos.pieces(PAWN, WHITE); - Bitboard blackPawns = pos.pieces(PAWN, BLACK); - pi->pawnAttacks[WHITE] = ((whitePawns << 9) & ~FileABB) | ((whitePawns << 7) & ~FileHBB); - pi->pawnAttacks[BLACK] = ((blackPawns >> 7) & ~FileABB) | ((blackPawns >> 9) & ~FileHBB); + kingSquares[Us] = ksq; + castlingRights[Us] = pos.can_castle(Us); + int minKingPawnDistance = 0; - // Evaluate pawns for both colors - Values whiteValues = evaluate_pawns(pos, whitePawns, blackPawns, pi); - Values blackValues = evaluate_pawns(pos, blackPawns, whitePawns, pi); + Bitboard pawns = pos.pieces(Us, PAWN); + if (pawns) + while (!(DistanceRingBB[ksq][minKingPawnDistance++] & pawns)) {} - pi->mgValue = int16_t(whiteValues.first - blackValues.first); - pi->egValue = int16_t(whiteValues.second - blackValues.second); - return pi; -} + Value bonus = shelter_storm(pos, ksq); + // If we can castle use the bonus after the castling if it is bigger + if (pos.can_castle(MakeCastling::right)) + bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_G1))); -/// PawnInfoTable::evaluate_pawns() evaluates each pawn of the given color + if (pos.can_castle(MakeCastling::right)) + bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_C1))); -template -PawnInfoTable::Values PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns, - Bitboard theirPawns, PawnInfo* pi) { - Square s; - File f; - Rank r; - bool passed, isolated, doubled, chain, backward, candidate; - int bonus; - Value mgValue = Value(0); - Value egValue = Value(0); - Bitboard pawns = ourPawns; - - // Initialize pawn storm scores by giving bonuses for open files - for (File f = FILE_A; f <= FILE_H; f++) - if (!(ourPawns & file_bb(f))) - { - pi->ksStormValue[Us] += KStormOpenFileBonus[f]; - pi->qsStormValue[Us] += QStormOpenFileBonus[f]; - pi->halfOpenFiles[Us] |= (1 << f); - } - - // Loop through all pawns of the current color and score each pawn - while (pawns) - { - s = pop_1st_bit(&pawns); - f = square_file(s); - r = square_rank(s); - - assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN)); - - // Passed, isolated or doubled pawn? - passed = Position::pawn_is_passed(theirPawns, Us, s); - isolated = Position::pawn_is_isolated(ourPawns, s); - doubled = Position::pawn_is_doubled(ourPawns, Us, s); - - // We calculate kingside and queenside pawn storm - // scores for both colors. These are used when evaluating - // middle game positions with opposite side castling. - // - // Each pawn is given a base score given by a piece square table - // (KStormTable[] or QStormTable[]). Pawns which seem to have good - // chances of creating an open file by exchanging itself against an - // enemy pawn on an adjacent file gets an additional bonus. - - // Kingside pawn storms - bonus = KStormTable[relative_square(Us, s)]; - if (f >= FILE_F) - { - Bitboard b = outpost_mask(Us, s) & theirPawns & (FileFBB | FileGBB | FileHBB); - while (b) - { - Square s2 = pop_1st_bit(&b); - if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2))) - { - // The enemy pawn has no pawn beside itself, which makes it - // particularly vulnerable. Big bonus, especially against a - // weakness on the rook file. - if (square_file(s2) == FILE_H) - bonus += 4*StormLeverBonus[f] - 8*square_distance(s, s2); - else - bonus += 2*StormLeverBonus[f] - 4*square_distance(s, s2); - } else - // There is at least one enemy pawn beside the enemy pawn we look - // at, which means that the pawn has somewhat better chances of - // defending itself by advancing. Smaller bonus. - bonus += StormLeverBonus[f] - 2*square_distance(s, s2); - } - } - pi->ksStormValue[Us] += bonus; - - // Queenside pawn storms - bonus = QStormTable[relative_square(Us, s)]; - if (f <= FILE_C) - { - Bitboard b = outpost_mask(Us, s) & theirPawns & (FileABB | FileBBB | FileCBB); - while (b) - { - Square s2 = pop_1st_bit(&b); - if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2))) - { - // The enemy pawn has no pawn beside itself, which makes it - // particularly vulnerable. Big bonus, especially against a - // weakness on the rook file. - if (square_file(s2) == FILE_A) - bonus += 4*StormLeverBonus[f] - 16*square_distance(s, s2); - else - bonus += 2*StormLeverBonus[f] - 8*square_distance(s, s2); - } else - // There is at least one enemy pawn beside the enemy pawn we look - // at, which means that the pawn has somewhat better chances of - // defending itself by advancing. Smaller bonus. - bonus += StormLeverBonus[f] - 4*square_distance(s, s2); - } - } - pi->qsStormValue[Us] += bonus; - - // Member of a pawn chain (but not the backward one)? We could speed up - // the test a little by introducing an array of masks indexed by color - // and square for doing the test, but because everything is hashed, - // it probably won't make any noticable difference. - chain = ourPawns - & neighboring_files_bb(f) - & (rank_bb(r) | rank_bb(r - (Us == WHITE ? 1 : -1))); - - // Test for backward pawn - // - // If the pawn is passed, isolated, or member of a pawn chain - // it cannot be backward. If can capture an enemy pawn or if - // there are friendly pawns behind on neighboring files it cannot - // be backward either. - if ( (passed | isolated | chain) - || (ourPawns & behind_bb(Us, r) & neighboring_files_bb(f)) - || (pos.attacks_from(s, Us) & theirPawns)) - backward = false; - else - { - // We now know that there are no friendly pawns beside or behind this - // pawn on neighboring files. We now check whether the pawn is - // backward by looking in the forward direction on the neighboring - // files, and seeing whether we meet a friendly or an enemy pawn first. - Bitboard b = pos.attacks_from(s, Us); - - // Note that we are sure to find something because pawn is not passed - // nor isolated, so loop is potentially infinite, but it isn't. - while (!(b & (ourPawns | theirPawns))) - Us == WHITE ? b <<= 8 : b >>= 8; - - // The friendly pawn needs to be at least two ranks closer than the enemy - // pawn in order to help the potentially backward pawn advance. - backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns; - } - - // Test for candidate passed pawn - candidate = !passed - && !(theirPawns & file_bb(f)) - && ( count_1s_max_15(neighboring_files_bb(f) & (behind_bb(Us, r) | rank_bb(r)) & ourPawns) - - count_1s_max_15(neighboring_files_bb(f) & in_front_bb(Us, r) & theirPawns) - >= 0); - - // In order to prevent doubled passed pawns from receiving a too big - // bonus, only the frontmost passed pawn on each file is considered as - // a true passed pawn. - if (passed && (ourPawns & squares_in_front_of(Us, s))) - passed = false; - - // Score this pawn - if (passed) - set_bit(&(pi->passedPawns), s); - - if (isolated) - { - mgValue -= IsolatedPawnMidgamePenalty[f]; - egValue -= IsolatedPawnEndgamePenalty[f]; - if (!(theirPawns & file_bb(f))) - { - mgValue -= IsolatedPawnMidgamePenalty[f] / 2; - egValue -= IsolatedPawnEndgamePenalty[f] / 2; - } - } - if (doubled) - { - mgValue -= DoubledPawnMidgamePenalty[f]; - egValue -= DoubledPawnEndgamePenalty[f]; - } - if (backward) - { - mgValue -= BackwardPawnMidgamePenalty[f]; - egValue -= BackwardPawnEndgamePenalty[f]; - if (!(theirPawns & file_bb(f))) - { - mgValue -= BackwardPawnMidgamePenalty[f] / 2; - egValue -= BackwardPawnEndgamePenalty[f] / 2; - } - } - if (chain) - { - mgValue += ChainMidgameBonus[f]; - egValue += ChainEndgameBonus[f]; - } - if (candidate) - { - mgValue += CandidateMidgameBonus[relative_rank(Us, s)]; - egValue += CandidateEndgameBonus[relative_rank(Us, s)]; - } - } // while (pawns) - - return Values(mgValue, egValue); + return make_score(bonus, -16 * minKingPawnDistance); } +// Explicit template instantiation +template Score Entry::do_king_safety(const Position& pos, Square ksq); +template Score Entry::do_king_safety(const Position& pos, Square ksq); -/// PawnInfo::updateShelter calculates and caches king shelter. It is called -/// only when king square changes, about 20% of total get_king_shelter() calls. -int PawnInfo::updateShelter(const Position& pos, Color c, Square ksq) { - - unsigned shelter = 0; - Bitboard pawns = pos.pieces(PAWN, c) & this_and_neighboring_files_bb(ksq); - unsigned r = ksq & (7 << 3); - for (int i = 1, k = (c ? -8 : 8); i < 4; i++) - { - r += k; - shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (128 >> i); - } - kingSquares[c] = ksq; - kingShelters[c] = shelter; - return shelter; -} +} // namespace Pawns