internal void CreateCheckInfo(Position pos) { Color them = pos.sideToMove ^ 1; ksq = pos.pieceList[them][PieceTypeC.KING][0]; pinned = pos.pinned_pieces(); dcCandidates = pos.discovered_check_candidates(); checkSq[PieceTypeC.PAWN] = Utils.StepAttacksBB[((them << 3) | PieceTypeC.PAWN)][ksq]; checkSq[PieceTypeC.KNIGHT] = Utils.StepAttacksBB_KNIGHT[ksq]; #if X64 checkSq[PieceTypeC.BISHOP] = Utils.BAttacks[ksq][(((pos.occupied_squares & Utils.BMasks[ksq]) * Utils.BMagics[ksq]) >> Utils.BShifts[ksq])]; checkSq[PieceTypeC.ROOK] = Utils.RAttacks[ksq][(((pos.occupied_squares & Utils.RMasks[ksq]) * Utils.RMagics[ksq]) >> Utils.RShifts[ksq])]; #else checkSq[PieceTypeC.BISHOP] = pos.attacks_from_BISHOP(ksq); checkSq[PieceTypeC.ROOK] = pos.attacks_from_ROOK(ksq); #endif checkSq[PieceTypeC.QUEEN] = checkSq[PieceTypeC.BISHOP] | checkSq[PieceTypeC.ROOK]; checkSq[PieceTypeC.KING] = 0; }
// 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_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]); }
internal static void generate_evasion(Position pos, MoveStack[] ms, ref int mpos) { /// 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. Debug.Assert(pos.in_check()); ulong b; int from, checksq; var checkersCnt = 0; var us = pos.sideToMove; var ksq = pos.king_square(us); ulong sliderAttacks = 0; var checkers = pos.st.checkersBB; Debug.Assert(checkers != 0); // Find squares attacked by slider checkers, we will remove them from the king // evasions so to skip known illegal moves avoiding useless legality check later. b = checkers; do { checkersCnt++; checksq = Utils.pop_lsb(ref b); Debug.Assert(Utils.color_of(pos.piece_on(checksq)) == Utils.flip_C(us)); switch (Utils.type_of(pos.piece_on(checksq))) { case PieceTypeC.BISHOP: sliderAttacks |= Utils.PseudoAttacks[PieceTypeC.BISHOP][checksq]; break; case PieceTypeC.ROOK: sliderAttacks |= Utils.PseudoAttacks[PieceTypeC.ROOK][checksq]; break; case PieceTypeC.QUEEN: // If queen and king are far or not on a diagonal line we can safely // remove all the squares attacked in the other direction becuase are // not reachable by the king anyway. if ((Utils.between_bb(ksq, checksq) != 0) || ((Utils.bit_is_set(Utils.PseudoAttacks[PieceTypeC.BISHOP][checksq], ksq)) == 0)) { sliderAttacks |= Utils.PseudoAttacks[PieceTypeC.QUEEN][checksq]; } // Otherwise we need to use real rook attacks to check if king is safe // to move in the other direction. For example: king in B2, queen in A1 // a knight in B1, and we can safely move to C1. else { sliderAttacks |= Utils.PseudoAttacks[PieceTypeC.BISHOP][checksq] | pos.attacks_from_ROOK(checksq); } break; default: break; } } while (b != 0); // Generate evasions for king, capture and non capture moves b = Position.attacks_from_KING(ksq) & ~pos.pieces_C(us) & ~sliderAttacks; from = ksq; while (b != 0) { ms[mpos++].move = Utils.make_move(from, Utils.pop_lsb(ref b)); } // Generate evasions for other pieces only if not under a double check if (checkersCnt > 1) { return; } // Blocking evasions or captures of the checking piece var target = Utils.between_bb(checksq, ksq) | checkers; generate_all(GenType.EVASIONS, pos, ms, ref mpos, us, target, null); }