stellar

UCI Chess engine written in C++20
git clone git://git.dimitrijedobrota.com/stellar.git
Log | Files | Refs | README | LICENSE

evaluate.cpp (7644B)


0 #include "evaluate.hpp" 1 #include "bit.hpp" 2 #include "bitboard.hpp" 3 #include "piece.hpp" 4 #include "score.hpp" 5 #include "utils.hpp" 6 7 #include <array> 8 #include <vector> 9 #include <numeric> 10 11 namespace evaluate { 12 13 using mask_fr_array = std::array<U64, 8>; 14 inline constexpr const mask_fr_array mask_rank = []() constexpr -> mask_fr_array { 15 mask_fr_array mask_rank; 16 U64 mask = 0xFF; 17 for (uint8_t rank = 0; rank < 8; rank++) { 18 mask_rank[rank] = mask; 19 mask = bitboard::nortOne(mask); 20 } 21 return mask_rank; 22 }(); 23 24 inline constexpr const mask_fr_array mask_file = []() constexpr -> mask_fr_array { 25 mask_fr_array mask_file; 26 U64 mask = 0x0101010101010101; 27 for (uint8_t file = 0; file < 8; file++) { 28 mask_file[file] = mask; 29 mask = bitboard::eastOne(mask); 30 } 31 return mask_file; 32 }(); 33 34 inline constexpr const mask_fr_array mask_isolated = []() constexpr -> mask_fr_array { 35 mask_fr_array mask_isolated; 36 37 mask_isolated[0] = 0x0202020202020202; 38 39 U64 mask = 0x0505050505050505; 40 for (uint8_t file = 1; file < 8; file++) { 41 mask_isolated[file] = mask; 42 mask = bitboard::eastOne(mask); 43 } 44 45 return mask_isolated; 46 }(); 47 48 using mask_passed_array = std::array<std::array<U64, 64>, 2>; 49 inline constexpr const mask_passed_array mask_passed = []() constexpr -> mask_passed_array { 50 mask_passed_array mask_passed; 51 52 U64 maskW = 0, maskB = 0; 53 for (uint8_t file = 0; file < 8; file++) { 54 maskW = maskB = mask_file[file] | mask_isolated[file]; 55 for (uint8_t rank = 0; rank < 8; rank++) { 56 maskW = bitboard::nortOne(maskW); 57 mask_passed[0][rank * 8 + file] = maskW; 58 59 maskB = bitboard::soutOne(maskB); 60 mask_passed[1][(7 - rank) * 8 + file] = maskB; 61 } 62 } 63 64 return mask_passed; 65 }(); 66 67 struct Hashe { 68 U32 key; 69 int16_t opening; 70 int16_t endgame; 71 int16_t total; 72 }; 73 74 template <U32 SIZE> struct PTable { 75 76 static void clear() { memset(table, 0x00, SIZE * sizeof(Hashe)); } 77 78 [[nodiscard]] static inline U32 read(U32 hash, Color side) { 79 const U32 key = side * SIZE + hash % SIZE; 80 const Hashe &phashe = table[key]; 81 return phashe.key == hash ? key : std::numeric_limits<uint32_t>::max(); 82 } 83 84 [[nodiscard]] static inline int16_t read_opening(U32 key) { return table[key].opening; } 85 [[nodiscard]] static inline int16_t read_endgame(U32 key) { return table[key].endgame; } 86 [[nodiscard]] static inline int16_t read_total(U32 key) { return table[key].total; } 87 88 static inline void write(U32 hash, Color side, int16_t opening, int16_t endgame, int16_t total) { 89 table[side * SIZE + hash % SIZE] = {hash, opening, endgame, total}; 90 } 91 92 private: 93 static std::array<Hashe, 2 * SIZE> table; 94 }; 95 96 template <U32 SIZE> std::array<Hashe, 2 * SIZE> PTable<SIZE>::table = {{{0}}}; 97 98 PTable<65537> ptable; 99 100 uint16_t score_game_phase(const Board &board) { 101 int16_t total = 0; 102 for (int type_i = KNIGHT; type_i < KING; type_i++) { 103 const Type type = static_cast<Type>(type_i); 104 total += bit::count(board.get_bitboard_piece(type)) * score::get(type); 105 } 106 return total; 107 } 108 109 int16_t score_position_side(const Board &board, const Color side, const uint16_t phase_score) { 110 using score::Phase::ENDGAME; 111 using score::Phase::OPENING; 112 113 U64 bitboard; 114 int8_t square_i; 115 116 const U64 pawns = board.get_bitboard_piece(PAWN); 117 const U64 pawnsS = board.get_bitboard_piece(PAWN, side); 118 const U64 pawnsO = pawns & ~pawnsS; 119 120 int16_t total = 0, opening = 0, endgame = 0; 121 122 const U32 hash = board.get_hash_pawn(); 123 const U32 key = ptable.read(hash, side); 124 if (key == std::numeric_limits<uint32_t>::max()) { 125 bitboard = board.get_bitboard_piece(PAWN, side); 126 bitboard_for_each_bit(square_i, bitboard) { 127 const auto square = static_cast<Square>(square_i); 128 opening += score::get(PAWN, side, square, OPENING) + score::get(PAWN, OPENING); 129 endgame += score::get(PAWN, side, square, ENDGAME) + score::get(PAWN, ENDGAME); 130 131 // check isolated, doubled and passed pawns 132 const uint8_t file = get_file(square), rank = get_rank(square); 133 if (!(mask_isolated[file] & pawnsS)) { 134 opening -= score::pawn_isolated_opening; 135 endgame -= score::pawn_isolated_endgame; 136 } 137 138 if (bit::count(pawnsS & mask_file[file]) > 1) { 139 opening -= score::pawn_double_opening; 140 endgame -= score::pawn_double_endgame; 141 } 142 143 if (!(pawnsO & mask_passed[side][square_i])) total += score::pawn_passed[side][rank]; 144 } 145 146 ptable.write(hash, side, opening, endgame, total); 147 } else { 148 opening = ptable.read_opening(key); 149 endgame = ptable.read_endgame(key); 150 total = ptable.read_total(key); 151 } 152 153 bitboard = board.get_bitboard_piece(KNIGHT, side); 154 bitboard_for_each_bit(square_i, bitboard) { 155 const auto square = static_cast<Square>(square_i); 156 opening += score::get(KNIGHT, side, square, OPENING) + score::get(KNIGHT, OPENING); 157 endgame += score::get(KNIGHT, side, square, ENDGAME) + score::get(KNIGHT, ENDGAME); 158 } 159 160 bitboard = board.get_bitboard_piece(BISHOP, side); 161 bitboard_for_each_bit(square_i, bitboard) { 162 const auto square = static_cast<Square>(square_i); 163 opening += score::get(BISHOP, side, square, OPENING) + score::get(BISHOP, OPENING); 164 endgame += score::get(BISHOP, side, square, ENDGAME) + score::get(BISHOP, ENDGAME); 165 } 166 167 bitboard = board.get_bitboard_piece(ROOK, side); 168 bitboard_for_each_bit(square_i, bitboard) { 169 const auto square = static_cast<Square>(square_i); 170 opening += score::get(ROOK, side, square, OPENING) + score::get(ROOK, OPENING); 171 endgame += score::get(ROOK, side, square, ENDGAME) + score::get(ROOK, ENDGAME); 172 173 // rook on open and semi-open files 174 const uint8_t file = get_file(square); 175 if (!(pawns & mask_file[file])) total += score::file_open; 176 if (!(pawnsS & mask_file[file])) total += score::file_open_semi; 177 } 178 179 bitboard = board.get_bitboard_piece(QUEEN, side); 180 bitboard_for_each_bit(square_i, bitboard) { 181 const auto square = static_cast<Square>(square_i); 182 opening += score::get(QUEEN, side, square, OPENING) + score::get(QUEEN, OPENING); 183 endgame += score::get(QUEEN, side, square, ENDGAME) + score::get(QUEEN, ENDGAME); 184 } 185 186 bitboard = board.get_bitboard_piece(KING, side); 187 bitboard_for_each_bit(square_i, bitboard) { 188 const auto square = static_cast<Square>(square_i); 189 opening += score::get(KING, side, square, OPENING) + score::get(KING, OPENING); 190 endgame += score::get(KING, side, square, ENDGAME) + score::get(KING, ENDGAME); 191 192 // king on open and semi-open files 193 const uint8_t file = get_file(square); 194 if (!(pawns & mask_file[file])) total -= score::file_open; 195 if (!(pawnsS & mask_file[file])) total -= score::file_open_semi; 196 } 197 198 if (phase_score > score::phase_opening) return opening + total; 199 if (phase_score < score::phase_endgame) return endgame + total; 200 return score::interpolate(phase_score, opening, endgame) + total; 201 } 202 203 int16_t score_position(const Board &board) { 204 const uint16_t phase_score = score_game_phase(board); 205 const int16_t score = 206 score_position_side(board, WHITE, phase_score) - score_position_side(board, BLACK, phase_score); 207 return board.get_side() == WHITE ? score : -score; 208 } 209 210 } // namespace evaluate