internal void CreateCheckInfo(Position pos) { Color them = pos.sideToMove ^ 1; ksq = pos.pieceList[them][PieceTypeC.KING][0]; pinned = pos.pinned_pieces(); dcCandidates = pos.discovered_check_candidates(); checkSq[PieceTypeC.PAWN] = Utils.StepAttacksBB[((them << 3) | PieceTypeC.PAWN)][ksq]; checkSq[PieceTypeC.KNIGHT] = Utils.StepAttacksBB_KNIGHT[ksq]; #if X64 checkSq[PieceTypeC.BISHOP] = Utils.BAttacks[ksq][(((pos.occupied_squares & Utils.BMasks[ksq]) * Utils.BMagics[ksq]) >> Utils.BShifts[ksq])]; checkSq[PieceTypeC.ROOK] = Utils.RAttacks[ksq][(((pos.occupied_squares & Utils.RMasks[ksq]) * Utils.RMagics[ksq]) >> Utils.RShifts[ksq])]; #else checkSq[PieceTypeC.BISHOP] = pos.attacks_from_BISHOP(ksq); checkSq[PieceTypeC.ROOK] = pos.attacks_from_ROOK(ksq); #endif checkSq[PieceTypeC.QUEEN] = checkSq[PieceTypeC.BISHOP] | checkSq[PieceTypeC.ROOK]; checkSq[PieceTypeC.KING] = 0; }
// evaluate_king<>() assigns bonuses and penalties to a king of a given color private static int evaluate_king( int Us, bool Trace, Position pos, EvalInfo ei, ref Value marginsWHITE, ref Value marginsBLACK) { var Them = (Us == ColorC.WHITE ? ColorC.BLACK : ColorC.WHITE); ulong undefended, b, b1, b2, safe; int attackUnits; int kingScore; var ksq = pos.pieceList[Us][PieceTypeC.KING][0]; // King shelter and enemy pawns storm var score = ei.pi.king_safety(Us, pos, ksq); // King safety. This is quite complicated, and is almost certainly far // from optimally tuned. if (ei.kingAttackersCount[Them] >= 2 && (ei.kingAdjacentZoneAttacksCount[Them] != 0)) { // Find the attacked squares around the king which has no defenders // apart from the king itself undefended = ei.attackedBy[Them][0] & ei.attackedBy[Us][PieceTypeC.KING]; undefended &= ~(ei.attackedBy[Us][PieceTypeC.PAWN] | ei.attackedBy[Us][PieceTypeC.KNIGHT] | ei.attackedBy[Us][PieceTypeC.BISHOP] | ei.attackedBy[Us][PieceTypeC.ROOK] | ei.attackedBy[Us][PieceTypeC.QUEEN]); #if X64 // Initialize the 'attackUnits' variable, which is used later on as an // index to the KingDangerTable[] array. The initial value is based on // the number and types of the enemy's attacking pieces, the number of // attacked and undefended squares around our king, the square of the // king, and the quality of the pawn shelter. b = undefended - ((undefended >> 1) & 0x5555555555555555UL); b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits = Math.Min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + ((int)((b * 0x1111111111111111UL) >> 60))) + InitKingDanger[(ksq ^ (Us * 56))] - ((((score) + 32768) & ~0xffff) / 0x10000) / 32; // Analyse enemy's safe queen contact checks. First find undefended // squares around the king attacked by enemy queen... b = undefended & ei.attackedBy[Them][PieceTypeC.QUEEN] & ~(pos.byColorBB[Them]); if (b != 0) { // ...then remove squares not supported by another enemy piece b &= (ei.attackedBy[Them][PieceTypeC.PAWN] | ei.attackedBy[Them][PieceTypeC.KNIGHT] | ei.attackedBy[Them][PieceTypeC.BISHOP] | ei.attackedBy[Them][PieceTypeC.ROOK]); if (b != 0) { b -= (b >> 1) & 0x5555555555555555UL; b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits += QueenContactCheckBonus * ((int)((b * 0x1111111111111111UL) >> 60)) * (Them == pos.sideToMove ? 2 : 1); } } // Analyse enemy's safe rook contact checks. First find undefended // squares around the king attacked by enemy rooks... b = undefended & ei.attackedBy[Them][PieceTypeC.ROOK] & ~pos.byColorBB[Them]; // Consider only squares where the enemy rook gives check b &= Utils.PseudoAttacks_ROOK[ksq]; if (b != 0) { // ...then remove squares not supported by another enemy piece b &= (ei.attackedBy[Them][PieceTypeC.PAWN] | ei.attackedBy[Them][PieceTypeC.KNIGHT] | ei.attackedBy[Them][PieceTypeC.BISHOP] | ei.attackedBy[Them][PieceTypeC.QUEEN]); if (b != 0) { b -= (b >> 1) & 0x5555555555555555UL; b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits += RookContactCheckBonus * ((int)((b * 0x1111111111111111UL) >> 60)) * (Them == pos.sideToMove ? 2 : 1); } } // Analyse enemy's safe distance checks for sliders and knights safe = ~(pos.byColorBB[Them] | ei.attackedBy[Us][0]); b1 = (Utils.RAttacks[ksq][(((pos.occupied_squares & Utils.RMasks[ksq]) * Utils.RMagics[ksq]) >> Utils.RShifts[ksq])]) & safe; b2 = (Utils.BAttacks[ksq][(((pos.occupied_squares & Utils.BMasks[ksq]) * Utils.BMagics[ksq]) >> Utils.BShifts[ksq])]) & safe; // Enemy queen safe checks b = (b1 | b2) & ei.attackedBy[Them][PieceTypeC.QUEEN]; if (b != 0) { b -= (b >> 1) & 0x5555555555555555UL; b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits += QueenCheckBonus * ((int)((b * 0x1111111111111111UL) >> 60)); } // Enemy rooks safe checks b = b1 & ei.attackedBy[Them][PieceTypeC.ROOK]; if (b != 0) { b -= (b >> 1) & 0x5555555555555555UL; b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits += RookCheckBonus * ((int)((b * 0x1111111111111111UL) >> 60)); } // Enemy bishops safe checks b = b2 & ei.attackedBy[Them][PieceTypeC.BISHOP]; if (b != 0) { b -= (b >> 1) & 0x5555555555555555UL; b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits += BishopCheckBonus * ((int)((b * 0x1111111111111111UL) >> 60)); } // Enemy knights safe checks b = Utils.StepAttacksBB_KNIGHT[ksq] & ei.attackedBy[Them][PieceTypeC.KNIGHT] & safe; if (b != 0) { b -= (b >> 1) & 0x5555555555555555UL; b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits += KnightCheckBonus * ((int)((b * 0x1111111111111111UL) >> 60)); } #else // Initialize the 'attackUnits' variable, which is used later on as an // index to the KingDangerTable[] array. The initial value is based on // the number and types of the enemy's attacking pieces, the number of // attacked and undefended squares around our king, the square of the // king, and the quality of the pawn shelter. attackUnits = Math.Min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + Bitcount.popcount_1s_Max15(undefended)) + InitKingDanger[(ksq ^ (Us * 56))] - ((((score) + 32768) & ~0xffff) / 0x10000) / 32; // Analyse enemy's safe queen contact checks. First find undefended // squares around the king attacked by enemy queen... b = undefended & ei.attackedBy[Them][PieceTypeC.QUEEN] & ~(pos.byColorBB[Them]); if (b != 0) { // ...then remove squares not supported by another enemy piece b &= (ei.attackedBy[Them][PieceTypeC.PAWN] | ei.attackedBy[Them][PieceTypeC.KNIGHT] | ei.attackedBy[Them][PieceTypeC.BISHOP] | ei.attackedBy[Them][PieceTypeC.ROOK]); if (b != 0) { attackUnits += QueenContactCheckBonus * Bitcount.popcount_1s_Max15(b) * (Them == pos.sideToMove ? 2 : 1); } } // Analyse enemy's safe rook contact checks. First find undefended // squares around the king attacked by enemy rooks... b = undefended & ei.attackedBy[Them][PieceTypeC.ROOK] & ~pos.byColorBB[Them]; // Consider only squares where the enemy rook gives check b &= Utils.PseudoAttacks_ROOK[ksq]; if (b != 0) { // ...then remove squares not supported by another enemy piece b &= (ei.attackedBy[Them][PieceTypeC.PAWN] | ei.attackedBy[Them][PieceTypeC.KNIGHT] | ei.attackedBy[Them][PieceTypeC.BISHOP] | ei.attackedBy[Them][PieceTypeC.QUEEN]); if (b != 0) { attackUnits += RookContactCheckBonus * Bitcount.popcount_1s_Max15(b) * (Them == pos.sideToMove ? 2 : 1); } } // Analyse enemy's safe distance checks for sliders and knights safe = ~(pos.byColorBB[Them] | ei.attackedBy[Us][0]); b1 = pos.attacks_from_ROOK(ksq) & safe; b2 = pos.attacks_from_BISHOP(ksq) & safe; // Enemy queen safe checks b = (b1 | b2) & ei.attackedBy[Them][PieceTypeC.QUEEN]; if (b != 0) { attackUnits += QueenCheckBonus * Bitcount.popcount_1s_Max15(b); } // Enemy rooks safe checks b = b1 & ei.attackedBy[Them][PieceTypeC.ROOK]; if (b != 0) { attackUnits += RookCheckBonus * Bitcount.popcount_1s_Max15(b); } // Enemy bishops safe checks b = b2 & ei.attackedBy[Them][PieceTypeC.BISHOP]; if (b != 0) { attackUnits += BishopCheckBonus * Bitcount.popcount_1s_Max15(b); } // Enemy knights safe checks b = Utils.StepAttacksBB_KNIGHT[ksq] & ei.attackedBy[Them][PieceTypeC.KNIGHT] & safe; if (b != 0) { attackUnits += KnightCheckBonus * Bitcount.popcount_1s_Max15(b); } #endif // To index KingDangerTable[] attackUnits must be in [0, 99] range attackUnits = Math.Min(99, Math.Max(0, attackUnits)); // Finally, extract the king danger score from the KingDangerTable[] // array and subtract the score from evaluation. Set also margins[] // value that will be used for pruning because this value can sometimes // be very big, and so capturing a single attacking piece can therefore // result in a score change far bigger than the value of the captured piece. kingScore = KingDangerTable[Us == Search.RootColor ? 1 : 0][attackUnits]; score -= kingScore; if (Us == ColorC.WHITE) { marginsWHITE += (((kingScore + 32768) & ~0xffff) / 0x10000); } else { marginsBLACK += (((kingScore + 32768) & ~0xffff) / 0x10000); } } if (Trace) { TracedScores[Us][PieceTypeC.KING] = score; } return score; }
/// K, bishop and two pawns vs K and bishop. It detects a few basic draws with /// opposite-colored bishops. internal static ScaleFactor Endgame_KBPPKB(Color strongerSide, Position pos) { Color weakerSide = strongerSide ^ 1; Debug.Assert(pos.non_pawn_material(strongerSide) == Constants.BishopValueMidgame); Debug.Assert(pos.piece_count(strongerSide, PieceTypeC.BISHOP) == 1); Debug.Assert(pos.piece_count(strongerSide, PieceTypeC.PAWN) == 2); Debug.Assert(pos.non_pawn_material(weakerSide) == Constants.BishopValueMidgame); Debug.Assert(pos.piece_count(weakerSide, PieceTypeC.BISHOP) == 1); Debug.Assert(pos.piece_count(weakerSide, PieceTypeC.PAWN) == 0); Square wbsq = pos.pieceList[strongerSide][PieceTypeC.BISHOP][0]; Square bbsq = pos.pieceList[weakerSide][PieceTypeC.BISHOP][0]; if (!Utils.opposite_colors(wbsq, bbsq)) return ScaleFactorC.SCALE_FACTOR_NONE; Square ksq = pos.king_square(weakerSide); Square psq1 = pos.pieceList[strongerSide][PieceTypeC.PAWN][0]; Square psq2 = pos.pieceList[strongerSide][PieceTypeC.PAWN][1]; Rank r1 = Utils.rank_of(psq1); Rank r2 = Utils.rank_of(psq2); Square blockSq1, blockSq2; if (Utils.relative_rank_CS(strongerSide, psq1) > Utils.relative_rank_CS(strongerSide, psq2)) { blockSq1 = psq1 + Utils.pawn_push(strongerSide); blockSq2 = Utils.make_square(Utils.file_of(psq2), Utils.rank_of(psq1)); } else { blockSq1 = psq2 + Utils.pawn_push(strongerSide); blockSq2 = Utils.make_square(Utils.file_of(psq1), Utils.rank_of(psq2)); } switch (Utils.file_distance(psq1, psq2)) { case 0: // Both pawns are on the same file. Easy draw if defender firmly controls // some square in the frontmost pawn's path. if (Utils.file_of(ksq) == Utils.file_of(blockSq1) && Utils.relative_rank_CS(strongerSide, ksq) >= Utils.relative_rank_CS(strongerSide, blockSq1) && Utils.opposite_colors(ksq, wbsq)) return ScaleFactorC.SCALE_FACTOR_DRAW; else return ScaleFactorC.SCALE_FACTOR_NONE; case 1: // Pawns on adjacent files. Draw if defender firmly controls the square // in front of the frontmost pawn's path, and the square diagonally behind // this square on the file of the other pawn. if (ksq == blockSq1 && Utils.opposite_colors(ksq, wbsq) && (bbsq == blockSq2 || (((pos.attacks_from_BISHOP(blockSq2) & pos.pieces_PTC(PieceTypeC.BISHOP, weakerSide))) != 0) || Math.Abs(r1 - r2) >= 2)) return ScaleFactorC.SCALE_FACTOR_DRAW; else if (ksq == blockSq2 && Utils.opposite_colors(ksq, wbsq) && (bbsq == blockSq1 || (((pos.attacks_from_BISHOP(blockSq1) & pos.pieces_PTC(PieceTypeC.BISHOP, weakerSide)))) != 0)) return ScaleFactorC.SCALE_FACTOR_DRAW; else return ScaleFactorC.SCALE_FACTOR_NONE; default: // The pawns are not on the same file or adjacent files. No scaling. return ScaleFactorC.SCALE_FACTOR_NONE; } }
/// K, bishop and a pawn vs K and a bishop. There are two rules: If the defending /// king is somewhere along the path of the pawn, and the square of the king is /// not of the same color as the stronger side's bishop, it's a draw. If the two /// bishops have opposite color, it's almost always a draw. internal static ScaleFactor Endgame_KBPKB(Color strongerSide, Position pos) { Color weakerSide = strongerSide ^ 1; Debug.Assert(pos.non_pawn_material(strongerSide) == Constants.BishopValueMidgame); Debug.Assert(pos.piece_count(strongerSide, PieceTypeC.BISHOP) == 1); Debug.Assert(pos.piece_count(strongerSide, PieceTypeC.PAWN) == 1); Debug.Assert(pos.non_pawn_material(weakerSide) == Constants.BishopValueMidgame); Debug.Assert(pos.piece_count(weakerSide, PieceTypeC.BISHOP) == 1); Debug.Assert(pos.piece_count(weakerSide, PieceTypeC.PAWN) == 0); Square pawnSq = pos.pieceList[strongerSide][PieceTypeC.PAWN][0]; Square strongerBishopSq = pos.pieceList[strongerSide][PieceTypeC.BISHOP][0]; Square weakerBishopSq = pos.pieceList[weakerSide][PieceTypeC.BISHOP][0]; Square weakerKingSq = pos.king_square(weakerSide); // Case 1: Defending king blocks the pawn, and cannot be driven away if (Utils.file_of(weakerKingSq) == Utils.file_of(pawnSq) && Utils.relative_rank_CS(strongerSide, pawnSq) < Utils.relative_rank_CS(strongerSide, weakerKingSq) && (Utils.opposite_colors(weakerKingSq, strongerBishopSq) || Utils.relative_rank_CS(strongerSide, weakerKingSq) <= RankC.RANK_6)) return ScaleFactorC.SCALE_FACTOR_DRAW; // Case 2: Opposite colored bishops if (Utils.opposite_colors(strongerBishopSq, weakerBishopSq)) { // We assume that the position is drawn in the following three situations: // // a. The pawn is on rank 5 or further back. // b. The defending king is somewhere in the pawn's path. // c. The defending bishop attacks some square along the pawn's path, // and is at least three squares away from the pawn. // // These rules are probably not perfect, but in practice they work // reasonably well. if (Utils.relative_rank_CS(strongerSide, pawnSq) <= RankC.RANK_5) return ScaleFactorC.SCALE_FACTOR_DRAW; else { Bitboard path = Utils.forward_bb(strongerSide, pawnSq); if ((path & pos.pieces_PTC(PieceTypeC.KING, weakerSide)) != 0) return ScaleFactorC.SCALE_FACTOR_DRAW; if (((pos.attacks_from_BISHOP(weakerBishopSq) & path) != 0) && Utils.square_distance(weakerBishopSq, pawnSq) >= 3) return ScaleFactorC.SCALE_FACTOR_DRAW; } } return ScaleFactorC.SCALE_FACTOR_NONE; }