endif
### Source and object files
-SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \
- material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \
+SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \
+ misc.cpp movegen.cpp movepick.cpp position.cpp psqt.cpp \
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
nnue/evaluate_nnue.cpp nnue/features/half_ka_v2_hm.cpp
list.emplace_back("setoption name Hash value " + ttSize);
list.emplace_back("ucinewgame");
- size_t posCounter = 0;
-
for (const string& fen : fens)
if (fen.find("setoption") != string::npos)
list.emplace_back(fen);
else
{
- if (evalType == "classical" || (evalType == "mixed" && posCounter % 2 == 0))
- list.emplace_back("setoption name Use NNUE value false");
- else if (evalType == "NNUE" || (evalType == "mixed" && posCounter % 2 != 0))
- list.emplace_back("setoption name Use NNUE value true");
list.emplace_back("position fen " + fen);
list.emplace_back(go);
- ++posCounter;
}
- list.emplace_back("setoption name Use NNUE value true");
-
return list;
}
+++ /dev/null
-/*
- Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
-
- Stockfish is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Stockfish is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <cassert>
-#include <vector>
-#include <bitset>
-
-#include "bitboard.h"
-#include "types.h"
-
-namespace Stockfish {
-
-namespace {
-
- // There are 24 possible pawn squares: files A to D and ranks from 2 to 7.
- // Positions with the pawn on files E to H will be mirrored before probing.
- constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608
-
- std::bitset<MAX_INDEX> KPKBitbase;
-
- // A KPK bitbase index is an integer in [0, IndexMax] range
- //
- // Information is mapped in a way that minimizes the number of iterations:
- //
- // bit 0- 5: white king square (from SQ_A1 to SQ_H8)
- // bit 6-11: black king square (from SQ_A1 to SQ_H8)
- // bit 12: side to move (WHITE or BLACK)
- // bit 13-14: white pawn file (from FILE_A to FILE_D)
- // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2)
- unsigned index(Color stm, Square bksq, Square wksq, Square psq) {
- return int(wksq) | (bksq << 6) | (stm << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15);
- }
-
- enum Result {
- INVALID = 0,
- UNKNOWN = 1,
- DRAW = 2,
- WIN = 4
- };
-
- Result& operator|=(Result& r, Result v) { return r = Result(r | v); }
-
- struct KPKPosition {
- KPKPosition() = default;
- explicit KPKPosition(unsigned idx);
- operator Result() const { return result; }
- Result classify(const std::vector<KPKPosition>& db);
-
- Color stm;
- Square ksq[COLOR_NB], psq;
- Result result;
- };
-
-} // namespace
-
-bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) {
-
- assert(file_of(wpsq) <= FILE_D);
-
- return KPKBitbase[index(stm, bksq, wksq, wpsq)];
-}
-
-
-void Bitbases::init() {
-
- std::vector<KPKPosition> db(MAX_INDEX);
- unsigned idx, repeat = 1;
-
- // Initialize db with known win / draw positions
- for (idx = 0; idx < MAX_INDEX; ++idx)
- db[idx] = KPKPosition(idx);
-
- // Iterate through the positions until none of the unknown positions can be
- // changed to either wins or draws (15 cycles needed).
- while (repeat)
- for (repeat = idx = 0; idx < MAX_INDEX; ++idx)
- repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN);
-
- // Fill the bitbase with the decisive results
- for (idx = 0; idx < MAX_INDEX; ++idx)
- if (db[idx] == WIN)
- KPKBitbase.set(idx);
-}
-
-namespace {
-
- KPKPosition::KPKPosition(unsigned idx) {
-
- ksq[WHITE] = Square((idx >> 0) & 0x3F);
- ksq[BLACK] = Square((idx >> 6) & 0x3F);
- stm = Color ((idx >> 12) & 0x01);
- psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7)));
-
- // Invalid if two pieces are on the same square or if a king can be captured
- if ( distance(ksq[WHITE], ksq[BLACK]) <= 1
- || ksq[WHITE] == psq
- || ksq[BLACK] == psq
- || (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK])))
- result = INVALID;
-
- // Win if the pawn can be promoted without getting captured
- else if ( stm == WHITE
- && rank_of(psq) == RANK_7
- && ksq[WHITE] != psq + NORTH
- && ( distance(ksq[BLACK], psq + NORTH) > 1
- || (distance(ksq[WHITE], psq + NORTH) == 1)))
- result = WIN;
-
- // Draw if it is stalemate or the black king can capture the pawn
- else if ( stm == BLACK
- && ( !(attacks_bb<KING>(ksq[BLACK]) & ~(attacks_bb<KING>(ksq[WHITE]) | pawn_attacks_bb(WHITE, psq)))
- || (attacks_bb<KING>(ksq[BLACK]) & ~attacks_bb<KING>(ksq[WHITE]) & psq)))
- result = DRAW;
-
- // Position will be classified later
- else
- result = UNKNOWN;
- }
-
- Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
-
- // White to move: If one move leads to a position classified as WIN, the result
- // of the current position is WIN. If all moves lead to positions classified
- // as DRAW, the current position is classified as DRAW, otherwise the current
- // position is classified as UNKNOWN.
- //
- // Black to move: If one move leads to a position classified as DRAW, the result
- // of the current position is DRAW. If all moves lead to positions classified
- // as WIN, the position is classified as WIN, otherwise the current position is
- // classified as UNKNOWN.
- const Result Good = (stm == WHITE ? WIN : DRAW);
- const Result Bad = (stm == WHITE ? DRAW : WIN);
-
- Result r = INVALID;
- Bitboard b = attacks_bb<KING>(ksq[stm]);
-
- while (b)
- r |= stm == WHITE ? db[index(BLACK, ksq[BLACK], pop_lsb(b), psq)]
- : db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)];
-
- if (stm == WHITE)
- {
- if (rank_of(psq) < RANK_7) // Single push
- r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH)];
-
- if ( rank_of(psq) == RANK_2 // Double push
- && psq + NORTH != ksq[WHITE]
- && psq + NORTH != ksq[BLACK])
- r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH + NORTH)];
- }
-
- return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad;
- }
-
-} // namespace
-
-} // namespace Stockfish
namespace Stockfish {
-namespace Bitbases {
-
-void init();
-bool probe(Square wksq, Square wpsq, Square bksq, Color us);
-
-} // namespace Stockfish::Bitbases
-
namespace Bitboards {
void init();
+++ /dev/null
-/*
- Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
-
- Stockfish is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Stockfish is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <cassert>
-
-#include "bitboard.h"
-#include "endgame.h"
-#include "movegen.h"
-
-namespace Stockfish {
-
-namespace {
-
- // Used to drive the king towards the edge of the board
- // in KX vs K and KQ vs KR endgames.
- // Values range from 27 (center squares) to 90 (in the corners)
- inline int push_to_edge(Square s) {
- int rd = edge_distance(rank_of(s)), fd = edge_distance(file_of(s));
- return 90 - (7 * fd * fd / 2 + 7 * rd * rd / 2);
- }
-
- // Used to drive the king towards A1H8 corners in KBN vs K endgames.
- // Values range from 0 on A8H1 diagonal to 7 in A1H8 corners
- inline int push_to_corner(Square s) {
- return abs(7 - rank_of(s) - file_of(s));
- }
-
- // Drive a piece close to or away from another piece
- inline int push_close(Square s1, Square s2) { return 140 - 20 * distance(s1, s2); }
- inline int push_away(Square s1, Square s2) { return 120 - push_close(s1, s2); }
-
-#ifndef NDEBUG
- bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) {
- return pos.non_pawn_material(c) == npm && pos.count<PAWN>(c) == pawnsCnt;
- }
-#endif
-
- // Map the square as if strongSide is white and strongSide's only pawn
- // is on the left half of the board.
- Square normalize(const Position& pos, Color strongSide, Square sq) {
-
- assert(pos.count<PAWN>(strongSide) == 1);
-
- if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E)
- sq = flip_file(sq);
-
- return strongSide == WHITE ? sq : flip_rank(sq);
- }
-
-} // namespace
-
-
-namespace Endgames {
-
- std::pair<Map<Value>, Map<ScaleFactor>> maps;
-
- void init() {
-
- add<KPK>("KPK");
- add<KNNK>("KNNK");
- add<KBNK>("KBNK");
- add<KRKP>("KRKP");
- add<KRKB>("KRKB");
- add<KRKN>("KRKN");
- add<KQKP>("KQKP");
- add<KQKR>("KQKR");
- add<KNNKP>("KNNKP");
-
- add<KRPKR>("KRPKR");
- add<KRPKB>("KRPKB");
- add<KBPKB>("KBPKB");
- add<KBPKN>("KBPKN");
- add<KBPPKB>("KBPPKB");
- add<KRPPKRP>("KRPPKRP");
- }
-}
-
-
-/// Mate with KX vs K. This function is used to evaluate positions with
-/// king and plenty of material vs a lone king. It simply gives the
-/// attacking side a bonus for driving the defending king towards the edge
-/// of the board, and for keeping the distance between the two kings small.
-template<>
-Value Endgame<KXK>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
- assert(!pos.checkers()); // Eval is never called when in check
-
- // Stalemate detection with lone king
- if (pos.side_to_move() == weakSide && !MoveList<LEGAL>(pos).size())
- return VALUE_DRAW;
-
- Square strongKing = pos.square<KING>(strongSide);
- Square weakKing = pos.square<KING>(weakSide);
-
- Value result = pos.non_pawn_material(strongSide)
- + pos.count<PAWN>(strongSide) * PawnValueEg
- + push_to_edge(weakKing)
- + push_close(strongKing, weakKing);
-
- if ( pos.count<QUEEN>(strongSide)
- || pos.count<ROOK>(strongSide)
- ||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide))
- || ( (pos.pieces(strongSide, BISHOP) & ~DarkSquares)
- && (pos.pieces(strongSide, BISHOP) & DarkSquares)))
- result = std::min(result + VALUE_KNOWN_WIN, VALUE_TB_WIN_IN_MAX_PLY - 1);
-
- return strongSide == pos.side_to_move() ? result : -result;
-}
-
-
-/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
-/// defending king towards a corner square that our bishop attacks.
-template<>
-Value Endgame<KBNK>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0));
- assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
-
- Square strongKing = pos.square<KING>(strongSide);
- Square strongBishop = pos.square<BISHOP>(strongSide);
- Square weakKing = pos.square<KING>(weakSide);
-
- // If our bishop does not attack A1/H8, we flip the enemy king square
- // to drive to opposite corners (A8/H1).
-
- Value result = (VALUE_KNOWN_WIN + 3520)
- + push_close(strongKing, weakKing)
- + 420 * push_to_corner(opposite_colors(strongBishop, SQ_A1) ? flip_file(weakKing) : weakKing);
-
- assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY);
- return strongSide == pos.side_to_move() ? result : -result;
-}
-
-
-/// KP vs K. This endgame is evaluated with the help of a bitbase
-template<>
-Value Endgame<KPK>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, VALUE_ZERO, 1));
- assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
-
- // Assume strongSide is white and the pawn is on files A-D
- Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
- Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
- Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
-
- Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
-
- if (!Bitbases::probe(strongKing, strongPawn, weakKing, us))
- return VALUE_DRAW;
-
- Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(strongPawn));
-
- return strongSide == pos.side_to_move() ? result : -result;
-}
-
-
-/// KR vs KP. This is a somewhat tricky endgame to evaluate precisely without
-/// a bitbase. The function below returns drawish scores when the pawn is
-/// far advanced with support of the king, while the attacking king is far
-/// away.
-template<>
-Value Endgame<KRKP>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, RookValueMg, 0));
- assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
-
- Square strongKing = pos.square<KING>(strongSide);
- Square weakKing = pos.square<KING>(weakSide);
- Square strongRook = pos.square<ROOK>(strongSide);
- Square weakPawn = pos.square<PAWN>(weakSide);
- Square queeningSquare = make_square(file_of(weakPawn), relative_rank(weakSide, RANK_8));
- Value result;
-
- // If the stronger side's king is in front of the pawn, it's a win
- if (forward_file_bb(strongSide, strongKing) & weakPawn)
- result = RookValueEg - distance(strongKing, weakPawn);
-
- // If the weaker side's king is too far from the pawn and the rook,
- // it's a win.
- else if ( distance(weakKing, weakPawn) >= 3 + (pos.side_to_move() == weakSide)
- && distance(weakKing, strongRook) >= 3)
- result = RookValueEg - distance(strongKing, weakPawn);
-
- // If the pawn is far advanced and supported by the defending king,
- // the position is drawish
- else if ( relative_rank(strongSide, weakKing) <= RANK_3
- && distance(weakKing, weakPawn) == 1
- && relative_rank(strongSide, strongKing) >= RANK_4
- && distance(strongKing, weakPawn) > 2 + (pos.side_to_move() == strongSide))
- result = Value(80) - 8 * distance(strongKing, weakPawn);
-
- else
- result = Value(200) - 8 * ( distance(strongKing, weakPawn + pawn_push(weakSide))
- - distance(weakKing, weakPawn + pawn_push(weakSide))
- - distance(weakPawn, queeningSquare));
-
- return strongSide == pos.side_to_move() ? result : -result;
-}
-
-
-/// KR vs KB. This is very simple, and always returns drawish scores. The
-/// score is slightly bigger when the defending king is close to the edge.
-template<>
-Value Endgame<KRKB>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, RookValueMg, 0));
- assert(verify_material(pos, weakSide, BishopValueMg, 0));
-
- Value result = Value(push_to_edge(pos.square<KING>(weakSide)));
- return strongSide == pos.side_to_move() ? result : -result;
-}
-
-
-/// KR vs KN. The attacking side has slightly better winning chances than
-/// in KR vs KB, particularly if the king and the knight are far apart.
-template<>
-Value Endgame<KRKN>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, RookValueMg, 0));
- assert(verify_material(pos, weakSide, KnightValueMg, 0));
-
- Square weakKing = pos.square<KING>(weakSide);
- Square weakKnight = pos.square<KNIGHT>(weakSide);
- Value result = Value(push_to_edge(weakKing) + push_away(weakKing, weakKnight));
- return strongSide == pos.side_to_move() ? result : -result;
-}
-
-
-/// KQ vs KP. In general, this is a win for the stronger side, but there are a
-/// few important exceptions. A pawn on 7th rank and on the A,C,F or H files
-/// with a king positioned next to it can be a draw, so in that case, we only
-/// use the distance between the kings.
-template<>
-Value Endgame<KQKP>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, QueenValueMg, 0));
- assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
-
- Square strongKing = pos.square<KING>(strongSide);
- Square weakKing = pos.square<KING>(weakSide);
- Square weakPawn = pos.square<PAWN>(weakSide);
-
- Value result = Value(push_close(strongKing, weakKing));
-
- if ( relative_rank(weakSide, weakPawn) != RANK_7
- || distance(weakKing, weakPawn) != 1
- || ((FileBBB | FileDBB | FileEBB | FileGBB) & weakPawn))
- result += QueenValueEg - PawnValueEg;
-
- return strongSide == pos.side_to_move() ? result : -result;
-}
-
-
-/// KQ vs KR. This is almost identical to KX vs K: we give the attacking
-/// king a bonus for having the kings close together, and for forcing the
-/// defending king towards the edge. If we also take care to avoid null move for
-/// the defending side in the search, this is usually sufficient to win KQ vs KR.
-template<>
-Value Endgame<KQKR>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, QueenValueMg, 0));
- assert(verify_material(pos, weakSide, RookValueMg, 0));
-
- Square strongKing = pos.square<KING>(strongSide);
- Square weakKing = pos.square<KING>(weakSide);
-
- Value result = QueenValueEg
- - RookValueEg
- + push_to_edge(weakKing)
- + push_close(strongKing, weakKing);
-
- return strongSide == pos.side_to_move() ? result : -result;
-}
-
-
-/// KNN vs KP. Very drawish, but there are some mate opportunities if we can
-/// press the weakSide King to a corner before the pawn advances too much.
-template<>
-Value Endgame<KNNKP>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0));
- assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
-
- Square weakKing = pos.square<KING>(weakSide);
- Square weakPawn = pos.square<PAWN>(weakSide);
-
- Value result = PawnValueEg
- + 2 * push_to_edge(weakKing)
- - 10 * relative_rank(weakSide, weakPawn);
-
- return strongSide == pos.side_to_move() ? result : -result;
-}
-
-
-/// Some cases of trivial draws
-template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; }
-
-
-/// KB and one or more pawns vs K. It checks for draws with rook pawns and
-/// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW
-/// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
-/// will be used.
-template<>
-ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
-
- assert(pos.non_pawn_material(strongSide) == BishopValueMg);
- assert(pos.count<PAWN>(strongSide) >= 1);
-
- // No assertions about the material of weakSide, because we want draws to
- // be detected even when the weaker side has some pawns.
-
- Bitboard strongPawns = pos.pieces(strongSide, PAWN);
- Bitboard allPawns = pos.pieces(PAWN);
-
- Square strongBishop = pos.square<BISHOP>(strongSide);
- Square weakKing = pos.square<KING>(weakSide);
- Square strongKing = pos.square<KING>(strongSide);
-
- // All strongSide pawns are on a single rook file?
- if (!(strongPawns & ~FileABB) || !(strongPawns & ~FileHBB))
- {
- Square queeningSquare = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8));
-
- if ( opposite_colors(queeningSquare, strongBishop)
- && distance(queeningSquare, weakKing) <= 1)
- return SCALE_FACTOR_DRAW;
- }
-
- // If all the pawns are on the same B or G file, then it's potentially a draw
- if ((!(allPawns & ~FileBBB) || !(allPawns & ~FileGBB))
- && pos.non_pawn_material(weakSide) == 0
- && pos.count<PAWN>(weakSide) >= 1)
- {
- // Get the least advanced weakSide pawn
- Square weakPawn = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN));
-
- // There's potential for a draw if our pawn is blocked on the 7th rank,
- // the bishop cannot attack it or they only have one pawn left.
- if ( relative_rank(strongSide, weakPawn) == RANK_7
- && (strongPawns & (weakPawn + pawn_push(weakSide)))
- && (opposite_colors(strongBishop, weakPawn) || !more_than_one(strongPawns)))
- {
- int strongKingDist = distance(weakPawn, strongKing);
- int weakKingDist = distance(weakPawn, weakKing);
-
- // It's a draw if the weak king is on its back two ranks, within 2
- // squares of the blocking pawn and the strong king is not
- // closer. (I think this rule only fails in practically
- // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w
- // and positions where qsearch will immediately correct the
- // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w).
- if ( relative_rank(strongSide, weakKing) >= RANK_7
- && weakKingDist <= 2
- && weakKingDist <= strongKingDist)
- return SCALE_FACTOR_DRAW;
- }
- }
-
- return SCALE_FACTOR_NONE;
-}
-
-
-/// KQ vs KR and one or more pawns. It tests for fortress draws with a rook on
-/// the third rank defended by a pawn.
-template<>
-ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, QueenValueMg, 0));
- assert(pos.count<ROOK>(weakSide) == 1);
- assert(pos.count<PAWN>(weakSide) >= 1);
-
- Square strongKing = pos.square<KING>(strongSide);
- Square weakKing = pos.square<KING>(weakSide);
- Square weakRook = pos.square<ROOK>(weakSide);
-
- if ( relative_rank(weakSide, weakKing) <= RANK_2
- && relative_rank(weakSide, strongKing) >= RANK_4
- && relative_rank(weakSide, weakRook) == RANK_3
- && ( pos.pieces(weakSide, PAWN)
- & attacks_bb<KING>(weakKing)
- & pawn_attacks_bb(strongSide, weakRook)))
- return SCALE_FACTOR_DRAW;
-
- return SCALE_FACTOR_NONE;
-}
-
-
-/// KRP vs KR. This function knows a handful of the most important classes of
-/// drawn positions, but is far from perfect. It would probably be a good idea
-/// to add more knowledge in the future.
-///
-/// It would also be nice to rewrite the actual code for this function,
-/// which is mostly copied from Glaurung 1.x, and isn't very pretty.
-template<>
-ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, RookValueMg, 1));
- assert(verify_material(pos, weakSide, RookValueMg, 0));
-
- // Assume strongSide is white and the pawn is on files A-D
- Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
- Square strongRook = normalize(pos, strongSide, pos.square<ROOK>(strongSide));
- Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
- Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
- Square weakRook = normalize(pos, strongSide, pos.square<ROOK>(weakSide));
-
- File pawnFile = file_of(strongPawn);
- Rank pawnRank = rank_of(strongPawn);
- Square queeningSquare = make_square(pawnFile, RANK_8);
- int tempo = (pos.side_to_move() == strongSide);
-
- // If the pawn is not too far advanced and the defending king defends the
- // queening square, use the third-rank defence.
- if ( pawnRank <= RANK_5
- && distance(weakKing, queeningSquare) <= 1
- && strongKing <= SQ_H5
- && (rank_of(weakRook) == RANK_6 || (pawnRank <= RANK_3 && rank_of(strongRook) != RANK_6)))
- return SCALE_FACTOR_DRAW;
-
- // The defending side saves a draw by checking from behind in case the pawn
- // has advanced to the 6th rank with the king behind.
- if ( pawnRank == RANK_6
- && distance(weakKing, queeningSquare) <= 1
- && rank_of(strongKing) + tempo <= RANK_6
- && (rank_of(weakRook) == RANK_1 || (!tempo && distance<File>(weakRook, strongPawn) >= 3)))
- return SCALE_FACTOR_DRAW;
-
- if ( pawnRank >= RANK_6
- && weakKing == queeningSquare
- && rank_of(weakRook) == RANK_1
- && (!tempo || distance(strongKing, strongPawn) >= 2))
- return SCALE_FACTOR_DRAW;
-
- // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7
- // and the black rook is behind the pawn.
- if ( strongPawn == SQ_A7
- && strongRook == SQ_A8
- && (weakKing == SQ_H7 || weakKing == SQ_G7)
- && file_of(weakRook) == FILE_A
- && (rank_of(weakRook) <= RANK_3 || file_of(strongKing) >= FILE_D || rank_of(strongKing) <= RANK_5))
- return SCALE_FACTOR_DRAW;
-
- // If the defending king blocks the pawn and the attacking king is too far
- // away, it's a draw.
- if ( pawnRank <= RANK_5
- && weakKing == strongPawn + NORTH
- && distance(strongKing, strongPawn) - tempo >= 2
- && distance(strongKing, weakRook) - tempo >= 2)
- return SCALE_FACTOR_DRAW;
-
- // Pawn on the 7th rank supported by the rook from behind usually wins if the
- // attacking king is closer to the queening square than the defending king,
- // and the defending king cannot gain tempi by threatening the attacking rook.
- if ( pawnRank == RANK_7
- && pawnFile != FILE_A
- && file_of(strongRook) == pawnFile
- && strongRook != queeningSquare
- && (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo)
- && (distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo))
- return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(strongKing, queeningSquare));
-
- // Similar to the above, but with the pawn further back
- if ( pawnFile != FILE_A
- && file_of(strongRook) == pawnFile
- && strongRook < strongPawn
- && (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo)
- && (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn + NORTH) - 2 + tempo)
- && ( distance(weakKing, strongRook) + tempo >= 3
- || ( distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo
- && (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn) + tempo))))
- return ScaleFactor( SCALE_FACTOR_MAX
- - 8 * distance(strongPawn, queeningSquare)
- - 2 * distance(strongKing, queeningSquare));
-
- // If the pawn is not far advanced and the defending king is somewhere in
- // the pawn's path, it's probably a draw.
- if (pawnRank <= RANK_4 && weakKing > strongPawn)
- {
- if (file_of(weakKing) == file_of(strongPawn))
- return ScaleFactor(10);
- if ( distance<File>(weakKing, strongPawn) == 1
- && distance(strongKing, weakKing) > 2)
- return ScaleFactor(24 - 2 * distance(strongKing, weakKing));
- }
- return SCALE_FACTOR_NONE;
-}
-
-template<>
-ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, RookValueMg, 1));
- assert(verify_material(pos, weakSide, BishopValueMg, 0));
-
- // Test for a rook pawn
- if (pos.pieces(PAWN) & (FileABB | FileHBB))
- {
- Square weakKing = pos.square<KING>(weakSide);
- Square weakBishop = pos.square<BISHOP>(weakSide);
- Square strongKing = pos.square<KING>(strongSide);
- Square strongPawn = pos.square<PAWN>(strongSide);
- Rank pawnRank = relative_rank(strongSide, strongPawn);
- Direction push = pawn_push(strongSide);
-
- // If the pawn is on the 5th rank and the pawn (currently) is on
- // the same color square as the bishop then there is a chance of
- // a fortress. Depending on the king position give a moderate
- // reduction or a stronger one if the defending king is near the
- // corner but not trapped there.
- if (pawnRank == RANK_5 && !opposite_colors(weakBishop, strongPawn))
- {
- int d = distance(strongPawn + 3 * push, weakKing);
-
- if (d <= 2 && !(d == 0 && weakKing == strongKing + 2 * push))
- return ScaleFactor(24);
- else
- return ScaleFactor(48);
- }
-
- // When the pawn has moved to the 6th rank we can be fairly sure
- // it's drawn if the bishop attacks the square in front of the
- // pawn from a reasonable distance and the defending king is near
- // the corner
- if ( pawnRank == RANK_6
- && distance(strongPawn + 2 * push, weakKing) <= 1
- && (attacks_bb<BISHOP>(weakBishop) & (strongPawn + push))
- && distance<File>(weakBishop, strongPawn) >= 2)
- return ScaleFactor(8);
- }
-
- return SCALE_FACTOR_NONE;
-}
-
-/// KRPP vs KRP. There is just a single rule: if the stronger side has no passed
-/// pawns and the defending king is actively placed, the position is drawish.
-template<>
-ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, RookValueMg, 2));
- assert(verify_material(pos, weakSide, RookValueMg, 1));
-
- Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN));
- Square strongPawn2 = msb(pos.pieces(strongSide, PAWN));
- Square weakKing = pos.square<KING>(weakSide);
-
- // Does the stronger side have a passed pawn?
- if (pos.pawn_passed(strongSide, strongPawn1) || pos.pawn_passed(strongSide, strongPawn2))
- return SCALE_FACTOR_NONE;
-
- Rank pawnRank = std::max(relative_rank(strongSide, strongPawn1), relative_rank(strongSide, strongPawn2));
-
- if ( distance<File>(weakKing, strongPawn1) <= 1
- && distance<File>(weakKing, strongPawn2) <= 1
- && relative_rank(strongSide, weakKing) > pawnRank)
- {
- assert(pawnRank > RANK_1 && pawnRank < RANK_7);
- return ScaleFactor(7 * pawnRank);
- }
- return SCALE_FACTOR_NONE;
-}
-
-
-/// K and two or more pawns vs K. There is just a single rule here: if all pawns
-/// are on the same rook file and are blocked by the defending king, it's a draw.
-template<>
-ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
-
- assert(pos.non_pawn_material(strongSide) == VALUE_ZERO);
- assert(pos.count<PAWN>(strongSide) >= 2);
- assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
-
- Square weakKing = pos.square<KING>(weakSide);
- Bitboard strongPawns = pos.pieces(strongSide, PAWN);
-
- // If all pawns are ahead of the king on a single rook file, it's a draw.
- if ( !(strongPawns & ~(FileABB | FileHBB))
- && !(strongPawns & ~passed_pawn_span(weakSide, weakKing)))
- return SCALE_FACTOR_DRAW;
-
- return SCALE_FACTOR_NONE;
-}
-
-
-/// KBP vs KB. There are two rules: if the defending king is somewhere along the
-/// path of the pawn, and the square of the king is not of the same color as the
-/// stronger side's bishop, it's a draw. If the two bishops have opposite color,
-/// it's almost always a draw.
-template<>
-ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, BishopValueMg, 1));
- assert(verify_material(pos, weakSide, BishopValueMg, 0));
-
- Square strongPawn = pos.square<PAWN>(strongSide);
- Square strongBishop = pos.square<BISHOP>(strongSide);
- Square weakBishop = pos.square<BISHOP>(weakSide);
- Square weakKing = pos.square<KING>(weakSide);
-
- // Case 1: Defending king blocks the pawn, and cannot be driven away
- if ( (forward_file_bb(strongSide, strongPawn) & weakKing)
- && ( opposite_colors(weakKing, strongBishop)
- || relative_rank(strongSide, weakKing) <= RANK_6))
- return SCALE_FACTOR_DRAW;
-
- // Case 2: Opposite colored bishops
- if (opposite_colors(strongBishop, weakBishop))
- return SCALE_FACTOR_DRAW;
-
- return SCALE_FACTOR_NONE;
-}
-
-
-/// KBPP vs KB. It detects a few basic draws with opposite-colored bishops
-template<>
-ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, BishopValueMg, 2));
- assert(verify_material(pos, weakSide, BishopValueMg, 0));
-
- Square strongBishop = pos.square<BISHOP>(strongSide);
- Square weakBishop = pos.square<BISHOP>(weakSide);
-
- if (!opposite_colors(strongBishop, weakBishop))
- return SCALE_FACTOR_NONE;
-
- Square weakKing = pos.square<KING>(weakSide);
- Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN));
- Square strongPawn2 = msb(pos.pieces(strongSide, PAWN));
- Square blockSq1, blockSq2;
-
- if (relative_rank(strongSide, strongPawn1) > relative_rank(strongSide, strongPawn2))
- {
- blockSq1 = strongPawn1 + pawn_push(strongSide);
- blockSq2 = make_square(file_of(strongPawn2), rank_of(strongPawn1));
- }
- else
- {
- blockSq1 = strongPawn2 + pawn_push(strongSide);
- blockSq2 = make_square(file_of(strongPawn1), rank_of(strongPawn2));
- }
-
- switch (distance<File>(strongPawn1, strongPawn2))
- {
- case 0:
- // Both pawns are on the same file. It's an easy draw if the defender firmly
- // controls some square in the frontmost pawn's path.
- if ( file_of(weakKing) == file_of(blockSq1)
- && relative_rank(strongSide, weakKing) >= relative_rank(strongSide, blockSq1)
- && opposite_colors(weakKing, strongBishop))
- return SCALE_FACTOR_DRAW;
- else
- return SCALE_FACTOR_NONE;
-
- case 1:
- // Pawns on adjacent files. It's a draw if the defender firmly controls the
- // square in front of the frontmost pawn's path, and the square diagonally
- // behind this square on the file of the other pawn.
- if ( weakKing == blockSq1
- && opposite_colors(weakKing, strongBishop)
- && ( weakBishop == blockSq2
- || (attacks_bb<BISHOP>(blockSq2, pos.pieces()) & pos.pieces(weakSide, BISHOP))
- || distance<Rank>(strongPawn1, strongPawn2) >= 2))
- return SCALE_FACTOR_DRAW;
-
- else if ( weakKing == blockSq2
- && opposite_colors(weakKing, strongBishop)
- && ( weakBishop == blockSq1
- || (attacks_bb<BISHOP>(blockSq1, pos.pieces()) & pos.pieces(weakSide, BISHOP))))
- return SCALE_FACTOR_DRAW;
- else
- return SCALE_FACTOR_NONE;
-
- default:
- // The pawns are not on the same file or adjacent files. No scaling.
- return SCALE_FACTOR_NONE;
- }
-}
-
-
-/// KBP vs KN. There is a single rule: if the defending king is somewhere along
-/// the path of the pawn, and the square of the king is not of the same color as
-/// the stronger side's bishop, it's a draw.
-template<>
-ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, BishopValueMg, 1));
- assert(verify_material(pos, weakSide, KnightValueMg, 0));
-
- Square strongPawn = pos.square<PAWN>(strongSide);
- Square strongBishop = pos.square<BISHOP>(strongSide);
- Square weakKing = pos.square<KING>(weakSide);
-
- if ( file_of(weakKing) == file_of(strongPawn)
- && relative_rank(strongSide, strongPawn) < relative_rank(strongSide, weakKing)
- && ( opposite_colors(weakKing, strongBishop)
- || relative_rank(strongSide, weakKing) <= RANK_6))
- return SCALE_FACTOR_DRAW;
-
- return SCALE_FACTOR_NONE;
-}
-
-
-/// KP vs KP. This is done by removing the weakest side's pawn and probing the
-/// KP vs K bitbase: if the weakest side has a draw without the pawn, it probably
-/// has at least a draw with the pawn as well. The exception is when the stronger
-/// side's pawn is far advanced and not on a rook file; in this case it is often
-/// possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
-template<>
-ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
-
- assert(verify_material(pos, strongSide, VALUE_ZERO, 1));
- assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
-
- // Assume strongSide is white and the pawn is on files A-D
- Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
- Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
- Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
-
- Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
-
- // If the pawn has advanced to the fifth rank or further, and is not a
- // rook pawn, it's too dangerous to assume that it's at least a draw.
- if (rank_of(strongPawn) >= RANK_5 && file_of(strongPawn) != FILE_A)
- return SCALE_FACTOR_NONE;
-
- // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
- // it's probably at least a draw even with the pawn.
- return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
-}
-
-} // namespace Stockfish
+++ /dev/null
-/*
- Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
-
- Stockfish is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Stockfish is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef ENDGAME_H_INCLUDED
-#define ENDGAME_H_INCLUDED
-
-#include <memory>
-#include <string>
-#include <type_traits>
-#include <unordered_map>
-#include <utility>
-
-#include "position.h"
-#include "types.h"
-
-namespace Stockfish {
-
-/// EndgameCode lists all supported endgame functions by corresponding codes
-
-enum EndgameCode {
-
- EVALUATION_FUNCTIONS,
- KNNK, // KNN vs K
- KNNKP, // KNN vs KP
- KXK, // Generic "mate lone king" eval
- KBNK, // KBN vs K
- KPK, // KP vs K
- KRKP, // KR vs KP
- KRKB, // KR vs KB
- KRKN, // KR vs KN
- KQKP, // KQ vs KP
- KQKR, // KQ vs KR
-
- SCALING_FUNCTIONS,
- KBPsK, // KB and pawns vs K
- KQKRPs, // KQ vs KR and pawns
- KRPKR, // KRP vs KR
- KRPKB, // KRP vs KB
- KRPPKRP, // KRPP vs KRP
- KPsK, // K and pawns vs K
- KBPKB, // KBP vs KB
- KBPPKB, // KBPP vs KB
- KBPKN, // KBP vs KN
- KPKP // KP vs KP
-};
-
-
-/// Endgame functions can be of two types depending on whether they return a
-/// Value or a ScaleFactor.
-
-template<EndgameCode E> using
-eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type;
-
-
-/// Base and derived functors for endgame evaluation and scaling functions
-
-template<typename T>
-struct EndgameBase {
-
- explicit EndgameBase(Color c) : strongSide(c), weakSide(~c) {}
- virtual ~EndgameBase() = default;
- virtual T operator()(const Position&) const = 0;
-
- const Color strongSide, weakSide;
-};
-
-
-template<EndgameCode E, typename T = eg_type<E>>
-struct Endgame : public EndgameBase<T> {
-
- explicit Endgame(Color c) : EndgameBase<T>(c) {}
- T operator()(const Position&) const override;
-};
-
-
-/// The Endgames namespace handles the pointers to endgame evaluation and scaling
-/// base objects in two std::map. We use polymorphism to invoke the actual
-/// endgame function by calling its virtual operator().
-
-namespace Endgames {
-
- template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>;
- template<typename T> using Map = std::unordered_map<Key, Ptr<T>>;
-
- extern std::pair<Map<Value>, Map<ScaleFactor>> maps;
-
- void init();
-
- template<typename T>
- Map<T>& map() {
- return std::get<std::is_same<T, ScaleFactor>::value>(maps);
- }
-
- template<EndgameCode E, typename T = eg_type<E>>
- void add(const std::string& code) {
-
- StateInfo st;
- map<T>()[Position().set(code, WHITE, &st).material_key()] = Ptr<T>(new Endgame<E>(WHITE));
- map<T>()[Position().set(code, BLACK, &st).material_key()] = Ptr<T>(new Endgame<E>(BLACK));
- }
-
- template<typename T>
- const EndgameBase<T>* probe(Key key) {
- auto it = map<T>().find(key);
- return it != map<T>().end() ? it->second.get() : nullptr;
- }
-}
-
-} // namespace Stockfish
-
-#endif // #ifndef ENDGAME_H_INCLUDED
#include <algorithm>
#include <cassert>
-#include <cstdlib>
-#include <cstring> // For std::memset
#include <fstream>
#include <iomanip>
#include <sstream>
#include "bitboard.h"
#include "evaluate.h"
-#include "material.h"
#include "misc.h"
-#include "pawns.h"
#include "thread.h"
#include "timeman.h"
#include "uci.h"
namespace Eval {
- bool useNNUE;
string currentEvalFileName = "None";
+ static double to_cp(Value v) { return double(v) / UCI::NormalizeToPawnValue; }
+
/// NNUE::init() tries to load a NNUE network at startup time, or when the engine
/// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue"
/// The name of the NNUE network is always retrieved from the EvalFile option.
void NNUE::init() {
- useNNUE = Options["Use NNUE"];
- if (!useNNUE)
- return;
-
string eval_file = string(Options["EvalFile"]);
if (eval_file.empty())
eval_file = EvalFileDefaultName;
if (eval_file.empty())
eval_file = EvalFileDefaultName;
- if (useNNUE && currentEvalFileName != eval_file)
+ if (currentEvalFileName != eval_file)
{
- string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available.";
+ string msg1 = "Network evaluation parameters compatible with the engine must be available.";
string msg2 = "The option is set to true, but the network file " + eval_file + " was not loaded successfully.";
string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file.";
string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + std::string(EvalFileDefaultName);
exit(EXIT_FAILURE);
}
- if (useNNUE)
- sync_cout << "info string NNUE evaluation using " << eval_file << " enabled" << sync_endl;
- else
- sync_cout << "info string classical evaluation enabled" << sync_endl;
- }
-}
-
-namespace Trace {
-
- enum Tracing { NO_TRACE, TRACE };
-
- enum Term { // The first 8 entries are reserved for PieceType
- MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, WINNABLE, TOTAL, TERM_NB
- };
-
- Score scores[TERM_NB][COLOR_NB];
-
- static double to_cp(Value v) { return double(v) / UCI::NormalizeToPawnValue; }
-
- static void add(int idx, Color c, Score s) {
- scores[idx][c] = s;
- }
-
- static void add(int idx, Score w, Score b = SCORE_ZERO) {
- scores[idx][WHITE] = w;
- scores[idx][BLACK] = b;
- }
-
- static std::ostream& operator<<(std::ostream& os, Score s) {
- os << std::setw(5) << to_cp(mg_value(s)) << " "
- << std::setw(5) << to_cp(eg_value(s));
- return os;
- }
-
- static std::ostream& operator<<(std::ostream& os, Term t) {
-
- if (t == MATERIAL || t == IMBALANCE || t == WINNABLE || t == TOTAL)
- os << " ---- ----" << " | " << " ---- ----";
- else
- os << scores[t][WHITE] << " | " << scores[t][BLACK];
-
- os << " | " << scores[t][WHITE] - scores[t][BLACK] << " |\n";
- return os;
+ sync_cout << "info string NNUE evaluation using " << eval_file << sync_endl;
}
}
-using namespace Trace;
-
-namespace {
-
- // Threshold for lazy and space evaluation
- constexpr Value LazyThreshold1 = Value(3622);
- constexpr Value LazyThreshold2 = Value(1962);
- constexpr Value SpaceThreshold = Value(11551);
-
- // KingAttackWeights[PieceType] contains king attack weights by piece type
- constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 76, 46, 45, 14 };
-
- // SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type,
- // higher if multiple safe checks are possible for that piece type.
- constexpr int SafeCheck[][2] = {
- {}, {}, {805, 1292}, {650, 984}, {1071, 1886}, {730, 1128}
- };
-
-#define S(mg, eg) make_score(mg, eg)
-
- // MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game,
- // indexed by piece type and number of attacked squares in the mobility area.
- constexpr Score MobilityBonus[][32] = {
- { S(-62,-79), S(-53,-57), S(-12,-31), S( -3,-17), S( 3, 7), S( 12, 13), // Knight
- S( 21, 16), S( 28, 21), S( 37, 26) },
- { S(-47,-59), S(-20,-25), S( 14, -8), S( 29, 12), S( 39, 21), S( 53, 40), // Bishop
- S( 53, 56), S( 60, 58), S( 62, 65), S( 69, 72), S( 78, 78), S( 83, 87),
- S( 91, 88), S( 96, 98) },
- { S(-60,-82), S(-24,-15), S( 0, 17) ,S( 3, 43), S( 4, 72), S( 14,100), // Rook
- S( 20,102), S( 30,122), S( 41,133), S(41 ,139), S( 41,153), S( 45,160),
- S( 57,165), S( 58,170), S( 67,175) },
- { S(-29,-49), S(-16,-29), S( -8, -8), S( -8, 17), S( 18, 39), S( 25, 54), // Queen
- S( 23, 59), S( 37, 73), S( 41, 76), S( 54, 95), S( 65, 95) ,S( 68,101),
- S( 69,124), S( 70,128), S( 70,132), S( 70,133) ,S( 71,136), S( 72,140),
- S( 74,147), S( 76,149), S( 90,153), S(104,169), S(105,171), S(106,171),
- S(112,178), S(114,185), S(114,187), S(119,221) }
- };
-
- // BishopPawns[distance from edge] contains a file-dependent penalty for pawns on
- // squares of the same color as our bishop.
- constexpr Score BishopPawns[int(FILE_NB) / 2] = {
- S(3, 8), S(3, 9), S(2, 7), S(3, 7)
- };
-
- // KingProtector[knight/bishop] contains penalty for each distance unit to own king
- constexpr Score KingProtector[] = { S(9, 9), S(7, 9) };
-
- // Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a
- // pawn protected square on rank 4 to 6 which is also safe from a pawn attack.
- constexpr Score Outpost[] = { S(54, 34), S(31, 25) };
-
- // PassedRank[Rank] contains a bonus according to the rank of a passed pawn
- constexpr Score PassedRank[RANK_NB] = {
- S(0, 0), S(2, 38), S(15, 36), S(22, 50), S(64, 81), S(166, 184), S(284, 269)
- };
-
- constexpr Score RookOnClosedFile = S(10, 5);
- constexpr Score RookOnOpenFile[] = { S(18, 8), S(49, 26) };
-
- // ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
- // which piece type attacks which one. Attacks on lesser pieces which are
- // pawn-defended are not considered.
- constexpr Score ThreatByMinor[PIECE_TYPE_NB] = {
- S(0, 0), S(6, 37), S(64, 50), S(82, 57), S(103, 130), S(81, 163)
- };
-
- constexpr Score ThreatByRook[PIECE_TYPE_NB] = {
- S(0, 0), S(3, 44), S(36, 71), S(44, 59), S(0, 39), S(60, 39)
- };
-
- constexpr Value CorneredBishop = Value(50);
-
- // Assorted bonuses and penalties
- constexpr Score UncontestedOutpost = S( 0, 10);
- constexpr Score BishopOnKingRing = S( 24, 0);
- constexpr Score BishopXRayPawns = S( 4, 5);
- constexpr Score FlankAttacks = S( 8, 0);
- constexpr Score Hanging = S( 72, 40);
- constexpr Score KnightOnQueen = S( 16, 11);
- constexpr Score LongDiagonalBishop = S( 45, 0);
- constexpr Score MinorBehindPawn = S( 18, 3);
- constexpr Score PassedFile = S( 13, 8);
- constexpr Score PawnlessFlank = S( 19, 97);
- constexpr Score ReachableOutpost = S( 33, 19);
- constexpr Score RestrictedPiece = S( 6, 7);
- constexpr Score RookOnKingRing = S( 16, 0);
- constexpr Score SliderOnQueen = S( 62, 21);
- constexpr Score ThreatByKing = S( 24, 87);
- constexpr Score ThreatByPawnPush = S( 48, 39);
- constexpr Score ThreatBySafePawn = S(167, 99);
- constexpr Score TrappedRook = S( 55, 13);
- constexpr Score WeakQueenProtection = S( 14, 0);
- constexpr Score WeakQueen = S( 57, 19);
-
-
-#undef S
-
- // Evaluation class computes and stores attacks tables and other working data
- template<Tracing T>
- class Evaluation {
-
- public:
- Evaluation() = delete;
- explicit Evaluation(const Position& p) : pos(p) {}
- Evaluation& operator=(const Evaluation&) = delete;
- Value value();
-
- private:
- template<Color Us> void initialize();
- template<Color Us, PieceType Pt> Score pieces();
- template<Color Us> Score king() const;
- template<Color Us> Score threats() const;
- template<Color Us> Score passed() const;
- template<Color Us> Score space() const;
- Value winnable(Score score) const;
-
- const Position& pos;
- Material::Entry* me;
- Pawns::Entry* pe;
- Bitboard mobilityArea[COLOR_NB];
- Score mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO };
-
- // attackedBy[color][piece type] is a bitboard representing all squares
- // attacked by a given color and piece type. Special "piece types" which
- // is also calculated is ALL_PIECES.
- Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
-
- // attackedBy2[color] are the squares attacked by at least 2 units of a given
- // color, including x-rays. But diagonal x-rays through pawns are not computed.
- Bitboard attackedBy2[COLOR_NB];
-
- // kingRing[color] are the squares adjacent to the king plus some other
- // very near squares, depending on king position.
- Bitboard kingRing[COLOR_NB];
-
- // kingAttackersCount[color] is the number of pieces of the given color
- // which attack a square in the kingRing of the enemy king.
- int kingAttackersCount[COLOR_NB];
-
- // kingAttackersWeight[color] is the sum of the "weights" of the pieces of
- // the given color which attack a square in the kingRing of the enemy king.
- // The weights of the individual piece types are given by the elements in
- // the KingAttackWeights array.
- int kingAttackersWeight[COLOR_NB];
-
- // kingAttacksCount[color] is the number of attacks by the given color to
- // squares directly adjacent to the enemy king. Pieces which attack more
- // than one square are counted multiple times. For instance, if there is
- // a white knight on g5 and black's king is on g8, this white knight adds 2
- // to kingAttacksCount[WHITE].
- int kingAttacksCount[COLOR_NB];
- };
-
-
- // Evaluation::initialize() computes king and pawn attacks, and the king ring
- // bitboard for a given color. This is done at the beginning of the evaluation.
-
- template<Tracing T> template<Color Us>
- void Evaluation<T>::initialize() {
-
- constexpr Color Them = ~Us;
- constexpr Direction Up = pawn_push(Us);
- constexpr Direction Down = -Up;
- constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB);
-
- const Square ksq = pos.square<KING>(Us);
-
- Bitboard dblAttackByPawn = pawn_double_attacks_bb<Us>(pos.pieces(Us, PAWN));
-
- // Find our pawns that are blocked or on the first two ranks
- Bitboard b = pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | LowRanks);
-
- // Squares occupied by those pawns, by our king or queen, by blockers to attacks on our king
- // or controlled by enemy pawns are excluded from the mobility area.
- mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pos.blockers_for_king(Us) | pe->pawn_attacks(Them));
-
- // Initialize attackedBy[] for king and pawns
- attackedBy[Us][KING] = attacks_bb<KING>(ksq);
- attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
- attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
- attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
-
- // Init our king safety tables
- Square s = make_square(std::clamp(file_of(ksq), FILE_B, FILE_G),
- std::clamp(rank_of(ksq), RANK_2, RANK_7));
- kingRing[Us] = attacks_bb<KING>(s) | s;
-
- kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
- kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
-
- // Remove from kingRing[] the squares defended by two pawns
- kingRing[Us] &= ~dblAttackByPawn;
- }
-
-
- // Evaluation::pieces() scores pieces of a given color and type
-
- template<Tracing T> template<Color Us, PieceType Pt>
- Score Evaluation<T>::pieces() {
-
- constexpr Color Them = ~Us;
- [[maybe_unused]] constexpr Direction Down = -pawn_push(Us);
- [[maybe_unused]] constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
- : Rank5BB | Rank4BB | Rank3BB);
- Bitboard b1 = pos.pieces(Us, Pt);
- Bitboard b, bb;
- Score score = SCORE_ZERO;
-
- attackedBy[Us][Pt] = 0;
-
- while (b1)
- {
- Square s = pop_lsb(b1);
-
- // Find attacked squares, including x-ray attacks for bishops and rooks
- b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN))
- : Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK))
- : attacks_bb<Pt>(s, pos.pieces());
-
- if (pos.blockers_for_king(Us) & s)
- b &= line_bb(pos.square<KING>(Us), s);
-
- attackedBy2[Us] |= attackedBy[Us][ALL_PIECES] & b;
- attackedBy[Us][Pt] |= b;
- attackedBy[Us][ALL_PIECES] |= b;
-
- if (b & kingRing[Them])
- {
- kingAttackersCount[Us]++;
- kingAttackersWeight[Us] += KingAttackWeights[Pt];
- kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]);
- }
-
- else if (Pt == ROOK && (file_bb(s) & kingRing[Them]))
- score += RookOnKingRing;
-
- else if (Pt == BISHOP && (attacks_bb<BISHOP>(s, pos.pieces(PAWN)) & kingRing[Them]))
- score += BishopOnKingRing;
-
- int mob = popcount(b & mobilityArea[Us]);
- mobility[Us] += MobilityBonus[Pt - 2][mob];
-
- if constexpr (Pt == BISHOP || Pt == KNIGHT)
- {
- // Bonus if the piece is on an outpost square or can reach one
- // Bonus for knights (UncontestedOutpost) if few relevant targets
- bb = OutpostRanks & (attackedBy[Us][PAWN] | shift<Down>(pos.pieces(PAWN)))
- & ~pe->pawn_attacks_span(Them);
- Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN);
-
- if ( Pt == KNIGHT
- && bb & s & ~CenterFiles // on a side outpost
- && !(b & targets) // no relevant attacks
- && (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide))))
- score += UncontestedOutpost * popcount(pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide));
- else if (bb & s)
- score += Outpost[Pt == BISHOP];
- else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
- score += ReachableOutpost;
-
- // Bonus for a knight or bishop shielded by pawn
- if (shift<Down>(pos.pieces(PAWN)) & s)
- score += MinorBehindPawn;
-
- // Penalty if the piece is far from the king
- score -= KingProtector[Pt == BISHOP] * distance(pos.square<KING>(Us), s);
-
- if constexpr (Pt == BISHOP)
- {
- // Penalty according to the number of our pawns on the same color square as the
- // bishop, bigger when the center files are blocked with pawns and smaller
- // when the bishop is outside the pawn chain.
- Bitboard blocked = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces());
-
- score -= BishopPawns[edge_distance(file_of(s))] * pos.pawns_on_same_color_squares(Us, s)
- * (!(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles));
-
- // Penalty for all enemy pawns x-rayed
- score -= BishopXRayPawns * popcount(attacks_bb<BISHOP>(s) & pos.pieces(Them, PAWN));
-
- // Bonus for bishop on a long diagonal which can "see" both center squares
- if (more_than_one(attacks_bb<BISHOP>(s, pos.pieces(PAWN)) & Center))
- score += LongDiagonalBishop;
-
- // 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 ( pos.is_chess960()
- && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
- {
- Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
- if (pos.piece_on(s + d) == make_piece(Us, PAWN))
- score -= !pos.empty(s + d + pawn_push(Us)) ? 4 * make_score(CorneredBishop, CorneredBishop)
- : 3 * make_score(CorneredBishop, CorneredBishop);
- }
- }
- }
-
- if constexpr (Pt == ROOK)
- {
- // Bonuses for rook on a (semi-)open or closed file
- if (pos.is_on_semiopen_file(Us, s))
- {
- score += RookOnOpenFile[pos.is_on_semiopen_file(Them, s)];
- }
- else
- {
- // If our pawn on this file is blocked, increase penalty
- if ( pos.pieces(Us, PAWN)
- & shift<Down>(pos.pieces())
- & file_bb(s))
- {
- score -= RookOnClosedFile;
- }
-
- // Penalty when trapped by the king, even more if the king cannot castle
- if (mob <= 3)
- {
- File kf = file_of(pos.square<KING>(Us));
- if ((kf < FILE_E) == (file_of(s) < kf))
- score -= TrappedRook * (1 + !pos.castling_rights(Us));
- }
- }
- }
-
- if constexpr (Pt == QUEEN)
- {
- // Penalty if any relative pin or discovered attack against the queen
- Bitboard queenPinners;
- if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners))
- score -= WeakQueen;
- }
- }
- if constexpr (T)
- Trace::add(Pt, Us, score);
-
- return score;
- }
-
-
- // Evaluation::king() assigns bonuses and penalties to a king of a given color
-
- template<Tracing T> template<Color Us>
- Score Evaluation<T>::king() const {
-
- constexpr Color Them = ~Us;
- constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB
- : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB);
-
- Bitboard weak, b1, b2, b3, safe, unsafeChecks = 0;
- Bitboard rookChecks, queenChecks, bishopChecks, knightChecks;
- int kingDanger = 0;
- const Square ksq = pos.square<KING>(Us);
-
- // Init the score with king shelter and enemy pawns storm
- Score score = pe->king_safety<Us>(pos);
-
- // Attacked squares defended at most once by our queen or king
- weak = attackedBy[Them][ALL_PIECES]
- & ~attackedBy2[Us]
- & (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]);
-
- // Analyse the safe enemy's checks which are possible on next move
- safe = ~pos.pieces(Them);
- safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]);
-
- b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
- b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
-
- // Enemy rooks checks
- rookChecks = b1 & attackedBy[Them][ROOK] & safe;
- if (rookChecks)
- kingDanger += SafeCheck[ROOK][more_than_one(rookChecks)];
- else
- unsafeChecks |= b1 & attackedBy[Them][ROOK];
-
- // Enemy queen safe checks: count them only if the checks are from squares from
- // which opponent cannot give a rook check, because rook checks are more valuable.
- queenChecks = (b1 | b2) & attackedBy[Them][QUEEN] & safe
- & ~(attackedBy[Us][QUEEN] | rookChecks);
- if (queenChecks)
- kingDanger += SafeCheck[QUEEN][more_than_one(queenChecks)];
-
- // Enemy bishops checks: count them only if they are from squares from which
- // opponent cannot give a queen check, because queen checks are more valuable.
- bishopChecks = b2 & attackedBy[Them][BISHOP] & safe
- & ~queenChecks;
- if (bishopChecks)
- kingDanger += SafeCheck[BISHOP][more_than_one(bishopChecks)];
-
- else
- unsafeChecks |= b2 & attackedBy[Them][BISHOP];
-
- // Enemy knights checks
- knightChecks = attacks_bb<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
- if (knightChecks & safe)
- kingDanger += SafeCheck[KNIGHT][more_than_one(knightChecks & safe)];
- else
- unsafeChecks |= knightChecks;
-
- // Find the squares that opponent attacks in our king flank, the squares
- // which they attack twice in that flank, and the squares that we defend.
- b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
- b2 = b1 & attackedBy2[Them];
- b3 = attackedBy[Us][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
-
- int kingFlankAttack = popcount(b1) + popcount(b2);
- int kingFlankDefense = popcount(b3);
-
- kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] // (~10 Elo)
- + 183 * popcount(kingRing[Us] & weak) // (~15 Elo)
- + 148 * popcount(unsafeChecks) // (~4 Elo)
- + 98 * popcount(pos.blockers_for_king(Us)) // (~2 Elo)
- + 69 * kingAttacksCount[Them] // (~0.5 Elo)
- + 3 * kingFlankAttack * kingFlankAttack / 8 // (~0.5 Elo)
- + mg_value(mobility[Them] - mobility[Us]) // (~0.5 Elo)
- - 873 * !pos.count<QUEEN>(Them) // (~24 Elo)
- - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) // (~5 Elo)
- - 6 * mg_value(score) / 8 // (~8 Elo)
- - 4 * kingFlankDefense // (~5 Elo)
- + 37; // (~0.5 Elo)
-
- // Transform the kingDanger units into a Score, and subtract it from the evaluation
- if (kingDanger > 100)
- score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);
-
- // Penalty when our king is on a pawnless flank
- if (!(pos.pieces(PAWN) & KingFlank[file_of(ksq)]))
- score -= PawnlessFlank;
-
- // Penalty if king flank is under attack, potentially moving toward the king
- score -= FlankAttacks * kingFlankAttack;
-
- if constexpr (T)
- Trace::add(KING, Us, score);
-
- return score;
- }
-
-
- // Evaluation::threats() assigns bonuses according to the types of the
- // attacking and the attacked pieces.
-
- template<Tracing T> template<Color Us>
- Score Evaluation<T>::threats() const {
-
- constexpr Color Them = ~Us;
- constexpr Direction Up = pawn_push(Us);
- constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
-
- Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe;
- Score score = SCORE_ZERO;
-
- // Non-pawn enemies
- nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(PAWN);
-
- // Squares strongly protected by the enemy, either because they defend the
- // square with a pawn, or because they defend the square twice and we don't.
- stronglyProtected = attackedBy[Them][PAWN]
- | (attackedBy2[Them] & ~attackedBy2[Us]);
-
- // Non-pawn enemies, strongly protected
- defended = nonPawnEnemies & stronglyProtected;
-
- // Enemies not strongly protected and under our attack
- weak = pos.pieces(Them) & ~stronglyProtected & attackedBy[Us][ALL_PIECES];
-
- // Bonus according to the kind of attacking pieces
- if (defended | weak)
- {
- b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]);
- while (b)
- score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(b)))];
-
- b = weak & attackedBy[Us][ROOK];
- while (b)
- score += ThreatByRook[type_of(pos.piece_on(pop_lsb(b)))];
-
- if (weak & attackedBy[Us][KING])
- score += ThreatByKing;
-
- b = ~attackedBy[Them][ALL_PIECES]
- | (nonPawnEnemies & attackedBy2[Us]);
- score += Hanging * popcount(weak & b);
-
- // Additional bonus if weak piece is only protected by a queen
- score += WeakQueenProtection * popcount(weak & attackedBy[Them][QUEEN]);
- }
-
- // Bonus for restricting their piece moves
- b = attackedBy[Them][ALL_PIECES]
- & ~stronglyProtected
- & attackedBy[Us][ALL_PIECES];
- score += RestrictedPiece * popcount(b);
-
- // Protected or unattacked squares
- safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES];
-
- // Bonus for attacking enemy pieces with our relatively safe pawns
- b = pos.pieces(Us, PAWN) & safe;
- b = pawn_attacks_bb<Us>(b) & nonPawnEnemies;
- score += ThreatBySafePawn * popcount(b);
-
- // Find squares where our pawns can push on the next move
- b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces();
- b |= shift<Up>(b & TRank3BB) & ~pos.pieces();
-
- // Keep only the squares which are relatively safe
- b &= ~attackedBy[Them][PAWN] & safe;
-
- // Bonus for safe pawn threats on the next move
- b = pawn_attacks_bb<Us>(b) & nonPawnEnemies;
- score += ThreatByPawnPush * popcount(b);
-
- // Bonus for threats on the next moves against enemy queen
- if (pos.count<QUEEN>(Them) == 1)
- {
- bool queenImbalance = pos.count<QUEEN>() == 1;
-
- Square s = pos.square<QUEEN>(Them);
- safe = mobilityArea[Us]
- & ~pos.pieces(Us, PAWN)
- & ~stronglyProtected;
-
- b = attackedBy[Us][KNIGHT] & attacks_bb<KNIGHT>(s);
-
- score += KnightOnQueen * popcount(b & safe) * (1 + queenImbalance);
-
- b = (attackedBy[Us][BISHOP] & attacks_bb<BISHOP>(s, pos.pieces()))
- | (attackedBy[Us][ROOK ] & attacks_bb<ROOK >(s, pos.pieces()));
-
- score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance);
- }
-
- if constexpr (T)
- Trace::add(THREAT, Us, score);
-
- return score;
- }
-
- // Evaluation::passed() evaluates the passed pawns and candidate passed
- // pawns of the given color.
-
- template<Tracing T> template<Color Us>
- Score Evaluation<T>::passed() const {
-
- constexpr Color Them = ~Us;
- constexpr Direction Up = pawn_push(Us);
- constexpr Direction Down = -Up;
-
- auto king_proximity = [&](Color c, Square s) {
- return std::min(distance(pos.square<KING>(c), s), 5);
- };
-
- Bitboard b, bb, squaresToQueen, unsafeSquares, blockedPassers, helpers;
- Score score = SCORE_ZERO;
-
- b = pe->passed_pawns(Us);
-
- blockedPassers = b & shift<Down>(pos.pieces(Them, PAWN));
- if (blockedPassers)
- {
- helpers = shift<Up>(pos.pieces(Us, PAWN))
- & ~pos.pieces(Them)
- & (~attackedBy2[Them] | attackedBy[Us][ALL_PIECES]);
-
- // Remove blocked candidate passers that don't have help to pass
- b &= ~blockedPassers
- | shift<WEST>(helpers)
- | shift<EAST>(helpers);
- }
-
- while (b)
- {
- Square s = pop_lsb(b);
-
- assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up)));
-
- int r = relative_rank(Us, s);
-
- Score bonus = PassedRank[r];
-
- if (r > RANK_3)
- {
- int w = 5 * r - 13;
- Square blockSq = s + Up;
-
- // Adjust bonus based on the king's proximity
- bonus += make_score(0, ( king_proximity(Them, blockSq) * 19 / 4
- - king_proximity(Us, blockSq) * 2) * w);
-
- // If blockSq is not the queening square then consider also a second push
- if (r != RANK_7)
- bonus -= make_score(0, king_proximity(Us, blockSq + Up) * w);
-
- // If the pawn is free to advance, then increase the bonus
- if (pos.empty(blockSq))
- {
- squaresToQueen = forward_file_bb(Us, s);
- unsafeSquares = passed_pawn_span(Us, s);
-
- bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
-
- if (!(pos.pieces(Them) & bb))
- unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
-
- // If there are no enemy pieces or attacks on passed pawn span, assign a big bonus.
- // Or if there is some, but they are all attacked by our pawns, assign a bit smaller bonus.
- // Otherwise assign a smaller bonus if the path to queen is not attacked
- // and even smaller bonus if it is attacked but block square is not.
- int k = !unsafeSquares ? 36 :
- !(unsafeSquares & ~attackedBy[Us][PAWN]) ? 30 :
- !(unsafeSquares & squaresToQueen) ? 17 :
- !(unsafeSquares & blockSq) ? 7 :
- 0 ;
-
- // Assign a larger bonus if the block square is defended
- if ((pos.pieces(Us) & bb) || (attackedBy[Us][ALL_PIECES] & blockSq))
- k += 5;
-
- bonus += make_score(k * w, k * w);
- }
- } // r > RANK_3
-
- score += bonus - PassedFile * edge_distance(file_of(s));
- }
-
- if constexpr (T)
- Trace::add(PASSED, Us, score);
-
- return score;
- }
-
-
- // Evaluation::space() computes a space evaluation for a given side, aiming to improve game
- // play in the opening. It is based on the number of safe squares on the four central files
- // on ranks 2 to 4. Completely safe squares behind a friendly pawn are counted twice.
- // Finally, the space bonus is multiplied by a weight which decreases according to occupancy.
-
- template<Tracing T> template<Color Us>
- Score Evaluation<T>::space() const {
-
- // Early exit if, for example, both queens or 6 minor pieces have been exchanged
- if (pos.non_pawn_material() < SpaceThreshold)
- return SCORE_ZERO;
-
- constexpr Color Them = ~Us;
- constexpr Direction Down = -pawn_push(Us);
- constexpr Bitboard SpaceMask =
- Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB)
- : CenterFiles & (Rank7BB | Rank6BB | Rank5BB);
-
- // Find the available squares for our pieces inside the area defined by SpaceMask
- Bitboard safe = SpaceMask
- & ~pos.pieces(Us, PAWN)
- & ~attackedBy[Them][PAWN];
-
- // Find all squares which are at most three squares behind some friendly pawn
- Bitboard behind = pos.pieces(Us, PAWN);
- behind |= shift<Down>(behind);
- behind |= shift<Down+Down>(behind);
-
- // Compute space score based on the number of safe squares and number of our pieces
- // increased with number of total blocked pawns in position.
- int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]);
- int weight = pos.count<ALL_PIECES>(Us) - 3 + std::min(pe->blocked_count(), 9);
- Score score = make_score(bonus * weight * weight / 16, 0);
-
- if constexpr (T)
- Trace::add(SPACE, Us, score);
-
- return score;
- }
-
-
- // Evaluation::winnable() adjusts the midgame and endgame score components, based on
- // the known attacking/defending status of the players. The final value is derived
- // by interpolation from the midgame and endgame values.
-
- template<Tracing T>
- Value Evaluation<T>::winnable(Score score) const {
-
- int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
- + int(rank_of(pos.square<KING>(WHITE)) - rank_of(pos.square<KING>(BLACK)));
-
- bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide)
- && (pos.pieces(PAWN) & KingSide);
-
- bool almostUnwinnable = outflanking < 0
- && !pawnsOnBothFlanks;
-
- bool infiltration = rank_of(pos.square<KING>(WHITE)) > RANK_4
- || rank_of(pos.square<KING>(BLACK)) < RANK_5;
-
- // Compute the initiative bonus for the attacking side
- int complexity = 9 * pe->passed_count()
- + 12 * pos.count<PAWN>()
- + 9 * outflanking
- + 21 * pawnsOnBothFlanks
- + 24 * infiltration
- + 51 * !pos.non_pawn_material()
- - 43 * almostUnwinnable
- -110 ;
-
- Value mg = mg_value(score);
- Value eg = eg_value(score);
-
- // Now apply the bonus: note that we find the attacking side by extracting the
- // sign of the midgame or endgame values, and that we carefully cap the bonus
- // so that the midgame and endgame scores do not change sign after the bonus.
- int u = ((mg > 0) - (mg < 0)) * std::clamp(complexity + 50, -abs(mg), 0);
- int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg));
-
- mg += u;
- eg += v;
-
- // Compute the scale factor for the winning side
- Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
- int sf = me->scale_factor(pos, strongSide);
-
- // If scale factor is not already specific, scale up/down via general heuristics
- if (sf == SCALE_FACTOR_NORMAL)
- {
- if (pos.opposite_bishops())
- {
- // For pure opposite colored bishops endgames use scale factor
- // based on the number of passed pawns of the strong side.
- if ( pos.non_pawn_material(WHITE) == BishopValueMg
- && pos.non_pawn_material(BLACK) == BishopValueMg)
- sf = 18 + 4 * popcount(pe->passed_pawns(strongSide));
- // For every other opposite colored bishops endgames use scale factor
- // based on the number of all pieces of the strong side.
- else
- sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide);
- }
- // For rook endgames with strong side not having overwhelming pawn number advantage
- // and its pawns being on one flank and weak side protecting its pieces with a king
- // use lower scale factor.
- else if ( pos.non_pawn_material(WHITE) == RookValueMg
- && pos.non_pawn_material(BLACK) == RookValueMg
- && pos.count<PAWN>(strongSide) - pos.count<PAWN>(~strongSide) <= 1
- && bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN))
- && (attacks_bb<KING>(pos.square<KING>(~strongSide)) & pos.pieces(~strongSide, PAWN)))
- sf = 36;
- // For queen vs no queen endgames use scale factor
- // based on number of minors of side that doesn't have queen.
- else if (pos.count<QUEEN>() == 1)
- sf = 37 + 3 * (pos.count<QUEEN>(WHITE) == 1 ? pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK)
- : pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE));
- // In every other case use scale factor based on
- // the number of pawns of the strong side reduced if pawns are on a single flank.
- else
- sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide)) - 4 * !pawnsOnBothFlanks;
-
- // Reduce scale factor in case of pawns being on a single flank
- sf -= 4 * !pawnsOnBothFlanks;
- }
-
- // Interpolate between the middlegame and (scaled by 'sf') endgame score
- v = mg * int(me->game_phase())
- + eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL;
- v /= PHASE_MIDGAME;
-
- if constexpr (T)
- {
- Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score)));
- Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL));
- }
-
- return Value(v);
- }
-
-
- // Evaluation::value() is the main function of the class. It computes the various
- // parts of the evaluation and returns the value of the position from the point
- // of view of the side to move.
-
- template<Tracing T>
- Value Evaluation<T>::value() {
-
- assert(!pos.checkers());
-
- // Probe the material hash table
- me = Material::probe(pos);
-
- // If we have a specialized evaluation function for the current material
- // configuration, call it and return.
- if (me->specialized_eval_exists())
- return me->evaluate(pos);
-
- // Initialize score by reading the incrementally updated scores included in
- // the position object (material + piece square tables) and the material
- // imbalance. Score is computed internally from the white point of view.
- Score score = pos.psq_score() + me->imbalance();
-
- // Probe the pawn hash table
- pe = Pawns::probe(pos);
- score += pe->pawn_score(WHITE) - pe->pawn_score(BLACK);
-
- // Early exit if score is high
- auto lazy_skip = [&](Value lazyThreshold) {
- return abs(mg_value(score) + eg_value(score)) > lazyThreshold
- + std::abs(pos.this_thread()->bestValue) * 5 / 4
- + pos.non_pawn_material() / 32;
- };
-
- if (lazy_skip(LazyThreshold1))
- goto make_v;
-
- // Main evaluation begins here
- initialize<WHITE>();
- initialize<BLACK>();
-
- // Pieces evaluated first (also populates attackedBy, attackedBy2).
- // Note that the order of evaluation of the terms is left unspecified.
- score += pieces<WHITE, KNIGHT>() - pieces<BLACK, KNIGHT>()
- + pieces<WHITE, BISHOP>() - pieces<BLACK, BISHOP>()
- + pieces<WHITE, ROOK >() - pieces<BLACK, ROOK >()
- + pieces<WHITE, QUEEN >() - pieces<BLACK, QUEEN >();
-
- score += mobility[WHITE] - mobility[BLACK];
-
- // More complex interactions that require fully populated attack bitboards
- score += king< WHITE>() - king< BLACK>()
- + passed< WHITE>() - passed< BLACK>();
-
- if (lazy_skip(LazyThreshold2))
- goto make_v;
-
- score += threats<WHITE>() - threats<BLACK>()
- + space< WHITE>() - space< BLACK>();
-
-make_v:
- // Derive single value from mg and eg parts of score
- Value v = winnable(score);
-
- // In case of tracing add all remaining individual evaluation terms
- if constexpr (T)
- {
- Trace::add(MATERIAL, pos.psq_score());
- Trace::add(IMBALANCE, me->imbalance());
- Trace::add(PAWN, pe->pawn_score(WHITE), pe->pawn_score(BLACK));
- Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]);
- }
-
- // Evaluation grain
- v = (v / 16) * 16;
-
- // Side to move point of view
- v = (pos.side_to_move() == WHITE ? v : -v);
-
- return v;
- }
-
-} // namespace Eval
-
-
/// evaluate() is the evaluator for the outer world. It returns a static
/// evaluation of the position from the point of view of the side to move.
Value v;
Value psq = pos.psq_eg_stm();
- // We use the much less accurate but faster Classical eval when the NNUE
- // option is set to false. Otherwise we use the NNUE eval unless the
- // PSQ advantage is decisive. (~4 Elo at STC, 1 Elo at LTC)
- bool useClassical = !useNNUE || abs(psq) > 2048;
+ int nnueComplexity;
+ int npm = pos.non_pawn_material() / 64;
- if (useClassical)
- v = Evaluation<NO_TRACE>(pos).value();
- else
- {
- int nnueComplexity;
- int npm = pos.non_pawn_material() / 64;
+ Color stm = pos.side_to_move();
+ Value optimism = pos.this_thread()->optimism[stm];
- Color stm = pos.side_to_move();
- Value optimism = pos.this_thread()->optimism[stm];
+ Value nnue = NNUE::evaluate(pos, true, &nnueComplexity);
- Value nnue = NNUE::evaluate(pos, true, &nnueComplexity);
-
- // Blend optimism with nnue complexity and (semi)classical complexity
- optimism += optimism * (nnueComplexity + abs(psq - nnue)) / 512;
- v = (nnue * (945 + npm) + optimism * (150 + npm)) / 1024;
- }
+ // Blend optimism with nnue complexity and (semi)classical complexity
+ optimism += optimism * (nnueComplexity + abs(psq - nnue)) / 512;
+ v = (nnue * (945 + npm) + optimism * (150 + npm)) / 1024;
// Damp down the evaluation linearly when shuffling
v = v * (200 - pos.rule50_count()) / 214;
if (pos.checkers())
return "Final evaluation: none (in check)";
- std::stringstream ss;
- ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2);
-
- Value v;
-
- std::memset(scores, 0, sizeof(scores));
-
// Reset any global variable used in eval
pos.this_thread()->bestValue = VALUE_ZERO;
pos.this_thread()->optimism[WHITE] = VALUE_ZERO;
pos.this_thread()->optimism[BLACK] = VALUE_ZERO;
- v = Evaluation<TRACE>(pos).value();
-
- ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
- << " Contributing terms for the classical eval:\n"
- << "+------------+-------------+-------------+-------------+\n"
- << "| Term | White | Black | Total |\n"
- << "| | MG EG | MG EG | MG EG |\n"
- << "+------------+-------------+-------------+-------------+\n"
- << "| Material | " << Term(MATERIAL)
- << "| Imbalance | " << Term(IMBALANCE)
- << "| Pawns | " << Term(PAWN)
- << "| Knights | " << Term(KNIGHT)
- << "| Bishops | " << Term(BISHOP)
- << "| Rooks | " << Term(ROOK)
- << "| Queens | " << Term(QUEEN)
- << "| Mobility | " << Term(MOBILITY)
- << "|King safety | " << Term(KING)
- << "| Threats | " << Term(THREAT)
- << "| Passed | " << Term(PASSED)
- << "| Space | " << Term(SPACE)
- << "| Winnable | " << Term(WINNABLE)
- << "+------------+-------------+-------------+-------------+\n"
- << "| Total | " << Term(TOTAL)
- << "+------------+-------------+-------------+-------------+\n";
-
- if (Eval::useNNUE)
- ss << '\n' << NNUE::trace(pos) << '\n';
+ std::stringstream ss;
+ ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2);
+ ss << '\n' << NNUE::trace(pos) << '\n';
ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15);
+ Value v;
+ v = NNUE::evaluate(pos, false);
v = pos.side_to_move() == WHITE ? v : -v;
- ss << "\nClassical evaluation " << to_cp(v) << " (white side)\n";
- if (Eval::useNNUE)
- {
- v = NNUE::evaluate(pos, false);
- v = pos.side_to_move() == WHITE ? v : -v;
- ss << "NNUE evaluation " << to_cp(v) << " (white side)\n";
- }
+ ss << "NNUE evaluation " << to_cp(v) << " (white side)\n";
v = evaluate(pos);
v = pos.side_to_move() == WHITE ? v : -v;
ss << "Final evaluation " << to_cp(v) << " (white side)";
- if (Eval::useNNUE)
- ss << " [with scaled NNUE, hybrid, ...]";
+ ss << " [with scaled NNUE, ...]";
ss << "\n";
return ss.str();
#include <iostream>
#include "bitboard.h"
-#include "endgame.h"
#include "position.h"
#include "psqt.h"
#include "search.h"
PSQT::init();
Bitboards::init();
Position::init();
- Bitbases::init();
- Endgames::init();
Threads.set(size_t(Options["Threads"]));
Search::clear(); // After threads are up
Eval::NNUE::init();
+++ /dev/null
-/*
- Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
-
- Stockfish is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Stockfish is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <cassert>
-#include <cstring> // For std::memset
-
-#include "material.h"
-#include "thread.h"
-
-using namespace std;
-
-namespace Stockfish {
-
-namespace {
- #define S(mg, eg) make_score(mg, eg)
-
- // Polynomial material imbalance parameters
-
- // One Score parameter for each pair (our piece, another of our pieces)
- constexpr Score QuadraticOurs[][PIECE_TYPE_NB] = {
- // OUR PIECE 2
- // bishop pair pawn knight bishop rook queen
- {S(1419, 1455) }, // Bishop pair
- {S( 101, 28), S( 37, 39) }, // Pawn
- {S( 57, 64), S(249, 187), S(-49, -62) }, // Knight OUR PIECE 1
- {S( 0, 0), S(118, 137), S( 10, 27), S( 0, 0) }, // Bishop
- {S( -63, -68), S( -5, 3), S(100, 81), S(132, 118), S(-246, -244) }, // Rook
- {S(-210, -211), S( 37, 14), S(147, 141), S(161, 105), S(-158, -174), S(-9,-31) } // Queen
- };
-
- // One Score parameter for each pair (our piece, their piece)
- constexpr Score QuadraticTheirs[][PIECE_TYPE_NB] = {
- // THEIR PIECE
- // bishop pair pawn knight bishop rook queen
- { }, // Bishop pair
- {S( 33, 30) }, // Pawn
- {S( 46, 18), S(106, 84) }, // Knight OUR PIECE
- {S( 75, 35), S( 59, 44), S( 60, 15) }, // Bishop
- {S( 26, 35), S( 6, 22), S( 38, 39), S(-12, -2) }, // Rook
- {S( 97, 93), S(100, 163), S(-58, -91), S(112, 192), S(276, 225) } // Queen
- };
-
- #undef S
-
- // Endgame evaluation and scaling functions are accessed directly and not through
- // the function maps because they correspond to more than one material hash key.
- Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
-
- Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) };
- Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) };
- Endgame<KPsK> ScaleKPsK[] = { Endgame<KPsK>(WHITE), Endgame<KPsK>(BLACK) };
- Endgame<KPKP> ScaleKPKP[] = { Endgame<KPKP>(WHITE), Endgame<KPKP>(BLACK) };
-
- // Helper used to detect a given material distribution
- bool is_KXK(const Position& pos, Color us) {
- return !more_than_one(pos.pieces(~us))
- && pos.non_pawn_material(us) >= RookValueMg;
- }
-
- bool is_KBPsK(const Position& pos, Color us) {
- return pos.non_pawn_material(us) == BishopValueMg
- && pos.count<PAWN>(us) >= 1;
- }
-
- bool is_KQKRPs(const Position& pos, Color us) {
- return !pos.count<PAWN>(us)
- && pos.non_pawn_material(us) == QueenValueMg
- && pos.count<ROOK>(~us) == 1
- && pos.count<PAWN>(~us) >= 1;
- }
-
-
- /// imbalance() calculates the imbalance by comparing the piece count of each
- /// piece type for both colors.
-
- template<Color Us>
- Score imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
-
- constexpr Color Them = ~Us;
-
- Score bonus = SCORE_ZERO;
-
- // Second-degree polynomial material imbalance, by Tord Romstad
- for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
- {
- if (!pieceCount[Us][pt1])
- continue;
-
- int v = QuadraticOurs[pt1][pt1] * pieceCount[Us][pt1];
-
- for (int pt2 = NO_PIECE_TYPE; pt2 < pt1; ++pt2)
- v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2]
- + QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2];
-
- bonus += pieceCount[Us][pt1] * v;
- }
-
- return bonus;
- }
-
-} // namespace
-
-namespace Material {
-
-
-/// Material::probe() looks up the current position's material configuration in
-/// the material hash table. It returns a pointer to the Entry if the position
-/// is found. Otherwise a new Entry is computed and stored there, so we don't
-/// have to recompute all when the same material configuration occurs again.
-
-Entry* probe(const Position& pos) {
-
- Key key = pos.material_key();
- Entry* e = pos.this_thread()->materialTable[key];
-
- if (e->key == key)
- return e;
-
- std::memset(e, 0, sizeof(Entry));
- e->key = key;
- e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
-
- Value npm_w = pos.non_pawn_material(WHITE);
- Value npm_b = pos.non_pawn_material(BLACK);
- Value npm = std::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
-
- // Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
- e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
-
- // 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 ((e->evaluationFunction = Endgames::probe<Value>(key)) != nullptr)
- return e;
-
- for (Color c : { WHITE, BLACK })
- if (is_KXK(pos, c))
- {
- e->evaluationFunction = &EvaluateKXK[c];
- return e;
- }
-
- // OK, we didn't find any special evaluation function for the current material
- // configuration. Is there a suitable specialized scaling function?
- const auto* sf = Endgames::probe<ScaleFactor>(key);
-
- if (sf)
- {
- e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned
- return e;
- }
-
- // We didn't find any specialized scaling function, so fall back on generic
- // ones that refer to more than one material distribution. Note that in this
- // case we don't return after setting the function.
- for (Color c : { WHITE, BLACK })
- {
- if (is_KBPsK(pos, c))
- e->scalingFunction[c] = &ScaleKBPsK[c];
-
- else if (is_KQKRPs(pos, c))
- e->scalingFunction[c] = &ScaleKQKRPs[c];
- }
-
- if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board
- {
- if (!pos.count<PAWN>(BLACK))
- {
- assert(pos.count<PAWN>(WHITE) >= 2);
-
- e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
- }
- else if (!pos.count<PAWN>(WHITE))
- {
- assert(pos.count<PAWN>(BLACK) >= 2);
-
- e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
- }
- else if (pos.count<PAWN>(WHITE) == 1 && pos.count<PAWN>(BLACK) == 1)
- {
- // This is a special case because we set scaling functions
- // for both colors instead of only one.
- e->scalingFunction[WHITE] = &ScaleKPKP[WHITE];
- e->scalingFunction[BLACK] = &ScaleKPKP[BLACK];
- }
- }
-
- // Zero or just one pawn makes it difficult to win, even with a small material
- // advantage. This catches some trivial draws like KK, KBK and KNK and gives a
- // drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN).
- if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
- e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW :
- npm_b <= BishopValueMg ? 4 : 14);
-
- if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg)
- e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW :
- npm_w <= BishopValueMg ? 4 : 14);
-
- // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
- // for the bishop pair "extended piece", which allows us to be more flexible
- // in defining bishop pair bonuses.
- const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
- { pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),
- pos.count<BISHOP>(WHITE) , pos.count<ROOK>(WHITE), pos.count<QUEEN >(WHITE) },
- { pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
- pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
-
- e->score = (imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16;
- return e;
-}
-
-} // namespace Material
-
-} // namespace Stockfish
+++ /dev/null
-/*
- Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
-
- Stockfish is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Stockfish is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef MATERIAL_H_INCLUDED
-#define MATERIAL_H_INCLUDED
-
-#include "endgame.h"
-#include "misc.h"
-#include "position.h"
-#include "types.h"
-
-namespace Stockfish::Material {
-
-/// Material::Entry contains various information about a material configuration.
-/// It contains a material imbalance evaluation, a function pointer to a special
-/// endgame evaluation function (which in most cases is nullptr, meaning that the
-/// standard evaluation function will be used), and scale factors.
-///
-/// The scale factors are used to scale the evaluation score up or down. For
-/// instance, in KRB vs KR endgames, the score is scaled down by a factor of 4,
-/// which will result in scores of absolute value less than one pawn.
-
-struct Entry {
-
- Score imbalance() const { return score; }
- Phase game_phase() const { return (Phase)gamePhase; }
- bool specialized_eval_exists() const { return evaluationFunction != nullptr; }
- Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); }
-
- // scale_factor() takes a position and a color as input and returns a scale factor
- // for the given color. We have to provide the position in addition to the color
- // because the scale factor may also be a function which should be applied to
- // the position. For instance, in KBP vs K endgames, the scaling function looks
- // for rook pawns and wrong-colored bishops.
- ScaleFactor scale_factor(const Position& pos, Color c) const {
- ScaleFactor sf = scalingFunction[c] ? (*scalingFunction[c])(pos)
- : SCALE_FACTOR_NONE;
- return sf != SCALE_FACTOR_NONE ? sf : ScaleFactor(factor[c]);
- }
-
- Key key;
- const EndgameBase<Value>* evaluationFunction;
- const EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
- // side (e.g. KPKP, KBPsK)
- Score score;
- int16_t gamePhase;
- uint8_t factor[COLOR_NB];
-};
-
-using Table = HashTable<Entry, 8192>;
-
-Entry* probe(const Position& pos);
-
-} // namespace Stockfish::Material
-
-#endif // #ifndef MATERIAL_H_INCLUDED
}
void hint_common_parent_position(const Position& pos) {
- if (Eval::useNNUE)
- featureTransformer->hint_common_access(pos);
+ featureTransformer->hint_common_access(pos);
}
// Evaluation function. Perform differential calculation.
+++ /dev/null
-/*
- Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
-
- Stockfish is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Stockfish is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <algorithm>
-#include <cassert>
-
-#include "bitboard.h"
-#include "pawns.h"
-#include "position.h"
-#include "thread.h"
-
-namespace Stockfish {
-
-namespace {
-
- #define V Value
- #define S(mg, eg) make_score(mg, eg)
-
- // Pawn penalties
- constexpr Score Backward = S( 6, 19);
- constexpr Score Doubled = S(11, 51);
- constexpr Score DoubledEarly = S(17, 7);
- constexpr Score Isolated = S( 1, 20);
- constexpr Score WeakLever = S( 2, 57);
- constexpr Score WeakUnopposed = S(15, 18);
-
- // Bonus for blocked pawns at 5th or 6th rank
- constexpr Score BlockedPawn[2] = { S(-19, -8), S(-7, 3) };
-
- constexpr Score BlockedStorm[RANK_NB] = {
- S(0, 0), S(0, 0), S(64, 75), S(-3, 14), S(-12, 19), S(-7, 4), S(-10, 5)
- };
-
- // Connected pawn bonus
- constexpr int Connected[RANK_NB] = { 0, 3, 7, 7, 15, 54, 86 };
-
- // Strength of pawn shelter for our king by [distance from edge][rank].
- // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king.
- constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = {
- { V(-2), V(85), V(95), V(53), V(39), V(23), V(25) },
- { V(-55), V(64), V(32), V(-55), V(-30), V(-11), V(-61) },
- { V(-11), V(75), V(19), V(-6), V(26), V(9), V(-47) },
- { V(-41), V(-11), V(-27), V(-58), V(-42), V(-66), V(-163) }
- };
-
- // Danger of enemy pawns moving toward our king by [distance from edge][rank].
- // RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn
- // is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn
- // on edge, likely blocked by our king.
- constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = {
- { V(94), V(-280), V(-170), V(90), V(59), V(47), V(53) },
- { V(43), V(-17), V(128), V(39), V(26), V(-17), V(15) },
- { V(-9), V(62), V(170), V(34), V(-5), V(-20), V(-11) },
- { V(-27), V(-19), V(106), V(10), V(2), V(-13), V(-24) }
- };
-
-
- // KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties
- // for king when the king is on a semi-open or open file.
- constexpr Score KingOnFile[2][2] = {{ S(-18,11), S(-6,-3) },
- { S( 0, 0), S( 5,-4) }};
-
- #undef S
- #undef V
-
-
- /// evaluate() calculates a score for the static pawn structure of the given position.
- /// We cannot use the location of pieces or king in this function, as the evaluation
- /// of the pawn structure will be stored in a small cache for speed reasons, and will
- /// be re-used even when the pieces have moved.
-
- template<Color Us>
- Score evaluate(const Position& pos, Pawns::Entry* e) {
-
- constexpr Color Them = ~Us;
- constexpr Direction Up = pawn_push(Us);
- constexpr Direction Down = -Up;
-
- Bitboard neighbours, stoppers, support, phalanx, opposed;
- Bitboard lever, leverPush, blocked;
- Square s;
- bool backward, passed, doubled;
- Score score = SCORE_ZERO;
- Bitboard b = pos.pieces(Us, PAWN);
-
- Bitboard ourPawns = pos.pieces( Us, PAWN);
- Bitboard theirPawns = pos.pieces(Them, PAWN);
-
- Bitboard doubleAttackThem = pawn_double_attacks_bb<Them>(theirPawns);
-
- e->passedPawns[Us] = 0;
- e->kingSquares[Us] = SQ_NONE;
- e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb<Us>(ourPawns);
- e->blockedCount += popcount(shift<Up>(ourPawns) & (theirPawns | doubleAttackThem));
-
- // Loop through all pawns of the current color and score each pawn
- while (b)
- {
- s = pop_lsb(b);
-
- assert(pos.piece_on(s) == make_piece(Us, PAWN));
-
- Rank r = relative_rank(Us, s);
-
- // Flag the pawn
- opposed = theirPawns & forward_file_bb(Us, s);
- blocked = theirPawns & (s + Up);
- stoppers = theirPawns & passed_pawn_span(Us, s);
- lever = theirPawns & pawn_attacks_bb(Us, s);
- leverPush = theirPawns & pawn_attacks_bb(Us, s + Up);
- doubled = ourPawns & (s - Up);
- neighbours = ourPawns & adjacent_files_bb(s);
- phalanx = neighbours & rank_bb(s);
- support = neighbours & rank_bb(s - Up);
-
- if (doubled)
- {
- // Additional doubled penalty if none of their pawns is fixed
- if (!(ourPawns & shift<Down>(theirPawns | pawn_attacks_bb<Them>(theirPawns))))
- score -= DoubledEarly;
- }
-
- // A pawn is backward when it is behind all pawns of the same color on
- // the adjacent files and cannot safely advance.
- backward = !(neighbours & forward_ranks_bb(Them, s + Up))
- && (leverPush | blocked);
-
- // Compute additional span if pawn is not backward nor blocked
- if (!backward && !blocked)
- e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
-
- // A pawn is passed if one of the three following conditions is true:
- // (a) there is no stoppers except some levers
- // (b) the only stoppers are the leverPush, but we outnumber them
- // (c) there is only one front stopper which can be levered.
- // (Refined in Evaluation::passed)
- passed = !(stoppers ^ lever)
- || ( !(stoppers ^ leverPush)
- && popcount(phalanx) >= popcount(leverPush))
- || ( stoppers == blocked && r >= RANK_5
- && (shift<Up>(support) & ~(theirPawns | doubleAttackThem)));
-
- passed &= !(forward_file_bb(Us, s) & ourPawns);
-
- // Passed pawns will be properly scored later in evaluation when we have
- // full attack info.
- if (passed)
- e->passedPawns[Us] |= s;
-
- // Score this pawn
- if (support | phalanx)
- {
- int v = Connected[r] * (2 + bool(phalanx) - bool(opposed))
- + 22 * popcount(support);
-
- score += make_score(v, v * (r - 2) / 4);
- }
-
- else if (!neighbours)
- {
- if ( opposed
- && (ourPawns & forward_file_bb(Them, s))
- && !(theirPawns & adjacent_files_bb(s)))
- score -= Doubled;
- else
- score -= Isolated
- + WeakUnopposed * !opposed;
- }
-
- else if (backward)
- score -= Backward
- + WeakUnopposed * !opposed * bool(~(FileABB | FileHBB) & s);
-
- if (!support)
- score -= Doubled * doubled
- + WeakLever * more_than_one(lever);
-
- if (blocked && r >= RANK_5)
- score += BlockedPawn[r - RANK_5];
- }
-
- return score;
- }
-
-} // namespace
-
-namespace Pawns {
-
-
-/// Pawns::probe() looks up the current position's pawns configuration in
-/// the pawns hash table. It returns a pointer to the Entry if the position
-/// is found. Otherwise a new Entry is computed and stored there, so we don't
-/// have to recompute all when the same pawns configuration occurs again.
-
-Entry* probe(const Position& pos) {
-
- Key key = pos.pawn_key();
- Entry* e = pos.this_thread()->pawnsTable[key];
-
- if (e->key == key)
- return e;
-
- e->key = key;
- e->blockedCount = 0;
- e->scores[WHITE] = evaluate<WHITE>(pos, e);
- e->scores[BLACK] = evaluate<BLACK>(pos, e);
-
- return e;
-}
-
-
-/// Entry::evaluate_shelter() calculates the shelter bonus and the storm
-/// penalty for a king, looking at the king file and the two closest files.
-
-template<Color Us>
-Score Entry::evaluate_shelter(const Position& pos, Square ksq) const {
-
- constexpr Color Them = ~Us;
-
- Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
- Bitboard ourPawns = b & pos.pieces(Us) & ~pawnAttacks[Them];
- Bitboard theirPawns = b & pos.pieces(Them);
-
- Score bonus = make_score(5, 5);
-
- File center = std::clamp(file_of(ksq), FILE_B, FILE_G);
- for (File f = File(center - 1); f <= File(center + 1); ++f)
- {
- b = ourPawns & file_bb(f);
- int ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;
-
- b = theirPawns & file_bb(f);
- int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;
-
- int d = edge_distance(f);
- bonus += make_score(ShelterStrength[d][ourRank], 0);
-
- if (ourRank && (ourRank == theirRank - 1))
- bonus -= BlockedStorm[theirRank];
- else
- bonus -= make_score(UnblockedStorm[d][theirRank], 0);
- }
-
- // King On File
- bonus -= KingOnFile[pos.is_on_semiopen_file(Us, ksq)][pos.is_on_semiopen_file(Them, ksq)];
-
- return bonus;
-}
-
-
-/// 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.
-
-template<Color Us>
-Score Entry::do_king_safety(const Position& pos) {
-
- Square ksq = pos.square<KING>(Us);
- kingSquares[Us] = ksq;
- castlingRights[Us] = pos.castling_rights(Us);
- auto compare = [](Score a, Score b) { return mg_value(a) < mg_value(b); };
-
- Score shelter = evaluate_shelter<Us>(pos, ksq);
-
- // If we can castle use the bonus after castling if it is bigger
-
- if (pos.can_castle(Us & KING_SIDE))
- shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)), compare);
-
- if (pos.can_castle(Us & QUEEN_SIDE))
- shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)), compare);
-
- // In endgame we like to bring our king near our closest pawn
- Bitboard pawns = pos.pieces(Us, PAWN);
- int minPawnDist = 6;
-
- if (pawns & attacks_bb<KING>(ksq))
- minPawnDist = 1;
- else while (pawns)
- minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(pawns)));
-
- return shelter - make_score(0, 16 * minPawnDist);
-}
-
-// Explicit template instantiation
-template Score Entry::do_king_safety<WHITE>(const Position& pos);
-template Score Entry::do_king_safety<BLACK>(const Position& pos);
-
-} // namespace Pawns
-
-} // namespace Stockfish
+++ /dev/null
-/*
- Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
-
- Stockfish is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Stockfish is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef PAWNS_H_INCLUDED
-#define PAWNS_H_INCLUDED
-
-#include "misc.h"
-#include "position.h"
-#include "types.h"
-
-namespace Stockfish::Pawns {
-
-/// Pawns::Entry contains various information about a pawn structure. A lookup
-/// to the pawn hash table (performed by calling the probe function) returns a
-/// pointer to an Entry object.
-
-struct Entry {
-
- Score pawn_score(Color c) const { return scores[c]; }
- Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
- Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
- Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
- int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); }
- int blocked_count() const { return blockedCount; }
-
- template<Color Us>
- Score king_safety(const Position& pos) {
- return kingSquares[Us] == pos.square<KING>(Us) && castlingRights[Us] == pos.castling_rights(Us)
- ? kingSafety[Us] : (kingSafety[Us] = do_king_safety<Us>(pos));
- }
-
- template<Color Us>
- Score do_king_safety(const Position& pos);
-
- template<Color Us>
- Score evaluate_shelter(const Position& pos, Square ksq) const;
-
- Key key;
- Score scores[COLOR_NB];
- Bitboard passedPawns[COLOR_NB];
- Bitboard pawnAttacks[COLOR_NB];
- Bitboard pawnAttacksSpan[COLOR_NB];
- Square kingSquares[COLOR_NB];
- Score kingSafety[COLOR_NB];
- int castlingRights[COLOR_NB];
- int blockedCount;
-};
-
-using Table = HashTable<Entry, 131072>;
-
-Entry* probe(const Position& pos);
-
-} // namespace Stockfish::Pawns
-
-#endif // #ifndef PAWNS_H_INCLUDED
else
st->nonPawnMaterial[them] -= PieceValue[MG][captured];
- if (Eval::useNNUE)
- {
- dp.dirty_num = 2; // 1 piece moved, 1 piece captured
- dp.piece[1] = captured;
- dp.from[1] = capsq;
- dp.to[1] = SQ_NONE;
- }
+ dp.dirty_num = 2; // 1 piece moved, 1 piece captured
+ dp.piece[1] = captured;
+ dp.from[1] = capsq;
+ dp.to[1] = SQ_NONE;
// Update board and piece lists
remove_piece(capsq);
// Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[captured][capsq];
st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]];
- prefetch(thisThread->materialTable[st->materialKey]);
// Reset rule 50 counter
st->rule50 = 0;
// Move the piece. The tricky Chess960 castling is handled earlier
if (type_of(m) != CASTLING)
{
- if (Eval::useNNUE)
- {
- dp.piece[0] = pc;
- dp.from[0] = from;
- dp.to[0] = to;
- }
+ dp.piece[0] = pc;
+ dp.from[0] = from;
+ dp.to[0] = to;
move_piece(from, to);
}
remove_piece(to);
put_piece(promotion, to);
- if (Eval::useNNUE)
- {
- // Promoting pawn to SQ_NONE, promoted piece from SQ_NONE
- dp.to[0] = SQ_NONE;
- dp.piece[dp.dirty_num] = promotion;
- dp.from[dp.dirty_num] = SQ_NONE;
- dp.to[dp.dirty_num] = to;
- dp.dirty_num++;
- }
+ // Promoting pawn to SQ_NONE, promoted piece from SQ_NONE
+ dp.to[0] = SQ_NONE;
+ dp.piece[dp.dirty_num] = promotion;
+ dp.from[dp.dirty_num] = SQ_NONE;
+ dp.to[dp.dirty_num] = to;
+ dp.dirty_num++;
// Update hash keys
k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to];
rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
- if (Do && Eval::useNNUE)
+ if (Do)
{
auto& dp = st->dirtyPiece;
dp.piece[0] = make_piece(us, KING);
#include <thread>
#include <vector>
-#include "material.h"
#include "movepick.h"
-#include "pawns.h"
#include "position.h"
#include "search.h"
#include "thread_win32_osx.h"
void wait_for_search_finished();
size_t id() const { return idx; }
- Pawns::Table pawnsTable;
- Material::Table materialTable;
size_t pvIdx, pvLast;
std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
int selDepth, nmpMinPly;
static void on_logger(const Option& o) { start_logger(o); }
static void on_threads(const Option& o) { Threads.set(size_t(o)); }
static void on_tb_path(const Option& o) { Tablebases::init(o); }
-static void on_use_NNUE(const Option&) { Eval::NNUE::init(); }
static void on_eval_file(const Option&) { Eval::NNUE::init(); }
/// Our case insensitive less() function as required by UCI protocol
o["SyzygyProbeDepth"] << Option(1, 1, 100);
o["Syzygy50MoveRule"] << Option(true);
o["SyzygyProbeLimit"] << Option(7, 0, 7);
- o["Use NNUE"] << Option(true, on_use_NNUE);
o["EvalFile"] << Option(EvalFileDefaultName, on_eval_file);
}