X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fevaluate.cpp;h=30ed08793e948744db4bef81500057b3de5e3bde;hp=ed04aa54026722219ce4e71444163a287c2ad137;hb=d0e51bc0f0c77f93323aaa86c9c2485c41d38271;hpb=dfcfed6432a2e4cb315c87a84081e599a32b93cc diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ed04aa54..30ed0879 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -63,6 +63,7 @@ namespace { const int WeightPassedPawnsMidgameInternal = 0x100; const int WeightPassedPawnsEndgameInternal = 0x100; const int WeightKingSafetyInternal = 0x100; + const int WeightKingOppSafetyInternal = 0x100; // Visually better to define tables constants typedef Value V; @@ -269,11 +270,12 @@ namespace { EvalInfo &ei); inline Value apply_weight(Value v, int w); - Value scale_by_game_phase(Value mv, Value ev, Phase ph, ScaleFactor sf[]); + Value scale_by_game_phase(Value mv, Value ev, Phase ph, const ScaleFactor sf[]); int count_1s_8bit(Bitboard b); int compute_weight(int uciWeight, int internalWeight); + int weight_option(const std::string& opt, int weight); void init_safety(); } @@ -288,141 +290,122 @@ namespace { /// between them based on the remaining material. Value evaluate(const Position &pos, EvalInfo &ei, int threadID) { - Color stm; - Square s; - ScaleFactor factor[2] = {SCALE_FACTOR_NORMAL, SCALE_FACTOR_NORMAL}; - Phase phase; - - memset(&ei, 0, sizeof(EvalInfo)); assert(pos.is_ok()); assert(threadID >= 0 && threadID < THREAD_MAX); - stm = pos.side_to_move(); + memset(&ei, 0, sizeof(EvalInfo)); // Initialize by reading the incrementally updated scores included in the - // position object (material + piece square tables): + // position object (material + piece square tables) ei.mgValue = pos.mg_value(); ei.egValue = pos.eg_value(); - // Probe the material hash table: + // Probe the material hash table ei.mi = MaterialTable[threadID]->get_material_info(pos); ei.mgValue += ei.mi->mg_value(); ei.egValue += ei.mi->eg_value(); - factor[WHITE] = ei.mi->scale_factor(pos, WHITE); - factor[BLACK] = ei.mi->scale_factor(pos, BLACK); - // If we have a specialized evaluation function for the current material - // configuration, call it and return: - if(ei.mi->specialized_eval_exists()) - return ei.mi->evaluate(pos); + // configuration, call it and return + if (ei.mi->specialized_eval_exists()) + return ei.mi->evaluate(pos); - phase = pos.game_phase(); + // After get_material_info() call that modifies them + ScaleFactor factor[2]; + factor[WHITE] = ei.mi->scale_factor(pos, WHITE); + factor[BLACK] = ei.mi->scale_factor(pos, BLACK); - // Probe the pawn hash table: + // Probe the pawn hash table ei.pi = PawnTable[threadID]->get_pawn_info(pos); ei.mgValue += apply_weight(ei.pi->mg_value(), WeightPawnStructureMidgame); ei.egValue += apply_weight(ei.pi->eg_value(), WeightPawnStructureEndgame); - // Initialize king attack bitboards and king attack zones for both sides: - ei.attackedBy[WHITE][KING] = pos.king_attacks(pos.king_square(WHITE)); - ei.attackedBy[BLACK][KING] = pos.king_attacks(pos.king_square(BLACK)); - ei.kingZone[WHITE] = - ei.attackedBy[BLACK][KING] | (ei.attackedBy[BLACK][KING] >> 8); - ei.kingZone[BLACK] = - ei.attackedBy[WHITE][KING] | (ei.attackedBy[WHITE][KING] << 8); - - // Initialize pawn attack bitboards for both sides: - ei.attackedBy[WHITE][PAWN] = - ((pos.pawns(WHITE) << 9) & ~FileABB) | ((pos.pawns(WHITE) << 7) & ~FileHBB); - ei.kingAttackersCount[WHITE] += - count_1s_max_15(ei.attackedBy[WHITE][PAWN] & ei.attackedBy[BLACK][KING])/2; - ei.attackedBy[BLACK][PAWN] = - ((pos.pawns(BLACK) >> 7) & ~FileABB) | ((pos.pawns(BLACK) >> 9) & ~FileHBB); - ei.kingAttackersCount[BLACK] += - count_1s_max_15(ei.attackedBy[BLACK][PAWN] & ei.attackedBy[WHITE][KING])/2; - - // Evaluate pieces: - for(Color c = WHITE; c <= BLACK; c++) { - Bitboard b; + // Initialize king attack bitboards and king attack zones for both sides + ei.attackedBy[WHITE][KING] = pos.piece_attacks(pos.king_square(WHITE)); + ei.attackedBy[BLACK][KING] = pos.piece_attacks(pos.king_square(BLACK)); + ei.kingZone[WHITE] = ei.attackedBy[BLACK][KING] | (ei.attackedBy[BLACK][KING] >> 8); + ei.kingZone[BLACK] = ei.attackedBy[WHITE][KING] | (ei.attackedBy[WHITE][KING] << 8); + + // Initialize pawn attack bitboards for both sides + ei.attackedBy[WHITE][PAWN] = ((pos.pawns(WHITE) << 9) & ~FileABB) | ((pos.pawns(WHITE) << 7) & ~FileHBB); + ei.attackedBy[BLACK][PAWN] = ((pos.pawns(BLACK) >> 7) & ~FileABB) | ((pos.pawns(BLACK) >> 9) & ~FileHBB); + ei.kingAttackersCount[WHITE] = count_1s_max_15(ei.attackedBy[WHITE][PAWN] & ei.attackedBy[BLACK][KING])/2; + ei.kingAttackersCount[BLACK] = count_1s_max_15(ei.attackedBy[BLACK][PAWN] & ei.attackedBy[WHITE][KING])/2; + // Evaluate pieces + for (Color c = WHITE; c <= BLACK; c++) + { // Knights - for(int i = 0; i < pos.knight_count(c); i++) { - s = pos.knight_list(c, i); - evaluate_knight(pos, s, c, ei); - } + for (int i = 0; i < pos.piece_count(c, KNIGHT); i++) + evaluate_knight(pos, pos.piece_list(c, KNIGHT, i), c, ei); // Bishops - for(int i = 0; i < pos.bishop_count(c); i++) { - s = pos.bishop_list(c, i); - evaluate_bishop(pos, s, c, ei); - } + for (int i = 0; i < pos.piece_count(c, BISHOP); i++) + evaluate_bishop(pos, pos.piece_list(c, BISHOP, i), c, ei); // Rooks - for(int i = 0; i < pos.rook_count(c); i++) { - s = pos.rook_list(c, i); - evaluate_rook(pos, s, c, ei); - } + for (int i = 0; i < pos.piece_count(c, ROOK); i++) + evaluate_rook(pos, pos.piece_list(c, ROOK, i), c, ei); // Queens - for(int i = 0; i < pos.queen_count(c); i++) { - s = pos.queen_list(c, i); - evaluate_queen(pos, s, c, ei); - } - - // Some special patterns: + for(int i = 0; i < pos.piece_count(c, QUEEN); i++) + evaluate_queen(pos, pos.piece_list(c, QUEEN, i), c, ei); - // Trapped bishops on a7/h7/a2/h2 - b = pos.bishops(c) & MaskA7H7[c]; - while(b) { - s = pop_1st_bit(&b); - evaluate_trapped_bishop_a7h7(pos, s, c, ei); + // Special pattern: trapped bishops on a7/h7/a2/h2 + Bitboard b = pos.bishops(c) & MaskA7H7[c]; + while (b) + { + Square s = pop_1st_bit(&b); + evaluate_trapped_bishop_a7h7(pos, s, c, ei); } - // Trapped bishops on a1/h1/a8/h8 in Chess960: - if(Chess960) { - b = pos.bishops(c) & MaskA1H1[c]; - while(b) { - s = pop_1st_bit(&b); - evaluate_trapped_bishop_a1h1(pos, s, c, ei); - } + // Special pattern: trapped bishops on a1/h1/a8/h8 in Chess960: + if (Chess960) + { + b = pos.bishops(c) & MaskA1H1[c]; + while (b) + { + Square s = pop_1st_bit(&b); + evaluate_trapped_bishop_a1h1(pos, s, c, ei); + } } - ei.attackedBy[c][0] = - ei.attackedBy[c][PAWN] | ei.attackedBy[c][KNIGHT] - | ei.attackedBy[c][BISHOP] | ei.attackedBy[c][ROOK] - | ei.attackedBy[c][QUEEN] | ei.attackedBy[c][KING]; + // Sum up all attacked squares + ei.attackedBy[c][0] = ei.attackedBy[c][PAWN] | ei.attackedBy[c][KNIGHT] + | ei.attackedBy[c][BISHOP] | ei.attackedBy[c][ROOK] + | ei.attackedBy[c][QUEEN] | ei.attackedBy[c][KING]; } // Kings. Kings are evaluated after all other pieces for both sides, // because we need complete attack information for all pieces when computing // the king safety evaluation. - for(Color c = WHITE; c <= BLACK; c++) { - s = pos.king_square(c); - evaluate_king(pos, s, c, ei); - } + for (Color c = WHITE; c <= BLACK; c++) + evaluate_king(pos, pos.king_square(c), c, ei); // Evaluate passed pawns. We evaluate passed pawns for both sides at once, // because we need to know which side promotes first in positions where // both sides have an unstoppable passed pawn. - if(ei.pi->passed_pawns()) - evaluate_passed_pawns(pos, ei); + if (ei.pi->passed_pawns()) + evaluate_passed_pawns(pos, ei); - // Middle-game specific evaluation terms - if(phase > PHASE_ENDGAME) { + Phase phase = pos.game_phase(); + // Middle-game specific evaluation terms + if (phase > PHASE_ENDGAME) + { // Pawn storms in positions with opposite castling. - if(square_file(pos.king_square(WHITE)) >= FILE_E && - square_file(pos.king_square(BLACK)) <= FILE_D) - ei.mgValue += - ei.pi->queenside_storm_value(WHITE) - - ei.pi->kingside_storm_value(BLACK); - else if(square_file(pos.king_square(WHITE)) <= FILE_D && - square_file(pos.king_square(BLACK)) >= FILE_E) - ei.mgValue += - ei.pi->kingside_storm_value(WHITE) - - ei.pi->queenside_storm_value(BLACK); + if ( square_file(pos.king_square(WHITE)) >= FILE_E + && square_file(pos.king_square(BLACK)) <= FILE_D) + + ei.mgValue += ei.pi->queenside_storm_value(WHITE) + - ei.pi->kingside_storm_value(BLACK); + + else if ( square_file(pos.king_square(WHITE)) <= FILE_D + && square_file(pos.king_square(BLACK)) >= FILE_E) + + ei.mgValue += ei.pi->kingside_storm_value(WHITE) + - ei.pi->queenside_storm_value(BLACK); } // Mobility @@ -431,46 +414,40 @@ Value evaluate(const Position &pos, EvalInfo &ei, int threadID) { // If we don't already have an unusual scale factor, check for opposite // colored bishop endgames, and use a lower scale for those: - if(phase < PHASE_MIDGAME && pos.opposite_colored_bishops() - && ((factor[WHITE] == SCALE_FACTOR_NORMAL && ei.egValue > Value(0)) || - (factor[BLACK] == SCALE_FACTOR_NORMAL && ei.egValue < Value(0)))) { - if(pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == - 2*BishopValueMidgame) { - // Only the two bishops - if(pos.pawn_count(WHITE) + pos.pawn_count(BLACK) == 1) { - // KBP vs KB with only a single pawn; almost certainly a draw. - if(factor[WHITE] == SCALE_FACTOR_NORMAL) - factor[WHITE] = ScaleFactor(8); - if(factor[BLACK] == SCALE_FACTOR_NORMAL) - factor[BLACK] = ScaleFactor(8); - } - else { - // At least two pawns - if(factor[WHITE] == SCALE_FACTOR_NORMAL) - factor[WHITE] = ScaleFactor(32); - if(factor[BLACK] == SCALE_FACTOR_NORMAL) - factor[BLACK] = ScaleFactor(32); + if ( phase < PHASE_MIDGAME + && pos.opposite_colored_bishops() + && ( (factor[WHITE] == SCALE_FACTOR_NORMAL && ei.egValue > Value(0)) + || (factor[BLACK] == SCALE_FACTOR_NORMAL && ei.egValue < Value(0)))) + { + ScaleFactor sf; + + // Only the two bishops ? + if ( pos.non_pawn_material(WHITE) == BishopValueMidgame + && pos.non_pawn_material(BLACK) == BishopValueMidgame) + { + // Check for KBP vs KB with only a single pawn that is almost + // certainly a draw or at least two pawns. + bool one_pawn = (pos.piece_count(WHITE, PAWN) + pos.piece_count(BLACK, PAWN) == 1); + sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32); } - } - else { - // Endgame with opposite-colored bishops, but also other pieces. - // Still a bit drawish, but not as drawish as with only the two - // bishops. - if(factor[WHITE] == SCALE_FACTOR_NORMAL) - factor[WHITE] = ScaleFactor(50); - if(factor[BLACK] == SCALE_FACTOR_NORMAL) - factor[BLACK] = ScaleFactor(50); - } + else + // Endgame with opposite-colored bishops, but also other pieces. Still + // a bit drawish, but not as drawish as with only the two bishops. + sf = ScaleFactor(50); + + if (factor[WHITE] == SCALE_FACTOR_NORMAL) + factor[WHITE] = sf; + if (factor[BLACK] == SCALE_FACTOR_NORMAL) + factor[BLACK] = sf; } // Interpolate between the middle game and the endgame score, and - // return: - Value value = scale_by_game_phase(ei.mgValue, ei.egValue, phase, factor); + // return + Color stm = pos.side_to_move(); + + Value v = Sign[stm] * scale_by_game_phase(ei.mgValue, ei.egValue, phase, factor); - if(ei.mateThreat[stm] != MOVE_NONE) - return 8 * QueenValueMidgame - Sign[stm] * value; - else - return Sign[stm] * value; + return (ei.mateThreat[stm] == MOVE_NONE ? v : 8 * QueenValueMidgame - v); } @@ -479,22 +456,18 @@ Value evaluate(const Position &pos, EvalInfo &ei, int threadID) { /// we should add scores from the pawn and material hash tables? Value quick_evaluate(const Position &pos) { - Color stm; - Value mgValue, egValue; - ScaleFactor factor[2] = {SCALE_FACTOR_NORMAL, SCALE_FACTOR_NORMAL}; - Phase phase; assert(pos.is_ok()); - stm = pos.side_to_move(); + static const + ScaleFactor sf[2] = {SCALE_FACTOR_NORMAL, SCALE_FACTOR_NORMAL}; - mgValue = pos.mg_value(); - egValue = pos.eg_value(); - phase = pos.game_phase(); + Value mgv = pos.mg_value(); + Value egv = pos.eg_value(); + Phase ph = pos.game_phase(); + Color stm = pos.side_to_move(); - Value value = scale_by_game_phase(mgValue, egValue, phase, factor); - - return Sign[stm] * value; + return Sign[stm] * scale_by_game_phase(mgv, egv, ph, sf); } @@ -528,9 +501,11 @@ void init_eval(int threads) { /// quit_eval() releases heap-allocated memory at program termination. void quit_eval() { - for(int i = 0; i < THREAD_MAX; i++) { - delete PawnTable[i]; - delete MaterialTable[i]; + + for (int i = 0; i < THREAD_MAX; i++) + { + delete PawnTable[i]; + delete MaterialTable[i]; } } @@ -538,32 +513,19 @@ void quit_eval() { /// read_weights() reads evaluation weights from the corresponding UCI /// parameters. -void read_weights(Color sideToMove) { - WeightMobilityMidgame = - compute_weight(get_option_value_int("Mobility (Middle Game)"), - WeightMobilityMidgameInternal); - WeightMobilityEndgame = - compute_weight(get_option_value_int("Mobility (Endgame)"), - WeightMobilityEndgameInternal); - WeightPawnStructureMidgame = - compute_weight(get_option_value_int("Pawn Structure (Middle Game)"), - WeightPawnStructureMidgameInternal); - WeightPawnStructureEndgame = - compute_weight(get_option_value_int("Pawn Structure (Endgame)"), - WeightPawnStructureEndgameInternal); - WeightPassedPawnsMidgame = - compute_weight(get_option_value_int("Passed Pawns (Middle Game)"), - WeightPassedPawnsMidgameInternal); - WeightPassedPawnsEndgame = - compute_weight(get_option_value_int("Passed Pawns (Endgame)"), - WeightPassedPawnsEndgameInternal); - WeightKingSafety[sideToMove] = - compute_weight(get_option_value_int("Cowardice"), WeightKingSafetyInternal); - WeightKingSafety[opposite_color(sideToMove)] = - compute_weight(get_option_value_int("Aggressiveness"), - WeightKingSafetyInternal); - WeightKingSafety[opposite_color(sideToMove)] = - (get_option_value_int("Aggressiveness") * 0x100) / 100; +void read_weights(Color us) { + + WeightMobilityMidgame = weight_option("Mobility (Middle Game)", WeightMobilityMidgameInternal); + WeightMobilityEndgame = weight_option("Mobility (Endgame)", WeightMobilityEndgameInternal); + WeightPawnStructureMidgame = weight_option("Pawn Structure (Middle Game)", WeightPawnStructureMidgameInternal); + WeightPawnStructureEndgame = weight_option("Pawn Structure (Endgame)", WeightPawnStructureEndgameInternal); + WeightPassedPawnsMidgame = weight_option("Passed Pawns (Middle Game)", WeightPassedPawnsMidgameInternal); + WeightPassedPawnsEndgame = weight_option("Passed Pawns (Endgame)", WeightPassedPawnsEndgameInternal); + + Color them = opposite_color(us); + + WeightKingSafety[us] = weight_option("Cowardice", WeightKingSafetyInternal); + WeightKingSafety[them] = weight_option("Aggressiveness", WeightKingOppSafetyInternal); init_safety(); } @@ -582,11 +544,11 @@ namespace { // King attack if (b & ei.kingZone[us]) { - ei.kingAttackersCount[us]++; - ei.kingAttackersWeight[us] += AttackWeight; - Bitboard bb = (b & ei.attackedBy[them][KING]); - if (bb) - ei.kingZoneAttacksCount[us] += count_1s_max_15(bb); + ei.kingAttackersCount[us]++; + ei.kingAttackersWeight[us] += AttackWeight; + Bitboard bb = (b & ei.attackedBy[them][KING]); + if (bb) + ei.kingAdjacentZoneAttacksCount[us] += count_1s_max_15(bb); } // Mobility @@ -607,7 +569,7 @@ namespace { if (v && (p.pawn_attacks(them, s) & p.pawns(us))) { bonus += v / 2; - if ( p.knight_count(them) == 0 + if ( p.piece_count(them, KNIGHT) == 0 && (SquaresByColorBB[square_color(s)] & p.bishops(them)) == EmptyBoardBB) bonus += v; } @@ -622,7 +584,7 @@ namespace { void evaluate_knight(const Position &p, Square s, Color us, EvalInfo &ei) { - Bitboard b = p.knight_attacks(s); + Bitboard b = p.piece_attacks(s); ei.attackedBy[us][KNIGHT] |= b; // King attack, mobility and outposts @@ -688,7 +650,7 @@ namespace { // king has lost right to castle if (mob > 6 || ei.pi->file_is_half_open(us, f)) return; - + Square ksq = p.king_square(us); if ( square_file(ksq) >= FILE_E @@ -717,7 +679,7 @@ namespace { void evaluate_queen(const Position &p, Square s, Color us, EvalInfo &ei) { - Bitboard b = p.queen_attacks(s); + Bitboard b = p.piece_attacks(s); ei.attackedBy[us][QUEEN] |= b; // King attack and mobility @@ -735,6 +697,10 @@ namespace { } } + inline Bitboard shiftRowsDown(const Bitboard& b, int num) { + + return b >> (num << 3); + } // evaluate_king() assigns bonuses and penalties to a king of a given // color on a given square. @@ -743,32 +709,37 @@ namespace { int shelter = 0, sign = Sign[us]; - // King shelter. - if(relative_rank(us, s) <= RANK_4) { - Bitboard pawns = p.pawns(us) & this_and_neighboring_files_bb(s); - Rank r = square_rank(s); - for(int i = 0; i < 3; i++) - shelter += count_1s_8bit(pawns >> ((r+(i+1)*sign) * 8)) * (64>>i); - ei.mgValue += sign * Value(shelter); + // King shelter + if (relative_rank(us, s) <= RANK_4) + { + Bitboard pawns = p.pawns(us) & this_and_neighboring_files_bb(s); + Rank r = square_rank(s); + for (int i = 1; i < 4; i++) + shelter += count_1s_8bit(shiftRowsDown(pawns, r+i*sign)) * (128>>i); + + ei.mgValue += sign * Value(shelter); } // King safety. This is quite complicated, and is almost certainly far // from optimally tuned. Color them = opposite_color(us); - if(p.queen_count(them) >= 1 && ei.kingAttackersCount[them] >= 2 - && p.non_pawn_material(them) >= QueenValueMidgame + RookValueMidgame - && ei.kingZoneAttacksCount[them]) { + if ( p.piece_count(them, QUEEN) >= 1 + && ei.kingAttackersCount[them] >= 2 + && p.non_pawn_material(them) >= QueenValueMidgame + RookValueMidgame + && ei.kingAdjacentZoneAttacksCount[them]) + { // Is it the attackers turn to move? bool sente = (them == p.side_to_move()); // Find the attacked squares around the king which has no defenders - // apart from the king itself: + // apart from the king itself Bitboard undefended = - ei.attacked_by(them) & ~ei.attacked_by(us, PAWN) - & ~ei.attacked_by(us, KNIGHT) & ~ei.attacked_by(us, BISHOP) - & ~ei.attacked_by(us, ROOK) & ~ei.attacked_by(us, QUEEN) - & ei.attacked_by(us, KING); + ei.attacked_by(them) & ~ei.attacked_by(us, PAWN) + & ~ei.attacked_by(us, KNIGHT) & ~ei.attacked_by(us, BISHOP) + & ~ei.attacked_by(us, ROOK) & ~ei.attacked_by(us, QUEEN) + & ei.attacked_by(us, KING); + Bitboard occ = p.occupied_squares(), b, b2; // Initialize the 'attackUnits' variable, which is used later on as an @@ -777,113 +748,134 @@ namespace { // undefended squares around the king, the square of the king, and the // quality of the pawn shelter. int attackUnits = - Min((ei.kingAttackersCount[them] * ei.kingAttackersWeight[them]) / 2, 25) - + (ei.kingZoneAttacksCount[them] + count_1s_max_15(undefended)) * 3 - + InitKingDanger[relative_square(us, s)] - shelter / 32; + Min((ei.kingAttackersCount[them] * ei.kingAttackersWeight[them]) / 2, 25) + + (ei.kingAdjacentZoneAttacksCount[them] + count_1s_max_15(undefended)) * 3 + + InitKingDanger[relative_square(us, s)] - shelter / 32; - // Analyse safe queen contact checks: + // Analyse safe queen contact checks b = undefended & ei.attacked_by(them, QUEEN) & ~p.pieces_of_color(them); - if(b) { + if (b) + { Bitboard attackedByOthers = - ei.attacked_by(them, PAWN) | ei.attacked_by(them, KNIGHT) - | ei.attacked_by(them, BISHOP) | ei.attacked_by(them, ROOK); + ei.attacked_by(them, PAWN) | ei.attacked_by(them, KNIGHT) + | ei.attacked_by(them, BISHOP) | ei.attacked_by(them, ROOK); + b &= attackedByOthers; - if(b) { + if (b) + { // The bitboard b now contains the squares available for safe queen // contact checks. int count = count_1s_max_15(b); - attackUnits += QueenContactCheckBonus * count * (sente? 2 : 1); + attackUnits += QueenContactCheckBonus * count * (sente ? 2 : 1); // Is there a mate threat? - if(QueenContactMates && !p.is_check()) { + if (QueenContactMates && !p.is_check()) + { Bitboard escapeSquares = - p.king_attacks(s) & ~p.pieces_of_color(us) & ~attackedByOthers; - while(b) { - Square from, to = pop_1st_bit(&b); - if(!(escapeSquares - & ~queen_attacks_bb(to, occ & clear_mask_bb(s)))) { - // We have a mate, unless the queen is pinned or there - // is an X-ray attack through the queen. - for(int i = 0; i < p.queen_count(them); i++) { - from = p.queen_list(them, i); - if(bit_is_set(p.queen_attacks(from), to) - && !bit_is_set(p.pinned_pieces(them), from) - && !(rook_attacks_bb(to, occ & clear_mask_bb(from)) - & p.rooks_and_queens(us)) - && !(rook_attacks_bb(to, occ & clear_mask_bb(from)) - & p.rooks_and_queens(us))) - ei.mateThreat[them] = make_move(from, to); + p.piece_attacks(s) & ~p.pieces_of_color(us) & ~attackedByOthers; + + while (b) + { + Square from, to = pop_1st_bit(&b); + if (!(escapeSquares & ~queen_attacks_bb(to, occ & clear_mask_bb(s)))) + { + // We have a mate, unless the queen is pinned or there + // is an X-ray attack through the queen. + for (int i = 0; i < p.piece_count(them, QUEEN); i++) + { + from = p.piece_list(them, QUEEN, i); + if ( bit_is_set(p.piece_attacks(from), to) + && !bit_is_set(p.pinned_pieces(them), from) + && !(rook_attacks_bb(to, occ & clear_mask_bb(from)) & p.rooks_and_queens(us)) + && !(rook_attacks_bb(to, occ & clear_mask_bb(from)) & p.rooks_and_queens(us))) + + ei.mateThreat[them] = make_move(from, to); + } } - } } } } } - // Analyse safe rook contact checks: - if(RookContactCheckBonus) { - b = undefended & ei.attacked_by(them, ROOK) & ~p.pieces_of_color(them); - if(b) { - Bitboard attackedByOthers = - ei.attacked_by(them, PAWN) | ei.attacked_by(them, KNIGHT) - | ei.attacked_by(them, BISHOP) | ei.attacked_by(them, QUEEN); - b &= attackedByOthers; - if(b) { - int count = count_1s_max_15(b); - attackUnits += (RookContactCheckBonus * count * (sente? 2 : 1)); + if (RookContactCheckBonus) + { + b = undefended & ei.attacked_by(them, ROOK) & ~p.pieces_of_color(them); + if (b) + { + Bitboard attackedByOthers = + ei.attacked_by(them, PAWN) | ei.attacked_by(them, KNIGHT) + | ei.attacked_by(them, BISHOP) | ei.attacked_by(them, QUEEN); + + b &= attackedByOthers; + if (b) + { + int count = count_1s_max_15(b); + attackUnits += (RookContactCheckBonus * count * (sente? 2 : 1)); + } } - } } - // Analyse safe distance checks: - if(QueenCheckBonus > 0 || RookCheckBonus > 0) { - b = p.rook_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us); - - // Queen checks - b2 = b & ei.attacked_by(them, QUEEN); - if(b2) attackUnits += QueenCheckBonus * count_1s_max_15(b2); - - // Rook checks - b2 = b & ei.attacked_by(them, ROOK); - if(b2) attackUnits += RookCheckBonus * count_1s_max_15(b2); + if (QueenCheckBonus > 0 || RookCheckBonus > 0) + { + b = p.piece_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us); + + // Queen checks + b2 = b & ei.attacked_by(them, QUEEN); + if( b2) + attackUnits += QueenCheckBonus * count_1s_max_15(b2); + + // Rook checks + b2 = b & ei.attacked_by(them, ROOK); + if (b2) + attackUnits += RookCheckBonus * count_1s_max_15(b2); } - if(QueenCheckBonus > 0 || BishopCheckBonus > 0) { - b = p.bishop_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us); - // Queen checks - b2 = b & ei.attacked_by(them, QUEEN); - if(b2) attackUnits += QueenCheckBonus * count_1s_max_15(b2); - - // Bishop checks - b2 = b & ei.attacked_by(them, BISHOP); - if(b2) attackUnits += BishopCheckBonus * count_1s_max_15(b2); + if (QueenCheckBonus > 0 || BishopCheckBonus > 0) + { + b = p.piece_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us); + + // Queen checks + b2 = b & ei.attacked_by(them, QUEEN); + if (b2) + attackUnits += QueenCheckBonus * count_1s_max_15(b2); + + // Bishop checks + b2 = b & ei.attacked_by(them, BISHOP); + if (b2) + attackUnits += BishopCheckBonus * count_1s_max_15(b2); } - if(KnightCheckBonus > 0) { - b = p.knight_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us); - // Knight checks - b2 = b & ei.attacked_by(them, KNIGHT); - if(b2) attackUnits += KnightCheckBonus * count_1s_max_15(b2); + if (KnightCheckBonus > 0) + { + b = p.piece_attacks(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us); + + // Knight checks + b2 = b & ei.attacked_by(them, KNIGHT); + if (b2) + attackUnits += KnightCheckBonus * count_1s_max_15(b2); } // Analyse discovered checks (only for non-pawns right now, consider // adding pawns later). - if(DiscoveredCheckBonus) { + if (DiscoveredCheckBonus) + { b = p.discovered_check_candidates(them) & ~p.pawns(); - if(b) - attackUnits += - DiscoveredCheckBonus * count_1s_max_15(b) * (sente? 2 : 1); + if (b) + attackUnits += DiscoveredCheckBonus * count_1s_max_15(b) * (sente? 2 : 1); } // Has a mate threat been found? We don't do anything here if the // side with the mating move is the side to move, because in that // case the mating side will get a huge bonus at the end of the main // evaluation function instead. - if(ei.mateThreat[them] != MOVE_NONE) - attackUnits += MateThreatBonus; + if (ei.mateThreat[them] != MOVE_NONE) + attackUnits += MateThreatBonus; // Ensure that attackUnits is between 0 and 99, in order to avoid array // out of bounds errors: - if(attackUnits < 0) attackUnits = 0; - if(attackUnits >= 100) attackUnits = 99; + if (attackUnits < 0) + attackUnits = 0; + + if (attackUnits >= 100) + attackUnits = 99; // Finally, extract the king safety score from the SafetyTable[] array. // Add the score to the evaluation, and also to ei.futilityMargin. The @@ -892,9 +884,11 @@ namespace { // capturing a single attacking piece can therefore result in a score // change far bigger than the value of the captured piece. Value v = apply_weight(SafetyTable[attackUnits], WeightKingSafety[us]); + ei.mgValue -= sign * v; - if(us == p.side_to_move()) - ei.futilityMargin += v; + + if (us == p.side_to_move()) + ei.futilityMargin += v; } } @@ -1000,7 +994,7 @@ namespace { // value if the other side has a rook or queen. if(square_file(s) == FILE_A || square_file(s) == FILE_H) { if(pos.non_pawn_material(them) == KnightValueMidgame - && pos.knight_count(them) == 1) + && pos.piece_count(them, KNIGHT) == 1) ebonus += ebonus / 4; else if(pos.rooks_and_queens(them)) ebonus -= ebonus / 4; @@ -1117,7 +1111,7 @@ namespace { // score, based on game phase. It also scales the return value by a // ScaleFactor array. - Value scale_by_game_phase(Value mv, Value ev, Phase ph, ScaleFactor sf[]) { + Value scale_by_game_phase(Value mv, Value ev, Phase ph, const ScaleFactor sf[]) { assert(mv > -VALUE_INFINITE && mv < VALUE_INFINITE); assert(ev > -VALUE_INFINITE && ev < VALUE_INFINITE); @@ -1154,6 +1148,13 @@ namespace { } + // helper used in read_weights() + int weight_option(const std::string& opt, int weight) { + + return compute_weight(get_option_value_int(opt), weight); + } + + // init_safety() initizes the king safety evaluation, based on UCI // parameters. It is called from read_weights().