/// evaluate() is the main evaluation function. It always computes two /// values, an endgame score and a middle game score, and interpolates /// between them based on the remaining material. internal static int do_evaluate(bool Trace, Position pos, ref int margin) { Debug.Assert(!pos.in_check()); var ei = EvalInfoBroker.GetObject(); Value marginsWHITE, marginsBLACK; int score = 0, mobilityWhite = 0, mobilityBlack = 0; // margins[] store the uncertainty estimation of position's evaluation // that typically is used by the search for pruning decisions. marginsWHITE = marginsBLACK = ValueC.VALUE_ZERO; // Initialize score by reading the incrementally updated scores included // in the position object (material + piece square tables) and adding // Tempo bonus. Score is computed from the point of view of white. score = pos.st.psqScore + (pos.sideToMove == ColorC.WHITE ? Tempo : -Tempo); // Probe the material hash table pos.this_thread().materialTable.probe(pos, out ei.mi); score += ((ei.mi.value << 16) + ei.mi.value); // If we have a specialized evaluation function for the current material // configuration, call it and return. if (ei.mi.evaluationFunction != null) { margin = ValueC.VALUE_ZERO; var retval = ei.mi.evaluationFunction(ei.mi.evaluationFunctionColor, pos); ei.pi = null; ei.mi = null; EvalInfoBroker.Free(); return retval; } // Probe the pawn hash table pos.this_thread().pawnTable.probe(pos, out ei.pi); score += ei.pi.value; // Initialize attack and king safety bitboards init_eval_info(ColorC.WHITE, pos, ei); init_eval_info(ColorC.BLACK, pos, ei); // Evaluate pieces and mobility score += evaluate_pieces_of_color(ColorC.WHITE, Trace, pos, ei, ref mobilityWhite) - evaluate_pieces_of_color(ColorC.BLACK, Trace, pos, ei, ref mobilityBlack); score += (((((((mobilityWhite - mobilityBlack) + 32768) & ~0xffff) / 0x10000 * (((Weights[EvalWeightC.Mobility] + 32768) & ~0xffff) / 0x10000)) / 0x100) << 16) + (((short)((mobilityWhite - mobilityBlack) & 0xffff) * ((short)(Weights[EvalWeightC.Mobility] & 0xffff))) / 0x100)); // Evaluate kings after all other pieces because we need complete attack // information when computing the king safety evaluation. score += evaluate_king(ColorC.WHITE, Trace, pos, ei, ref marginsWHITE, ref marginsBLACK) - evaluate_king(ColorC.BLACK, Trace, pos, ei, ref marginsWHITE, ref marginsBLACK); // Evaluate tactical threats, we need full attack information including king score += evaluate_threats(ColorC.WHITE, pos, ei) - evaluate_threats(ColorC.BLACK, pos, ei); // Evaluate passed pawns, we need full attack information including king score += evaluate_passed_pawns(ColorC.WHITE, pos, ei) - evaluate_passed_pawns(ColorC.BLACK, pos, ei); // If one side has only a king, check whether exists any unstoppable passed pawn if ((pos.st.npMaterialWHITE == 0) || (pos.st.npMaterialBLACK == 0)) { score += evaluate_unstoppable_pawns(pos, ei); } // Evaluate space for both sides, only in middle-game. if (ei.mi.spaceWeight != 0) { var s = evaluate_space(ColorC.WHITE, pos, ei) - evaluate_space(ColorC.BLACK, pos, ei); score += ((((((((s * ei.mi.spaceWeight) << 16) + 32768) & ~0xffff) / 0x10000 * (((Weights[EvalWeightC.Space] + 32768) & ~0xffff) / 0x10000)) / 0x100) << 16) + (((short)(((s * ei.mi.spaceWeight) << 16) & 0xffff) * ((short)(Weights[EvalWeightC.Space] & 0xffff))) / 0x100)); } // Scale winning side if position is more drawish that what it appears var sf = ((short)(score & 0xffff)) > ValueC.VALUE_DRAW ? ei.mi.scale_factor_WHITE(pos) : ei.mi.scale_factor_BLACK(pos); // 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.gamePhase < PhaseC.PHASE_MIDGAME && (pos.pieceCount[ColorC.WHITE][PieceTypeC.BISHOP] == 1 && pos.pieceCount[ColorC.BLACK][PieceTypeC.BISHOP] == 1 && (((((pos.pieceList[ColorC.WHITE][PieceTypeC.BISHOP][0] ^ pos.pieceList[ColorC.BLACK][PieceTypeC.BISHOP][0]) >> 3) ^ (pos.pieceList[ColorC.WHITE][PieceTypeC.BISHOP][0] ^ pos.pieceList[ColorC.BLACK][PieceTypeC.BISHOP][0])) & 1) != 0)) && sf == ScaleFactorC.SCALE_FACTOR_NORMAL) { // Only the two bishops ? if (pos.st.npMaterialWHITE == Constants.BishopValueMidgame && pos.st.npMaterialBLACK == Constants.BishopValueMidgame) { // Check for KBP vs KB with only a single pawn that is almost // certainly a draw or at least two pawns. sf = (pos.pieceCount[ColorC.WHITE][PieceTypeC.PAWN] + pos.pieceCount[ColorC.BLACK][PieceTypeC.PAWN] == 1) ? (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); } } // Interpolate between the middle game and the endgame score margin = pos.sideToMove == ColorC.WHITE ? marginsWHITE : marginsBLACK; // interpolate //Value v = interpolate(score, ei.mi.game_phase(), sf); var ev = (((short)(score & 0xffff)) * sf) / ScaleFactorC.SCALE_FACTOR_NORMAL; var result = ((((score + 32768) & ~0xffff) / 0x10000) * ei.mi.gamePhase + ev * (128 - ei.mi.gamePhase)) / 128; var v = ((result + GrainSize / 2) & ~(GrainSize - 1)); // In case of tracing add all single evaluation contributions for both white and black if (Trace) { trace_add(TracedTypeC.PST, pos.psq_score()); trace_add(TracedTypeC.IMBALANCE, ei.mi.material_value()); trace_add(PieceTypeC.PAWN, ei.pi.pawns_value()); trace_add( TracedTypeC.MOBILITY, Utils.apply_weight(mobilityWhite, Weights[EvalWeightC.Mobility]), Utils.apply_weight(mobilityBlack, Weights[EvalWeightC.Mobility])); trace_add( TracedTypeC.THREAT, evaluate_threats(ColorC.WHITE, pos, ei), evaluate_threats(ColorC.BLACK, pos, ei)); trace_add( TracedTypeC.PASSED, evaluate_passed_pawns(ColorC.WHITE, pos, ei), evaluate_passed_pawns(ColorC.BLACK, pos, ei)); trace_add(TracedTypeC.UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei)); var w = Utils.make_score(ei.mi.space_weight() * evaluate_space(ColorC.WHITE, pos, ei), 0); var b = Utils.make_score(ei.mi.space_weight() * evaluate_space(ColorC.BLACK, pos, ei), 0); trace_add( TracedTypeC.SPACE, Utils.apply_weight(w, Weights[EvalWeightC.Space]), Utils.apply_weight(b, Weights[EvalWeightC.Space])); trace_add(TracedTypeC.TOTAL, score); TraceStream.Append("\nUncertainty margin: White: "); TraceStream.Append(FormatDouble(to_cp(marginsWHITE), null, true)); TraceStream.Append(", Black: "); TraceStream.Append(FormatDouble(to_cp(marginsBLACK), null, true)); TraceStream.Append("\nScaling: "); TraceStream.Append(FormatDouble((100.0 * ei.mi.game_phase() / 128.0), 6, false)); TraceStream.Append("% MG, "); TraceStream.Append(FormatDouble((100.0 * (1.0 - ei.mi.game_phase() / 128.0)), 6, false)); TraceStream.Append("% * "); TraceStream.Append(FormatDouble(((100.0 * sf) / ScaleFactorC.SCALE_FACTOR_NORMAL), 6, false)); TraceStream.Append("% EG.\n"); TraceStream.Append("Total evaluation: "); TraceStream.Append(FormatDouble(to_cp(v), null, false)); } ei.pi = null; ei.mi = null; EvalInfoBroker.Free(); return pos.sideToMove == ColorC.WHITE ? v : -v; }