@@ -19,7 +19,9 @@ std::pair<bool, Value> checkGameStatus(Position& board) {
1919 ) {
2020 return {true , DRAW_VALUE};
2121 }
22- if (board.isRepetition ()) { return {true , DRAW_VALUE}; }
22+ if (board.isRepetition ()) {
23+ return {true , DRAW_VALUE};
24+ }
2325 // Game is not over
2426 return {false , 0 };
2527}
@@ -76,7 +78,9 @@ class Evaluator {
7678 {TYPE_PAWN, TYPE_KNIGHT, TYPE_BISHOP, TYPE_ROOK, TYPE_QUEEN, TYPE_KING}) {
7779 Bitboard bb = pos.pieces (pt, C);
7880 // Count non pawn material
79- if (pt != TYPE_PAWN) { cache.nonPawnMaterial [C] += PIECE_VALUE[pt] * bb.count (); }
81+ if (pt != TYPE_PAWN) {
82+ cache.nonPawnMaterial [C] += PIECE_VALUE[pt] * bb.count ();
83+ }
8084 // Count piece square table score
8185 while (bb) {
8286 Square sq = bb.pop ();
@@ -149,6 +153,14 @@ class Evaluator {
149153 bool supported = (bool ) (cache.attackedBy [C][Pawn] & sqbb);
150154 total += OUTPOST_BONUS[PT == Bishop][supported];
151155 }
156+ // Penalty for being far away from king
157+ int kingDistance = 0 ;
158+ if (PT == Knight) {
159+ kingDistance = chess::dist::knight (sq, pos.kingSq (C));
160+ } else {
161+ kingDistance = chess::dist::chebyshev (sq, pos.kingSq (C));
162+ }
163+ total -= KING_DISTANCE_PENALTY * kingDistance;
152164
153165 // todo this hurts performance too
154166 // if (PT == Bishop) {
@@ -171,22 +183,23 @@ class Evaluator {
171183 total += OPEN_ROOK_BONUS[0 ];
172184 }
173185 }
174- // todo this drags down performance, investigate some time
186+
187+ // // todo this drags down performance, investigate some time
175188 // // Penalty for being trapped by the king, and even more
176189 // // if the king cannot castle
177190 // if (attackMap.count() <= 3) {
178191 // chess::File f = sq.file();
179192 // chess::File kingFile = pos.kingSq(C).file();
180- // if ((f > chess::File::FILE_E && kingFile >= chess::File::FILE_E) ||
181- // (f < chess::File::FILE_D && kingFile <= chess::File::FILE_D)) {
193+ // if ((kingFile < chess::File::FILE_E) == (f < kingFile)) {
182194 // total -= TRAPPED_ROOK_PENALTY;
183- // if (!pos.castlingRights().has(C)) { total -= TRAPPED_ROOK_PENALTY; }
184195 // }
185196 // }
186197 }
187198 if (PT == Queen) {
188199 Bitboard potentialPinners = pos.pieces (TYPE_ROOK, _C) | pos.pieces (TYPE_BISHOP, _C);
189- if ((bool ) (attackMap & potentialPinners)) { total -= WEAK_QUEEN_PENALTY; }
200+ if ((bool ) (attackMap & potentialPinners)) {
201+ total -= WEAK_QUEEN_PENALTY;
202+ }
190203 }
191204 }
192205
@@ -223,12 +236,18 @@ class Evaluator {
223236 const Bitboard doubled =
224237 ours & (C == White ? (sq.rank ().bb () << 8 ) : (sq.rank ().bb () >> 8 ));
225238 // Punish isolated pawns
226- if (!neighbors) { total -= ISOLATED_PAWN_PENALTY; }
239+ if (!neighbors) {
240+ total -= ISOLATED_PAWN_PENALTY;
241+ }
227242 // Punish doubled and unspported pawns
228- if (doubled && !supported) { total -= DOUBLED_PAWN_PENALTY; }
243+ if (doubled && !supported) {
244+ total -= DOUBLED_PAWN_PENALTY;
245+ }
229246
230247 // Cache passed pawns so we can evaluate them later
231- if (!stoppers && !doubled) { cache.passedPawns [C].set (sq.index ()); }
248+ if (!stoppers && !doubled) {
249+ cache.passedPawns [C].set (sq.index ());
250+ }
232251 }
233252
234253 return total;
@@ -248,6 +267,19 @@ class Evaluator {
248267 return total;
249268 }
250269
270+ /* *
271+ * Space advantage
272+ */
273+ template <EvalColor C> Score _space () {
274+ if ((int ) (cache.nonPawnMaterial [White] + cache.nonPawnMaterial [Black]).mg < 6100 ) {
275+ return S (0 , 0 );
276+ }
277+ const Bitboard squares =
278+ SPACE_FOR_MINORS[C] & (~cache.attackedBy [_C][Pawn]) & ~(pos.pieces (TYPE_PAWN, C));
279+ const int weight = pos.us (C).count () - pos.countPieces (TYPE_PAWN, C) - 1 ;
280+ return SPACE_BONUS * (weight * weight * squares.count () / 16 );
281+ }
282+
251283 /* *
252284 * Estimate game phase from material. This must be called after
253285 * `_check_material`.
@@ -260,6 +292,25 @@ class Evaluator {
260292 return ((m - ENDGAME_LIMIT) * ALL_GAME_PHASES) / (MIDGAME_LIMIT - ENDGAME_LIMIT);
261293 }
262294
295+ /* *
296+ * Scales down endgame evaluation with simple heuristics.
297+ */
298+ int _endgameScaler () {
299+ // Gather basic information
300+ int wp = pos.countPieces (TYPE_PAWN, WHITE), bp = pos.countPieces (TYPE_PAWN, BLACK);
301+ int wb = pos.countPieces (TYPE_BISHOP, WHITE), bb = pos.countPieces (TYPE_BISHOP, BLACK);
302+
303+ Score npm = cache.nonPawnMaterial [White] + cache.nonPawnMaterial [Black];
304+ if (wb == 1 && bb == 1 && ((int ) npm.eg <= 720 )) {
305+ // Check opposite colors
306+ Square w = Square (pos.pieces (TYPE_BISHOP, WHITE).lsb ());
307+ Square b = Square (pos.pieces (TYPE_BISHOP, BLACK).lsb ());
308+ if (!Square::same_color (w, b))
309+ return std::min (std::max (std::abs (wp - bp) - 2 , 0 ) * 14 , 64 );
310+ }
311+ return 64 ;
312+ }
313+
263314public:
264315 Value operator ()() {
265316 // First, check material and cache the results
@@ -278,12 +329,14 @@ class Evaluator {
278329 total += _piece<White, Rook>() - _piece<Black, Rook>();
279330 total += _piece<White, Queen>() - _piece<Black, Queen>();
280331 total += _pawns<White>() - _pawns<Black>();
332+ // total += _space<White>() - _space<Black>(); // todo this doesn't help
281333 total += _passed<White>() - _passed<Black>();
282334
283335 total += _king<White>() - _king<Black>();
284336
285- int phase = _phase ();
286- Value v = total.fuse (phase);
337+ int phase = _phase ();
338+ total.eg = (int ) (total.eg ) * _endgameScaler () / 64 ;
339+ Value v = total.fuse (phase);
287340
288341 if (pos.sideToMove () == BLACK) {
289342 v = ~v; // flip the score so it is always POV
0 commit comments