Line data Source code
1 : #include "attack.hpp"
2 : #include "bitboardTools.hpp"
3 : #include "dynamicConfig.hpp"
4 : #include "evalConfig.hpp"
5 : #include "evalTools.hpp"
6 : #include "hash.hpp"
7 : #include "moveApply.hpp"
8 : #include "positionTools.hpp"
9 : #include "score.hpp"
10 : #include "searcher.hpp"
11 : #include "timers.hpp"
12 :
13 : using namespace BB; // to much of it ...
14 :
15 : #pragma GCC diagnostic push
16 : #pragma GCC diagnostic ignored "-Wconversion"
17 :
18 : template<Color C>
19 : FORCE_FINLINE void evalPawnPasser(const Position &p, BitBoard pieceBBiterator, EvalScore &score) {
20 662166 : applyOn(pieceBBiterator, [&](const Square & k){
21 182329 : const EvalScore kingNearBonus = (isValidSquare(p.king[C]) ? EvalConfig::kingNearPassedPawnSupport[chebyshevDistance(p.king[C], k)][ColorRank<C>(k)] : 0) +
22 182329 : (isValidSquare(p.king[~C]) ? EvalConfig::kingNearPassedPawnDefend[chebyshevDistance(p.king[~C], k)][ColorRank<~C>(k)] : 0);
23 190768 : const bool unstoppable = (p.mat[~C][M_t] == 0) && ((chebyshevDistance(p.king[~C], PromotionSquare<C>(k)) - static_cast<int>(p.c != C)) >
24 10106 : std::min(static_cast<Square>(5), chebyshevDistance(PromotionSquare<C>(k), k)));
25 : if (unstoppable)
26 1667 : score += ColorSignHelper<C>() * (value(P_wr) - value(P_wp)); // yes rook not queen to force promotion asap
27 : else
28 361324 : score += (EvalConfig::passerBonus[ColorRank<C>(k)] + kingNearBonus) * ColorSignHelper<C>();
29 : });
30 : }
31 :
32 : template<Color C>
33 : FORCE_FINLINE void evalPawn(BitBoard pieceBBiterator, EvalScore &score) {
34 1722984 : applyOn(pieceBBiterator, [&](const Square & k){
35 787115 : const Square kk = ColorSquarePstHelper<C>(k);
36 2423047 : score += EvalConfig::PST[0][kk] * ColorSignHelper<C>();
37 : });
38 : }
39 :
40 : template<Color C>
41 : FORCE_FINLINE void evalPawnFreePasser(const Position &p, BitBoard pieceBBiterator, EvalScore &score) {
42 3032982 : applyOn(pieceBBiterator, [&](const Square & k){
43 1439046 : score += EvalConfig::freePasserBonus[ColorRank<C>(k)] * ScoreType((BBTools::frontSpan<C>(k) & p.allPieces[~C]) == emptyBitBoard) * ColorSignHelper<C>();
44 : });
45 : }
46 :
47 : template<Color C>
48 : FORCE_FINLINE void evalPawnProtected(BitBoard pieceBBiterator, EvalScore &score) {
49 160420 : applyOn(pieceBBiterator, [&](const Square & k){
50 30260 : score += EvalConfig::protectedPasserBonus[ColorRank<C>(k)] * ColorSignHelper<C>();
51 : });
52 : }
53 :
54 : template<Color C>
55 : FORCE_FINLINE void evalPawnCandidate(BitBoard pieceBBiterator, EvalScore &score) {
56 148754 : applyOn(pieceBBiterator, [&](const Square & k){
57 57773 : score += EvalConfig::candidate[ColorRank<C>(k)] * ColorSignHelper<C>();
58 : });
59 : }
60 :
61 : template<Color C>
62 3187872 : BitBoard getPinned(const Position &p, const Square s) {
63 3187872 : BitBoard pinned = emptyBitBoard;
64 3187872 : if (!isValidSquare(s)) return pinned; // this allows for pinned to queen detection more easily
65 4638854 : const BitBoard pinner = BBTools::attack<P_wb>(s, p.pieces_const<P_wb>(~C) | p.pieces_const<P_wq>(~C), p.allPieces[~C]) |
66 2319427 : BBTools::attack<P_wr>(s, p.pieces_const<P_wr>(~C) | p.pieces_const<P_wq>(~C), p.allPieces[~C]);
67 2971753 : applyOn(pinner, [&](const Square & k){
68 326163 : pinned |= BBTools::between(k,p.king[C]) & p.allPieces[C];
69 : });
70 2319427 : return pinned;
71 : }
72 :
73 2918516 : bool isLazyHigh(ScoreType lazyThreshold, const EvalFeatures &features, EvalScore &score) {
74 : //if (DynamicConfig::FRC) return false;
75 2918516 : score = features.SumUp();
76 2918516 : return Abs(score[MG] + score[EG]) / 2 > lazyThreshold;
77 : }
78 :
79 0 : bool forbidNNUE([[maybe_unused]] const Position &p){
80 : /*
81 : // for opposite colored bishop alone (with pawns)
82 : if (p.mat[Co_White][M_t] == 1 &&
83 : p.mat[Co_Black][M_t] == 1 &&
84 : p.mat[Co_White][M_b] == 1 &&
85 : p.mat[Co_Black][M_b] == 1 &&
86 : countBit(p.allBishop() & whiteSquare) == 1 &&
87 : Abs(p.mat[Co_White][M_p] - p.mat[Co_Black][M_p]) < 4 ) return true;
88 : */
89 0 : return false;
90 : }
91 :
92 0 : ScoreType armageddonScore(ScoreType score, unsigned int ply, DepthType height, Color c) {
93 0 : return std::clamp(shiftArmageddon(score, ply, c), matedScore(height), matingScore(height-1));
94 : }
95 :
96 0 : ScoreType variantScore(ScoreType score, unsigned int ply, DepthType height, Color c) {
97 0 : if (DynamicConfig::armageddon) return armageddonScore(score, ply, height, c);
98 : return score;
99 : }
100 :
101 : // if antichess, we'll just try to minimize material for now
102 0 : ScoreType evalAntiChess(const Position &p, EvalData &data, [[maybe_unused]] Searcher &context, [[maybe_unused]] bool allowEGEvaluation, [[maybe_unused]] bool display) {
103 0 : const bool white2Play = p.c == Co_White;
104 :
105 : //@todo some special case : opposite color bishop => draw
106 :
107 0 : ScoreType matScoreW = 0;
108 0 : ScoreType matScoreB = 0;
109 0 : data.gp = gamePhase(p.mat, matScoreW, matScoreB);
110 : EvalScore score = EvalScore(
111 0 : (- p.mat[Co_White][M_k] + p.mat[Co_Black][M_k]) * absValue(P_wk) +
112 : (- p.mat[Co_White][M_q] + p.mat[Co_Black][M_q]) * absValue(P_wq) +
113 0 : (- p.mat[Co_White][M_r] + p.mat[Co_Black][M_r]) * absValue(P_wr) +
114 : (- p.mat[Co_White][M_b] + p.mat[Co_Black][M_b]) * absValue(P_wb) +
115 0 : (- p.mat[Co_White][M_n] + p.mat[Co_Black][M_n]) * absValue(P_wn) +
116 : (- p.mat[Co_White][M_p] + p.mat[Co_Black][M_p]) * absValue(P_wp) ,
117 0 : (- p.mat[Co_White][M_k] + p.mat[Co_Black][M_k]) * absValueEG(P_wk) +
118 0 : (- p.mat[Co_White][M_q] + p.mat[Co_Black][M_q]) * absValueEG(P_wq) +
119 0 : (- p.mat[Co_White][M_r] + p.mat[Co_Black][M_r]) * absValueEG(P_wr) +
120 0 : (- p.mat[Co_White][M_b] + p.mat[Co_Black][M_b]) * absValueEG(P_wb) +
121 0 : (- p.mat[Co_White][M_n] + p.mat[Co_Black][M_n]) * absValueEG(P_wn) +
122 0 : (- p.mat[Co_White][M_p] + p.mat[Co_Black][M_p]) * absValueEG(P_wp) );
123 :
124 : const ScoreType ret = ScaleScore(score, data.gp, 1.f);
125 0 : return (white2Play ? +1 : -1) * ret;
126 : }
127 :
128 : #ifdef WITH_NNUE
129 2127467 : ScoreType NNUEEVal(const Position & p, EvalData &data, Searcher &context, EvalFeatures &features, bool secondTime = false){
130 : START_TIMER
131 2127467 : if (DynamicConfig::armageddon) features.scalingFactor = 1.f; ///@todo better
132 : /*
133 : // king bucket
134 : constexpr int kingBucket[64] = {
135 : 0, 0, 0, 1, 1, 1, 2, 2,
136 : 0, 3, 3, 3, 4, 4, 5, 5,
137 : 3, 3, 3, 3, 4, 4, 5, 5,
138 : 3, 3, 3, 4, 4, 4, 5, 5,
139 : 3, 3, 3, 4, 4, 6, 6, 5,
140 : 6, 6, 6, 6, 6, 6, 6, 6,
141 : 6, 6, 6, 6, 6, 6, 6, 6,
142 : 6, 6, 6, 6, 6, 6, 6, 6,
143 : };
144 : const int bucket = kingBucket[relative_square(p.c, p.king[p.c])];
145 : */
146 : // or phase
147 : //const int bucketDivisor = 32/p.evaluator().weights.nbuckets;
148 : //const int bucket = std::max(0, std::min(p.evaluator().weights.nbuckets-1, (std::min(32,static_cast<int>(BB::countBit(p.occupancy()))-1) / bucketDivisor)));
149 : int bucket = 0;
150 2127467 : if (data.gp < 0.45f)
151 : bucket = 0;
152 : else
153 : bucket = 1;
154 :
155 : // call the net
156 2127467 : ScoreType nnueScore = static_cast<ScoreType>(p.evaluator().propagate(p.c, bucket));
157 :
158 : // fuse MG and EG score applying the EG scaling factor ///@todo, doesn't the net already learned that ????
159 2127467 : nnueScore = ScaleScore({nnueScore, nnueScore}, data.gp, features.scalingFactor);
160 : // NNUE evaluation scaling
161 2127467 : nnueScore = static_cast<ScoreType>((nnueScore * DynamicConfig::NNUEScaling) / 64);
162 : // take contempt into account (no tempo with NNUE,
163 : // the HalfKA net already take it into account but this is necessary for "dynamic" contempt from opponent)
164 2127467 : nnueScore += ScaleScore(context.contempt, data.gp);
165 : // clamp score
166 2127467 : nnueScore = clampScore(nnueScore);
167 2127467 : if (!DynamicConfig::armageddon){
168 : // apply scaling factor based on fifty move rule
169 2127467 : nnueScore = fiftyScale(nnueScore, p.fifty);
170 : }
171 2127467 : context.stats.incr(secondTime ? Stats::sid_evalNNUE2 : Stats::sid_evalNNUE);
172 : // apply variants scoring if requiered
173 : STOP_AND_SUM_TIMER(NNUE)
174 2127467 : return variantScore(nnueScore, p.halfmoves, context.height_, p.c);
175 : }
176 : #endif
177 :
178 2919045 : ScoreType eval(const Position &p, EvalData &data, Searcher &context, bool allowEGEvaluation, bool display) {
179 : START_TIMER
180 :
181 2919045 : if (DynamicConfig::antichess) return evalAntiChess(p, data, context, allowEGEvaluation, display);
182 : ///@todo other variants
183 :
184 : // in some variant (like antichess) king is not mandatory
185 : const bool kingIsMandatory = DynamicConfig::isKingMandatory();
186 :
187 : // if no more pieces except maybe kings, end of game as draw in most standard variants
188 2919045 : if ( (p.occupancy() & ~p.allKing()) == emptyBitBoard ){
189 : if (kingIsMandatory ){
190 : // drawScore take some variants into account like armageddon
191 0 : return context.drawScore(p, context.height_);
192 : }
193 : else {
194 : ///@todo implements things for some variants ?
195 : }
196 : }
197 :
198 : // is it player with the white pieces turn ?
199 2919045 : const bool white2Play = p.c == Co_White;
200 :
201 : #ifdef DEBUG_KING_CAP
202 : if (kingIsMandatory) {
203 : if (p.king[Co_White] == INVALIDSQUARE) {
204 : STOP_AND_SUM_TIMER(Eval)
205 : context.stats.incr(Stats::sid_evalNoKing);
206 : return data.gp = 0, (white2Play ? -1 : +1) * matingScore(0);
207 : }
208 : if (p.king[Co_Black] == INVALIDSQUARE) {
209 : STOP_AND_SUM_TIMER(Eval)
210 : context.stats.incr(Stats::sid_evalNoKing);
211 : return data.gp = 0, (white2Play ? +1 : -1) * matingScore(0);
212 : }
213 : }
214 : #endif
215 :
216 : ///@todo activate (or modulate) some features based on skill level
217 :
218 : // main features (feature won't survive Eval scope), but EvalData will !
219 : EvalFeatures features;
220 :
221 : // Material evaluation (most often from Material table)
222 2919045 : if (const Hash matHash = MaterialHash::getMaterialHash(p.mat); matHash != nullHash) {
223 : context.stats.incr(Stats::sid_materialTableHits);
224 : // Get material hash data
225 : const MaterialHash::MaterialHashEntry &MEntry = MaterialHash::materialHashTable[matHash];
226 : // update EvalData and EvalFeatures
227 2918200 : data.gp = MEntry.gamePhase();
228 2918200 : features.scores[F_material] += MEntry.score;
229 :
230 : // end game knowledge (helper or scaling), only for most standard chess variants with kings on the board
231 2918200 : if (allowEGEvaluation && kingIsMandatory && (p.mat[Co_White][M_t] + p.mat[Co_Black][M_t] < 5) ) {
232 : // let's verify that the is no naive capture before using end-game knowledge
233 1489005 : MoveList moves;
234 1489005 : MoveGen::generate<MoveGen::GP_cap>(p, moves);
235 : bool hasCapture = false;
236 1554297 : for (auto m : moves){
237 1295726 : Position p2 = p;
238 1295726 : if (const MoveInfo moveInfo(p2, m); applyMove(p2,moveInfo, true)){
239 : hasCapture = true;
240 1230434 : break;
241 : }
242 1295726 : }
243 : // probe endgame knowledge only if position is quiet from stm pov
244 : if (!hasCapture) {
245 258571 : const Color winningSideEG = features.scores[F_material][EG] > 0 ? Co_White : Co_Black;
246 : // helpers for various endgame
247 258571 : if (MEntry.t == MaterialHash::Ter_WhiteWinWithHelper || MEntry.t == MaterialHash::Ter_BlackWinWithHelper) {
248 : STOP_AND_SUM_TIMER(Eval)
249 : context.stats.incr(Stats::sid_materialTableHelper);
250 : const ScoreType materialTableScore =
251 39 : (white2Play ? +1 : -1) * (MaterialHash::helperTable[matHash](p, winningSideEG, features.scores[F_material][EG], context.height_));
252 30 : return variantScore(materialTableScore, p.halfmoves, context.height_, p.c);
253 : }
254 : // real FIDE draws (shall not happens for now in fact ///@todo !!!)
255 : else if (MEntry.t == MaterialHash::Ter_Draw) {
256 0 : if (!isPosInCheck(p)) {
257 : STOP_AND_SUM_TIMER(Eval)
258 : context.stats.incr(Stats::sid_materialTableDraw);
259 0 : return context.drawScore(p, context.height_); // drawScore take armageddon into account
260 : }
261 : }
262 : // non FIDE draws
263 : else if (MEntry.t == MaterialHash::Ter_MaterialDraw) {
264 56 : if (!isPosInCheck(p)) {
265 : STOP_AND_SUM_TIMER(Eval)
266 : context.stats.incr(Stats::sid_materialTableDraw2);
267 56 : return context.drawScore(p, context.height_); // drawScore take armageddon into account
268 : }
269 : }
270 : // apply some scaling (more later...)
271 : else if (MEntry.t == MaterialHash::Ter_WhiteWin || MEntry.t == MaterialHash::Ter_BlackWin)
272 0 : features.scalingFactor = static_cast<float>(EvalConfig::scalingFactorWin) / 128.f;
273 : else if (MEntry.t == MaterialHash::Ter_HardToWin)
274 38 : features.scalingFactor = static_cast<float>(EvalConfig::scalingFactorHardWin) / 128.f;
275 : else if (MEntry.t == MaterialHash::Ter_LikelyDraw)
276 5 : features.scalingFactor = static_cast<float>(EvalConfig::scalingFactorLikelyDraw) / 128.f;
277 : }
278 : /*
279 : else {
280 : std::cout << "posible cap" << std::endl;
281 : }
282 : */
283 : }
284 : }
285 : else { // game phase and material scores out of table
286 : ///@todo we don't care about imbalance here ?
287 845 : ScoreType matScoreW = 0;
288 845 : ScoreType matScoreB = 0;
289 845 : data.gp = gamePhase(p.mat, matScoreW, matScoreB);
290 845 : features.scores[F_material] += EvalScore(
291 845 : (p.mat[Co_White][M_q] - p.mat[Co_Black][M_q]) * absValue(P_wq) +
292 : (p.mat[Co_White][M_r] - p.mat[Co_Black][M_r]) * absValue(P_wr) +
293 845 : (p.mat[Co_White][M_b] - p.mat[Co_Black][M_b]) * absValue(P_wb) +
294 845 : (p.mat[Co_White][M_n] - p.mat[Co_Black][M_n]) * absValue(P_wn) +
295 : (p.mat[Co_White][M_p] - p.mat[Co_Black][M_p]) * absValue(P_wp) ,
296 845 : (p.mat[Co_White][M_q] - p.mat[Co_Black][M_q]) * absValueEG(P_wq) +
297 845 : (p.mat[Co_White][M_r] - p.mat[Co_Black][M_r]) * absValueEG(P_wr) +
298 845 : (p.mat[Co_White][M_b] - p.mat[Co_Black][M_b]) * absValueEG(P_wb) +
299 845 : (p.mat[Co_White][M_n] - p.mat[Co_Black][M_n]) * absValueEG(P_wn) +
300 845 : (p.mat[Co_White][M_p] - p.mat[Co_Black][M_p]) * absValueEG(P_wp) );
301 : //std::cout << ToString(p,true) << std::endl;
302 : context.stats.incr(Stats::sid_materialTableMiss);
303 : }
304 :
305 : //std::cout << "allow material eval? " << allowEGEvaluation << std::endl;
306 : //std::cout << GetFEN(p) << std::endl;
307 :
308 : #ifndef WITH_MATERIAL_TABLE
309 : // in case we are not using a material table, we still need KPK, KRK and KBNK helper
310 : // but only for most standard chess variants with kings on the board
311 : // end game knowledge (helper or scaling)
312 : if (allowEGEvaluation && kingIsMandatory && (p.mat[Co_White][M_t] + p.mat[Co_Black][M_t] < 5)) {
313 : static const auto matHashKPK = MaterialHash::getMaterialHash2(MaterialHash::materialFromString("KPK"));
314 : static const auto matHashKKP = MaterialHash::getMaterialHash2(MaterialHash::materialFromString("KKP"));
315 : static const auto matHashKQK = MaterialHash::getMaterialHash2(MaterialHash::materialFromString("KQK"));
316 : static const auto matHashKKQ = MaterialHash::getMaterialHash2(MaterialHash::materialFromString("KKQ"));
317 : static const auto matHashKRK = MaterialHash::getMaterialHash2(MaterialHash::materialFromString("KRK"));
318 : static const auto matHashKKR = MaterialHash::getMaterialHash2(MaterialHash::materialFromString("KKR"));
319 : static const auto matHashKLNK = MaterialHash::getMaterialHash2(MaterialHash::materialFromString("KLNK"));
320 : static const auto matHashKKLN = MaterialHash::getMaterialHash2(MaterialHash::materialFromString("KKLN"));
321 : static const auto matHashKDNK = MaterialHash::getMaterialHash2(MaterialHash::materialFromString("KDNK"));
322 : static const auto matHashKKDN = MaterialHash::getMaterialHash2(MaterialHash::materialFromString("KKDN"));
323 : STOP_AND_SUM_TIMER(Eval)
324 : const Color winningSideEG = features.scores[F_material][EG] > 0 ? Co_White : Co_Black;
325 : context.stats.incr(Stats::sid_materialTableHelper);
326 : ScoreType materialTableScore = 0;
327 : const Hash matHash2 = MaterialHash::getMaterialHash2(p.mat);
328 : bool matHelperHit = false;
329 : #if !defined(WITH_SMALL_MEMORY)
330 : if (matHash2 == matHashKPK || matHash2 == matHashKKP){
331 : materialTableScore = (white2Play ? +1 : -1) * (MaterialHash::helperKPK(p, winningSideEG, features.scores[F_material][EG], context.height_));
332 : matHelperHit = true;
333 : }
334 : #endif
335 : if (matHash2 == matHashKQK || matHash2 == matHashKKQ || matHash2 == matHashKRK || matHash2 == matHashKKR){
336 : materialTableScore = (white2Play ? +1 : -1) * (MaterialHash::helperKXK(p, winningSideEG, features.scores[F_material][EG], context.height_));
337 : matHelperHit = true;
338 : }
339 : if (matHash2 == matHashKLNK || matHash2 == matHashKKLN || matHash2 == matHashKDNK || matHash2 == matHashKKDN){
340 : materialTableScore = (white2Play ? +1 : -1) * (MaterialHash::helperKmmK(p, winningSideEG, features.scores[F_material][EG], context.height_));
341 : matHelperHit = true;
342 : }
343 : if (matHelperHit){
344 : return variantScore(materialTableScore, p.halfmoves, context.height_, p.c);
345 : }
346 : }
347 : #endif
348 :
349 : #ifdef WITH_EVAL_TUNING
350 : features.scores[F_material] += MaterialHash::Imbalance(p.mat, Co_White) - MaterialHash::Imbalance(p.mat, Co_Black);
351 : #endif
352 :
353 : #ifdef WITH_NNUE
354 : const bool forbiddenNNUE = forbidNNUE(p) && !DynamicConfig::forceNNUE;
355 2918959 : if (DynamicConfig::useNNUE && !forbiddenNNUE) {
356 : // we will stay to classic eval when the game is already decided (to gain some nps)
357 : ///@todo use data.gp inside NNUE condition ?
358 5837474 : if (EvalScore score; DynamicConfig::forceNNUE ||
359 2918516 : !isLazyHigh(static_cast<ScoreType>(DynamicConfig::NNUEThreshold), features, score)) {
360 : STOP_AND_SUM_TIMER(Eval)
361 2121991 : ScoreType nnueEval = NNUEEVal(p, data, context, features);
362 :
363 : // random factor in opening if requiered
364 2121991 : if (p.halfmoves < 10 && DynamicConfig::randomOpen != 0){
365 0 : nnueEval += static_cast<ScoreType>(randomInt<int /*NO SEED USE => random device*/>(-1*DynamicConfig::randomOpen, DynamicConfig::randomOpen));
366 : }
367 2121991 : return nnueEval;
368 : }
369 : // fall back to classic eval
370 : context.stats.incr(Stats::sid_evalStd);
371 : }
372 : #endif
373 :
374 : STOP_AND_SUM_TIMER(Eval1)
375 :
376 : // prefetch pawn evaluation table as soon as possible
377 796968 : context.prefetchPawn(computeHash(p));
378 :
379 : // pre compute some usefull bitboards (///@todo maybe this is not efficient ...)
380 : const colored<BitBoard> pawns = {p.whitePawn(), p.blackPawn()};
381 : const colored<BitBoard> knights = {p.whiteKnight(), p.blackKnight()};
382 : const colored<BitBoard> bishops = {p.whiteBishop(), p.blackBishop()};
383 : const colored<BitBoard> rooks = {p.whiteRook(), p.blackRook()};
384 796968 : const colored<BitBoard> queens = {p.whiteQueen(), p.blackQueen()};
385 : const colored<BitBoard> kings = {p.whiteKing(), p.blackKing()};
386 796968 : const colored<BitBoard> nonPawnMat = {p.allPieces[Co_White] & ~pawns[Co_White],
387 796968 : p.allPieces[Co_Black] & ~pawns[Co_Black]};
388 796968 : const colored<BitBoard> kingZone = {isValidSquare(p.king[Co_White]) ? BBTools::mask[p.king[Co_White]].kingZone : emptyBitBoard,
389 796968 : isValidSquare(p.king[Co_Black]) ? BBTools::mask[p.king[Co_Black]].kingZone : emptyBitBoard};
390 796968 : const BitBoard occupancy = p.occupancy();
391 :
392 : // helpers that will be filled by evalPiece calls
393 796968 : colored<ScoreType> kdanger = {0, 0};
394 796968 : colored<BitBoard> att = {emptyBitBoard, emptyBitBoard}; // bitboard of squares attacked by Color
395 796968 : colored<BitBoard> att2 = {emptyBitBoard, emptyBitBoard}; // bitboard of squares attacked twice by Color
396 796968 : array2d<BitBoard,2,6> attFromPiece = {{emptyBitBoard}}; // bitboard of squares attacked by specific piece of Color
397 796968 : array2d<BitBoard,2,6> checkers = {{emptyBitBoard}}; // bitboard of Color pieces squares attacking king
398 :
399 : // PST, attack, danger
400 796968 : const bool withForwadness = DynamicConfig::styleForwardness != 50;
401 796968 : const array1d<ScoreType,2> staticColorSignHelper = { +1, -1};
402 2390904 : for (Color c = Co_White ; c <= Co_Black ; ++c){
403 : // will do pawns later
404 9563616 : for (Piece pp = P_wn ; pp <= P_wk ; ++pp){
405 15939360 : applyOn(p.pieces_const(c,pp), [&](const Square & k){
406 4906538 : const Square kk = relative_square(~c,k);
407 9813076 : features.scores[F_positional] += EvalConfig::PST[pp - 1][kk] * staticColorSignHelper[c];
408 4906538 : if (withForwadness)
409 0 : features.scores[F_positional] += EvalScore {ScoreType(((DynamicConfig::styleForwardness - 50) * SQRANK(kk)) / 8), 0} * staticColorSignHelper[c];
410 : // aligned threats removing own piece (not pawn) in occupancy
411 4906538 : const BitBoard shadowTarget = BBTools::pfCoverage[pp - 1](k, occupancy ^ nonPawnMat[c], c);
412 4906538 : if (shadowTarget) {
413 4906538 : kdanger[~c] += countBit(shadowTarget & kingZone[~c]) * EvalConfig::kingAttWeight[EvalConfig::katt_attack][pp - 1];
414 4906538 : const BitBoard target = BBTools::pfCoverage[pp - 1](k, occupancy, c); // real targets
415 4906538 : if (target) {
416 4906538 : attFromPiece[c][pp - 1] |= target;
417 4906538 : att2[c] |= att[c] & target;
418 4906538 : att[c] |= target;
419 4906538 : if (target & p.pieces_const<P_wk>(~c)) checkers[c][pp - 1] |= SquareToBitboard(k);
420 4906538 : kdanger[c] -= countBit(target & kingZone[c]) * EvalConfig::kingAttWeight[EvalConfig::katt_defence][pp - 1];
421 : }
422 : }
423 4906538 : });
424 : }
425 : }
426 :
427 : // random factor in opening if requiered
428 796968 : if (p.halfmoves < 10 && DynamicConfig::randomOpen != 0){
429 0 : features.scores[F_positional] += static_cast<ScoreType>(randomInt<int /*NO SEED USE => random device*/>(-1*DynamicConfig::randomOpen, DynamicConfig::randomOpen));
430 : }
431 :
432 : STOP_AND_SUM_TIMER(Eval2)
433 :
434 796968 : Searcher::PawnEntry *pePtr = nullptr;
435 : #ifdef WITH_EVAL_TUNING
436 : Searcher::PawnEntry dummy; // used for evaluations tuning
437 : pePtr = &dummy;
438 : {
439 : #else
440 796968 : if (!context.getPawnEntry(computePHash(p), pePtr)) {
441 : #endif
442 148754 : assert(pePtr);
443 : Searcher::PawnEntry &pe = *pePtr;
444 : pe.reset();
445 148754 : pe.pawnTargets[Co_White] = BBTools::pawnAttacks<Co_White>(pawns[Co_White]);
446 148754 : pe.pawnTargets[Co_Black] = BBTools::pawnAttacks<Co_Black>(pawns[Co_Black]);
447 : // semiOpen white means with white pawn, and without black pawn
448 148754 : pe.semiOpenFiles[Co_White] = BBTools::fillFile(pawns[Co_White]) & ~BBTools::fillFile(pawns[Co_Black]);
449 148754 : pe.semiOpenFiles[Co_Black] = BBTools::fillFile(pawns[Co_Black]) & ~BBTools::fillFile(pawns[Co_White]);
450 148754 : pe.passed[Co_White] = BBTools::pawnPassed<Co_White>(pawns[Co_White], pawns[Co_Black]);
451 148754 : pe.passed[Co_Black] = BBTools::pawnPassed<Co_Black>(pawns[Co_Black], pawns[Co_White]);
452 148754 : pe.holes[Co_White] = BBTools::pawnHoles<Co_White>(pawns[Co_White]) & holesZone[Co_White];
453 148754 : pe.holes[Co_Black] = BBTools::pawnHoles<Co_Black>(pawns[Co_Black]) & holesZone[Co_Black];
454 148754 : pe.openFiles = BBTools::openFiles(pawns[Co_White], pawns[Co_Black]);
455 :
456 : // PST
457 148754 : evalPawn<Co_White>(pawns[Co_White], pe.score);
458 : evalPawn<Co_Black>(pawns[Co_Black], pe.score);
459 :
460 : // danger in king zone (from pawns attacking or defending)
461 148754 : pe.danger[Co_White] -= countBit(pe.pawnTargets[Co_White] & kingZone[Co_White]) * EvalConfig::kingAttWeight[EvalConfig::katt_defence][0];
462 148754 : pe.danger[Co_White] += countBit(pe.pawnTargets[Co_Black] & kingZone[Co_White]) * EvalConfig::kingAttWeight[EvalConfig::katt_attack][0];
463 148754 : pe.danger[Co_Black] -= countBit(pe.pawnTargets[Co_Black] & kingZone[Co_Black]) * EvalConfig::kingAttWeight[EvalConfig::katt_defence][0];
464 148754 : pe.danger[Co_Black] += countBit(pe.pawnTargets[Co_White] & kingZone[Co_Black]) * EvalConfig::kingAttWeight[EvalConfig::katt_attack][0];
465 :
466 : // pawn passer
467 148754 : evalPawnPasser<Co_White>(p, pe.passed[Co_White], pe.score);
468 148754 : evalPawnPasser<Co_Black>(p, pe.passed[Co_Black], pe.score);
469 :
470 : // pawn protected
471 148754 : evalPawnProtected<Co_White>(pe.passed[Co_White] & pe.pawnTargets[Co_White], pe.score);
472 148754 : evalPawnProtected<Co_Black>(pe.passed[Co_Black] & pe.pawnTargets[Co_Black], pe.score);
473 :
474 : // pawn candidate
475 148754 : evalPawnCandidate<Co_White>(BBTools::pawnCandidates<Co_White>(pawns[Co_White], pawns[Co_Black]), pe.score);
476 148754 : evalPawnCandidate<Co_Black>(BBTools::pawnCandidates<Co_Black>(pawns[Co_Black], pawns[Co_White]), pe.score);
477 :
478 : ///@todo hidden passed
479 :
480 : // bad pawns
481 : const colored<BitBoard> semiOpenPawn = {BBTools::pawnSemiOpen<Co_White>(pawns[Co_White], pawns[Co_Black]),
482 : BBTools::pawnSemiOpen<Co_Black>(pawns[Co_Black], pawns[Co_White])};
483 148754 : const colored<BitBoard> backward = {BBTools::pawnBackward<Co_White>(pawns[Co_White], pawns[Co_Black]),
484 148754 : BBTools::pawnBackward<Co_Black>(pawns[Co_Black], pawns[Co_White])};
485 148754 : const BitBoard backwardOpenW = backward[Co_White] & semiOpenPawn[Co_White];
486 148754 : const BitBoard backwardCloseW = backward[Co_White] & ~semiOpenPawn[Co_White];
487 148754 : const BitBoard backwardOpenB = backward[Co_Black] & semiOpenPawn[Co_Black];
488 148754 : const BitBoard backwardCloseB = backward[Co_Black] & ~semiOpenPawn[Co_Black];
489 : const colored<BitBoard> doubled = {BBTools::pawnDoubled<Co_White>(pawns[Co_White]),
490 : BBTools::pawnDoubled<Co_Black>(pawns[Co_Black])};
491 148754 : const BitBoard doubledOpenW = doubled[Co_White] & semiOpenPawn[Co_White];
492 148754 : const BitBoard doubledCloseW = doubled[Co_White] & ~semiOpenPawn[Co_White];
493 148754 : const BitBoard doubledOpenB = doubled[Co_Black] & semiOpenPawn[Co_Black];
494 148754 : const BitBoard doubledCloseB = doubled[Co_Black] & ~semiOpenPawn[Co_Black];
495 148754 : const colored<BitBoard> isolated = {BBTools::pawnIsolated(pawns[Co_White]),
496 148754 : BBTools::pawnIsolated(pawns[Co_Black])};
497 148754 : const BitBoard isolatedOpenW = isolated[Co_White] & semiOpenPawn[Co_White];
498 148754 : const BitBoard isolatedCloseW = isolated[Co_White] & ~semiOpenPawn[Co_White];
499 148754 : const BitBoard isolatedOpenB = isolated[Co_Black] & semiOpenPawn[Co_Black];
500 148754 : const BitBoard isolatedCloseB = isolated[Co_Black] & ~semiOpenPawn[Co_Black];
501 : const colored<BitBoard> detached = {BBTools::pawnDetached<Co_White>(pawns[Co_White], pawns[Co_Black]),
502 : BBTools::pawnDetached<Co_Black>(pawns[Co_Black], pawns[Co_White])};
503 148754 : const BitBoard detachedOpenW = detached[Co_White] & semiOpenPawn[Co_White] & ~backward[Co_White];
504 148754 : const BitBoard detachedCloseW = detached[Co_White] & ~semiOpenPawn[Co_White] & ~backward[Co_White];
505 148754 : const BitBoard detachedOpenB = detached[Co_Black] & semiOpenPawn[Co_Black] & ~backward[Co_Black];
506 148754 : const BitBoard detachedCloseB = detached[Co_Black] & ~semiOpenPawn[Co_Black] & ~backward[Co_Black];
507 :
508 1041278 : for (Rank r = Rank_2; r <= Rank_7; ++r) { // simply r2..r7 for classic chess works
509 : // pawn backward
510 1785048 : pe.score -= EvalConfig::backwardPawn[r][EvalConfig::Close] * countBit(backwardCloseW & ranks[r]);
511 1785048 : pe.score -= EvalConfig::backwardPawn[r][EvalConfig::SemiOpen] * countBit(backwardOpenW & ranks[r]);
512 : // double pawn malus
513 1785048 : pe.score -= EvalConfig::doublePawn[r][EvalConfig::Close] * countBit(doubledCloseW & ranks[r]);
514 1785048 : pe.score -= EvalConfig::doublePawn[r][EvalConfig::SemiOpen] * countBit(doubledOpenW & ranks[r]);
515 : // isolated pawn malus
516 1785048 : pe.score -= EvalConfig::isolatedPawn[r][EvalConfig::Close] * countBit(isolatedCloseW & ranks[r]);
517 1785048 : pe.score -= EvalConfig::isolatedPawn[r][EvalConfig::SemiOpen] * countBit(isolatedOpenW & ranks[r]);
518 : // detached pawn (not backward)
519 1785048 : pe.score -= EvalConfig::detachedPawn[r][EvalConfig::Close] * countBit(detachedCloseW & ranks[r]);
520 1785048 : pe.score -= EvalConfig::detachedPawn[r][EvalConfig::SemiOpen] * countBit(detachedOpenW & ranks[r]);
521 :
522 : // pawn backward
523 1785048 : pe.score += EvalConfig::backwardPawn[7 - r][EvalConfig::Close] * countBit(backwardCloseB & ranks[r]);
524 1785048 : pe.score += EvalConfig::backwardPawn[7 - r][EvalConfig::SemiOpen] * countBit(backwardOpenB & ranks[r]);
525 : // double pawn malus
526 1785048 : pe.score += EvalConfig::doublePawn[7 - r][EvalConfig::Close] * countBit(doubledCloseB & ranks[r]);
527 1785048 : pe.score += EvalConfig::doublePawn[7 - r][EvalConfig::SemiOpen] * countBit(doubledOpenB & ranks[r]);
528 : // isolated pawn malus
529 1785048 : pe.score += EvalConfig::isolatedPawn[7 - r][EvalConfig::Close] * countBit(isolatedCloseB & ranks[r]);
530 1785048 : pe.score += EvalConfig::isolatedPawn[7 - r][EvalConfig::SemiOpen] * countBit(isolatedOpenB & ranks[r]);
531 : // detached pawn (not backward)
532 1785048 : pe.score += EvalConfig::detachedPawn[7 - r][EvalConfig::Close] * countBit(detachedCloseB & ranks[r]);
533 1785048 : pe.score += EvalConfig::detachedPawn[7 - r][EvalConfig::SemiOpen] * countBit(detachedOpenB & ranks[r]);
534 : }
535 :
536 : // pawn impact on king safety (PST and king troppism alone is not enough)
537 : if (kingIsMandatory){
538 : // pawn shield
539 148754 : const colored<BitBoard> kingShield = {kingZone[Co_White] & ~BBTools::shiftS<Co_White>(ranks[SQRANK(p.king[Co_White])]),
540 148754 : kingZone[Co_Black] & ~BBTools::shiftS<Co_Black>(ranks[SQRANK(p.king[Co_Black])])};
541 148754 : const int pawnShieldW = countBit(kingShield[Co_White] & pawns[Co_White]);
542 148754 : const int pawnShieldB = countBit(kingShield[Co_Black] & pawns[Co_Black]);
543 297508 : pe.score += EvalConfig::pawnShieldBonus * std::min(pawnShieldW * pawnShieldW, 9);
544 297508 : pe.score -= EvalConfig::pawnShieldBonus * std::min(pawnShieldB * pawnShieldB, 9);
545 :
546 : // malus for king on a pawnless flank
547 148754 : const File wkf = SQFILE(p.king[Co_White]);
548 148754 : const File bkf = SQFILE(p.king[Co_Black]);
549 148754 : if (!(pawns[Co_White] & kingFlank[wkf])) pe.score += EvalConfig::pawnlessFlank;
550 148754 : if (!(pawns[Co_Black] & kingFlank[bkf])) pe.score -= EvalConfig::pawnlessFlank;
551 :
552 : // pawn storm
553 297508 : pe.score -= EvalConfig::pawnStormMalus * countBit(kingFlank[wkf] & (rank3 | rank4) & pawns[Co_Black]);
554 297508 : pe.score += EvalConfig::pawnStormMalus * countBit(kingFlank[bkf] & (rank5 | rank6) & pawns[Co_White]);
555 :
556 : // open file near king
557 148754 : pe.danger[Co_White] += EvalConfig::kingAttOpenfile * countBit(kingFlank[wkf] & pe.openFiles) / 8;
558 148754 : pe.danger[Co_White] += EvalConfig::kingAttSemiOpenfileOpp * countBit(kingFlank[wkf] & pe.semiOpenFiles[Co_White]) / 8;
559 148754 : pe.danger[Co_White] += EvalConfig::kingAttSemiOpenfileOur * countBit(kingFlank[wkf] & pe.semiOpenFiles[Co_Black]) / 8;
560 148754 : pe.danger[Co_Black] += EvalConfig::kingAttOpenfile * countBit(kingFlank[bkf] & pe.openFiles) / 8;
561 148754 : pe.danger[Co_Black] += EvalConfig::kingAttSemiOpenfileOpp * countBit(kingFlank[bkf] & pe.semiOpenFiles[Co_Black]) / 8;
562 148754 : pe.danger[Co_Black] += EvalConfig::kingAttSemiOpenfileOur * countBit(kingFlank[bkf] & pe.semiOpenFiles[Co_White]) / 8;
563 :
564 : // fawn (mostly for fun ...)
565 148754 : pe.score -= EvalConfig::pawnFawnMalusKS *
566 297508 : (countBit((pawns[Co_White] & (BBSq_h2 | BBSq_g3)) | (pawns[Co_Black] & BBSq_h3) | (kings[Co_White] & kingSide)) / 4);
567 148754 : pe.score += EvalConfig::pawnFawnMalusKS *
568 297508 : (countBit((pawns[Co_Black] & (BBSq_h7 | BBSq_g6)) | (pawns[Co_White] & BBSq_h6) | (kings[Co_Black] & kingSide)) / 4);
569 148754 : pe.score -= EvalConfig::pawnFawnMalusQS *
570 297508 : (countBit((pawns[Co_White] & (BBSq_a2 | BBSq_b3)) | (pawns[Co_Black] & BBSq_a3) | (kings[Co_White] & queenSide)) / 4);
571 148754 : pe.score += EvalConfig::pawnFawnMalusQS *
572 297508 : (countBit((pawns[Co_Black] & (BBSq_a7 | BBSq_b6)) | (pawns[Co_White] & BBSq_a6) | (kings[Co_Black] & queenSide)) / 4);
573 : }
574 :
575 : context.stats.incr(Stats::sid_ttPawnInsert);
576 148754 : pe.h = Hash64to32(computePHash(p)); // this associate the pawn entry to the current K&P positions
577 : }
578 796968 : assert(pePtr);
579 : const Searcher::PawnEntry &pe = *pePtr; // let's use a ref instead of a pointer
580 : // add computed or hash score for pawn structure
581 796968 : features.scores[F_pawnStruct] += pe.score;
582 :
583 : // update global things with freshy gotten pawn entry stuff
584 796968 : kdanger[Co_White] += pe.danger[Co_White];
585 796968 : kdanger[Co_Black] += pe.danger[Co_Black];
586 796968 : checkers[Co_White][0] = BBTools::pawnAttacks<Co_Black>(kings[Co_Black]) & pawns[Co_White];
587 796968 : checkers[Co_Black][0] = BBTools::pawnAttacks<Co_White>(kings[Co_White]) & pawns[Co_Black];
588 796968 : att2[Co_White] |= att[Co_White] & pe.pawnTargets[Co_White];
589 796968 : att2[Co_Black] |= att[Co_Black] & pe.pawnTargets[Co_Black];
590 796968 : att[Co_White] |= pe.pawnTargets[Co_White];
591 796968 : att[Co_Black] |= pe.pawnTargets[Co_Black];
592 :
593 : STOP_AND_SUM_TIMER(Eval3)
594 :
595 : //const BitBoard attackedOrNotDefended[2] = {att[Co_White] | ~att[Co_Black] , att[Co_Black] | ~att[Co_White] };
596 796968 : const colored<BitBoard> attackedAndNotDefended = {att[Co_White] & ~att[Co_Black], att[Co_Black] & ~att[Co_White]};
597 796968 : const colored<BitBoard> attacked2AndNotDefended2 = {att2[Co_White] & ~att2[Co_Black], att2[Co_Black] & ~att2[Co_White]};
598 :
599 796968 : const colored<BitBoard> weakSquare = { att[Co_Black] & ~att2[Co_White] & (~att[Co_White] | attFromPiece[Co_White][P_wk - 1] | attFromPiece[Co_White][P_wq - 1]),
600 796968 : att[Co_White] & ~att2[Co_Black] & (~att[Co_Black] | attFromPiece[Co_Black][P_wk - 1] | attFromPiece[Co_Black][P_wq - 1])};
601 796968 : const colored<BitBoard> safeSquare = { ~att[Co_Black] | (weakSquare[Co_Black] & att2[Co_White]),
602 796968 : ~att[Co_White] | (weakSquare[Co_White] & att2[Co_Black])};
603 796968 : const colored<BitBoard> protectedSquare = {pe.pawnTargets[Co_White] | attackedAndNotDefended[Co_White] | attacked2AndNotDefended2[Co_White],
604 796968 : pe.pawnTargets[Co_Black] | attackedAndNotDefended[Co_Black] | attacked2AndNotDefended2[Co_Black]};
605 :
606 796968 : data.haveThreats[Co_White] = (att[Co_White] & p.allPieces[Co_Black]) != emptyBitBoard;
607 796968 : data.haveThreats[Co_Black] = (att[Co_Black] & p.allPieces[Co_White]) != emptyBitBoard;
608 :
609 796968 : data.goodThreats[Co_White] = ((attFromPiece[Co_White][P_wp-1] & p.allPieces[Co_Black] & ~p.blackPawn())
610 796968 : | (attFromPiece[Co_White][P_wn-1] & (p.blackQueen() | p.blackRook()))
611 796968 : | (attFromPiece[Co_White][P_wb-1] & (p.blackQueen() | p.blackRook()))
612 796968 : | (attFromPiece[Co_White][P_wr-1] & p.blackQueen())) != emptyBitBoard;
613 796968 : data.goodThreats[Co_Black] = ((attFromPiece[Co_Black][P_wp-1] & p.allPieces[Co_White] & ~p.whitePawn())
614 796968 : | (attFromPiece[Co_Black][P_wn-1] & (p.whiteQueen() | p.whiteRook()))
615 796968 : | (attFromPiece[Co_Black][P_wb-1] & (p.whiteQueen() | p.whiteRook()))
616 796968 : | (attFromPiece[Co_Black][P_wr-1] & p.whiteQueen())) != emptyBitBoard;
617 :
618 : // reward safe checks
619 796968 : kdanger[Co_White] += EvalConfig::kingAttSafeCheck[0] * countBit(checkers[Co_Black][0] & att[Co_Black]);
620 796968 : kdanger[Co_Black] += EvalConfig::kingAttSafeCheck[0] * countBit(checkers[Co_White][0] & att[Co_White]);
621 3984840 : for (Piece pp = P_wn; pp < P_wk; ++pp) {
622 3187872 : kdanger[Co_White] += EvalConfig::kingAttSafeCheck[pp - 1] * countBit(checkers[Co_Black][pp - 1] & safeSquare[Co_Black]);
623 3187872 : kdanger[Co_Black] += EvalConfig::kingAttSafeCheck[pp - 1] * countBit(checkers[Co_White][pp - 1] & safeSquare[Co_White]);
624 : }
625 :
626 : // less danger if no enemy queen
627 796968 : const Square whiteQueenSquare = queens[Co_White] ? BBTools::SquareFromBitBoard(queens[Co_White]) : INVALIDSQUARE;
628 796968 : const Square blackQueenSquare = queens[Co_Black] ? BBTools::SquareFromBitBoard(queens[Co_Black]) : INVALIDSQUARE;
629 796968 : kdanger[Co_White] -= blackQueenSquare == INVALIDSQUARE ? EvalConfig::kingAttNoQueen : 0;
630 796968 : kdanger[Co_Black] -= whiteQueenSquare == INVALIDSQUARE ? EvalConfig::kingAttNoQueen : 0;
631 :
632 : // danger : use king danger score. **DO NOT** apply this in end-game
633 796968 : const ScoreType dw = EvalConfig::kingAttTable[std::min(std::max(ScoreType(kdanger[Co_White] / 32), ScoreType(0)), ScoreType(63))];
634 1593936 : const ScoreType db = EvalConfig::kingAttTable[std::min(std::max(ScoreType(kdanger[Co_Black] / 32), ScoreType(0)), ScoreType(63))];
635 796968 : features.scores[F_attack] -= EvalScore(dw, 0);
636 796968 : features.scores[F_attack] += EvalScore(db, 0);
637 796968 : data.danger[Co_White] = kdanger[Co_White];
638 796968 : data.danger[Co_Black] = kdanger[Co_Black];
639 :
640 : // own piece in front of pawn
641 1593936 : features.scores[F_development] += EvalConfig::pieceFrontPawn * countBit(BBTools::shiftN<Co_White>(pawns[Co_White]) & nonPawnMat[Co_White]);
642 1593936 : features.scores[F_development] -= EvalConfig::pieceFrontPawn * countBit(BBTools::shiftN<Co_Black>(pawns[Co_Black]) & nonPawnMat[Co_Black]);
643 :
644 : // pawn in front of own minor
645 796968 : const colored<BitBoard> minor = {knights[Co_White] | bishops[Co_White], knights[Co_Black] | bishops[Co_Black]};
646 796968 : applyOn(BBTools::shiftS<Co_White>(pawns[Co_White]) & minor[Co_White], [&](const Square & k){
647 8191 : features.scores[F_development] += EvalConfig::pawnFrontMinor[ColorRank<Co_White>(k)];
648 : });
649 796968 : applyOn(BBTools::shiftS<Co_Black>(pawns[Co_Black]) & minor[Co_Black], [&](const Square & k){
650 9560 : features.scores[F_development] -= EvalConfig::pawnFrontMinor[ColorRank<Co_Black>(k)];
651 : });
652 :
653 : // center control
654 1593936 : features.scores[F_development] += EvalConfig::centerControl * countBit(protectedSquare[Co_White] & extendedCenter);
655 1593936 : features.scores[F_development] -= EvalConfig::centerControl * countBit(protectedSquare[Co_Black] & extendedCenter);
656 :
657 : // pawn hole, unprotected
658 1593936 : features.scores[F_positional] += EvalConfig::holesMalus * countBit(pe.holes[Co_White] & ~protectedSquare[Co_White]) / 4;
659 1593936 : features.scores[F_positional] -= EvalConfig::holesMalus * countBit(pe.holes[Co_Black] & ~protectedSquare[Co_Black]) / 4;
660 :
661 : // free passer bonus
662 796968 : evalPawnFreePasser<Co_White>(p, pe.passed[Co_White], features.scores[F_pawnStruct]);
663 796968 : evalPawnFreePasser<Co_Black>(p, pe.passed[Co_Black], features.scores[F_pawnStruct]);
664 :
665 : // rook behind passed
666 796968 : features.scores[F_pawnStruct] += EvalConfig::rookBehindPassed * (countBit(rooks[Co_White] & BBTools::rearSpan<Co_White>(pe.passed[Co_White])) -
667 1593936 : countBit(rooks[Co_Black] & BBTools::rearSpan<Co_White>(pe.passed[Co_White])));
668 796968 : features.scores[F_pawnStruct] -= EvalConfig::rookBehindPassed * (countBit(rooks[Co_Black] & BBTools::rearSpan<Co_Black>(pe.passed[Co_Black])) -
669 1593936 : countBit(rooks[Co_White] & BBTools::rearSpan<Co_Black>(pe.passed[Co_Black])));
670 :
671 : // protected minor blocking openfile
672 1593936 : features.scores[F_positional] += EvalConfig::minorOnOpenFile * countBit(pe.openFiles & (minor[Co_White]) & pe.pawnTargets[Co_White]);
673 1593936 : features.scores[F_positional] -= EvalConfig::minorOnOpenFile * countBit(pe.openFiles & (minor[Co_Black]) & pe.pawnTargets[Co_Black]);
674 :
675 : // knight on opponent hole, protected by pawn
676 1593936 : features.scores[F_positional] += EvalConfig::outpostN * countBit(pe.holes[Co_Black] & knights[Co_White] & pe.pawnTargets[Co_White]);
677 1593936 : features.scores[F_positional] -= EvalConfig::outpostN * countBit(pe.holes[Co_White] & knights[Co_Black] & pe.pawnTargets[Co_Black]);
678 :
679 : // bishop on opponent hole, protected by pawn
680 1593936 : features.scores[F_positional] += EvalConfig::outpostB * countBit(pe.holes[Co_Black] & bishops[Co_White] & pe.pawnTargets[Co_White]);
681 1593936 : features.scores[F_positional] -= EvalConfig::outpostB * countBit(pe.holes[Co_White] & bishops[Co_Black] & pe.pawnTargets[Co_Black]);
682 :
683 : // knight far from both kings gets a penalty
684 : if (kingIsMandatory){
685 796968 : applyOn(knights[Co_White], [&](const Square & knighSq){
686 12731 : features.scores[F_positional] +=
687 13995 : EvalConfig::knightTooFar[std::min(chebyshevDistance(p.king[Co_White], knighSq), chebyshevDistance(p.king[Co_Black], knighSq))];
688 12731 : });
689 805366 : applyOn(knights[Co_Black], [&](const Square & knighSq){
690 8398 : features.scores[F_positional] -=
691 15982 : EvalConfig::knightTooFar[std::min(chebyshevDistance(p.king[Co_White], knighSq), chebyshevDistance(p.king[Co_Black], knighSq))];
692 8398 : });
693 : }
694 :
695 : // number of hanging pieces
696 796968 : const colored<BitBoard> hanging = {nonPawnMat[Co_White] & weakSquare[Co_White], nonPawnMat[Co_Black] & weakSquare[Co_Black]};
697 1593936 : features.scores[F_attack] += EvalConfig::hangingPieceMalus * (countBit(hanging[Co_White]) - countBit(hanging[Co_Black]));
698 :
699 : // threats by minor
700 796968 : BitBoard targetThreat = (nonPawnMat[Co_White] | (pawns[Co_White] & weakSquare[Co_White])) & (attFromPiece[Co_Black][P_wn - 1] | attFromPiece[Co_Black][P_wb - 1]);
701 798860 : applyOn(targetThreat, [&](const Square & k){
702 3784 : features.scores[F_attack] += EvalConfig::threatByMinor[PieceTools::getPieceType(p, k) - 1];
703 1892 : });
704 796968 : targetThreat = (nonPawnMat[Co_Black] | (pawns[Co_Black] & weakSquare[Co_Black])) & (attFromPiece[Co_White][P_wn - 1] | attFromPiece[Co_White][P_wb - 1]);
705 799332 : applyOn(targetThreat, [&](const Square & k){
706 4728 : features.scores[F_attack] -= EvalConfig::threatByMinor[PieceTools::getPieceType(p, k) - 1];
707 2364 : });
708 :
709 : // threats by rook
710 796968 : targetThreat = p.allPieces[Co_White] & weakSquare[Co_White] & attFromPiece[Co_Black][P_wr - 1];
711 1203371 : applyOn(targetThreat, [&](const Square & k){
712 812806 : features.scores[F_attack] += EvalConfig::threatByRook[PieceTools::getPieceType(p, k) - 1];
713 406403 : });
714 796968 : targetThreat = p.allPieces[Co_Black] & weakSquare[Co_Black] & attFromPiece[Co_White][P_wr - 1];
715 1218999 : applyOn(targetThreat, [&](const Square & k){
716 844062 : features.scores[F_attack] -= EvalConfig::threatByRook[PieceTools::getPieceType(p, k) - 1];
717 422031 : });
718 :
719 : // threats by queen
720 796968 : targetThreat = p.allPieces[Co_White] & weakSquare[Co_White] & attFromPiece[Co_Black][P_wq - 1];
721 1105139 : applyOn(targetThreat, [&](const Square & k){
722 616342 : features.scores[F_attack] += EvalConfig::threatByQueen[PieceTools::getPieceType(p, k) - 1];
723 308171 : });
724 796968 : targetThreat = p.allPieces[Co_Black] & weakSquare[Co_Black] & attFromPiece[Co_White][P_wq - 1];
725 1131919 : applyOn(targetThreat, [&](const Square & k){
726 669902 : features.scores[F_attack] -= EvalConfig::threatByQueen[PieceTools::getPieceType(p, k) - 1];
727 334951 : });
728 :
729 : // threats by king
730 796968 : targetThreat = p.allPieces[Co_White] & weakSquare[Co_White] & attFromPiece[Co_Black][P_wk - 1];
731 831643 : applyOn(targetThreat, [&](const Square & k){
732 69350 : features.scores[F_attack] += EvalConfig::threatByKing[PieceTools::getPieceType(p, k) - 1];
733 34675 : });
734 796968 : targetThreat = p.allPieces[Co_Black] & weakSquare[Co_Black] & attFromPiece[Co_White][P_wk - 1];
735 820917 : applyOn(targetThreat, [&](const Square & k){
736 47898 : features.scores[F_attack] -= EvalConfig::threatByKing[PieceTools::getPieceType(p, k) - 1];
737 23949 : });
738 :
739 : // threat by safe pawn
740 796968 : const colored<BitBoard> safePawnAtt = {nonPawnMat[Co_Black] & BBTools::pawnAttacks<Co_White>(pawns[Co_White] & safeSquare[Co_White]),
741 796968 : nonPawnMat[Co_White] & BBTools::pawnAttacks<Co_Black>(pawns[Co_Black] & safeSquare[Co_Black])};
742 1593936 : features.scores[F_attack] += EvalConfig::pawnSafeAtt * (countBit(safePawnAtt[Co_White]) - countBit(safePawnAtt[Co_Black]));
743 :
744 : // safe pawn push (protected once or not attacked)
745 796968 : const colored<BitBoard> safePawnPush = {BBTools::shiftN<Co_White>(pawns[Co_White]) & ~occupancy & safeSquare[Co_White],
746 796968 : BBTools::shiftN<Co_Black>(pawns[Co_Black]) & ~occupancy & safeSquare[Co_Black]};
747 1593936 : features.scores[F_mobility] += EvalConfig::pawnMobility * (countBit(safePawnPush[Co_White]) - countBit(safePawnPush[Co_Black]));
748 :
749 : // threat by safe pawn push
750 : features.scores[F_attack] +=
751 1593936 : EvalConfig::pawnSafePushAtt * (countBit(nonPawnMat[Co_Black] & BBTools::pawnAttacks<Co_White>(safePawnPush[Co_White])) -
752 1593936 : countBit(nonPawnMat[Co_White] & BBTools::pawnAttacks<Co_Black>(safePawnPush[Co_Black])));
753 :
754 : STOP_AND_SUM_TIMER(Eval4)
755 :
756 : // pieces mobility (second attack loop needed, knowing safeSquare ...)
757 796968 : evalMob<P_wn, Co_White>(p, knights[Co_White], features.scores[F_mobility], safeSquare[Co_White], occupancy, data.mobility[Co_White]);
758 796968 : evalMob<P_wb, Co_White>(p, bishops[Co_White], features.scores[F_mobility], safeSquare[Co_White], occupancy, data.mobility[Co_White]);
759 796968 : evalMob<P_wr, Co_White>(p, rooks [Co_White], features.scores[F_mobility], safeSquare[Co_White], occupancy, data.mobility[Co_White]);
760 796968 : evalMobQ< Co_White>(p, queens [Co_White], features.scores[F_mobility], safeSquare[Co_White], occupancy, data.mobility[Co_White]);
761 1593936 : evalMobK< Co_White>(p, kings [Co_White], features.scores[F_mobility], ~att[Co_Black] , occupancy, data.mobility[Co_White]);
762 796968 : evalMob<P_wn, Co_Black>(p, knights[Co_Black], features.scores[F_mobility], safeSquare[Co_Black], occupancy, data.mobility[Co_Black]);
763 796968 : evalMob<P_wb, Co_Black>(p, bishops[Co_Black], features.scores[F_mobility], safeSquare[Co_Black], occupancy, data.mobility[Co_Black]);
764 796968 : evalMob<P_wr, Co_Black>(p, rooks [Co_Black], features.scores[F_mobility], safeSquare[Co_Black], occupancy, data.mobility[Co_Black]);
765 796968 : evalMobQ< Co_Black>(p, queens [Co_Black], features.scores[F_mobility], safeSquare[Co_Black], occupancy, data.mobility[Co_Black]);
766 796968 : evalMobK< Co_Black>(p, kings [Co_Black], features.scores[F_mobility], ~att[Co_White] , occupancy, data.mobility[Co_Black]);
767 :
768 : STOP_AND_SUM_TIMER(Eval5)
769 :
770 : // rook on open file
771 1593936 : features.scores[F_positional] += EvalConfig::rookOnOpenFile * countBit(rooks[Co_White] & pe.openFiles);
772 1593936 : features.scores[F_positional] += EvalConfig::rookOnOpenSemiFileOur * countBit(rooks[Co_White] & pe.semiOpenFiles[Co_White]);
773 1593936 : features.scores[F_positional] += EvalConfig::rookOnOpenSemiFileOpp * countBit(rooks[Co_White] & pe.semiOpenFiles[Co_Black]);
774 1593936 : features.scores[F_positional] -= EvalConfig::rookOnOpenFile * countBit(rooks[Co_Black] & pe.openFiles);
775 1593936 : features.scores[F_positional] -= EvalConfig::rookOnOpenSemiFileOur * countBit(rooks[Co_Black] & pe.semiOpenFiles[Co_Black]);
776 1593936 : features.scores[F_positional] -= EvalConfig::rookOnOpenSemiFileOpp * countBit(rooks[Co_Black] & pe.semiOpenFiles[Co_White]);
777 :
778 : // enemy rook facing king
779 1593936 : features.scores[F_positional] += EvalConfig::rookFrontKingMalus * countBit(BBTools::frontSpan<Co_White>(kings[Co_White]) & rooks[Co_Black]);
780 1593936 : features.scores[F_positional] -= EvalConfig::rookFrontKingMalus * countBit(BBTools::frontSpan<Co_Black>(kings[Co_Black]) & rooks[Co_White]);
781 :
782 : // enemy rook facing queen
783 1593936 : features.scores[F_positional] += EvalConfig::rookFrontQueenMalus * countBit(BBTools::frontSpan<Co_White>(queens[Co_White]) & rooks[Co_Black]);
784 1593936 : features.scores[F_positional] -= EvalConfig::rookFrontQueenMalus * countBit(BBTools::frontSpan<Co_Black>(queens[Co_Black]) & rooks[Co_White]);
785 :
786 : // queen aligned with own rook
787 1593936 : features.scores[F_positional] += EvalConfig::rookQueenSameFile * countBit(BBTools::fillFile(queens[Co_White]) & rooks[Co_White]);
788 1593936 : features.scores[F_positional] -= EvalConfig::rookQueenSameFile * countBit(BBTools::fillFile(queens[Co_Black]) & rooks[Co_Black]);
789 :
790 : // pins on king and queen
791 796968 : const colored<BitBoard> pinnedK = {getPinned<Co_White>(p, p.king[Co_White]), getPinned<Co_Black>(p, p.king[Co_Black])};
792 796968 : const colored<BitBoard> pinnedQ = {getPinned<Co_White>(p, whiteQueenSquare), getPinned<Co_Black>(p, blackQueenSquare)};
793 4781808 : for (Piece pp = P_wp; pp < P_wk; ++pp) {
794 3984840 : if (const BitBoard bw = p.pieces_const(Co_White, pp); bw) {
795 1919470 : if (pinnedK[Co_White] & bw) features.scores[F_attack] -= EvalConfig::pinnedKing[pp - 1] * countBit(pinnedK[Co_White] & bw);
796 1855410 : if (pinnedQ[Co_White] & bw) features.scores[F_attack] -= EvalConfig::pinnedQueen[pp - 1] * countBit(pinnedQ[Co_White] & bw);
797 : }
798 3984840 : if (const BitBoard bb = p.pieces_const(Co_Black, pp); bb) {
799 2104240 : if (pinnedK[Co_Black] & bb) features.scores[F_attack] += EvalConfig::pinnedKing[pp - 1] * countBit(pinnedK[Co_Black] & bb);
800 2033429 : if (pinnedQ[Co_Black] & bb) features.scores[F_attack] += EvalConfig::pinnedQueen[pp - 1] * countBit(pinnedQ[Co_Black] & bb);
801 : }
802 : }
803 :
804 : // attack : queen distance to opponent king (wrong if multiple queens ...)
805 796968 : if (kingIsMandatory && blackQueenSquare != INVALIDSQUARE)
806 896620 : features.scores[F_positional] -= EvalConfig::queenNearKing * (7 - chebyshevDistance(p.king[Co_White], blackQueenSquare));
807 796968 : if (kingIsMandatory && whiteQueenSquare != INVALIDSQUARE)
808 554362 : features.scores[F_positional] += EvalConfig::queenNearKing * (7 - chebyshevDistance(p.king[Co_Black], whiteQueenSquare));
809 :
810 : // number of pawn and piece type value ///@todo closedness instead ?
811 1593936 : features.scores[F_material] += EvalConfig::adjRook[p.mat[Co_White][M_p]] * ScoreType(p.mat[Co_White][M_r]);
812 1593936 : features.scores[F_material] -= EvalConfig::adjRook[p.mat[Co_Black][M_p]] * ScoreType(p.mat[Co_Black][M_r]);
813 1593936 : features.scores[F_material] += EvalConfig::adjKnight[p.mat[Co_White][M_p]] * ScoreType(p.mat[Co_White][M_n]);
814 1593936 : features.scores[F_material] -= EvalConfig::adjKnight[p.mat[Co_Black][M_p]] * ScoreType(p.mat[Co_Black][M_n]);
815 :
816 : // bad bishop
817 796968 : if (p.whiteLightBishop()) features.scores[F_material] -= EvalConfig::badBishop[countBit(pawns[Co_White] & whiteSquare)];
818 796968 : if (p.whiteDarkBishop()) features.scores[F_material] -= EvalConfig::badBishop[countBit(pawns[Co_White] & blackSquare)];
819 796968 : if (p.blackLightBishop()) features.scores[F_material] += EvalConfig::badBishop[countBit(pawns[Co_Black] & whiteSquare)];
820 796968 : if (p.blackDarkBishop()) features.scores[F_material] += EvalConfig::badBishop[countBit(pawns[Co_Black] & blackSquare)];
821 :
822 : // adjust piece pair score
823 796968 : features.scores[F_material] += ((p.mat[Co_White][M_b] > 1 ? EvalConfig::bishopPairBonus[p.mat[Co_White][M_p]] : 0) -
824 1593936 : (p.mat[Co_Black][M_b] > 1 ? EvalConfig::bishopPairBonus[p.mat[Co_Black][M_p]] : 0));
825 796968 : features.scores[F_material] += ((p.mat[Co_White][M_n] > 1 ? EvalConfig::knightPairMalus : 0) -
826 1593936 : (p.mat[Co_Black][M_n] > 1 ? EvalConfig::knightPairMalus : 0));
827 796968 : features.scores[F_material] += ((p.mat[Co_White][M_r] > 1 ? EvalConfig::rookPairMalus : 0) -
828 1593936 : (p.mat[Co_Black][M_r] > 1 ? EvalConfig::rookPairMalus : 0));
829 :
830 : // complexity
831 : // an "anti human" trick : winning side wants to keep the position more complex
832 1593936 : features.scores[F_complexity] += EvalScore {1, 0} * (white2Play ? +1 : -1) *
833 1593936 : (p.mat[Co_White][M_t] + p.mat[Co_Black][M_t] + (p.mat[Co_White][M_p] + p.mat[Co_Black][M_p]) / 2);
834 :
835 796968 : if (display) displayEval(data, features);
836 :
837 : // taking Style into account, giving each score a [0..2] factor
838 796968 : features.scores[F_material] = features.scores[F_material] .scale(1 + (DynamicConfig::styleMaterial - 50) / 50.f, 1.f);
839 796968 : features.scores[F_positional] = features.scores[F_positional] .scale(1 + (DynamicConfig::stylePositional - 50) / 50.f, 1.f);
840 796968 : features.scores[F_development] = features.scores[F_development].scale(1 + (DynamicConfig::styleDevelopment - 50) / 50.f, 1.f);
841 796968 : features.scores[F_mobility] = features.scores[F_mobility] .scale(1 + (DynamicConfig::styleMobility - 50) / 50.f, 1.f);
842 796968 : features.scores[F_pawnStruct] = features.scores[F_pawnStruct] .scale(1 + (DynamicConfig::stylePawnStruct - 50) / 50.f, 1.f);
843 796968 : features.scores[F_attack] = features.scores[F_attack] .scale(1 + (DynamicConfig::styleAttack - 50) / 50.f, 1.f);
844 796968 : features.scores[F_complexity] = features.scores[F_complexity] .scale((DynamicConfig::styleComplexity - 50) / 50.f, 0.f);
845 :
846 : // sum every score contribution
847 796968 : EvalScore score = features.SumUp();
848 :
849 : #ifdef VERBOSE_EVAL
850 : if (display) {
851 : Logging::LogIt(Logging::logInfo) << "Post correction ... ";
852 : displayEval(data, features);
853 : Logging::LogIt(Logging::logInfo) << "> All (before 2nd order) " << score;
854 : }
855 : #endif
856 :
857 : // initiative (kind of second order pawn structure stuff for end-games)
858 : const BitBoard &allPawns = p.allPawn();
859 796968 : EvalScore initiativeBonus = EvalConfig::initiative[0] * countBit(allPawns) +
860 796968 : EvalConfig::initiative[1] * ((allPawns & queenSide) && (allPawns & kingSide)) +
861 796968 : EvalConfig::initiative[2] * (countBit(occupancy & ~allPawns) == 2) - EvalConfig::initiative[3];
862 1593958 : initiativeBonus = EvalScore(sgn(score[MG]) * std::max(initiativeBonus[MG], static_cast<ScoreType>(-Abs(score[MG]))),
863 796968 : sgn(score[EG]) * std::max(initiativeBonus[EG], static_cast<ScoreType>(-Abs(score[EG]))));
864 796968 : score += initiativeBonus;
865 :
866 : #ifdef VERBOSE_EVAL
867 : if (display) {
868 : Logging::LogIt(Logging::logInfo) << "Initiative " << initiativeBonus;
869 : Logging::LogIt(Logging::logInfo) << "> All (with initiative) " << score;
870 : }
871 : #endif
872 :
873 : // add contempt
874 796968 : score += context.contempt;
875 :
876 : #ifdef VERBOSE_EVAL
877 : if (display) { Logging::LogIt(Logging::logInfo) << "> All (with contempt) " << score; }
878 : #endif
879 :
880 : // compute a scale factor in some other end-game cases that are not using the material table:
881 796968 : if (features.scalingFactor == 1.f && !DynamicConfig::armageddon) {
882 796968 : const Color strongSide = score[EG] > 0 ? Co_White : Co_Black;
883 :
884 : // opposite colored bishops (scale based on passed pawn of strong side)
885 796968 : if (p.mat[Co_White][M_b] == 1 && p.mat[Co_Black][M_b] == 1 && countBit(p.allBishop() & whiteSquare) == 1) {
886 5 : if (p.mat[Co_White][M_t] == 1 && p.mat[Co_Black][M_t] == 1) // only bishops and pawn
887 0 : features.scalingFactor =
888 0 : (EvalConfig::scalingFactorOppBishopAlone + EvalConfig::scalingFactorOppBishopAloneSlope * countBit(pe.passed[strongSide])) / 128.f;
889 : else // more pieces present
890 5 : features.scalingFactor =
891 5 : (EvalConfig::scalingFactorOppBishop + EvalConfig::scalingFactorOppBishopSlope * countBit(p.allPieces[strongSide])) / 128.f;
892 : }
893 :
894 : // queen versus no queen (scale on number of minor of no queen side)
895 796963 : else if (countBit(p.allQueen()) == 1) {
896 546658 : features.scalingFactor =
897 905971 : (EvalConfig::scalingFactorQueenNoQueen + EvalConfig::scalingFactorQueenNoQueenSlope * p.mat[p.whiteQueen() ? Co_Black : Co_White][M_m]) / 128.f;
898 : }
899 :
900 : // scale based on the number of pawn of strong side
901 : else
902 250305 : features.scalingFactor =
903 252312 : std::min(1.f, (EvalConfig::scalingFactorPawns + EvalConfig::scalingFactorPawnsSlope * p.mat[strongSide][M_p]) / 128.f);
904 :
905 : // moreover scaledown if pawn on only one side
906 796968 : if (allPawns && !((allPawns & queenSide) && (allPawns & kingSide))) { features.scalingFactor -= EvalConfig::scalingFactorPawnsOneSide / 128.f; }
907 : }
908 796968 : if (DynamicConfig::armageddon) features.scalingFactor = 1.f; ///@todo better
909 :
910 : #ifdef VERBOSE_EVAL
911 : if (display) { Logging::LogIt(Logging::logInfo) << "Scaling factor (corrected) " << features.scalingFactor; }
912 : #endif
913 :
914 : // fuse MG and EG score
915 : // scale both game phase and end-game scaling factor
916 796968 : ScoreType ret = ScaleScore(score, data.gp, features.scalingFactor);
917 :
918 : #ifdef VERBOSE_EVAL
919 : if (display) { Logging::LogIt(Logging::logInfo) << "> All (with game phase scaling) " << ret; }
920 : #endif
921 :
922 796968 : if (!DynamicConfig::armageddon){
923 : // apply scaling factor based on fifty move rule
924 796968 : ret = fiftyScale(ret, p.fifty);
925 : }
926 :
927 : #ifdef VERBOSE_EVAL
928 : if (display) { Logging::LogIt(Logging::logInfo) << "> All (with last scaling) " << ret; }
929 : #endif
930 :
931 : // apply tempo
932 1163671 : ret += EvalConfig::tempo * (white2Play ? +1 : -1);
933 :
934 : #ifdef VERBOSE_EVAL
935 : if (display) { Logging::LogIt(Logging::logInfo) << "> All (with tempo) " << ret; }
936 : #endif
937 :
938 : // clamp score
939 796968 : ret = clampScore(ret);
940 :
941 : #ifdef VERBOSE_EVAL
942 : if (display) { Logging::LogIt(Logging::logInfo) << "> All (clamped) " << ret; }
943 : #endif
944 :
945 : // take side to move into account
946 796968 : ret = (white2Play ? +1 : -1) * ret;
947 :
948 796969 : if (display) { Logging::LogIt(Logging::logInfo) << "==> All (fully scaled) " << ret; }
949 :
950 796968 : data.evalDone = true;
951 : // apply variante scoring if requiered
952 796968 : const ScoreType hceScore = variantScore(ret, p.halfmoves, context.height_, p.c);
953 :
954 : #ifdef WITH_NNUE
955 796968 : if ( DynamicConfig::useNNUE && !forbiddenNNUE && Abs(hceScore) <= DynamicConfig::NNUEThreshold2 ){
956 : // if HCE is small (there is something more than just material value going on ...), fall back to NNUE;
957 5476 : const ScoreType nnueScore = NNUEEVal(p, data, context, features, true);
958 : STOP_AND_SUM_TIMER(Eval)
959 5476 : return nnueScore;
960 : }
961 : #endif
962 :
963 : STOP_AND_SUM_TIMER(Eval)
964 : return hceScore;
965 : }
966 :
967 : #pragma GCC diagnostic pop
|