/// Entry::do_king_safety() calculates a bonus for king safety. It is called only /// when king square changes, which is about 20% of total king_safety() calls. public Score do_king_safety(Position pos, Square ksq, Color Us) { kingSquares[Us] = ksq; castlingRights[Us] = pos.can_castle_color(Us); minKPdistance[Us] = 0; Bitboard pawns = pos.pieces_color_piecetype(Us, PieceTypeS.PAWN); if (pawns != 0) { while (0 == (BitBoard.DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) { } } if (Types.relative_rank_square(Us, ksq) > RankS.RANK_4) { return(Types.make_score(0, -16 * minKPdistance[Us])); } Value bonus = shelter_storm(pos, ksq, Us); // If we can castle use the bonus after the castle if is bigger if (pos.can_castle_castleright((new MakeCastlingS(Us, CastlingSideS.KING_SIDE)).right) != 0) { bonus = Math.Max(bonus, shelter_storm(pos, Types.relative_square(Us, SquareS.SQ_G1), Us)); } if (pos.can_castle_castleright((new MakeCastlingS(Us, CastlingSideS.QUEEN_SIDE)).right) != 0) { bonus = Math.Max(bonus, shelter_storm(pos, Types.relative_square(Us, SquareS.SQ_C1), Us)); } return(Types.make_score(bonus, -16 * minKPdistance[Us])); }
// evaluate_outposts() evaluates bishop and knight outpost squares public static Score evaluate_outposts(Position pos, EvalInfo ei, Square s, PieceType Pt, Color Us) { Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); Debug.Assert(Pt == PieceTypeS.BISHOP || Pt == PieceTypeS.KNIGHT); // Initial bonus based on square Value bonus = Outpost[Pt == PieceTypeS.BISHOP ? 1 : 0][Types.relative_square(Us, s)]; // Increase bonus if supported by pawn, especially if the opponent has // no minor piece which can trade with the outpost piece. if (bonus != 0 && (ei.attackedBy[Us][PieceTypeS.PAWN] & BitBoard.SquareBB[s]) != 0) { if (0 == pos.pieces_color_piecetype(Them, PieceTypeS.KNIGHT) && 0 == (BitBoard.squares_of_color(s) & pos.pieces_color_piecetype(Them, PieceTypeS.BISHOP))) { bonus += bonus + bonus / 2; } else { bonus += bonus / 2; } } return(Types.make_score(bonus, bonus)); }
public static void init() { int bonus; //Score[/*FILE_NB*/][/*RANK_NB*/] Connected Connected = new Score[FileS.FILE_NB][]; for (File f = FileS.FILE_A; f <= FileS.FILE_H; ++f) { Connected[f] = new Score[RankS.RANK_NB]; } for (Rank r = RankS.RANK_1; r < RankS.RANK_8; ++r) { for (File f = FileS.FILE_A; f <= FileS.FILE_H; ++f) { bonus = r * (r - 1) * (r - 2) + bonusesByFile[f] * (r / 2 + 1); Connected[f][r] = Types.make_score(bonus, bonus); } } }
/// init() computes evaluation weights from the corresponding UCI parameters /// and setup king tables. public static void init() { Weights[EvalWeightS.Mobility] = weight_option("Mobility (Midgame)", "Mobility (Endgame)", WeightsInternal[EvalWeightS.Mobility]); Weights[EvalWeightS.PawnStructure] = weight_option("Pawn Structure (Midgame)", "Pawn Structure (Endgame)", WeightsInternal[EvalWeightS.PawnStructure]); Weights[EvalWeightS.PassedPawns] = weight_option("Passed Pawns (Midgame)", "Passed Pawns (Endgame)", WeightsInternal[EvalWeightS.PassedPawns]); Weights[EvalWeightS.Space] = weight_option("Space", "Space", WeightsInternal[EvalWeightS.Space]); Weights[EvalWeightS.KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[EvalWeightS.KingDangerUs]); Weights[EvalWeightS.KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[EvalWeightS.KingDangerThem]); const int MaxSlope = 30; const int Peak = 1280; for (int t = 0, i = 1; i < 100; ++i) { t = Math.Min(Peak, Math.Min((int)(0.4 * i * i), t + MaxSlope)); KingDanger[1][i] = Eval.apply_weight(Types.make_score(t, 0), Weights[EvalWeightS.KingDangerUs]); KingDanger[0][i] = Eval.apply_weight(Types.make_score(t, 0), Weights[EvalWeightS.KingDangerThem]); } }
public static Score S(int mg, int eg) { return(Types.make_score(mg, eg)); }
// evaluate_passed_pawns() evaluates the passed pawns of the given color public static Score evaluate_passed_pawns(Position pos, EvalInfo ei, Color Us, bool Trace) { Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE); Bitboard b, squaresToQueen, defendedSquares, unsafeSquares; Score score = ScoreS.SCORE_ZERO; b = ei.pi.passed_pawns(Us); while (b != 0) { Square s = BitBoard.pop_lsb(ref b); Debug.Assert(pos.pawn_passed(Us, s)); int r = (int)(Types.relative_rank_square(Us, s) - RankS.RANK_2); int rr = r * (r - 1); // Base bonus based on rank Value mbonus = (17 * rr), ebonus = (7 * (rr + r + 1)); if (rr != 0) { Square blockSq = s + Types.pawn_push(Us); // Adjust bonus based on kings proximity ebonus += (BitBoard.square_distance(pos.king_square(Them), blockSq) * 5 * rr) - (BitBoard.square_distance(pos.king_square(Us), blockSq) * 2 * rr); // If blockSq is not the queening square then consider also a second push if (Types.relative_rank_square(Us, blockSq) != RankS.RANK_8) { ebonus -= (BitBoard.square_distance(pos.king_square(Us), blockSq + Types.pawn_push(Us)) * rr); } // If the pawn is free to advance, increase bonus if (pos.empty(blockSq)) { squaresToQueen = BitBoard.forward_bb(Us, s); // 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 ((BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Them, PieceTypeS.ROOK, PieceTypeS.QUEEN)) != 0 && (BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Them, PieceTypeS.ROOK, PieceTypeS.QUEEN) & pos.attacks_from_square_piecetype(s, PieceTypeS.ROOK)) != 0) { unsafeSquares = squaresToQueen; } else { unsafeSquares = squaresToQueen & (ei.attackedBy[Them][PieceTypeS.ALL_PIECES] | pos.pieces_color(Them)); } if ((BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Us, PieceTypeS.ROOK, PieceTypeS.QUEEN)) != 0 && (BitBoard.forward_bb(Them, s) & pos.pieces_color_piecetype(Us, PieceTypeS.ROOK, PieceTypeS.QUEEN) & pos.attacks_from_square_piecetype(s, PieceTypeS.ROOK)) != 0) { defendedSquares = squaresToQueen; } else { defendedSquares = squaresToQueen & ei.attackedBy[Us][PieceTypeS.ALL_PIECES]; } // If there aren't any enemy attacks, assign a big bonus. Otherwise // assign a smaller bonus if the block square isn't attacked. int k = 0 == unsafeSquares? 15 : 0 == (unsafeSquares & BitBoard.SquareBB[blockSq]) ? 9 : 0; // If the path to queen is fully defended, assign a big bonus. // Otherwise assign a smaller bonus if the block square is defended. if (defendedSquares == squaresToQueen) { k += 6; } else if ((defendedSquares & BitBoard.SquareBB[blockSq]) != 0) { k += 4; } mbonus += (k * rr); ebonus += (k * rr); } } // rr != 0 if (pos.count(Us, PieceTypeS.PAWN) < pos.count(Them, PieceTypeS.PAWN)) { ebonus += ebonus / 4; } score += Types.make_score(mbonus, ebonus); } if (Trace) { Tracing.terms[Us][TermsS.PASSED] = apply_weight(score, Weights[EvalWeightS.PassedPawns]); } // Add the scores to the middle game and endgame eval return(Eval.apply_weight(score, Weights[EvalWeightS.PassedPawns])); }
// 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)); }
public static Score[][] KingDanger = new Score[ColorS.COLOR_NB][] { new Score[128], new Score[128] }; // 2, 128 // apply_weight() weighs score 'v' by weight 'w' trying to prevent overflow public static Score apply_weight(Score v, WeightS w) { return(Types.make_score(Types.mg_value(v) * w.mg / 256, Types.eg_value(v) * w.eg / 256)); }
public Score material_value() { return(Types.make_score(value, value)); }
/// Material::probe() takes a position object as input, looks up a MaterialEntry /// object, and returns a pointer to it. If the material configuration is not /// already present in the table, it is computed and stored there, so we don't /// have to recompute everything when the same material configuration occurs again. public static Material.Entry probe(Position pos, Material.Table entries, Endgames endgames) { Key key = pos.material_key(); Material.Entry e = entries[key]; // If e.key matches the position's material hash key, it means that we // have analysed this material configuration before, and we can simply // return the information we found the last time instead of recomputing it. if (e.key == key) { return(e); } e.value = 0; e.evaluationFunction = null; e.scalingFunction[0] = e.scalingFunction[1] = null; e.spaceWeight = 0; e.key = key; e.factor[ColorS.WHITE] = e.factor[ColorS.BLACK] = (byte)ScaleFactorS.SCALE_FACTOR_NORMAL; e.gamePhase = game_phase(pos); // Let's look if we have a specialized evaluation function for this particular // material configuration. Firstly we look for a fixed configuration one, then // for a generic one if the previous search failed. if (endgames.probeValueFunction(key, out e.evaluationFunction) != null) { return(e); } if (is_KXK(pos, ColorS.WHITE)) { e.evaluationFunction = EvaluateKXK[ColorS.WHITE]; return(e); } if (is_KXK(pos, ColorS.BLACK)) { e.evaluationFunction = EvaluateKXK[ColorS.BLACK]; return(e); } // OK, we didn't find any special evaluation function for the current // material configuration. Is there a suitable scaling function? // // We face problems when there are several conflicting applicable // scaling functions and we need to decide which one to use. EndgameBase sf; if (endgames.probeScaleFunction(key, out sf) != null) { e.scalingFunction[sf.color()] = sf; return(e); } // Generic scaling functions that refer to more than one material // distribution. They should be probed after the specialized ones. // Note that these ones don't return after setting the function. if (is_KBPsKs(pos, ColorS.WHITE)) { e.scalingFunction[ColorS.WHITE] = ScaleKBPsK[ColorS.WHITE]; } if (is_KBPsKs(pos, ColorS.BLACK)) { e.scalingFunction[ColorS.BLACK] = ScaleKBPsK[ColorS.BLACK]; } if (is_KQKRPs(pos, ColorS.WHITE)) { e.scalingFunction[ColorS.WHITE] = ScaleKQKRPs[ColorS.WHITE]; } else if (is_KQKRPs(pos, ColorS.BLACK)) { e.scalingFunction[ColorS.BLACK] = ScaleKQKRPs[ColorS.BLACK]; } Value npm_w = pos.non_pawn_material(ColorS.WHITE); Value npm_b = pos.non_pawn_material(ColorS.BLACK); if (npm_w + npm_b == ValueS.VALUE_ZERO && pos.pieces_piecetype(PieceTypeS.PAWN) != 0) { if (0 == pos.count(ColorS.BLACK, PieceTypeS.PAWN)) { Debug.Assert(pos.count(ColorS.WHITE, PieceTypeS.PAWN) >= 2); e.scalingFunction[ColorS.WHITE] = ScaleKPsK[ColorS.WHITE]; } else if (0 == pos.count(ColorS.WHITE, PieceTypeS.PAWN)) { Debug.Assert(pos.count(ColorS.BLACK, PieceTypeS.PAWN) >= 2); e.scalingFunction[ColorS.BLACK] = ScaleKPsK[ColorS.BLACK]; } else if (pos.count(ColorS.WHITE, PieceTypeS.PAWN) == 1 && pos.count(ColorS.BLACK, PieceTypeS.PAWN) == 1) { // This is a special case because we set scaling functions // for both colors instead of only one. e.scalingFunction[ColorS.WHITE] = ScaleKPKP[ColorS.WHITE]; e.scalingFunction[ColorS.BLACK] = ScaleKPKP[ColorS.BLACK]; } } // No pawns makes it difficult to win, even with a material advantage. This // catches some trivial draws like KK, KBK and KNK and gives a very drawish // scale factor for cases such as KRKBP and KmmKm (except for KBBKN). if (0 == pos.count(ColorS.WHITE, PieceTypeS.PAWN) && npm_w - npm_b <= ValueS.BishopValueMg) { e.factor[ColorS.WHITE] = (byte)(npm_w < ValueS.RookValueMg ? ScaleFactorS.SCALE_FACTOR_DRAW : npm_b <= ValueS.BishopValueMg ? 4 : 12); } if (0 == pos.count(ColorS.BLACK, PieceTypeS.PAWN) && npm_b - npm_w <= ValueS.BishopValueMg) { e.factor[ColorS.BLACK] = (byte)(npm_b < ValueS.RookValueMg ? ScaleFactorS.SCALE_FACTOR_DRAW : npm_w <= ValueS.BishopValueMg ? 4 : 12); } if (pos.count(ColorS.WHITE, PieceTypeS.PAWN) == 1 && npm_w - npm_b <= ValueS.BishopValueMg) { e.factor[ColorS.WHITE] = (byte)ScaleFactorS.SCALE_FACTOR_ONEPAWN; } if (pos.count(ColorS.BLACK, PieceTypeS.PAWN) == 1 && npm_b - npm_w <= ValueS.BishopValueMg) { e.factor[ColorS.BLACK] = (byte)ScaleFactorS.SCALE_FACTOR_ONEPAWN; } // Compute the space weight if (npm_w + npm_b >= 2 * ValueS.QueenValueMg + 4 * ValueS.RookValueMg + 2 * ValueS.KnightValueMg) { int minorPieceCount = pos.count(ColorS.WHITE, PieceTypeS.KNIGHT) + pos.count(ColorS.WHITE, PieceTypeS.BISHOP) + pos.count(ColorS.BLACK, PieceTypeS.KNIGHT) + pos.count(ColorS.BLACK, PieceTypeS.BISHOP); e.spaceWeight = Types.make_score(minorPieceCount * minorPieceCount, 0); } // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // for the bishop pair "extended piece", this allow us to be more flexible // in defining bishop pair bonuses. int[][] pieceCount = new int[ColorS.COLOR_NB][] { new int[] { pos.count(ColorS.WHITE, PieceTypeS.BISHOP) > 1 ? 1 : 0, pos.count(ColorS.WHITE, PieceTypeS.PAWN), pos.count(ColorS.WHITE, PieceTypeS.KNIGHT), pos.count(ColorS.WHITE, PieceTypeS.BISHOP), pos.count(ColorS.WHITE, PieceTypeS.ROOK), pos.count(ColorS.WHITE, PieceTypeS.QUEEN) }, new int[] { pos.count(ColorS.BLACK, PieceTypeS.BISHOP) > 1 ? 1 : 0, pos.count(ColorS.BLACK, PieceTypeS.PAWN), pos.count(ColorS.BLACK, PieceTypeS.KNIGHT), pos.count(ColorS.BLACK, PieceTypeS.BISHOP), pos.count(ColorS.BLACK, PieceTypeS.ROOK), pos.count(ColorS.BLACK, PieceTypeS.QUEEN) } }; e.value = (Int16)((imbalance(pieceCount, ColorS.WHITE) - imbalance(pieceCount, ColorS.BLACK)) / 16); return(e); }