/// KBP vs KB. 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. public ScaleFactor KBPKB(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.BishopValueMg, 1)); Debug.Assert(verify_material(pos, weakSide, ValueS.BishopValueMg, 0)); Square pawnSq = pos.list(strongSide, PieceTypeS.PAWN)[0]; Square strongerBishopSq = pos.list(strongSide, PieceTypeS.BISHOP)[0]; Square weakerBishopSq = pos.list(weakSide, PieceTypeS.BISHOP)[0]; Square weakerKingSq = pos.king_square(weakSide); // Case 1: Defending king blocks the pawn, and cannot be driven away if (Types.file_of(weakerKingSq) == Types.file_of(pawnSq) && Types.relative_rank_square(strongSide, pawnSq) < Types.relative_rank_square(strongSide, weakerKingSq) && (Types.opposite_colors(weakerKingSq, strongerBishopSq) || Types.relative_rank_square(strongSide, weakerKingSq) <= RankS.RANK_6)) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } // Case 2: Opposite colored bishops if (Types.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 (Types.relative_rank_square(strongSide, pawnSq) <= RankS.RANK_5) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } else { Bitboard path = BitBoard.forward_bb(strongSide, pawnSq); if ((path & pos.pieces_color_piecetype(weakSide, PieceTypeS.KING)) != 0) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } if (((pos.attacks_from_square_piecetype(weakerBishopSq, PieceTypeS.BISHOP) & path) != 0) && BitBoard.square_distance(weakerBishopSq, pawnSq) >= 3) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } } } return(ScaleFactorS.SCALE_FACTOR_NONE); }
/// KNP vs KB. If knight can block bishop from taking pawn, it's a win. /// Otherwise the position is drawn. public ScaleFactor KNPKB(Position pos) { Square pawnSq = pos.list(strongSide, PieceTypeS.PAWN)[0]; Square bishopSq = pos.list(weakSide, PieceTypeS.BISHOP)[0]; Square weakerKingSq = pos.king_square(weakSide); // King needs to get close to promoting pawn to prevent knight from blocking. // Rules for this are very tricky, so just approximate. if ((BitBoard.forward_bb(strongSide, pawnSq) & pos.attacks_from_square_piecetype(bishopSq, PieceTypeS.BISHOP)) != 0) { return(BitBoard.square_distance(weakerKingSq, pawnSq)); } return(ScaleFactorS.SCALE_FACTOR_NONE); }
public static Score evaluate(Position pos, Pawns.Entry e, Color Us) { Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); Square Up = (Us == ColorS.WHITE ? SquareS.DELTA_N : SquareS.DELTA_S); Square Right = (Us == ColorS.WHITE ? SquareS.DELTA_NE : SquareS.DELTA_SW); Square Left = (Us == ColorS.WHITE ? SquareS.DELTA_NW : SquareS.DELTA_SE); Bitboard b, p, doubled; Square s; File f; Rank r; bool passed, isolated, opposed, connected, backward, candidate, unsupported; Score value = ScoreS.SCORE_ZERO; Square[] pl = pos.list(Us, PieceTypeS.PAWN); int plPos = 0; Bitboard ourPawns = pos.pieces_color_piecetype(Us, PieceTypeS.PAWN); Bitboard theirPawns = pos.pieces_color_piecetype(Them, PieceTypeS.PAWN); e.passedPawns[Us] = e.candidatePawns[Us] = 0; e.kingSquares[Us] = SquareS.SQ_NONE; e.semiopenFiles[Us] = 0xFF; e.pawnAttacks[Us] = BitBoard.shift_bb(ourPawns, Right) | BitBoard.shift_bb(ourPawns, Left); e.pawnsOnSquares[Us][ColorS.BLACK] = Bitcount.popcount_Max15(ourPawns & BitBoard.DarkSquares); e.pawnsOnSquares[Us][ColorS.WHITE] = pos.count(Us, PieceTypeS.PAWN) - e.pawnsOnSquares[Us][ColorS.BLACK]; // Loop through all pawns of the current color and score each pawn while ((s = pl[plPos++]) != SquareS.SQ_NONE) { Debug.Assert(pos.piece_on(s) == Types.make_piece(Us, PieceTypeS.PAWN)); f = Types.file_of(s); // This file cannot be semi-open e.semiopenFiles[Us] &= ~(1 << f); // Previous rank p = BitBoard.rank_bb_square(s - Types.pawn_push(Us)); // Our rank plus previous one b = BitBoard.rank_bb_square(s) | p; // Flag the pawn as passed, isolated, doubled, // unsupported or connected (but not the backward one). connected = (ourPawns & BitBoard.adjacent_files_bb(f) & b) != 0; unsupported = (0 == (ourPawns & BitBoard.adjacent_files_bb(f) & p)); isolated = (0 == (ourPawns & BitBoard.adjacent_files_bb(f))); doubled = ourPawns & BitBoard.forward_bb(Us, s); opposed = (theirPawns & BitBoard.forward_bb(Us, s)) != 0; passed = (0 == (theirPawns & BitBoard.passed_pawn_mask(Us, s))); // Test for backward pawn. // If the pawn is passed, isolated, or connected it cannot be // backward. If there are friendly pawns behind on adjacent files // or if it can capture an enemy pawn it cannot be backward either. if ((passed | isolated | connected) || (ourPawns & BitBoard.pawn_attack_span(Them, s)) != 0 || (pos.attacks_from_pawn(s, Us) & theirPawns) != 0) { backward = false; } else { // We now know that there are no friendly pawns beside or behind this // pawn on adjacent files. We now check whether the pawn is // backward by looking in the forward direction on the adjacent // files, and picking the closest pawn there. b = BitBoard.pawn_attack_span(Us, s) & (ourPawns | theirPawns); b = BitBoard.pawn_attack_span(Us, s) & BitBoard.rank_bb_square(BitBoard.backmost_sq(Us, b)); // If we have an enemy pawn in the same or next rank, the pawn is // backward because it cannot advance without being captured. backward = ((b | BitBoard.shift_bb(b, Up)) & theirPawns) != 0; } Debug.Assert(opposed | passed | (BitBoard.pawn_attack_span(Us, s) & theirPawns) != 0); // A not-passed pawn is a candidate to become passed, if it is free to // advance and if the number of friendly pawns beside or behind this // pawn on adjacent files is higher than or equal to the number of // enemy pawns in the forward direction on the adjacent files. candidate = !(opposed | passed | backward | isolated) && (b = BitBoard.pawn_attack_span(Them, s + Types.pawn_push(Us)) & ourPawns) != 0 && Bitcount.popcount_Max15(b) >= Bitcount.popcount_Max15(BitBoard.pawn_attack_span(Us, s) & theirPawns); // Passed pawns will be properly scored in evaluation because we need // full attack info to evaluate passed pawns. Only the frontmost passed // pawn on each file is considered a true passed pawn. if (passed && 0 == doubled) { e.passedPawns[Us] |= BitBoard.SquareBB[s]; } // Score this pawn if (isolated) { value -= Isolated[opposed ? 1 : 0][f]; } if (unsupported && !isolated) { value -= UnsupportedPawnPenalty; } if (doubled != 0) { value -= Types.divScore(Doubled[f], BitBoard.rank_distance(s, BitBoard.lsb(doubled))); } if (backward) { value -= Backward[opposed ? 1 : 0][f]; } if (connected) { value += Connected[f][Types.relative_rank_square(Us, s)]; } if (candidate) { value += CandidatePassed[Types.relative_rank_square(Us, s)]; if (0 == doubled) { e.candidatePawns[Us] |= BitBoard.SquareBB[s]; } } } // In endgame it's better to have pawns on both wings. So give a bonus according // to file distance between left and right outermost pawns. if (pos.count(Us, PieceTypeS.PAWN) > 1) { b = (Bitboard)(e.semiopenFiles[Us] ^ 0xFF); value += PawnsFileSpan * (BitBoard.msb(b) - BitBoard.lsb(b)); } return(value); }
// evaluate_passed_pawns() evaluates the passed pawns of the given color public static Score evaluate_passed_pawns(Position pos, EvalInfo ei, Color Us, bool Trace) { Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); Bitboard b, squaresToQueen, defendedSquares, unsafeSquares; Score score = ScoreS.SCORE_ZERO; b = ei.pi.passed_pawns(Us); while (b != 0) { Square s = BitBoard.pop_lsb(ref b); Debug.Assert(pos.pawn_passed(Us, s)); int r = (int)(Types.relative_rank_square(Us, s) - RankS.RANK_2); int rr = r * (r - 1); // Base bonus based on rank Value mbonus = (17 * rr), ebonus = (7 * (rr + r + 1)); if (rr != 0) { Square blockSq = s + Types.pawn_push(Us); // Adjust bonus based on kings proximity ebonus += (BitBoard.square_distance(pos.king_square(Them), blockSq) * 5 * rr) - (BitBoard.square_distance(pos.king_square(Us), blockSq) * 2 * rr); // If blockSq is not the queening square then consider also a second push if (Types.relative_rank_square(Us, blockSq) != RankS.RANK_8) { ebonus -= (BitBoard.square_distance(pos.king_square(Us), blockSq + Types.pawn_push(Us)) * rr); } // If the pawn is free to advance, increase bonus if (pos.empty(blockSq)) { squaresToQueen = BitBoard.forward_bb(Us, s); // If there is an enemy rook or queen attacking the pawn from behind, // add all X-ray attacks by the rook or queen. Otherwise consider only // the squares in the pawn's path attacked or occupied by the enemy. if ((BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Them, PieceTypeS.ROOK, PieceTypeS.QUEEN)) != 0 && (BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Them, PieceTypeS.ROOK, PieceTypeS.QUEEN) & pos.attacks_from_square_piecetype(s, PieceTypeS.ROOK)) != 0) { unsafeSquares = squaresToQueen; } else { unsafeSquares = squaresToQueen & (ei.attackedBy[Them][PieceTypeS.ALL_PIECES] | pos.pieces_color(Them)); } if ((BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Us, PieceTypeS.ROOK, PieceTypeS.QUEEN)) != 0 && (BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Us, PieceTypeS.ROOK, PieceTypeS.QUEEN) & pos.attacks_from_square_piecetype(s, PieceTypeS.ROOK)) != 0) { defendedSquares = squaresToQueen; } else { defendedSquares = squaresToQueen & ei.attackedBy[Us][PieceTypeS.ALL_PIECES]; } // If there aren't any enemy attacks, assign a big bonus. Otherwise // assign a smaller bonus if the block square isn't attacked. int k = 0 == unsafeSquares? 15 : 0 == (unsafeSquares & BitBoard.SquareBB[blockSq]) ? 9 : 0; // If the path to queen is fully defended, assign a big bonus. // Otherwise assign a smaller bonus if the block square is defended. if (defendedSquares == squaresToQueen) { k += 6; } else if ((defendedSquares & BitBoard.SquareBB[blockSq]) != 0) { k += 4; } mbonus += (k * rr); ebonus += (k * rr); } } // rr != 0 if (pos.count(Us, PieceTypeS.PAWN) < pos.count(Them, PieceTypeS.PAWN)) { ebonus += ebonus / 4; } score += Types.make_score(mbonus, ebonus); } if (Trace) { Tracing.terms[Us][TermsS.PASSED] = apply_weight(score, Weights[EvalWeightS.PassedPawns]); } // Add the scores to the middle game and endgame eval return(Eval.apply_weight(score, Weights[EvalWeightS.PassedPawns])); }