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); }
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, 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 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.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; }
/// 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); }
/// generate /// QUIET_CHECKS /// generates all pseudo-legal non-captures and knight /// underpromotions that give check. Returns a pointer to the end of the move list. private static ExtMoveArrayWrapper generate_QUIET_CHECKS(Position pos, ExtMoveArrayWrapper moveList) { Debug.Assert(pos.checkers() == 0); var us = pos.side_to_move(); var ci = new CheckInfo(pos); var dc = ci.dcCandidates; while (dc != 0) { var from = Utils.pop_lsb(ref dc); var pt = Piece.type_of(pos.piece_on(from)); if (pt == PieceType.PAWN) { continue; // Will be generated together with direct checks } var b = pos.attacks_from_PtS(pt, from) & ~pos.pieces(); if (pt == PieceType.KING) { b &= ~Utils.PseudoAttacks[PieceType.QUEEN, ci.ksq]; } while (b != 0) { (moveList).Add(Move.make_move(from, Utils.pop_lsb(ref b))); } } return us == Color.WHITE ? generate_all(Color.WHITE, GenType.QUIET_CHECKS, pos, moveList, ~pos.pieces(), ci) : generate_all(Color.BLACK, GenType.QUIET_CHECKS, pos, moveList, ~pos.pieces(), ci); }
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; }
// 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]); }
// evaluate_king() assigns bonuses and penalties to a king of a given color private static ScoreT evaluate_king(ColorT Us, bool DoTrace, Position pos, EvalInfo ei) { var Them = (Us == Color.WHITE ? Color.BLACK : Color.WHITE); var ksq = pos.square(PieceType.KING, Us); // King shelter and enemy pawns storm var score = ei.pi.king_safety(Us, pos, ksq); // Main king safety evaluation if (ei.kingAttackersCount[Them] != 0) { // Find the attacked squares around the king which have no defenders // apart from the king itself var undefended = ei.attackedBy[Them, PieceType.ALL_PIECES] & ei.attackedBy[Us, PieceType.KING] & ~(ei.attackedBy[Us, PieceType.PAWN] | ei.attackedBy[Us, PieceType.KNIGHT] | ei.attackedBy[Us, PieceType.BISHOP] | ei.attackedBy[Us, PieceType.ROOK] | ei.attackedBy[Us, PieceType.QUEEN]); // Initialize the 'attackUnits' variable, which is used later on as an // index into the KingDanger[] array. The initial value is based on the // number and types of the enemy's attacking pieces, the number of // attacked and undefended squares around our king and the quality of // the pawn shelter (current 'score' value). var attackUnits = Math.Min(72, ei.kingAttackersCount[Them] *ei.kingAttackersWeight[Them]) + 9*ei.kingAdjacentZoneAttacksCount[Them] + 27*Bitcount.popcount_Max15(undefended) + 11*((ulong)ei.pinnedPieces[Us] != 0 ? 1 : 0) - 64*(pos.count(PieceType.QUEEN, Them) == 0 ? 1 : 0) - Score.mg_value(score)/8; // Analyse the enemy's safe queen contact checks. Firstly, find the // undefended squares around the king reachable by the enemy queen... var b = undefended & ei.attackedBy[Them, PieceType.QUEEN] & ~pos.pieces_Ct(Them); if (b!=0) { // ...and then remove squares not supported by another enemy piece b &= ei.attackedBy[Them, PieceType.PAWN] | ei.attackedBy[Them, PieceType.KNIGHT] | ei.attackedBy[Them, PieceType.BISHOP] | ei.attackedBy[Them, PieceType.ROOK]; if (b!=0) { attackUnits += QueenContactCheck*Bitcount.popcount_Max15(b); } } // Analyse the enemy's safe distance checks for sliders and knights var safe = ~(ei.attackedBy[Us, PieceType.ALL_PIECES] | pos.pieces_Ct(Them)); var b1 = pos.attacks_from_PtS(PieceType.ROOK, ksq) & safe; var b2 = pos.attacks_from_PtS(PieceType.BISHOP, ksq) & safe; // Enemy queen safe checks b = (b1 | b2) & ei.attackedBy[Them, PieceType.QUEEN]; if (b!=0) { attackUnits += QueenCheck*Bitcount.popcount_Max15(b); score -= Checked; } // Enemy rooks safe checks b = b1 & ei.attackedBy[Them, PieceType.ROOK]; if (b!=0) { attackUnits += RookCheck*Bitcount.popcount_Max15(b); score -= Checked; } // Enemy bishops safe checks b = b2 & ei.attackedBy[Them, PieceType.BISHOP]; if (b!=0) { attackUnits += BishopCheck*Bitcount.popcount_Max15(b); score -= Checked; } // Enemy knights safe checks b = pos.attacks_from_PtS(PieceType.KNIGHT, ksq) & ei.attackedBy[Them, PieceType.KNIGHT] & safe; if (b!=0) { attackUnits += KnightCheck*Bitcount.popcount_Max15(b); score -= Checked; } // Finally, extract the king danger score from the KingDanger[] // array and subtract the score from evaluation. score -= KingDanger[Math.Max(Math.Min(attackUnits, 399), 0)]; } if (DoTrace) { add_IdxCtSt(PieceType.KING, Us, score); } return score; }
// evaluate_pieces() assigns bonuses and penalties to the pieces of a given color private static ScoreT evaluate_pieces( PieceTypeT pieceType, ColorT Us, bool DoTrace, Position pos, EvalInfo ei, ScoreT[] mobility, BitboardT[] mobilityArea) { int Pt = pieceType; if (Pt == PieceType.KING) { return Score.SCORE_ZERO; } var score = Score.SCORE_ZERO; var NextPt = (Us == Color.WHITE ? pieceType : pieceType + 1); var Them = (Us == Color.WHITE ? Color.BLACK : Color.WHITE); ei.attackedBy[Us, Pt] = Bitboard.Create(0); for(var idx=0; idx<16;idx++) { var s = pos.square(pieceType, Us, idx); if (s == Square.SQ_NONE) { break; } // Find attacked squares, including x-ray attacks for bishops and rooks var b = Pt == PieceType.BISHOP ? Utils.attacks_bb_PtSBb(PieceType.BISHOP, s, pos.pieces() ^ pos.pieces_CtPt(Us, PieceType.QUEEN)) : Pt == PieceType.ROOK ? Utils.attacks_bb_PtSBb( PieceType.ROOK, s, pos.pieces() ^ pos.pieces_CtPtPt(Us, PieceType.ROOK, PieceType.QUEEN)) : pos.attacks_from_PtS(pieceType, s); if (Bitboard.AndWithSquare(ei.pinnedPieces[Us], s)!=0) { b &= Utils.LineBB[pos.square(PieceType.KING, Us), s]; } ei.attackedBy[Us, PieceType.ALL_PIECES] |= ei.attackedBy[Us, Pt] |= b; if ((b & ei.kingRing[Them])!=0) { ei.kingAttackersCount[Us]++; ei.kingAttackersWeight[Us] += KingAttackWeights[Pt]; var bb = b & ei.attackedBy[Them, PieceType.KING]; if (bb!=0) { ei.kingAdjacentZoneAttacksCount[Us] += Bitcount.popcount_Max15(bb); } } if (Pt == PieceType.QUEEN) { b &= ~(ei.attackedBy[Them, PieceType.KNIGHT] | ei.attackedBy[Them, PieceType.BISHOP] | ei.attackedBy[Them, PieceType.ROOK]); } var mob = Pt == PieceType.QUEEN ? Bitcount.popcount_Full(b & mobilityArea[Us]) : Bitcount.popcount_Max15(b & mobilityArea[Us]); mobility[Us] += MobilityBonus[Pt][mob]; if (Pt == PieceType.BISHOP || Pt == PieceType.KNIGHT) { // Bonus for outpost square if (Rank.relative_rank_CtSt(Us, s) >= Rank.RANK_4 && Rank.relative_rank_CtSt(Us, s) <= Rank.RANK_6 && (pos.pieces_CtPt(Them, PieceType.PAWN) & Utils.pawn_attack_span(Us, s))==0) { score += Outpost[Pt == PieceType.BISHOP ? 1 : 0][Bitboard.AndWithSquare(ei.attackedBy[Us, PieceType.PAWN], s)!=0 ? 1 : 0]; } // Bonus when behind a pawn if (Rank.relative_rank_CtSt(Us, s) < Rank.RANK_5 && Bitboard.AndWithSquare(pos.pieces_Pt(PieceType.PAWN), (s + Square.pawn_push(Us)))!=0) { score += MinorBehindPawn; } // Penalty for pawns on same color square of bishop if (Pt == PieceType.BISHOP) { score -= BishopPawns*ei.pi.pawns_on_same_color_squares(Us, s); } // An important Chess960 pattern: A cornered bishop blocked by a friendly // pawn diagonally in front of it is a very serious problem, especially // when that pawn is also blocked. if (Pt == PieceType.BISHOP && pos.is_chess960() && (s == Square.relative_square(Us, Square.SQ_A1) || s == Square.relative_square(Us, Square.SQ_H1))) { var d = Square.pawn_push(Us) + (Square.file_of(s) == File.FILE_A ? Square.DELTA_E : Square.DELTA_W); if (pos.piece_on(s + d) == Piece.make_piece(Us, PieceType.PAWN)) { score -= !pos.empty(s + d + Square.pawn_push(Us)) ? TrappedBishopA1H1*4 : pos.piece_on(s + d + d) == Piece.make_piece(Us, PieceType.PAWN) ? TrappedBishopA1H1*2 : TrappedBishopA1H1; } } } if (Pt == PieceType.ROOK) { // Bonus for aligning with enemy pawns on the same rank/file if (Rank.relative_rank_CtSt(Us, s) >= Rank.RANK_5) { var alignedPawns = pos.pieces_CtPt(Them, PieceType.PAWN) & Utils.PseudoAttacks[PieceType.ROOK, s]; if (alignedPawns!=0) { score += Bitcount.popcount_Max15(alignedPawns)*RookOnPawn; } } // Bonus when on an open or semi-open file if (ei.pi.semiopen_file(Us, Square.file_of(s)) != 0) { score += ei.pi.semiopen_file(Them, Square.file_of(s)) != 0 ? RookOnOpenFile : RookOnSemiOpenFile; } // Penalize when trapped by the king, even more if king cannot castle if (mob <= 3 && 0 == ei.pi.semiopen_file(Us, Square.file_of(s))) { var ksq = pos.square(PieceType.KING, Us); if (((Square.file_of(ksq) < File.FILE_E) == (Square.file_of(s) < Square.file_of(ksq))) && (Square.rank_of(ksq) == Square.rank_of(s) || Rank.relative_rank_CtSt(Us, ksq) == Rank.RANK_1) && 0 == ei.pi.semiopen_side(Us, Square.file_of(ksq), Square.file_of(s) < Square.file_of(ksq))) { score -= (TrappedRook - Score.make_score(mob*22, 0))*(1 + (pos.can_castle(Us) == 0 ? 1 : 0)); } } } } if (DoTrace) { add_IdxCtSt(Pt, Us, score); } // Recursively call evaluate_pieces() of next piece type until KING excluded return score - evaluate_pieces(NextPt, Them, DoTrace, pos, ei, mobility, mobilityArea); }
// init_eval_info() initializes king bitboards for given color adding // pawn attacks. To be done at the beginning of the evaluation. private static void init_eval_info(ColorT Us, Position pos, EvalInfo ei) { var Them = (Us == Color.WHITE ? Color.BLACK : Color.WHITE); var Down = (Us == Color.WHITE ? Square.DELTA_S : Square.DELTA_N); ei.pinnedPieces[Us] = pos.pinned_pieces(Us); var b = ei.attackedBy[Them, PieceType.KING] = pos.attacks_from_PtS(PieceType.KING, pos.square(PieceType.KING, Them)); ei.attackedBy[Them, PieceType.ALL_PIECES] |= b; ei.attackedBy[Us, PieceType.ALL_PIECES] |= ei.attackedBy[Us, PieceType.PAWN] = ei.pi.pawn_attacks(Us); // Init king safety tables only if we are going to use them if (pos.non_pawn_material(Us) >= Value.QueenValueMg) { ei.kingRing[Them] = b | Bitboard.shift_bb(Down, b); b &= ei.attackedBy[Us, PieceType.PAWN]; ei.kingAttackersCount[Us] = b!=0 ? Bitcount.popcount_Max15(b) : 0; ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0; } else { ei.kingRing[Them] = Bitboard.Create(0); ei.kingAttackersCount[Us] = 0; } }