// 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; }
// refutes() tests whether a 'first' move is able to defend against a 'second' // opponent's move. In this case will not be pruned. Normally the second move // is the threat (the best move returned from a null search that fails low). private static bool refutes(Position pos, int move, int threat) { Debug.Assert(Utils.is_ok_M(move)); Debug.Assert(Utils.is_ok_M(threat)); Square mfrom = Utils.from_sq(move); Square mto = Utils.to_sq(move); Square tfrom = Utils.from_sq(threat); Square tto = Utils.to_sq(threat); // Don't prune moves of the threatened piece if (mfrom == tto) { return true; } // If the threatened piece has value less than or equal to the value of the // threat piece, don't prune moves which defend it. if (pos.is_capture(threat) && (Position.PieceValue[PhaseC.MG][pos.piece_on(tfrom)] >= Position.PieceValue[PhaseC.MG][pos.piece_on(tto)] || Utils.type_of(pos.piece_on(tfrom)) == PieceTypeC.KING)) { // Update occupancy as if the piece and the threat are moving var occ = Utils.xor_bit(Utils.xor_bit(Utils.xor_bit(pos.occupied_squares, mfrom), mto), tfrom); Piece piece = pos.piece_on(mfrom); // The piece moved in 'to' attacks the square 's' ? if (Utils.bit_is_set(Position.attacks_from(piece, mto, occ), tto) != 0) { return true; } // Scan for possible X-ray attackers behind the moved piece var xray = (Utils.rook_attacks_bb(tto, occ) & pos.pieces(PieceTypeC.ROOK, PieceTypeC.QUEEN, Utils.color_of(piece))) | (Utils.bishop_attacks_bb(tto, occ) & pos.pieces(PieceTypeC.BISHOP, PieceTypeC.QUEEN, Utils.color_of(piece))); // Verify attackers are triggered by our move and not already existing if ((xray != 0) && ((xray ^ (xray & pos.attacks_from_QUEEN(tto))) != 0)) { return true; } } // Don't prune safe moves which block the threat path if ((Utils.bit_is_set(Utils.between_bb(tfrom, tto), mto) != 0) && pos.see(move, true) >= 0) { return true; } return false; }