From 3a23ad68e6f3b4216be05bb4fe2db34468577c31 Mon Sep 17 00:00:00 2001 From: henrique Date: Wed, 3 Aug 2022 15:49:38 +0100 Subject: [PATCH 01/22] added generate hash key --- src/engine/movepicker/zobrist.cpp | 37 +++++++++++++++++++++++++++++++ src/engine/movepicker/zobrist.hpp | 4 ++++ 2 files changed, 41 insertions(+) diff --git a/src/engine/movepicker/zobrist.cpp b/src/engine/movepicker/zobrist.cpp index 51b338a..dee5af9 100644 --- a/src/engine/movepicker/zobrist.cpp +++ b/src/engine/movepicker/zobrist.cpp @@ -44,4 +44,41 @@ namespace zobrist side_key = generate_random_number_u64(); } + u64 generate_hash_key(const Board &board) + { + u64 final_key = 0ULL; + + for (int piece = PAWN; piece < N_PIECES; piece++) + { + for (int side = WHITE; side < BOTH; side++) + { + u64 bitboard = board.get_pieces(side, piece); + + while (bitboard) + { + int sq = bitboard::bit_scan_forward(bitboard); + + final_key ^= piece_keys[side][piece][sq]; + + bitboard::pop_bit(bitboard, sq); + } + } + } + + if (board.get_en_passant_square() != EMPTY_SQUARE) + { + final_key ^= en_passant_keys[board.get_en_passant_square()]; + } + + final_key ^= castle_keys[board.get_castling_rights()]; + + if (board.get_side_to_move() == BLACK) + { + final_key ^= side_key; + } + // final_key ^= board.get_side_to_move() == WHITE ? 0ULL : side_key; + + return final_key; + } + } // namespace zobrist diff --git a/src/engine/movepicker/zobrist.hpp b/src/engine/movepicker/zobrist.hpp index 125b182..0655726 100644 --- a/src/engine/movepicker/zobrist.hpp +++ b/src/engine/movepicker/zobrist.hpp @@ -1,9 +1,13 @@ #pragma once #include +#include +#include namespace zobrist { void init(); + u64 generate_hash_key(const Board &board); + } // namespace zobrist From 409e4ec42e889677ac46d91dd5a0f738f7ccf56c Mon Sep 17 00:00:00 2001 From: henrique Date: Fri, 5 Aug 2022 15:50:39 +0100 Subject: [PATCH 02/22] hash key update and tests --- src/engine/board.cpp | 64 +++++++++++++++++-- src/engine/board.hpp | 5 ++ src/engine/movepicker/zobrist.cpp | 27 ++++---- src/engine/movepicker/zobrist.hpp | 5 ++ tests/movepicker/test_zobrist.cpp | 102 ++++++++++++++++++++++++++++++ 5 files changed, 185 insertions(+), 18 deletions(-) create mode 100644 tests/movepicker/test_zobrist.cpp diff --git a/src/engine/board.cpp b/src/engine/board.cpp index 6260618..c2594a0 100644 --- a/src/engine/board.cpp +++ b/src/engine/board.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -21,6 +22,7 @@ Board::Board(const Board &board) _en_passant_square = board._en_passant_square; _half_move_clock = board._half_move_clock; _full_move_number = board._full_move_number; + _hash_key = board._hash_key; _ascii = board._ascii; _white_on_bottom = board._white_on_bottom; @@ -110,6 +112,16 @@ int Board::get_full_move_number() const return _full_move_number; } +u64 Board::calculate_hash_key() +{ + return zobrist::generate_hash_key(*this); +} + +u64 Board::get_hash_key() +{ + return _hash_key; +} + Board::Piece Board::get_piece_from_square(int sq) const { return _square[sq]; @@ -233,7 +245,7 @@ std::string Board::get_fen() const struct Board::GameState Board::get_state() const { - return GameState{_en_passant_square, _castling_rights, _half_move_clock}; + return GameState{_en_passant_square, _castling_rights, _half_move_clock, _hash_key}; } void Board::set_en_passant_square(int sq) @@ -251,6 +263,7 @@ void Board::set_state(GameState state) _en_passant_square = state.en_passant_square; _castling_rights = state.castling_rights; _half_move_clock = state.half_move_clock; + _hash_key = state.hash_key; } void Board::display() const @@ -321,6 +334,7 @@ void Board::clear() _en_passant_square = EMPTY_SQUARE; _half_move_clock = 0; _full_move_number = 0; + _hash_key = 0ULL; // replace with original _white_on_bottom = true; @@ -466,6 +480,8 @@ void Board::set_from_fen(const std::string &piece_placements, _full_move_number = std::stoi(full_move_number); this->update_bb_from_squares(); + + _hash_key = this->calculate_hash_key(); } int Board::switch_side_to_move() @@ -505,27 +521,42 @@ void Board::make_move(const Move move) _square[from_square].type = EMPTY_PIECE; _square[from_square].color = BLACK; + // remove from hash key moved piece + _hash_key ^= zobrist::piece_keys[_to_move][piece][from_square]; + if (is_en_passant) { int captured_piece_square = to_square + pawn_push_en_passant_offset; _square[captured_piece_square].type = EMPTY_PIECE; _square[captured_piece_square].color = BLACK; bitboard::pop_bit(_pieces[this->get_opponent()][PAWN], captured_piece_square); + + // remove from hash key captured pawn + _hash_key ^= zobrist::piece_keys[this->get_opponent()][PAWN][captured_piece_square]; } else if (is_capture) { bitboard::pop_bit(_pieces[this->get_opponent()][captured_piece], to_square); + + // remove from hash key captured piece + _hash_key ^= zobrist::piece_keys[this->get_opponent()][captured_piece][to_square]; } if (is_promotion) { _square[to_square].type = promoted_piece; bitboard::set_bit(_pieces[_to_move][promoted_piece], to_square); + + // add to hash key promoted piece + _hash_key ^= zobrist::piece_keys[_to_move][promoted_piece][to_square]; } else { _square[to_square].type = piece; bitboard::set_bit(_pieces[_to_move][piece], to_square); + + // add to hash key moved piece + _hash_key ^= zobrist::piece_keys[_to_move][piece][to_square]; } _square[to_square].color = _to_move; @@ -550,13 +581,31 @@ void Board::make_move(const Move move) _square[rook_to_square].color = _to_move; bitboard::pop_bit(_pieces[_to_move][ROOK], rook_from_square); + // remove from hash key rook + _hash_key ^= zobrist::piece_keys[_to_move][ROOK][rook_from_square]; bitboard::set_bit(_pieces[_to_move][ROOK], rook_to_square); + // add to hash key rook + _hash_key ^= zobrist::piece_keys[_to_move][ROOK][rook_to_square]; + } + + // remove from hash key castling rights and en passant + if (_en_passant_square != EMPTY_SQUARE) + { + _hash_key ^= zobrist::en_passant_keys[_en_passant_square]; } + _hash_key ^= zobrist::castle_keys[_castling_rights]; _en_passant_square = is_double_push ? to_square + pawn_push_en_passant_offset : -1; _castling_rights &= castling_rights[from_square]; _castling_rights &= castling_rights[to_square]; + // update hash key with castling rights and en passant + if (_en_passant_square != EMPTY_SQUARE) + { + _hash_key ^= zobrist::en_passant_keys[_en_passant_square]; + } + _hash_key ^= zobrist::castle_keys[_castling_rights]; + if (piece == PAWN || (is_capture)) { _half_move_clock = 0; @@ -571,11 +620,15 @@ void Board::make_move(const Move move) _full_move_number++; } + // update from hash key side to move + _hash_key ^= zobrist::side_key[BLACK]; this->switch_side_to_move(); + // _hash_key ^= zobrist::side_key[_to_move]; + this->update_occupancies(); } -void Board::unmake_move(const Move move, const GameState info_board) +void Board::unmake_move(const Move move, const GameState state) { this->switch_side_to_move(); @@ -651,9 +704,10 @@ void Board::unmake_move(const Move move, const GameState info_board) _full_move_number--; } - _en_passant_square = info_board.en_passant_square; - _castling_rights = info_board.castling_rights; - _half_move_clock = info_board.half_move_clock; + _en_passant_square = state.en_passant_square; + _castling_rights = state.castling_rights; + _half_move_clock = state.half_move_clock; + _hash_key = state.hash_key; this->update_occupancies(); } \ No newline at end of file diff --git a/src/engine/board.hpp b/src/engine/board.hpp index f75a874..90c687a 100644 --- a/src/engine/board.hpp +++ b/src/engine/board.hpp @@ -18,6 +18,7 @@ class Board int en_passant_square; int castling_rights; int half_move_clock; + u64 hash_key; }; private: @@ -26,6 +27,7 @@ class Board int _en_passant_square; int _half_move_clock; int _full_move_number; + u64 _hash_key; u64 _pieces[N_SIDES][N_PIECES]; u64 _occupancies[N_SIDES + 1]; @@ -61,6 +63,9 @@ class Board void set_fifty_move(int fifty_move); void set_state(GameState state); + u64 calculate_hash_key(); + u64 get_hash_key(); + void clear(); void set_starting_position(); void set_from_fen(const std::string &piece_placements, diff --git a/src/engine/movepicker/zobrist.cpp b/src/engine/movepicker/zobrist.cpp index dee5af9..d0b3a0d 100644 --- a/src/engine/movepicker/zobrist.cpp +++ b/src/engine/movepicker/zobrist.cpp @@ -2,14 +2,6 @@ #include -static u64 piece_keys[N_SIDES][N_PIECES][N_SQUARES]; - -static u64 en_passant_keys[N_SQUARES]; - -static u64 castle_keys[16]; - -static u64 side_key; - static u64 generate_random_number_u64() { u64 n1 = ((u64)std::rand()); @@ -19,6 +11,14 @@ static u64 generate_random_number_u64() namespace zobrist { + u64 piece_keys[N_SIDES][N_PIECES][N_SQUARES]; + + u64 en_passant_keys[N_SQUARES]; + + u64 castle_keys[16]; + + u64 side_key[BOTH] = {}; + void init() { @@ -41,7 +41,7 @@ namespace zobrist castle_keys[castle_state] = generate_random_number_u64(); } - side_key = generate_random_number_u64(); + side_key[1] = generate_random_number_u64(); } u64 generate_hash_key(const Board &board) @@ -72,11 +72,12 @@ namespace zobrist final_key ^= castle_keys[board.get_castling_rights()]; - if (board.get_side_to_move() == BLACK) - { - final_key ^= side_key; - } + // if (board.get_side_to_move() == BLACK) + // { + // final_key ^= side_key[1]; + // } // final_key ^= board.get_side_to_move() == WHITE ? 0ULL : side_key; + final_key ^= side_key[board.get_side_to_move()]; return final_key; } diff --git a/src/engine/movepicker/zobrist.hpp b/src/engine/movepicker/zobrist.hpp index 0655726..70f475f 100644 --- a/src/engine/movepicker/zobrist.hpp +++ b/src/engine/movepicker/zobrist.hpp @@ -8,6 +8,11 @@ namespace zobrist { void init(); + extern u64 piece_keys[N_SIDES][N_PIECES][N_SQUARES]; + extern u64 en_passant_keys[N_SQUARES]; + extern u64 castle_keys[16]; + extern u64 side_key[BOTH]; + u64 generate_hash_key(const Board &board); } // namespace zobrist diff --git a/tests/movepicker/test_zobrist.cpp b/tests/movepicker/test_zobrist.cpp new file mode 100644 index 0000000..f27d606 --- /dev/null +++ b/tests/movepicker/test_zobrist.cpp @@ -0,0 +1,102 @@ +#define CATCH_CONFIG_MAIN + +#include "../catch.hpp" + +#include + +#include +#include + +#include +#include + +void setup() +{ + magics::init(); + tables::init(); + zobrist::init(); +} + +TEST_CASE("hash_key") +{ + setup(); + + Board board = Board(); + SECTION("fen hash key") + { + board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R", "w", "KQkq", "-", "0", "1"); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + } + + SECTION("moves hash key") + { + board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R", "w", "KQkq", "-", "0", "1"); + Move move = Move(D5, D6, PAWN, EMPTY_PIECE, EMPTY_PIECE, false, false, false); + Board::GameState state = board.get_state(); + board.make_move(move); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(zobrist::generate_hash_key(board) == board.get_hash_key()); + board.unmake_move(move, state); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + } + + SECTION("capture hash key") + { + board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R", "w", "KQkq", "-", "0", "1"); + Move move = Move(F3, F6, QUEEN, KNIGHT, EMPTY_PIECE, false, false, false); + Board::GameState state = board.get_state(); + board.make_move(move); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(zobrist::generate_hash_key(board) == board.get_hash_key()); + board.unmake_move(move, state); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + } + + SECTION("double push hash key") + { + board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R", "w", "KQkq", "-", "0", "1"); + Move move = Move(A2, A4, PAWN, EMPTY_PIECE, EMPTY_PIECE, true, false, false); + Board::GameState state = board.get_state(); + board.make_move(move); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(zobrist::generate_hash_key(board) == board.get_hash_key()); + board.unmake_move(move, state); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + } + + SECTION("promotion hash key") + { + board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q2/PPPBBP1p/R3K1P1", "b", "KQkq", "-", "0", "1"); + Move move = Move(H2, H1, PAWN, EMPTY_PIECE, KNIGHT, false, false, false); + Board::GameState state = board.get_state(); + board.make_move(move); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(zobrist::generate_hash_key(board) == board.get_hash_key()); + board.unmake_move(move, state); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + } + + SECTION("promotion + capture hash key") + { + board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q2/PPPBBP1p/R3K1PR", "b", "KQkq", "-", "0", "1"); + Move move = Move(H2, G1, PAWN, PAWN, QUEEN, false, false, false); + Board::GameState state = board.get_state(); + board.make_move(move); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(zobrist::generate_hash_key(board) == board.get_hash_key()); + board.unmake_move(move, state); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + } + + SECTION("en passant hash key") + { + board.set_from_fen("rnbqkbnr/3ppppp/8/p1p5/2pP2P1/5N1B/PP2PP1P/RNBQK2R", "b", "KQkq", "d3", "0", "5"); + Move move = Move(C4, D3, PAWN, PAWN, EMPTY_PIECE, false, true, false); + Board::GameState state = board.get_state(); + board.make_move(move); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(zobrist::generate_hash_key(board) == board.get_hash_key()); + board.unmake_move(move, state); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + } +} \ No newline at end of file From 0a77fe7a6b3f6f73cb2993e3ebfd73de49231426 Mon Sep 17 00:00:00 2001 From: henrique Date: Tue, 9 Aug 2022 14:12:50 +0100 Subject: [PATCH 03/22] zobrist out of movegen and perft tests --- CMakeLists.txt | 2 +- src/engine/movegen/perft.cpp | 8 +++ src/engine/zobrist.cpp | 85 +++++++++++++++++++++++++++++++ src/engine/zobrist.hpp | 18 +++++++ tests/movepicker/test_zobrist.cpp | 32 ++++++++++++ 5 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/engine/zobrist.cpp create mode 100644 src/engine/zobrist.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e528214..f015f70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ project(chess_engine VERSION 1.0 DESCRIPTION "Chess Engine") set(CMAKE_CXX_FLAGS "-Wall -Wpedantic -Wextra -Wconversion -Werror") set(CMAKE_CXX_FLAGS_DEBUG "-g") -set(CMAKE_CXX_FLAGS_RELEASE "-O3") +# set(CMAKE_CXX_FLAGS_RELEASE "-O3") if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) diff --git a/src/engine/movegen/perft.cpp b/src/engine/movegen/perft.cpp index b03413b..b98c56e 100644 --- a/src/engine/movegen/perft.cpp +++ b/src/engine/movegen/perft.cpp @@ -19,6 +19,10 @@ namespace perft { Board::GameState board_info = board.get_state(); board.make_move(move); + if (board.calculate_hash_key() != board.get_hash_key()) + { + std::cout << "make: " << board.get_fen() << " " << move.get_uci() << std::endl; + } int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); int attacker_side = board.get_side_to_move(); if (!board.is_square_attacked(king_sq, attacker_side)) @@ -26,6 +30,10 @@ namespace perft nodes += perft(board, depth - 1); } board.unmake_move(move, board_info); + if (board.calculate_hash_key() != board.get_hash_key()) + { + std::cout << "unmake: " << board.get_fen() << " " << move.get_uci() << std::endl; + } } return nodes; diff --git a/src/engine/zobrist.cpp b/src/engine/zobrist.cpp new file mode 100644 index 0000000..b3d47ac --- /dev/null +++ b/src/engine/zobrist.cpp @@ -0,0 +1,85 @@ +#include + +#include + +static u64 generate_random_number_u64() +{ + u64 n1 = ((u64)std::rand()); + u64 n2 = ((u64)std::rand()); + return n1 | (n2 << 32); +} + +namespace zobrist +{ + u64 piece_keys[N_SIDES][N_PIECES][N_SQUARES]; + + u64 en_passant_keys[N_SQUARES]; + + u64 castle_keys[16]; + + u64 side_key[BOTH] = {}; + + void init() + { + + for (int piece = PAWN; piece < N_PIECES; piece++) + { + for (int square = A1; square < N_SQUARES; square++) + { + piece_keys[WHITE][piece][square] = generate_random_number_u64(); + piece_keys[BLACK][piece][square] = generate_random_number_u64(); + } + } + + for (int square = A1; square < N_SQUARES; square++) + { + en_passant_keys[square] = generate_random_number_u64(); + } + + for (int castle_state = 0; castle_state < 16; castle_state++) + { + castle_keys[castle_state] = generate_random_number_u64(); + } + + side_key[1] = generate_random_number_u64(); + } + + u64 generate_hash_key(const Board &board) + { + u64 final_key = 0ULL; + + for (int piece = PAWN; piece < N_PIECES; piece++) + { + for (int side = WHITE; side < BOTH; side++) + { + u64 bitboard = board.get_pieces(side, piece); + + while (bitboard) + { + int sq = bitboard::bit_scan_forward(bitboard); + + final_key ^= piece_keys[side][piece][sq]; + + bitboard::pop_bit(bitboard, sq); + } + } + } + + if (board.get_en_passant_square() != EMPTY_SQUARE) + { + final_key ^= en_passant_keys[board.get_en_passant_square()]; + } + + final_key ^= castle_keys[board.get_castling_rights()]; + + // if (board.get_side_to_move() == BLACK) + // { + // final_key ^= side_key[1]; + // } + // final_key ^= board.get_side_to_move() == WHITE ? 0ULL : side_key; + final_key ^= side_key[board.get_side_to_move()]; + + return final_key; + } + +} // namespace zobrist diff --git a/src/engine/zobrist.hpp b/src/engine/zobrist.hpp new file mode 100644 index 0000000..70f475f --- /dev/null +++ b/src/engine/zobrist.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +namespace zobrist +{ + void init(); + + extern u64 piece_keys[N_SIDES][N_PIECES][N_SQUARES]; + extern u64 en_passant_keys[N_SQUARES]; + extern u64 castle_keys[16]; + extern u64 side_key[BOTH]; + + u64 generate_hash_key(const Board &board); + +} // namespace zobrist diff --git a/tests/movepicker/test_zobrist.cpp b/tests/movepicker/test_zobrist.cpp index f27d606..0e4886a 100644 --- a/tests/movepicker/test_zobrist.cpp +++ b/tests/movepicker/test_zobrist.cpp @@ -9,6 +9,7 @@ #include #include +#include void setup() { @@ -17,6 +18,32 @@ void setup() zobrist::init(); } +int perft(Board &board, int depth) +{ + if (depth == 0) + { + return 1; + } + + int nodes = 0; + for (const Move &move : movegen::generate_pseudo_legal_moves(board)) + { + Board::GameState board_info = board.get_state(); + board.make_move(move); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); + int attacker_side = board.get_side_to_move(); + if (!board.is_square_attacked(king_sq, attacker_side)) + { + nodes += perft(board, depth - 1); + } + board.unmake_move(move, board_info); + REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + } + + return nodes; +} + TEST_CASE("hash_key") { setup(); @@ -99,4 +126,9 @@ TEST_CASE("hash_key") board.unmake_move(move, state); REQUIRE(board.calculate_hash_key() == board.get_hash_key()); } + + SECTION("perft hash key") + { + perft(board, 5); + } } \ No newline at end of file From 5a7e58d1302560b8fa45096fbf671460bd72eb39 Mon Sep 17 00:00:00 2001 From: henrique Date: Wed, 10 Aug 2022 15:57:08 +0100 Subject: [PATCH 04/22] zobrist in engine --- src/engine/board.cpp | 2 +- src/engine/movepicker/zobrist.cpp | 85 ---------------------------- src/engine/movepicker/zobrist.hpp | 18 ------ src/interfaces/cli/cli.cpp | 2 +- src/interfaces/uci/uci.cpp | 2 +- tests/movepicker/test_movepicker.cpp | 2 +- tests/movepicker/test_zobrist.cpp | 2 +- 7 files changed, 5 insertions(+), 108 deletions(-) delete mode 100644 src/engine/movepicker/zobrist.cpp delete mode 100644 src/engine/movepicker/zobrist.hpp diff --git a/src/engine/board.cpp b/src/engine/board.cpp index c2594a0..174818f 100644 --- a/src/engine/board.cpp +++ b/src/engine/board.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/engine/movepicker/zobrist.cpp b/src/engine/movepicker/zobrist.cpp deleted file mode 100644 index d0b3a0d..0000000 --- a/src/engine/movepicker/zobrist.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include - -#include - -static u64 generate_random_number_u64() -{ - u64 n1 = ((u64)std::rand()); - u64 n2 = ((u64)std::rand()); - return n1 | (n2 << 32); -} - -namespace zobrist -{ - u64 piece_keys[N_SIDES][N_PIECES][N_SQUARES]; - - u64 en_passant_keys[N_SQUARES]; - - u64 castle_keys[16]; - - u64 side_key[BOTH] = {}; - - void init() - { - - for (int piece = PAWN; piece < N_PIECES; piece++) - { - for (int square = A1; square < N_SQUARES; square++) - { - piece_keys[WHITE][piece][square] = generate_random_number_u64(); - piece_keys[BLACK][piece][square] = generate_random_number_u64(); - } - } - - for (int square = A1; square < N_SQUARES; square++) - { - en_passant_keys[square] = generate_random_number_u64(); - } - - for (int castle_state = 0; castle_state < 16; castle_state++) - { - castle_keys[castle_state] = generate_random_number_u64(); - } - - side_key[1] = generate_random_number_u64(); - } - - u64 generate_hash_key(const Board &board) - { - u64 final_key = 0ULL; - - for (int piece = PAWN; piece < N_PIECES; piece++) - { - for (int side = WHITE; side < BOTH; side++) - { - u64 bitboard = board.get_pieces(side, piece); - - while (bitboard) - { - int sq = bitboard::bit_scan_forward(bitboard); - - final_key ^= piece_keys[side][piece][sq]; - - bitboard::pop_bit(bitboard, sq); - } - } - } - - if (board.get_en_passant_square() != EMPTY_SQUARE) - { - final_key ^= en_passant_keys[board.get_en_passant_square()]; - } - - final_key ^= castle_keys[board.get_castling_rights()]; - - // if (board.get_side_to_move() == BLACK) - // { - // final_key ^= side_key[1]; - // } - // final_key ^= board.get_side_to_move() == WHITE ? 0ULL : side_key; - final_key ^= side_key[board.get_side_to_move()]; - - return final_key; - } - -} // namespace zobrist diff --git a/src/engine/movepicker/zobrist.hpp b/src/engine/movepicker/zobrist.hpp deleted file mode 100644 index 70f475f..0000000 --- a/src/engine/movepicker/zobrist.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace zobrist -{ - void init(); - - extern u64 piece_keys[N_SIDES][N_PIECES][N_SQUARES]; - extern u64 en_passant_keys[N_SQUARES]; - extern u64 castle_keys[16]; - extern u64 side_key[BOTH]; - - u64 generate_hash_key(const Board &board); - -} // namespace zobrist diff --git a/src/interfaces/cli/cli.cpp b/src/interfaces/cli/cli.cpp index b53c622..2bda3ee 100644 --- a/src/interfaces/cli/cli.cpp +++ b/src/interfaces/cli/cli.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include diff --git a/src/interfaces/uci/uci.cpp b/src/interfaces/uci/uci.cpp index 8d376c9..b1a00a9 100644 --- a/src/interfaces/uci/uci.cpp +++ b/src/interfaces/uci/uci.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include diff --git a/tests/movepicker/test_movepicker.cpp b/tests/movepicker/test_movepicker.cpp index 93911db..c1cc1ac 100644 --- a/tests/movepicker/test_movepicker.cpp +++ b/tests/movepicker/test_movepicker.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include void setup() { diff --git a/tests/movepicker/test_zobrist.cpp b/tests/movepicker/test_zobrist.cpp index 0e4886a..f0659f7 100644 --- a/tests/movepicker/test_zobrist.cpp +++ b/tests/movepicker/test_zobrist.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include From 7a91347a0e070262dc1143ef624e4e354772737f Mon Sep 17 00:00:00 2001 From: henrique Date: Wed, 10 Aug 2022 16:29:37 +0100 Subject: [PATCH 05/22] bug fixed --- src/engine/movepicker/hashtable.cpp | 48 ++++++++++++++++++++++++++++ src/engine/movepicker/hashtable.hpp | 36 +++++++++++++++++++++ src/engine/movepicker/movepicker.cpp | 31 +++++++++++++++++- src/engine/movepicker/movepicker.hpp | 7 ++++ 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 src/engine/movepicker/hashtable.cpp create mode 100644 src/engine/movepicker/hashtable.hpp diff --git a/src/engine/movepicker/hashtable.cpp b/src/engine/movepicker/hashtable.cpp new file mode 100644 index 0000000..6149899 --- /dev/null +++ b/src/engine/movepicker/hashtable.cpp @@ -0,0 +1,48 @@ +#include + +#include + +void TranspositionTable::clear() +{ + memset(_table, 0, sizeof(_table)); +} + +void TranspositionTable::set_entry(u64 hash_key, int depth, int flag, int score) +{ + int entry = hash_key % HASH_SIZE; + _table[entry].depth = depth; + _table[entry].flag = flag; + _table[entry].score = score; + // TTEntry *hash_entry = &_table[hash_key % HASH_SIZE]; + // hash_entry->hash_key = hash_key; + // hash_entry->depth = depth; + // hash_entry->flag = flag; + // hash_entry->score = score; +} + +TranspositionTable::TTOutput TranspositionTable::read_hash(u64 hash_key, int alpha, int beta, int depth) +{ + TTEntry *hash_entry = &_table[hash_key % HASH_SIZE]; + TTOutput search_result = {false, 0}; + + if ((hash_entry->hash_key == hash_key) && (hash_entry->depth >= depth)) + { + search_result.found = true; + if (hash_entry->flag == hash_flag_score) + { + search_result.score = hash_entry->score; + } + + if ((hash_entry->flag == hash_flag_alpha) && (hash_entry->score <= alpha)) + { + search_result.score = alpha; + } + + if ((hash_entry->flag == hash_flag_beta) && (hash_entry->score > beta)) + { + search_result.score = beta; + } + } + + return search_result; +} \ No newline at end of file diff --git a/src/engine/movepicker/hashtable.hpp b/src/engine/movepicker/hashtable.hpp new file mode 100644 index 0000000..ff63ab2 --- /dev/null +++ b/src/engine/movepicker/hashtable.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +#define hash_flag_score 0 +#define hash_flag_alpha 1 +#define hash_flag_beta 2 + +class TranspositionTable +{ +private: + struct TTEntry // transposition table entry + { + u64 hash_key; + int depth; + int flag; + int score; + // TODO: list of moves + }; + + static const int HASH_SIZE = 0x400000; + + TTEntry _table[HASH_SIZE]{}; + +public: + struct TTOutput // transposition table entry + { + bool found; + int score; + // TODO: list of moves + }; + + void clear(); + void set_entry(u64 hash_key, int depth, int flag, int score); + TTOutput read_hash(u64 hash_key, int alpha, int beta, int depth); +}; \ No newline at end of file diff --git a/src/engine/movepicker/movepicker.cpp b/src/engine/movepicker/movepicker.cpp index a177f1f..e3b8c1a 100644 --- a/src/engine/movepicker/movepicker.cpp +++ b/src/engine/movepicker/movepicker.cpp @@ -16,6 +16,9 @@ #define FULL_DEPTH_MOVES 4 #define WINDOW_EXPANSION 50 #define MIN_EVAL (INT_MIN + 1) +#define hash_flag_exact 0 +#define hash_flag_alpha 1 +#define hash_flag_beta 2 static bool can_lmr(const Move move) { @@ -62,7 +65,6 @@ int MovePicker::score(const Move move) int MovePicker::search(int depth, int alpha, int beta) { - auto moves = movegen::generate_pseudo_legal_moves(_board); std::sort(moves.begin(), moves.end(), _move_more_than_key); @@ -103,6 +105,14 @@ int MovePicker::negamax(int alpha, int beta, int depth) { _current_nodes++; + u64 hash_key = _board.get_hash_key(); + TranspositionTable::TTOutput hash_read = _tt.read_hash(_board.get_hash_key(), alpha, beta, depth); + + if (hash_read.found) + { + return hash_read.score; + } + // Terminal Node if (_board.get_half_move_clock() == 100) { @@ -131,6 +141,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) _current_depth -= 2; if (score >= beta) { + _tt.set_entry(hash_key, depth, hash_flag_beta, beta); return beta; } } @@ -141,6 +152,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) Move best_move = Move(); int n_moves_searched = 0; bool has_legal_moves = false; + int alpha_cutoff = hash_flag_alpha; for (const Move &move : moves) { _board.make_move(move); @@ -200,6 +212,8 @@ int MovePicker::negamax(int alpha, int beta, int depth) } _board.unmake_move(move, state); + + _tt.set_entry(hash_key, depth, hash_flag_beta, beta); return beta; } else if (score > alpha) @@ -211,6 +225,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) } alpha = score; + alpha_cutoff = hash_flag_score; best_move = move; _pv_table.add(best_move, _current_depth); } @@ -228,13 +243,16 @@ int MovePicker::negamax(int alpha, int beta, int depth) int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_side_to_move(), KING)); if (_board.is_square_attacked(king_sq, _board.get_opponent())) { + _tt.set_entry(hash_key, depth, hash_flag_score, MIN_EVAL + _current_depth); return MIN_EVAL + _current_depth; } // Stale Mate + _tt.set_entry(hash_key, depth, hash_flag_score, 0); return 0; } + _tt.set_entry(hash_key, depth, alpha_cutoff, alpha); return alpha; } @@ -242,8 +260,11 @@ int MovePicker::quiescence(int alpha, int beta) { _current_nodes++; + u64 hash_key = _board.get_hash_key(); + if (_board.get_half_move_clock() == 100) { + _tt.set_entry(hash_key, 0, hash_flag_score, 0); return 0; } @@ -252,9 +273,11 @@ int MovePicker::quiescence(int alpha, int beta) int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_side_to_move(), KING)); if (_board.is_square_attacked(king_sq, _board.get_opponent())) { + _tt.set_entry(hash_key, 0, hash_flag_score, MIN_EVAL + _current_depth); return MIN_EVAL + _current_depth; } + _tt.set_entry(hash_key, 0, hash_flag_score, 0); return 0; } @@ -262,11 +285,14 @@ int MovePicker::quiescence(int alpha, int beta) if (stand_pat >= beta) { + _tt.set_entry(hash_key, 0, hash_flag_beta, beta); return beta; } + int alpha_cutoff = hash_flag_alpha; if (stand_pat > alpha) { + alpha_cutoff = hash_flag_score; alpha = stand_pat; } @@ -287,10 +313,12 @@ int MovePicker::quiescence(int alpha, int beta) if (score >= beta) { _board.unmake_move(capture, state); + _tt.set_entry(hash_key, 0, hash_flag_beta, beta); return beta; } if (score > alpha) { + alpha_cutoff = hash_flag_score; alpha = score; } } @@ -298,6 +326,7 @@ int MovePicker::quiescence(int alpha, int beta) _board.unmake_move(capture, state); } + _tt.set_entry(hash_key, 0, alpha_cutoff, alpha); return alpha; } diff --git a/src/engine/movepicker/movepicker.hpp b/src/engine/movepicker/movepicker.hpp index 3978072..f0205dd 100644 --- a/src/engine/movepicker/movepicker.hpp +++ b/src/engine/movepicker/movepicker.hpp @@ -5,6 +5,7 @@ #include #include +#include #include @@ -12,6 +13,7 @@ class MovePicker { private: static const int DEFAULT_MAX_DEPTH = 64; + static const int HASH_SIZE = 0x400000; Board &_board; @@ -23,6 +25,7 @@ class MovePicker Move _killer_moves[2][DEFAULT_MAX_DEPTH]{}; PVTable _pv_table; + TranspositionTable _tt{}; struct MoveMoreThanKey { @@ -56,6 +59,10 @@ class MovePicker void clear_search_counters(); + void clear_tt() + { + } + public: struct SearchResult { From 2c8b4832e3a928e77929b5b32e18c3bbe1d2bffd Mon Sep 17 00:00:00 2001 From: jsilll Date: Sat, 13 Aug 2022 18:03:03 +0100 Subject: [PATCH 06/22] small cleanups --- src/engine/board.cpp | 5 ---- src/engine/board.hpp | 1 - src/engine/movegen/perft.cpp | 5 ++-- src/engine/movepicker/hashtable.cpp | 6 ++-- src/engine/movepicker/hashtable.hpp | 6 ++-- src/engine/movepicker/movepicker.cpp | 38 ++++++++++++------------- src/engine/zobrist.cpp | 12 -------- tests/movepicker/test_zobrist.cpp | 42 ++++++++++++++-------------- 8 files changed, 48 insertions(+), 67 deletions(-) diff --git a/src/engine/board.cpp b/src/engine/board.cpp index 174818f..7107591 100644 --- a/src/engine/board.cpp +++ b/src/engine/board.cpp @@ -117,11 +117,6 @@ u64 Board::calculate_hash_key() return zobrist::generate_hash_key(*this); } -u64 Board::get_hash_key() -{ - return _hash_key; -} - Board::Piece Board::get_piece_from_square(int sq) const { return _square[sq]; diff --git a/src/engine/board.hpp b/src/engine/board.hpp index 90c687a..80a4adf 100644 --- a/src/engine/board.hpp +++ b/src/engine/board.hpp @@ -64,7 +64,6 @@ class Board void set_state(GameState state); u64 calculate_hash_key(); - u64 get_hash_key(); void clear(); void set_starting_position(); diff --git a/src/engine/movegen/perft.cpp b/src/engine/movegen/perft.cpp index b98c56e..0eb043b 100644 --- a/src/engine/movegen/perft.cpp +++ b/src/engine/movegen/perft.cpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace perft { @@ -19,7 +20,7 @@ namespace perft { Board::GameState board_info = board.get_state(); board.make_move(move); - if (board.calculate_hash_key() != board.get_hash_key()) + if (board.calculate_hash_key() != zobrist::generate_hash_key(board)) { std::cout << "make: " << board.get_fen() << " " << move.get_uci() << std::endl; } @@ -30,7 +31,7 @@ namespace perft nodes += perft(board, depth - 1); } board.unmake_move(move, board_info); - if (board.calculate_hash_key() != board.get_hash_key()) + if (board.calculate_hash_key() != zobrist::generate_hash_key(board)) { std::cout << "unmake: " << board.get_fen() << " " << move.get_uci() << std::endl; } diff --git a/src/engine/movepicker/hashtable.cpp b/src/engine/movepicker/hashtable.cpp index 6149899..3d214b0 100644 --- a/src/engine/movepicker/hashtable.cpp +++ b/src/engine/movepicker/hashtable.cpp @@ -28,17 +28,17 @@ TranspositionTable::TTOutput TranspositionTable::read_hash(u64 hash_key, int alp if ((hash_entry->hash_key == hash_key) && (hash_entry->depth >= depth)) { search_result.found = true; - if (hash_entry->flag == hash_flag_score) + if (hash_entry->flag == HASH_FLAG_SCORE) { search_result.score = hash_entry->score; } - if ((hash_entry->flag == hash_flag_alpha) && (hash_entry->score <= alpha)) + if ((hash_entry->flag == HASH_FLAG_ALPHA) && (hash_entry->score <= alpha)) { search_result.score = alpha; } - if ((hash_entry->flag == hash_flag_beta) && (hash_entry->score > beta)) + if ((hash_entry->flag == HASH_FLAG_BETA) && (hash_entry->score > beta)) { search_result.score = beta; } diff --git a/src/engine/movepicker/hashtable.hpp b/src/engine/movepicker/hashtable.hpp index ff63ab2..d6bcd19 100644 --- a/src/engine/movepicker/hashtable.hpp +++ b/src/engine/movepicker/hashtable.hpp @@ -2,9 +2,9 @@ #include -#define hash_flag_score 0 -#define hash_flag_alpha 1 -#define hash_flag_beta 2 +#define HASH_FLAG_SCORE 0 +#define HASH_FLAG_ALPHA 1 +#define HASH_FLAG_BETA 2 class TranspositionTable { diff --git a/src/engine/movepicker/movepicker.cpp b/src/engine/movepicker/movepicker.cpp index e3b8c1a..3743aad 100644 --- a/src/engine/movepicker/movepicker.cpp +++ b/src/engine/movepicker/movepicker.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -16,9 +17,6 @@ #define FULL_DEPTH_MOVES 4 #define WINDOW_EXPANSION 50 #define MIN_EVAL (INT_MIN + 1) -#define hash_flag_exact 0 -#define hash_flag_alpha 1 -#define hash_flag_beta 2 static bool can_lmr(const Move move) { @@ -105,8 +103,8 @@ int MovePicker::negamax(int alpha, int beta, int depth) { _current_nodes++; - u64 hash_key = _board.get_hash_key(); - TranspositionTable::TTOutput hash_read = _tt.read_hash(_board.get_hash_key(), alpha, beta, depth); + u64 hash_key = zobrist::generate_hash_key(_board); + TranspositionTable::TTOutput hash_read = _tt.read_hash(zobrist::generate_hash_key(_board), alpha, beta, depth); if (hash_read.found) { @@ -141,7 +139,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) _current_depth -= 2; if (score >= beta) { - _tt.set_entry(hash_key, depth, hash_flag_beta, beta); + _tt.set_entry(hash_key, depth, HASH_FLAG_BETA, beta); return beta; } } @@ -152,7 +150,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) Move best_move = Move(); int n_moves_searched = 0; bool has_legal_moves = false; - int alpha_cutoff = hash_flag_alpha; + int alpha_cutoff = HASH_FLAG_ALPHA; for (const Move &move : moves) { _board.make_move(move); @@ -213,7 +211,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) _board.unmake_move(move, state); - _tt.set_entry(hash_key, depth, hash_flag_beta, beta); + _tt.set_entry(hash_key, depth, HASH_FLAG_BETA, beta); return beta; } else if (score > alpha) @@ -225,7 +223,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) } alpha = score; - alpha_cutoff = hash_flag_score; + alpha_cutoff = HASH_FLAG_SCORE; best_move = move; _pv_table.add(best_move, _current_depth); } @@ -243,12 +241,12 @@ int MovePicker::negamax(int alpha, int beta, int depth) int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_side_to_move(), KING)); if (_board.is_square_attacked(king_sq, _board.get_opponent())) { - _tt.set_entry(hash_key, depth, hash_flag_score, MIN_EVAL + _current_depth); + _tt.set_entry(hash_key, depth, HASH_FLAG_SCORE, MIN_EVAL + _current_depth); return MIN_EVAL + _current_depth; } // Stale Mate - _tt.set_entry(hash_key, depth, hash_flag_score, 0); + _tt.set_entry(hash_key, depth, HASH_FLAG_SCORE, 0); return 0; } @@ -260,11 +258,11 @@ int MovePicker::quiescence(int alpha, int beta) { _current_nodes++; - u64 hash_key = _board.get_hash_key(); + u64 hash_key = zobrist::generate_hash_key(_board); if (_board.get_half_move_clock() == 100) { - _tt.set_entry(hash_key, 0, hash_flag_score, 0); + _tt.set_entry(hash_key, 0, HASH_FLAG_SCORE, 0); return 0; } @@ -273,11 +271,11 @@ int MovePicker::quiescence(int alpha, int beta) int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_side_to_move(), KING)); if (_board.is_square_attacked(king_sq, _board.get_opponent())) { - _tt.set_entry(hash_key, 0, hash_flag_score, MIN_EVAL + _current_depth); + _tt.set_entry(hash_key, 0, HASH_FLAG_SCORE, MIN_EVAL + _current_depth); return MIN_EVAL + _current_depth; } - _tt.set_entry(hash_key, 0, hash_flag_score, 0); + _tt.set_entry(hash_key, 0, HASH_FLAG_SCORE, 0); return 0; } @@ -285,14 +283,14 @@ int MovePicker::quiescence(int alpha, int beta) if (stand_pat >= beta) { - _tt.set_entry(hash_key, 0, hash_flag_beta, beta); + _tt.set_entry(hash_key, 0, HASH_FLAG_BETA, beta); return beta; } - int alpha_cutoff = hash_flag_alpha; + int alpha_cutoff = HASH_FLAG_ALPHA; if (stand_pat > alpha) { - alpha_cutoff = hash_flag_score; + alpha_cutoff = HASH_FLAG_SCORE; alpha = stand_pat; } @@ -313,12 +311,12 @@ int MovePicker::quiescence(int alpha, int beta) if (score >= beta) { _board.unmake_move(capture, state); - _tt.set_entry(hash_key, 0, hash_flag_beta, beta); + _tt.set_entry(hash_key, 0, HASH_FLAG_BETA, beta); return beta; } if (score > alpha) { - alpha_cutoff = hash_flag_score; + alpha_cutoff = HASH_FLAG_SCORE; alpha = score; } } diff --git a/src/engine/zobrist.cpp b/src/engine/zobrist.cpp index b3d47ac..0f47183 100644 --- a/src/engine/zobrist.cpp +++ b/src/engine/zobrist.cpp @@ -21,7 +21,6 @@ namespace zobrist void init() { - for (int piece = PAWN; piece < N_PIECES; piece++) { for (int square = A1; square < N_SQUARES; square++) @@ -47,19 +46,15 @@ namespace zobrist u64 generate_hash_key(const Board &board) { u64 final_key = 0ULL; - for (int piece = PAWN; piece < N_PIECES; piece++) { for (int side = WHITE; side < BOTH; side++) { u64 bitboard = board.get_pieces(side, piece); - while (bitboard) { int sq = bitboard::bit_scan_forward(bitboard); - final_key ^= piece_keys[side][piece][sq]; - bitboard::pop_bit(bitboard, sq); } } @@ -71,14 +66,7 @@ namespace zobrist } final_key ^= castle_keys[board.get_castling_rights()]; - - // if (board.get_side_to_move() == BLACK) - // { - // final_key ^= side_key[1]; - // } - // final_key ^= board.get_side_to_move() == WHITE ? 0ULL : side_key; final_key ^= side_key[board.get_side_to_move()]; - return final_key; } diff --git a/tests/movepicker/test_zobrist.cpp b/tests/movepicker/test_zobrist.cpp index f0659f7..acaff61 100644 --- a/tests/movepicker/test_zobrist.cpp +++ b/tests/movepicker/test_zobrist.cpp @@ -30,7 +30,7 @@ int perft(Board &board, int depth) { Board::GameState board_info = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); int attacker_side = board.get_side_to_move(); if (!board.is_square_attacked(king_sq, attacker_side)) @@ -38,7 +38,7 @@ int perft(Board &board, int depth) nodes += perft(board, depth - 1); } board.unmake_move(move, board_info); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); } return nodes; @@ -52,7 +52,7 @@ TEST_CASE("hash_key") SECTION("fen hash key") { board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R", "w", "KQkq", "-", "0", "1"); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); } SECTION("moves hash key") @@ -61,10 +61,10 @@ TEST_CASE("hash_key") Move move = Move(D5, D6, PAWN, EMPTY_PIECE, EMPTY_PIECE, false, false, false); Board::GameState state = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); - REQUIRE(zobrist::generate_hash_key(board) == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); } SECTION("capture hash key") @@ -73,10 +73,10 @@ TEST_CASE("hash_key") Move move = Move(F3, F6, QUEEN, KNIGHT, EMPTY_PIECE, false, false, false); Board::GameState state = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); - REQUIRE(zobrist::generate_hash_key(board) == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); } SECTION("double push hash key") @@ -85,10 +85,10 @@ TEST_CASE("hash_key") Move move = Move(A2, A4, PAWN, EMPTY_PIECE, EMPTY_PIECE, true, false, false); Board::GameState state = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); - REQUIRE(zobrist::generate_hash_key(board) == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); } SECTION("promotion hash key") @@ -97,10 +97,10 @@ TEST_CASE("hash_key") Move move = Move(H2, H1, PAWN, EMPTY_PIECE, KNIGHT, false, false, false); Board::GameState state = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); - REQUIRE(zobrist::generate_hash_key(board) == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); } SECTION("promotion + capture hash key") @@ -109,10 +109,10 @@ TEST_CASE("hash_key") Move move = Move(H2, G1, PAWN, PAWN, QUEEN, false, false, false); Board::GameState state = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); - REQUIRE(zobrist::generate_hash_key(board) == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); } SECTION("en passant hash key") @@ -121,10 +121,10 @@ TEST_CASE("hash_key") Move move = Move(C4, D3, PAWN, PAWN, EMPTY_PIECE, false, true, false); Board::GameState state = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); - REQUIRE(zobrist::generate_hash_key(board) == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.calculate_hash_key() == board.get_hash_key()); + REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); } SECTION("perft hash key") From e9cb66ec30d9b20c597e199be482678332492760 Mon Sep 17 00:00:00 2001 From: jsilll Date: Sat, 13 Aug 2022 19:20:32 +0100 Subject: [PATCH 07/22] fixed refactoring errors --- CMakeLists.txt | 10 ++++++++-- src/engine/board.cpp | 6 +++--- src/engine/board.hpp | 2 +- src/engine/movegen/perft.cpp | 4 ++-- src/engine/movepicker/hashtable.cpp | 25 ++++++++++++----------- src/engine/movepicker/movepicker.cpp | 8 ++++---- tests/movepicker/test_zobrist.cpp | 30 ++++++++++++++-------------- 7 files changed, 46 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f015f70..1e21fd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,13 +7,19 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) project(chess_engine VERSION 1.0 DESCRIPTION "Chess Engine") set(CMAKE_CXX_FLAGS "-Wall -Wpedantic -Wextra -Wconversion -Werror") -set(CMAKE_CXX_FLAGS_DEBUG "-g") -# set(CMAKE_CXX_FLAGS_RELEASE "-O3") if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) endif() +if(CMAKE_BUILD_TYPE MATCHES Debug) + set(CMAKE_CXX_FLAGS_DEBUG "-g") +endif(CMAKE_BUILD_TYPE MATCHES Debug) + +if(CMAKE_BUILD_TYPE MATCHES Release) + set(CMAKE_CXX_FLAGS_RELEASE "-O3") +endif(CMAKE_BUILD_TYPE MATCHES Release) + set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) diff --git a/src/engine/board.cpp b/src/engine/board.cpp index 7107591..e42254b 100644 --- a/src/engine/board.cpp +++ b/src/engine/board.cpp @@ -112,9 +112,9 @@ int Board::get_full_move_number() const return _full_move_number; } -u64 Board::calculate_hash_key() +u64 Board::get_hash_key() const { - return zobrist::generate_hash_key(*this); + return _hash_key; } Board::Piece Board::get_piece_from_square(int sq) const @@ -476,7 +476,7 @@ void Board::set_from_fen(const std::string &piece_placements, this->update_bb_from_squares(); - _hash_key = this->calculate_hash_key(); + _hash_key = zobrist::generate_hash_key(*this); } int Board::switch_side_to_move() diff --git a/src/engine/board.hpp b/src/engine/board.hpp index 80a4adf..864ae7a 100644 --- a/src/engine/board.hpp +++ b/src/engine/board.hpp @@ -63,7 +63,7 @@ class Board void set_fifty_move(int fifty_move); void set_state(GameState state); - u64 calculate_hash_key(); + u64 get_hash_key() const; void clear(); void set_starting_position(); diff --git a/src/engine/movegen/perft.cpp b/src/engine/movegen/perft.cpp index 0eb043b..240f4d6 100644 --- a/src/engine/movegen/perft.cpp +++ b/src/engine/movegen/perft.cpp @@ -20,7 +20,7 @@ namespace perft { Board::GameState board_info = board.get_state(); board.make_move(move); - if (board.calculate_hash_key() != zobrist::generate_hash_key(board)) + if (board.get_hash_key() != zobrist::generate_hash_key(board)) { std::cout << "make: " << board.get_fen() << " " << move.get_uci() << std::endl; } @@ -31,7 +31,7 @@ namespace perft nodes += perft(board, depth - 1); } board.unmake_move(move, board_info); - if (board.calculate_hash_key() != zobrist::generate_hash_key(board)) + if (board.get_hash_key() != zobrist::generate_hash_key(board)) { std::cout << "unmake: " << board.get_fen() << " " << move.get_uci() << std::endl; } diff --git a/src/engine/movepicker/hashtable.cpp b/src/engine/movepicker/hashtable.cpp index 3d214b0..14a1b9f 100644 --- a/src/engine/movepicker/hashtable.cpp +++ b/src/engine/movepicker/hashtable.cpp @@ -1,48 +1,49 @@ #include #include +#include void TranspositionTable::clear() { + std::cout << "entrei" << std::endl; memset(_table, 0, sizeof(_table)); + std::cout << "sai" << std::endl; } void TranspositionTable::set_entry(u64 hash_key, int depth, int flag, int score) { + std::cout << "entrei" << std::endl; int entry = hash_key % HASH_SIZE; _table[entry].depth = depth; _table[entry].flag = flag; _table[entry].score = score; - // TTEntry *hash_entry = &_table[hash_key % HASH_SIZE]; - // hash_entry->hash_key = hash_key; - // hash_entry->depth = depth; - // hash_entry->flag = flag; - // hash_entry->score = score; + std::cout << "sai" << std::endl; } TranspositionTable::TTOutput TranspositionTable::read_hash(u64 hash_key, int alpha, int beta, int depth) { - TTEntry *hash_entry = &_table[hash_key % HASH_SIZE]; + std::cout << "entrei" << std::endl; + TTEntry hash_entry = _table[hash_key % HASH_SIZE]; TTOutput search_result = {false, 0}; - if ((hash_entry->hash_key == hash_key) && (hash_entry->depth >= depth)) + if ((hash_entry.hash_key == hash_key) && (hash_entry.depth >= depth)) { search_result.found = true; - if (hash_entry->flag == HASH_FLAG_SCORE) + if (hash_entry.flag == HASH_FLAG_SCORE) { - search_result.score = hash_entry->score; + search_result.score = hash_entry.score; } - if ((hash_entry->flag == HASH_FLAG_ALPHA) && (hash_entry->score <= alpha)) + if ((hash_entry.flag == HASH_FLAG_ALPHA) && (hash_entry.score <= alpha)) { search_result.score = alpha; } - if ((hash_entry->flag == HASH_FLAG_BETA) && (hash_entry->score > beta)) + if ((hash_entry.flag == HASH_FLAG_BETA) && (hash_entry.score > beta)) { search_result.score = beta; } } - + std::cout << "sai" << std::endl; return search_result; } \ No newline at end of file diff --git a/src/engine/movepicker/movepicker.cpp b/src/engine/movepicker/movepicker.cpp index 3743aad..a7c5d9b 100644 --- a/src/engine/movepicker/movepicker.cpp +++ b/src/engine/movepicker/movepicker.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -103,8 +102,9 @@ int MovePicker::negamax(int alpha, int beta, int depth) { _current_nodes++; - u64 hash_key = zobrist::generate_hash_key(_board); - TranspositionTable::TTOutput hash_read = _tt.read_hash(zobrist::generate_hash_key(_board), alpha, beta, depth); + u64 hash_key = _board.get_hash_key(); + + TranspositionTable::TTOutput hash_read = _tt.read_hash(_board.get_hash_key(), alpha, beta, depth); if (hash_read.found) { @@ -258,7 +258,7 @@ int MovePicker::quiescence(int alpha, int beta) { _current_nodes++; - u64 hash_key = zobrist::generate_hash_key(_board); + u64 hash_key = _board.get_hash_key(); if (_board.get_half_move_clock() == 100) { diff --git a/tests/movepicker/test_zobrist.cpp b/tests/movepicker/test_zobrist.cpp index acaff61..0b534b9 100644 --- a/tests/movepicker/test_zobrist.cpp +++ b/tests/movepicker/test_zobrist.cpp @@ -30,7 +30,7 @@ int perft(Board &board, int depth) { Board::GameState board_info = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); int attacker_side = board.get_side_to_move(); if (!board.is_square_attacked(king_sq, attacker_side)) @@ -38,7 +38,7 @@ int perft(Board &board, int depth) nodes += perft(board, depth - 1); } board.unmake_move(move, board_info); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); } return nodes; @@ -52,7 +52,7 @@ TEST_CASE("hash_key") SECTION("fen hash key") { board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R", "w", "KQkq", "-", "0", "1"); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); } SECTION("moves hash key") @@ -61,10 +61,10 @@ TEST_CASE("hash_key") Move move = Move(D5, D6, PAWN, EMPTY_PIECE, EMPTY_PIECE, false, false, false); Board::GameState state = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); } SECTION("capture hash key") @@ -73,10 +73,10 @@ TEST_CASE("hash_key") Move move = Move(F3, F6, QUEEN, KNIGHT, EMPTY_PIECE, false, false, false); Board::GameState state = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); } SECTION("double push hash key") @@ -85,10 +85,10 @@ TEST_CASE("hash_key") Move move = Move(A2, A4, PAWN, EMPTY_PIECE, EMPTY_PIECE, true, false, false); Board::GameState state = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); } SECTION("promotion hash key") @@ -97,10 +97,10 @@ TEST_CASE("hash_key") Move move = Move(H2, H1, PAWN, EMPTY_PIECE, KNIGHT, false, false, false); Board::GameState state = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); } SECTION("promotion + capture hash key") @@ -109,10 +109,10 @@ TEST_CASE("hash_key") Move move = Move(H2, G1, PAWN, PAWN, QUEEN, false, false, false); Board::GameState state = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); } SECTION("en passant hash key") @@ -121,10 +121,10 @@ TEST_CASE("hash_key") Move move = Move(C4, D3, PAWN, PAWN, EMPTY_PIECE, false, true, false); Board::GameState state = board.get_state(); board.make_move(move); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.calculate_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); } SECTION("perft hash key") From edfb99287497fc0a504a47eb85544ec6c9c75f15 Mon Sep 17 00:00:00 2001 From: jsilll Date: Sat, 13 Aug 2022 20:38:24 +0100 Subject: [PATCH 08/22] fixed stack overflow bug and removed debug checks from perft --- CMakeLists.txt | 10 +---- src/engine/movegen/perft.cpp | 8 ---- src/engine/movepicker/movepicker.cpp | 43 ++++++++++--------- src/engine/movepicker/movepicker.hpp | 9 ++-- .../movepicker/{hashtable.cpp => ttable.cpp} | 27 ++++++------ .../movepicker/{hashtable.hpp => ttable.hpp} | 17 +++++--- 6 files changed, 51 insertions(+), 63 deletions(-) rename src/engine/movepicker/{hashtable.cpp => ttable.cpp} (54%) rename src/engine/movepicker/{hashtable.hpp => ttable.hpp} (66%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e21fd0..828029f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,19 +7,13 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) project(chess_engine VERSION 1.0 DESCRIPTION "Chess Engine") set(CMAKE_CXX_FLAGS "-Wall -Wpedantic -Wextra -Wconversion -Werror") +set(CMAKE_CXX_FLAGS_RELEASE "-O3") +set(CMAKE_CXX_FLAGS_DEBUG "-g") if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) endif() -if(CMAKE_BUILD_TYPE MATCHES Debug) - set(CMAKE_CXX_FLAGS_DEBUG "-g") -endif(CMAKE_BUILD_TYPE MATCHES Debug) - -if(CMAKE_BUILD_TYPE MATCHES Release) - set(CMAKE_CXX_FLAGS_RELEASE "-O3") -endif(CMAKE_BUILD_TYPE MATCHES Release) - set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) diff --git a/src/engine/movegen/perft.cpp b/src/engine/movegen/perft.cpp index 240f4d6..413860a 100644 --- a/src/engine/movegen/perft.cpp +++ b/src/engine/movegen/perft.cpp @@ -20,10 +20,6 @@ namespace perft { Board::GameState board_info = board.get_state(); board.make_move(move); - if (board.get_hash_key() != zobrist::generate_hash_key(board)) - { - std::cout << "make: " << board.get_fen() << " " << move.get_uci() << std::endl; - } int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); int attacker_side = board.get_side_to_move(); if (!board.is_square_attacked(king_sq, attacker_side)) @@ -31,10 +27,6 @@ namespace perft nodes += perft(board, depth - 1); } board.unmake_move(move, board_info); - if (board.get_hash_key() != zobrist::generate_hash_key(board)) - { - std::cout << "unmake: " << board.get_fen() << " " << move.get_uci() << std::endl; - } } return nodes; diff --git a/src/engine/movepicker/movepicker.cpp b/src/engine/movepicker/movepicker.cpp index a7c5d9b..c64e195 100644 --- a/src/engine/movepicker/movepicker.cpp +++ b/src/engine/movepicker/movepicker.cpp @@ -102,9 +102,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) { _current_nodes++; - u64 hash_key = _board.get_hash_key(); - - TranspositionTable::TTOutput hash_read = _tt.read_hash(_board.get_hash_key(), alpha, beta, depth); + TTable::TTOutput hash_read = _tt.read_hash(_board.get_hash_key(), alpha, beta, depth); if (hash_read.found) { @@ -125,6 +123,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) return quiescence(alpha, beta); } + u64 hash_key = _board.get_hash_key(); Board::GameState state = _board.get_state(); // Null Move Pruning (TODO: Zugzwang checking??) @@ -139,7 +138,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) _current_depth -= 2; if (score >= beta) { - _tt.set_entry(hash_key, depth, HASH_FLAG_BETA, beta); + _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_BETA, beta); return beta; } } @@ -150,7 +149,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) Move best_move = Move(); int n_moves_searched = 0; bool has_legal_moves = false; - int alpha_cutoff = HASH_FLAG_ALPHA; + int alpha_cutoff = TTable::HASH_FLAG_ALPHA; for (const Move &move : moves) { _board.make_move(move); @@ -211,7 +210,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) _board.unmake_move(move, state); - _tt.set_entry(hash_key, depth, HASH_FLAG_BETA, beta); + _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_BETA, beta); return beta; } else if (score > alpha) @@ -223,7 +222,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) } alpha = score; - alpha_cutoff = HASH_FLAG_SCORE; + alpha_cutoff = TTable::HASH_FLAG_SCORE; best_move = move; _pv_table.add(best_move, _current_depth); } @@ -241,12 +240,12 @@ int MovePicker::negamax(int alpha, int beta, int depth) int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_side_to_move(), KING)); if (_board.is_square_attacked(king_sq, _board.get_opponent())) { - _tt.set_entry(hash_key, depth, HASH_FLAG_SCORE, MIN_EVAL + _current_depth); + _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_SCORE, MIN_EVAL + _current_depth); return MIN_EVAL + _current_depth; } // Stale Mate - _tt.set_entry(hash_key, depth, HASH_FLAG_SCORE, 0); + _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_SCORE, 0); return 0; } @@ -258,11 +257,9 @@ int MovePicker::quiescence(int alpha, int beta) { _current_nodes++; - u64 hash_key = _board.get_hash_key(); - if (_board.get_half_move_clock() == 100) { - _tt.set_entry(hash_key, 0, HASH_FLAG_SCORE, 0); + _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_SCORE, 0); return 0; } @@ -271,11 +268,11 @@ int MovePicker::quiescence(int alpha, int beta) int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_side_to_move(), KING)); if (_board.is_square_attacked(king_sq, _board.get_opponent())) { - _tt.set_entry(hash_key, 0, HASH_FLAG_SCORE, MIN_EVAL + _current_depth); + _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_SCORE, MIN_EVAL + _current_depth); return MIN_EVAL + _current_depth; } - _tt.set_entry(hash_key, 0, HASH_FLAG_SCORE, 0); + _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_SCORE, 0); return 0; } @@ -283,20 +280,21 @@ int MovePicker::quiescence(int alpha, int beta) if (stand_pat >= beta) { - _tt.set_entry(hash_key, 0, HASH_FLAG_BETA, beta); + _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_BETA, beta); return beta; } - int alpha_cutoff = HASH_FLAG_ALPHA; + int alpha_cutoff = TTable::HASH_FLAG_ALPHA; if (stand_pat > alpha) { - alpha_cutoff = HASH_FLAG_SCORE; + alpha_cutoff = TTable::HASH_FLAG_SCORE; alpha = stand_pat; } auto captures = movegen::generate_pseudo_legal_captures(_board); std::sort(captures.begin(), captures.end(), _move_more_than_key); + u64 hash_key = _board.get_hash_key(); Board::GameState state = _board.get_state(); for (const Move &capture : captures) { @@ -311,12 +309,12 @@ int MovePicker::quiescence(int alpha, int beta) if (score >= beta) { _board.unmake_move(capture, state); - _tt.set_entry(hash_key, 0, HASH_FLAG_BETA, beta); + _tt.set_entry(hash_key, 0, TTable::HASH_FLAG_BETA, beta); return beta; } if (score > alpha) { - alpha_cutoff = HASH_FLAG_SCORE; + alpha_cutoff = TTable::HASH_FLAG_SCORE; alpha = score; } } @@ -324,7 +322,7 @@ int MovePicker::quiescence(int alpha, int beta) _board.unmake_move(capture, state); } - _tt.set_entry(hash_key, 0, alpha_cutoff, alpha); + _tt.set_entry(_board.get_hash_key(), 0, alpha_cutoff, alpha); return alpha; } @@ -350,6 +348,11 @@ void MovePicker::clear_search_counters() _current_depth = 0; } +void MovePicker::clear_tt() +{ + _tt.clear(); +} + // Public Methods int MovePicker::get_max_depth() const diff --git a/src/engine/movepicker/movepicker.hpp b/src/engine/movepicker/movepicker.hpp index f0205dd..b88df2f 100644 --- a/src/engine/movepicker/movepicker.hpp +++ b/src/engine/movepicker/movepicker.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include @@ -13,7 +13,6 @@ class MovePicker { private: static const int DEFAULT_MAX_DEPTH = 64; - static const int HASH_SIZE = 0x400000; Board &_board; @@ -25,7 +24,7 @@ class MovePicker Move _killer_moves[2][DEFAULT_MAX_DEPTH]{}; PVTable _pv_table; - TranspositionTable _tt{}; + TTable _tt; struct MoveMoreThanKey { @@ -59,9 +58,7 @@ class MovePicker void clear_search_counters(); - void clear_tt() - { - } + void clear_tt(); public: struct SearchResult diff --git a/src/engine/movepicker/hashtable.cpp b/src/engine/movepicker/ttable.cpp similarity index 54% rename from src/engine/movepicker/hashtable.cpp rename to src/engine/movepicker/ttable.cpp index 14a1b9f..86a7b7d 100644 --- a/src/engine/movepicker/hashtable.cpp +++ b/src/engine/movepicker/ttable.cpp @@ -1,31 +1,30 @@ -#include +#include #include #include -void TranspositionTable::clear() +TTable::~TTable() { - std::cout << "entrei" << std::endl; - memset(_table, 0, sizeof(_table)); - std::cout << "sai" << std::endl; + delete[] _table; } -void TranspositionTable::set_entry(u64 hash_key, int depth, int flag, int score) +void TTable::clear() { - std::cout << "entrei" << std::endl; - int entry = hash_key % HASH_SIZE; + memset(_table, 0, TABLE_SIZE); +} + +void TTable::set_entry([[maybe_unused]] u64 hash_key, int depth, int flag, int score) +{ + int entry = hash_key % TABLE_SIZE; _table[entry].depth = depth; _table[entry].flag = flag; _table[entry].score = score; - std::cout << "sai" << std::endl; } -TranspositionTable::TTOutput TranspositionTable::read_hash(u64 hash_key, int alpha, int beta, int depth) +TTable::TTOutput TTable::read_hash(u64 hash_key, int alpha, int beta, int depth) { - std::cout << "entrei" << std::endl; - TTEntry hash_entry = _table[hash_key % HASH_SIZE]; TTOutput search_result = {false, 0}; - + TTEntry hash_entry = _table[hash_key % TABLE_SIZE]; if ((hash_entry.hash_key == hash_key) && (hash_entry.depth >= depth)) { search_result.found = true; @@ -44,6 +43,6 @@ TranspositionTable::TTOutput TranspositionTable::read_hash(u64 hash_key, int alp search_result.score = beta; } } - std::cout << "sai" << std::endl; + return search_result; } \ No newline at end of file diff --git a/src/engine/movepicker/hashtable.hpp b/src/engine/movepicker/ttable.hpp similarity index 66% rename from src/engine/movepicker/hashtable.hpp rename to src/engine/movepicker/ttable.hpp index d6bcd19..e1788bf 100644 --- a/src/engine/movepicker/hashtable.hpp +++ b/src/engine/movepicker/ttable.hpp @@ -2,11 +2,7 @@ #include -#define HASH_FLAG_SCORE 0 -#define HASH_FLAG_ALPHA 1 -#define HASH_FLAG_BETA 2 - -class TranspositionTable +class TTable { private: struct TTEntry // transposition table entry @@ -18,11 +14,15 @@ class TranspositionTable // TODO: list of moves }; - static const int HASH_SIZE = 0x400000; + static const int TABLE_SIZE = 0x400000; - TTEntry _table[HASH_SIZE]{}; + TTEntry *_table; public: + static const int HASH_FLAG_SCORE = 0; + static const int HASH_FLAG_ALPHA = 1; + static const int HASH_FLAG_BETA = 2; + struct TTOutput // transposition table entry { bool found; @@ -30,6 +30,9 @@ class TranspositionTable // TODO: list of moves }; + TTable() : _table(new TTEntry[TABLE_SIZE]){}; + ~TTable(); + void clear(); void set_entry(u64 hash_key, int depth, int flag, int score); TTOutput read_hash(u64 hash_key, int alpha, int beta, int depth); From a6c470219e202fc755f57675d081846d26a0fbb9 Mon Sep 17 00:00:00 2001 From: jsilll Date: Sat, 13 Aug 2022 22:00:16 +0100 Subject: [PATCH 09/22] only one movepicker instatiated now --- src/interfaces/cli/cli.cpp | 74 ++++++++++---------- src/interfaces/cli/commands/ascii.cpp | 4 +- src/interfaces/cli/commands/captures.cpp | 4 +- src/interfaces/cli/commands/commands.hpp | 54 +++++++++----- src/interfaces/cli/commands/display.cpp | 4 +- src/interfaces/cli/commands/dividedperft.cpp | 4 +- src/interfaces/cli/commands/eval.cpp | 4 +- src/interfaces/cli/commands/exit.cpp | 2 +- src/interfaces/cli/commands/getfen.cpp | 4 +- src/interfaces/cli/commands/help.cpp | 2 +- src/interfaces/cli/commands/info.cpp | 10 +-- src/interfaces/cli/commands/magics.cpp | 2 +- src/interfaces/cli/commands/move.cpp | 6 +- src/interfaces/cli/commands/moves.cpp | 4 +- src/interfaces/cli/commands/new.cpp | 4 +- src/interfaces/cli/commands/perft.cpp | 4 +- src/interfaces/cli/commands/plmoves.cpp | 4 +- src/interfaces/cli/commands/rotate.cpp | 4 +- src/interfaces/cli/commands/setfen.cpp | 4 +- src/interfaces/cli/commands/switch.cpp | 4 +- src/interfaces/uci/commands/commands.hpp | 26 +++++-- src/interfaces/uci/commands/display.cpp | 4 +- src/interfaces/uci/commands/exit.cpp | 2 +- src/interfaces/uci/commands/go.cpp | 14 ++-- src/interfaces/uci/commands/isready.cpp | 2 +- src/interfaces/uci/commands/position.cpp | 8 +-- src/interfaces/uci/commands/uci.cpp | 2 +- src/interfaces/uci/commands/ucinewgame.cpp | 4 +- src/interfaces/uci/uci.cpp | 28 ++++---- src/interfaces/utils.hpp | 6 +- 30 files changed, 165 insertions(+), 133 deletions(-) diff --git a/src/interfaces/cli/cli.cpp b/src/interfaces/cli/cli.cpp index 2bda3ee..a568bb7 100644 --- a/src/interfaces/cli/cli.cpp +++ b/src/interfaces/cli/cli.cpp @@ -26,27 +26,27 @@ namespace cli eval::init(); zobrist::init(); - AsciiCommand ascii_command = AsciiCommand(); - CapturesCommand captures_command = CapturesCommand(); - DisplayCommand display_command = DisplayCommand(); - DividedPerftCommand dperft_command = DividedPerftCommand(); - EvalCommand eval_command = EvalCommand(); - ExitCommand exit_command = ExitCommand(); - GetFenCommand get_fen_command = GetFenCommand(); - HelpCommand help_command = HelpCommand(); - InfoCommand info_command = InfoCommand(); - MagicsCommand magics_command = MagicsCommand(); - MoveCommand move_command = MoveCommand(); - MovesCommand moves_command = MovesCommand(); - NewCommand new_command = NewCommand(); - PerftCommand perft_command = PerftCommand(); - PLMovesCommand pl_moves_command = PLMovesCommand(); - RotateCommand rotate_command = RotateCommand(); - SetFenCommand set_fen_command = SetFenCommand(); - SwitchCommand switch_command = SwitchCommand(); - Board board = Board(); + AsciiCommand ascii_command = AsciiCommand(board); + CapturesCommand captures_command = CapturesCommand(board); + DisplayCommand display_command = DisplayCommand(board); + DividedPerftCommand dperft_command = DividedPerftCommand(board); + EvalCommand eval_command = EvalCommand(board); + ExitCommand exit_command = ExitCommand(board); + GetFenCommand get_fen_command = GetFenCommand(board); + HelpCommand help_command = HelpCommand(board); + InfoCommand info_command = InfoCommand(board); + MagicsCommand magics_command = MagicsCommand(board); + MoveCommand move_command = MoveCommand(board); + MovesCommand moves_command = MovesCommand(board); + NewCommand new_command = NewCommand(board); + PerftCommand perft_command = PerftCommand(board); + PLMovesCommand pl_moves_command = PLMovesCommand(board); + RotateCommand rotate_command = RotateCommand(board); + SetFenCommand set_fen_command = SetFenCommand(board); + SwitchCommand switch_command = SwitchCommand(board); + for (;;) { std::cout << "> " << std::flush; @@ -65,75 +65,75 @@ namespace cli if (cmd == "help") { - help_command.execute(args, board); + help_command.execute(args); } else if (cmd == "ascii") { - ascii_command.execute(args, board); + ascii_command.execute(args); } else if (cmd == "display") { - display_command.execute(args, board); + display_command.execute(args); } else if (cmd == "dperft") { - dperft_command.execute(args, board); + dperft_command.execute(args); } else if (cmd == "eval") { - eval_command.execute(args, board); + eval_command.execute(args); } else if (cmd == "exit") { - exit_command.execute(args, board); + exit_command.execute(args); } else if (cmd == "info") { - info_command.execute(args, board); + info_command.execute(args); } else if (cmd == "magics") { - magics_command.execute(args, board); + magics_command.execute(args); } else if (cmd == "move") { - move_command.execute(args, board); + move_command.execute(args); } else if (cmd == "moves") { - moves_command.execute(args, board); + moves_command.execute(args); } else if (cmd == "plmoves") { - pl_moves_command.execute(args, board); + pl_moves_command.execute(args); } else if (cmd == "captures") { - captures_command.execute(args, board); + captures_command.execute(args); } else if (cmd == "new") { - new_command.execute(args, board); + new_command.execute(args); } else if (cmd == "perft") { - perft_command.execute(args, board); + perft_command.execute(args); } else if (cmd == "getfen") { - get_fen_command.execute(args, board); + get_fen_command.execute(args); } else if (cmd == "rotate") { - rotate_command.execute(args, board); + rotate_command.execute(args); } else if (cmd == "setfen") { - set_fen_command.execute(args, board); + set_fen_command.execute(args); } else if (cmd == "switch") { - switch_command.execute(args, board); + switch_command.execute(args); } else { diff --git a/src/interfaces/cli/commands/ascii.cpp b/src/interfaces/cli/commands/ascii.cpp index a43d96e..0dfdff7 100644 --- a/src/interfaces/cli/commands/ascii.cpp +++ b/src/interfaces/cli/commands/ascii.cpp @@ -2,7 +2,7 @@ #include -void cli::AsciiCommand::execute([[maybe_unused]] std::vector &args, Board &board) +void cli::AsciiCommand::execute([[maybe_unused]] std::vector &args) { - std::cout << "ascii mode toggled " << (board.toggle_ascii() ? "on" : "off") << std::endl; + std::cout << "ascii mode toggled " << (_board.toggle_ascii() ? "on" : "off") << std::endl; } \ No newline at end of file diff --git a/src/interfaces/cli/commands/captures.cpp b/src/interfaces/cli/commands/captures.cpp index 44bd04a..337f31f 100644 --- a/src/interfaces/cli/commands/captures.cpp +++ b/src/interfaces/cli/commands/captures.cpp @@ -5,9 +5,9 @@ #include #include -void cli::CapturesCommand::execute([[maybe_unused]] std::vector &args, Board &board) +void cli::CapturesCommand::execute([[maybe_unused]] std::vector &args) { - std::vector captures = movegen::generate_legal_captures(board); + std::vector captures = movegen::generate_legal_captures(_board); std::for_each(captures.begin(), captures.end(), [](const Move &capture) { std::cout << capture.get_uci() << "\n"; }); std::cout << "Total number of captures: " << captures.size() << std::endl; diff --git a/src/interfaces/cli/commands/commands.hpp b/src/interfaces/cli/commands/commands.hpp index 25f6aa9..43f084c 100644 --- a/src/interfaces/cli/commands/commands.hpp +++ b/src/interfaces/cli/commands/commands.hpp @@ -7,109 +7,127 @@ namespace cli class AsciiCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + AsciiCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class CapturesCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + CapturesCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class DisplayCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + DisplayCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class DividedPerftCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + DividedPerftCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class EvalCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + EvalCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class ExitCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + ExitCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class GetFenCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + GetFenCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class HelpCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + HelpCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class InfoCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + InfoCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class MagicsCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + MagicsCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class MoveCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + MoveCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class MovesCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + MovesCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class NewCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + NewCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class PerftCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + PerftCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class PLMovesCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + PLMovesCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class RotateCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + RotateCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class SetFenCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + SetFenCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class SwitchCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + SwitchCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; } // namespace cli diff --git a/src/interfaces/cli/commands/display.cpp b/src/interfaces/cli/commands/display.cpp index aaa04ca..2636eeb 100644 --- a/src/interfaces/cli/commands/display.cpp +++ b/src/interfaces/cli/commands/display.cpp @@ -1,6 +1,6 @@ #include -void cli::DisplayCommand::execute([[maybe_unused]] std::vector &args, Board &board) +void cli::DisplayCommand::execute([[maybe_unused]] std::vector &args) { - board.display(); + _board.display(); } \ No newline at end of file diff --git a/src/interfaces/cli/commands/dividedperft.cpp b/src/interfaces/cli/commands/dividedperft.cpp index 688bba7..0488066 100644 --- a/src/interfaces/cli/commands/dividedperft.cpp +++ b/src/interfaces/cli/commands/dividedperft.cpp @@ -35,7 +35,7 @@ static void dperft(int depth, Board &board) std::cout << "Nodes Per Second: " << (double)total_nodes / elapsed_seconds.count() << std::endl; } -void cli::DividedPerftCommand::execute(std::vector &args, Board &board) +void cli::DividedPerftCommand::execute(std::vector &args) { if (args.size() == 0) { @@ -56,7 +56,7 @@ void cli::DividedPerftCommand::execute(std::vector &args, Board &bo if (depth >= 0) { - dperft(depth, board); + dperft(depth, _board); } else { diff --git a/src/interfaces/cli/commands/eval.cpp b/src/interfaces/cli/commands/eval.cpp index 8f913a8..49c3aaa 100644 --- a/src/interfaces/cli/commands/eval.cpp +++ b/src/interfaces/cli/commands/eval.cpp @@ -4,7 +4,7 @@ #include -void cli::EvalCommand::execute([[maybe_unused]] std::vector &args, Board &board) +void cli::EvalCommand::execute([[maybe_unused]] std::vector &args) { - std::cout << "Static Evaluation: " << eval::eval(board) << std::endl; + std::cout << "Static Evaluation: " << eval::eval(_board) << std::endl; } diff --git a/src/interfaces/cli/commands/exit.cpp b/src/interfaces/cli/commands/exit.cpp index f5ef355..0778924 100644 --- a/src/interfaces/cli/commands/exit.cpp +++ b/src/interfaces/cli/commands/exit.cpp @@ -1,6 +1,6 @@ #include -void cli::ExitCommand::execute([[maybe_unused]] std::vector &args, [[maybe_unused]] Board &board) +void cli::ExitCommand::execute([[maybe_unused]] std::vector &args) { exit(EXIT_SUCCESS); } \ No newline at end of file diff --git a/src/interfaces/cli/commands/getfen.cpp b/src/interfaces/cli/commands/getfen.cpp index 5e85e1a..042f066 100644 --- a/src/interfaces/cli/commands/getfen.cpp +++ b/src/interfaces/cli/commands/getfen.cpp @@ -2,7 +2,7 @@ #include -void cli::GetFenCommand::execute([[maybe_unused]] std::vector &args, Board &board) +void cli::GetFenCommand::execute([[maybe_unused]] std::vector &args) { - std::cout << board.get_fen(); + std::cout << _board.get_fen(); } \ No newline at end of file diff --git a/src/interfaces/cli/commands/help.cpp b/src/interfaces/cli/commands/help.cpp index 6a37b6e..8e32814 100644 --- a/src/interfaces/cli/commands/help.cpp +++ b/src/interfaces/cli/commands/help.cpp @@ -2,7 +2,7 @@ #include -void cli::HelpCommand::execute([[maybe_unused]] std::vector &args, [[maybe_unused]] Board &board) +void cli::HelpCommand::execute([[maybe_unused]] std::vector &args) { std::cout << "ascii Toggles between ascii and utf-8 board representation\n" diff --git a/src/interfaces/cli/commands/info.cpp b/src/interfaces/cli/commands/info.cpp index b34e6f0..7129357 100644 --- a/src/interfaces/cli/commands/info.cpp +++ b/src/interfaces/cli/commands/info.cpp @@ -4,9 +4,9 @@ #include -void cli::InfoCommand::execute([[maybe_unused]] std::vector &args, Board &board) +void cli::InfoCommand::execute([[maybe_unused]] std::vector &args) { - std::string fen = board.get_fen(); + std::string fen = _board.get_fen(); std::vector split_fen = utils::tokenize(fen); std::cout << "Side to Play = " << split_fen[1] << "\nCastling Rights = " << split_fen[2] @@ -14,7 +14,7 @@ void cli::InfoCommand::execute([[maybe_unused]] std::vector &args, << "\nFifty Move Count = " << split_fen[4] << "\nFull Move Number = " << split_fen[5]; std::cout << "\nOccupied Squares:\n"; - bitboard::print(board.get_occupancies(BOTH)); - bitboard::print(board.get_occupancies(WHITE)); - bitboard::print(board.get_occupancies(BLACK)); + bitboard::print(_board.get_occupancies(BOTH)); + bitboard::print(_board.get_occupancies(WHITE)); + bitboard::print(_board.get_occupancies(BLACK)); } \ No newline at end of file diff --git a/src/interfaces/cli/commands/magics.cpp b/src/interfaces/cli/commands/magics.cpp index 19f41f3..99968d8 100644 --- a/src/interfaces/cli/commands/magics.cpp +++ b/src/interfaces/cli/commands/magics.cpp @@ -2,7 +2,7 @@ #include -void cli::MagicsCommand::execute([[maybe_unused]] std::vector &args, [[maybe_unused]] Board &board) +void cli::MagicsCommand::execute([[maybe_unused]] std::vector &args) { magics::generate(); } \ No newline at end of file diff --git a/src/interfaces/cli/commands/move.cpp b/src/interfaces/cli/commands/move.cpp index 558117d..730b287 100644 --- a/src/interfaces/cli/commands/move.cpp +++ b/src/interfaces/cli/commands/move.cpp @@ -4,18 +4,18 @@ #include -void cli::MoveCommand::execute(std::vector &args, Board &board) +void cli::MoveCommand::execute(std::vector &args) { if (args.size() == 0) { return; } - for (const Move &move : movegen::generateLegalMoves(board)) + for (const Move &move : movegen::generateLegalMoves(_board)) { if (move.get_uci() == args[0]) { - board.make_move(move); + _board.make_move(move); return; } } diff --git a/src/interfaces/cli/commands/moves.cpp b/src/interfaces/cli/commands/moves.cpp index 02719af..236ac40 100644 --- a/src/interfaces/cli/commands/moves.cpp +++ b/src/interfaces/cli/commands/moves.cpp @@ -5,9 +5,9 @@ #include #include -void cli::MovesCommand::execute([[maybe_unused]] std::vector &args, Board &board) +void cli::MovesCommand::execute([[maybe_unused]] std::vector &args) { - auto moves = movegen::generateLegalMoves(board); + auto moves = movegen::generateLegalMoves(_board); std::for_each(moves.begin(), moves.end(), [](const Move &move) { std::cout << move.get_uci() << "\n"; }); std::cout << "Total number of moves: " << moves.size() << std::endl; diff --git a/src/interfaces/cli/commands/new.cpp b/src/interfaces/cli/commands/new.cpp index 620605c..c2d0b1b 100644 --- a/src/interfaces/cli/commands/new.cpp +++ b/src/interfaces/cli/commands/new.cpp @@ -1,6 +1,6 @@ #include -void cli::NewCommand::execute([[maybe_unused]] std::vector &args, Board &board) +void cli::NewCommand::execute([[maybe_unused]] std::vector &args) { - board.set_starting_position(); + _board.set_starting_position(); } \ No newline at end of file diff --git a/src/interfaces/cli/commands/perft.cpp b/src/interfaces/cli/commands/perft.cpp index bd340a9..c7f7b20 100644 --- a/src/interfaces/cli/commands/perft.cpp +++ b/src/interfaces/cli/commands/perft.cpp @@ -18,7 +18,7 @@ static void timed_perft(int depth, Board &board) std::cout << "Nodes Per Second: " << (double)nodes / elapsed_seconds.count() << std::endl; } -void cli::PerftCommand::execute(std::vector &args, Board &board) +void cli::PerftCommand::execute(std::vector &args) { if (args.size() == 0) @@ -39,7 +39,7 @@ void cli::PerftCommand::execute(std::vector &args, Board &board) } if (depth >= 0) { - timed_perft(depth, board); + timed_perft(depth, _board); } else { diff --git a/src/interfaces/cli/commands/plmoves.cpp b/src/interfaces/cli/commands/plmoves.cpp index 6083f0d..2f7cb8b 100644 --- a/src/interfaces/cli/commands/plmoves.cpp +++ b/src/interfaces/cli/commands/plmoves.cpp @@ -5,9 +5,9 @@ #include #include -void cli::PLMovesCommand::execute([[maybe_unused]] std::vector &args, Board &board) +void cli::PLMovesCommand::execute([[maybe_unused]] std::vector &args) { - auto moves = movegen::generate_pseudo_legal_moves(board); + auto moves = movegen::generate_pseudo_legal_moves(_board); std::for_each(moves.begin(), moves.end(), [](const Move &move) { std::cout << move.get_uci() << "\n"; }); std::cout << "Total number of pseudo legal moves: " << moves.size() << std::endl; diff --git a/src/interfaces/cli/commands/rotate.cpp b/src/interfaces/cli/commands/rotate.cpp index 7c5b15e..d85fa58 100644 --- a/src/interfaces/cli/commands/rotate.cpp +++ b/src/interfaces/cli/commands/rotate.cpp @@ -2,7 +2,7 @@ #include -void cli::RotateCommand::execute([[maybe_unused]] std::vector &args, Board &board) +void cli::RotateCommand::execute([[maybe_unused]] std::vector &args) { - std::cout << (board.rotate_display() ? "white" : "black") << " is now on bottom" << std::endl; + std::cout << (_board.rotate_display() ? "white" : "black") << " is now on bottom" << std::endl; } \ No newline at end of file diff --git a/src/interfaces/cli/commands/setfen.cpp b/src/interfaces/cli/commands/setfen.cpp index 64c32bb..1da12c7 100644 --- a/src/interfaces/cli/commands/setfen.cpp +++ b/src/interfaces/cli/commands/setfen.cpp @@ -3,7 +3,7 @@ #include #include -void cli::SetFenCommand::execute(std::vector &args, Board &board) +void cli::SetFenCommand::execute(std::vector &args) { if (!utils::is_fen_valid(args)) { @@ -11,5 +11,5 @@ void cli::SetFenCommand::execute(std::vector &args, Board &board) return; } - board.set_from_fen(args[0], args[1], args[2], args[3], args[4], args[5]); + _board.set_from_fen(args[0], args[1], args[2], args[3], args[4], args[5]); } diff --git a/src/interfaces/cli/commands/switch.cpp b/src/interfaces/cli/commands/switch.cpp index d19a32a..1170bf6 100644 --- a/src/interfaces/cli/commands/switch.cpp +++ b/src/interfaces/cli/commands/switch.cpp @@ -2,7 +2,7 @@ #include -void cli::SwitchCommand::execute([[maybe_unused]] std::vector &args, Board &board) +void cli::SwitchCommand::execute([[maybe_unused]] std::vector &args) { - std::cout << "side to play is now " << (board.switch_side_to_move() == WHITE ? "white" : "black") << std::endl; + std::cout << "side to play is now " << (_board.switch_side_to_move() == WHITE ? "white" : "black") << std::endl; } \ No newline at end of file diff --git a/src/interfaces/uci/commands/commands.hpp b/src/interfaces/uci/commands/commands.hpp index a29f45c..6f31e6e 100644 --- a/src/interfaces/uci/commands/commands.hpp +++ b/src/interfaces/uci/commands/commands.hpp @@ -2,42 +2,54 @@ #include +#include + namespace uci { class UCICommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + UCICommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class IsReadyCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + IsReadyCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class UCINewGameCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + UCINewGameCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class PositionCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + PositionCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class DisplayCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + DisplayCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; class GoCommand : public utils::Command { + private: + MovePicker _move_picker; + public: - void execute([[maybe_unused]] std::vector &args, Board &board); + GoCommand(Board &board) : Command(board), _move_picker(MovePicker(board)){}; + void execute([[maybe_unused]] std::vector &args); }; class QuitCommand : public utils::Command { public: - void execute([[maybe_unused]] std::vector &args, Board &board); + QuitCommand(Board &board) : Command(board){}; + void execute([[maybe_unused]] std::vector &args); }; } // namespace cli diff --git a/src/interfaces/uci/commands/display.cpp b/src/interfaces/uci/commands/display.cpp index 434f56c..61f4f92 100644 --- a/src/interfaces/uci/commands/display.cpp +++ b/src/interfaces/uci/commands/display.cpp @@ -1,6 +1,6 @@ #include -void uci::DisplayCommand::execute([[maybe_unused]] std::vector &args, Board &board) +void uci::DisplayCommand::execute([[maybe_unused]] std::vector &args) { - board.display(); + _board.display(); } \ No newline at end of file diff --git a/src/interfaces/uci/commands/exit.cpp b/src/interfaces/uci/commands/exit.cpp index 5727d09..0a31413 100644 --- a/src/interfaces/uci/commands/exit.cpp +++ b/src/interfaces/uci/commands/exit.cpp @@ -1,6 +1,6 @@ #include -void uci::QuitCommand::execute([[maybe_unused]] std::vector &args, [[maybe_unused]] Board &board) +void uci::QuitCommand::execute([[maybe_unused]] std::vector &args) { exit(EXIT_SUCCESS); } \ No newline at end of file diff --git a/src/interfaces/uci/commands/go.cpp b/src/interfaces/uci/commands/go.cpp index 58e135a..e15a111 100644 --- a/src/interfaces/uci/commands/go.cpp +++ b/src/interfaces/uci/commands/go.cpp @@ -105,7 +105,7 @@ static void search(std::future future, MovePicker &move_picker, MovePicker } } -void uci::GoCommand::execute(std::vector &args, Board &board) +void uci::GoCommand::execute(std::vector &args) { enum ArgCommand : int { @@ -207,23 +207,21 @@ void uci::GoCommand::execute(std::vector &args, Board &board) } MovePicker::SearchResult result; - MovePicker move_picker = MovePicker(board); - if (!depth && !infinite && wtime && btime) { - move_picker.set_max_depth(DEFAULT_MAX_DEPTH); + _move_picker.set_max_depth(DEFAULT_MAX_DEPTH); } else { - move_picker.set_max_depth(DEFAULT_MIN_DEPTH); + _move_picker.set_max_depth(DEFAULT_MIN_DEPTH); } std::promise signal_exit; std::future signal_exit_future = signal_exit.get_future(); - std::thread search_thread(search, std::move(signal_exit_future), std::ref(move_picker), std::ref(result)); - if ((board.get_side_to_move() == WHITE && wtime) || (board.get_side_to_move() == BLACK && btime)) + std::thread search_thread(search, std::move(signal_exit_future), std::ref(_move_picker), std::ref(result)); + if ((_board.get_side_to_move() == WHITE && wtime) || (_board.get_side_to_move() == BLACK && btime)) { - std::this_thread::sleep_for(std::chrono::milliseconds(timectl::get_time_budget_ms(wtime, btime, board))); + std::this_thread::sleep_for(std::chrono::milliseconds(timectl::get_time_budget_ms(wtime, btime, _board))); signal_exit.set_value(); } search_thread.join(); diff --git a/src/interfaces/uci/commands/isready.cpp b/src/interfaces/uci/commands/isready.cpp index 504e4e6..598dc3a 100644 --- a/src/interfaces/uci/commands/isready.cpp +++ b/src/interfaces/uci/commands/isready.cpp @@ -2,7 +2,7 @@ #include -void uci::IsReadyCommand::execute([[maybe_unused]] std::vector &args, [[maybe_unused]] Board &board) +void uci::IsReadyCommand::execute([[maybe_unused]] std::vector &args) { std::cout << "readyok" << std::endl; } \ No newline at end of file diff --git a/src/interfaces/uci/commands/position.cpp b/src/interfaces/uci/commands/position.cpp index 9e1bf65..132c0dc 100644 --- a/src/interfaces/uci/commands/position.cpp +++ b/src/interfaces/uci/commands/position.cpp @@ -31,7 +31,7 @@ static void handle_moves(std::vector &moves, Board &board) } } -void uci::PositionCommand::execute(std::vector &args, Board &board) +void uci::PositionCommand::execute(std::vector &args) { if (args.size() == 0) { @@ -42,7 +42,7 @@ void uci::PositionCommand::execute(std::vector &args, Board &board) { args.erase(args.begin()); - board.set_starting_position(); + _board.set_starting_position(); } else if (args[0] == "fen") { @@ -60,7 +60,7 @@ void uci::PositionCommand::execute(std::vector &args, Board &board) } else { - board.set_from_fen(args[0], args[1], args[2], args[3], args[4], args[5]); + _board.set_from_fen(args[0], args[1], args[2], args[3], args[4], args[5]); args.erase(args.begin(), args.begin() + 6); } } @@ -78,6 +78,6 @@ void uci::PositionCommand::execute(std::vector &args, Board &board) { args.erase(args.begin()); - handle_moves(args, board); + handle_moves(args, _board); } } diff --git a/src/interfaces/uci/commands/uci.cpp b/src/interfaces/uci/commands/uci.cpp index 1cab709..6c33584 100644 --- a/src/interfaces/uci/commands/uci.cpp +++ b/src/interfaces/uci/commands/uci.cpp @@ -2,7 +2,7 @@ #include -void uci::UCICommand::execute([[maybe_unused]] std::vector &args, [[maybe_unused]] Board &board) +void uci::UCICommand::execute([[maybe_unused]] std::vector &args) { std::cout << "id name Chess Engine\n"; std::cout << "id name Chess Engine\n"; diff --git a/src/interfaces/uci/commands/ucinewgame.cpp b/src/interfaces/uci/commands/ucinewgame.cpp index bde04c3..1a5ebdb 100644 --- a/src/interfaces/uci/commands/ucinewgame.cpp +++ b/src/interfaces/uci/commands/ucinewgame.cpp @@ -1,6 +1,6 @@ #include -void uci::UCINewGameCommand::execute([[maybe_unused]] std::vector &args, Board &board) +void uci::UCINewGameCommand::execute([[maybe_unused]] std::vector &args) { - board.set_starting_position(); + _board.set_starting_position(); } diff --git a/src/interfaces/uci/uci.cpp b/src/interfaces/uci/uci.cpp index b1a00a9..c4e6d1c 100644 --- a/src/interfaces/uci/uci.cpp +++ b/src/interfaces/uci/uci.cpp @@ -27,13 +27,13 @@ namespace uci Board board = Board(); // TODO: use a map instead of if statements - uci::UCICommand uciCommand; - uci::IsReadyCommand isReadyCommand; - uci::UCINewGameCommand uciNewGameCommand; - uci::PositionCommand positionCommand; - uci::DisplayCommand displayCommand; - uci::GoCommand goCommand; - uci::QuitCommand quitCommand; + uci::UCICommand uciCommand = UCICommand(board); + uci::IsReadyCommand isReadyCommand = IsReadyCommand(board); + uci::UCINewGameCommand uciNewGameCommand = UCINewGameCommand(board); + uci::PositionCommand positionCommand = PositionCommand(board); + uci::DisplayCommand displayCommand = DisplayCommand(board); + uci::GoCommand goCommand = GoCommand(board); + uci::QuitCommand quitCommand = QuitCommand(board); std::string line; while (std::getline(std::cin, line)) @@ -52,31 +52,31 @@ namespace uci if (cmd == "uci") { - uciCommand.execute(args, board); + uciCommand.execute(args); } else if (cmd == "isready") { - isReadyCommand.execute(args, board); + isReadyCommand.execute(args); } else if (cmd == "ucinewgame") { - uciNewGameCommand.execute(args, board); + uciNewGameCommand.execute(args); } else if (cmd == "position") { - positionCommand.execute(args, board); + positionCommand.execute(args); } else if (cmd == "d") { - displayCommand.execute(args, board); + displayCommand.execute(args); } else if (cmd == "go") { - goCommand.execute(args, board); + goCommand.execute(args); } else if (cmd == "quit") { - quitCommand.execute(args, board); + quitCommand.execute(args); } } } diff --git a/src/interfaces/utils.hpp b/src/interfaces/utils.hpp index bae9094..3b28db5 100644 --- a/src/interfaces/utils.hpp +++ b/src/interfaces/utils.hpp @@ -31,8 +31,12 @@ namespace utils */ class Command { + protected: + Board &_board; + public: - virtual void execute(std::vector &args, Board &board) = 0; + Command(Board &board) : _board(board){}; + virtual void execute(std::vector &args) = 0; }; } // namespace utils \ No newline at end of file From 80c466b56d748ecd09ea94aa59e7aa29556ce0d7 Mon Sep 17 00:00:00 2001 From: jsilll Date: Sun, 14 Aug 2022 12:06:24 +0100 Subject: [PATCH 10/22] removed constructor from ttable --- src/engine/movepicker/ttable.hpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/engine/movepicker/ttable.hpp b/src/engine/movepicker/ttable.hpp index e1788bf..ddd79ac 100644 --- a/src/engine/movepicker/ttable.hpp +++ b/src/engine/movepicker/ttable.hpp @@ -5,32 +5,31 @@ class TTable { private: - struct TTEntry // transposition table entry + struct TTEntry { u64 hash_key; int depth; int flag; int score; - // TODO: list of moves + // TODO: List Of Moves }; static const int TABLE_SIZE = 0x400000; - TTEntry *_table; + TTEntry *_table = new TTEntry[TABLE_SIZE]; public: static const int HASH_FLAG_SCORE = 0; static const int HASH_FLAG_ALPHA = 1; static const int HASH_FLAG_BETA = 2; - struct TTOutput // transposition table entry + struct TTOutput { bool found; int score; - // TODO: list of moves + // TODO: List Of Moves }; - TTable() : _table(new TTEntry[TABLE_SIZE]){}; ~TTable(); void clear(); From fe42084f6437fc87af8e1940d58b4b1fb45f534b Mon Sep 17 00:00:00 2001 From: jsilll Date: Sun, 14 Aug 2022 12:07:21 +0100 Subject: [PATCH 11/22] get_pv typo --- src/engine/movepicker/movepicker.cpp | 4 ++-- src/engine/movepicker/pvtable.cpp | 2 +- src/engine/movepicker/pvtable.hpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/movepicker/movepicker.cpp b/src/engine/movepicker/movepicker.cpp index c64e195..1e0dd45 100644 --- a/src/engine/movepicker/movepicker.cpp +++ b/src/engine/movepicker/movepicker.cpp @@ -399,7 +399,7 @@ MovePicker::SearchResult MovePicker::find_best_move() beta = score + WINDOW_EXPANSION; } - auto result = SearchResult{alpha, _current_nodes, _pv_table.get_pV()}; + auto result = SearchResult{alpha, _current_nodes, _pv_table.get_pv()}; return result; } @@ -414,7 +414,7 @@ MovePicker::SearchResult MovePicker::find_best_move(int depth, int alpha, int be { this->clear_search_counters(); int score = search(depth, alpha, beta); - auto result = SearchResult{score, _current_nodes, _pv_table.get_pV()}; + auto result = SearchResult{score, _current_nodes, _pv_table.get_pv()}; return result; } diff --git a/src/engine/movepicker/pvtable.cpp b/src/engine/movepicker/pvtable.cpp index 8c0b3d5..6f3e315 100644 --- a/src/engine/movepicker/pvtable.cpp +++ b/src/engine/movepicker/pvtable.cpp @@ -9,7 +9,7 @@ Move PVTable::get_pv_move(int depth) return _table[depth][depth]; } -std::vector PVTable::get_pV() +std::vector PVTable::get_pv() { std::vector res; for (int i = 0; i < _length[0]; i++) diff --git a/src/engine/movepicker/pvtable.hpp b/src/engine/movepicker/pvtable.hpp index aa96333..9e1a5b6 100644 --- a/src/engine/movepicker/pvtable.hpp +++ b/src/engine/movepicker/pvtable.hpp @@ -14,7 +14,7 @@ class PVTable public: Move get_pv_move(int depth); - std::vector get_pV(); + std::vector get_pv(); void clear(); void set_length(int depth); From c6523c741d446a1702822dce3169312079fd1cd3 Mon Sep 17 00:00:00 2001 From: jsilll Date: Sun, 14 Aug 2022 18:43:59 +0100 Subject: [PATCH 12/22] fixed mate representation when losing --- src/interfaces/uci/commands/go.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interfaces/uci/commands/go.cpp b/src/interfaces/uci/commands/go.cpp index e15a111..b4d4c5e 100644 --- a/src/interfaces/uci/commands/go.cpp +++ b/src/interfaces/uci/commands/go.cpp @@ -34,11 +34,11 @@ static UCIScore score_to_uci(int score) } else { - int check_mate_delta = -MIN_EVAL - score; + int check_mate_delta = -(MIN_EVAL - score); int moves_for_mate = (check_mate_delta / 2) + (check_mate_delta % 2); - if (-16 <= moves_for_mate && moves_for_mate < 0) + if (0 < moves_for_mate && moves_for_mate <= 16) { - return UCIScore{std::string("mate ") + std::to_string(moves_for_mate), true}; + return UCIScore{std::string("mate ") + std::to_string(-moves_for_mate), true}; } } From b3c957a1971a33e04a5e3175903f3e4aaa13beea Mon Sep 17 00:00:00 2001 From: jsilll Date: Mon, 15 Aug 2022 10:57:11 +0100 Subject: [PATCH 13/22] small refactor --- src/engine/bitboard.hpp | 8 +++++++- src/engine/board.cpp | 8 ++++---- src/engine/board.hpp | 8 ++++++-- src/engine/constants.hpp | 5 ----- src/engine/movegen/attacks.hpp | 2 +- src/engine/movegen/magics.hpp | 1 + src/engine/movepicker/ttable.hpp | 2 +- src/interfaces/cli/commands/info.cpp | 29 ++++++++++++++++++++++++++++ 8 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/engine/bitboard.hpp b/src/engine/bitboard.hpp index 46ff290..b0c8120 100644 --- a/src/engine/bitboard.hpp +++ b/src/engine/bitboard.hpp @@ -1,9 +1,15 @@ #pragma once -#include +#include + +typedef std::uint64_t u64; + +constexpr u64 ONE = 1ULL; +constexpr u64 ZERO = 0ULL; namespace bitboard { + // Common Bitboard Operations inline bool get_bit(u64 bb, int sq) { return ((bb >> sq) & 1ULL); } inline void pop_bit(u64 &bn, int sq) { bn &= ~(1ULL << sq); } diff --git a/src/engine/board.cpp b/src/engine/board.cpp index e42254b..97f2b28 100644 --- a/src/engine/board.cpp +++ b/src/engine/board.cpp @@ -53,7 +53,7 @@ void Board::update_occupancies() _occupancies[BOTH] |= _occupancies[BLACK]; } -void Board::update_bb_from_squares() +void Board::update_bitboards_from_squares() { for (int piece_type = PAWN; piece_type < N_PIECES; piece_type++) { @@ -319,9 +319,9 @@ void Board::clear() { _pieces[color][piece_type] = ZERO; } - _occupancies[color] = ZERO; } + _occupancies[BOTH] = ZERO; _to_move = WHITE; @@ -329,7 +329,7 @@ void Board::clear() _en_passant_square = EMPTY_SQUARE; _half_move_clock = 0; _full_move_number = 0; - _hash_key = 0ULL; // replace with original + _hash_key = ZERO; _white_on_bottom = true; @@ -474,7 +474,7 @@ void Board::set_from_fen(const std::string &piece_placements, _full_move_number = std::stoi(full_move_number); - this->update_bb_from_squares(); + this->update_bitboards_from_squares(); _hash_key = zobrist::generate_hash_key(*this); } diff --git a/src/engine/board.hpp b/src/engine/board.hpp index 864ae7a..c5c609f 100644 --- a/src/engine/board.hpp +++ b/src/engine/board.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include class Move; @@ -32,12 +33,12 @@ class Board u64 _pieces[N_SIDES][N_PIECES]; u64 _occupancies[N_SIDES + 1]; - bool _ascii; + bool _ascii{}; bool _white_on_bottom; Piece _square[N_SQUARES]; void update_occupancies(); - void update_bb_from_squares(); + void update_bitboards_from_squares(); public: Board(); @@ -82,6 +83,7 @@ class Board bool rotate_display(); }; +#ifdef DEBUG inline bool operator==(const Board::Piece &p1, const Board::Piece &p2) { return p1.color == p2.color && p1.type == p2.type; @@ -162,3 +164,5 @@ inline bool operator==(const Board &b1, const Board &b2) return true; } + +#endif diff --git a/src/engine/constants.hpp b/src/engine/constants.hpp index e082760..6503164 100644 --- a/src/engine/constants.hpp +++ b/src/engine/constants.hpp @@ -2,11 +2,6 @@ #include -typedef uint64_t u64; - -constexpr u64 ONE = 1ULL; -constexpr u64 ZERO = 0ULL; - // clang-format off enum Square : int { A1, B1, C1, D1, E1, F1, G1, H1, diff --git a/src/engine/movegen/attacks.hpp b/src/engine/movegen/attacks.hpp index eb0a43d..7397fd8 100644 --- a/src/engine/movegen/attacks.hpp +++ b/src/engine/movegen/attacks.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace attacks { diff --git a/src/engine/movegen/magics.hpp b/src/engine/movegen/magics.hpp index 5f15861..925da96 100644 --- a/src/engine/movegen/magics.hpp +++ b/src/engine/movegen/magics.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include namespace magics diff --git a/src/engine/movepicker/ttable.hpp b/src/engine/movepicker/ttable.hpp index ddd79ac..e943f9c 100644 --- a/src/engine/movepicker/ttable.hpp +++ b/src/engine/movepicker/ttable.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include class TTable { diff --git a/src/interfaces/cli/commands/info.cpp b/src/interfaces/cli/commands/info.cpp index 7129357..76d0f43 100644 --- a/src/interfaces/cli/commands/info.cpp +++ b/src/interfaces/cli/commands/info.cpp @@ -14,7 +14,36 @@ void cli::InfoCommand::execute([[maybe_unused]] std::vector &args) << "\nFifty Move Count = " << split_fen[4] << "\nFull Move Number = " << split_fen[5]; std::cout << "\nOccupied Squares:\n"; + + std::cout << "WHITE, PAWN" << std::endl; + bitboard::print(_board.get_pieces(WHITE, PAWN)); + std::cout << "WHITE, KNIGHT" << std::endl; + bitboard::print(_board.get_pieces(WHITE, KNIGHT)); + std::cout << "WHITE, BISHOP" << std::endl; + bitboard::print(_board.get_pieces(WHITE, BISHOP)); + std::cout << "WHITE, ROOK" << std::endl; + bitboard::print(_board.get_pieces(WHITE, ROOK)); + std::cout << "WHITE, QUEEN" << std::endl; + bitboard::print(_board.get_pieces(WHITE, QUEEN)); + std::cout << "WHITE, KING" << std::endl; + bitboard::print(_board.get_pieces(WHITE, KING)); + std::cout << "BLACK, PAWN" << std::endl; + bitboard::print(_board.get_pieces(BLACK, PAWN)); + std::cout << "BLACK, KNIGHT" << std::endl; + bitboard::print(_board.get_pieces(BLACK, KNIGHT)); + std::cout << "BLACK, BISHOP" << std::endl; + bitboard::print(_board.get_pieces(BLACK, BISHOP)); + std::cout << "BLACK, ROOK" << std::endl; + bitboard::print(_board.get_pieces(BLACK, ROOK)); + std::cout << "BLACK, QUEEN" << std::endl; + bitboard::print(_board.get_pieces(BLACK, QUEEN)); + std::cout << "BLACK, KING" << std::endl; + bitboard::print(_board.get_pieces(BLACK, KING)); + + std::cout << "BOTH" << std::endl; bitboard::print(_board.get_occupancies(BOTH)); + std::cout << "WHITE" << std::endl; bitboard::print(_board.get_occupancies(WHITE)); + std::cout << "BLACK" << std::endl; bitboard::print(_board.get_occupancies(BLACK)); } \ No newline at end of file From 73d28d8169f6fc0c22d36fd3d6cc4d4e84a5b6f1 Mon Sep 17 00:00:00 2001 From: henrique Date: Fri, 19 Aug 2022 18:13:24 +0100 Subject: [PATCH 14/22] added list of moves to tt (not sure if correct) --- CMakeLists.txt | 2 +- src/engine/movepicker/movepicker.cpp | 23 ++++++++++++----------- src/engine/movepicker/pvtable.cpp | 21 +++++++++++++++++++++ src/engine/movepicker/pvtable.hpp | 2 ++ src/engine/movepicker/ttable.cpp | 14 +++++++++++--- src/engine/movepicker/ttable.hpp | 11 +++++++---- 6 files changed, 54 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 828029f..ddb9a8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) project(chess_engine VERSION 1.0 DESCRIPTION "Chess Engine") set(CMAKE_CXX_FLAGS "-Wall -Wpedantic -Wextra -Wconversion -Werror") -set(CMAKE_CXX_FLAGS_RELEASE "-O3") +# set(CMAKE_CXX_FLAGS_RELEASE "-O3") set(CMAKE_CXX_FLAGS_DEBUG "-g") if(NOT CMAKE_BUILD_TYPE) diff --git a/src/engine/movepicker/movepicker.cpp b/src/engine/movepicker/movepicker.cpp index 1e0dd45..de5097b 100644 --- a/src/engine/movepicker/movepicker.cpp +++ b/src/engine/movepicker/movepicker.cpp @@ -106,6 +106,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) if (hash_read.found) { + _pv_table.add_pv_from_depth(hash_read.moves, _current_depth); return hash_read.score; } @@ -138,7 +139,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) _current_depth -= 2; if (score >= beta) { - _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_BETA, beta); + _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); return beta; } } @@ -210,7 +211,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) _board.unmake_move(move, state); - _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_BETA, beta); + _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); return beta; } else if (score > alpha) @@ -240,16 +241,16 @@ int MovePicker::negamax(int alpha, int beta, int depth) int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_side_to_move(), KING)); if (_board.is_square_attacked(king_sq, _board.get_opponent())) { - _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_SCORE, MIN_EVAL + _current_depth); + _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_SCORE, MIN_EVAL + _current_depth, _pv_table.get_pv_from_depth(_current_depth)); return MIN_EVAL + _current_depth; } // Stale Mate - _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_SCORE, 0); + _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_SCORE, 0, _pv_table.get_pv_from_depth(_current_depth)); return 0; } - _tt.set_entry(hash_key, depth, alpha_cutoff, alpha); + _tt.set_entry(hash_key, depth, alpha_cutoff, alpha, _pv_table.get_pv_from_depth(_current_depth)); return alpha; } @@ -259,7 +260,7 @@ int MovePicker::quiescence(int alpha, int beta) if (_board.get_half_move_clock() == 100) { - _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_SCORE, 0); + _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_SCORE, 0, _pv_table.get_pv_from_depth(_current_depth)); return 0; } @@ -268,11 +269,11 @@ int MovePicker::quiescence(int alpha, int beta) int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_side_to_move(), KING)); if (_board.is_square_attacked(king_sq, _board.get_opponent())) { - _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_SCORE, MIN_EVAL + _current_depth); + _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_SCORE, MIN_EVAL + _current_depth, _pv_table.get_pv_from_depth(_current_depth)); return MIN_EVAL + _current_depth; } - _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_SCORE, 0); + _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_SCORE, 0, _pv_table.get_pv_from_depth(_current_depth)); return 0; } @@ -280,7 +281,7 @@ int MovePicker::quiescence(int alpha, int beta) if (stand_pat >= beta) { - _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_BETA, beta); + _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); return beta; } @@ -309,7 +310,7 @@ int MovePicker::quiescence(int alpha, int beta) if (score >= beta) { _board.unmake_move(capture, state); - _tt.set_entry(hash_key, 0, TTable::HASH_FLAG_BETA, beta); + _tt.set_entry(hash_key, 0, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); return beta; } if (score > alpha) @@ -322,7 +323,7 @@ int MovePicker::quiescence(int alpha, int beta) _board.unmake_move(capture, state); } - _tt.set_entry(_board.get_hash_key(), 0, alpha_cutoff, alpha); + _tt.set_entry(_board.get_hash_key(), 0, alpha_cutoff, alpha, _pv_table.get_pv_from_depth(_current_depth)); return alpha; } diff --git a/src/engine/movepicker/pvtable.cpp b/src/engine/movepicker/pvtable.cpp index 6f3e315..04afe38 100644 --- a/src/engine/movepicker/pvtable.cpp +++ b/src/engine/movepicker/pvtable.cpp @@ -20,6 +20,17 @@ std::vector PVTable::get_pv() return res; } +std::vector PVTable::get_pv_from_depth(int start_depth) +{ + std::vector res; + for (int i = start_depth; i < _length[start_depth]; i++) + { + res.push_back(_table[start_depth][i]); + } + + return res; +} + void PVTable::add(Move const move, int depth) { // Write Principal Variation Move @@ -32,6 +43,16 @@ void PVTable::add(Move const move, int depth) _length[depth] = _length[depth + 1]; } +void PVTable::add_pv_from_depth(std::vector moves, int starting_depth) +{ + std::reverse(moves.begin(), moves.end()); + for (const Move &move : moves) + { + this->add(move, starting_depth); + starting_depth++; + } +} + void PVTable::clear() { memset(_table, 0, sizeof(_table)); diff --git a/src/engine/movepicker/pvtable.hpp b/src/engine/movepicker/pvtable.hpp index 9e1a5b6..a9dd6b7 100644 --- a/src/engine/movepicker/pvtable.hpp +++ b/src/engine/movepicker/pvtable.hpp @@ -15,8 +15,10 @@ class PVTable public: Move get_pv_move(int depth); std::vector get_pv(); + std::vector get_pv_from_depth(int start_depth); void clear(); void set_length(int depth); void add(Move const move, int depth); + void add_pv_from_depth(std::vector moves, int starting_depth); }; \ No newline at end of file diff --git a/src/engine/movepicker/ttable.cpp b/src/engine/movepicker/ttable.cpp index 86a7b7d..b03ce78 100644 --- a/src/engine/movepicker/ttable.cpp +++ b/src/engine/movepicker/ttable.cpp @@ -10,37 +10,45 @@ TTable::~TTable() void TTable::clear() { - memset(_table, 0, TABLE_SIZE); + for (int p = 0; p < TABLE_SIZE; p++) + { + _table[p] = {0ULL, 0, 0, 0, {}}; + } } -void TTable::set_entry([[maybe_unused]] u64 hash_key, int depth, int flag, int score) +void TTable::set_entry([[maybe_unused]] u64 hash_key, int depth, int flag, int score, std::vector moves) { int entry = hash_key % TABLE_SIZE; _table[entry].depth = depth; _table[entry].flag = flag; _table[entry].score = score; + _table[entry].moves = moves; } TTable::TTOutput TTable::read_hash(u64 hash_key, int alpha, int beta, int depth) { - TTOutput search_result = {false, 0}; + TTOutput search_result = {false, 0, {}}; TTEntry hash_entry = _table[hash_key % TABLE_SIZE]; if ((hash_entry.hash_key == hash_key) && (hash_entry.depth >= depth)) { search_result.found = true; + search_result.moves = hash_entry.moves; if (hash_entry.flag == HASH_FLAG_SCORE) { search_result.score = hash_entry.score; + search_result.moves = hash_entry.moves; } if ((hash_entry.flag == HASH_FLAG_ALPHA) && (hash_entry.score <= alpha)) { search_result.score = alpha; + search_result.moves = hash_entry.moves; } if ((hash_entry.flag == HASH_FLAG_BETA) && (hash_entry.score > beta)) { search_result.score = beta; + search_result.moves = hash_entry.moves; } } diff --git a/src/engine/movepicker/ttable.hpp b/src/engine/movepicker/ttable.hpp index e943f9c..2c8d7ff 100644 --- a/src/engine/movepicker/ttable.hpp +++ b/src/engine/movepicker/ttable.hpp @@ -1,6 +1,9 @@ #pragma once #include +#include + +#include class TTable { @@ -11,7 +14,7 @@ class TTable int depth; int flag; int score; - // TODO: List Of Moves + std::vector moves; }; static const int TABLE_SIZE = 0x400000; @@ -27,12 +30,12 @@ class TTable { bool found; int score; - // TODO: List Of Moves + std::vector moves; }; ~TTable(); void clear(); - void set_entry(u64 hash_key, int depth, int flag, int score); + void set_entry(u64 hash_key, int depth, int flag, int score, std::vector moves); TTOutput read_hash(u64 hash_key, int alpha, int beta, int depth); -}; \ No newline at end of file +}; // TODO: List Of Moves \ No newline at end of file From 3a72b77d505d2808f1d2482223efadcf544818ef Mon Sep 17 00:00:00 2001 From: jsilll Date: Mon, 22 Aug 2022 00:23:33 +0100 Subject: [PATCH 15/22] Minor Fixes --- CMakeLists.txt | 2 +- src/engine/movepicker/pvtable.cpp | 16 ++++++++++++---- src/engine/movepicker/ttable.cpp | 2 +- src/interfaces/uci/commands/go.cpp | 5 +++++ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ddb9a8c..828029f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) project(chess_engine VERSION 1.0 DESCRIPTION "Chess Engine") set(CMAKE_CXX_FLAGS "-Wall -Wpedantic -Wextra -Wconversion -Werror") -# set(CMAKE_CXX_FLAGS_RELEASE "-O3") +set(CMAKE_CXX_FLAGS_RELEASE "-O3") set(CMAKE_CXX_FLAGS_DEBUG "-g") if(NOT CMAKE_BUILD_TYPE) diff --git a/src/engine/movepicker/pvtable.cpp b/src/engine/movepicker/pvtable.cpp index 04afe38..79a8509 100644 --- a/src/engine/movepicker/pvtable.cpp +++ b/src/engine/movepicker/pvtable.cpp @@ -3,6 +3,7 @@ #include #include +#include Move PVTable::get_pv_move(int depth) { @@ -45,11 +46,18 @@ void PVTable::add(Move const move, int depth) void PVTable::add_pv_from_depth(std::vector moves, int starting_depth) { - std::reverse(moves.begin(), moves.end()); - for (const Move &move : moves) + int last_depth = starting_depth + (int)moves.size() - 1; + + // Update PV with moves from starting_depth to last depth (reverse order) + for (int i = (int)moves.size() - 1; i >= 0; i--) + { + this->add(moves[(std::vector::size_type)i], last_depth--); + } + + // Update the Higher Depths + for (int depth = starting_depth - 1; depth >= 0; depth--) { - this->add(move, starting_depth); - starting_depth++; + memcpy(&_table[depth][depth + 1], &_table[depth + 1][depth + 1], (unsigned long)_length[depth + 1] * sizeof(int)); } } diff --git a/src/engine/movepicker/ttable.cpp b/src/engine/movepicker/ttable.cpp index b03ce78..82c9030 100644 --- a/src/engine/movepicker/ttable.cpp +++ b/src/engine/movepicker/ttable.cpp @@ -16,7 +16,7 @@ void TTable::clear() } } -void TTable::set_entry([[maybe_unused]] u64 hash_key, int depth, int flag, int score, std::vector moves) +void TTable::set_entry(u64 hash_key, int depth, int flag, int score, std::vector moves) { int entry = hash_key % TABLE_SIZE; _table[entry].depth = depth; diff --git a/src/interfaces/uci/commands/go.cpp b/src/interfaces/uci/commands/go.cpp index b4d4c5e..d4654c0 100644 --- a/src/interfaces/uci/commands/go.cpp +++ b/src/interfaces/uci/commands/go.cpp @@ -81,6 +81,11 @@ static void search(std::future future, MovePicker &move_picker, MovePicker result = move_picker.find_best_move(depth, alpha, beta); auto end = std::chrono::system_clock::now(); + if (depth == 1 && result.nodes == 1) + { + break; + } + bool found_mate = display_search_iteration(result, depth, end - start); if (found_mate) { From b4a0867014a237aadaa7a08c7d0c0c1d31e57e7f Mon Sep 17 00:00:00 2001 From: jsilll Date: Mon, 22 Aug 2022 00:26:51 +0100 Subject: [PATCH 16/22] small variable refactor --- src/engine/movepicker/pvtable.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/movepicker/pvtable.cpp b/src/engine/movepicker/pvtable.cpp index 79a8509..e4fc84a 100644 --- a/src/engine/movepicker/pvtable.cpp +++ b/src/engine/movepicker/pvtable.cpp @@ -55,9 +55,9 @@ void PVTable::add_pv_from_depth(std::vector moves, int starting_depth) } // Update the Higher Depths - for (int depth = starting_depth - 1; depth >= 0; depth--) + for (int current_depth = starting_depth - 1; current_depth >= 0; current_depth--) { - memcpy(&_table[depth][depth + 1], &_table[depth + 1][depth + 1], (unsigned long)_length[depth + 1] * sizeof(int)); + memcpy(&_table[current_depth][current_depth + 1], &_table[current_depth + 1][current_depth + 1], (unsigned long)_length[current_depth + 1] * sizeof(int)); } } From 755f737760a5449e3e33e0d39c865a02910ca361 Mon Sep 17 00:00:00 2001 From: jsilll Date: Mon, 22 Aug 2022 12:18:08 +0100 Subject: [PATCH 17/22] small fix on only one legal move edge case --- src/engine/movepicker/movepicker.cpp | 2 +- src/interfaces/uci/commands/go.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/engine/movepicker/movepicker.cpp b/src/engine/movepicker/movepicker.cpp index de5097b..16b043d 100644 --- a/src/engine/movepicker/movepicker.cpp +++ b/src/engine/movepicker/movepicker.cpp @@ -134,9 +134,9 @@ int MovePicker::negamax(int alpha, int beta, int depth) _board.set_en_passant_square(EMPTY_SQUARE); _current_depth += 2; int score = -negamax(-beta, -beta + 1, depth - 1 - R); + _current_depth -= 2; _board.set_state(state); _board.switch_side_to_move(); - _current_depth -= 2; if (score >= beta) { _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); diff --git a/src/interfaces/uci/commands/go.cpp b/src/interfaces/uci/commands/go.cpp index d4654c0..3917132 100644 --- a/src/interfaces/uci/commands/go.cpp +++ b/src/interfaces/uci/commands/go.cpp @@ -81,13 +81,14 @@ static void search(std::future future, MovePicker &move_picker, MovePicker result = move_picker.find_best_move(depth, alpha, beta); auto end = std::chrono::system_clock::now(); - if (depth == 1 && result.nodes == 1) + bool found_mate = display_search_iteration(result, depth, end - start); + + if (found_mate) { break; } - bool found_mate = display_search_iteration(result, depth, end - start); - if (found_mate) + if (result.nodes == 2) { break; } From 8db92163fd89c796257f70c27935956c93ba15f1 Mon Sep 17 00:00:00 2001 From: jsilll Date: Mon, 22 Aug 2022 23:07:32 +0100 Subject: [PATCH 18/22] small tweaks --- src/engine/board.cpp | 41 +++++++++---- src/engine/movepicker/movepicker.cpp | 69 +++++++++++----------- src/engine/movepicker/movepicker.hpp | 12 ++-- src/interfaces/uci/commands/commands.hpp | 9 ++- src/interfaces/uci/commands/go.cpp | 5 +- src/interfaces/uci/commands/ucinewgame.cpp | 2 + src/interfaces/uci/uci.cpp | 5 +- tests/movepicker/test_zobrist.cpp | 50 ++++------------ 8 files changed, 99 insertions(+), 94 deletions(-) diff --git a/src/engine/board.cpp b/src/engine/board.cpp index 97f2b28..28dfecc 100644 --- a/src/engine/board.cpp +++ b/src/engine/board.cpp @@ -245,7 +245,16 @@ struct Board::GameState Board::get_state() const void Board::set_en_passant_square(int sq) { + // Remove from hash key en passant square + if (_en_passant_square != EMPTY_SQUARE) + { + _hash_key ^= zobrist::en_passant_keys[_en_passant_square]; + } + _en_passant_square = sq; + + // Update hash key with new en passant square + _hash_key ^= zobrist::en_passant_keys[_en_passant_square]; } void Board::set_castling_rights(int castling_rights) @@ -516,7 +525,7 @@ void Board::make_move(const Move move) _square[from_square].type = EMPTY_PIECE; _square[from_square].color = BLACK; - // remove from hash key moved piece + // Remove from hash key moved piece _hash_key ^= zobrist::piece_keys[_to_move][piece][from_square]; if (is_en_passant) @@ -526,14 +535,14 @@ void Board::make_move(const Move move) _square[captured_piece_square].color = BLACK; bitboard::pop_bit(_pieces[this->get_opponent()][PAWN], captured_piece_square); - // remove from hash key captured pawn + // Remove from hash key captured pawn _hash_key ^= zobrist::piece_keys[this->get_opponent()][PAWN][captured_piece_square]; } else if (is_capture) { bitboard::pop_bit(_pieces[this->get_opponent()][captured_piece], to_square); - // remove from hash key captured piece + // Remove from hash key captured piece _hash_key ^= zobrist::piece_keys[this->get_opponent()][captured_piece][to_square]; } @@ -542,7 +551,7 @@ void Board::make_move(const Move move) _square[to_square].type = promoted_piece; bitboard::set_bit(_pieces[_to_move][promoted_piece], to_square); - // add to hash key promoted piece + // Update hash key with promoted piece _hash_key ^= zobrist::piece_keys[_to_move][promoted_piece][to_square]; } else @@ -550,7 +559,7 @@ void Board::make_move(const Move move) _square[to_square].type = piece; bitboard::set_bit(_pieces[_to_move][piece], to_square); - // add to hash key moved piece + // Update hash key with moved piece _hash_key ^= zobrist::piece_keys[_to_move][piece][to_square]; } @@ -576,29 +585,36 @@ void Board::make_move(const Move move) _square[rook_to_square].color = _to_move; bitboard::pop_bit(_pieces[_to_move][ROOK], rook_from_square); - // remove from hash key rook + + // Remove from hash key rook _hash_key ^= zobrist::piece_keys[_to_move][ROOK][rook_from_square]; + bitboard::set_bit(_pieces[_to_move][ROOK], rook_to_square); - // add to hash key rook + + // Update hash key with rook _hash_key ^= zobrist::piece_keys[_to_move][ROOK][rook_to_square]; } - // remove from hash key castling rights and en passant + // Remove from hash key en passant square if (_en_passant_square != EMPTY_SQUARE) { _hash_key ^= zobrist::en_passant_keys[_en_passant_square]; } + + // Remove from hash key castling rights _hash_key ^= zobrist::castle_keys[_castling_rights]; _en_passant_square = is_double_push ? to_square + pawn_push_en_passant_offset : -1; _castling_rights &= castling_rights[from_square]; _castling_rights &= castling_rights[to_square]; - // update hash key with castling rights and en passant + // Update hash key with en passant square if (_en_passant_square != EMPTY_SQUARE) { _hash_key ^= zobrist::en_passant_keys[_en_passant_square]; } + + // Update hash key with castling rights _hash_key ^= zobrist::castle_keys[_castling_rights]; if (piece == PAWN || (is_capture)) @@ -615,10 +631,13 @@ void Board::make_move(const Move move) _full_move_number++; } - // update from hash key side to move + // Remove (and Update) from hash key side to move + // This works because zobrist::side_key[WHITE] = 0 + // So XOR side[BLACK] + XOR side[WHITE] = XOR side[WHITE] + side[BLACK] = XOR side[BLACK] _hash_key ^= zobrist::side_key[BLACK]; + + // Update from hash key new side to move this->switch_side_to_move(); - // _hash_key ^= zobrist::side_key[_to_move]; this->update_occupancies(); } diff --git a/src/engine/movepicker/movepicker.cpp b/src/engine/movepicker/movepicker.cpp index 16b043d..a2886d0 100644 --- a/src/engine/movepicker/movepicker.cpp +++ b/src/engine/movepicker/movepicker.cpp @@ -127,7 +127,8 @@ int MovePicker::negamax(int alpha, int beta, int depth) u64 hash_key = _board.get_hash_key(); Board::GameState state = _board.get_state(); - // Null Move Pruning (TODO: Zugzwang checking??) + // Null Move Pruning + // TODO: Zugzwang checking if (depth >= 3) { _board.switch_side_to_move(); @@ -201,21 +202,22 @@ int MovePicker::negamax(int alpha, int beta, int depth) } } - if (score >= beta) + if (score > alpha) { - // Killer Move Heuristic - if (!move.is_capture()) + if (score >= beta) { - this->add_to_killer_moves(move); - } + // Killer Move Heuristic + if (!move.is_capture()) + { + this->add_to_killer_moves(move); + } - _board.unmake_move(move, state); + _board.unmake_move(move, state); + + _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); + return beta; + } - _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); - return beta; - } - else if (score > alpha) - { // History Move Heuristic if (!move.is_capture()) { @@ -277,17 +279,17 @@ int MovePicker::quiescence(int alpha, int beta) return 0; } - int stand_pat = eval::eval(_board); - - if (stand_pat >= beta) - { - _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); - return beta; - } - int alpha_cutoff = TTable::HASH_FLAG_ALPHA; + + int stand_pat = eval::eval(_board); if (stand_pat > alpha) { + if (stand_pat >= beta) + { + _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); + return beta; + } + alpha_cutoff = TTable::HASH_FLAG_SCORE; alpha = stand_pat; } @@ -307,14 +309,15 @@ int MovePicker::quiescence(int alpha, int beta) _current_depth++; int score = -quiescence(-beta, -alpha); _current_depth--; - if (score >= beta) - { - _board.unmake_move(capture, state); - _tt.set_entry(hash_key, 0, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); - return beta; - } if (score > alpha) { + if (score >= beta) + { + _board.unmake_move(capture, state); + _tt.set_entry(hash_key, 0, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); + return beta; + } + alpha_cutoff = TTable::HASH_FLAG_SCORE; alpha = score; } @@ -349,11 +352,6 @@ void MovePicker::clear_search_counters() _current_depth = 0; } -void MovePicker::clear_tt() -{ - _tt.clear(); -} - // Public Methods int MovePicker::get_max_depth() const @@ -376,7 +374,7 @@ MovePicker::SearchResult MovePicker::find_best_move() int alpha = MIN_EVAL; int beta = -MIN_EVAL; - this->clear_tables(); + this->clear_move_tables(); // Iterative Deepening for (int depth = 1; depth <= _max_depth; depth++) @@ -419,10 +417,15 @@ MovePicker::SearchResult MovePicker::find_best_move(int depth, int alpha, int be return result; } -void MovePicker::clear_tables() +void MovePicker::clear_move_tables() { memset(_history_moves, 0, sizeof(_history_moves)); memset(_killer_moves, 0, sizeof(_killer_moves)); memset(_killer_moves, 0, sizeof(_killer_moves)); _pv_table.clear(); +} + +void MovePicker::clear_tranposition_table() +{ + _tt.clear(); } \ No newline at end of file diff --git a/src/engine/movepicker/movepicker.hpp b/src/engine/movepicker/movepicker.hpp index b88df2f..6fcb585 100644 --- a/src/engine/movepicker/movepicker.hpp +++ b/src/engine/movepicker/movepicker.hpp @@ -58,8 +58,6 @@ class MovePicker void clear_search_counters(); - void clear_tt(); - public: struct SearchResult { @@ -75,10 +73,16 @@ class MovePicker void set_max_depth(int depth); /** - * @brief Clears all the tables + * @brief Clears all the move tables + * + */ + void clear_move_tables(); + + /** + * @brief Clears the transposition table * */ - void clear_tables(); + void clear_tranposition_table(); /** * @brief Searches the current position with max_depth diff --git a/src/interfaces/uci/commands/commands.hpp b/src/interfaces/uci/commands/commands.hpp index 6f31e6e..7cc5a00 100644 --- a/src/interfaces/uci/commands/commands.hpp +++ b/src/interfaces/uci/commands/commands.hpp @@ -20,8 +20,11 @@ namespace uci }; class UCINewGameCommand : public utils::Command { + private: + MovePicker _move_picker; + public: - UCINewGameCommand(Board &board) : Command(board){}; + UCINewGameCommand(Board &board, MovePicker &move_picker) : Command(board), _move_picker(move_picker){}; void execute([[maybe_unused]] std::vector &args); }; class PositionCommand : public utils::Command @@ -39,10 +42,10 @@ namespace uci class GoCommand : public utils::Command { private: - MovePicker _move_picker; + MovePicker &_move_picker; public: - GoCommand(Board &board) : Command(board), _move_picker(MovePicker(board)){}; + GoCommand(Board &board, MovePicker &move_picker) : Command(board), _move_picker(move_picker){}; void execute([[maybe_unused]] std::vector &args); }; class QuitCommand : public utils::Command diff --git a/src/interfaces/uci/commands/go.cpp b/src/interfaces/uci/commands/go.cpp index 3917132..190c471 100644 --- a/src/interfaces/uci/commands/go.cpp +++ b/src/interfaces/uci/commands/go.cpp @@ -71,10 +71,11 @@ static bool display_search_iteration(MovePicker::SearchResult result, int depth, static void search(std::future future, MovePicker &move_picker, MovePicker::SearchResult &result) { - move_picker.clear_tables(); - int alpha = MIN_EVAL; int beta = -MIN_EVAL; + + move_picker.clear_move_tables(); + for (int depth = 1; depth <= move_picker.get_max_depth(); depth++) { auto start = std::chrono::system_clock::now(); diff --git a/src/interfaces/uci/commands/ucinewgame.cpp b/src/interfaces/uci/commands/ucinewgame.cpp index 1a5ebdb..3b749c7 100644 --- a/src/interfaces/uci/commands/ucinewgame.cpp +++ b/src/interfaces/uci/commands/ucinewgame.cpp @@ -3,4 +3,6 @@ void uci::UCINewGameCommand::execute([[maybe_unused]] std::vector &args) { _board.set_starting_position(); + _move_picker.clear_move_tables(); + _move_picker.clear_tranposition_table(); } diff --git a/src/interfaces/uci/uci.cpp b/src/interfaces/uci/uci.cpp index c4e6d1c..789d13f 100644 --- a/src/interfaces/uci/uci.cpp +++ b/src/interfaces/uci/uci.cpp @@ -25,14 +25,15 @@ namespace uci zobrist::init(); Board board = Board(); + MovePicker move_picker = MovePicker(board); // TODO: use a map instead of if statements uci::UCICommand uciCommand = UCICommand(board); uci::IsReadyCommand isReadyCommand = IsReadyCommand(board); - uci::UCINewGameCommand uciNewGameCommand = UCINewGameCommand(board); + uci::UCINewGameCommand uciNewGameCommand = UCINewGameCommand(board, move_picker); uci::PositionCommand positionCommand = PositionCommand(board); uci::DisplayCommand displayCommand = DisplayCommand(board); - uci::GoCommand goCommand = GoCommand(board); + uci::GoCommand goCommand = GoCommand(board, move_picker); uci::QuitCommand quitCommand = QuitCommand(board); std::string line; diff --git a/tests/movepicker/test_zobrist.cpp b/tests/movepicker/test_zobrist.cpp index 0b534b9..b9dd2fb 100644 --- a/tests/movepicker/test_zobrist.cpp +++ b/tests/movepicker/test_zobrist.cpp @@ -10,6 +10,7 @@ #include #include #include +#include void setup() { @@ -18,37 +19,12 @@ void setup() zobrist::init(); } -int perft(Board &board, int depth) -{ - if (depth == 0) - { - return 1; - } - - int nodes = 0; - for (const Move &move : movegen::generate_pseudo_legal_moves(board)) - { - Board::GameState board_info = board.get_state(); - board.make_move(move); - REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); - int attacker_side = board.get_side_to_move(); - if (!board.is_square_attacked(king_sq, attacker_side)) - { - nodes += perft(board, depth - 1); - } - board.unmake_move(move, board_info); - REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - } - - return nodes; -} - TEST_CASE("hash_key") { setup(); Board board = Board(); + SECTION("fen hash key") { board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R", "w", "KQkq", "-", "0", "1"); @@ -62,9 +38,8 @@ TEST_CASE("hash_key") Board::GameState state = board.get_state(); board.make_move(move); REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == state.hash_key); } SECTION("capture hash key") @@ -74,9 +49,8 @@ TEST_CASE("hash_key") Board::GameState state = board.get_state(); board.make_move(move); REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == state.hash_key); } SECTION("double push hash key") @@ -86,9 +60,8 @@ TEST_CASE("hash_key") Board::GameState state = board.get_state(); board.make_move(move); REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == state.hash_key); } SECTION("promotion hash key") @@ -98,9 +71,8 @@ TEST_CASE("hash_key") Board::GameState state = board.get_state(); board.make_move(move); REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == state.hash_key); } SECTION("promotion + capture hash key") @@ -110,9 +82,8 @@ TEST_CASE("hash_key") Board::GameState state = board.get_state(); board.make_move(move); REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == state.hash_key); } SECTION("en passant hash key") @@ -122,13 +93,14 @@ TEST_CASE("hash_key") Board::GameState state = board.get_state(); board.make_move(move); REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - REQUIRE(zobrist::generate_hash_key(board) == zobrist::generate_hash_key(board)); board.unmake_move(move, state); - REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); + REQUIRE(board.get_hash_key() == state.hash_key); } SECTION("perft hash key") { - perft(board, 5); + Board::GameState state = board.get_state(); + perft::perft(board, 5); + REQUIRE(board.get_hash_key() == state.hash_key); } } \ No newline at end of file From e146cc900215b55416f2bd735653665cc004d87a Mon Sep 17 00:00:00 2001 From: jsilll Date: Tue, 23 Aug 2022 01:29:38 +0100 Subject: [PATCH 19/22] added repetition detection --- src/engine/board.cpp | 9 +- src/engine/board.hpp | 4 +- src/engine/movegen/movegen.cpp | 14 +-- src/engine/movegen/perft.cpp | 4 +- src/engine/movepicker/histtable.cpp | 34 ++++++ src/engine/movepicker/histtable.hpp | 22 ++++ src/engine/movepicker/movepicker.cpp | 116 ++++++++++++------- src/engine/movepicker/movepicker.hpp | 22 +++- src/interfaces/cli/commands/dividedperft.cpp | 4 +- src/interfaces/cli/commands/move.cpp | 2 +- src/interfaces/uci/commands/commands.hpp | 5 +- src/interfaces/uci/commands/position.cpp | 18 ++- src/interfaces/uci/commands/ucinewgame.cpp | 4 +- src/interfaces/uci/uci.cpp | 2 +- tests/movegen/test_unmake.cpp | 28 ++--- tests/movepicker/test_zobrist.cpp | 24 ++-- 16 files changed, 217 insertions(+), 95 deletions(-) create mode 100644 src/engine/movepicker/histtable.cpp create mode 100644 src/engine/movepicker/histtable.hpp diff --git a/src/engine/board.cpp b/src/engine/board.cpp index 28dfecc..c09ec70 100644 --- a/src/engine/board.cpp +++ b/src/engine/board.cpp @@ -254,7 +254,10 @@ void Board::set_en_passant_square(int sq) _en_passant_square = sq; // Update hash key with new en passant square - _hash_key ^= zobrist::en_passant_keys[_en_passant_square]; + if (_en_passant_square != EMPTY_SQUARE) + { + _hash_key ^= zobrist::en_passant_keys[_en_passant_square]; + } } void Board::set_castling_rights(int castling_rights) @@ -493,7 +496,7 @@ int Board::switch_side_to_move() return _to_move = this->get_opponent(); } -void Board::make_move(const Move move) +void Board::make(const Move move) { // clang-format off static const int castling_rights[64] = { @@ -642,7 +645,7 @@ void Board::make_move(const Move move) this->update_occupancies(); } -void Board::unmake_move(const Move move, const GameState state) +void Board::unmake(const Move move, const GameState state) { this->switch_side_to_move(); diff --git a/src/engine/board.hpp b/src/engine/board.hpp index c5c609f..d06799c 100644 --- a/src/engine/board.hpp +++ b/src/engine/board.hpp @@ -75,8 +75,8 @@ class Board const std::string &halfmove_clock, const std::string &fullmove_number); int switch_side_to_move(); - void make_move(const Move move); - void unmake_move(const Move move, const GameState info_board); + void make(const Move move); + void unmake(const Move move, const GameState info_board); void display() const; bool toggle_ascii(); diff --git a/src/engine/movegen/movegen.cpp b/src/engine/movegen/movegen.cpp index 68dd137..eb0a045 100644 --- a/src/engine/movegen/movegen.cpp +++ b/src/engine/movegen/movegen.cpp @@ -47,14 +47,14 @@ namespace movegen Board::GameState state = board.get_state(); for (const Move &move : movegen::generate_pseudo_legal_moves(board)) { - board.make_move(move); + board.make(move); int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); if (!board.is_square_attacked(king_sq, board.get_side_to_move())) { - board.unmake_move(move, state); + board.unmake(move, state); return true; } - board.unmake_move(move, state); + board.unmake(move, state); } return false; } @@ -158,14 +158,14 @@ namespace movegen Board::GameState state = board.get_state(); for (const Move &move : movegen::generate_pseudo_legal_moves(board)) { - board.make_move(move); + board.make(move); int attacker_side = board.get_side_to_move(); int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); if (!board.is_square_attacked(king_sq, attacker_side)) { moves.push_back(move); } - board.unmake_move(move, state); + board.unmake(move, state); } return moves; } @@ -178,14 +178,14 @@ namespace movegen Board::GameState state = board.get_state(); for (const Move &move : movegen::generate_pseudo_legal_captures(board)) { - board.make_move(move); + board.make(move); int attacker_side = board.get_side_to_move(); int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); if (!board.is_square_attacked(king_sq, attacker_side)) { captures.push_back(move); } - board.unmake_move(move, state); + board.unmake(move, state); } return captures; } diff --git a/src/engine/movegen/perft.cpp b/src/engine/movegen/perft.cpp index 413860a..f7746b7 100644 --- a/src/engine/movegen/perft.cpp +++ b/src/engine/movegen/perft.cpp @@ -19,14 +19,14 @@ namespace perft for (const Move &move : movegen::generate_pseudo_legal_moves(board)) { Board::GameState board_info = board.get_state(); - board.make_move(move); + board.make(move); int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); int attacker_side = board.get_side_to_move(); if (!board.is_square_attacked(king_sq, attacker_side)) { nodes += perft(board, depth - 1); } - board.unmake_move(move, board_info); + board.unmake(move, board_info); } return nodes; diff --git a/src/engine/movepicker/histtable.cpp b/src/engine/movepicker/histtable.cpp new file mode 100644 index 0000000..bf2ce8c --- /dev/null +++ b/src/engine/movepicker/histtable.cpp @@ -0,0 +1,34 @@ +#include + +HistoryTable::~HistoryTable() +{ + delete[] _hist_table; +} + +bool HistoryTable::is_repetition(u64 key) const +{ + for (int index = 0; index < _index; index++) + { + if (_hist_table[index] == key) + { + return true; + } + } + + return false; +} + +void HistoryTable::push(u64 key) +{ + _hist_table[_index++] = key; +} + +void HistoryTable::pop() +{ + --_index; +} + +void HistoryTable::clear() +{ + _index = 0; +} diff --git a/src/engine/movepicker/histtable.hpp b/src/engine/movepicker/histtable.hpp new file mode 100644 index 0000000..9656aef --- /dev/null +++ b/src/engine/movepicker/histtable.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +class HistoryTable +{ +private: + static const int MAX_HISTORY_SIZE = 128; + + int _index = 0; + u64 *_hist_table = new u64[MAX_HISTORY_SIZE]; + +public: + ~HistoryTable(); + + bool is_repetition(u64 key) const; + + void push(u64 key); + void pop(); + + void clear(); +}; \ No newline at end of file diff --git a/src/engine/movepicker/movepicker.cpp b/src/engine/movepicker/movepicker.cpp index a2886d0..9924aab 100644 --- a/src/engine/movepicker/movepicker.cpp +++ b/src/engine/movepicker/movepicker.cpp @@ -69,14 +69,16 @@ int MovePicker::search(int depth, int alpha, int beta) Board::GameState state = _board.get_state(); for (const Move &move : moves) { - _board.make_move(move); + _board.make(move); int attacker_side = _board.get_side_to_move(); int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_opponent(), KING)); if (!_board.is_square_attacked(king_sq, attacker_side)) { _current_depth++; + _hist_table.push(state.hash_key); int score = -negamax(-beta, -alpha, depth - 1); + _hist_table.pop(); _current_depth--; if (score > alpha) { @@ -92,7 +94,7 @@ int MovePicker::search(int depth, int alpha, int beta) } } - _board.unmake_move(move, state); + _board.unmake(move, state); } return alpha; @@ -102,21 +104,27 @@ int MovePicker::negamax(int alpha, int beta, int depth) { _current_nodes++; - TTable::TTOutput hash_read = _tt.read_hash(_board.get_hash_key(), alpha, beta, depth); - - if (hash_read.found) + // Terminal Node + if (_board.get_half_move_clock() == 100) { - _pv_table.add_pv_from_depth(hash_read.moves, _current_depth); - return hash_read.score; + // Draw + return 0; } // Terminal Node - if (_board.get_half_move_clock() == 100) + if (_hist_table.is_repetition(_board.get_hash_key())) { - // Draw + // Three-Fold Draw return 0; } + TTable::TTOutput hash_read = _tt.read_hash(_board.get_hash_key(), alpha, beta, depth); + if (hash_read.found) + { + _pv_table.add_pv_from_depth(hash_read.moves, _current_depth); + return hash_read.score; + } + // Forced Terminal Node if (depth <= 0) { @@ -124,7 +132,6 @@ int MovePicker::negamax(int alpha, int beta, int depth) return quiescence(alpha, beta); } - u64 hash_key = _board.get_hash_key(); Board::GameState state = _board.get_state(); // Null Move Pruning @@ -140,7 +147,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) _board.switch_side_to_move(); if (score >= beta) { - _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); + _tt.set_entry(state.hash_key, depth, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); return beta; } } @@ -154,7 +161,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) int alpha_cutoff = TTable::HASH_FLAG_ALPHA; for (const Move &move : moves) { - _board.make_move(move); + _board.make(move); int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_opponent(), KING)); if (!_board.is_square_attacked(king_sq, _board.get_side_to_move())) @@ -166,7 +173,9 @@ int MovePicker::negamax(int alpha, int beta, int depth) if (n_moves_searched == 0) { _current_depth++; + _hist_table.push(state.hash_key); score = -negamax(-beta, -alpha, depth - 1); + _hist_table.pop(); _current_depth--; } else @@ -177,7 +186,9 @@ int MovePicker::negamax(int alpha, int beta, int depth) { // Perform a Null Window Search _current_depth++; + _hist_table.push(state.hash_key); score = -negamax(-(alpha + 1), -alpha, depth - 2); + _hist_table.pop(); _current_depth--; } else @@ -190,33 +201,37 @@ int MovePicker::negamax(int alpha, int beta, int depth) if (score > alpha) { _current_depth++; + _hist_table.push(state.hash_key); score = -negamax(-(alpha + 1), -alpha, depth - 1); + _hist_table.pop(); _current_depth--; if ((score > alpha) && (score < beta)) { _current_depth++; + _hist_table.push(state.hash_key); score = -negamax(-beta, -alpha, depth - 1); + _hist_table.pop(); _current_depth--; } } } - if (score > alpha) + if (score >= beta) { - if (score >= beta) + // Killer Move Heuristic + if (!move.is_capture()) { - // Killer Move Heuristic - if (!move.is_capture()) - { - this->add_to_killer_moves(move); - } + this->add_to_killer_moves(move); + } - _board.unmake_move(move, state); + _board.unmake(move, state); - _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); - return beta; - } + _tt.set_entry(state.hash_key, depth, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); + return beta; + } + else if (score > alpha) + { // History Move Heuristic if (!move.is_capture()) @@ -233,7 +248,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) n_moves_searched++; } - _board.unmake_move(move, state); + _board.unmake(move, state); } // Terminal Node @@ -243,16 +258,16 @@ int MovePicker::negamax(int alpha, int beta, int depth) int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_side_to_move(), KING)); if (_board.is_square_attacked(king_sq, _board.get_opponent())) { - _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_SCORE, MIN_EVAL + _current_depth, _pv_table.get_pv_from_depth(_current_depth)); + _tt.set_entry(state.hash_key, depth, TTable::HASH_FLAG_SCORE, MIN_EVAL + _current_depth, _pv_table.get_pv_from_depth(_current_depth)); return MIN_EVAL + _current_depth; } // Stale Mate - _tt.set_entry(hash_key, depth, TTable::HASH_FLAG_SCORE, 0, _pv_table.get_pv_from_depth(_current_depth)); + _tt.set_entry(state.hash_key, depth, TTable::HASH_FLAG_SCORE, 0, _pv_table.get_pv_from_depth(_current_depth)); return 0; } - _tt.set_entry(hash_key, depth, alpha_cutoff, alpha, _pv_table.get_pv_from_depth(_current_depth)); + _tt.set_entry(state.hash_key, depth, alpha_cutoff, alpha, _pv_table.get_pv_from_depth(_current_depth)); return alpha; } @@ -282,13 +297,13 @@ int MovePicker::quiescence(int alpha, int beta) int alpha_cutoff = TTable::HASH_FLAG_ALPHA; int stand_pat = eval::eval(_board); - if (stand_pat > alpha) + if (stand_pat >= beta) + { + _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); + return beta; + } + else if (stand_pat > alpha) { - if (stand_pat >= beta) - { - _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); - return beta; - } alpha_cutoff = TTable::HASH_FLAG_SCORE; alpha = stand_pat; @@ -301,7 +316,7 @@ int MovePicker::quiescence(int alpha, int beta) Board::GameState state = _board.get_state(); for (const Move &capture : captures) { - _board.make_move(capture); + _board.make(capture); int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_opponent(), KING)); if (!_board.is_square_attacked(king_sq, _board.get_side_to_move())) @@ -309,21 +324,22 @@ int MovePicker::quiescence(int alpha, int beta) _current_depth++; int score = -quiescence(-beta, -alpha); _current_depth--; - if (score > alpha) + + if (score >= beta) + { + _board.unmake(capture, state); + _tt.set_entry(hash_key, 0, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); + return beta; + } + else if (score > alpha) { - if (score >= beta) - { - _board.unmake_move(capture, state); - _tt.set_entry(hash_key, 0, TTable::HASH_FLAG_BETA, beta, _pv_table.get_pv_from_depth(_current_depth)); - return beta; - } alpha_cutoff = TTable::HASH_FLAG_SCORE; alpha = score; } } - _board.unmake_move(capture, state); + _board.unmake(capture, state); } _tt.set_entry(_board.get_hash_key(), 0, alpha_cutoff, alpha, _pv_table.get_pv_from_depth(_current_depth)); @@ -365,6 +381,10 @@ void MovePicker::set_max_depth(int depth) { throw std::invalid_argument("Depth argument must be positive integer."); } + else if (depth > DEFAULT_MAX_DEPTH) + { + throw std::invalid_argument("Depth argument must be less than 64."); + } _max_depth = depth; } @@ -425,7 +445,17 @@ void MovePicker::clear_move_tables() _pv_table.clear(); } -void MovePicker::clear_tranposition_table() +void MovePicker::clear_transposition_table() { _tt.clear(); +} + +void MovePicker::add_to_history(u64 key) +{ + _hist_table.push(key); +} + +void MovePicker::clear_history() +{ + _hist_table.clear(); } \ No newline at end of file diff --git a/src/engine/movepicker/movepicker.hpp b/src/engine/movepicker/movepicker.hpp index 6fcb585..4ff37a1 100644 --- a/src/engine/movepicker/movepicker.hpp +++ b/src/engine/movepicker/movepicker.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -24,6 +25,7 @@ class MovePicker Move _killer_moves[2][DEFAULT_MAX_DEPTH]{}; PVTable _pv_table; + HistoryTable _hist_table; TTable _tt; struct MoveMoreThanKey @@ -70,8 +72,26 @@ class MovePicker [[nodiscard]] int get_max_depth() const; + /** + * @brief Set the max depth for the search + * + * @param depth + */ void set_max_depth(int depth); + /** + * @brief Adds a board hash to the current known history + * + * @param key + */ + void add_to_history(u64 key); + + /** + * @brief Clears all known game history + * + */ + void clear_history(); + /** * @brief Clears all the move tables * @@ -82,7 +102,7 @@ class MovePicker * @brief Clears the transposition table * */ - void clear_tranposition_table(); + void clear_transposition_table(); /** * @brief Searches the current position with max_depth diff --git a/src/interfaces/cli/commands/dividedperft.cpp b/src/interfaces/cli/commands/dividedperft.cpp index 0488066..54dae0a 100644 --- a/src/interfaces/cli/commands/dividedperft.cpp +++ b/src/interfaces/cli/commands/dividedperft.cpp @@ -15,7 +15,7 @@ static void dperft(int depth, Board &board) for (const Move &move : movegen::generate_pseudo_legal_moves(board)) { Board::GameState state = board.get_state(); - board.make_move(move); + board.make(move); int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); int attacker_side = board.get_side_to_move(); if (!board.is_square_attacked(king_sq, attacker_side)) @@ -24,7 +24,7 @@ static void dperft(int depth, Board &board) std::cout << move.get_uci() << ": " << nodes << std::endl; total_nodes += nodes; } - board.unmake_move(move, state); + board.unmake(move, state); } std::chrono::time_point end = std::chrono::system_clock::now(); std::chrono::duration elapsed_seconds = end - start; diff --git a/src/interfaces/cli/commands/move.cpp b/src/interfaces/cli/commands/move.cpp index 730b287..0ded84b 100644 --- a/src/interfaces/cli/commands/move.cpp +++ b/src/interfaces/cli/commands/move.cpp @@ -15,7 +15,7 @@ void cli::MoveCommand::execute(std::vector &args) { if (move.get_uci() == args[0]) { - _board.make_move(move); + _board.make(move); return; } } diff --git a/src/interfaces/uci/commands/commands.hpp b/src/interfaces/uci/commands/commands.hpp index 7cc5a00..bac2348 100644 --- a/src/interfaces/uci/commands/commands.hpp +++ b/src/interfaces/uci/commands/commands.hpp @@ -29,8 +29,11 @@ namespace uci }; class PositionCommand : public utils::Command { + private: + MovePicker &_move_picker; + public: - PositionCommand(Board &board) : Command(board){}; + PositionCommand(Board &board, MovePicker &move_picker) : Command(board), _move_picker(move_picker){}; void execute([[maybe_unused]] std::vector &args); }; class DisplayCommand : public utils::Command diff --git a/src/interfaces/uci/commands/position.cpp b/src/interfaces/uci/commands/position.cpp index 132c0dc..43564a7 100644 --- a/src/interfaces/uci/commands/position.cpp +++ b/src/interfaces/uci/commands/position.cpp @@ -6,7 +6,7 @@ #include -static std::optional parse_mov(std::string move_uci, Board &board) +static std::optional parse_move(std::string move_uci, Board &board) { for (Move const &move : movegen::generateLegalMoves(board)) { @@ -19,14 +19,17 @@ static std::optional parse_mov(std::string move_uci, Board &board) return std::nullopt; } -static void handle_moves(std::vector &moves, Board &board) +static void handle_moves(std::vector &moves, Board &board, MovePicker &move_picker) { + move_picker.add_to_history(board.get_hash_key()); // For Three-Fold Draws for (std::string move_uci : moves) { - std::optional parsed_move = parse_mov(move_uci, board); + std::optional parsed_move = parse_move(move_uci, board); if (parsed_move.has_value()) { - board.make_move(parsed_move.value()); + Move move = parsed_move.value(); + board.make(move); + move_picker.add_to_history(board.get_hash_key()); // For Three-Fold Draws } } } @@ -74,10 +77,15 @@ void uci::PositionCommand::execute(std::vector &args) return; } + _move_picker.clear_history(); + if (args[0] == "moves") { args.erase(args.begin()); - handle_moves(args, _board); + handle_moves(args, _board, _move_picker); } + + _move_picker.clear_move_tables(); + _move_picker.clear_transposition_table(); } diff --git a/src/interfaces/uci/commands/ucinewgame.cpp b/src/interfaces/uci/commands/ucinewgame.cpp index 3b749c7..f45d130 100644 --- a/src/interfaces/uci/commands/ucinewgame.cpp +++ b/src/interfaces/uci/commands/ucinewgame.cpp @@ -3,6 +3,8 @@ void uci::UCINewGameCommand::execute([[maybe_unused]] std::vector &args) { _board.set_starting_position(); + + _move_picker.clear_history(); _move_picker.clear_move_tables(); - _move_picker.clear_tranposition_table(); + _move_picker.clear_transposition_table(); } diff --git a/src/interfaces/uci/uci.cpp b/src/interfaces/uci/uci.cpp index 789d13f..6f3fb50 100644 --- a/src/interfaces/uci/uci.cpp +++ b/src/interfaces/uci/uci.cpp @@ -31,7 +31,7 @@ namespace uci uci::UCICommand uciCommand = UCICommand(board); uci::IsReadyCommand isReadyCommand = IsReadyCommand(board); uci::UCINewGameCommand uciNewGameCommand = UCINewGameCommand(board, move_picker); - uci::PositionCommand positionCommand = PositionCommand(board); + uci::PositionCommand positionCommand = PositionCommand(board, move_picker); uci::DisplayCommand displayCommand = DisplayCommand(board); uci::GoCommand goCommand = GoCommand(board, move_picker); uci::QuitCommand quitCommand = QuitCommand(board); diff --git a/tests/movegen/test_unmake.cpp b/tests/movegen/test_unmake.cpp index 10d3cd7..48fd315 100644 --- a/tests/movegen/test_unmake.cpp +++ b/tests/movegen/test_unmake.cpp @@ -27,8 +27,8 @@ TEST_CASE("unmake move") // board.set_from_fen("1k6/p6p/K6P/8/8/8/8/1q4q1", "b", "-", "-", "0", "1"); Move move = Move(E2, E4, PAWN, EMPTY_PIECE, EMPTY_PIECE, true, false, false); Board::GameState state = board.get_state(); - board.make_move(move); - board.unmake_move(move, state); + board.make(move); + board.unmake(move, state); REQUIRE(board.get_fen() == "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1\n"); } @@ -37,8 +37,8 @@ TEST_CASE("unmake move") // board.set_from_fen("1k6/p6p/K6P/8/8/8/8/1q4q1", "b", "-", "-", "0", "1"); Move move = Move(B1, H7, KNIGHT, PAWN, EMPTY_PIECE, false, false, false); Board::GameState state = board.get_state(); - board.make_move(move); - board.unmake_move(move, state); + board.make(move); + board.unmake(move, state); REQUIRE(board.get_fen() == "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1\n"); } @@ -47,8 +47,8 @@ TEST_CASE("unmake move") board.set_from_fen("rnbqkbnr/3ppppp/8/ppp5/6P1/5N1B/PPPPPP1P/RNBQK2R", "w", "KQkq", "-", "0", "4"); Move move = Move(E1, G1, KING, EMPTY_PIECE, EMPTY_PIECE, false, false, true); Board::GameState state = board.get_state(); - board.make_move(move); - board.unmake_move(move, state); + board.make(move); + board.unmake(move, state); REQUIRE(board.get_fen() == "rnbqkbnr/3ppppp/8/ppp5/6P1/5N1B/PPPPPP1P/RNBQK2R w KQkq - 0 4\n"); } @@ -58,8 +58,8 @@ TEST_CASE("unmake move") Move move = Move(B5, C4, PAWN, PAWN, EMPTY_PIECE, false, false, false); REQUIRE(move.is_capture() == true); Board::GameState state = board.get_state(); - board.make_move(move); - board.unmake_move(move, state); + board.make(move); + board.unmake(move, state); REQUIRE(board.get_fen() == "rnbqkbnr/3ppppp/8/ppp5/2P3P1/5N1B/PP1PPP1P/RNBQK2R b KQkq - 0 4\n"); } @@ -68,8 +68,8 @@ TEST_CASE("unmake move") board.set_from_fen("4kbnr/P2ppppp/3q4/8/6P1/5N1B/PP1PPP1P/RNBQK1KR", "w", "k", "-", "0", "4"); Move move = Move(A7, A8, PAWN, EMPTY_PIECE, QUEEN, false, false, false); Board::GameState state = board.get_state(); - board.make_move(move); - board.unmake_move(move, state); + board.make(move); + board.unmake(move, state); REQUIRE(board.get_fen() == "4kbnr/P2ppppp/3q4/8/6P1/5N1B/PP1PPP1P/RNBQK1KR w k - 0 4\n"); } @@ -78,8 +78,8 @@ TEST_CASE("unmake move") board.set_from_fen("rnbqkbnr/3ppppp/8/p1p5/2pP2P1/5N1B/PP2PP1P/RNBQK2R", "b", "KQkq", "d3", "0", "5"); Move move = Move(C4, D3, PAWN, PAWN, EMPTY_PIECE, false, true, false); Board::GameState state = board.get_state(); - board.make_move(move); - board.unmake_move(move, state); + board.make(move); + board.unmake(move, state); REQUIRE(board.get_fen() == "rnbqkbnr/3ppppp/8/p1p5/2pP2P1/5N1B/PP2PP1P/RNBQK2R b KQkq d3 0 5\n"); } @@ -88,8 +88,8 @@ TEST_CASE("unmake move") board.set_from_fen("rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR", "w", "KQkq", "-", "0", "1"); Move move = Move(D2, D4, PAWN, EMPTY_PIECE, EMPTY_PIECE, true, false, false); Board::GameState state = board.get_state(); - board.make_move(move); - board.unmake_move(move, state); + board.make(move); + board.unmake(move, state); REQUIRE(board.get_fen() == "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1\n"); } } diff --git a/tests/movepicker/test_zobrist.cpp b/tests/movepicker/test_zobrist.cpp index b9dd2fb..e6997d9 100644 --- a/tests/movepicker/test_zobrist.cpp +++ b/tests/movepicker/test_zobrist.cpp @@ -36,9 +36,9 @@ TEST_CASE("hash_key") board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R", "w", "KQkq", "-", "0", "1"); Move move = Move(D5, D6, PAWN, EMPTY_PIECE, EMPTY_PIECE, false, false, false); Board::GameState state = board.get_state(); - board.make_move(move); + board.make(move); REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - board.unmake_move(move, state); + board.unmake(move, state); REQUIRE(board.get_hash_key() == state.hash_key); } @@ -47,9 +47,9 @@ TEST_CASE("hash_key") board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R", "w", "KQkq", "-", "0", "1"); Move move = Move(F3, F6, QUEEN, KNIGHT, EMPTY_PIECE, false, false, false); Board::GameState state = board.get_state(); - board.make_move(move); + board.make(move); REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - board.unmake_move(move, state); + board.unmake(move, state); REQUIRE(board.get_hash_key() == state.hash_key); } @@ -58,9 +58,9 @@ TEST_CASE("hash_key") board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R", "w", "KQkq", "-", "0", "1"); Move move = Move(A2, A4, PAWN, EMPTY_PIECE, EMPTY_PIECE, true, false, false); Board::GameState state = board.get_state(); - board.make_move(move); + board.make(move); REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - board.unmake_move(move, state); + board.unmake(move, state); REQUIRE(board.get_hash_key() == state.hash_key); } @@ -69,9 +69,9 @@ TEST_CASE("hash_key") board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q2/PPPBBP1p/R3K1P1", "b", "KQkq", "-", "0", "1"); Move move = Move(H2, H1, PAWN, EMPTY_PIECE, KNIGHT, false, false, false); Board::GameState state = board.get_state(); - board.make_move(move); + board.make(move); REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - board.unmake_move(move, state); + board.unmake(move, state); REQUIRE(board.get_hash_key() == state.hash_key); } @@ -80,9 +80,9 @@ TEST_CASE("hash_key") board.set_from_fen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q2/PPPBBP1p/R3K1PR", "b", "KQkq", "-", "0", "1"); Move move = Move(H2, G1, PAWN, PAWN, QUEEN, false, false, false); Board::GameState state = board.get_state(); - board.make_move(move); + board.make(move); REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - board.unmake_move(move, state); + board.unmake(move, state); REQUIRE(board.get_hash_key() == state.hash_key); } @@ -91,9 +91,9 @@ TEST_CASE("hash_key") board.set_from_fen("rnbqkbnr/3ppppp/8/p1p5/2pP2P1/5N1B/PP2PP1P/RNBQK2R", "b", "KQkq", "d3", "0", "5"); Move move = Move(C4, D3, PAWN, PAWN, EMPTY_PIECE, false, true, false); Board::GameState state = board.get_state(); - board.make_move(move); + board.make(move); REQUIRE(board.get_hash_key() == zobrist::generate_hash_key(board)); - board.unmake_move(move, state); + board.unmake(move, state); REQUIRE(board.get_hash_key() == state.hash_key); } From 8a0086879b57fb838e148aa054b42238a37e66bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Silveira?= Date: Tue, 23 Aug 2022 01:37:54 +0100 Subject: [PATCH 20/22] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c58c75f..ca5f291 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Features - [Null Move Pruning](https://www.chessprogramming.org/Null_Move_Pruning) - [Null Window Search](https://www.chessprogramming.org/Null_Window) - [Transposition Table](https://en.wikipedia.org/wiki/Transposition_table) +- [Repetition Detection](https://www.chessprogramming.org/Repetitions) Building and Installation === From ae86a467405993f6c5c5a8dcf7d28ce8475d6142 Mon Sep 17 00:00:00 2001 From: jsilll Date: Tue, 23 Aug 2022 21:24:55 +0100 Subject: [PATCH 21/22] time control budget calculation adjustments --- src/interfaces/uci/timectl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/interfaces/uci/timectl.cpp b/src/interfaces/uci/timectl.cpp index 9faa54a..ee3409b 100644 --- a/src/interfaces/uci/timectl.cpp +++ b/src/interfaces/uci/timectl.cpp @@ -7,7 +7,8 @@ namespace uci::timectl int get_time_budget_ms(int wtime, int btime, const Board &board) { int remaining_time = board.get_side_to_move() == WHITE ? wtime : btime; - return remaining_time / std::max(40 - board.get_full_move_number(), 10); + int remaining_moves_pred = board.get_full_move_number() < 40 ? 40 - board.get_full_move_number() : std::max(80 - board.get_full_move_number(), 10); + return remaining_time / remaining_moves_pred; } } // namespace uci::timectl From 17e218ecebc6dae43a56d6b4df1ae3d0b3bfc34e Mon Sep 17 00:00:00 2001 From: jsilll Date: Wed, 24 Aug 2022 02:05:13 +0100 Subject: [PATCH 22/22] improved types and moved big tables to the heap --- src/engine/bitboard.cpp | 22 +-- src/engine/bitboard.hpp | 12 +- src/engine/board.cpp | 140 +++++++-------- src/engine/board.hpp | 26 +-- src/engine/constants.hpp | 28 ++- src/engine/move.cpp | 26 +-- src/engine/move.hpp | 10 +- src/engine/movegen/attacks.cpp | 108 ++++++------ src/engine/movegen/attacks.hpp | 8 +- src/engine/movegen/magics.cpp | 175 +++++++++++++++++-- src/engine/movegen/magics.hpp | 138 +-------------- src/engine/movegen/movegen.cpp | 118 ++++++------- src/engine/movegen/perft.cpp | 4 +- src/engine/movegen/tables.cpp | 111 ++++++++++-- src/engine/movegen/tables.hpp | 144 +++++++++------ src/engine/movepicker/eval.cpp | 14 +- src/engine/movepicker/movepicker.cpp | 18 +- src/engine/movepicker/ttable.hpp | 2 +- src/engine/utils.hpp | 13 +- src/engine/zobrist.cpp | 4 +- src/interfaces/cli/commands/dividedperft.cpp | 4 +- src/interfaces/cli/commands/exit.cpp | 3 + src/interfaces/uci/commands/exit.cpp | 3 + tests/movegen/test_board.cpp | 2 +- tests/movegen/test_move.cpp | 40 ++--- tests/movegen/test_tables.cpp | 40 ++--- 26 files changed, 694 insertions(+), 519 deletions(-) diff --git a/src/engine/bitboard.cpp b/src/engine/bitboard.cpp index 1786e5e..67f419b 100644 --- a/src/engine/bitboard.cpp +++ b/src/engine/bitboard.cpp @@ -19,16 +19,17 @@ namespace bitboard return (int)count; } - int bit_scan(u64 bb) + Square bit_scan(u64 bb) { if (bb) { - return bit_count((bb & -bb) - 1); + return (Square)bit_count((bb & -bb) - 1); } - return -1; + + return EMPTY_SQUARE; } - int bit_scan_forward(u64 bb) + Square bit_scan_forward(u64 bb) { static const int index64[64] = { 0, 47, 1, 56, 48, 27, 2, 60, @@ -41,7 +42,7 @@ namespace bitboard 13, 18, 8, 12, 7, 6, 5, 63}; static const u64 debruijn64 = 0x03f79d71b4cb0a89; - return index64[((bb ^ (bb - 1)) * debruijn64) >> 58]; + return (Square)index64[((bb ^ (bb - 1)) * debruijn64) >> 58]; } u64 set_occupancy(int index, int bits_in_mask, u64 attack_mask) @@ -49,24 +50,25 @@ namespace bitboard u64 occupancy = ZERO; for (int bit = 0; bit < bits_in_mask; bit++) { - int lsb_sq = bit_scan_forward(attack_mask); + Square lsb_sq = bit_scan_forward(attack_mask); pop_bit(attack_mask, lsb_sq); if (index & (1 << bit)) { - occupancy |= tables::SQUARE_BB[lsb_sq]; + occupancy |= tables::square_to_bitboard((Square)lsb_sq); } } + return occupancy; } void print(u64 bb) { - for (int i = 7; i >= 0; i--) + for (int i = RANK_8; i >= RANK_1; i--) { std::cout << i + 1 << " "; - for (int n = 0; n < 8; n++) + for (int n = FILE_A; n < N_FILES; n++) { - std::cout << ((bb >> utils::get_square(i, n)) & ONE) << " "; + std::cout << ((bb >> utils::get_square((Rank)i, (File)n)) & ONE) << " "; } std::cout << "\n"; } diff --git a/src/engine/bitboard.hpp b/src/engine/bitboard.hpp index b0c8120..001198f 100644 --- a/src/engine/bitboard.hpp +++ b/src/engine/bitboard.hpp @@ -2,6 +2,8 @@ #include +#include + typedef std::uint64_t u64; constexpr u64 ONE = 1ULL; @@ -11,10 +13,10 @@ namespace bitboard { // Common Bitboard Operations - inline bool get_bit(u64 bb, int sq) { return ((bb >> sq) & 1ULL); } - inline void pop_bit(u64 &bn, int sq) { bn &= ~(1ULL << sq); } + inline bool get_bit(u64 bb, Square sq) { return ((bb >> sq) & 1ULL); } + inline void pop_bit(u64 &bn, Square sq) { bn &= ~(1ULL << sq); } inline void pop_last_bit(u64 &bb) { bb &= bb - 1; } - inline void set_bit(u64 &bb, int sq) { bb |= (1ULL << sq); } + inline void set_bit(u64 &bb, Square sq) { bb |= (1ULL << sq); } // Common Bitboard Shifts inline u64 sout_one(u64 bb) { return (bb >> 8); } @@ -40,7 +42,7 @@ namespace bitboard * @param bb * @return unsigned int */ - int bit_scan(u64 bb); + Square bit_scan(u64 bb); /** * @brief Returns index of LSB bit @@ -48,7 +50,7 @@ namespace bitboard * @param bb * @return unsigned int */ - int bit_scan_forward(u64 bb); + Square bit_scan_forward(u64 bb); /** * @brief Sets the occupancy bits diff --git a/src/engine/board.cpp b/src/engine/board.cpp index c09ec70..5caf118 100644 --- a/src/engine/board.cpp +++ b/src/engine/board.cpp @@ -65,29 +65,29 @@ void Board::update_bitboards_from_squares() { if (_square[sq].type != EMPTY_PIECE) { - bitboard::set_bit(_pieces[_square[sq].color][_square[sq].type], sq); + bitboard::set_bit(_pieces[_square[sq].color][_square[sq].type], (Square)sq); } } this->update_occupancies(); } -u64 Board::get_pieces(int color, int type) const +u64 Board::get_pieces(Color color, PieceType type) const { return _pieces[color][type]; } -u64 Board::get_occupancies(int color) const +u64 Board::get_occupancies(Color color) const { return _occupancies[color]; } -int Board::get_side_to_move() const +Color Board::get_side_to_move() const { return _to_move; } -int Board::get_opponent() const +Color Board::get_opponent() const { return utils::get_opponent(_to_move); } @@ -97,7 +97,7 @@ int Board::get_castling_rights() const return _castling_rights; } -int Board::get_en_passant_square() const +Square Board::get_en_passant_square() const { return _en_passant_square; } @@ -117,25 +117,25 @@ u64 Board::get_hash_key() const return _hash_key; } -Board::Piece Board::get_piece_from_square(int sq) const +Board::Piece Board::get_piece_from_square(Square sq) const { return _square[sq]; } -bool Board::is_square_attacked(int sq, int attacker) const +bool Board::is_square_attacked(Square sq, Color attacker) const { u64 pawns = _pieces[attacker][PAWN]; - if (tables::ATTACKS_PAWN[utils::get_opponent(attacker)][sq] & pawns) + if (tables::get_pawn_attacks(utils::get_opponent(attacker), sq) & pawns) { return true; } u64 knights = _pieces[attacker][KNIGHT]; - if (tables::ATTACKS_KNIGHT[sq] & knights) + if (tables::get_knight_attacks(sq) & knights) { return true; } u64 king = _pieces[attacker][KING]; - if (tables::ATTACKS_KING[sq] & king) + if (tables::get_king_attacks(sq) & king) { return true; } @@ -172,11 +172,11 @@ std::string Board::get_fen() const std::string full_move_number; int empty_squares = 0; - for (int rank = 7; rank >= 0; rank--) + for (int rank = RANK_8; rank >= RANK_1; rank--) { - for (int file = 0; file < 8; file++) + for (int file = FILE_A; file < N_FILES; file++) { - int sq = utils::get_square(rank, file); + Square sq = utils::get_square((Rank)rank, (File)file); if (file == 0) { if (empty_squares) @@ -243,7 +243,7 @@ struct Board::GameState Board::get_state() const return GameState{_en_passant_square, _castling_rights, _half_move_clock, _hash_key}; } -void Board::set_en_passant_square(int sq) +void Board::set_en_passant_square(Square sq) { // Remove from hash key en passant square if (_en_passant_square != EMPTY_SQUARE) @@ -280,13 +280,13 @@ void Board::display() const if (!_white_on_bottom) { std::cout << " h g f e d c b a\n"; - for (int rank = 0; rank < 8; rank++) + for (int rank = RANK_1; rank < RANK_8; rank++) { std::cout << " +---+---+---+---+---+---+---+---+\n" << " |"; - for (int file = 7; file >= 0; file--) + for (int file = FILE_H; file >= FILE_A; file--) { - struct Piece piece = _square[utils::get_square(rank, file)]; + struct Piece piece = _square[utils::get_square((Rank)rank, (File)file)]; std::cout << " " << PIECE_REPR[piece.type + offset + (6 * piece.color)] << " |"; } std::cout << std::setw(3) << rank + 1 << "\n"; @@ -295,14 +295,14 @@ void Board::display() const } else { - for (int rank = 7; rank >= 0; rank--) + for (int rank = RANK_8; rank >= RANK_1; rank--) { std::cout << " +---+---+---+---+---+---+---+---+\n" << std::setw(3) << rank + 1 << " |"; for (int file = 0; file < 8; file++) { - struct Piece piece = _square[utils::get_square(rank, file)]; + struct Piece piece = _square[utils::get_square((Rank)rank, (File)file)]; std::cout << " " << PIECE_REPR[piece.type + offset + (6 * piece.color)] << " |"; } std::cout << '\n'; @@ -366,69 +366,69 @@ void Board::set_from_fen(const std::string &piece_placements, { this->clear(); - int file = 0, rank = 7; + int file = FILE_A, rank = RANK_8; for (const char &c : piece_placements) { switch (c) { case 'p': - _square[utils::get_square(rank, file)].type = PAWN; - _square[utils::get_square(rank, file)].color = BLACK; + _square[utils::get_square((Rank)rank, (File)file)].type = PAWN; + _square[utils::get_square((Rank)rank, (File)file)].color = BLACK; file = (file + 1) % 8; break; case 'n': - _square[utils::get_square(rank, file)].type = KNIGHT; - _square[utils::get_square(rank, file)].color = BLACK; + _square[utils::get_square((Rank)rank, (File)file)].type = KNIGHT; + _square[utils::get_square((Rank)rank, (File)file)].color = BLACK; file = (file + 1) % 8; break; case 'b': - _square[utils::get_square(rank, file)].type = BISHOP; - _square[utils::get_square(rank, file)].color = BLACK; + _square[utils::get_square((Rank)rank, (File)file)].type = BISHOP; + _square[utils::get_square((Rank)rank, (File)file)].color = BLACK; file = (file + 1) % 8; break; case 'r': - _square[utils::get_square(rank, file)].type = ROOK; - _square[utils::get_square(rank, file)].color = BLACK; + _square[utils::get_square((Rank)rank, (File)file)].type = ROOK; + _square[utils::get_square((Rank)rank, (File)file)].color = BLACK; file = (file + 1) % 8; break; case 'q': - _square[utils::get_square(rank, file)].type = QUEEN; - _square[utils::get_square(rank, file)].color = BLACK; + _square[utils::get_square((Rank)rank, (File)file)].type = QUEEN; + _square[utils::get_square((Rank)rank, (File)file)].color = BLACK; file = (file + 1) % 8; break; case 'k': - _square[utils::get_square(rank, file)].type = KING; - _square[utils::get_square(rank, file)].color = BLACK; + _square[utils::get_square((Rank)rank, (File)file)].type = KING; + _square[utils::get_square((Rank)rank, (File)file)].color = BLACK; file = (file + 1) % 8; break; case 'P': - _square[utils::get_square(rank, file)].type = PAWN; - _square[utils::get_square(rank, file)].color = WHITE; + _square[utils::get_square((Rank)rank, (File)file)].type = PAWN; + _square[utils::get_square((Rank)rank, (File)file)].color = WHITE; file = (file + 1) % 8; break; case 'N': - _square[utils::get_square(rank, file)].type = KNIGHT; - _square[utils::get_square(rank, file)].color = WHITE; + _square[utils::get_square((Rank)rank, (File)file)].type = KNIGHT; + _square[utils::get_square((Rank)rank, (File)file)].color = WHITE; file = (file + 1) % 8; break; case 'B': - _square[utils::get_square(rank, file)].type = BISHOP; - _square[utils::get_square(rank, file)].color = WHITE; + _square[utils::get_square((Rank)rank, (File)file)].type = BISHOP; + _square[utils::get_square((Rank)rank, (File)file)].color = WHITE; file = (file + 1) % 8; break; case 'R': - _square[utils::get_square(rank, file)].type = ROOK; - _square[utils::get_square(rank, file)].color = WHITE; + _square[utils::get_square((Rank)rank, (File)file)].type = ROOK; + _square[utils::get_square((Rank)rank, (File)file)].color = WHITE; file = (file + 1) % 8; break; case 'Q': - _square[utils::get_square(rank, file)].type = QUEEN; - _square[utils::get_square(rank, file)].color = WHITE; + _square[utils::get_square((Rank)rank, (File)file)].type = QUEEN; + _square[utils::get_square((Rank)rank, (File)file)].color = WHITE; file = (file + 1) % 8; break; case 'K': - _square[utils::get_square(rank, file)].type = KING; - _square[utils::get_square(rank, file)].color = WHITE; + _square[utils::get_square((Rank)rank, (File)file)].type = KING; + _square[utils::get_square((Rank)rank, (File)file)].color = WHITE; file = (file + 1) % 8; break; case '/': @@ -475,7 +475,7 @@ void Board::set_from_fen(const std::string &piece_placements, { int en_passant_file = en_passant[0] - 'a'; int en_passant_rank = en_passant[1] - '1'; - _en_passant_square = utils::get_square(en_passant_rank, en_passant_file); + _en_passant_square = utils::get_square((Rank)en_passant_rank, (File)en_passant_file); } else { @@ -511,11 +511,11 @@ void Board::make(const Move move) }; // clang-format on - int from_square = move.get_from_square(); - int to_square = move.get_to_square(); - int piece = move.get_piece(); - int captured_piece = move.get_captured_piece(); - int promoted_piece = move.get_promoted_piece(); + Square from_square = move.get_from_square(); + Square to_square = move.get_to_square(); + PieceType piece_type = move.get_piece_type(); + PieceType captured_piece = move.get_captured_piece_type(); + PieceType promoted_piece = move.get_promoted_piece_type(); bool is_capture = move.is_capture(); bool is_promotion = move.is_promotion(); bool is_double_push = move.is_double_push(); @@ -524,16 +524,16 @@ void Board::make(const Move move) int pawn_push_en_passant_offset = _to_move == WHITE ? -8 : 8; - bitboard::pop_bit(_pieces[_to_move][piece], from_square); + bitboard::pop_bit(_pieces[_to_move][piece_type], (Square)from_square); _square[from_square].type = EMPTY_PIECE; _square[from_square].color = BLACK; // Remove from hash key moved piece - _hash_key ^= zobrist::piece_keys[_to_move][piece][from_square]; + _hash_key ^= zobrist::piece_keys[_to_move][piece_type][from_square]; if (is_en_passant) { - int captured_piece_square = to_square + pawn_push_en_passant_offset; + Square captured_piece_square = (Square)(to_square + pawn_push_en_passant_offset); _square[captured_piece_square].type = EMPTY_PIECE; _square[captured_piece_square].color = BLACK; bitboard::pop_bit(_pieces[this->get_opponent()][PAWN], captured_piece_square); @@ -559,18 +559,18 @@ void Board::make(const Move move) } else { - _square[to_square].type = piece; - bitboard::set_bit(_pieces[_to_move][piece], to_square); + _square[to_square].type = piece_type; + bitboard::set_bit(_pieces[_to_move][piece_type], to_square); // Update hash key with moved piece - _hash_key ^= zobrist::piece_keys[_to_move][piece][to_square]; + _hash_key ^= zobrist::piece_keys[_to_move][piece_type][to_square]; } _square[to_square].color = _to_move; if (is_castle) { - int rook_from_square, rook_to_square; + Square rook_from_square, rook_to_square; if (to_square - from_square > 0) { rook_from_square = _to_move == WHITE ? H1 : H8; @@ -607,7 +607,7 @@ void Board::make(const Move move) // Remove from hash key castling rights _hash_key ^= zobrist::castle_keys[_castling_rights]; - _en_passant_square = is_double_push ? to_square + pawn_push_en_passant_offset : -1; + _en_passant_square = is_double_push ? (Square)(to_square + pawn_push_en_passant_offset) : EMPTY_SQUARE; _castling_rights &= castling_rights[from_square]; _castling_rights &= castling_rights[to_square]; @@ -620,7 +620,7 @@ void Board::make(const Move move) // Update hash key with castling rights _hash_key ^= zobrist::castle_keys[_castling_rights]; - if (piece == PAWN || (is_capture)) + if (piece_type == PAWN || (is_capture)) { _half_move_clock = 0; } @@ -649,25 +649,25 @@ void Board::unmake(const Move move, const GameState state) { this->switch_side_to_move(); - int from_square = move.get_from_square(); - int to_square = move.get_to_square(); - int piece = move.get_piece(); - int captured_piece = move.get_captured_piece(); - int promoted_piece = move.get_promoted_piece(); + Square from_square = move.get_from_square(); + Square to_square = move.get_to_square(); + PieceType piece_type = move.get_piece_type(); + PieceType captured_piece = move.get_captured_piece_type(); + PieceType promoted_piece = move.get_promoted_piece_type(); bool is_capture = move.is_capture(); bool is_promotion = move.is_promotion(); bool is_en_passant = move.is_en_passant(); bool is_castle = move.is_castle(); - _square[from_square].type = piece; + _square[from_square].type = piece_type; _square[from_square].color = _to_move; - bitboard::set_bit(_pieces[_to_move][piece], from_square); + bitboard::set_bit(_pieces[_to_move][piece_type], from_square); - bitboard::pop_bit(_pieces[_to_move][piece], to_square); + bitboard::pop_bit(_pieces[_to_move][piece_type], to_square); if (is_en_passant) { - int captured_piece_square = _to_move == WHITE ? to_square - 8 : to_square + 8; + Square captured_piece_square = _to_move == WHITE ? (Square)(to_square - 8) : (Square)(to_square + 8); _square[captured_piece_square].type = PAWN; _square[captured_piece_square].color = this->get_opponent(); @@ -695,7 +695,7 @@ void Board::unmake(const Move move, const GameState state) if (is_castle) { - int rook_from_square, rook_to_square; + Square rook_from_square, rook_to_square; if (to_square - from_square > 0) { rook_from_square = _to_move == WHITE ? H1 : H8; diff --git a/src/engine/board.hpp b/src/engine/board.hpp index d06799c..ae26872 100644 --- a/src/engine/board.hpp +++ b/src/engine/board.hpp @@ -10,22 +10,22 @@ class Board public: struct Piece { - int type; - int color; + PieceType type; + Color color; }; struct GameState { - int en_passant_square; + Square en_passant_square; int castling_rights; int half_move_clock; u64 hash_key; }; private: - int _to_move; + Color _to_move; int _castling_rights; - int _en_passant_square; + Square _en_passant_square; int _half_move_clock; int _full_move_number; u64 _hash_key; @@ -44,22 +44,22 @@ class Board Board(); Board(const Board &board); - u64 get_pieces(int color, int type) const; - u64 get_occupancies(int color) const; - int get_side_to_move() const; - int get_opponent() const; + u64 get_pieces(Color color, PieceType type) const; + u64 get_occupancies(Color color) const; + Color get_side_to_move() const; + Color get_opponent() const; int get_castling_rights() const; - int get_en_passant_square() const; + Square get_en_passant_square() const; int get_half_move_clock() const; int get_full_move_number() const; - Piece get_piece_from_square(int sq) const; - bool is_square_attacked(int sq, int attacker_side) const; + Piece get_piece_from_square(Square sq) const; + bool is_square_attacked(Square sq, Color attacker_side) const; bool is_ascii() const; bool is_white_on_bottom() const; GameState get_state() const; std::string get_fen() const; - void set_en_passant_square(int sq); + void set_en_passant_square(Square sq); void set_castling_rights(int castling_rights); void set_fifty_move(int fifty_move); void set_state(GameState state); diff --git a/src/engine/constants.hpp b/src/engine/constants.hpp index 6503164..c30c1a8 100644 --- a/src/engine/constants.hpp +++ b/src/engine/constants.hpp @@ -2,6 +2,32 @@ #include +enum Rank : int +{ + RANK_1, + RANK_2, + RANK_3, + RANK_4, + RANK_5, + RANK_6, + RANK_7, + RANK_8, + N_RANKS, +}; + +enum File : int +{ + FILE_A, + FILE_B, + FILE_C, + FILE_D, + FILE_E, + FILE_F, + FILE_G, + FILE_H, + N_FILES, +}; + // clang-format off enum Square : int { A1, B1, C1, D1, E1, F1, G1, H1, @@ -43,7 +69,7 @@ enum Direction : int N_DIRECTIONS = 8, }; -enum PieceColor : int +enum Color : int { WHITE = 0, BLACK = 1, diff --git a/src/engine/move.cpp b/src/engine/move.cpp index d33c4a5..7baf698 100644 --- a/src/engine/move.cpp +++ b/src/engine/move.cpp @@ -4,7 +4,7 @@ std::string Move::get_uci() const { if (this->is_promotion()) { - return SQUARE_NAMES[this->get_from_square()] + SQUARE_NAMES[this->get_to_square()] + PIECE_REPR[this->get_promoted_piece() + 6]; + return SQUARE_NAMES[this->get_from_square()] + SQUARE_NAMES[this->get_to_square()] + PIECE_REPR[this->get_promoted_piece_type() + 6]; } return SQUARE_NAMES[this->get_from_square()] + SQUARE_NAMES[this->get_to_square()]; } @@ -14,29 +14,29 @@ Move::Move(int source_square, int target_square, int piece, int captured_piece, _move_encoded = (uint32_t)source_square | ((uint32_t)target_square << 6) | ((uint32_t)piece << 12) | ((uint32_t)captured_piece << 15) | ((uint32_t)promoted_piece << 18) | ((uint32_t)is_double_push << 21) | ((uint32_t)is_en_passant << 22) | ((uint32_t)is_castle << 23); } -int Move::get_from_square() const +Square Move::get_from_square() const { - return (_move_encoded & 0x3f); + return (Square)(_move_encoded & 0x3f); } -int Move::get_to_square() const +Square Move::get_to_square() const { - return ((_move_encoded & 0xfc0) >> 6); + return (Square)((_move_encoded & 0xfc0) >> 6); } -int Move::get_piece() const +PieceType Move::get_piece_type() const { - return ((_move_encoded & 0x7000) >> 12); + return (PieceType)((_move_encoded & 0x7000) >> 12); } -int Move::get_captured_piece() const +PieceType Move::get_captured_piece_type() const { - return ((_move_encoded & 0x38000) >> 15); + return (PieceType)((_move_encoded & 0x38000) >> 15); } -int Move::get_promoted_piece() const +PieceType Move::get_promoted_piece_type() const { - return ((_move_encoded & 0x1C0000) >> 18); + return (PieceType)((_move_encoded & 0x1C0000) >> 18); } bool Move::is_double_push() const @@ -56,12 +56,12 @@ bool Move::is_castle() const bool Move::is_capture() const { - return this->get_captured_piece() != EMPTY_PIECE; + return this->get_captured_piece_type() != EMPTY_PIECE; } bool Move::is_promotion() const { - return this->get_promoted_piece() != EMPTY_PIECE; + return this->get_promoted_piece_type() != EMPTY_PIECE; } uint32_t Move::get_encoded() const diff --git a/src/engine/move.hpp b/src/engine/move.hpp index f437ab9..799844f 100644 --- a/src/engine/move.hpp +++ b/src/engine/move.hpp @@ -15,11 +15,11 @@ class Move Move() : _move_encoded(0){}; Move(int source_square, int target_square, int piece, int captured_piece, int promoted_piece, bool is_double_push, bool is_en_passant, bool is_castle); - [[nodiscard]] int get_from_square() const; - [[nodiscard]] int get_to_square() const; - [[nodiscard]] int get_piece() const; - [[nodiscard]] int get_captured_piece() const; - [[nodiscard]] int get_promoted_piece() const; + [[nodiscard]] Square get_from_square() const; + [[nodiscard]] Square get_to_square() const; + [[nodiscard]] PieceType get_piece_type() const; + [[nodiscard]] PieceType get_captured_piece_type() const; + [[nodiscard]] PieceType get_promoted_piece_type() const; [[nodiscard]] bool is_double_push() const; [[nodiscard]] bool is_en_passant() const; [[nodiscard]] bool is_castle() const; diff --git a/src/engine/movegen/attacks.cpp b/src/engine/movegen/attacks.cpp index 132c684..6115707 100644 --- a/src/engine/movegen/attacks.cpp +++ b/src/engine/movegen/attacks.cpp @@ -53,101 +53,98 @@ namespace attacks return attacks; } - u64 mask_bishop_attack_rays(int sq) + u64 mask_bishop_attack_rays(Square sq) { u64 attacks = ZERO; - int rank = utils::get_rank(sq); - int file = utils::get_file(sq); - - for (int r = rank + 1, f = file + 1; r < 7 && f < 7; r++, f++) + Rank rank = utils::get_rank(sq); + File file = utils::get_file(sq); + for (int r = rank + 1, f = file + 1; r < RANK_8 && f < FILE_H; r++, f++) { - attacks |= (ONE << utils::get_square(r, f)); + attacks |= (ONE << utils::get_square((Rank)r, (File)f)); } - for (int r = rank + 1, f = file - 1; r < 7 && f > 0; r++, f--) + for (int r = rank + 1, f = file - 1; r < RANK_8 && f > FILE_A; r++, f--) { - attacks |= (ONE << utils::get_square(r, f)); + attacks |= (ONE << utils::get_square((Rank)r, (File)f)); } - for (int r = rank - 1, f = file + 1; r > 0 && f < 7; r--, f++) + for (int r = rank - 1, f = file + 1; r > RANK_1 && f < FILE_H; r--, f++) { - attacks |= (ONE << utils::get_square(r, f)); + attacks |= (ONE << utils::get_square((Rank)r, (File)f)); } - for (int r = rank - 1, f = file - 1; r > 0 && f > 0; r--, f--) + for (int r = rank - 1, f = file - 1; r > RANK_1 && f > FILE_A; r--, f--) { - attacks |= (ONE << utils::get_square(r, f)); + attacks |= (ONE << utils::get_square((Rank)r, (File)f)); } return attacks; } - u64 mask_rook_attack_rays(int sq) + u64 mask_rook_attack_rays(Square sq) { u64 attacks = ZERO; - int rank = utils::get_rank(sq); - int file = utils::get_file(sq); - - for (int r = rank + 1; r < 7; r++) + Rank rank = utils::get_rank(sq); + File file = utils::get_file(sq); + for (int r = rank + 1; r < RANK_8; r++) { - attacks |= (ONE << utils::get_square(r, file)); + attacks |= (ONE << utils::get_square((Rank)r, file)); } - for (int r = rank - 1; r > 0; r--) + for (int r = rank - 1; r > RANK_1; r--) { - attacks |= (ONE << utils::get_square(r, file)); + attacks |= (ONE << utils::get_square((Rank)r, file)); } - for (int f = file + 1; f < 7; f++) + for (int f = file + 1; f < FILE_H; f++) { - attacks |= (ONE << utils::get_square(rank, f)); + attacks |= (ONE << utils::get_square(rank, (File)f)); } - for (int f = file - 1; f > 0; f--) + for (int f = file - 1; f > FILE_A; f--) { - attacks |= (ONE << utils::get_square(rank, f)); + attacks |= (ONE << utils::get_square(rank, (File)f)); } return attacks; } - u64 mask_bishop_attacks(int sq, u64 block) + u64 mask_bishop_xray_attacks(Square sq, u64 block) { u64 attacks = ZERO; - int rank = utils::get_rank(sq); - int file = utils::get_file(sq); - - for (int r = rank + 1, f = file + 1; r < 8 && f < 8; r++, f++) + Rank rank = utils::get_rank(sq); + File file = utils::get_file(sq); + for (int r = rank + 1, f = file + 1; r < N_RANKS && f < N_FILES; r++, f++) { - attacks |= (ONE << utils::get_square(r, f)); - if ((ONE << utils::get_square(r, f)) & block) + attacks |= (ONE << utils::get_square((Rank)r, (File)f)); + if ((ONE << utils::get_square((Rank)r, (File)f)) & block) { break; } } - for (int r = rank + 1, f = file - 1; r < 8 && f >= 0; r++, f--) + for (int r = rank + 1, f = file - 1; r < N_RANKS && f >= FILE_A; r++, f--) { - attacks |= (ONE << utils::get_square(r, f)); - if ((ONE << utils::get_square(r, f)) & block) + attacks |= (ONE << utils::get_square((Rank)r, (File)f)); + if ((ONE << utils::get_square((Rank)r, (File)f)) & block) { break; } } - for (int r = rank - 1, f = file + 1; r >= 0 && f < 8; r--, f++) + for (int r = rank - 1, f = file + 1; r >= RANK_1 && f < N_FILES; r--, f++) { - attacks |= (ONE << utils::get_square(r, f)); - if ((ONE << utils::get_square(r, f)) & block) + attacks |= (ONE << utils::get_square((Rank)r, (File)f)); + if ((ONE << utils::get_square((Rank)r, (File)f)) & block) { break; } } - for (int r = rank - 1, f = file - 1; r >= 0 && f >= 0; r--, f--) + for (int r = rank - 1, f = file - 1; r >= RANK_1 && f >= FILE_A; r--, f--) { - attacks |= (ONE << utils::get_square(r, f)); - if ((ONE << utils::get_square(r, f)) & block) + attacks |= (ONE << utils::get_square((Rank)r, (File)f)); + if ((ONE << utils::get_square((Rank)r, (File)f)) & block) { break; } @@ -156,43 +153,42 @@ namespace attacks return attacks; } - u64 mask_rook_attacks(int sq, u64 block) + u64 mask_rook_xray_attacks(Square sq, u64 block) { u64 attacks = ZERO; - int rank = utils::get_rank(sq); - int file = utils::get_file(sq); - - for (int r = rank + 1; r < 8; r++) + Rank rank = utils::get_rank(sq); + File file = utils::get_file(sq); + for (int r = rank + 1; r < N_RANKS; r++) { - attacks |= (ONE << utils::get_square(r, file)); - if ((ONE << utils::get_square(r, file)) & block) + attacks |= (ONE << utils::get_square((Rank)r, file)); + if ((ONE << utils::get_square((Rank)r, file)) & block) { break; } } - for (int r = rank - 1; r >= 0; r--) + for (int r = rank - 1; r >= RANK_1; r--) { - attacks |= (ONE << utils::get_square(r, file)); - if ((ONE << utils::get_square(r, file)) & block) + attacks |= (ONE << utils::get_square((Rank)r, file)); + if ((ONE << utils::get_square((Rank)r, file)) & block) { break; } } - for (int f = file + 1; f < 8; f++) + for (int f = file + 1; f < N_FILES; f++) { - attacks |= (ONE << utils::get_square(rank, f)); - if ((ONE << utils::get_square(rank, f)) & block) + attacks |= (ONE << utils::get_square(rank, (File)f)); + if ((ONE << utils::get_square(rank, (File)f)) & block) { break; } } - for (int f = file - 1; f >= 0; f--) + for (int f = file - 1; f >= FILE_A; f--) { - attacks |= (ONE << utils::get_square(rank, f)); - if ((ONE << utils::get_square(rank, f)) & block) + attacks |= (ONE << utils::get_square(rank, (File)f)); + if ((ONE << utils::get_square(rank, (File)f)) & block) { break; } diff --git a/src/engine/movegen/attacks.hpp b/src/engine/movegen/attacks.hpp index 7397fd8..90439e2 100644 --- a/src/engine/movegen/attacks.hpp +++ b/src/engine/movegen/attacks.hpp @@ -11,15 +11,15 @@ namespace attacks u64 mask_black_pawn_double_pushes(u64 bpawns, u64 empty); // Bishop and Rook Attack Rays - u64 mask_rook_attack_rays(int sq); - u64 mask_bishop_attack_rays(int sq); + u64 mask_rook_attack_rays(Square sq); + u64 mask_bishop_attack_rays(Square sq); // Attacks u64 mask_white_pawn_any_attacks(u64 wpawns); u64 mask_black_pawn_any_attacks(u64 bpawns); u64 mask_knight_attacks(u64 knights); u64 mask_king_attacks(u64 kings); - u64 mask_bishop_attacks(int sq, u64 block); - u64 mask_rook_attacks(int sq, u64 block); + u64 mask_bishop_xray_attacks(Square sq, u64 block); + u64 mask_rook_xray_attacks(Square sq, u64 block); } // namespace attacks \ No newline at end of file diff --git a/src/engine/movegen/magics.cpp b/src/engine/movegen/magics.cpp index 467988c..f8a124f 100644 --- a/src/engine/movegen/magics.cpp +++ b/src/engine/movegen/magics.cpp @@ -11,10 +11,142 @@ namespace magics { - Magic MAGIC_TABLE_BISHOP[N_SQUARES]; - Magic MAGIC_TABLE_ROOK[N_SQUARES]; + static const u64 MAGICS_BISHOP[N_SQUARES] = { + 0x40040844404084ULL, + 0x2004208a004208ULL, + 0x10190041080202ULL, + 0x108060845042010ULL, + 0x581104180800210ULL, + 0x2112080446200010ULL, + 0x1080820820060210ULL, + 0x3c0808410220200ULL, + 0x4050404440404ULL, + 0x21001420088ULL, + 0x24d0080801082102ULL, + 0x1020a0a020400ULL, + 0x40308200402ULL, + 0x4011002100800ULL, + 0x401484104104005ULL, + 0x801010402020200ULL, + 0x400210c3880100ULL, + 0x404022024108200ULL, + 0x810018200204102ULL, + 0x4002801a02003ULL, + 0x85040820080400ULL, + 0x810102c808880400ULL, + 0xe900410884800ULL, + 0x8002020480840102ULL, + 0x220200865090201ULL, + 0x2010100a02021202ULL, + 0x152048408022401ULL, + 0x20080002081110ULL, + 0x4001001021004000ULL, + 0x800040400a011002ULL, + 0xe4004081011002ULL, + 0x1c004001012080ULL, + 0x8004200962a00220ULL, + 0x8422100208500202ULL, + 0x2000402200300c08ULL, + 0x8646020080080080ULL, + 0x80020a0200100808ULL, + 0x2010004880111000ULL, + 0x623000a080011400ULL, + 0x42008c0340209202ULL, + 0x209188240001000ULL, + 0x400408a884001800ULL, + 0x110400a6080400ULL, + 0x1840060a44020800ULL, + 0x90080104000041ULL, + 0x201011000808101ULL, + 0x1a2208080504f080ULL, + 0x8012020600211212ULL, + 0x500861011240000ULL, + 0x180806108200800ULL, + 0x4000020e01040044ULL, + 0x300000261044000aULL, + 0x802241102020002ULL, + 0x20906061210001ULL, + 0x5a84841004010310ULL, + 0x4010801011c04ULL, + 0xa010109502200ULL, + 0x4a02012000ULL, + 0x500201010098b028ULL, + 0x8040002811040900ULL, + 0x28000010020204ULL, + 0x6000020202d0240ULL, + 0x8918844842082200ULL, + 0x4010011029020020ULL}; - static u64 generate_dense_random_number_u64() + static const u64 MAGICS_ROOK[N_SQUARES] = { + 0x8a80104000800020ULL, + 0x140002000100040ULL, + 0x2801880a0017001ULL, + 0x100081001000420ULL, + 0x200020010080420ULL, + 0x3001c0002010008ULL, + 0x8480008002000100ULL, + 0x2080088004402900ULL, + 0x800098204000ULL, + 0x2024401000200040ULL, + 0x100802000801000ULL, + 0x120800800801000ULL, + 0x208808088000400ULL, + 0x2802200800400ULL, + 0x2200800100020080ULL, + 0x801000060821100ULL, + 0x80044006422000ULL, + 0x100808020004000ULL, + 0x12108a0010204200ULL, + 0x140848010000802ULL, + 0x481828014002800ULL, + 0x8094004002004100ULL, + 0x4010040010010802ULL, + 0x20008806104ULL, + 0x100400080208000ULL, + 0x2040002120081000ULL, + 0x21200680100081ULL, + 0x20100080080080ULL, + 0x2000a00200410ULL, + 0x20080800400ULL, + 0x80088400100102ULL, + 0x80004600042881ULL, + 0x4040008040800020ULL, + 0x440003000200801ULL, + 0x4200011004500ULL, + 0x188020010100100ULL, + 0x14800401802800ULL, + 0x2080040080800200ULL, + 0x124080204001001ULL, + 0x200046502000484ULL, + 0x480400080088020ULL, + 0x1000422010034000ULL, + 0x30200100110040ULL, + 0x100021010009ULL, + 0x2002080100110004ULL, + 0x202008004008002ULL, + 0x20020004010100ULL, + 0x2048440040820001ULL, + 0x101002200408200ULL, + 0x40802000401080ULL, + 0x4008142004410100ULL, + 0x2060820c0120200ULL, + 0x1001004080100ULL, + 0x20c020080040080ULL, + 0x2935610830022400ULL, + 0x44440041009200ULL, + 0x280001040802101ULL, + 0x2100190040002085ULL, + 0x80c0084100102001ULL, + 0x4024081001000421ULL, + 0x20030a0244872ULL, + 0x12001008414402ULL, + 0x2006104900a0804ULL, + 0x1004081002402ULL}; + + static Magic MAGIC_TABLE_BISHOP[N_SQUARES]; + static Magic MAGIC_TABLE_ROOK[N_SQUARES]; + + static inline u64 generate_dense_random_number_u64() { u64 n1 = ((u64)std::rand()) & 0xFFFF; u64 n2 = ((u64)std::rand()) & 0xFFFF; @@ -23,9 +155,12 @@ namespace magics return n1 | (n2 << 16) | (n3 << 32) | (n4 << 48); } - static inline u64 get_magic_number_candidate() { return generate_dense_random_number_u64() & generate_dense_random_number_u64() & generate_dense_random_number_u64(); } + static inline u64 get_magic_number_candidate() + { + return generate_dense_random_number_u64() & generate_dense_random_number_u64() & generate_dense_random_number_u64(); + } - static u64 generate_magic_number(int sq, int relevant_bits, u64 (*mask_attacks_fun)(int), u64 (*mask_attacks_occ_fun)(int, u64)) + static u64 generate_magic_number(Square sq, int relevant_bits, u64 (*mask_attacks_fun)(Square), u64 (*mask_attacks_occ_fun)(Square, u64)) { int occupancy_indices = 1 << relevant_bits; u64 attack_mask = mask_attacks_fun(sq); @@ -75,19 +210,19 @@ namespace magics void generate() { std::cout << "Rook Magic Numbers" << std::endl; - for (int sq = 0; sq < 64; sq++) + for (int sq = A1; sq < N_SQUARES; sq++) { - int bit_count = tables::RELEVANT_BITS_COUNT_ROOK[sq]; - u64 magic = generate_magic_number(sq, bit_count, &attacks::mask_rook_attack_rays, &attacks::mask_rook_attacks); + int bit_count = tables::get_relevant_bits_count_rook((Square)sq); + u64 magic = generate_magic_number((Square)sq, bit_count, &attacks::mask_rook_attack_rays, &attacks::mask_rook_xray_attacks); printf("%d : 0x%lxULL\n", sq, magic); } std::cout << std::endl; std::cout << "Bishop Magic Numbers" << std::endl; - for (int sq = 0; sq < 64; sq++) + for (int sq = A1; sq < N_SQUARES; sq++) { - int bit_count = tables::RELEVANT_BITS_COUNT_BISHOP[sq]; - u64 magic = generate_magic_number(sq, bit_count, &attacks::mask_bishop_attack_rays, &attacks::mask_bishop_attacks); + int bit_count = tables::get_relevant_bits_count_bishop((Square)sq); + u64 magic = generate_magic_number((Square)sq, bit_count, &attacks::mask_bishop_attack_rays, &attacks::mask_bishop_xray_attacks); printf("%d : 0x%lxULL\n", sq, magic); } std::cout << std::endl; @@ -97,17 +232,27 @@ namespace magics { for (int sq = A1; sq < N_SQUARES; sq++) { - MAGIC_TABLE_BISHOP[sq].mask = attacks::mask_bishop_attack_rays(sq); + MAGIC_TABLE_BISHOP[sq].mask = attacks::mask_bishop_attack_rays((Square)sq); MAGIC_TABLE_BISHOP[sq].magic = MAGICS_BISHOP[sq]; - MAGIC_TABLE_BISHOP[sq].shift = 64 - tables::RELEVANT_BITS_COUNT_BISHOP[sq]; + MAGIC_TABLE_BISHOP[sq].shift = 64 - tables::get_relevant_bits_count_bishop((Square)sq); } for (int sq = A1; sq < N_SQUARES; sq++) { - MAGIC_TABLE_ROOK[sq].mask = attacks::mask_rook_attack_rays(sq); + MAGIC_TABLE_ROOK[sq].mask = attacks::mask_rook_attack_rays((Square)sq); MAGIC_TABLE_ROOK[sq].magic = MAGICS_ROOK[sq]; - MAGIC_TABLE_ROOK[sq].shift = 64 - tables::RELEVANT_BITS_COUNT_ROOK[sq]; + MAGIC_TABLE_ROOK[sq].shift = 64 - tables::get_relevant_bits_count_rook((Square)sq); } } + Magic get_bishop_magic(Square sq) + { + return MAGIC_TABLE_BISHOP[sq]; + } + + Magic get_rook_magic(Square sq) + { + return MAGIC_TABLE_ROOK[sq]; + } + } // namespace magics \ No newline at end of file diff --git a/src/engine/movegen/magics.hpp b/src/engine/movegen/magics.hpp index 925da96..3b75236 100644 --- a/src/engine/movegen/magics.hpp +++ b/src/engine/movegen/magics.hpp @@ -5,138 +5,6 @@ namespace magics { - constexpr u64 MAGICS_BISHOP[N_SQUARES] = { - 0x40040844404084ULL, - 0x2004208a004208ULL, - 0x10190041080202ULL, - 0x108060845042010ULL, - 0x581104180800210ULL, - 0x2112080446200010ULL, - 0x1080820820060210ULL, - 0x3c0808410220200ULL, - 0x4050404440404ULL, - 0x21001420088ULL, - 0x24d0080801082102ULL, - 0x1020a0a020400ULL, - 0x40308200402ULL, - 0x4011002100800ULL, - 0x401484104104005ULL, - 0x801010402020200ULL, - 0x400210c3880100ULL, - 0x404022024108200ULL, - 0x810018200204102ULL, - 0x4002801a02003ULL, - 0x85040820080400ULL, - 0x810102c808880400ULL, - 0xe900410884800ULL, - 0x8002020480840102ULL, - 0x220200865090201ULL, - 0x2010100a02021202ULL, - 0x152048408022401ULL, - 0x20080002081110ULL, - 0x4001001021004000ULL, - 0x800040400a011002ULL, - 0xe4004081011002ULL, - 0x1c004001012080ULL, - 0x8004200962a00220ULL, - 0x8422100208500202ULL, - 0x2000402200300c08ULL, - 0x8646020080080080ULL, - 0x80020a0200100808ULL, - 0x2010004880111000ULL, - 0x623000a080011400ULL, - 0x42008c0340209202ULL, - 0x209188240001000ULL, - 0x400408a884001800ULL, - 0x110400a6080400ULL, - 0x1840060a44020800ULL, - 0x90080104000041ULL, - 0x201011000808101ULL, - 0x1a2208080504f080ULL, - 0x8012020600211212ULL, - 0x500861011240000ULL, - 0x180806108200800ULL, - 0x4000020e01040044ULL, - 0x300000261044000aULL, - 0x802241102020002ULL, - 0x20906061210001ULL, - 0x5a84841004010310ULL, - 0x4010801011c04ULL, - 0xa010109502200ULL, - 0x4a02012000ULL, - 0x500201010098b028ULL, - 0x8040002811040900ULL, - 0x28000010020204ULL, - 0x6000020202d0240ULL, - 0x8918844842082200ULL, - 0x4010011029020020ULL}; - - constexpr u64 MAGICS_ROOK[N_SQUARES] = { - 0x8a80104000800020ULL, - 0x140002000100040ULL, - 0x2801880a0017001ULL, - 0x100081001000420ULL, - 0x200020010080420ULL, - 0x3001c0002010008ULL, - 0x8480008002000100ULL, - 0x2080088004402900ULL, - 0x800098204000ULL, - 0x2024401000200040ULL, - 0x100802000801000ULL, - 0x120800800801000ULL, - 0x208808088000400ULL, - 0x2802200800400ULL, - 0x2200800100020080ULL, - 0x801000060821100ULL, - 0x80044006422000ULL, - 0x100808020004000ULL, - 0x12108a0010204200ULL, - 0x140848010000802ULL, - 0x481828014002800ULL, - 0x8094004002004100ULL, - 0x4010040010010802ULL, - 0x20008806104ULL, - 0x100400080208000ULL, - 0x2040002120081000ULL, - 0x21200680100081ULL, - 0x20100080080080ULL, - 0x2000a00200410ULL, - 0x20080800400ULL, - 0x80088400100102ULL, - 0x80004600042881ULL, - 0x4040008040800020ULL, - 0x440003000200801ULL, - 0x4200011004500ULL, - 0x188020010100100ULL, - 0x14800401802800ULL, - 0x2080040080800200ULL, - 0x124080204001001ULL, - 0x200046502000484ULL, - 0x480400080088020ULL, - 0x1000422010034000ULL, - 0x30200100110040ULL, - 0x100021010009ULL, - 0x2002080100110004ULL, - 0x202008004008002ULL, - 0x20020004010100ULL, - 0x2048440040820001ULL, - 0x101002200408200ULL, - 0x40802000401080ULL, - 0x4008142004410100ULL, - 0x2060820c0120200ULL, - 0x1001004080100ULL, - 0x20c020080040080ULL, - 0x2935610830022400ULL, - 0x44440041009200ULL, - 0x280001040802101ULL, - 0x2100190040002085ULL, - 0x80c0084100102001ULL, - 0x4024081001000421ULL, - 0x20030a0244872ULL, - 0x12001008414402ULL, - 0x2006104900a0804ULL, - 0x1004081002402ULL}; - struct Magic { u64 mask; @@ -144,11 +12,11 @@ namespace magics int shift; }; - extern Magic MAGIC_TABLE_BISHOP[N_SQUARES]; - extern Magic MAGIC_TABLE_ROOK[N_SQUARES]; - void generate(); void init(); + Magic get_bishop_magic(Square sq); + Magic get_rook_magic(Square sq); + } // namespace magics \ No newline at end of file diff --git a/src/engine/movegen/movegen.cpp b/src/engine/movegen/movegen.cpp index eb0a045..60c1072 100644 --- a/src/engine/movegen/movegen.cpp +++ b/src/engine/movegen/movegen.cpp @@ -17,29 +17,29 @@ namespace movegen }; // Castling - template + template static void generate_castling_moves(std::vector &move_list, const Board &board); // Pawns - template + template static void generate_en_passant_capture(std::vector &move_list, const Board &board); - template + template static void generate_pawn_single_push_with_promotion(std::vector &move_list, const Board &board); - template + template static void generate_pawn_single_push_no_promotion(std::vector &move_list, const Board &board); - template + template static void generate_pawn_captures_with_promotion(std::vector &move_list, const Board &board); - template + template static void generate_pawn_captures_no_promotion(std::vector &move_list, const Board &board); - template + template static void generate_pawn_double_pushes(std::vector &move_list, const Board &board); // Leaper Pieces - template + template static void generate_leaper_moves(std::vector &move_list, const Board &board); // Slider Pieces - template + template static void generate_slider_moves(std::vector &move_list, const Board &board); bool has_legal_moves(Board &board) @@ -48,7 +48,7 @@ namespace movegen for (const Move &move : movegen::generate_pseudo_legal_moves(board)) { board.make(move); - int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); + Square king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); if (!board.is_square_attacked(king_sq, board.get_side_to_move())) { board.unmake(move, state); @@ -159,8 +159,8 @@ namespace movegen for (const Move &move : movegen::generate_pseudo_legal_moves(board)) { board.make(move); - int attacker_side = board.get_side_to_move(); - int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); + Color attacker_side = board.get_side_to_move(); + Square king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); if (!board.is_square_attacked(king_sq, attacker_side)) { moves.push_back(move); @@ -179,8 +179,8 @@ namespace movegen for (const Move &move : movegen::generate_pseudo_legal_captures(board)) { board.make(move); - int attacker_side = board.get_side_to_move(); - int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); + Color attacker_side = board.get_side_to_move(); + Square king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); if (!board.is_square_attacked(king_sq, attacker_side)) { captures.push_back(move); @@ -190,30 +190,30 @@ namespace movegen return captures; } - template + template static void generate_castling_moves(std::vector &move_list, const Board &board) { - constexpr PieceColor OPPONENT = (ToMove == WHITE ? BLACK : WHITE); - constexpr int CASTLE_B_SQ = (ToMove == WHITE ? B1 : B8); - constexpr int CASTLE_C_SQ = (ToMove == WHITE ? C1 : C8); - constexpr int CASTLE_D_SQ = (ToMove == WHITE ? D1 : D8); - constexpr int CASTLE_E_SQ = (ToMove == WHITE ? E1 : E8); - constexpr int CASTLE_F_SQ = (ToMove == WHITE ? F1 : F8); - constexpr int CASTLE_G_SQ = (ToMove == WHITE ? G1 : G8); - constexpr int CASTLE_KING_MASK = (ToMove == WHITE ? CASTLE_KING_WHITE : CASTLE_KING_BLACK); - constexpr int CASTLE_QUEEN_MASK = (ToMove == WHITE ? CASTLE_QUEEN_WHITE : CASTLE_QUEEN_BLACK); - if (!board.is_square_attacked(CASTLE_E_SQ, OPPONENT)) + constexpr Color Opponent = (ToMove == WHITE ? BLACK : WHITE); + constexpr Square CASTLE_B_SQ = (ToMove == WHITE ? B1 : B8); + constexpr Square CASTLE_C_SQ = (ToMove == WHITE ? C1 : C8); + constexpr Square CASTLE_D_SQ = (ToMove == WHITE ? D1 : D8); + constexpr Square CASTLE_E_SQ = (ToMove == WHITE ? E1 : E8); + constexpr Square CASTLE_F_SQ = (ToMove == WHITE ? F1 : F8); + constexpr Square CASTLE_G_SQ = (ToMove == WHITE ? G1 : G8); + constexpr CastlingRight CASTLE_KING_MASK = (ToMove == WHITE ? CASTLE_KING_WHITE : CASTLE_KING_BLACK); + constexpr CastlingRight CASTLE_QUEEN_MASK = (ToMove == WHITE ? CASTLE_QUEEN_WHITE : CASTLE_QUEEN_BLACK); + if (!board.is_square_attacked(CASTLE_E_SQ, Opponent)) { if ((board.get_castling_rights() & CASTLE_KING_MASK) && !bitboard::get_bit(board.get_occupancies(BOTH), CASTLE_F_SQ) && !bitboard::get_bit(board.get_occupancies(BOTH), CASTLE_G_SQ)) { - if (!board.is_square_attacked(CASTLE_F_SQ, OPPONENT) && !board.is_square_attacked(CASTLE_G_SQ, OPPONENT)) + if (!board.is_square_attacked(CASTLE_F_SQ, Opponent) && !board.is_square_attacked(CASTLE_G_SQ, Opponent)) { move_list.emplace_back(CASTLE_E_SQ, CASTLE_G_SQ, KING, EMPTY_PIECE, EMPTY_PIECE, false, false, true); } } if ((board.get_castling_rights() & CASTLE_QUEEN_MASK) && !bitboard::get_bit(board.get_occupancies(BOTH), CASTLE_D_SQ) && !bitboard::get_bit(board.get_occupancies(BOTH), CASTLE_C_SQ) && !bitboard::get_bit(board.get_occupancies(BOTH), CASTLE_B_SQ)) { - if (!board.is_square_attacked(CASTLE_D_SQ, OPPONENT) && !board.is_square_attacked(CASTLE_C_SQ, OPPONENT)) + if (!board.is_square_attacked(CASTLE_D_SQ, Opponent) && !board.is_square_attacked(CASTLE_C_SQ, Opponent)) { move_list.emplace_back(CASTLE_E_SQ, CASTLE_C_SQ, KING, EMPTY_PIECE, EMPTY_PIECE, false, false, true); } @@ -221,7 +221,7 @@ namespace movegen } } - template + template static void generate_pawn_double_pushes(std::vector &move_list, const Board &board) { constexpr int PAWN_DOUBLE_PUSH_OFFSET = ToMove == WHITE ? -16 : 16; @@ -236,7 +236,7 @@ namespace movegen } } - template + template static void generate_pawn_single_push_with_promotion(std::vector &move_list, const Board &board) { constexpr int PAWN_SINGLE_PUSH_OFFSET = ToMove == WHITE ? -8 : 8; @@ -255,7 +255,7 @@ namespace movegen } } - template + template static void generate_pawn_single_push_no_promotion(std::vector &move_list, const Board &board) { constexpr int PAWN_SINGLE_PUSH_OFFSET = ToMove == WHITE ? -8 : 8; @@ -271,19 +271,19 @@ namespace movegen } } - template + template static void generate_pawn_captures_with_promotion(std::vector &move_list, const Board &board) { - constexpr PieceColor OPPONENT = ToMove == WHITE ? BLACK : WHITE; + constexpr Color Opponent = ToMove == WHITE ? BLACK : WHITE; constexpr int MASK_INDEX = ToMove == WHITE ? 6 : 1; u64 pawns_can_capture_with_promo = board.get_pieces(ToMove, PAWN) & tables::MASK_RANK[MASK_INDEX]; while (pawns_can_capture_with_promo) { - int from_square = bitboard::bit_scan_forward(pawns_can_capture_with_promo); - u64 pawn_captures_promo = tables::ATTACKS_PAWN[ToMove][from_square] & board.get_occupancies(OPPONENT); + Square from_square = bitboard::bit_scan_forward(pawns_can_capture_with_promo); + u64 pawn_captures_promo = tables::get_pawn_attacks(ToMove, from_square) & board.get_occupancies(Opponent); while (pawn_captures_promo) { - int to_square = bitboard::bit_scan_forward(pawn_captures_promo); + Square to_square = bitboard::bit_scan_forward(pawn_captures_promo); move_list.emplace_back(from_square, to_square, PAWN, board.get_piece_from_square(to_square).type, QUEEN, false, false, false); move_list.emplace_back(from_square, to_square, PAWN, board.get_piece_from_square(to_square).type, KNIGHT, false, false, false); move_list.emplace_back(from_square, to_square, PAWN, board.get_piece_from_square(to_square).type, ROOK, false, false, false); @@ -294,19 +294,19 @@ namespace movegen } } - template + template static void generate_pawn_captures_no_promotion(std::vector &move_list, const Board &board) { - constexpr PieceColor OPPONENT = ToMove == WHITE ? BLACK : WHITE; + constexpr Color Opponent = ToMove == WHITE ? BLACK : WHITE; constexpr int MASK_INDEX = ToMove == WHITE ? 6 : 1; u64 pawns_can_capture_no_promo = board.get_pieces(ToMove, PAWN) & tables::MASK_CLEAR_RANK[MASK_INDEX]; while (pawns_can_capture_no_promo) { - int from_square = bitboard::bit_scan_forward(pawns_can_capture_no_promo); - u64 pawn_captures_no_promo = tables::ATTACKS_PAWN[ToMove][from_square] & board.get_occupancies(OPPONENT); + Square from_square = bitboard::bit_scan_forward(pawns_can_capture_no_promo); + u64 pawn_captures_no_promo = tables::get_pawn_attacks(ToMove, from_square) & board.get_occupancies(Opponent); while (pawn_captures_no_promo) { - int to_square = bitboard::bit_scan_forward(pawn_captures_no_promo); + Square to_square = bitboard::bit_scan_forward(pawn_captures_no_promo); move_list.emplace_back(from_square, to_square, PAWN, board.get_piece_from_square(to_square).type, EMPTY_PIECE, false, false, false); bitboard::pop_last_bit(pawn_captures_no_promo); } @@ -314,13 +314,13 @@ namespace movegen } } - template + template static void generate_en_passant_capture(std::vector &move_list, const Board &board) { - constexpr PieceColor OPPONENT = ToMove == WHITE ? BLACK : WHITE; + constexpr Color Opponent = ToMove == WHITE ? BLACK : WHITE; if (board.get_en_passant_square() != -1) { - u64 pawns_can_en_passant = tables::ATTACKS_PAWN[OPPONENT][board.get_en_passant_square()] & board.get_pieces(ToMove, PAWN); + u64 pawns_can_en_passant = tables::get_pawn_attacks(Opponent, board.get_en_passant_square()) & board.get_pieces(ToMove, PAWN); while (pawns_can_en_passant) { int from_square = bitboard::bit_scan_forward(pawns_can_en_passant); @@ -330,31 +330,31 @@ namespace movegen } } - template + template static void generate_leaper_moves(std::vector &move_list, const Board &board) { static_assert(PType == KNIGHT || PType == KING, "Unsupported piece type in generateLeaperMoves()"); - constexpr PieceColor OPPONENT = ToMove == WHITE ? BLACK : WHITE; - constexpr u64 *ATTACKS_TABLE = PType == KNIGHT ? tables::ATTACKS_KNIGHT : tables::ATTACKS_KING; + constexpr Color Opponent = ToMove == WHITE ? BLACK : WHITE; + constexpr u64 (*ATTACKS_FUNC)(Square sq) = PType == KNIGHT ? tables::get_knight_attacks : tables::get_king_attacks; u64 to_move_occupancies = board.get_occupancies(ToMove); - u64 opponent_occupancies = board.get_occupancies(OPPONENT); + u64 opponent_occupancies = board.get_occupancies(Opponent); u64 to_move_pieces = board.get_pieces(ToMove, PType); while (to_move_pieces) { - int from_square = bitboard::bit_scan_forward(to_move_pieces); + Square from_square = bitboard::bit_scan_forward(to_move_pieces); u64 moves; if constexpr (GType == QUIETS) { - moves = ATTACKS_TABLE[from_square] & ~to_move_occupancies & ~opponent_occupancies; + moves = ATTACKS_FUNC(from_square) & ~to_move_occupancies & ~opponent_occupancies; } else { - moves = ATTACKS_TABLE[from_square] & ~to_move_occupancies & opponent_occupancies; + moves = ATTACKS_FUNC(from_square) & ~to_move_occupancies & opponent_occupancies; } while (moves) { - int to_square = bitboard::bit_scan_forward(moves); + Square to_square = bitboard::bit_scan_forward(moves); if constexpr (GType == QUIETS) { move_list.emplace_back(from_square, to_square, PType, EMPTY_PIECE, EMPTY_PIECE, false, false, false); @@ -369,21 +369,21 @@ namespace movegen } } - template + template static void generate_slider_moves(std::vector &move_list, const Board &board) { static_assert(PType == BISHOP || PType == ROOK || PType == QUEEN, "Unsupported piece type in generate_slider_moves()"); - constexpr PieceColor OPPONENT = ToMove == WHITE ? BLACK : WHITE; - constexpr u64 (*ATTACKS_FUNC)(int sq, u64 occ) = (PType == BISHOP ? tables::get_bishop_attacks - : PType == ROOK ? tables::get_rook_attacks - : tables::get_queen_attacks); + constexpr Color Opponent = ToMove == WHITE ? BLACK : WHITE; + constexpr u64 (*ATTACKS_FUNC)(const Square sq, u64 occ) = (PType == BISHOP ? tables::get_bishop_attacks + : PType == ROOK ? tables::get_rook_attacks + : tables::get_queen_attacks); u64 to_move_occupancies = board.get_occupancies(ToMove); - u64 opponent_occupancies = board.get_occupancies(OPPONENT); + u64 opponent_occupancies = board.get_occupancies(Opponent); u64 to_move_pieces = board.get_pieces(ToMove, PType); while (to_move_pieces) { - int from_square = bitboard::bit_scan_forward(to_move_pieces); + Square from_square = bitboard::bit_scan_forward(to_move_pieces); u64 moves; if constexpr (GType == QUIETS) { @@ -395,7 +395,7 @@ namespace movegen } while (moves) { - int to_square = bitboard::bit_scan_forward(moves); + Square to_square = bitboard::bit_scan_forward(moves); if constexpr (GType == QUIETS) { move_list.emplace_back(from_square, to_square, PType, EMPTY_PIECE, EMPTY_PIECE, false, false, false); diff --git a/src/engine/movegen/perft.cpp b/src/engine/movegen/perft.cpp index f7746b7..538041a 100644 --- a/src/engine/movegen/perft.cpp +++ b/src/engine/movegen/perft.cpp @@ -20,8 +20,8 @@ namespace perft { Board::GameState board_info = board.get_state(); board.make(move); - int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); - int attacker_side = board.get_side_to_move(); + Square king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); + Color attacker_side = board.get_side_to_move(); if (!board.is_square_attacked(king_sq, attacker_side)) { nodes += perft(board, depth - 1); diff --git a/src/engine/movegen/tables.cpp b/src/engine/movegen/tables.cpp index 6c6432e..641c03b 100644 --- a/src/engine/movegen/tables.cpp +++ b/src/engine/movegen/tables.cpp @@ -7,11 +7,53 @@ namespace tables { u64 SQUARE_BB[N_SQUARES]; - u64 ATTACKS_PAWN[N_SIDES][N_SQUARES]; - u64 ATTACKS_KNIGHT[N_SQUARES]; - u64 ATTACKS_KING[N_SQUARES]; - u64 ATTACKS_BISHOP[N_SQUARES][512]; - u64 ATTACKS_ROOK[N_SQUARES][4096]; + + // clang-format off + static const int RELEVANT_BITS_COUNT_BISHOP[N_SQUARES] = { + 6, 5, 5, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 7, 7, 7, 7, 5, 5, + 5, 5, 7, 9, 9, 7, 5, 5, + 5, 5, 7, 9, 9, 7, 5, 5, + 5, 5, 7, 7, 7, 7, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 5, 5, 5, 5, 5, 5, 6, + }; + // clang-format on + + // clang-format off + static const int RELEVANT_BITS_COUNT_ROOK[N_SQUARES] = { + 12, 11, 11, 11, 11, 11, 11, 12, + 11, 10, 10, 10, 10, 10, 10, 11, + 11, 10, 10, 10, 10, 10, 10, 11, + 11, 10, 10, 10, 10, 10, 10, 11, + 11, 10, 10, 10, 10, 10, 10, 11, + 11, 10, 10, 10, 10, 10, 10, 11, + 11, 10, 10, 10, 10, 10, 10, 11, + 12, 11, 11, 11, 11, 11, 11, 12, + }; + // clang-format on + + static auto ATTACKS_PAWN = new u64[N_SIDES][N_SQUARES]; + static u64 *ATTACKS_KNIGHT = new u64[N_SQUARES]; + static u64 *ATTACKS_KING = new u64[N_SQUARES]; + static auto ATTACKS_BISHOP = new u64[N_SQUARES][512]; + static auto ATTACKS_ROOK = new u64[N_SQUARES][4096]; + + u64 square_to_bitboard(Square sq) + { + return SQUARE_BB[sq]; + } + + int get_relevant_bits_count_bishop(Square sq) + { + return RELEVANT_BITS_COUNT_BISHOP[sq]; + } + + int get_relevant_bits_count_rook(Square sq) + { + return RELEVANT_BITS_COUNT_ROOK[sq]; + } void init() { @@ -44,9 +86,10 @@ namespace tables int occupancy_indices = 1 << RELEVANT_BITS_COUNT_BISHOP[sq]; for (int i = 0; i < occupancy_indices; i++) { - u64 occupancy = bitboard::set_occupancy(i, RELEVANT_BITS_COUNT_BISHOP[sq], magics::MAGIC_TABLE_BISHOP[sq].mask); - int magic = (int)((occupancy * magics::MAGIC_TABLE_BISHOP[sq].magic) >> magics::MAGIC_TABLE_BISHOP[sq].shift); - ATTACKS_BISHOP[sq][magic] = attacks::mask_bishop_attacks(sq, occupancy); + magics::Magic magic = magics::get_bishop_magic((Square)sq); + u64 occupancy = bitboard::set_occupancy(i, RELEVANT_BITS_COUNT_BISHOP[sq], magic.mask); + int index = (int)((occupancy * magic.magic) >> magic.shift); + ATTACKS_BISHOP[sq][index] = attacks::mask_bishop_xray_attacks((Square)sq, occupancy); } } @@ -55,11 +98,57 @@ namespace tables int occupancy_indices = 1 << RELEVANT_BITS_COUNT_ROOK[sq]; for (int i = 0; i < occupancy_indices; i++) { - u64 occupancy = bitboard::set_occupancy(i, RELEVANT_BITS_COUNT_ROOK[sq], magics::MAGIC_TABLE_ROOK[sq].mask); - int magic = (int)((occupancy * magics::MAGIC_TABLE_ROOK[sq].magic) >> magics::MAGIC_TABLE_ROOK[sq].shift); - ATTACKS_ROOK[sq][magic] = attacks::mask_rook_attacks(sq, occupancy); + magics::Magic magic = magics::get_rook_magic((Square)sq); + u64 occupancy = bitboard::set_occupancy(i, RELEVANT_BITS_COUNT_ROOK[sq], magic.mask); + int index = (int)((occupancy * magic.magic) >> magic.shift); + ATTACKS_ROOK[sq][index] = attacks::mask_rook_xray_attacks((Square)sq, occupancy); } } } + void teardown() + { + delete[] ATTACKS_PAWN; + delete[] ATTACKS_BISHOP; + delete[] ATTACKS_ROOK; + } + + u64 get_pawn_attacks(Color color, Square sq) + { + return ATTACKS_PAWN[color][sq]; + } + + u64 get_knight_attacks(Square sq) + { + return ATTACKS_KNIGHT[sq]; + } + + u64 get_king_attacks(Square sq) + { + return ATTACKS_KING[sq]; + } + + u64 get_bishop_attacks(const Square sq, u64 occ) + { + magics::Magic magic = magics::get_bishop_magic(sq); + occ &= magic.mask; + occ *= magic.magic; + occ >>= magic.shift; + return ATTACKS_BISHOP[sq][occ]; + } + + u64 get_rook_attacks(const Square sq, u64 occ) + { + magics::Magic magic = magics::get_rook_magic(sq); + occ &= magic.mask; + occ *= magic.magic; + occ >>= magic.shift; + return ATTACKS_ROOK[sq][occ]; + } + + u64 get_queen_attacks(const Square sq, u64 occ) + { + return get_bishop_attacks(sq, occ) | get_rook_attacks(sq, occ); + } + } // namespace tables \ No newline at end of file diff --git a/src/engine/movegen/tables.hpp b/src/engine/movegen/tables.hpp index 14fdc50..6c418b0 100644 --- a/src/engine/movegen/tables.hpp +++ b/src/engine/movegen/tables.hpp @@ -6,6 +6,10 @@ namespace tables { + /** + * This arrays should only be indexed with compile time values + */ + constexpr u64 MASK_RANK[] = { 0xFF, 0xFF00, @@ -46,60 +50,94 @@ namespace tables 0xBFBFBFBFBFBFBFBF, 0x7F7F7F7F7F7F7F7F}; - // clang-format off - constexpr int RELEVANT_BITS_COUNT_BISHOP[N_SQUARES] = { - 6, 5, 5, 5, 5, 5, 5, 6, - 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 7, 7, 7, 7, 5, 5, - 5, 5, 7, 9, 9, 7, 5, 5, - 5, 5, 7, 9, 9, 7, 5, 5, - 5, 5, 7, 7, 7, 7, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 6, 5, 5, 5, 5, 5, 5, 6, - }; - - constexpr int RELEVANT_BITS_COUNT_ROOK[N_SQUARES] = { - 12, 11, 11, 11, 11, 11, 11, 12, - 11, 10, 10, 10, 10, 10, 10, 11, - 11, 10, 10, 10, 10, 10, 10, 11, - 11, 10, 10, 10, 10, 10, 10, 11, - 11, 10, 10, 10, 10, 10, 10, 11, - 11, 10, 10, 10, 10, 10, 10, 11, - 11, 10, 10, 10, 10, 10, 10, 11, - 12, 11, 11, 11, 11, 11, 11, 12, - }; - // clang-format on - - extern u64 SQUARE_BB[N_SQUARES]; - - extern u64 ATTACKS_PAWN[BOTH][N_SQUARES]; // Direct Access TODO: make this accessible through function only? - extern u64 ATTACKS_KNIGHT[N_SQUARES]; // Direct Access - extern u64 ATTACKS_KING[N_SQUARES]; // Direct Access - - extern u64 ATTACKS_BISHOP[N_SQUARES][512]; // Needs Magics Bitboards For Accessing - extern u64 ATTACKS_ROOK[N_SQUARES][4096]; // Needs Magics Bitboards For Accessing - + /** + * @brief Converts a square to its bitboard representation + * + * @param sq + * @return u64 + */ + u64 square_to_bitboard(Square sq); + + /** + * @brief Gets the relevant bits count for the bishop piece + * + * @param sq + * @return int + */ + int get_relevant_bits_count_bishop(Square sq); + + /** + * @brief Gets the relevant bits count for the rook piece + * + * @param sq + * @return int + */ + int get_relevant_bits_count_rook(Square sq); + + /** + * @brief Inits all the tables + * + */ void init(); - inline u64 get_bishop_attacks(const int sq, u64 occ) - { - occ &= magics::MAGIC_TABLE_BISHOP[sq].mask; - occ *= magics::MAGIC_TABLE_BISHOP[sq].magic; - occ >>= magics::MAGIC_TABLE_BISHOP[sq].shift; - return ATTACKS_BISHOP[sq][occ]; - } - - inline u64 get_rook_attacks(const int sq, u64 occ) - { - occ &= magics::MAGIC_TABLE_ROOK[sq].mask; - occ *= magics::MAGIC_TABLE_ROOK[sq].magic; - occ >>= magics::MAGIC_TABLE_ROOK[sq].shift; - return ATTACKS_ROOK[sq][occ]; - } - - inline u64 get_queen_attacks(const int sq, u64 occ) - { - return get_bishop_attacks(sq, occ) | get_rook_attacks(sq, occ); - } + /** + * @brief Deallocates all the heap allocated tables + * + */ + void teardown(); + + /** + * @brief Gets the pawn attacks + * + * @param color + * @param sq + * @return u64 + */ + u64 get_pawn_attacks(Color color, Square sq); + + /** + * @brief Gets the knight attacks + * + * @param color + * @param sq + * @return u64 + */ + u64 get_knight_attacks(Square sq); + + /** + * @brief Gets the king attacks + * + * @param color + * @param sq + * @return u64 + */ + u64 get_king_attacks(Square sq); + + /** + * @brief Gets the bishop attacks + * + * @param sq + * @param occ + * @return u64 + */ + u64 get_bishop_attacks(const Square sq, u64 occ); + + /** + * @brief Gets the rook attacks + * + * @param sq + * @param occ + * @return u64 + */ + u64 get_rook_attacks(const Square sq, u64 occ); + + /** + * @brief Gets the queen attacks + * + * @param sq + * @param occ + * @return u64 + */ + u64 get_queen_attacks(const Square sq, u64 occ); } // namespace tables \ No newline at end of file diff --git a/src/engine/movepicker/eval.cpp b/src/engine/movepicker/eval.cpp index a2cd499..c756b60 100644 --- a/src/engine/movepicker/eval.cpp +++ b/src/engine/movepicker/eval.cpp @@ -152,7 +152,7 @@ namespace eval { for (int sq = A1; sq < N_SQUARES; sq++) { - int sq_flipped = utils::flipSquare(sq); + Square sq_flipped = utils::flip_square((Square)sq); MG_TABLE[WHITE][PAWN][sq] = MG_PAWN_TABLE[sq_flipped]; MG_TABLE[WHITE][KNIGHT][sq] = MG_KNIGHT_TABLE[sq_flipped]; @@ -193,10 +193,10 @@ namespace eval for (int piece_type = PAWN; piece_type < N_PIECES; piece_type++) { - u64 pieces_white = board.get_pieces(WHITE, piece_type); + u64 pieces_white = board.get_pieces(WHITE, (PieceType)piece_type); while (pieces_white) { - int sq = bitboard::bit_scan_forward(pieces_white); + Square sq = bitboard::bit_scan_forward(pieces_white); mg[WHITE] += MG_TABLE[WHITE][piece_type][sq]; eg[WHITE] += EG_TABLE[WHITE][piece_type][sq]; material[WHITE] += PIECE_SCORES[piece_type]; @@ -204,10 +204,10 @@ namespace eval bitboard::pop_bit(pieces_white, sq); } - u64 pieces_black = board.get_pieces(BLACK, piece_type); + u64 pieces_black = board.get_pieces(BLACK, (PieceType)piece_type); while (pieces_black) { - int sq = bitboard::bit_scan_forward(pieces_black); + Square sq = bitboard::bit_scan_forward(pieces_black); mg[BLACK] += MG_TABLE[BLACK][piece_type][sq]; eg[BLACK] += EG_TABLE[BLACK][piece_type][sq]; material[BLACK] += PIECE_SCORES[piece_type]; @@ -216,8 +216,8 @@ namespace eval } } - int to_move = board.get_side_to_move(); - int opponent = board.get_opponent(); + Color to_move = board.get_side_to_move(); + Color opponent = board.get_opponent(); int mg_score = mg[to_move] - mg[opponent]; int eg_score = eg[to_move] - eg[opponent]; diff --git a/src/engine/movepicker/movepicker.cpp b/src/engine/movepicker/movepicker.cpp index 9924aab..69bd09d 100644 --- a/src/engine/movepicker/movepicker.cpp +++ b/src/engine/movepicker/movepicker.cpp @@ -44,7 +44,7 @@ int MovePicker::score(const Move move) if (move.is_capture()) { - return MVV_LVA[move.get_piece()][move.get_captured_piece()]; + return MVV_LVA[move.get_piece_type()][move.get_captured_piece_type()]; } if (_killer_moves[0][_current_depth] == move) @@ -57,7 +57,7 @@ int MovePicker::score(const Move move) return 8000; } - return _history_moves[_board.get_side_to_move()][move.get_piece()][move.get_to_square()]; + return _history_moves[_board.get_side_to_move()][move.get_piece_type()][move.get_to_square()]; } int MovePicker::search(int depth, int alpha, int beta) @@ -71,8 +71,8 @@ int MovePicker::search(int depth, int alpha, int beta) { _board.make(move); - int attacker_side = _board.get_side_to_move(); - int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_opponent(), KING)); + Color attacker_side = _board.get_side_to_move(); + Square king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_opponent(), KING)); if (!_board.is_square_attacked(king_sq, attacker_side)) { _current_depth++; @@ -163,7 +163,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) { _board.make(move); - int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_opponent(), KING)); + Square king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_opponent(), KING)); if (!_board.is_square_attacked(king_sq, _board.get_side_to_move())) { has_legal_moves = true; @@ -255,7 +255,7 @@ int MovePicker::negamax(int alpha, int beta, int depth) if (!has_legal_moves) { // Check Mate - int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_side_to_move(), KING)); + Square king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_side_to_move(), KING)); if (_board.is_square_attacked(king_sq, _board.get_opponent())) { _tt.set_entry(state.hash_key, depth, TTable::HASH_FLAG_SCORE, MIN_EVAL + _current_depth, _pv_table.get_pv_from_depth(_current_depth)); @@ -283,7 +283,7 @@ int MovePicker::quiescence(int alpha, int beta) if (!movegen::has_legal_moves(_board)) { - int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_side_to_move(), KING)); + Square king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_side_to_move(), KING)); if (_board.is_square_attacked(king_sq, _board.get_opponent())) { _tt.set_entry(_board.get_hash_key(), 0, TTable::HASH_FLAG_SCORE, MIN_EVAL + _current_depth, _pv_table.get_pv_from_depth(_current_depth)); @@ -318,7 +318,7 @@ int MovePicker::quiescence(int alpha, int beta) { _board.make(capture); - int king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_opponent(), KING)); + Square king_sq = bitboard::bit_scan_forward(_board.get_pieces(_board.get_opponent(), KING)); if (!_board.is_square_attacked(king_sq, _board.get_side_to_move())) { _current_depth++; @@ -359,7 +359,7 @@ void MovePicker::add_to_killer_moves(const Move move) void MovePicker::add_to_history_moves(const Move move) { // History Move Heuristic - _history_moves[_board.get_side_to_move()][move.get_piece()][move.get_to_square()] += _current_depth; + _history_moves[_board.get_side_to_move()][move.get_piece_type()][move.get_to_square()] += _current_depth; } void MovePicker::clear_search_counters() diff --git a/src/engine/movepicker/ttable.hpp b/src/engine/movepicker/ttable.hpp index 2c8d7ff..5a87e0f 100644 --- a/src/engine/movepicker/ttable.hpp +++ b/src/engine/movepicker/ttable.hpp @@ -38,4 +38,4 @@ class TTable void clear(); void set_entry(u64 hash_key, int depth, int flag, int score, std::vector moves); TTOutput read_hash(u64 hash_key, int alpha, int beta, int depth); -}; // TODO: List Of Moves \ No newline at end of file +}; \ No newline at end of file diff --git a/src/engine/utils.hpp b/src/engine/utils.hpp index 1ad7b2b..e52a57d 100644 --- a/src/engine/utils.hpp +++ b/src/engine/utils.hpp @@ -1,17 +1,20 @@ #pragma once +#include + #include #include namespace utils { // Square Operations - inline int get_square(const int rk, const int fl) { return 8 * rk + fl; } - inline int get_file(const int sq) { return sq & 7; } - inline int get_rank(const int sq) { return sq >> 3; } - inline int flipSquare(const int sq) { return sq ^ 56; } + inline Square get_square(const Rank rk, const File fl) { return (Square)(8 * rk + fl); } + inline Square flip_square(const Square sq) { return (Square)(sq ^ 56); } + + inline File get_file(const Square sq) { return (File)(sq & 7); } + inline Rank get_rank(const Square sq) { return (Rank)(sq >> 3); } // Flip Side to Move - inline int get_opponent(int to_move) { return to_move ^ 1; } + inline Color get_opponent(Color to_move) { return (Color)((int)to_move ^ 1); } } // namespace utils \ No newline at end of file diff --git a/src/engine/zobrist.cpp b/src/engine/zobrist.cpp index 0f47183..e1d4b57 100644 --- a/src/engine/zobrist.cpp +++ b/src/engine/zobrist.cpp @@ -50,10 +50,10 @@ namespace zobrist { for (int side = WHITE; side < BOTH; side++) { - u64 bitboard = board.get_pieces(side, piece); + u64 bitboard = board.get_pieces((Color)side, (PieceType)piece); while (bitboard) { - int sq = bitboard::bit_scan_forward(bitboard); + Square sq = bitboard::bit_scan_forward(bitboard); final_key ^= piece_keys[side][piece][sq]; bitboard::pop_bit(bitboard, sq); } diff --git a/src/interfaces/cli/commands/dividedperft.cpp b/src/interfaces/cli/commands/dividedperft.cpp index 54dae0a..64a13d1 100644 --- a/src/interfaces/cli/commands/dividedperft.cpp +++ b/src/interfaces/cli/commands/dividedperft.cpp @@ -16,8 +16,8 @@ static void dperft(int depth, Board &board) { Board::GameState state = board.get_state(); board.make(move); - int king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); - int attacker_side = board.get_side_to_move(); + Square king_sq = bitboard::bit_scan_forward(board.get_pieces(board.get_opponent(), KING)); + Color attacker_side = board.get_side_to_move(); if (!board.is_square_attacked(king_sq, attacker_side)) { int nodes = perft::perft(board, depth - 1); diff --git a/src/interfaces/cli/commands/exit.cpp b/src/interfaces/cli/commands/exit.cpp index 0778924..f91d176 100644 --- a/src/interfaces/cli/commands/exit.cpp +++ b/src/interfaces/cli/commands/exit.cpp @@ -1,6 +1,9 @@ #include +#include + void cli::ExitCommand::execute([[maybe_unused]] std::vector &args) { + tables::teardown(); exit(EXIT_SUCCESS); } \ No newline at end of file diff --git a/src/interfaces/uci/commands/exit.cpp b/src/interfaces/uci/commands/exit.cpp index 0a31413..3b87e56 100644 --- a/src/interfaces/uci/commands/exit.cpp +++ b/src/interfaces/uci/commands/exit.cpp @@ -1,6 +1,9 @@ #include +#include + void uci::QuitCommand::execute([[maybe_unused]] std::vector &args) { + tables::teardown(); exit(EXIT_SUCCESS); } \ No newline at end of file diff --git a/tests/movegen/test_board.cpp b/tests/movegen/test_board.cpp index ba817f9..ecb8650 100644 --- a/tests/movegen/test_board.cpp +++ b/tests/movegen/test_board.cpp @@ -7,7 +7,7 @@ #include #include -void setup() +static void setup() { magics::init(); tables::init(); diff --git a/tests/movegen/test_move.cpp b/tests/movegen/test_move.cpp index 599f9be..3ed6aa1 100644 --- a/tests/movegen/test_move.cpp +++ b/tests/movegen/test_move.cpp @@ -13,9 +13,9 @@ TEST_CASE("Test Moves") Move move = Move(0, 63, PAWN, QUEEN, QUEEN, true, false, true); REQUIRE(move.get_from_square() == 0); REQUIRE(move.get_to_square() == 63); - REQUIRE(move.get_piece() == PAWN); - REQUIRE(move.get_captured_piece() == QUEEN); - REQUIRE(move.get_promoted_piece() == QUEEN); + REQUIRE(move.get_piece_type() == PAWN); + REQUIRE(move.get_captured_piece_type() == QUEEN); + REQUIRE(move.get_promoted_piece_type() == QUEEN); REQUIRE(move.is_double_push() == true); REQUIRE(move.is_en_passant() == false); REQUIRE(move.is_castle() == true); @@ -28,9 +28,9 @@ TEST_CASE("Test Moves") Move move = Move(0, 1, PAWN, KING, EMPTY_PIECE, false, false, false); REQUIRE(move.get_from_square() == 0); REQUIRE(move.get_to_square() == 1); - REQUIRE(move.get_piece() == PAWN); - REQUIRE(move.get_captured_piece() == KING); - REQUIRE(move.get_promoted_piece() == EMPTY_PIECE); + REQUIRE(move.get_piece_type() == PAWN); + REQUIRE(move.get_captured_piece_type() == KING); + REQUIRE(move.get_promoted_piece_type() == EMPTY_PIECE); REQUIRE(move.is_promotion() == false); REQUIRE(move.is_double_push() == false); REQUIRE(move.is_en_passant() == false); @@ -45,9 +45,9 @@ TEST_CASE("Test Moves") Move move = Move(0, 1, PAWN, QUEEN, EMPTY_PIECE, false, false, false); REQUIRE(move.get_from_square() == 0); REQUIRE(move.get_to_square() == 1); - REQUIRE(move.get_piece() == PAWN); - REQUIRE(move.get_captured_piece() == QUEEN); - REQUIRE(move.get_promoted_piece() == EMPTY_PIECE); + REQUIRE(move.get_piece_type() == PAWN); + REQUIRE(move.get_captured_piece_type() == QUEEN); + REQUIRE(move.get_promoted_piece_type() == EMPTY_PIECE); REQUIRE(move.is_promotion() == false); REQUIRE(move.is_double_push() == false); REQUIRE(move.is_en_passant() == false); @@ -61,9 +61,9 @@ TEST_CASE("Test Moves") Move move = Move(0, 1, PAWN, ROOK, EMPTY_PIECE, false, false, false); REQUIRE(move.get_from_square() == 0); REQUIRE(move.get_to_square() == 1); - REQUIRE(move.get_piece() == PAWN); - REQUIRE(move.get_captured_piece() == ROOK); - REQUIRE(move.get_promoted_piece() == EMPTY_PIECE); + REQUIRE(move.get_piece_type() == PAWN); + REQUIRE(move.get_captured_piece_type() == ROOK); + REQUIRE(move.get_promoted_piece_type() == EMPTY_PIECE); REQUIRE(move.is_promotion() == false); REQUIRE(move.is_double_push() == false); REQUIRE(move.is_en_passant() == false); @@ -77,9 +77,9 @@ TEST_CASE("Test Moves") Move move = Move(0, 1, PAWN, BISHOP, EMPTY_PIECE, false, false, false); REQUIRE(move.get_from_square() == 0); REQUIRE(move.get_to_square() == 1); - REQUIRE(move.get_piece() == PAWN); - REQUIRE(move.get_captured_piece() == BISHOP); - REQUIRE(move.get_promoted_piece() == EMPTY_PIECE); + REQUIRE(move.get_piece_type() == PAWN); + REQUIRE(move.get_captured_piece_type() == BISHOP); + REQUIRE(move.get_promoted_piece_type() == EMPTY_PIECE); REQUIRE(move.is_promotion() == false); REQUIRE(move.is_double_push() == false); REQUIRE(move.is_en_passant() == false); @@ -93,9 +93,9 @@ TEST_CASE("Test Moves") Move move = Move(0, 1, PAWN, KNIGHT, EMPTY_PIECE, false, false, false); REQUIRE(move.get_from_square() == 0); REQUIRE(move.get_to_square() == 1); - REQUIRE(move.get_piece() == PAWN); - REQUIRE(move.get_captured_piece() == KNIGHT); - REQUIRE(move.get_promoted_piece() == EMPTY_PIECE); + REQUIRE(move.get_piece_type() == PAWN); + REQUIRE(move.get_captured_piece_type() == KNIGHT); + REQUIRE(move.get_promoted_piece_type() == EMPTY_PIECE); REQUIRE(move.is_promotion() == false); REQUIRE(move.is_double_push() == false); REQUIRE(move.is_en_passant() == false); @@ -109,8 +109,8 @@ TEST_CASE("Test Moves") Move move = Move(0, 1, PAWN, PAWN, EMPTY_PIECE, false, false, false); REQUIRE(move.get_from_square() == 0); REQUIRE(move.get_to_square() == 1); - REQUIRE(move.get_piece() == PAWN); - REQUIRE(move.get_captured_piece() == PAWN); + REQUIRE(move.get_piece_type() == PAWN); + REQUIRE(move.get_captured_piece_type() == PAWN); REQUIRE(move.is_promotion() == false); REQUIRE(move.is_double_push() == false); REQUIRE(move.is_en_passant() == false); diff --git a/tests/movegen/test_tables.cpp b/tests/movegen/test_tables.cpp index 6f3bd6a..2f49f6a 100644 --- a/tests/movegen/test_tables.cpp +++ b/tests/movegen/test_tables.cpp @@ -20,28 +20,28 @@ TEST_CASE("Test Pawn Moves") SECTION("Corners Start") { - REQUIRE(tables::ATTACKS_PAWN[WHITE][A2] == 0x20000); - REQUIRE(tables::ATTACKS_PAWN[WHITE][H2] == 0x400000); - REQUIRE(tables::ATTACKS_PAWN[BLACK][A7] == 0x20000000000); - REQUIRE(tables::ATTACKS_PAWN[BLACK][H7] == 0x400000000000); + REQUIRE(tables::get_pawn_attacks(WHITE, A2) == 0x20000); + REQUIRE(tables::get_pawn_attacks(WHITE, H2) == 0x400000); + REQUIRE(tables::get_pawn_attacks(BLACK, A7) == 0x20000000000); + REQUIRE(tables::get_pawn_attacks(BLACK, H7) == 0x400000000000); } SECTION("Limits White") { - REQUIRE(tables::ATTACKS_PAWN[WHITE][A8] == 0x0); - REQUIRE(tables::ATTACKS_PAWN[WHITE][H8] == 0x0); + REQUIRE(tables::get_pawn_attacks(WHITE, A8) == 0x0); + REQUIRE(tables::get_pawn_attacks(WHITE, H8) == 0x0); } SECTION("Limits Black") { - REQUIRE(tables::ATTACKS_PAWN[BLACK][A1] == 0x0); - REQUIRE(tables::ATTACKS_PAWN[BLACK][H1] == 0x0); + REQUIRE(tables::get_pawn_attacks(BLACK, A1) == 0x0); + REQUIRE(tables::get_pawn_attacks(BLACK, H1) == 0x0); } SECTION("Middle of the Board") { - REQUIRE(tables::ATTACKS_PAWN[WHITE][D4] == 0x1400000000); - REQUIRE(tables::ATTACKS_PAWN[BLACK][E5] == 0x28000000); + REQUIRE(tables::get_pawn_attacks(WHITE, D4) == 0x1400000000); + REQUIRE(tables::get_pawn_attacks(BLACK, E5) == 0x28000000); } } @@ -53,15 +53,15 @@ TEST_CASE("Test King Moves") SECTION("Corners") { - REQUIRE(tables::ATTACKS_KING[A1] == 0x302); - REQUIRE(tables::ATTACKS_KING[H1] == 0xc040); - REQUIRE(tables::ATTACKS_KING[A8] == 0x203000000000000); - REQUIRE(tables::ATTACKS_KING[H8] == 0x40c0000000000000); + REQUIRE(tables::get_king_attacks(A1) == 0x302); + REQUIRE(tables::get_king_attacks(H1) == 0xc040); + REQUIRE(tables::get_king_attacks(A8) == 0x203000000000000); + REQUIRE(tables::get_king_attacks(H8) == 0x40c0000000000000); } SECTION("Middle of the Board") { - REQUIRE(tables::ATTACKS_KING[D4] == 0x1c141c0000); + REQUIRE(tables::get_king_attacks(D4) == 0x1c141c0000); } } @@ -73,15 +73,15 @@ TEST_CASE("Test Knights Moves") SECTION("Corners") { - REQUIRE(tables::ATTACKS_KNIGHT[A1] == 0x20400); - REQUIRE(tables::ATTACKS_KNIGHT[H1] == 0x402000); - REQUIRE(tables::ATTACKS_KNIGHT[A8] == 0x4020000000000); - REQUIRE(tables::ATTACKS_KNIGHT[H8] == 0x20400000000000); + REQUIRE(tables::get_knight_attacks(A1) == 0x20400); + REQUIRE(tables::get_knight_attacks(H1) == 0x402000); + REQUIRE(tables::get_knight_attacks(A8) == 0x4020000000000); + REQUIRE(tables::get_knight_attacks(H8) == 0x20400000000000); } SECTION("Middle of the Board") { - REQUIRE(tables::ATTACKS_KNIGHT[D4] == 0x142200221400); + REQUIRE(tables::get_knight_attacks(D4) == 0x142200221400); } }