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"
7 #include <array>
8 #include <vector>
9 #include <numeric>
11 namespace evaluate {
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 }();
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 }();
34 inline constexpr const mask_fr_array mask_isolated = []() constexpr -> mask_fr_array {
35 mask_fr_array mask_isolated;
37 mask_isolated[0] = 0x0202020202020202;
39 U64 mask = 0x0505050505050505;
40 for (uint8_t file = 1; file < 8; file++) {
41 mask_isolated[file] = mask;
42 mask = bitboard::eastOne(mask);
43 }
45 return mask_isolated;
46 }();
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;
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;
59 maskB = bitboard::soutOne(maskB);
60 mask_passed[1][(7 - rank) * 8 + file] = maskB;
61 }
62 }
64 return mask_passed;
65 }();
67 struct Hashe {
68 U32 key;
69 int16_t opening;
70 int16_t endgame;
71 int16_t total;
72 };
74 template <U32 SIZE> struct PTable {
76 static void clear() { memset(table, 0x00, SIZE * sizeof(Hashe)); }
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 }
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; }
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 }
92 private:
93 static std::array<Hashe, 2 * SIZE> table;
94 };
96 template <U32 SIZE> std::array<Hashe, 2 * SIZE> PTable<SIZE>::table = {{{0}}};
98 PTable<65537> ptable;
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);
106 return total;
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;
113 U64 bitboard;
114 int8_t square_i;
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;
120 int16_t total = 0, opening = 0, endgame = 0;
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);
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;
138 if (bit::count(pawnsS & mask_file[file]) > 1) {
139 opening -= score::pawn_double_opening;
140 endgame -= score::pawn_double_endgame;
143 if (!(pawnsO & mask_passed[side][square_i])) total += score::pawn_passed[side][rank];
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);
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);
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);
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);
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;
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);
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);
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;
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;
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;
210 } // namespace evaluate