stellar

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

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