/// 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); }
public ScaleFactor KRPKB(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 1)); Debug.Assert(verify_material(pos, weakSide, ValueS.BishopValueMg, 0)); // Test for a rook pawn if ((pos.pieces_piecetype(PieceTypeS.PAWN) & (BitBoard.FileABB | BitBoard.FileHBB)) != 0) { Square ksq = pos.king_square(weakSide); Square bsq = pos.list(weakSide, PieceTypeS.BISHOP)[0]; Square psq = pos.list(strongSide, PieceTypeS.PAWN)[0]; Rank rk = Types.relative_rank_square(strongSide, psq); Square push = Types.pawn_push(strongSide); // If the pawn is on the 5th rank and the pawn (currently) is on // the same color square as the bishop then there is a chance of // a fortress. Depending on the king position give a moderate // reduction or a stronger one if the defending king is near the // corner but not trapped there. if (rk == RankS.RANK_5 && !Types.opposite_colors(bsq, psq)) { int d = BitBoard.square_distance(psq + 3 * push, ksq); if (d <= 2 && !(d == 0 && ksq == pos.king_square(strongSide) + 2 * push)) { return(24); } else { return(48); } } // When the pawn has moved to the 6th rank we can be fairly sure // it's drawn if the bishop attacks the square in front of the // pawn from a reasonable distance and the defending king is near // the corner if (rk == RankS.RANK_6 && BitBoard.square_distance(psq + 2 * push, ksq) <= 1 && (BitBoard.PseudoAttacks[PieceTypeS.BISHOP][bsq] & BitBoard.SquareBB[(psq + push)]) != 0 && BitBoard.file_distance(bsq, psq) >= 2) { return(8); } } return(ScaleFactorS.SCALE_FACTOR_NONE); }
/// KBP vs KN. There is a single rule: 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. public ScaleFactor KBPKN(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.BishopValueMg, 1)); Debug.Assert(verify_material(pos, weakSide, ValueS.KnightValueMg, 0)); Square pawnSq = pos.list(strongSide, PieceTypeS.PAWN)[0]; Square strongerBishopSq = pos.list(strongSide, PieceTypeS.BISHOP)[0]; Square weakerKingSq = pos.king_square(weakSide); 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); } return(ScaleFactorS.SCALE_FACTOR_NONE); }
/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the /// defending king towards a corner square of the right color. public Value KBNK(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.KnightValueMg + ValueS.BishopValueMg, 0)); Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 0)); Square winnerKSq = pos.king_square(strongSide); Square loserKSq = pos.king_square(weakSide); Square bishopSq = pos.list(strongSide, PieceTypeS.BISHOP)[0]; // kbnk_mate_table() tries to drive toward corners A1 or H8. If we have a // bishop that cannot reach the above squares, we flip the kings in order // to drive the enemy toward corners A8 or H1. if (Types.opposite_colors(bishopSq, SquareS.SQ_A1)) { winnerKSq = Types.notSquare(winnerKSq); loserKSq = Types.notSquare(loserKSq); } Value result = ValueS.VALUE_KNOWN_WIN + PushClose[BitBoard.square_distance(winnerKSq, loserKSq)] + PushToCorners[loserKSq]; return(strongSide == pos.side_to_move() ? result : -result); }
/// KBPP vs KB. It detects a few basic draws with opposite-colored bishops public ScaleFactor KBPPKB(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.BishopValueMg, 2)); Debug.Assert(verify_material(pos, weakSide, ValueS.BishopValueMg, 0)); Square wbsq = pos.list(strongSide, PieceTypeS.BISHOP)[0]; Square bbsq = pos.list(weakSide, PieceTypeS.BISHOP)[0]; if (!Types.opposite_colors(wbsq, bbsq)) { return(ScaleFactorS.SCALE_FACTOR_NONE); } Square ksq = pos.king_square(weakSide); Square psq1 = pos.list(strongSide, PieceTypeS.PAWN)[0]; Square psq2 = pos.list(strongSide, PieceTypeS.PAWN)[1]; Rank r1 = Types.rank_of(psq1); Rank r2 = Types.rank_of(psq2); Square blockSq1, blockSq2; if (Types.relative_rank_square(strongSide, psq1) > Types.relative_rank_square(strongSide, psq2)) { blockSq1 = psq1 + Types.pawn_push(strongSide); blockSq2 = Types.make_square(Types.file_of(psq2), Types.rank_of(psq1)); } else { blockSq1 = psq2 + Types.pawn_push(strongSide); blockSq2 = Types.make_square(Types.file_of(psq1), Types.rank_of(psq2)); } switch (BitBoard.file_distance(psq1, psq2)) { case 0: // Both pawns are on the same file. It's an easy draw if the defender firmly // controls some square in the frontmost pawn's path. if (Types.file_of(ksq) == Types.file_of(blockSq1) && Types.relative_rank_square(strongSide, ksq) >= Types.relative_rank_square(strongSide, blockSq1) && Types.opposite_colors(ksq, wbsq)) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } else { return(ScaleFactorS.SCALE_FACTOR_NONE); } case 1: // Pawns on adjacent files. It's a draw if the 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 && Types.opposite_colors(ksq, wbsq) && (bbsq == blockSq2 || (pos.attacks_from_square_piecetype(blockSq2, PieceTypeS.BISHOP) & pos.pieces_color_piecetype(weakSide, PieceTypeS.BISHOP)) != 0 || Math.Abs(r1 - r2) >= 2)) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } else if (ksq == blockSq2 && Types.opposite_colors(ksq, wbsq) && (bbsq == blockSq1 || (pos.attacks_from_square_piecetype(blockSq1, PieceTypeS.BISHOP) & pos.pieces_color_piecetype(weakSide, PieceTypeS.BISHOP)) != 0)) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } else { return(ScaleFactorS.SCALE_FACTOR_NONE); } default: // The pawns are not on the same file or adjacent files. No scaling. return(ScaleFactorS.SCALE_FACTOR_NONE); } }
/// KB and one or more pawns vs K. It checks for draws with rook pawns and /// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW /// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling /// will be used. public ScaleFactor KBPsK(Position pos) { Debug.Assert(pos.non_pawn_material(strongSide) == ValueS.BishopValueMg); Debug.Assert(pos.count(strongSide, PieceTypeS.PAWN) >= 1); // No assertions about the material of weakSide, because we want draws to // be detected even when the weaker side has some pawns. Bitboard pawns = pos.pieces_color_piecetype(strongSide, PieceTypeS.PAWN); File pawnFile = Types.file_of(pos.list(strongSide, PieceTypeS.PAWN)[0]); // All pawns are on a single rook file ? if ((pawnFile == FileS.FILE_A || pawnFile == FileS.FILE_H) && 0 == (pawns & ~BitBoard.file_bb_file(pawnFile))) { Square bishopSq = pos.list(strongSide, PieceTypeS.BISHOP)[0]; Square queeningSq = Types.relative_square(strongSide, Types.make_square(pawnFile, RankS.RANK_8)); Square kingSq = pos.king_square(weakSide); if (Types.opposite_colors(queeningSq, bishopSq) && BitBoard.square_distance(queeningSq, kingSq) <= 1) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } } // If all the pawns are on the same B or G file, then it's potentially a draw if ((pawnFile == FileS.FILE_B || pawnFile == FileS.FILE_G) && 0 == (pos.pieces_piecetype(PieceTypeS.PAWN) & ~BitBoard.file_bb_file(pawnFile)) && pos.non_pawn_material(weakSide) == 0 && pos.count(weakSide, PieceTypeS.PAWN) >= 1) { // Get weakSide pawn that is closest to the home rank Square weakPawnSq = BitBoard.backmost_sq(weakSide, pos.pieces_color_piecetype(weakSide, PieceTypeS.PAWN)); Square strongKingSq = pos.king_square(strongSide); Square weakKingSq = pos.king_square(weakSide); Square bishopSq = pos.list(strongSide, PieceTypeS.BISHOP)[0]; // There's potential for a draw if our pawn is blocked on the 7th rank, // the bishop cannot attack it or they only have one pawn left if (Types.relative_rank_square(strongSide, weakPawnSq) == RankS.RANK_7 && (pos.pieces_color_piecetype(strongSide, PieceTypeS.PAWN) & BitBoard.SquareBB[(weakPawnSq + Types.pawn_push(weakSide))]) != 0 && (Types.opposite_colors(bishopSq, weakPawnSq) || pos.count(strongSide, PieceTypeS.PAWN) == 1)) { int strongKingDist = BitBoard.square_distance(weakPawnSq, strongKingSq); int weakKingDist = BitBoard.square_distance(weakPawnSq, weakKingSq); // It's a draw if the weak king is on its back two ranks, within 2 // squares of the blocking pawn and the strong king is not // closer. (I think this rule only fails in practically // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w // and positions where qsearch will immediately correct the // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w) if (Types.relative_rank_square(strongSide, weakKingSq) >= RankS.RANK_7 && weakKingDist <= 2 && weakKingDist <= strongKingDist) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } } } return(ScaleFactorS.SCALE_FACTOR_NONE); }