internal static ExtMoveArrayWrapper generate_castling( CastlingRight Cr, bool Checks, bool Chess960, Position pos, ExtMoveArrayWrapper moveList, ColorT us, CheckInfo ci) { var KingSide = (Cr == CastlingRight.WHITE_OO || Cr == CastlingRight.BLACK_OO); if (pos.castling_impeded(Cr) || !pos.can_castle(Cr)) { return moveList; } // After castling, the rook and king final positions are the same in Chess960 // as they would be in standard chess. var kfrom = pos.square(PieceType.KING, us); var rfrom = pos.castling_rook_square(Cr); var kto = Square.relative_square(us, KingSide ? Square.SQ_G1 : Square.SQ_C1); var enemies = pos.pieces_Ct(Color.opposite(us)); Debug.Assert(pos.checkers() == 0); var K = Chess960 ? kto > kfrom ? Square.DELTA_W : Square.DELTA_E : KingSide ? Square.DELTA_W : Square.DELTA_E; for (var s = kto; s != kfrom; s += K) { if ((pos.attackers_to(s) & enemies) != 0) { return moveList; } } // Because we generate only legal castling moves we need to verify that // when moving the castling rook we do not discover some hidden checker. // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. if (Chess960 && ((Utils.attacks_bb_PtSBb(PieceType.ROOK, kto, Bitboard.XorWithSquare(pos.pieces(), rfrom)) & pos.pieces_CtPtPt(Color.opposite(us), PieceType.ROOK, PieceType.QUEEN)))!= 0) { return moveList; } var m = Move.make(MoveType.CASTLING, kfrom, rfrom); if (Checks && !pos.gives_check(m, ci)) { return moveList; } moveList.Add(m); return moveList; }
internal CheckInfo(Position pos) { var them = Color.opposite(pos.side_to_move()); ksq = pos.square(PieceType.KING, them); pinned = pos.pinned_pieces(pos.side_to_move()); dcCandidates = pos.discovered_check_candidates(); checkSquares[PieceType.PAWN] = pos.attacks_from_PS(PieceType.PAWN, ksq, them); checkSquares[PieceType.KNIGHT] = pos.attacks_from_PtS(PieceType.KNIGHT, ksq); checkSquares[PieceType.BISHOP] = pos.attacks_from_PtS(PieceType.BISHOP, ksq); checkSquares[PieceType.ROOK] = pos.attacks_from_PtS(PieceType.ROOK, ksq); checkSquares[PieceType.QUEEN] = checkSquares[PieceType.BISHOP] | checkSquares[PieceType.ROOK]; checkSquares[PieceType.KING] = Bitboard.Create(0); }
// Map the square as if strongSide is white and strongSide's only pawn // is on the left half of the board. protected static SquareT normalize(Position pos, ColorT strongSide, SquareT sq) { Debug.Assert(pos.count(PieceType.PAWN, strongSide) == 1); if (Square.file_of(pos.square(PieceType.PAWN, strongSide)) >= File.FILE_E) { sq = Square.Create(sq ^ 7); // Mirror SQ_H1 -> SQ_A1 } if (strongSide == Color.BLACK) { sq = Square.opposite(sq); } return sq; }
/// evaluate() is the main evaluation function. It returns a static evaluation /// of the position always from the point of view of the side to move. internal static ValueT evaluate(bool DoTrace, Position pos) { Debug.Assert(pos.checkers() == 0); var ei = new EvalInfo(); ScoreT[] mobility = {Score.SCORE_ZERO, Score.SCORE_ZERO}; // Initialize score by reading the incrementally updated scores included // in the position object (material + piece square tables). // Score is computed from the point of view of white. var score = pos.psq_score(); // Probe the material hash table var me = Material.probe(pos); score += me.imbalance(); // If we have a specialized evaluation function for the current material // configuration, call it and return. if (me.specialized_eval_exists()) { return me.evaluate(pos); } // Probe the pawn hash table ei.pi = Pawns.probe(pos); score += Score.Multiply(ei.pi.pawns_score(), Weights[PawnStructure]); // Initialize attack and king safety bitboards ei.attackedBy[Color.WHITE, PieceType.ALL_PIECES] = ei.attackedBy[Color.BLACK, PieceType.ALL_PIECES] = Bitboard.Create(0); init_eval_info(Color.WHITE, pos, ei); init_eval_info(Color.BLACK, pos, ei); // Pawns blocked or on ranks 2 and 3. Will be excluded from the mobility area BitboardT[] blockedPawns = { pos.pieces_CtPt(Color.WHITE, PieceType.PAWN) & (Bitboard.shift_bb(Square.DELTA_S, pos.pieces()) | Bitboard.Rank2BB | Bitboard.Rank3BB), pos.pieces_CtPt(Color.BLACK, PieceType.PAWN) & (Bitboard.shift_bb(Square.DELTA_N, pos.pieces()) | Bitboard.Rank7BB | Bitboard.Rank6BB) }; // Do not include in mobility squares protected by enemy pawns, or occupied // by our blocked pawns or king. BitboardT[] mobilityArea = { ~(Bitboard.OrWithSquare(ei.attackedBy[Color.BLACK, PieceType.PAWN] | blockedPawns[Color.WHITE] , pos.square(PieceType.KING, Color.WHITE))), ~(Bitboard.OrWithSquare(ei.attackedBy[Color.WHITE, PieceType.PAWN] | blockedPawns[Color.BLACK] , pos.square(PieceType.KING, Color.BLACK))) }; // Evaluate pieces and mobility score += evaluate_pieces(PieceType.KNIGHT, Color.WHITE, DoTrace, pos, ei, mobility, mobilityArea); score += Score.Multiply(mobility[Color.WHITE] - mobility[Color.BLACK], Weights[Mobility]); // Evaluate kings after all other pieces because we need complete attack // information when computing the king safety evaluation. score += evaluate_king(Color.WHITE, DoTrace, pos, ei) - evaluate_king(Color.BLACK, DoTrace, pos, ei); // Evaluate tactical threats, we need full attack information including king score += evaluate_threats(Color.WHITE, DoTrace, pos, ei) - evaluate_threats(Color.BLACK, DoTrace, pos, ei); // Evaluate passed pawns, we need full attack information including king score += evaluate_passed_pawns(Color.WHITE, DoTrace, pos, ei) - evaluate_passed_pawns(Color.BLACK, DoTrace, pos, ei); // If both sides have only pawns, score for potential unstoppable pawns if (pos.non_pawn_material(Color.WHITE) == 0 && pos.non_pawn_material(Color.BLACK) == 0) { BitboardT b; if ((b = ei.pi.passed_pawns(Color.WHITE)) != 0) { score += Rank.relative_rank_CtSt(Color.WHITE, Utils.frontmost_sq(Color.WHITE, b)) * Unstoppable; } if ((b = ei.pi.passed_pawns(Color.BLACK)) != 0) { score -= Rank.relative_rank_CtSt(Color.BLACK, Utils.frontmost_sq(Color.BLACK, b)) * Unstoppable; } } // Evaluate space for both sides, only during opening if (pos.non_pawn_material(Color.WHITE) + pos.non_pawn_material(Color.BLACK) >= 12222) { score += Score.Multiply(evaluate_space(Color.WHITE, pos, ei) - evaluate_space(Color.BLACK, pos, ei), Weights[Space]); } // Scale winning side if position is more drawish than it appears var strongSide = Score.eg_value(score) > Value.VALUE_DRAW ? Color.WHITE : Color.BLACK; var sf = me.scale_factor(pos, strongSide); // If we don't already have an unusual scale factor, check for certain // types of endgames, and use a lower scale for those. if (me.game_phase() < Phase.PHASE_MIDGAME && (sf == ScaleFactor.SCALE_FACTOR_NORMAL || sf == ScaleFactor.SCALE_FACTOR_ONEPAWN)) { if (pos.opposite_bishops()) { // Endgame with opposite-colored bishops and no other pieces (ignoring pawns) // is almost a draw, in case of KBP vs KB is even more a draw. if (pos.non_pawn_material(Color.WHITE) == Value.BishopValueMg && pos.non_pawn_material(Color.BLACK) == Value.BishopValueMg) { sf = Bitboard.more_than_one(pos.pieces_Pt(PieceType.PAWN)) ? (ScaleFactor) (31) : (ScaleFactor) (9); } // Endgame with opposite-colored bishops, but also other pieces. Still // a bit drawish, but not as drawish as with only the two bishops. else { sf = (ScaleFactor) (46*(int) sf/(int) ScaleFactor.SCALE_FACTOR_NORMAL); } } // Endings where weaker side can place his king in front of the opponent's // pawns are drawish. else if (Math.Abs(Score.eg_value(score)) <= Value.BishopValueEg && ei.pi.pawn_span(strongSide) <= 1 && !pos.pawn_passed(Color.opposite(strongSide), pos.square(PieceType.KING, Color.opposite(strongSide)))) { sf = ei.pi.pawn_span(strongSide) != 0 ? (ScaleFactor) (51) : (ScaleFactor) (37); } } // Scale endgame by number of pawns var p = pos.count(PieceType.PAWN, Color.WHITE) + pos.count(PieceType.PAWN, Color.BLACK); var vEg = 1 + Math.Abs(Score.eg_value(score)); sf = (ScaleFactor) (Math.Max((int) sf/2, (int) sf - 8*(int) ScaleFactor.SCALE_FACTOR_NORMAL*(12 - p)/vEg)); // Interpolate between a middlegame and a (scaled by 'sf') endgame score var v = Score.mg_value(score)*(int) (me.game_phase()) + Score.eg_value(score)*(Phase.PHASE_MIDGAME - me.game_phase())*(int) sf /(int) ScaleFactor.SCALE_FACTOR_NORMAL; v /= (int) (Phase.PHASE_MIDGAME); // In case of tracing add all single evaluation terms if (DoTrace) { add_IdxSt((int) Term.MATERIAL, pos.psq_score()); add_IdxSt((int) Term.IMBALANCE, me.imbalance()); add_IdxSt(PieceType.PAWN, ei.pi.pawns_score()); add_IdxStSt( (int) Term.MOBILITY, Score.Multiply(mobility[Color.WHITE], Weights[Mobility]), Score.Multiply(mobility[Color.BLACK], Weights[Mobility])); add_IdxStSt( (int) Term.SPACE, Score.Multiply(evaluate_space(Color.WHITE, pos, ei), Weights[Space]), Score.Multiply(evaluate_space(Color.BLACK, pos, ei), Weights[Space])); add_IdxSt((int) Term.TOTAL, score); } return (pos.side_to_move() == Color.WHITE ? v : -v) + Tempo; // Side to move point of view }
internal override ScaleFactor GetScaleFactor(Position pos) { var pawnSq = pos.square(PieceType.PAWN, strongSide); var bishopSq = pos.square(PieceType.BISHOP, weakSide); var weakKingSq = pos.square(PieceType.KING, weakSide); // King needs to get close to promoting pawn to prevent knight from blocking. // Rules for this are very tricky, so just approximate. if ((Utils.forward_bb(strongSide, pawnSq) & pos.attacks_from_PtS(PieceType.BISHOP, bishopSq))!=0) { return (ScaleFactor) (Utils.distance_Square(weakKingSq, pawnSq)); } return ScaleFactor.SCALE_FACTOR_NONE; }
internal override ScaleFactor GetScaleFactor(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.BishopValueMg, 1)); Debug.Assert(verify_material(pos, weakSide, Value.KnightValueMg, 0)); var pawnSq = pos.square(PieceType.PAWN, strongSide); var strongBishopSq = pos.square(PieceType.BISHOP, strongSide); var weakKingSq = pos.square(PieceType.KING, weakSide); if (Square.file_of(weakKingSq) == Square.file_of(pawnSq) && Rank.relative_rank_CtSt(strongSide, pawnSq) < Rank.relative_rank_CtSt(strongSide, weakKingSq) && (Square.opposite_colors(weakKingSq, strongBishopSq) || Rank.relative_rank_CtSt(strongSide, weakKingSq) <= Rank.RANK_6)) { return ScaleFactor.SCALE_FACTOR_DRAW; } return ScaleFactor.SCALE_FACTOR_NONE; }
internal override ScaleFactor GetScaleFactor(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.BishopValueMg, 1)); Debug.Assert(verify_material(pos, weakSide, Value.BishopValueMg, 0)); var pawnSq = pos.square(PieceType.PAWN, strongSide); var strongBishopSq = pos.square(PieceType.BISHOP, strongSide); var weakBishopSq = pos.square(PieceType.BISHOP, weakSide); var weakKingSq = pos.square(PieceType.KING, weakSide); // Case 1: Defending king blocks the pawn, and cannot be driven away if (Square.file_of(weakKingSq) == Square.file_of(pawnSq) && Rank.relative_rank_CtSt(strongSide, pawnSq) < Rank.relative_rank_CtSt(strongSide, weakKingSq) && (Square.opposite_colors(weakKingSq, strongBishopSq) || Rank.relative_rank_CtSt(strongSide, weakKingSq) <= Rank.RANK_6)) { return ScaleFactor.SCALE_FACTOR_DRAW; } // Case 2: Opposite colored bishops if (Square.opposite_colors(strongBishopSq, weakBishopSq)) { // 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 (Rank.relative_rank_CtSt(strongSide, pawnSq) <= Rank.RANK_5) { return ScaleFactor.SCALE_FACTOR_DRAW; } var path = Utils.forward_bb(strongSide, pawnSq); if ((path & pos.pieces_CtPt(weakSide, PieceType.KING))!=0) { return ScaleFactor.SCALE_FACTOR_DRAW; } if ((pos.attacks_from_PtS(PieceType.BISHOP, weakBishopSq) & path)!=0 && Utils.distance_Square(weakBishopSq, pawnSq) >= 3) { return ScaleFactor.SCALE_FACTOR_DRAW; } } return ScaleFactor.SCALE_FACTOR_NONE; }
internal override ScaleFactor GetScaleFactor(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.RookValueMg, 2)); Debug.Assert(verify_material(pos, weakSide, Value.RookValueMg, 1)); var wpsq1 = pos.square(PieceType.PAWN, strongSide, 0); var wpsq2 = pos.square(PieceType.PAWN, strongSide, 1); var bksq = pos.square(PieceType.KING, weakSide); // Does the stronger side have a passed pawn? if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2)) { return ScaleFactor.SCALE_FACTOR_NONE; } var r = Math.Max(Rank.relative_rank_CtSt(strongSide, wpsq1), Rank.relative_rank_CtSt(strongSide, wpsq2)); if (Utils.distance_File(bksq, wpsq1) <= 1 && Utils.distance_File(bksq, wpsq2) <= 1 && Rank.relative_rank_CtSt(strongSide, bksq) > r) { switch (r) { case 1 /*Rank.RANK_2*/: return (ScaleFactor) (9); case 2 /*Rank.RANK_3*/: return (ScaleFactor) (10); case 3 /*Rank.RANK_4*/: return (ScaleFactor) (14); case 4 /*Rank.RANK_5*/: return (ScaleFactor) (21); case 5 /*Rank.RANK_6*/: return (ScaleFactor) (44); default: Debug.Assert(false); break; } } return ScaleFactor.SCALE_FACTOR_NONE; }
internal override ScaleFactor GetScaleFactor(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.QueenValueMg, 0)); Debug.Assert(pos.count(PieceType.ROOK, weakSide) == 1); Debug.Assert(pos.count(PieceType.PAWN, weakSide) >= 1); var kingSq = pos.square(PieceType.KING, weakSide); var rsq = pos.square(PieceType.ROOK, weakSide); if (Rank.relative_rank_CtSt(weakSide, kingSq) <= Rank.RANK_2 && Rank.relative_rank_CtSt(weakSide, pos.square(PieceType.KING, strongSide)) >= Rank.RANK_4 && Rank.relative_rank_CtSt(weakSide, rsq) == Rank.RANK_3 && (pos.pieces_CtPt(weakSide, PieceType.PAWN) & pos.attacks_from_PtS(PieceType.KING, kingSq) & pos.attacks_from_PS(PieceType.PAWN, rsq, strongSide))!=0) { return ScaleFactor.SCALE_FACTOR_DRAW; } return ScaleFactor.SCALE_FACTOR_NONE; }
internal static ScoreT evaluate(ColorT Us, Position pos, Entry e) { var Them = (Us == Color.WHITE ? Color.BLACK : Color.WHITE); var Up = (Us == Color.WHITE ? Square.DELTA_N : Square.DELTA_S); var Right = (Us == Color.WHITE ? Square.DELTA_NE : Square.DELTA_SW); var Left = (Us == Color.WHITE ? Square.DELTA_NW : Square.DELTA_SE); BitboardT b; var score = Score.SCORE_ZERO; var ourPawns = pos.pieces_CtPt(Us, PieceType.PAWN); var theirPawns = pos.pieces_CtPt(Them, PieceType.PAWN); e.passedPawns[Us] = Bitboard.Create(0); e.kingSquares[Us] = Square.SQ_NONE; e.semiopenFiles[Us] = 0xFF; e.pawnAttacks[Us] = Bitboard.shift_bb(Right, ourPawns) | Bitboard.shift_bb(Left, ourPawns); e.pawnsOnSquares[Us, Color.BLACK] = Bitcount.popcount_Max15(ourPawns & Bitboard.DarkSquares); e.pawnsOnSquares[Us, Color.WHITE] = pos.count(PieceType.PAWN, Us) - e.pawnsOnSquares[Us, Color.BLACK]; // Loop through all pawns of the current color and score each pawn for (var idx = 0; idx < 16; idx++) { var s = pos.square(PieceType.PAWN, Us, idx); if (s == Square.SQ_NONE) { break; } Debug.Assert(pos.piece_on(s) == Piece.make_piece(Us, PieceType.PAWN)); var f = Square.file_of(s); // This file cannot be semi-open e.semiopenFiles[Us] &= ~(1 << f); // Flag the pawn var neighbours = ourPawns & Utils.adjacent_files_bb(f); var doubled = ourPawns & Utils.forward_bb(Us, s); bool opposed = (theirPawns & Utils.forward_bb(Us, s)) != 0; var passed = (theirPawns & Utils.passed_pawn_mask(Us, s)) == 0; bool lever = (theirPawns & Utils.StepAttacksBB[Piece.make_piece(Us, PieceType.PAWN), s]) != 0; var phalanx = neighbours & Utils.rank_bb_St(s); var supported = neighbours & Utils.rank_bb_St(s - Up); bool connected = (supported | phalanx) != 0; var isolated = neighbours == 0; // Test for backward pawn. // If the pawn is passed, isolated, lever or connected it cannot be // backward. If there are friendly pawns behind on adjacent files // or if it is sufficiently advanced, it cannot be backward either. bool backward; if ((passed | isolated | lever | connected) || (ourPawns & Utils.pawn_attack_span(Them, s)) != 0 || (Rank.relative_rank_CtSt(Us, s) >= Rank.RANK_5)) { backward = false; } else { // We now know 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 = Utils.pawn_attack_span(Us, s) & (ourPawns | theirPawns); b = Utils.pawn_attack_span(Us, s) & Utils.rank_bb_St(Utils.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(Up, b)) & theirPawns) != 0; } Debug.Assert(opposed | passed | (Utils.pawn_attack_span(Us, s) & theirPawns) != 0); // 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 && doubled == 0) { e.passedPawns[Us] = Bitboard.OrWithSquare(e.passedPawns[Us], s); } // Score this pawn if (isolated) { score -= Isolated[opposed ? 1 : 0][f]; } else if (backward) { score -= Backward[opposed ? 1 : 0]; } else if (supported == 0) { score -= UnsupportedPawnPenalty; } if (connected) { score += Connected[ opposed ? 1 : 0, phalanx != 0 ? 1 : 0, Bitboard.more_than_one(supported) ? 1 : 0, Rank.relative_rank_CtSt(Us, s)]; } if (doubled != 0) { score -= Score.Divide(Doubled[f], Utils.distance_Rank_StSt(s, Utils.frontmost_sq(Us, doubled))); } if (lever) { score += Lever[Rank.relative_rank_CtSt(Us, s)]; } } b = Bitboard.Create((uint) (e.semiopenFiles[Us] ^ 0xFF)); e.pawnSpan[Us] = b != 0 ? Utils.msb(b) - (int)Utils.lsb(b) : 0; // Center binds: Two pawns controlling the same central square b = Bitboard.shift_bb(Right, ourPawns) & Bitboard.shift_bb(Left, ourPawns) & CenterBindMask[Us]; score += Bitcount.popcount_Max15(b)*CenterBind; return score; }
/// generate /// LEGAL generates all the legal moves in the given position private static ExtMoveArrayWrapper generate_LEGAL(Position pos, ExtMoveArrayWrapper moveList) { var pinned = pos.pinned_pieces(pos.side_to_move()); var ksq = pos.square(PieceType.KING, pos.side_to_move()); var cur = moveList.current; moveList = pos.checkers() != 0 ? generate(GenType.EVASIONS, pos, moveList) : generate(GenType.NON_EVASIONS, pos, moveList); while (cur != moveList.current) { if ((pinned != 0 || Move.from_sq(moveList[cur]) == ksq || Move.type_of(moveList[cur]) == MoveType.ENPASSANT) && !pos.legal(moveList[cur], pinned)) { for (var idx = cur; idx < moveList.current; idx++) { moveList.table[idx] = moveList.table[idx + 1]; } --moveList; } else { ++cur; } } return moveList; }
/// generate /// EVASIONS /// generates all pseudo-legal check evasions when the side /// to move is in check. Returns a pointer to the end of the move list. private static ExtMoveArrayWrapper generate_EVASIONS(Position pos, ExtMoveArrayWrapper moveList) { Debug.Assert(pos.checkers() != 0); var us = pos.side_to_move(); var ksq = pos.square(PieceType.KING, us); var sliderAttacks = Bitboard.Create(0); var sliders = pos.checkers() & ~pos.pieces_PtPt(PieceType.KNIGHT, PieceType.PAWN); // Find all the squares attacked by slider checkers. We will remove them from // the king evasions in order to skip known illegal moves, which avoids any // useless legality checks later on. while (sliders != 0) { var checksq1 = Utils.pop_lsb(ref sliders); sliderAttacks |= Bitboard.XorWithSquare(Utils.LineBB[checksq1, ksq], checksq1); } // Generate evasions for king, capture and non capture moves var b = pos.attacks_from_PtS(PieceType.KING, ksq) & ~pos.pieces_Ct(us) & ~sliderAttacks; while (b != 0) { (moveList).Add(Move.make_move(ksq, Utils.pop_lsb(ref b))); } if (Bitboard.more_than_one(pos.checkers())) { return moveList; // Double check, only a king move can save the day } // Generate blocking evasions or captures of the checking piece var checksq = Utils.lsb(pos.checkers()); var target = Bitboard.OrWithSquare(Utils.between_bb(checksq, ksq), checksq); return us == Color.WHITE ? generate_all(Color.WHITE, GenType.EVASIONS, pos, moveList, target) : generate_all(Color.BLACK, GenType.EVASIONS, pos, moveList, target); }
internal static ExtMoveArrayWrapper generate_all( ColorT Us, GenType Type, Position pos, ExtMoveArrayWrapper moveList, BitboardT target, CheckInfo ci = null) { var Checks = Type == GenType.QUIET_CHECKS; moveList = generate_pawn_moves(Us, Type, pos, moveList, target, ci); moveList = generate_moves(PieceType.KNIGHT, Checks, pos, moveList, Us, target, ci); moveList = generate_moves(PieceType.BISHOP, Checks, pos, moveList, Us, target, ci); moveList = generate_moves(PieceType.ROOK, Checks, pos, moveList, Us, target, ci); moveList = generate_moves(PieceType.QUEEN, Checks, pos, moveList, Us, target, ci); if (Type != GenType.QUIET_CHECKS && Type != GenType.EVASIONS) { var ksq = pos.square(PieceType.KING, Us); var b = pos.attacks_from_PtS(PieceType.KING, ksq) & target; while (b != 0) { (moveList).Add(Move.make_move(ksq, Utils.pop_lsb(ref b))); } } if (Type != GenType.CAPTURES && Type != GenType.EVASIONS && pos.can_castle(Us) != 0) { if (pos.is_chess960()) { moveList = generate_castling( MakeCastling(Us, CastlingSide.KING_SIDE), Checks, true, pos, moveList, Us, ci); moveList = generate_castling( MakeCastling(Us, CastlingSide.QUEEN_SIDE), Checks, true, pos, moveList, Us, ci); } else { moveList = generate_castling( MakeCastling(Us, CastlingSide.KING_SIDE), Checks, false, pos, moveList, Us, ci); moveList = generate_castling( MakeCastling(Us, CastlingSide.QUEEN_SIDE), Checks, false, pos, moveList, Us, ci); } } return moveList; }
internal static ExtMoveArrayWrapper generate_moves( PieceTypeT pieceType, bool Checks, Position pos, ExtMoveArrayWrapper moveList, ColorT us, BitboardT target, CheckInfo ci) { var Pt = (int) pieceType; Debug.Assert(Pt != PieceType.KING && Pt != PieceType.PAWN); for(var idx=0; idx<16;idx++) { var square = pos.square(pieceType, us, idx); if (square == Square.SQ_NONE) { break; } if (Checks) { if ((Pt == PieceType.BISHOP || Pt == PieceType.ROOK || Pt == PieceType.QUEEN) && (Utils.PseudoAttacks[Pt, square] & target & ci.checkSquares[Pt]) == 0) { continue; } if (ci.dcCandidates != 0 && Bitboard.AndWithSquare(ci.dcCandidates, square)!=0) { continue; } } var b = pos.attacks_from_PtS(pieceType, square) & target; if (Checks) { b &= ci.checkSquares[Pt]; } while (b != 0) { (moveList).Add(Move.make_move(square, Utils.pop_lsb(ref b))); } } return moveList; }
internal override ValueT GetValue(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.QueenValueMg, 0)); Debug.Assert(verify_material(pos, weakSide, Value.RookValueMg, 0)); var winnerKSq = pos.square(PieceType.KING, strongSide); var loserKSq = pos.square(PieceType.KING, weakSide); var result = Value.QueenValueEg - Value.RookValueEg + PushToEdges[loserKSq] + PushClose[Utils.distance_Square(winnerKSq, loserKSq)]; return strongSide == pos.side_to_move() ? result : -result; }
internal override ValueT GetValue(Position pos) { Debug.Assert(verify_material(pos, weakSide, Value.VALUE_ZERO, 0)); Debug.Assert(pos.checkers()==0); // Eval is never called when in check // Stalemate detection with lone king if (pos.side_to_move() == weakSide && new MoveList(GenType.LEGAL, pos).size() == 0) { return Value.VALUE_DRAW; } var winnerKSq = pos.square(PieceType.KING, strongSide); var loserKSq = pos.square(PieceType.KING, weakSide); var result = pos.non_pawn_material(strongSide) + pos.count(PieceType.PAWN, strongSide)*Value.PawnValueEg + PushToEdges[loserKSq] + PushClose[Utils.distance_Square(winnerKSq, loserKSq)]; if (pos.count(PieceType.QUEEN, strongSide) > 0 || pos.count(PieceType.ROOK, strongSide) > 0 || (pos.count(PieceType.BISHOP, strongSide) > 0 && pos.count(PieceType.KNIGHT, strongSide) > 0) || (pos.count(PieceType.BISHOP, strongSide) > 1 && Square.opposite_colors( pos.square(PieceType.BISHOP, strongSide, 0), pos.square(PieceType.BISHOP, strongSide, 1)))) { result += Value.VALUE_KNOWN_WIN; } return strongSide == pos.side_to_move() ? result : -result; }
internal override ScaleFactor GetScaleFactor(Position pos) { Debug.Assert(pos.non_pawn_material(strongSide) == Value.BishopValueMg); Debug.Assert(pos.count(PieceType.PAWN, strongSide) >= 1); // No assertions about the material of weakSide, because we want draws to // be detected even when the weaker side has some pawns. var pawns = pos.pieces_CtPt(strongSide, PieceType.PAWN); var pawnsFile = Square.file_of(Utils.lsb(pawns)); // All pawns are on a single rook file? if ((pawnsFile == File.FILE_A || pawnsFile == File.FILE_H) && (pawns & ~Utils.file_bb_Ft(pawnsFile))==0) { var bishopSq = pos.square(PieceType.BISHOP, strongSide); var queeningSq = Square.relative_square(strongSide, Square.make_square(pawnsFile, Rank.RANK_8)); var kingSq = pos.square(PieceType.KING, weakSide); if (Square.opposite_colors(queeningSq, bishopSq) && Utils.distance_Square(queeningSq, kingSq) <= 1) { return ScaleFactor.SCALE_FACTOR_DRAW; } } // If all the pawns are on the same B or G file, then it's potentially a draw if ((pawnsFile == File.FILE_B || pawnsFile == File.FILE_G) && (pos.pieces_Pt(PieceType.PAWN) & ~Utils.file_bb_Ft(pawnsFile))==0 && pos.non_pawn_material(weakSide) == 0 && pos.count(PieceType.PAWN, weakSide) >= 1) { // Get weakSide pawn that is closest to the home rank var weakPawnSq = Utils.backmost_sq(weakSide, pos.pieces_CtPt(weakSide, PieceType.PAWN)); var strongKingSq = pos.square(PieceType.KING, strongSide); var weakKingSq = pos.square(PieceType.KING, weakSide); var bishopSq = pos.square(PieceType.BISHOP, strongSide); // 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 (Rank.relative_rank_CtSt(strongSide, weakPawnSq) == Rank.RANK_7 && Bitboard.AndWithSquare(pos.pieces_CtPt(strongSide, PieceType.PAWN), (weakPawnSq + Square.pawn_push(weakSide)))!=0 && (Square.opposite_colors(bishopSq, weakPawnSq) || pos.count(PieceType.PAWN, strongSide) == 1)) { var strongKingDist = Utils.distance_Square(weakPawnSq, strongKingSq); var weakKingDist = Utils.distance_Square(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 (Rank.relative_rank_CtSt(strongSide, weakKingSq) >= Rank.RANK_7 && weakKingDist <= 2 && weakKingDist <= strongKingDist) { return ScaleFactor.SCALE_FACTOR_DRAW; } } } return ScaleFactor.SCALE_FACTOR_NONE; }
internal override ValueT GetValue(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.KnightValueMg + Value.BishopValueMg, 0)); Debug.Assert(verify_material(pos, weakSide, Value.VALUE_ZERO, 0)); var winnerKSq = pos.square(PieceType.KING, strongSide); var loserKSq = pos.square(PieceType.KING, weakSide); var bishopSq = pos.square(PieceType.BISHOP, strongSide); // 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 (Square.opposite_colors(bishopSq, Square.SQ_A1)) { winnerKSq = Square.opposite(winnerKSq); loserKSq = Square.opposite(loserKSq); } var result = Value.VALUE_KNOWN_WIN + PushClose[Utils.distance_Square(winnerKSq, loserKSq)] + PushToCorners[loserKSq]; return strongSide == pos.side_to_move() ? result : -result; }
internal override ScaleFactor GetScaleFactor(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.RookValueMg, 1)); Debug.Assert(verify_material(pos, weakSide, Value.RookValueMg, 0)); // Assume strongSide is white and the pawn is on files A-D var wksq = normalize(pos, strongSide, pos.square(PieceType.KING, strongSide)); var bksq = normalize(pos, strongSide, pos.square(PieceType.KING, weakSide)); var wrsq = normalize(pos, strongSide, pos.square(PieceType.ROOK, strongSide)); var wpsq = normalize(pos, strongSide, pos.square(PieceType.PAWN, strongSide)); var brsq = normalize(pos, strongSide, pos.square(PieceType.ROOK, weakSide)); var f = Square.file_of(wpsq); var r = Square.rank_of(wpsq); var queeningSq = Square.make_square(f, Rank.RANK_8); var tempo = (pos.side_to_move() == strongSide) ? 1 : 0; // If the pawn is not too far advanced and the defending king defends the // queening square, use the third-rank defence. if (r <= Rank.RANK_5 && Utils.distance_Square(bksq, queeningSq) <= 1 && wksq <= Square.SQ_H5 && (Square.rank_of(brsq) == Rank.RANK_6 || (r <= Rank.RANK_3 && Square.rank_of(wrsq) != Rank.RANK_6))) { return ScaleFactor.SCALE_FACTOR_DRAW; } // The defending side saves a draw by checking from behind in case the pawn // has advanced to the 6th rank with the king behind. if (r == Rank.RANK_6 && Utils.distance_Square(bksq, queeningSq) <= 1 && (int)Square.rank_of(wksq) + tempo <= Rank.RANK_6 && (Square.rank_of(brsq) == Rank.RANK_1 || (tempo == 0 && Utils.distance_File(brsq, wpsq) >= 3))) { return ScaleFactor.SCALE_FACTOR_DRAW; } if (r >= Rank.RANK_6 && bksq == queeningSq && Square.rank_of(brsq) == Rank.RANK_1 && (tempo == 0 || Utils.distance_Square(wksq, wpsq) >= 2)) { return ScaleFactor.SCALE_FACTOR_DRAW; } // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7 // and the black rook is behind the pawn. if (wpsq == Square.SQ_A7 && wrsq == Square.SQ_A8 && (bksq == Square.SQ_H7 || bksq == Square.SQ_G7) && Square.file_of(brsq) == File.FILE_A && (Square.rank_of(brsq) <= Rank.RANK_3 || Square.file_of(wksq) >= File.FILE_D || Square.rank_of(wksq) <= Rank.RANK_5)) { return ScaleFactor.SCALE_FACTOR_DRAW; } // If the defending king blocks the pawn and the attacking king is too far // away, it's a draw. if (r <= Rank.RANK_5 && bksq == wpsq + Square.DELTA_N && Utils.distance_Square(wksq, wpsq) - tempo >= 2 && Utils.distance_Square(wksq, brsq) - tempo >= 2) { return ScaleFactor.SCALE_FACTOR_DRAW; } // Pawn on the 7th rank supported by the rook from behind usually wins if the // attacking king is closer to the queening square than the defending king, // and the defending king cannot gain tempi by threatening the attacking rook. if (r == Rank.RANK_7 && f != File.FILE_A && Square.file_of(wrsq) == f && wrsq != queeningSq && (Utils.distance_Square(wksq, queeningSq) < Utils.distance_Square(bksq, queeningSq) - 2 + tempo) && (Utils.distance_Square(wksq, queeningSq) < Utils.distance_Square(bksq, wrsq) + tempo)) { return ScaleFactor.SCALE_FACTOR_MAX - 2*Utils.distance_Square(wksq, queeningSq); } // Similar to the above, but with the pawn further back if (f != File.FILE_A && Square.file_of(wrsq) == f && wrsq < wpsq && (Utils.distance_Square(wksq, queeningSq) < Utils.distance_Square(bksq, queeningSq) - 2 + tempo) && (Utils.distance_Square(wksq, wpsq + Square.DELTA_N) < Utils.distance_Square(bksq, wpsq + Square.DELTA_N) - 2 + tempo) && (Utils.distance_Square(bksq, wrsq) + tempo >= 3 || (Utils.distance_Square(wksq, queeningSq) < Utils.distance_Square(bksq, wrsq) + tempo && (Utils.distance_Square(wksq, wpsq + Square.DELTA_N) < Utils.distance_Square(bksq, wrsq) + tempo)))) { return ScaleFactor.SCALE_FACTOR_MAX - 8*Utils.distance_Square(wpsq, queeningSq) - 2*Utils.distance_Square(wksq, queeningSq); } // If the pawn is not far advanced and the defending king is somewhere in // the pawn's path, it's probably a draw. if (r <= Rank.RANK_4 && bksq > wpsq) { if (Square.file_of(bksq) == Square.file_of(wpsq)) { return (ScaleFactor) (10); } if (Utils.distance_File(bksq, wpsq) == 1 && Utils.distance_Square(wksq, bksq) > 2) { return (ScaleFactor) (24 - 2*Utils.distance_Square(wksq, bksq)); } } return ScaleFactor.SCALE_FACTOR_NONE; }
internal override ValueT GetValue(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.VALUE_ZERO, 1)); Debug.Assert(verify_material(pos, weakSide, Value.VALUE_ZERO, 0)); // Assume strongSide is white and the pawn is on files A-D var wksq = normalize(pos, strongSide, pos.square(PieceType.KING, strongSide)); var bksq = normalize(pos, strongSide, pos.square(PieceType.KING, weakSide)); var psq = normalize(pos, strongSide, pos.square(PieceType.PAWN, strongSide)); var us = strongSide == pos.side_to_move() ? Color.WHITE : Color.BLACK; if (!Bitbases.probe(wksq, psq, bksq, us)) { return Value.VALUE_DRAW; } var result = Value.VALUE_KNOWN_WIN + Value.PawnValueEg + Value.Create(Square.rank_of(psq)); return strongSide == pos.side_to_move() ? result : -result; }
internal override ScaleFactor GetScaleFactor(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.RookValueMg, 1)); Debug.Assert(verify_material(pos, weakSide, Value.BishopValueMg, 0)); // Test for a rook pawn if ((pos.pieces_Pt(PieceType.PAWN) & (Bitboard.FileABB | Bitboard.FileHBB))!=0) { var ksq = pos.square(PieceType.KING, weakSide); var bsq = pos.square(PieceType.BISHOP, weakSide); var psq = pos.square(PieceType.PAWN, strongSide); var rk = Rank.relative_rank_CtSt(strongSide, psq); var push = Square.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 == Rank.RANK_5 && !Square.opposite_colors(bsq, psq)) { var d = Utils.distance_Square(psq + 3*push, ksq); if (d <= 2 && !(d == 0 && ksq == pos.square(PieceType.KING, strongSide) + 2*push)) { return (ScaleFactor) (24); } return (ScaleFactor) (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 == Rank.RANK_6 && Utils.distance_Square(psq + 2*push, ksq) <= 1 && Bitboard.AndWithSquare(Utils.PseudoAttacks[PieceType.BISHOP, bsq], (psq + push))!=0 && Utils.distance_File(bsq, psq) >= 2) { return (ScaleFactor) (8); } } return ScaleFactor.SCALE_FACTOR_NONE; }
internal override ValueT GetValue(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.RookValueMg, 0)); Debug.Assert(verify_material(pos, weakSide, Value.VALUE_ZERO, 1)); var wksq = Square.relative_square(strongSide, pos.square(PieceType.KING, strongSide)); var bksq = Square.relative_square(strongSide, pos.square(PieceType.KING, weakSide)); var rsq = Square.relative_square(strongSide, pos.square(PieceType.ROOK, strongSide)); var psq = Square.relative_square(strongSide, pos.square(PieceType.PAWN, weakSide)); var queeningSq = Square.make_square(Square.file_of(psq), Rank.RANK_1); ValueT result; // If the stronger side's king is in front of the pawn, it's a win if (wksq < psq && Square.file_of(wksq) == Square.file_of(psq)) { result = Value.RookValueEg - Utils.distance_Square(wksq, psq); } // If the weaker side's king is too far from the pawn and the rook, // it's a win. else if (Utils.distance_Square(bksq, psq) >= 3 + (pos.side_to_move() == weakSide ? 1 : 0) && Utils.distance_Square(bksq, rsq) >= 3) { result = Value.RookValueEg - Utils.distance_Square(wksq, psq); } // If the pawn is far advanced and supported by the defending king, // the position is drawish else if (Square.rank_of(bksq) <= Rank.RANK_3 && Utils.distance_Square(bksq, psq) == 1 && Square.rank_of(wksq) >= Rank.RANK_4 && Utils.distance_Square(wksq, psq) > 2 + (pos.side_to_move() == strongSide ? 1 : 0)) { result = Value.Create(80) - 8*Utils.distance_Square(wksq, psq); } else { result = Value.Create(200) - 8 *(Utils.distance_Square(wksq, psq + Square.DELTA_S) - Utils.distance_Square(bksq, psq + Square.DELTA_S) - Utils.distance_Square(psq, queeningSq)); } return strongSide == pos.side_to_move() ? result : -result; }
internal override ScaleFactor GetScaleFactor(Position pos) { Debug.Assert(pos.non_pawn_material(strongSide) == Value.VALUE_ZERO); Debug.Assert(pos.count(PieceType.PAWN, strongSide) >= 2); Debug.Assert(verify_material(pos, weakSide, Value.VALUE_ZERO, 0)); var ksq = pos.square(PieceType.KING, weakSide); var pawns = pos.pieces_CtPt(strongSide, PieceType.PAWN); // 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 ((pawns & ~Utils.in_front_bb(weakSide, Square.rank_of(ksq)))==0 && !((pawns & ~Bitboard.FileABB)!=0 && (pawns & ~Bitboard.FileHBB)!=0) && Utils.distance_File(ksq, Utils.lsb(pawns)) <= 1) { return ScaleFactor.SCALE_FACTOR_DRAW; } return ScaleFactor.SCALE_FACTOR_NONE; }
internal override ValueT GetValue(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.RookValueMg, 0)); Debug.Assert(verify_material(pos, weakSide, Value.BishopValueMg, 0)); var result = Value.Create(PushToEdges[pos.square(PieceType.KING, weakSide)]); return strongSide == pos.side_to_move() ? result : -result; }
internal override ScaleFactor GetScaleFactor(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.BishopValueMg, 2)); Debug.Assert(verify_material(pos, weakSide, Value.BishopValueMg, 0)); var wbsq = pos.square(PieceType.BISHOP, strongSide); var bbsq = pos.square(PieceType.BISHOP, weakSide); if (!Square.opposite_colors(wbsq, bbsq)) { return ScaleFactor.SCALE_FACTOR_NONE; } var ksq = pos.square(PieceType.KING, weakSide); var psq1 = pos.square(PieceType.PAWN, strongSide, 0); var psq2 = pos.square(PieceType.PAWN, strongSide, 1); var r1 = Square.rank_of(psq1); var r2 = Square.rank_of(psq2); SquareT blockSq1, blockSq2; if (Rank.relative_rank_CtSt(strongSide, psq1) > Rank.relative_rank_CtSt(strongSide, psq2)) { blockSq1 = psq1 + Square.pawn_push(strongSide); blockSq2 = Square.make_square(Square.file_of(psq2), Square.rank_of(psq1)); } else { blockSq1 = psq2 + Square.pawn_push(strongSide); blockSq2 = Square.make_square(Square.file_of(psq1), Square.rank_of(psq2)); } switch (Utils.distance_File(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 (Square.file_of(ksq) == Square.file_of(blockSq1) && Rank.relative_rank_CtSt(strongSide, ksq) >= Rank.relative_rank_CtSt(strongSide, blockSq1) && Square.opposite_colors(ksq, wbsq)) { return ScaleFactor.SCALE_FACTOR_DRAW; } return ScaleFactor.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 && Square.opposite_colors(ksq, wbsq) && (bbsq == blockSq2 || (pos.attacks_from_PtS(PieceType.BISHOP, blockSq2) & pos.pieces_CtPt(weakSide, PieceType.BISHOP))!=0 || Utils.distance_Rank(r1, r2) >= 2)) { return ScaleFactor.SCALE_FACTOR_DRAW; } if (ksq == blockSq2 && Square.opposite_colors(ksq, wbsq) && (bbsq == blockSq1 || (pos.attacks_from_PtS(PieceType.BISHOP, blockSq1) & pos.pieces_CtPt(weakSide, PieceType.BISHOP))!=0)) { return ScaleFactor.SCALE_FACTOR_DRAW; } return ScaleFactor.SCALE_FACTOR_NONE; default: // The pawns are not on the same file or adjacent files. No scaling. return ScaleFactor.SCALE_FACTOR_NONE; } }
internal override ValueT GetValue(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.RookValueMg, 0)); Debug.Assert(verify_material(pos, weakSide, Value.KnightValueMg, 0)); var bksq = pos.square(PieceType.KING, weakSide); var bnsq = pos.square(PieceType.KNIGHT, weakSide); var result = Value.Create(PushToEdges[bksq] + PushAway[Utils.distance_Square(bksq, bnsq)]); return strongSide == pos.side_to_move() ? result : -result; }
internal override ScaleFactor GetScaleFactor(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.KnightValueMg, 1)); Debug.Assert(verify_material(pos, weakSide, Value.VALUE_ZERO, 0)); // Assume strongSide is white and the pawn is on files A-D var pawnSq = normalize(pos, strongSide, pos.square(PieceType.PAWN, strongSide)); var weakKingSq = normalize(pos, strongSide, pos.square(PieceType.KING, weakSide)); if (pawnSq == Square.SQ_A7 && Utils.distance_Square(Square.SQ_A8, weakKingSq) <= 1) { return ScaleFactor.SCALE_FACTOR_DRAW; } return ScaleFactor.SCALE_FACTOR_NONE; }
internal override ValueT GetValue(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.QueenValueMg, 0)); Debug.Assert(verify_material(pos, weakSide, Value.VALUE_ZERO, 1)); var winnerKSq = pos.square(PieceType.KING, strongSide); var loserKSq = pos.square(PieceType.KING, weakSide); var pawnSq = pos.square(PieceType.PAWN, weakSide); var result = Value.Create(PushClose[Utils.distance_Square(winnerKSq, loserKSq)]); if (Rank.relative_rank_CtSt(weakSide, pawnSq) != Rank.RANK_7 || Utils.distance_Square(loserKSq, pawnSq) != 1 || Bitboard.AndWithSquare((Bitboard.FileABB | Bitboard.FileCBB | Bitboard.FileFBB | Bitboard.FileHBB), pawnSq)==0) { result += Value.QueenValueEg - Value.PawnValueEg; } return strongSide == pos.side_to_move() ? result : -result; }
internal override ScaleFactor GetScaleFactor(Position pos) { Debug.Assert(verify_material(pos, strongSide, Value.VALUE_ZERO, 1)); Debug.Assert(verify_material(pos, weakSide, Value.VALUE_ZERO, 1)); // Assume strongSide is white and the pawn is on files A-D var wksq = normalize(pos, strongSide, pos.square(PieceType.KING, strongSide)); var bksq = normalize(pos, strongSide, pos.square(PieceType.KING, weakSide)); var psq = normalize(pos, strongSide, pos.square(PieceType.PAWN, strongSide)); var us = strongSide == pos.side_to_move() ? Color.WHITE : Color.BLACK; // If the pawn has advanced to the fifth rank or further, and is not a // rook pawn, it's too dangerous to assume that it's at least a draw. if (Square.rank_of(psq) >= Rank.RANK_5 && Square.file_of(psq) != File.FILE_A) { return ScaleFactor.SCALE_FACTOR_NONE; } // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, // it's probably at least a draw even with the pawn. return Bitbases.probe(wksq, psq, bksq, us) ? ScaleFactor.SCALE_FACTOR_NONE : ScaleFactor.SCALE_FACTOR_DRAW; }
// evaluate_passed_pawns() evaluates the passed pawns of the given color private static ScoreT evaluate_passed_pawns(ColorT Us, bool DoTrace, Position pos, EvalInfo ei) { var Them = (Us == Color.WHITE ? Color.BLACK : Color.WHITE); var score = Score.SCORE_ZERO; var b = ei.pi.passed_pawns(Us); while (b!=0) { var s = Utils.pop_lsb(ref b); Debug.Assert(pos.pawn_passed(Us, s)); int r = Rank.relative_rank_CtSt(Us, s) - Rank.RANK_2; var rr = r*(r - 1); ValueT mbonus = Passed[(int) Phase.MG][r], ebonus = Passed[(int) Phase.EG][r]; if (rr != 0) { var blockSq = s + Square.pawn_push(Us); // Adjust bonus based on the king's proximity ebonus += Utils.distance_Square(pos.square(PieceType.KING, Them), blockSq)*5*rr - Utils.distance_Square(pos.square(PieceType.KING, Us), blockSq)*2*rr; // If blockSq is not the queening square then consider also a second push if (Rank.relative_rank_CtSt(Us, blockSq) != Rank.RANK_8) { ebonus -= Utils.distance_Square(pos.square(PieceType.KING, Us), blockSq + Square.pawn_push(Us))*rr; } // If the pawn is free to advance, then increase the bonus if (pos.empty(blockSq)) { // If there is a rook or queen attacking/defending the pawn from behind, // consider all the squaresToQueen. Otherwise consider only the squares // in the pawn's path attacked or occupied by the enemy. BitboardT squaresToQueen; BitboardT unsafeSquares; var defendedSquares = unsafeSquares = squaresToQueen = Utils.forward_bb(Us, s); var bb = Utils.forward_bb(Them, s) & pos.pieces_PtPt(PieceType.ROOK, PieceType.QUEEN) & pos.attacks_from_PtS(PieceType.ROOK, s); if ((pos.pieces_Ct(Us) & bb)==0) { defendedSquares &= ei.attackedBy[Us, PieceType.ALL_PIECES]; } if ((pos.pieces_Ct(Them) & bb) == 0) { unsafeSquares &= ei.attackedBy[Them, PieceType.ALL_PIECES] | pos.pieces_Ct(Them); } // If there aren't any enemy attacks, assign a big bonus. Otherwise // assign a smaller bonus if the block square isn't attacked. var k = unsafeSquares == 0 ? 18 : Bitboard.AndWithSquare(unsafeSquares, blockSq)==0 ? 8 : 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 (Bitboard.AndWithSquare(defendedSquares, blockSq)!=0) { k += 4; } mbonus += k*rr; ebonus += k*rr; } else if (Bitboard.AndWithSquare(pos.pieces_Ct(Us), blockSq)!=0) { mbonus += rr*3 + r*2 + 3; ebonus += rr + r*2; } } // rr != 0 if (pos.count(PieceType.PAWN, Us) < pos.count(PieceType.PAWN, Them)) { ebonus += ebonus/4; } score += Score.make_score(mbonus, ebonus) + PassedFile[Square.file_of(s)]; } if (DoTrace) { add_IdxCtSt((int) Term.PASSED, Us, Score.Multiply(score, Weights[PassedPawns])); } // Add the scores to the middlegame and endgame eval return Score.Multiply(score, Weights[PassedPawns]); }