// evaluate_space() computes the space evaluation for a given side. The // space evaluation is a simple bonus based on the number of safe squares // available for minor pieces on the central four files on ranks 2--4. Safe // squares one, two or three squares behind a friendly pawn are counted // twice. Finally, the space bonus is scaled by a weight taken from the // material hash table. The aim is to improve play on game opening. public static int evaluate_space(Position pos, EvalInfo ei, Color Us) { Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); // Find the safe squares for our pieces inside the area defined by // SpaceMask[]. A square is unsafe if it is attacked by an enemy // pawn, or if it is undefended and attacked by an enemy piece. Bitboard safe = SpaceMask[Us] & ~pos.pieces_color_piecetype(Us, PieceTypeS.PAWN) & ~ei.attackedBy[Them][PieceTypeS.PAWN] & (ei.attackedBy[Us][PieceTypeS.ALL_PIECES] | ~ei.attackedBy[Them][PieceTypeS.ALL_PIECES]); // Find all squares which are at most three squares behind some friendly pawn Bitboard behind = pos.pieces_color_piecetype(Us, PieceTypeS.PAWN); behind |= (Us == ColorS.WHITE ? behind >> 8 : behind << 8); behind |= (Us == ColorS.WHITE ? behind >> 16 : behind << 16); // Since SpaceMask[Us] is fully on our half of the board Debug.Assert((UInt32)(safe >> (Us == ColorS.WHITE ? 32 : 0)) == 0); // Count safe + (behind & safe) with a single popcount return(Bitcount.popcount((Us == ColorS.WHITE ? safe << 32 : safe >> 32) | (behind & safe))); }
// evaluate_pieces() assigns bonuses and penalties to the pieces of a given color public static Score evaluate_pieces(Position pos, EvalInfo ei, Score[] mobility, Bitboard[] mobilityArea, PieceType Pt, Color Us, bool Trace) { if (Us == ColorS.WHITE && Pt == PieceTypeS.KING) { return(ScoreS.SCORE_ZERO); } Bitboard b; Square s; Score score = ScoreS.SCORE_ZERO; PieceType NextPt = (Us == ColorS.WHITE ? Pt : (Pt + 1)); Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); Square[] pl = pos.list(Us, Pt); int plPos = 0; ei.attackedBy[Us][Pt] = 0; while ((s = pl[plPos++]) != SquareS.SQ_NONE) { // Find attacked squares, including x-ray attacks for bishops and rooks b = Pt == PieceTypeS.BISHOP ? BitBoard.attacks_bb_SBBPT(s, pos.pieces() ^ pos.pieces_color_piecetype(Us, PieceTypeS.QUEEN), PieceTypeS.BISHOP) : Pt == PieceTypeS.ROOK ? BitBoard.attacks_bb_SBBPT(s, pos.pieces() ^ pos.pieces_color_piecetype(Us, PieceTypeS.ROOK, PieceTypeS.QUEEN), PieceTypeS.ROOK) : pos.attacks_from_square_piecetype(s, Pt); if ((ei.pinnedPieces[Us] & BitBoard.SquareBB[s]) != 0) { b &= BitBoard.LineBB[pos.king_square(Us)][s]; } ei.attackedBy[Us][PieceTypeS.ALL_PIECES] |= ei.attackedBy[Us][Pt] |= b; if ((b & ei.kingRing[Them]) != 0) { ei.kingAttackersCount[Us]++; ei.kingAttackersWeight[Us] += KingAttackWeights[Pt]; Bitboard bb = (b & ei.attackedBy[Them][PieceTypeS.KING]); if (bb != 0) { ei.kingAdjacentZoneAttacksCount[Us] += Bitcount.popcount_Max15(bb); } } if (Pt == PieceTypeS.QUEEN) { b &= ~(ei.attackedBy[Them][PieceTypeS.KNIGHT] | ei.attackedBy[Them][PieceTypeS.BISHOP] | ei.attackedBy[Them][PieceTypeS.ROOK]); } int mob = (Pt != PieceTypeS.QUEEN ? Bitcount.popcount_Max15(b & mobilityArea[Us]) : Bitcount.popcount(b & mobilityArea[Us])); mobility[Us] += MobilityBonus[Pt][mob]; // Decrease score if we are attacked by an enemy pawn. The remaining part // of threat evaluation must be done later when we have full attack info. if ((ei.attackedBy[Them][PieceTypeS.PAWN] & BitBoard.SquareBB[s]) != 0) { score -= ThreatenedByPawn[Pt]; } if (Pt == PieceTypeS.BISHOP || Pt == PieceTypeS.KNIGHT) { // Penalty for bishop with same coloured pawns if (Pt == PieceTypeS.BISHOP) { score -= BishopPawns * ei.pi.pawns_on_same_color_squares(Us, s); } // Bishop and knight outposts squares if (0 == (pos.pieces_color_piecetype(Them, PieceTypeS.PAWN) & BitBoard.pawn_attack_span(Us, s))) { score += evaluate_outposts(pos, ei, s, Pt, Us); } // Bishop or knight behind a pawn if (Types.relative_rank_square(Us, s) < RankS.RANK_5 && (pos.pieces_piecetype(PieceTypeS.PAWN) & BitBoard.SquareBB[(s + Types.pawn_push(Us))]) != 0) { score += MinorBehindPawn; } } if (Pt == PieceTypeS.ROOK) { // Rook piece attacking enemy pawns on the same rank/file if (Types.relative_rank_square(Us, s) >= RankS.RANK_5) { Bitboard pawns = pos.pieces_color_piecetype(Them, PieceTypeS.PAWN) & BitBoard.PseudoAttacks[PieceTypeS.ROOK][s]; if (pawns != 0) { score += Bitcount.popcount_Max15(pawns) * RookOnPawn; } } // Give a bonus for a rook on a open or semi-open file if (ei.pi.semiopen_file(Us, Types.file_of(s)) != 0) { score += ei.pi.semiopen_file(Them, Types.file_of(s)) != 0 ? RookOpenFile : RookSemiopenFile; } if (mob > 3 || ei.pi.semiopen_file(Us, Types.file_of(s)) != 0) { continue; } Square ksq = pos.king_square(Us); // Penalize rooks which are trapped by a king. Penalize more if the // king has lost its castling capability. if (((Types.file_of(ksq) < FileS.FILE_E) == (Types.file_of(s) < Types.file_of(ksq))) && (Types.rank_of(ksq) == Types.rank_of(s) || Types.relative_rank_square(Us, ksq) == RankS.RANK_1) && 0 == ei.pi.semiopen_side(Us, Types.file_of(ksq), Types.file_of(s) < Types.file_of(ksq))) { score -= (TrappedRook - Types.make_score(mob * 8, 0)) * (1 + (pos.can_castle_color(Us) == 0 ? 1 : 0)); } } // 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 == PieceTypeS.BISHOP && pos.is_chess960() != 0 && (s == Types.relative_square(Us, SquareS.SQ_A1) || s == Types.relative_square(Us, SquareS.SQ_H1))) { Square d = Types.pawn_push(Us) + (Types.file_of(s) == FileS.FILE_A ? SquareS.DELTA_E : SquareS.DELTA_W); if (pos.piece_on(s + d) == Types.make_piece(Us, PieceTypeS.PAWN)) { score -= !pos.empty(s + d + Types.pawn_push(Us)) ? TrappedBishopA1H1 * 4 : pos.piece_on(s + d + d) == Types.make_piece(Us, PieceTypeS.PAWN) ? TrappedBishopA1H1 * 2 : TrappedBishopA1H1; } } } if (Trace) { Tracing.terms[Us][Pt] = score; } return(score - evaluate_pieces(pos, ei, mobility, mobilityArea, NextPt, Them, Trace)); }