internal static EvalInfo GetObject() { int slotID = System.Threading.Thread.CurrentThread.ManagedThreadId & Constants.BROKER_SLOT_MASK; if (_cnt[slotID] == _pool[slotID].Length) { int poolLength = _pool[slotID].Length; EvalInfo[] temp = new EvalInfo[poolLength + Constants.BrokerCapacity]; Array.Copy(_pool[slotID], temp, poolLength); for (int i = 0; i < Constants.BrokerCapacity; i++) { temp[poolLength + i] = new EvalInfo(); } _pool[slotID] = temp; } return _pool[slotID][_cnt[slotID]++]; }
internal static void init() { for (int i = 0; i < Constants.BROKER_SLOTS; i++) { _pool[i] = new EvalInfo[0]; } }
// evaluate_pieces_of_color<>() assigns bonuses and penalties to all the // pieces of a given color. private static int evaluate_pieces_of_color(int Us, bool Trace, Position pos, EvalInfo ei, ref int mobility) { var Them = (Us == ColorC.WHITE ? ColorC.BLACK : ColorC.WHITE); mobility = ScoreC.SCORE_ZERO; // Do not include in mobility squares protected by enemy pawns or occupied by our pieces var mobilityArea = ~(ei.attackedBy[Them][PieceTypeC.PAWN] | pos.byColorBB[Us]); #region Evaluate pieces ulong between = 0; var plPos = 0; int s, ksq; int mob; int f; int score, scores = ScoreC.SCORE_ZERO; var attackedByThemKing = ei.attackedBy[Them][PieceTypeC.KING]; var attackedByThemPawn = ei.attackedBy[Them][PieceTypeC.PAWN]; var kingRingThem = ei.kingRing[Them]; for (var Piece = PieceTypeC.KNIGHT; Piece < PieceTypeC.KING; Piece++) { score = ScoreC.SCORE_ZERO; ei.attackedBy[Us][Piece] = 0; var pl = pos.pieceList[Us][Piece]; plPos = 0; while ((s = pl[plPos++]) != SquareC.SQ_NONE) { // Find attacked squares, including x-ray attacks for bishops and rooks if (Piece == PieceTypeC.KNIGHT) { between = Utils.StepAttacksBB_KNIGHT[s]; } else if (Piece == PieceTypeC.QUEEN) { #if X64 b = Utils.BAttacks[s][(((pos.occupied_squares & Utils.BMasks[s]) * Utils.BMagics[s]) >> Utils.BShifts[s])] | Utils.RAttacks[s][(((pos.occupied_squares & Utils.RMasks[s]) * Utils.RMagics[s]) >> Utils.RShifts[s])]; #else between = Utils.bishop_attacks_bb(s, pos.occupied_squares) | Utils.rook_attacks_bb(s, pos.occupied_squares); #endif } else if (Piece == PieceTypeC.BISHOP) { #if X64 b = Utils.BAttacks[s][((( (pos.occupied_squares ^ (pos.byTypeBB[PieceTypeC.QUEEN] & pos.byColorBB[Us])) & Utils.BMasks[s]) * Utils.BMagics[s]) >> Utils.BShifts[s])]; #else between = Utils.bishop_attacks_bb(s, pos.occupied_squares ^ pos.pieces_PTC(PieceTypeC.QUEEN, Us)); #endif } else if (Piece == PieceTypeC.ROOK) { #if X64 b = Utils.RAttacks[s][((( (pos.occupied_squares ^ ((pos.byTypeBB[PieceTypeC.ROOK] | pos.byTypeBB[PieceTypeC.QUEEN]) & pos.byColorBB[Us])) & Utils.RMasks[s]) * Utils.RMagics[s]) >> Utils.RShifts[s])]; #else between = Utils.rook_attacks_bb( s, pos.occupied_squares ^ pos.pieces(PieceTypeC.ROOK, PieceTypeC.QUEEN, Us)); #endif } // Update attack info ei.attackedBy[Us][Piece] |= between; // King attacks if ((between & kingRingThem) != 0) { ei.kingAttackersCount[Us]++; ei.kingAttackersWeight[Us] += KingAttackWeights[Piece]; var bb = (between & attackedByThemKing); //ei.attackedBy[Them][PieceTypeC.KING]); if (bb != 0) { #if X64 bb -= (bb >> 1) & 0x5555555555555555UL; bb = ((bb >> 2) & 0x3333333333333333UL) + (bb & 0x3333333333333333UL); ei.kingAdjacentZoneAttacksCount[Us] += (int)((bb * 0x1111111111111111UL) >> 60); #else ei.kingAdjacentZoneAttacksCount[Us] += Bitcount.popcount_1s_Max15(bb); #endif } } // Mobility #if X64 Bitboard bmob = b & mobilityArea; if (Piece != PieceTypeC.QUEEN) { bmob -= (bmob >> 1) & 0x5555555555555555UL; bmob = ((bmob >> 2) & 0x3333333333333333UL) + (bmob & 0x3333333333333333UL); mob = (int)((bmob * 0x1111111111111111UL) >> 60); } else { bmob -= ((bmob >> 1) & 0x5555555555555555UL); bmob = ((bmob >> 2) & 0x3333333333333333UL) + (bmob & 0x3333333333333333UL); bmob = ((bmob >> 4) + bmob) & 0x0F0F0F0F0F0F0F0FUL; mob = (int)((bmob * 0x0101010101010101UL) >> 56); } #else mob = (Piece != PieceTypeC.QUEEN ? Bitcount.popcount_1s_Max15(between & mobilityArea) : Bitcount.popcount_1s_Full(between & mobilityArea)); #endif mobility += MobilityBonus[Piece][mob]; // Decrease score if we are attacked by an enemy pawn. Remaining part // of threat evaluation must be done later when we have full attack info. if ((attackedByThemPawn & Utils.SquareBB[s]) != 0) { score -= ThreatenedByPawnPenalty[Piece]; } else if ((Piece == PieceTypeC.BISHOP) && ((Utils.PseudoAttacks[Piece][pos.pieceList[Them][PieceTypeC.KING][0]] & Utils.SquareBB[s]) != 0)) { between = Utils.BetweenBB[s][pos.pieceList[Them][PieceTypeC.KING][0]] & pos.occupied_squares; if (!Utils.more_than_one(between)) { score += Utils.make_score(15, 25); } } // Bishop and knight outposts squares if ((Piece == PieceTypeC.BISHOP || Piece == PieceTypeC.KNIGHT) && (((pos.byTypeBB[PieceTypeC.PAWN] & pos.byColorBB[Them]) & Utils.AttackSpanMask[Us][s]) == 0)) { #region Evaluate outposts inlined // evaluate_outposts() evaluates bishop and knight outposts squares // Initial bonus based on square var bonus = OutpostBonus[Piece == PieceTypeC.BISHOP ? 1 : 0][s ^ (Us * 56)]; // Increase bonus if supported by pawn, especially if the opponent has // no minor piece which can exchange the outpost piece. if ((bonus != 0) && ((ei.attackedBy[Us][PieceTypeC.PAWN] & Utils.SquareBB[s]) != 0)) { if (((pos.byTypeBB[PieceTypeC.KNIGHT] & pos.byColorBB[Them]) == 0) && (((((0xAA55AA55AA55AA55UL & Utils.SquareBB[s]) != 0) ? 0xAA55AA55AA55AA55UL : ~0xAA55AA55AA55AA55UL) & (pos.byTypeBB[PieceTypeC.BISHOP] & pos.byColorBB[Them])) == 0)) { bonus += bonus + bonus / 2; } else { bonus += bonus / 2; } } score += ((bonus << 16) + bonus); // Utils.make_score(bonus, bonus); #endregion } if ((Piece == PieceTypeC.ROOK || Piece == PieceTypeC.QUEEN) && Utils.relative_rank_CS(Us, s) >= RankC.RANK_5) { // Major piece on 7th rank if (Utils.relative_rank_CS(Us, s) == RankC.RANK_7 && Utils.relative_rank_CS(Us, pos.king_square(Them)) == RankC.RANK_8) score += (Piece == PieceTypeC.ROOK ? RookOn7thBonus : QueenOn7thBonus); // Major piece attacking pawns on the same rank Bitboard pawns = pos.pieces_PTC(PieceTypeC.PAWN, Them) & Utils.rank_bb_S(s); if (pawns != 0) { score += (Piece == PieceTypeC.ROOK ? RookOnPawnBonus : QueenOnPawnBonus) * Bitcount.popcount_1s_Max15(pawns); } } // Special extra evaluation for bishops if (pos.chess960 && (Piece == PieceTypeC.BISHOP)) { // 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 (s == Utils.relative_square(Us, SquareC.SQ_A1) || s == Utils.relative_square(Us, SquareC.SQ_H1)) { var d = Utils.pawn_push(Us) + (Utils.file_of(s) == FileC.FILE_A ? SquareC.DELTA_E : SquareC.DELTA_W); if (pos.piece_on(s + d) == Utils.make_piece(Us, PieceTypeC.PAWN)) { if (!pos.is_empty(s + d + Utils.pawn_push(Us))) { score -= 2 * TrappedBishopA1H1Penalty; } else if (pos.piece_on(s + 2 * d) == Utils.make_piece(Us, PieceTypeC.PAWN)) { score -= TrappedBishopA1H1Penalty; } else { score -= TrappedBishopA1H1Penalty / 2; } } } } // Special extra evaluation for rooks if (Piece == PieceTypeC.ROOK) { // Open and half-open files f = (s & 7); var halfOpenUs = ((Us == ColorC.WHITE) ? (ei.pi.halfOpenFilesWHITE & (1 << f)) : (ei.pi.halfOpenFilesBLACK & (1 << f))) != 0; if (halfOpenUs) { if (((Them == ColorC.WHITE) ? (ei.pi.halfOpenFilesWHITE & (1 << f)) : (ei.pi.halfOpenFilesBLACK & (1 << f))) != 0) { score += RookOpenFileBonus; } else { score += RookHalfOpenFileBonus; } } // Penalize rooks which are trapped inside a king. Penalize more if // king has lost right to castle. if (mob > 6 || halfOpenUs) { continue; } ksq = pos.pieceList[Us][PieceTypeC.KING][0]; if (((ksq >> 3) ^ (Us * 7)) == RankC.RANK_1 || (ksq >> 3) == (s >> 3)) { if ((ksq & 7) >= FileC.FILE_E) { if (f > (ksq & 7)) { // Is there a half-open file between the king and the edge of the board? if (((Us == ColorC.WHITE) ? (ei.pi.halfOpenFilesWHITE & ~((1 << ((ksq & 7) + 1)) - 1)) : (ei.pi.halfOpenFilesBLACK & ~((1 << ((ksq & 7) + 1)) - 1))) == 0) { score -= ((((pos.st.castleRights & (CastleRightC.WHITE_ANY << (Us << 1))) != 0) ? (TrappedRookPenalty - mob * 16) / 2 : (TrappedRookPenalty - mob * 16)) << 16); } } } else { if (f < (ksq & 7)) { // Is there a half-open file between the king and the edge of the board? if (((Us == ColorC.WHITE) ? (ei.pi.halfOpenFilesWHITE & ((1 << (ksq & 7)) - 1)) : (ei.pi.halfOpenFilesBLACK & ((1 << (ksq & 7)) - 1))) == 0) { score -= ((((pos.st.castleRights & (CastleRightC.WHITE_ANY << (Us << 1))) != 0) ? (TrappedRookPenalty - mob * 16) / 2 : (TrappedRookPenalty - mob * 16)) << 16); } } } } } } scores += score; if (Trace) { TracedScores[Us][Piece] = score; } } #endregion // Sum up all attacked squares ei.attackedBy[Us][0] = ei.attackedBy[Us][PieceTypeC.PAWN] | ei.attackedBy[Us][PieceTypeC.KNIGHT] | ei.attackedBy[Us][PieceTypeC.BISHOP] | ei.attackedBy[Us][PieceTypeC.ROOK] | ei.attackedBy[Us][PieceTypeC.QUEEN] | ei.attackedBy[Us][PieceTypeC.KING]; return scores; }
// evaluate_king<>() assigns bonuses and penalties to a king of a given color private static int evaluate_king( int Us, bool Trace, Position pos, EvalInfo ei, ref Value marginsWHITE, ref Value marginsBLACK) { var Them = (Us == ColorC.WHITE ? ColorC.BLACK : ColorC.WHITE); ulong undefended, b, b1, b2, safe; int attackUnits; int kingScore; var ksq = pos.pieceList[Us][PieceTypeC.KING][0]; // King shelter and enemy pawns storm var score = ei.pi.king_safety(Us, pos, ksq); // King safety. This is quite complicated, and is almost certainly far // from optimally tuned. if (ei.kingAttackersCount[Them] >= 2 && (ei.kingAdjacentZoneAttacksCount[Them] != 0)) { // Find the attacked squares around the king which has no defenders // apart from the king itself undefended = ei.attackedBy[Them][0] & ei.attackedBy[Us][PieceTypeC.KING]; undefended &= ~(ei.attackedBy[Us][PieceTypeC.PAWN] | ei.attackedBy[Us][PieceTypeC.KNIGHT] | ei.attackedBy[Us][PieceTypeC.BISHOP] | ei.attackedBy[Us][PieceTypeC.ROOK] | ei.attackedBy[Us][PieceTypeC.QUEEN]); #if X64 // Initialize the 'attackUnits' variable, which is used later on as an // index to the KingDangerTable[] 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, the square of the // king, and the quality of the pawn shelter. b = undefended - ((undefended >> 1) & 0x5555555555555555UL); b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits = Math.Min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + ((int)((b * 0x1111111111111111UL) >> 60))) + InitKingDanger[(ksq ^ (Us * 56))] - ((((score) + 32768) & ~0xffff) / 0x10000) / 32; // Analyse enemy's safe queen contact checks. First find undefended // squares around the king attacked by enemy queen... b = undefended & ei.attackedBy[Them][PieceTypeC.QUEEN] & ~(pos.byColorBB[Them]); if (b != 0) { // ...then remove squares not supported by another enemy piece b &= (ei.attackedBy[Them][PieceTypeC.PAWN] | ei.attackedBy[Them][PieceTypeC.KNIGHT] | ei.attackedBy[Them][PieceTypeC.BISHOP] | ei.attackedBy[Them][PieceTypeC.ROOK]); if (b != 0) { b -= (b >> 1) & 0x5555555555555555UL; b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits += QueenContactCheckBonus * ((int)((b * 0x1111111111111111UL) >> 60)) * (Them == pos.sideToMove ? 2 : 1); } } // Analyse enemy's safe rook contact checks. First find undefended // squares around the king attacked by enemy rooks... b = undefended & ei.attackedBy[Them][PieceTypeC.ROOK] & ~pos.byColorBB[Them]; // Consider only squares where the enemy rook gives check b &= Utils.PseudoAttacks_ROOK[ksq]; if (b != 0) { // ...then remove squares not supported by another enemy piece b &= (ei.attackedBy[Them][PieceTypeC.PAWN] | ei.attackedBy[Them][PieceTypeC.KNIGHT] | ei.attackedBy[Them][PieceTypeC.BISHOP] | ei.attackedBy[Them][PieceTypeC.QUEEN]); if (b != 0) { b -= (b >> 1) & 0x5555555555555555UL; b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits += RookContactCheckBonus * ((int)((b * 0x1111111111111111UL) >> 60)) * (Them == pos.sideToMove ? 2 : 1); } } // Analyse enemy's safe distance checks for sliders and knights safe = ~(pos.byColorBB[Them] | ei.attackedBy[Us][0]); b1 = (Utils.RAttacks[ksq][(((pos.occupied_squares & Utils.RMasks[ksq]) * Utils.RMagics[ksq]) >> Utils.RShifts[ksq])]) & safe; b2 = (Utils.BAttacks[ksq][(((pos.occupied_squares & Utils.BMasks[ksq]) * Utils.BMagics[ksq]) >> Utils.BShifts[ksq])]) & safe; // Enemy queen safe checks b = (b1 | b2) & ei.attackedBy[Them][PieceTypeC.QUEEN]; if (b != 0) { b -= (b >> 1) & 0x5555555555555555UL; b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits += QueenCheckBonus * ((int)((b * 0x1111111111111111UL) >> 60)); } // Enemy rooks safe checks b = b1 & ei.attackedBy[Them][PieceTypeC.ROOK]; if (b != 0) { b -= (b >> 1) & 0x5555555555555555UL; b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits += RookCheckBonus * ((int)((b * 0x1111111111111111UL) >> 60)); } // Enemy bishops safe checks b = b2 & ei.attackedBy[Them][PieceTypeC.BISHOP]; if (b != 0) { b -= (b >> 1) & 0x5555555555555555UL; b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits += BishopCheckBonus * ((int)((b * 0x1111111111111111UL) >> 60)); } // Enemy knights safe checks b = Utils.StepAttacksBB_KNIGHT[ksq] & ei.attackedBy[Them][PieceTypeC.KNIGHT] & safe; if (b != 0) { b -= (b >> 1) & 0x5555555555555555UL; b = ((b >> 2) & 0x3333333333333333UL) + (b & 0x3333333333333333UL); attackUnits += KnightCheckBonus * ((int)((b * 0x1111111111111111UL) >> 60)); } #else // Initialize the 'attackUnits' variable, which is used later on as an // index to the KingDangerTable[] 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, the square of the // king, and the quality of the pawn shelter. attackUnits = Math.Min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + Bitcount.popcount_1s_Max15(undefended)) + InitKingDanger[(ksq ^ (Us * 56))] - ((((score) + 32768) & ~0xffff) / 0x10000) / 32; // Analyse enemy's safe queen contact checks. First find undefended // squares around the king attacked by enemy queen... b = undefended & ei.attackedBy[Them][PieceTypeC.QUEEN] & ~(pos.byColorBB[Them]); if (b != 0) { // ...then remove squares not supported by another enemy piece b &= (ei.attackedBy[Them][PieceTypeC.PAWN] | ei.attackedBy[Them][PieceTypeC.KNIGHT] | ei.attackedBy[Them][PieceTypeC.BISHOP] | ei.attackedBy[Them][PieceTypeC.ROOK]); if (b != 0) { attackUnits += QueenContactCheckBonus * Bitcount.popcount_1s_Max15(b) * (Them == pos.sideToMove ? 2 : 1); } } // Analyse enemy's safe rook contact checks. First find undefended // squares around the king attacked by enemy rooks... b = undefended & ei.attackedBy[Them][PieceTypeC.ROOK] & ~pos.byColorBB[Them]; // Consider only squares where the enemy rook gives check b &= Utils.PseudoAttacks_ROOK[ksq]; if (b != 0) { // ...then remove squares not supported by another enemy piece b &= (ei.attackedBy[Them][PieceTypeC.PAWN] | ei.attackedBy[Them][PieceTypeC.KNIGHT] | ei.attackedBy[Them][PieceTypeC.BISHOP] | ei.attackedBy[Them][PieceTypeC.QUEEN]); if (b != 0) { attackUnits += RookContactCheckBonus * Bitcount.popcount_1s_Max15(b) * (Them == pos.sideToMove ? 2 : 1); } } // Analyse enemy's safe distance checks for sliders and knights safe = ~(pos.byColorBB[Them] | ei.attackedBy[Us][0]); b1 = pos.attacks_from_ROOK(ksq) & safe; b2 = pos.attacks_from_BISHOP(ksq) & safe; // Enemy queen safe checks b = (b1 | b2) & ei.attackedBy[Them][PieceTypeC.QUEEN]; if (b != 0) { attackUnits += QueenCheckBonus * Bitcount.popcount_1s_Max15(b); } // Enemy rooks safe checks b = b1 & ei.attackedBy[Them][PieceTypeC.ROOK]; if (b != 0) { attackUnits += RookCheckBonus * Bitcount.popcount_1s_Max15(b); } // Enemy bishops safe checks b = b2 & ei.attackedBy[Them][PieceTypeC.BISHOP]; if (b != 0) { attackUnits += BishopCheckBonus * Bitcount.popcount_1s_Max15(b); } // Enemy knights safe checks b = Utils.StepAttacksBB_KNIGHT[ksq] & ei.attackedBy[Them][PieceTypeC.KNIGHT] & safe; if (b != 0) { attackUnits += KnightCheckBonus * Bitcount.popcount_1s_Max15(b); } #endif // To index KingDangerTable[] attackUnits must be in [0, 99] range attackUnits = Math.Min(99, Math.Max(0, attackUnits)); // Finally, extract the king danger score from the KingDangerTable[] // array and subtract the score from evaluation. Set also margins[] // value that will be used for pruning because this value can sometimes // be very big, and so capturing a single attacking piece can therefore // result in a score change far bigger than the value of the captured piece. kingScore = KingDangerTable[Us == Search.RootColor ? 1 : 0][attackUnits]; score -= kingScore; if (Us == ColorC.WHITE) { marginsWHITE += (((kingScore + 32768) & ~0xffff) / 0x10000); } else { marginsBLACK += (((kingScore + 32768) & ~0xffff) / 0x10000); } } if (Trace) { TracedScores[Us][PieceTypeC.KING] = score; } return score; }
// evaluate_threats<>() assigns bonuses according to the type of attacking piece // and the type of attacked one. private static int evaluate_threats(int Us, Position pos, EvalInfo ei) { var Them = (Us == ColorC.WHITE ? ColorC.BLACK : ColorC.WHITE); ulong b, undefendedMinors, weakEnemies; var score = ScoreC.SCORE_ZERO; // Undefended minors get penalized even if not under attack undefendedMinors = pos.byColorBB[Them] & (pos.byTypeBB[PieceTypeC.BISHOP] | pos.byTypeBB[PieceTypeC.KNIGHT]) & ~ei.attackedBy[Them][0]; if (undefendedMinors != 0) { score += (Utils.more_than_one(undefendedMinors)) ? UndefendedMinorPenalty * 2 : UndefendedMinorPenalty; } // Enemy pieces not defended by a pawn and under our attack weakEnemies = pos.byColorBB[Them] & ~ei.attackedBy[Them][PieceTypeC.PAWN] & ei.attackedBy[Us][0]; if (weakEnemies == 0) { return score; } // Add bonus according to type of attacked enemy piece and to the // type of attacking piece, from knights to queens. Kings are not // considered because are already handled in king evaluation. for (var pt1 = PieceTypeC.KNIGHT; pt1 < PieceTypeC.KING; pt1++) { b = ei.attackedBy[Us][pt1] & weakEnemies; if (b != 0) { for (var pt2 = PieceTypeC.PAWN; pt2 < PieceTypeC.KING; pt2++) { if ((b & pos.byTypeBB[pt2]) != 0) { score += ThreatBonus[pt1][pt2]; } } } } return score; }
// 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(int Us, Position pos, EvalInfo ei) { var Them = (Us == ColorC.WHITE ? ColorC.BLACK : ColorC.WHITE); var b = ei.attackedBy[Them][PieceTypeC.KING] = Utils.StepAttacksBB_KING[pos.pieceList[Them][PieceTypeC.KING][0]]; ei.attackedBy[Us][PieceTypeC.PAWN] = (Us == ColorC.WHITE) ? ei.pi.pawnAttacksWHITE : ei.pi.pawnAttacksBLACK; // Init king safety tables only if we are going to use them if ((pos.pieceCount[Us][PieceTypeC.QUEEN] != 0) && (Us == 0 ? pos.st.npMaterialWHITE : pos.st.npMaterialBLACK) > Constants.QueenValueMidgame + Constants.RookValueMidgame) { ei.kingRing[Them] = (b | (Us == ColorC.WHITE ? b >> 8 : b << 8)); b &= ei.attackedBy[Us][PieceTypeC.PAWN]; ei.kingAttackersCount[Us] = (b != 0) ? Bitcount.popcount_1s_Max15(b) / 2 : 0; ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0; } else { ei.kingRing[Them] = 0; ei.kingAttackersCount[Us] = 0; } }
// 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. private static int evaluate_space(int Us, Position pos, EvalInfo ei) { var Them = (Us == ColorC.WHITE ? ColorC.BLACK : ColorC.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. var behind = pos.byTypeBB[PieceTypeC.PAWN] & pos.byColorBB[Us]; var safe = SpaceMask[Us] & ~behind & ~ei.attackedBy[Them][PieceTypeC.PAWN] & (ei.attackedBy[Us][0] | ~ei.attackedBy[Them][0]); // Find all squares which are at most three squares behind some friendly pawn behind |= (Us == ColorC.WHITE ? behind >> 8 : behind << 8); behind |= (Us == ColorC.WHITE ? behind >> 16 : behind << 16); return Bitcount.popcount_1s_Max15(safe) + Bitcount.popcount_1s_Max15(behind & safe); }
// evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides, this is quite // conservative and returns a winning score only when we are very sure that the pawn is winning. private static int evaluate_unstoppable_pawns(Position pos, EvalInfo ei) { ulong b, b2, blockers, supporters, queeningPath, candidates; int s, blockSq, queeningSquare; int c, winnerSide, loserSide; bool pathDefended, opposed; int pliesToGo = 0, movesToGo, oppMovesToGo = 0, sacptg, blockersCount, minKingDist, kingptg, d; int pliesToQueenWHITE = 256, pliesToQueenBLACK = 256, pliesToQueenWinner = 256; // Step 1. Hunt for unstoppable passed pawns. If we find at least one, // record how many plies are required for promotion. for (c = ColorC.WHITE; c <= ColorC.BLACK; c++) { // Skip if other side has non-pawn pieces if (pos.non_pawn_material(Utils.flip_C(c)) != 0) { continue; } b = ei.pi.passed_pawns(c); while (b != 0) { s = Utils.pop_lsb(ref b); queeningSquare = Utils.relative_square(c, Utils.make_square(Utils.file_of(s), RankC.RANK_8)); queeningPath = Utils.forward_bb(c, s); // Compute plies to queening and check direct advancement movesToGo = Utils.rank_distance(s, queeningSquare) - (Utils.relative_rank_CS(c, s) == RankC.RANK_2 ? 1 : 0); oppMovesToGo = Utils.square_distance(pos.king_square(Utils.flip_C(c)), queeningSquare) - ((c != pos.sideToMove) ? 1 : 0); pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath); if (movesToGo >= oppMovesToGo && !pathDefended) { continue; } // Opponent king cannot block because path is defended and position // is not in check. So only friendly pieces can be blockers. Debug.Assert(!pos.in_check()); Debug.Assert((queeningPath & pos.occupied_squares) == (queeningPath & pos.pieces_C(c))); // Add moves needed to free the path from friendly pieces and retest condition movesToGo += Bitcount.popcount_1s_Max15(queeningPath & pos.pieces_C(c)); if (movesToGo >= oppMovesToGo && !pathDefended) { continue; } pliesToGo = 2 * movesToGo - ((c == pos.sideToMove) ? 1 : 0); if (c == ColorC.WHITE) { pliesToQueenWHITE = Math.Min(pliesToQueenWHITE, pliesToGo); } else { pliesToQueenBLACK = Math.Min(pliesToQueenBLACK, pliesToGo); } } } // Step 2. If either side cannot promote at least three plies before the other side then situation // becomes too complex and we give up. Otherwise we determine the possibly "winning side" if (Math.Abs(pliesToQueenWHITE - pliesToQueenBLACK) < 3) { return ScoreC.SCORE_ZERO; } winnerSide = (pliesToQueenWHITE < pliesToQueenBLACK ? ColorC.WHITE : ColorC.BLACK); pliesToQueenWinner = (winnerSide == ColorC.WHITE) ? pliesToQueenWHITE : pliesToQueenBLACK; loserSide = Utils.flip_C(winnerSide); // Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss? b = candidates = pos.pieces_PTC(PieceTypeC.PAWN, loserSide); while (b != 0) { s = Utils.pop_lsb(ref b); // Compute plies from queening queeningSquare = Utils.relative_square(loserSide, Utils.make_square(Utils.file_of(s), RankC.RANK_8)); movesToGo = Utils.rank_distance(s, queeningSquare) - ((Utils.relative_rank_CS(loserSide, s) == RankC.RANK_2) ? 1 : 0); pliesToGo = 2 * movesToGo - ((loserSide == pos.sideToMove) ? 1 : 0); // Check if (without even considering any obstacles) we're too far away or doubled if ((pliesToQueenWinner + 3 <= pliesToGo) || ((Utils.forward_bb(loserSide, s) & pos.pieces_PTC(PieceTypeC.PAWN, loserSide)) != 0)) { Utils.xor_bit(ref candidates, s); } } // If any candidate is already a passed pawn it _may_ promote in time. We give up. if ((candidates & ei.pi.passed_pawns(loserSide)) != 0) { return ScoreC.SCORE_ZERO; } // Step 4. Check new passed pawn creation through king capturing and pawn sacrifices b = candidates; while (b != 0) { s = Utils.pop_lsb(ref b); sacptg = blockersCount = 0; minKingDist = kingptg = 256; // Compute plies from queening queeningSquare = Utils.relative_square(loserSide, Utils.make_square(Utils.file_of(s), RankC.RANK_8)); movesToGo = Utils.rank_distance(s, queeningSquare) - ((Utils.relative_rank_CS(loserSide, s) == RankC.RANK_2) ? 1 : 0); pliesToGo = 2 * movesToGo - ((loserSide == pos.sideToMove) ? 1 : 0); // Generate list of blocking pawns and supporters supporters = Utils.adjacent_files_bb(Utils.file_of(s)) & candidates; opposed = (Utils.forward_bb(loserSide, s) & pos.pieces_PTC(PieceTypeC.PAWN, winnerSide)) != 0; blockers = Utils.passed_pawn_mask(loserSide, s) & pos.pieces_PTC(PieceTypeC.PAWN, winnerSide); Debug.Assert(blockers != 0); // How many plies does it take to remove all the blocking pawns? while (blockers != 0) { blockSq = Utils.pop_lsb(ref blockers); movesToGo = 256; // Check pawns that can give support to overcome obstacle, for instance // black pawns: a4, b4 white: b2 then pawn in b4 is giving support. if (!opposed) { b2 = supporters & Utils.in_front_bb_CS(winnerSide, blockSq + Utils.pawn_push(winnerSide)); while (b2 != 0) // This while-loop could be replaced with LSB/MSB (depending on color) { d = Utils.square_distance(blockSq, Utils.pop_lsb(ref b2)) - 2; movesToGo = Math.Min(movesToGo, d); } } // Check pawns that can be sacrificed against the blocking pawn b2 = Utils.attack_span_mask(winnerSide, blockSq) & candidates & ~(1UL << s); while (b2 != 0) // This while-loop could be replaced with LSB/MSB (depending on color) { d = Utils.square_distance(blockSq, Utils.pop_lsb(ref b2)) - 2; movesToGo = Math.Min(movesToGo, d); } // If obstacle can be destroyed with an immediate pawn exchange / sacrifice, // it's not a real obstacle and we have nothing to add to pliesToGo. if (movesToGo <= 0) { continue; } // Plies needed to sacrifice against all the blocking pawns sacptg += movesToGo * 2; blockersCount++; // Plies needed for the king to capture all the blocking pawns d = Utils.square_distance(pos.king_square(loserSide), blockSq); minKingDist = Math.Min(minKingDist, d); kingptg = (minKingDist + blockersCount) * 2; } // Check if pawn sacrifice plan _may_ save the day if (pliesToQueenWinner + 3 > pliesToGo + sacptg) { return ScoreC.SCORE_ZERO; } // Check if king capture plan _may_ save the day (contains some false positives) if (pliesToQueenWinner + 3 > pliesToGo + kingptg) { return ScoreC.SCORE_ZERO; } } // Winning pawn is unstoppable and will promote as first, return big score var score = Utils.make_score(0, 0x500 - 0x20 * pliesToQueenWinner); return winnerSide == ColorC.WHITE ? score : -score; }
// evaluate_passed_pawns<>() evaluates the passed pawns of the given color private static int evaluate_passed_pawns(int Us, Position pos, EvalInfo ei) { var Them = (Us == ColorC.WHITE ? ColorC.BLACK : ColorC.WHITE); ulong b, squaresToQueen, defendedSquares, unsafeSquares, supportingPawns; var score = ScoreC.SCORE_ZERO; b = (Us == ColorC.WHITE) ? ei.pi.passedPawnsWHITE : ei.pi.passedPawnsBLACK; if (b == 0) { return ScoreC.SCORE_ZERO; } do { #if X64 Bitboard bb = b; b &= (b - 1); Square s = (Utils.BSFTable[((bb & (0xffffffffffffffff - bb + 1)) * DeBruijn_64) >> 58]); #else var s = Utils.pop_lsb(ref b); #endif Debug.Assert(pos.pawn_is_passed(Us, s)); var r = ((s >> 3) ^ (Us * 7)) - RankC.RANK_2; var rr = r * (r - 1); // Base bonus based on rank var mbonus = (20 * rr); var ebonus = (10 * (rr + r + 1)); if (rr != 0) { var blockSq = s + (Us == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S); // Adjust bonus based on kings proximity ebonus += (Utils.SquareDistance[pos.pieceList[Them][PieceTypeC.KING][0]][blockSq] * 5 * rr); ebonus -= (Utils.SquareDistance[pos.pieceList[Us][PieceTypeC.KING][0]][blockSq] * 2 * rr); // If blockSq is not the queening square then consider also a second push if ((blockSq >> 3) != (Us == ColorC.WHITE ? RankC.RANK_8 : RankC.RANK_1)) { ebonus -= (Utils.SquareDistance[pos.pieceList[Us][PieceTypeC.KING][0]][ blockSq + (Us == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S)] * rr); } // If the pawn is free to advance, increase bonus if (pos.board[blockSq] == PieceC.NO_PIECE) { squaresToQueen = Utils.ForwardBB[Us][s]; defendedSquares = squaresToQueen & ei.attackedBy[Us][0]; // If there is an enemy rook or queen attacking the pawn from behind, // add all X-ray attacks by the rook or queen. Otherwise consider only // the squares in the pawn's path attacked or occupied by the enemy. if (((Utils.ForwardBB[Them][s] & ((pos.byTypeBB[PieceTypeC.ROOK] | pos.byTypeBB[PieceTypeC.QUEEN]) & pos.byColorBB[Them])) != 0) && ((Utils.ForwardBB[Them][s] & ((pos.byTypeBB[PieceTypeC.ROOK] | pos.byTypeBB[PieceTypeC.QUEEN]) & pos.byColorBB[Them]) & pos.attacks_from_ROOK(s)) != 0)) { unsafeSquares = squaresToQueen; } else { unsafeSquares = squaresToQueen & (ei.attackedBy[Them][0] | pos.byColorBB[Them]); } // If there aren't enemy attacks or pieces along the path to queen give // huge bonus. Even bigger if we protect the pawn's path. if (unsafeSquares == 0) { ebonus += (rr * (squaresToQueen == defendedSquares ? 17 : 15)); } else { // OK, there are enemy attacks or pieces (but not pawns). Are those // squares which are attacked by the enemy also attacked by us ? // If yes, big bonus (but smaller than when there are no enemy attacks), // if no, somewhat smaller bonus. ebonus += (rr * ((unsafeSquares & defendedSquares) == unsafeSquares ? 13 : 8)); } } } // rr != 0 // Increase the bonus if the passed pawn is supported by a friendly pawn // on the same rank and a bit smaller if it's on the previous rank. supportingPawns = (pos.byTypeBB[PieceTypeC.PAWN] & pos.byColorBB[Us]) & Utils.AdjacentFilesBB[s & 7]; if ((supportingPawns & Utils.RankBB[s >> 3]) != 0) { ebonus += (r * 20); } else if ((supportingPawns & Utils.RankBB[(s - (Us == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S)) >> 3]) != 0) { ebonus += (r * 12); } // Rook pawns are a special case: They are sometimes worse, and // sometimes better than other passed pawns. It is difficult to find // good rules for determining whether they are good or bad. For now, // we try the following: Increase the value for rook pawns if the // other side has no pieces apart from a knight, and decrease the // value if the other side has a rook or queen. if ((s & 7) == FileC.FILE_A || (s & 7) == FileC.FILE_H) { if ((Them == 0 ? pos.st.npMaterialWHITE : pos.st.npMaterialBLACK) <= Constants.KnightValueMidgame) { ebonus += ebonus / 4; } else if (((pos.byTypeBB[PieceTypeC.ROOK] | pos.byTypeBB[PieceTypeC.QUEEN]) & pos.byColorBB[Them]) != 0) { ebonus -= ebonus / 4; } } score += ((mbonus << 16) + ebonus); } while (b != 0); // Add the scores to the middle game and endgame eval return Utils.apply_weight(score, Weights[EvalWeightC.PassedPawns]); }