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