/// PawnTable::pawn_info() takes a position object as input, computes /// a PawnInfo object, and returns a pointer to it. The result is also stored /// in an hash table, so we don't have to recompute everything when the same /// pawn structure occurs again. internal void probe(Position pos, out PawnEntry e) { Key key = pos.pawn_key(); e = entries[((UInt32)key) & Constants.PawnTableMask]; // If pi.key matches the position's pawn hash key, it means that we // have analysed this pawn structure before, and we can simply return // the information we found the last time instead of recomputing it. if (e.key == key) { return; } // Initialize PawnInfo entry e.key = key; e.passedPawnsWHITE = e.passedPawnsBLACK = 0; e.kingSquaresWHITE = e.kingSquaresBLACK = SquareC.SQ_NONE; e.halfOpenFilesWHITE = e.halfOpenFilesBLACK = 0xFF; // Calculate pawn attacks Bitboard wPawns = pos.pieces_PTC(PieceTypeC.PAWN, ColorC.WHITE); Bitboard bPawns = pos.pieces_PTC(PieceTypeC.PAWN, ColorC.BLACK); e.pawnAttacksWHITE = ((wPawns & ~Constants.FileHBB) << 9) | ((wPawns & ~Constants.FileABB) << 7); e.pawnAttacksBLACK = ((bPawns & ~Constants.FileHBB) >> 7) | ((bPawns & ~Constants.FileABB) >> 9); // Evaluate pawns for both colors and weight the result e.value = evaluate_pawns(ColorC.WHITE, pos, wPawns, bPawns, e) - evaluate_pawns(ColorC.BLACK, pos, bPawns, wPawns, e); e.value = Utils.apply_weight(e.value, PawnStructureWeight); return; }
internal PawnTable() { for (int i = 0; i < Constants.PawnTableSize; i++) { entries[i] = new PawnEntry(); } }
/// PawnTable::evaluate_pawns() evaluates each pawn of the given color internal static Score evaluate_pawns(Color Us, Position pos, Bitboard ourPawns, Bitboard theirPawns, PawnEntry e) { Color Them = (Us == ColorC.WHITE ? ColorC.BLACK : ColorC.WHITE); Bitboard b; Square s; File f; Rank r; bool passed, isolated, doubled, opposed, chain, backward, candidate; Score value = ScoreC.SCORE_ZERO; Square[] pl = pos.pieceList[Us][PieceTypeC.PAWN]; int plPos = 0; // Loop through all pawns of the current color and score each pawn while ((s = pl[plPos++]) != SquareC.SQ_NONE) { Debug.Assert(pos.piece_on(s) == Utils.make_piece(Us, PieceTypeC.PAWN)); f = (s & 7); r = (s >> 3); // This file cannot be half open if (Us == ColorC.WHITE) { e.halfOpenFilesWHITE &= ~(1 << f); } else { e.halfOpenFilesBLACK &= ~(1 << f); } // Our rank plus previous one. Used for chain detection b = Utils.RankBB[r] | Utils.RankBB[Us == ColorC.WHITE ? r - 1 : r + 1]; // Flag the pawn as passed, isolated, doubled or member of a pawn // chain (but not the backward one). chain = (ourPawns & Utils.AdjacentFilesBB[f] & b) != 0; isolated = (ourPawns & Utils.AdjacentFilesBB[f]) == 0; doubled = (ourPawns & Utils.ForwardBB[Us][s]) != 0; opposed = (theirPawns & Utils.ForwardBB[Us][s]) != 0; passed = (theirPawns & Utils.PassedPawnMask[Us][s]) == 0; // Test for backward pawn backward = false; // If the pawn is passed, isolated, or member of a pawn chain it cannot // be backward. If there are friendly pawns behind on adjacent files // or if can capture an enemy pawn it cannot be backward either. if (!(passed | isolated | chain) && (((ourPawns & Utils.AttackSpanMask[Them][s])) == 0) && ((((Utils.StepAttacksBB[((Us << 3) | PieceTypeC.PAWN)][s]) & theirPawns)) == 0)) { // We now know that there are no friendly pawns beside or behind this // pawn on adjacent files. We now check whether the pawn is // backward by looking in the forward direction on the adjacent // files, and seeing whether we meet a friendly or an enemy pawn first. b = Utils.StepAttacksBB[((Us << 3) | PieceTypeC.PAWN)][s]; // Note that we are sure to find something because pawn is not passed // nor isolated, so loop is potentially infinite, but it isn't. while ((b & (ourPawns | theirPawns)) == 0) { if (Us == ColorC.WHITE) { b <<= 8; } else { b >>= 8; } } // The friendly pawn needs to be at least two ranks closer than the // enemy pawn in order to help the potentially backward pawn advance. backward = (((b | (Us == ColorC.WHITE ? b << 8 : b >> 8)) & theirPawns) != 0); } Debug.Assert(opposed | passed | (((Utils.AttackSpanMask[Us][s] & theirPawns)) != 0)); // A not passed pawn is a candidate to become passed if it is free to // advance and if the number of friendly pawns beside or behind this // pawn on adjacent files is higher or equal than the number of // enemy pawns in the forward direction on the adjacent files. candidate = !(opposed | passed | backward | isolated) && (b = Utils.AttackSpanMask[Them][s + (Us == ColorC.WHITE ? SquareC.DELTA_N : SquareC.DELTA_S)] & ourPawns) != 0 && Bitcount.popcount_1s_Max15(b) >= Bitcount.popcount_1s_Max15(Utils.AttackSpanMask[Us][s] & theirPawns); // Passed pawns will be properly scored in evaluation because we need // full attack info to evaluate passed pawns. Only the frontmost passed // pawn on each file is considered a true passed pawn. if (passed && !doubled) { if (Us == ColorC.WHITE) { e.passedPawnsWHITE |= Utils.SquareBB[s]; } else { e.passedPawnsBLACK |= Utils.SquareBB[s]; } } // Score this pawn if (isolated) { value -= IsolatedPawnPenalty[opposed ? 1 : 0][f]; } if (doubled) { value -= DoubledPawnPenalty[opposed ? 1 : 0][f]; } if (backward) { value -= BackwardPawnPenalty[opposed ? 1 : 0][f]; } if (chain) { value += ChainBonus[f]; } if (candidate) { value += CandidateBonus[((s >> 3) ^ (Us * 7))]; } } return(value); }