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); }
/// K and two or more pawns vs K. There is just a single rule here: If all pawns /// are on the same rook file and are blocked by the defending king, it's a draw. public ScaleFactor KPsK(Position pos) { Debug.Assert(pos.non_pawn_material(strongSide) == ValueS.VALUE_ZERO); Debug.Assert(pos.count(strongSide, PieceTypeS.PAWN) >= 2); Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 0)); Square ksq = pos.king_square(weakSide); Bitboard pawns = pos.pieces_color_piecetype(strongSide, PieceTypeS.PAWN); Square psq = pos.list(strongSide, PieceTypeS.PAWN)[0]; // If all pawns are ahead of the king, on a single rook file and // the king is within one file of the pawns, it's a draw. if (0 == (pawns & ~BitBoard.in_front_bb(weakSide, Types.rank_of(ksq))) && !((pawns & ~BitBoard.FileABB) != 0 && (pawns & ~BitBoard.FileHBB) != 0) && BitBoard.file_distance(ksq, psq) <= 1) { return(ScaleFactorS.SCALE_FACTOR_DRAW); } return(ScaleFactorS.SCALE_FACTOR_NONE); }
/// KRPP vs KRP. There is just a single rule: if the stronger side has no passed /// pawns and the defending king is actively placed, the position is drawish. public ScaleFactor KRPPKRP(Position pos) { Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 2)); Debug.Assert(verify_material(pos, weakSide, ValueS.RookValueMg, 1)); Square wpsq1 = pos.list(strongSide, PieceTypeS.PAWN)[0]; Square wpsq2 = pos.list(strongSide, PieceTypeS.PAWN)[1]; Square bksq = pos.king_square(weakSide); // Does the stronger side have a passed pawn? if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2)) { return(ScaleFactorS.SCALE_FACTOR_NONE); } Rank r = Math.Max(Types.relative_rank_square(strongSide, wpsq1), Types.relative_rank_square(strongSide, wpsq2)); if (BitBoard.file_distance(bksq, wpsq1) <= 1 && BitBoard.file_distance(bksq, wpsq2) <= 1 && Types.relative_rank_square(strongSide, bksq) > r) { switch (r) { case RankS.RANK_2: return(10); case RankS.RANK_3: return(10); case RankS.RANK_4: return(15); case RankS.RANK_5: return(20); case RankS.RANK_6: return(40); default: Debug.Assert(false); break; } } return(ScaleFactorS.SCALE_FACTOR_NONE); }
/// 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); } }