commit 0218a1559240963bdc53c7157b1cb16cfa4878f0
parent 00388d4c00764e6a1f68f488b2c3a20dec31baf0
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date: Tue, 19 Mar 2024 20:29:33 +0000
Pawn hash table
Diffstat:
7 files changed, 120 insertions(+), 46 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(
Stellar
- VERSION 1.3.7
+ VERSION 1.3.8
DESCRIPTION "Chess engine written in C++"
HOMEPAGE_URL https://git.dimitrijedobrota.com/stellar.git
LANGUAGES CXX
diff --git a/src/board/board.cpp b/src/board/board.cpp
@@ -12,6 +12,7 @@
namespace zobrist {
/* Init arrays for Zobris hashing */
+U32 keys_pawn[2][64] = {0};
U64 keys_piece[2][12][64] = {0};
U64 keys_enpassant[64] = {0};
U64 keys_castle[16] = {0};
@@ -57,6 +58,7 @@ Board::Board(const std::string &fen) {
enpassant = fen[++i] != '-' ? from_coordinates(fen.substr(i, 2)) : Square::no_sq;
+ hash_pawn = zobrist::hash_pawn(*this);
hash = zobrist::hash(*this);
}
diff --git a/src/board/board.hpp b/src/board/board.hpp
@@ -28,10 +28,11 @@ class Board {
/* Getters */
- [[nodiscard]] inline constexpr U64 get_hash() const;
- [[nodiscard]] inline constexpr Color get_side() const;
- [[nodiscard]] inline constexpr uint8_t get_castle() const;
- [[nodiscard]] inline constexpr Square get_enpassant() const;
+ [[nodiscard]] inline constexpr U64 get_hash() const { return hash; }
+ [[nodiscard]] inline constexpr Color get_side() const { return side; }
+ [[nodiscard]] inline constexpr uint8_t get_castle() const { return castle; }
+ [[nodiscard]] inline constexpr Square get_enpassant() const { return enpassant; }
+ [[nodiscard]] inline constexpr U64 get_hash_pawn() const { return hash_pawn; }
[[nodiscard]] inline constexpr U64 get_bitboard_color(Color side) const;
[[nodiscard]] inline constexpr U64 get_bitboard_occupancy() const;
@@ -49,6 +50,8 @@ class Board {
/* Setters */
inline constexpr void xor_hash(U64 op);
+ inline constexpr void xor_hash_pawn(U32 op);
+
inline constexpr void switch_side();
inline constexpr void and_castle(uint8_t right);
inline constexpr void set_enpassant(Square target);
@@ -74,16 +77,12 @@ class Board {
U64 colors[2] = {0};
U64 pieces[6] = {0};
U64 hash = 0;
+ U32 hash_pawn = 0;
Color side = WHITE;
Square enpassant = Square::no_sq;
uint8_t castle = 0;
};
-constexpr Color Board::get_side() const { return side; }
-constexpr U64 Board::get_hash() const { return hash; }
-constexpr uint8_t Board::get_castle() const { return castle; }
-constexpr Square Board::get_enpassant() const { return enpassant; }
-
constexpr U64 Board::get_bitboard_color(Color side) const { return colors[side]; }
constexpr U64 Board::get_bitboard_occupancy() const { return colors[WHITE] | colors[BLACK]; }
constexpr U64 Board::get_bitboard_piece(Type piece) const { return pieces[piece]; }
@@ -122,6 +121,8 @@ constexpr Type Board::get_square_piece_type(Square square) const {
/* Setters */
constexpr void Board::xor_hash(U64 op) { hash ^= op; }
+constexpr void Board::xor_hash_pawn(U32 op) { hash_pawn ^= op; }
+
constexpr void Board::and_castle(uint8_t right) {
hash ^= zobrist::key_castle(castle);
castle &= right;
@@ -203,4 +204,17 @@ U64 zobrist::hash(const Board &board) {
return key_final;
}
+U32 zobrist::hash_pawn(const Board &board) {
+ U32 key_final = C32(0);
+ uint8_t square = 0;
+
+ U64 bitboard_white = board.get_bitboard_piece(PAWN, WHITE);
+ bitboard_for_each_bit(square, bitboard_white) { key_final ^= keys_pawn[WHITE][square]; }
+
+ U64 bitboard_black = board.get_bitboard_piece(PAWN, BLACK);
+ bitboard_for_each_bit(square, bitboard_black) { key_final ^= keys_pawn[BLACK][square]; }
+
+ return key_final;
+}
+
#endif
diff --git a/src/board/zobrist.hpp b/src/board/zobrist.hpp
@@ -11,6 +11,7 @@
class Board;
namespace zobrist {
+extern U32 keys_pawn[2][64];
extern U64 keys_piece[2][12][64];
extern U64 keys_enpassant[64];
extern U64 keys_castle[16];
@@ -35,12 +36,22 @@ inline void init() {
for (int castle = 0; castle < 16; castle++) {
keys_castle[castle] = gen3();
}
+
+ Random gen4(C32(3642040919));
+ for (int c = 0; c < 2; c++) {
+ for (int square = 0; square < 64; square++) {
+ keys_pawn[c][square] = gen4.get_U32();
+ }
+ }
};
inline U64 hash(const Board &board);
+inline U32 hash_pawn(const Board &board);
+
inline constexpr U64 key_side() { return keys_side; }
inline constexpr U64 key_castle(int exp) { return keys_castle[exp]; }
inline constexpr U64 key_enpassant(Square square) { return keys_enpassant[square]; }
+inline constexpr U64 key_pawn(Color color, Square square) { return keys_pawn[color][square]; }
inline constexpr U64 key_piece(Type type, Color color, Square square) {
return keys_piece[color][type][square];
}
diff --git a/src/engine/evaluate.cpp b/src/engine/evaluate.cpp
@@ -6,6 +6,7 @@
#include "utils.hpp"
#include <array>
+#include <vector>
namespace evaluate {
@@ -63,8 +64,38 @@ inline constexpr const mask_passed_array mask_passed = []() constexpr -> mask_pa
return mask_passed;
}();
-using score::Phase::ENDGAME;
-using score::Phase::OPENING;
+struct Hashe {
+ U32 key;
+ int16_t opening;
+ int16_t endgame;
+ int16_t total;
+};
+
+template <U32 SIZE> struct PTable {
+
+ static void clear() { memset(table, 0x00, SIZE * sizeof(Hashe)); }
+
+ [[nodiscard]] static inline U32 read(U32 hash, Color side) {
+ const U32 key = side * SIZE + hash % SIZE;
+ const Hashe &phashe = table[key];
+ return phashe.key == hash ? key : __UINT32_MAX__;
+ }
+
+ [[nodiscard]] static inline int16_t read_opening(U32 key) { return table[key].opening; }
+ [[nodiscard]] static inline int16_t read_endgame(U32 key) { return table[key].endgame; }
+ [[nodiscard]] static inline int16_t read_total(U32 key) { return table[key].total; }
+
+ static inline void write(U32 hash, Color side, int16_t opening, int16_t endgame, int16_t total) {
+ table[side * SIZE + hash % SIZE] = {hash, opening, endgame, total};
+ }
+
+ private:
+ static std::array<Hashe, 2 * SIZE> table;
+};
+
+template <U32 SIZE> std::array<Hashe, 2 * SIZE> PTable<SIZE>::table = {{{0}}};
+
+PTable<65537> ptable;
uint16_t score_game_phase(const Board &board) {
int16_t total = 0;
@@ -76,34 +107,47 @@ uint16_t score_game_phase(const Board &board) {
}
int16_t score_position_side(const Board &board, const Color side, const uint16_t phase_score) {
- U64 bitboard;
+ using score::Phase::ENDGAME;
+ using score::Phase::OPENING;
- int16_t total = 0, opening = 0, endgame = 0;
+ U64 bitboard;
int8_t square_i;
const U64 pawns = board.get_bitboard_piece(PAWN);
const U64 pawnsS = board.get_bitboard_piece(PAWN, side);
const U64 pawnsO = pawns & ~pawnsS;
- bitboard = board.get_bitboard_piece(PAWN, side);
- bitboard_for_each_bit(square_i, bitboard) {
- const auto square = static_cast<Square>(square_i);
- opening += score::get(PAWN, side, square, OPENING) + score::get(PAWN, OPENING);
- endgame += score::get(PAWN, side, square, ENDGAME) + score::get(PAWN, ENDGAME);
-
- // check isolated, doubled and passed pawns
- const uint8_t file = get_file(square), rank = get_rank(square);
- if (!(mask_isolated[file] & pawnsS)) {
- opening -= score::pawn_isolated_opening;
- endgame -= score::pawn_isolated_endgame;
- }
+ int16_t total = 0, opening = 0, endgame = 0;
- if (bit::count(pawnsS & mask_file[file]) > 1) {
- opening -= score::pawn_double_opening;
- endgame -= score::pawn_double_endgame;
+ const U32 hash = board.get_hash_pawn();
+ const U32 key = ptable.read(hash, side);
+ if (key == __UINT32_MAX__) {
+ bitboard = board.get_bitboard_piece(PAWN, side);
+ bitboard_for_each_bit(square_i, bitboard) {
+ const auto square = static_cast<Square>(square_i);
+ opening += score::get(PAWN, side, square, OPENING) + score::get(PAWN, OPENING);
+ endgame += score::get(PAWN, side, square, ENDGAME) + score::get(PAWN, ENDGAME);
+
+ // check isolated, doubled and passed pawns
+ const uint8_t file = get_file(square), rank = get_rank(square);
+ if (!(mask_isolated[file] & pawnsS)) {
+ opening -= score::pawn_isolated_opening;
+ endgame -= score::pawn_isolated_endgame;
+ }
+
+ if (bit::count(pawnsS & mask_file[file]) > 1) {
+ opening -= score::pawn_double_opening;
+ endgame -= score::pawn_double_endgame;
+ }
+
+ if (!(pawnsO & mask_passed[side][square_i])) total += score::pawn_passed[side][rank];
}
- if (!(pawnsO & mask_passed[side][square_i])) total += score::pawn_passed[side][rank];
+ ptable.write(hash, side, opening, endgame, total);
+ } else {
+ opening = ptable.read_opening(key);
+ endgame = ptable.read_endgame(key);
+ total = ptable.read_total(key);
}
bitboard = board.get_bitboard_piece(KNIGHT, side);
@@ -151,10 +195,9 @@ int16_t score_position_side(const Board &board, const Color side, const uint16_t
if (!(pawnsS & mask_file[file])) total -= score::file_open_semi;
}
- opening += total, endgame += total;
- if (phase_score > score::phase_opening) return opening;
- if (phase_score < score::phase_endgame) return endgame;
- return score::interpolate(phase_score, opening, endgame);
+ if (phase_score > score::phase_opening) return opening + total;
+ if (phase_score < score::phase_endgame) return endgame + total;
+ return score::interpolate(phase_score, opening, endgame) + total;
}
int16_t score_position(const Board &board) {
diff --git a/src/move/move.cpp b/src/move/move.cpp
@@ -7,12 +7,16 @@
void Move::piece_remove(Board &board, Type type, Color color, Square square) const {
board.pop_piece(type, color, square);
+
board.xor_hash(zobrist::key_piece(type, color, square));
+ if (type == PAWN) board.xor_hash_pawn(zobrist::key_pawn(color, square));
}
void Move::piece_set(Board &board, Type type, Color color, Square square) const {
board.set_piece(type, color, square);
+
board.xor_hash(zobrist::key_piece(type, color, square));
+ if (type == PAWN) board.xor_hash_pawn(zobrist::key_pawn(color, square));
}
void Move::piece_move(Board &board, Type type, Color color, Square source, Square target) const {
diff --git a/src/move/move.hpp b/src/move/move.hpp
@@ -34,23 +34,23 @@ struct Move {
return a.source_i == b.source_i && a.target_i == b.target_i && a.flags_i == b.flags_i;
}
- [[nodiscard]] Square source() const { return static_cast<Square>(source_i); }
- [[nodiscard]] Square target() const { return static_cast<Square>(target_i); }
+ [[nodiscard]] constexpr Square source() const { return static_cast<Square>(source_i); }
+ [[nodiscard]] constexpr Square target() const { return static_cast<Square>(target_i); }
- [[nodiscard]] bool is_capture() const { return flags_i != PQUIET && (flags_i & CAPTURE); }
- [[nodiscard]] bool is_promote() const { return flags_i & 0x8; }
+ [[nodiscard]] constexpr bool is_capture() const { return flags_i != PQUIET && (flags_i & CAPTURE); }
+ [[nodiscard]] constexpr bool is_promote() const { return flags_i & 0x8; }
- [[nodiscard]] bool is_double() const { return flags_i == DOUBLE; }
- [[nodiscard]] bool is_repeatable() const { return flags_i == QUIET; }
- [[nodiscard]] bool is_quiet() const { return flags_i == QUIET || flags_i == PQUIET; }
+ [[nodiscard]] constexpr bool is_double() const { return flags_i == DOUBLE; }
+ [[nodiscard]] constexpr bool is_repeatable() const { return flags_i == QUIET; }
+ [[nodiscard]] constexpr bool is_quiet() const { return flags_i == QUIET || flags_i == PQUIET; }
- [[nodiscard]] bool is_castle() const { return flags_i == CASTLEK || flags_i == CASTLEQ; }
- [[nodiscard]] bool is_castle_king() const { return flags_i == CASTLEK; }
- [[nodiscard]] bool is_castle_queen() const { return flags_i == CASTLEQ; }
+ [[nodiscard]] constexpr bool is_castle() const { return flags_i == CASTLEK || flags_i == CASTLEQ; }
+ [[nodiscard]] constexpr bool is_castle_king() const { return flags_i == CASTLEK; }
+ [[nodiscard]] constexpr bool is_castle_queen() const { return flags_i == CASTLEQ; }
- [[nodiscard]] bool is_enpassant() const { return flags_i == ENPASSANT; }
+ [[nodiscard]] constexpr bool is_enpassant() const { return flags_i == ENPASSANT; }
- [[nodiscard]] const Type promoted() const { return static_cast<Type>((flags_i & 0x3) + 1); }
+ [[nodiscard]] constexpr const Type promoted() const { return static_cast<Type>((flags_i & 0x3) + 1); }
bool make(Board &board) const;