public Engine(string[] args) { inOut.WriteLine(Misc.engine_info(), MutexAction.ATOMIC); Uci.init(Options); BitBoard.init(); Position.init(); Bitbases.init_kpk(); Search.init(); Pawns.init(); Eval.init(); Threads.init(); TT.resize((ulong)Options["Hash"].getInt()); Uci.loop(args); Threads.exit(); }
/// 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]); } }
// do_evaluate() is the evaluation entry point, called directly from evaluate() public static Value do_evaluate(Position pos, bool Trace) { Debug.Assert(0 == pos.checkers()); EvalInfo ei = new EvalInfo(); Score score; Score[] mobility = new Score[] { ScoreS.SCORE_ZERO, ScoreS.SCORE_ZERO }; Thread thisThread = pos.this_thread(); // Initialize score by reading the incrementally updated scores included // in the position object (material + piece square tables) and adding a // Tempo bonus. Score is computed from the point of view of white. score = pos.psq_score() + (pos.side_to_move() == ColorS.WHITE ? Tempo : -Tempo); // Probe the material hash table ei.mi = Material.probe(pos, thisThread.materialTable, thisThread.endgames); score += ei.mi.material_value(); // If we have a specialized evaluation function for the current material // configuration, call it and return. if (ei.mi.specialized_eval_exists()) { return(ei.mi.evaluate(pos)); } // Probe the pawn hash table ei.pi = Pawns.probe(pos, thisThread.pawnsTable); score += apply_weight(ei.pi.pawns_value(), Weights[EvalWeightS.PawnStructure]); // Initialize attack and king safety bitboards init_eval_info(pos, ei, ColorS.WHITE); init_eval_info(pos, ei, ColorS.BLACK); ei.attackedBy[ColorS.WHITE][PieceTypeS.ALL_PIECES] |= ei.attackedBy[ColorS.WHITE][PieceTypeS.KING]; ei.attackedBy[ColorS.BLACK][PieceTypeS.ALL_PIECES] |= ei.attackedBy[ColorS.BLACK][PieceTypeS.KING]; // Do not include in mobility squares protected by enemy pawns or occupied by our pawns or king Bitboard[] mobilityArea = new Bitboard[] { ~(ei.attackedBy[ColorS.BLACK][PieceTypeS.PAWN] | pos.pieces_color_piecetype(ColorS.WHITE, PieceTypeS.PAWN, PieceTypeS.KING)), ~(ei.attackedBy[ColorS.WHITE][PieceTypeS.PAWN] | pos.pieces_color_piecetype(ColorS.BLACK, PieceTypeS.PAWN, PieceTypeS.KING)) }; // Evaluate pieces and mobility score += evaluate_pieces(pos, ei, mobility, mobilityArea, PieceTypeS.KNIGHT, ColorS.WHITE, Trace); score += Eval.apply_weight(mobility[ColorS.WHITE] - mobility[ColorS.BLACK], Weights[EvalWeightS.Mobility]); // Evaluate kings after all other pieces because we need complete attack // information when computing the king safety evaluation. score += evaluate_king(pos, ei, ColorS.WHITE, Trace) - evaluate_king(pos, ei, ColorS.BLACK, Trace); // Evaluate tactical threats, we need full attack information including king score += evaluate_threats(pos, ei, ColorS.WHITE, Trace) - evaluate_threats(pos, ei, ColorS.BLACK, Trace); // Evaluate passed pawns, we need full attack information including king score += evaluate_passed_pawns(pos, ei, ColorS.WHITE, Trace) - evaluate_passed_pawns(pos, ei, ColorS.BLACK, Trace); // If one side has only a king, check whether exists any unstoppable passed pawn if (0 == pos.non_pawn_material(ColorS.WHITE) || 0 == pos.non_pawn_material(ColorS.BLACK)) { score += evaluate_unstoppable_pawns(pos, ColorS.WHITE, ei) - evaluate_unstoppable_pawns(pos, ColorS.BLACK, ei); } // Evaluate space for both sides, only in middle-game. if (ei.mi.space_weight() != 0) { int s = evaluate_space(pos, ei, ColorS.WHITE) - evaluate_space(pos, ei, ColorS.BLACK); score += Eval.apply_weight(s * ei.mi.space_weight(), Weights[EvalWeightS.Space]); } // Scale winning side if position is more drawish that what it appears ScaleFactor sf = Types.eg_value(score) > ValueS.VALUE_DRAW ? ei.mi.scale_factor(pos, ColorS.WHITE) : ei.mi.scale_factor(pos, ColorS.BLACK); // If we don't already have an unusual scale factor, check for opposite // colored bishop endgames, and use a lower scale for those. if (ei.mi.game_phase() < PhaseS.PHASE_MIDGAME && pos.opposite_bishops() && (sf == ScaleFactorS.SCALE_FACTOR_NORMAL || sf == ScaleFactorS.SCALE_FACTOR_ONEPAWN)) { // Ignoring any pawns, do both sides only have a single bishop and no // other pieces? if (pos.non_pawn_material(ColorS.WHITE) == ValueS.BishopValueMg && pos.non_pawn_material(ColorS.BLACK) == ValueS.BishopValueMg) { // Check for KBP vs KB with only a single pawn that is almost // certainly a draw or at least two pawns. bool one_pawn = (pos.count(ColorS.WHITE, PieceTypeS.PAWN) + pos.count(ColorS.BLACK, PieceTypeS.PAWN) == 1); sf = one_pawn ? (8) : (32); } else { // Endgame with opposite-colored bishops, but also other pieces. Still // a bit drawish, but not as drawish as with only the two bishops. sf = (50 * sf / ScaleFactorS.SCALE_FACTOR_NORMAL); } } // Interpolate between a middlegame and a (scaled by 'sf') endgame score Value v = Types.mg_value(score) * (ei.mi.game_phase()) + Types.eg_value(score) * (PhaseS.PHASE_MIDGAME - ei.mi.game_phase()) * sf / ScaleFactorS.SCALE_FACTOR_NORMAL; v /= (PhaseS.PHASE_MIDGAME); // In case of tracing add all single evaluation contributions for both white and black if (Trace) { //Tracing.add_term(Tracing.PST, pos.psq_score()); //Tracing.add_term(Tracing.IMBALANCE, ei.mi.material_value()); //Tracing.add_term(PAWN, ei.pi.pawns_value()); //Tracing.add_term(Tracing.MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility]) // , apply_weight(mobility[BLACK], Weights[Mobility])); //Score w = ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei); //Score b = ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei); //Tracing.add_term(Tracing.SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space])); //Tracing.add_term(Tracing.TOTAL, score); //Tracing.ei = ei; //Tracing.sf = sf; } return(pos.side_to_move() == ColorS.WHITE ? v : -v); }
// 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])); }