Skip to content

Commit

Permalink
suggestion: incremental hash calculation (#24)
Browse files Browse the repository at this point in the history
* check-options require "false" or "true" as value, not 1/0

* compile fix & output true/false strings

* Compile xfix

* (partial) incremental hash calculation

* hashing test

* basic incremental hash update fixes

* returned check

* null move incremental hashing

* reset en-passant field from hash after next move

* double push

* double push (now for real)

* libraries should have debug info, also in release

* micro optimalization
  • Loading branch information
folkertvanheusden committed Apr 17, 2023
1 parent d3081d2 commit 35e0b5e
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 26 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Expand Up @@ -6,10 +6,10 @@ project(libchess)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "-Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g -fno-omit-frame-pointer")
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -O3")
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -Ofast")

if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif ()

enable_testing()
Expand Down
39 changes: 20 additions & 19 deletions Position.h
Expand Up @@ -127,6 +127,26 @@ class Position {
static std::optional<Position> from_fen(const std::string& fen);
static std::optional<Position> from_uci_position_line(const std::string& line);

hash_type calculate_hash() const {
hash_type hash_value = 0;
for (Color c : constants::COLORS) {
for (PieceType pt : constants::PIECE_TYPES) {
Bitboard bb = piece_type_bb(pt, c);
while (bb) {
hash_value ^= zobrist::piece_square_key(bb.forward_bitscan(), pt, c);
bb.forward_popbit();
}
}
}
auto ep_sq = enpassant_square();
if (ep_sq) {
hash_value ^= zobrist::enpassant_key(*ep_sq);
}
hash_value ^= zobrist::castling_rights_key(castling_rights());
hash_value ^= zobrist::side_to_move_key(side_to_move());
return hash_value;
}

protected:
// clang-format off
constexpr static int castling_spoilers[64] = {
Expand Down Expand Up @@ -170,25 +190,6 @@ class Position {
const State& state(int ply) const {
return history_[ply];
}
hash_type calculate_hash() const {
hash_type hash_value = 0;
for (Color c : constants::COLORS) {
for (PieceType pt : constants::PIECE_TYPES) {
Bitboard bb = piece_type_bb(pt, c);
while (bb) {
hash_value ^= zobrist::piece_square_key(bb.forward_bitscan(), pt, c);
bb.forward_popbit();
}
}
}
auto ep_sq = enpassant_square();
if (ep_sq) {
hash_value ^= zobrist::enpassant_key(*ep_sq);
}
hash_value ^= zobrist::castling_rights_key(castling_rights());
hash_value ^= zobrist::side_to_move_key(side_to_move());
return hash_value;
}
hash_type calculate_pawn_hash() const {
hash_type hash_value = 0;
for (Color c : constants::COLORS) {
Expand Down
36 changes: 32 additions & 4 deletions Position/MoveIntegration.h
Expand Up @@ -156,18 +156,34 @@ inline void Position::make_move(Move move) {
next_state.halfmoves_ = 0;
}

bool calc_hash = true;

switch (move_type) {
case Move::Type::NORMAL:
move_piece(from_square, to_square, *moving_pt, stm);
calc_hash = false;
next_state.hash_ = prev_state.hash_;
next_state.hash_ ^= zobrist::piece_square_key(from_square, *moving_pt, stm);
next_state.hash_ ^= zobrist::piece_square_key(to_square, *moving_pt, stm);
break;
case Move::Type::CAPTURE:
remove_piece(to_square, *captured_pt, !stm);
move_piece(from_square, to_square, *moving_pt, stm);
calc_hash = false;
next_state.hash_ = prev_state.hash_;
next_state.hash_ ^= zobrist::piece_square_key(to_square, *captured_pt, !stm);
next_state.hash_ ^= zobrist::piece_square_key(from_square, *moving_pt, stm);
next_state.hash_ ^= zobrist::piece_square_key(to_square, *moving_pt, stm);
break;
case Move::Type::DOUBLE_PUSH:
move_piece(from_square, to_square, constants::PAWN, stm);
calc_hash = false;
next_state.hash_ = prev_state.hash_;
next_state.enpassant_square_ =
stm == constants::WHITE ? Square(from_square + 8) : Square(from_square - 8);
next_state.hash_ ^= zobrist::enpassant_key(next_state.enpassant_square_.value());
next_state.hash_ ^= zobrist::piece_square_key(from_square, constants::PAWN, stm);
next_state.hash_ ^= zobrist::piece_square_key(to_square, constants::PAWN, stm);
break;
case Move::Type::ENPASSANT:
move_piece(from_square, to_square, constants::PAWN, stm);
Expand Down Expand Up @@ -209,12 +225,20 @@ inline void Position::make_move(Move move) {
next_state.captured_pt_ = captured_pt;
next_state.move_type_ = move_type;
reverse_side_to_move();
next_state.hash_ = calculate_hash();
if (calc_hash)
next_state.hash_ = calculate_hash();
else {
if (prev_state.enpassant_square_.has_value())
next_state.hash_ ^= zobrist::enpassant_key(prev_state.enpassant_square_.value());
next_state.hash_ ^= zobrist::side_to_move_key(!stm);
next_state.hash_ ^= zobrist::side_to_move_key(stm);
}
next_state.pawn_hash_ = calculate_pawn_hash();
}

inline void Position::make_null_move() {
if (side_to_move() == constants::BLACK) {
Color stm = side_to_move();
if (stm == constants::BLACK) {
++fullmoves_;
}
++ply_;
Expand All @@ -224,9 +248,13 @@ inline void Position::make_null_move() {
reverse_side_to_move();
next.previous_move_ = {};
next.halfmoves_ = prev.halfmoves_ + 1;
next.enpassant_square_ = {};
next.castling_rights_ = prev.castling_rights_;
next.hash_ = calculate_hash();
next.hash_ = prev.hash_;
if (prev.enpassant_square_.has_value())
next.hash_ ^= zobrist::enpassant_key(prev.enpassant_square_.value());
next.hash_ ^= zobrist::side_to_move_key(!stm);
next.hash_ ^= zobrist::side_to_move_key(stm);
next.enpassant_square_ = {};
next.pawn_hash_ = calculate_pawn_hash();
}

Expand Down
2 changes: 1 addition & 1 deletion Tuner.h
Expand Up @@ -48,7 +48,7 @@ class TunableParameter {
}

private:
std::string name_;
const std::string name_;
int value_;
};

Expand Down
1 change: 1 addition & 0 deletions UCIOption.h
Expand Up @@ -2,6 +2,7 @@
#define LIBCHESS_UCIOPTION_H

#include <functional>
#include <optional>
#include <string>
#include <unordered_map>
#include <unordered_set>
Expand Down
21 changes: 21 additions & 0 deletions tests/PositionTests.cpp
Expand Up @@ -22,6 +22,7 @@ TEST_CASE("Null Move Test", "[Position]") {
REQUIRE(!pos.previous_move());
REQUIRE(pos.halfmoves() == old_halfmoves + 1);
REQUIRE(pos.hash() != old_hash);
REQUIRE(pos.hash() == pos.calculate_hash());

pos.unmake_move();

Expand All @@ -39,8 +40,28 @@ TEST_CASE("Hash Test", "[Position]") {

pos.make_move({E2, E4});
REQUIRE(pos.hash() != old_hash);
REQUIRE(pos.hash() == pos.calculate_hash());
pos.unmake_move();
REQUIRE(pos.hash() == old_hash);

pos.make_move({E2, E3});
REQUIRE(pos.hash() != old_hash);
REQUIRE(pos.hash() == pos.calculate_hash());
pos.unmake_move();
REQUIRE(pos.hash() == old_hash);

pos.make_move({B1, C3});
REQUIRE(pos.hash() != old_hash);
REQUIRE(pos.hash() == pos.calculate_hash());
pos.make_move({B8, C6});
REQUIRE(pos.hash() != old_hash);
REQUIRE(pos.hash() == pos.calculate_hash());
pos.make_move({C3, B1});
REQUIRE(pos.hash() != old_hash);
REQUIRE(pos.hash() == pos.calculate_hash());
pos.make_move({C6, B8});
REQUIRE(pos.hash() == old_hash);
REQUIRE(pos.hash() == pos.calculate_hash());
}

TEST_CASE("Repetition Test", "[Position]") {
Expand Down

0 comments on commit 35e0b5e

Please sign in to comment.