/// Entry::shelter_storm() calculates shelter and storm penalties for the file /// the king is on, as well as the two adjacent files. public Value shelter_storm(Position pos, Square ksq, Color Us) { Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); Value safety = Pawns.MaxSafetyBonus; Bitboard b = pos.pieces_piecetype(PieceTypeS.PAWN) & (BitBoard.in_front_bb(Us, Types.rank_of(ksq)) | BitBoard.rank_bb_square(ksq)); Bitboard ourPawns = b & pos.pieces_color(Us); Bitboard theirPawns = b & pos.pieces_color(Them); Rank rkUs, rkThem; File kf = Math.Max(FileS.FILE_B, Math.Min(FileS.FILE_G, Types.file_of(ksq))); for (File f = kf - 1; f <= kf + 1; ++f) { b = ourPawns & BitBoard.file_bb_file(f); rkUs = b != 0 ? Types.relative_rank_square(Us, BitBoard.backmost_sq(Us, b)) : RankS.RANK_1; b = theirPawns & BitBoard.file_bb_file(f); rkThem = b != 0 ? Types.relative_rank_square(Us, BitBoard.frontmost_sq(Them, b)) : RankS.RANK_1; if ((MiddleEdges & BitBoard.SquareBB[Types.make_square(f, rkThem)]) != 0 && Types.file_of(ksq) == f && Types.relative_rank_square(Us, ksq) == rkThem - 1) { safety += 200; } else { safety -= ShelterWeakness[rkUs] + StormDanger[rkUs == RankS.RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem]; } } return(safety); }
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); }
/// 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); }