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; }
// 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); }