From c0e544438a73817396c75596a7b0195ed3dcf58b Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Fri, 28 Dec 2018 22:04:29 +0100 Subject: [PATCH 1/9] Added MagicBitBoards class + some initial routines --- src/chess/bitboard.cc | 80 +++++++++++++++++++++++++++++++++++++++++++ src/chess/bitboard.h | 33 ++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index bbee6bdaa9..bd15ccbd0a 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -28,6 +28,8 @@ #include "chess/bitboard.h" #include "utils/exception.h" +#include + namespace lczero { namespace { @@ -267,6 +269,55 @@ const Move kIdxToMove[] = { "g7g8b", "g7h8q", "g7h8r", "g7h8b", "h7g8q", "h7g8r", "h7g8b", "h7h8q", "h7h8r", "h7h8b"}; +// Which squares can rook attack from every of squares. +static const BitBoard kRookAttacks[] = { + 0x01010101010101FEULL, 0x02020202020202FDULL, 0x04040404040404FBULL, + 0x08080808080808F7ULL, 0x10101010101010EFULL, 0x20202020202020DFULL, + 0x40404040404040BFULL, 0x808080808080807FULL, 0x010101010101FE01ULL, + 0x020202020202FD02ULL, 0x040404040404FB04ULL, 0x080808080808F708ULL, + 0x101010101010EF10ULL, 0x202020202020DF20ULL, 0x404040404040BF40ULL, + 0x8080808080807F80ULL, 0x0101010101FE0101ULL, 0x0202020202FD0202ULL, + 0x0404040404FB0404ULL, 0x0808080808F70808ULL, 0x1010101010EF1010ULL, + 0x2020202020DF2020ULL, 0x4040404040BF4040ULL, 0x80808080807F8080ULL, + 0x01010101FE010101ULL, 0x02020202FD020202ULL, 0x04040404FB040404ULL, + 0x08080808F7080808ULL, 0x10101010EF101010ULL, 0x20202020DF202020ULL, + 0x40404040BF404040ULL, 0x808080807F808080ULL, 0x010101FE01010101ULL, + 0x020202FD02020202ULL, 0x040404FB04040404ULL, 0x080808F708080808ULL, + 0x101010EF10101010ULL, 0x202020DF20202020ULL, 0x404040BF40404040ULL, + 0x8080807F80808080ULL, 0x0101FE0101010101ULL, 0x0202FD0202020202ULL, + 0x0404FB0404040404ULL, 0x0808F70808080808ULL, 0x1010EF1010101010ULL, + 0x2020DF2020202020ULL, 0x4040BF4040404040ULL, 0x80807F8080808080ULL, + 0x01FE010101010101ULL, 0x02FD020202020202ULL, 0x04FB040404040404ULL, + 0x08F7080808080808ULL, 0x10EF101010101010ULL, 0x20DF202020202020ULL, + 0x40BF404040404040ULL, 0x807F808080808080ULL, 0xFE01010101010101ULL, + 0xFD02020202020202ULL, 0xFB04040404040404ULL, 0xF708080808080808ULL, + 0xEF10101010101010ULL, 0xDF20202020202020ULL, 0xBF40404040404040ULL, + 0x7F80808080808080ULL}; +// Which squares can bishop attack. +static const BitBoard kBishopAttacks[] = { + 0x8040201008040200ULL, 0x0080402010080500ULL, 0x0000804020110A00ULL, + 0x0000008041221400ULL, 0x0000000182442800ULL, 0x0000010204885000ULL, + 0x000102040810A000ULL, 0x0102040810204000ULL, 0x4020100804020002ULL, + 0x8040201008050005ULL, 0x00804020110A000AULL, 0x0000804122140014ULL, + 0x0000018244280028ULL, 0x0001020488500050ULL, 0x0102040810A000A0ULL, + 0x0204081020400040ULL, 0x2010080402000204ULL, 0x4020100805000508ULL, + 0x804020110A000A11ULL, 0x0080412214001422ULL, 0x0001824428002844ULL, + 0x0102048850005088ULL, 0x02040810A000A010ULL, 0x0408102040004020ULL, + 0x1008040200020408ULL, 0x2010080500050810ULL, 0x4020110A000A1120ULL, + 0x8041221400142241ULL, 0x0182442800284482ULL, 0x0204885000508804ULL, + 0x040810A000A01008ULL, 0x0810204000402010ULL, 0x0804020002040810ULL, + 0x1008050005081020ULL, 0x20110A000A112040ULL, 0x4122140014224180ULL, + 0x8244280028448201ULL, 0x0488500050880402ULL, 0x0810A000A0100804ULL, + 0x1020400040201008ULL, 0x0402000204081020ULL, 0x0805000508102040ULL, + 0x110A000A11204080ULL, 0x2214001422418000ULL, 0x4428002844820100ULL, + 0x8850005088040201ULL, 0x10A000A010080402ULL, 0x2040004020100804ULL, + 0x0200020408102040ULL, 0x0500050810204080ULL, 0x0A000A1120408000ULL, + 0x1400142241800000ULL, 0x2800284482010000ULL, 0x5000508804020100ULL, + 0xA000A01008040201ULL, 0x4000402010080402ULL, 0x0002040810204080ULL, + 0x0005081020408000ULL, 0x000A112040800000ULL, 0x0014224180000000ULL, + 0x0028448201000000ULL, 0x0050880402010000ULL, 0x00A0100804020100ULL, + 0x0040201008040201ULL}; + std::vector BuildMoveIndices() { std::vector res(4 * 64 * 64); for (size_t i = 0; i < sizeof(kIdxToMove) / sizeof(kIdxToMove[0]); ++i) { @@ -280,8 +331,37 @@ const int kKingCastleIndex = kMoveToIdx[BoardSquare("e1").as_int() * 64 + BoardSquare("h1").as_int()]; const int kQueenCastleIndex = kMoveToIdx[BoardSquare("e1").as_int() * 64 + BoardSquare("a1").as_int()]; + +// Instantiate static magic bitboards object to perform initialization once. +static MagicBitBoards magic_bitboards; } // namespace +const BitBoard MagicBitBoards::kRookMagicNumbers[64]; +const BitBoard MagicBitBoards::kBishopMagicNumbers[64]; + +MagicBitBoards::MagicParams MagicBitBoards::kRookMagicParams[64]; +MagicBitBoards::MagicParams MagicBitBoards::kBishopMagicParams[64]; + +MagicBitBoards::MagicBitBoards() { + // Initialize for all board squares. + for (int square = 0; square < 64; square++) { + const BoardSquare b_sq(square); + + // Calculate occupancy masks by subtracting the board edges from the total + // attack bitboards. + kRookMagicParams[square].mask_ = + kRookAttacks[square] - BoardSquare(b_sq.row(), 0) - + BoardSquare(b_sq.row(), 7) - BoardSquare(0, b_sq.col()) - + BoardSquare(7, b_sq.col()); + kBishopMagicParams[square].mask_ = + kBishopAttacks[square] - BitBoard(0xFF818181818181FFULL); + + // Print the table index size. + std::cout << "Number of relevant occupancy bits = " + << kBishopMagicParams[square].mask_.count() << std::endl; + } +} + Move::Move(const std::string& str, bool black) { if (str.size() < 4) throw Exception("Bad move: " + str); SetFrom(BoardSquare(str.substr(0, 2), black)); diff --git a/src/chess/bitboard.h b/src/chess/bitboard.h index 921e2d5e86..b505787f28 100644 --- a/src/chess/bitboard.h +++ b/src/chess/bitboard.h @@ -36,6 +36,8 @@ namespace lczero { +class MagicBitBoards; + // Stores a coordinates of a single square. class BoardSquare { public: @@ -229,6 +231,37 @@ class BitBoard { std::uint64_t board_ = 0; }; +// Holds magic bitboard routines and performs initialization. +// We use so-called "fancy" magic bitboards. +class MagicBitBoards { + public: + MagicBitBoards(); + + private: + // Structure holding all relevant magic parameters per square (except magic + // number). + struct MagicParams { + // Offset into lookup table. + uint32_t table_offset_; + // Number of index bits in the lookup table index. + uint8_t index_size_; + // Relevant occupancy mask. + BitBoard mask_; + }; + + // Magic numbers for each board square. + static const BitBoard kRookMagicNumbers[]; + static const BitBoard kBishopMagicNumbers[]; + + // Magic parameters for each board square. + static MagicParams kRookMagicParams[64]; + static MagicParams kBishopMagicParams[64]; + + // Magic attack lookup tables. + static BitBoard kRookAttacksTable[102400]; + static BitBoard kBishopAttacksTable[5248]; +}; + class Move { public: enum class Promotion : std::uint8_t { None, Queen, Rook, Bishop, Knight }; From 55f8d4a3b514ca936ff4b7af7613444dbd1a3b0c Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sat, 29 Dec 2018 13:59:40 +0100 Subject: [PATCH 2/9] Added magic initialization + routines to fetch attack bitboards --- src/chess/bitboard.cc | 184 ++++++++++++++++++++++++++++++++++++++---- src/chess/bitboard.h | 34 ++++++-- 2 files changed, 196 insertions(+), 22 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index bd15ccbd0a..e2ab38824c 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -28,7 +28,7 @@ #include "chess/bitboard.h" #include "utils/exception.h" -#include +#include namespace lczero { @@ -318,6 +318,12 @@ static const BitBoard kBishopAttacks[] = { 0x0028448201000000ULL, 0x0050880402010000ULL, 0x00A0100804020100ULL, 0x0040201008040201ULL}; +static const std::pair kRookDirections[] = { + {1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + +static const std::pair kBishopDirections[] = { + {1, 1}, {-1, 1}, {1, -1}, {-1, -1}}; + std::vector BuildMoveIndices() { std::vector res(4 * 64 * 64); for (size_t i = 0; i < sizeof(kIdxToMove) / sizeof(kIdxToMove[0]); ++i) { @@ -336,32 +342,182 @@ const int kQueenCastleIndex = static MagicBitBoards magic_bitboards; } // namespace -const BitBoard MagicBitBoards::kRookMagicNumbers[64]; -const BitBoard MagicBitBoards::kBishopMagicNumbers[64]; +const BitBoard MagicBitBoards::kRookMagicNumbers[64] = { + 0x088000102088C001ULL, 0x10C0200040001000ULL, 0x83001041000B2000ULL, + 0x0680280080041000ULL, 0x488004000A080080ULL, 0x0100180400010002ULL, + 0x040001C401021008ULL, 0x02000C04A980C302ULL, 0x0000800040082084ULL, + 0x5020C00820025000ULL, 0x0001002001044012ULL, 0x0402001020400A00ULL, + 0x00C0800800040080ULL, 0x4028800200040080ULL, 0x00A0804200802500ULL, + 0x8004800040802100ULL, 0x0080004000200040ULL, 0x1082810020400100ULL, + 0x0020004010080040ULL, 0x2004818010042800ULL, 0x0601010008005004ULL, + 0x4600808002001400ULL, 0x0010040009180210ULL, 0x020412000406C091ULL, + 0x040084228000C000ULL, 0x8000810100204000ULL, 0x0084110100402000ULL, + 0x0046001A00204210ULL, 0x2001040080080081ULL, 0x0144020080800400ULL, + 0x0840108400080229ULL, 0x0480308A0000410CULL, 0x0460324002800081ULL, + 0x620080A001804000ULL, 0x2800802000801006ULL, 0x0002809000800800ULL, + 0x4C09040080802800ULL, 0x4808800C00800200ULL, 0x0200311004001802ULL, + 0x0400008402002141ULL, 0x0410800140008020ULL, 0x000080C001050020ULL, + 0x004080204A020010ULL, 0x0224201001010038ULL, 0x0109001108010004ULL, + 0x0282004844020010ULL, 0x8228180110040082ULL, 0x0001000080C10002ULL, + 0x024000C120801080ULL, 0x0001406481060200ULL, 0x0101243200418600ULL, + 0x0108800800100080ULL, 0x4022080100100D00ULL, 0x0000843040600801ULL, + 0x8301000200CC0500ULL, 0x1000004500840200ULL, 0x1100104100800069ULL, + 0x2001008440001021ULL, 0x2002008830204082ULL, 0x0010145000082101ULL, + 0x01A2001004200842ULL, 0x1007000608040041ULL, 0x000A08100203028CULL, + 0x02D4048040290402ULL}; +const BitBoard MagicBitBoards::kBishopMagicNumbers[64] = { + 0x0008201802242020ULL, 0x0021040424806220ULL, 0x4006360602013080ULL, + 0x0004410020408002ULL, 0x2102021009001140ULL, 0x08C2021004000001ULL, + 0x6001031120200820ULL, 0x1018310402201410ULL, 0x401CE00210820484ULL, + 0x001029D001004100ULL, 0x2C00101080810032ULL, 0x0000082581000010ULL, + 0x10000A0210110020ULL, 0x200002016C202000ULL, 0x0201018821901000ULL, + 0x006A0300420A2100ULL, 0x0010014005450400ULL, 0x1008C12008028280ULL, + 0x00010010004A0040ULL, 0x3000820802044020ULL, 0x0000800405A02820ULL, + 0x8042004300420240ULL, 0x10060801210D2000ULL, 0x0210840500511061ULL, + 0x0008142118509020ULL, 0x0021109460040104ULL, 0x00A1480090019030ULL, + 0x0102008808008020ULL, 0x884084000880E001ULL, 0x040041020A030100ULL, + 0x3000810104110805ULL, 0x04040A2006808440ULL, 0x0044040404C01100ULL, + 0x4122B80800245004ULL, 0x0044020502380046ULL, 0x0100400888020200ULL, + 0x01C0002060020080ULL, 0x4008811100021001ULL, 0x8208450441040609ULL, + 0x0408004900008088ULL, 0x0294212051220882ULL, 0x000041080810E062ULL, + 0x10480A018E005000ULL, 0x80400A0204201600ULL, 0x2800200204100682ULL, + 0x0020200400204441ULL, 0x0A500600A5002400ULL, 0x801602004A010100ULL, + 0x0801841008040880ULL, 0x10010880C4200028ULL, 0x0400004424040000ULL, + 0x0401000142022100ULL, 0x00A00010020A0002ULL, 0x1010400204010810ULL, + 0x0829910400840000ULL, 0x0004235204010080ULL, 0x1002008143082000ULL, + 0x11840044440C2080ULL, 0x2802A02104030440ULL, 0x6100000900840401ULL, + 0x1C20A15A90420200ULL, 0x0088414004480280ULL, 0x0000204242881100ULL, + 0x0240080802809010ULL}; + +MagicBitBoards::MagicParams MagicBitBoards::rook_magic_params_[64]; +MagicBitBoards::MagicParams MagicBitBoards::bishop_magic_params_[64]; -MagicBitBoards::MagicParams MagicBitBoards::kRookMagicParams[64]; -MagicBitBoards::MagicParams MagicBitBoards::kBishopMagicParams[64]; +BitBoard MagicBitBoards::rook_attacks_table_[102400]; +BitBoard MagicBitBoards::bishop_attacks_table_[5248]; MagicBitBoards::MagicBitBoards() { - // Initialize for all board squares. - for (int square = 0; square < 64; square++) { + // Initialize masks for all board squares. + for (unsigned square = 0; square < 64; square++) { const BoardSquare b_sq(square); - // Calculate occupancy masks by subtracting the board edges from the total - // attack bitboards. - kRookMagicParams[square].mask_ = + // Calculate relevant occupancy masks by subtracting the board edges from + // the total attack bitboards. + rook_magic_params_[square].mask_ = kRookAttacks[square] - BoardSquare(b_sq.row(), 0) - BoardSquare(b_sq.row(), 7) - BoardSquare(0, b_sq.col()) - BoardSquare(7, b_sq.col()); - kBishopMagicParams[square].mask_ = + bishop_magic_params_[square].mask_ = kBishopAttacks[square] - BitBoard(0xFF818181818181FFULL); + } + + // Build attack tables. + BuildAttackTable(kRookMagicNumbers, rook_magic_params_, rook_attacks_table_, + kRookDirections); + BuildAttackTable(kBishopMagicNumbers, bishop_magic_params_, + bishop_attacks_table_, kBishopDirections); +} + +void MagicBitBoards::BuildAttackTable(const BitBoard* magic_numbers, + MagicParams* magic_params, + BitBoard* attacks_table, + const std::pair* directions) { + // Offset into lookup table. + uint32_t table_offset = 0; + + // Initialize for all board squares. + for (unsigned square = 0; square < 64; square++) { + const BoardSquare b_sq(square); + + // Cache the relevant occupancy board squares. + std::vector occupancy_squares; + + for (auto temp_sq : magic_params[square].mask_) { + occupancy_squares.emplace_back(temp_sq); + } + + // Set the number of shifted bits. + magic_params[square].shift_bits_ = 64 - occupancy_squares.size(); + + // Set the lookup table offset. + magic_params[square].table_offset_ = table_offset; + + // Clear the attack table. + for (int i = 0; i < (1 << occupancy_squares.size()); i++) { + attacks_table[table_offset + i] = 0; + } + + // Build square attack table for every possible masked occupancy bitboard. + for (int i = 0; i < (1 << occupancy_squares.size()); i++) { + BitBoard occupancy(0); + + for (size_t bit = 0; bit < occupancy_squares.size(); bit++) { + occupancy.set_if(occupancy_squares[bit], (1 << bit) & i); + } + + // Calculate attack bitboard corresponding to this occupancy bitboard. + BitBoard attack(0); + + for (int j = 0; j < 4; j++) { + auto direction = directions[j]; + auto dst_row = b_sq.row(); + auto dst_col = b_sq.col(); + while (true) { + dst_row += direction.first; + dst_col += direction.second; + if (!BoardSquare::IsValid(dst_row, dst_col)) break; + const BoardSquare destination(dst_row, dst_col); + attack.set(destination); + if (occupancy.get(destination)) break; + } + } + + // Calculate magic index. + uint64_t index = occupancy.as_int(); + index *= magic_numbers[square].as_int(); + index >>= magic_params[square].shift_bits_; + + // Update the table. + if (attacks_table[table_offset + index] != 0 && + attacks_table[table_offset + index] != attack) { + // Sanity check. + throw Exception("Invalid magic number!"); + } + + attacks_table[table_offset + index] = attack; + } - // Print the table index size. - std::cout << "Number of relevant occupancy bits = " - << kBishopMagicParams[square].mask_.count() << std::endl; + // Update table offset. + table_offset += (1 << occupancy_squares.size()); } } +inline BitBoard MagicBitBoards::GetRookAttacks(const BoardSquare rook_square, + const BitBoard pieces) const { + // Calculate magic index. + uint8_t square = rook_square.as_int(); + + uint64_t index = (pieces * rook_magic_params_[square].mask_).as_int(); + index *= kRookMagicNumbers[square].as_int(); + index >>= rook_magic_params_[square].shift_bits_; + + // Return attack bitboard. + return rook_attacks_table_[rook_magic_params_[square].table_offset_ + index]; +} + +inline BitBoard MagicBitBoards::GetBishopAttacks( + const BoardSquare bishop_square, const BitBoard pieces) const { + // Calculate magic index. + uint8_t square = bishop_square.as_int(); + + uint64_t index = (pieces * bishop_magic_params_[square].mask_).as_int(); + index *= kBishopMagicNumbers[square].as_int(); + index >>= bishop_magic_params_[square].shift_bits_; + + // Return attack bitboard. + return bishop_attacks_table_[bishop_magic_params_[square].table_offset_ + + index]; +} + Move::Move(const std::string& str, bool black) { if (str.size() < 4) throw Exception("Bad move: " + str); SetFrom(BoardSquare(str.substr(0, 2), black)); diff --git a/src/chess/bitboard.h b/src/chess/bitboard.h index b505787f28..74452385c3 100644 --- a/src/chess/bitboard.h +++ b/src/chess/bitboard.h @@ -179,6 +179,10 @@ class BitBoard { return board_ == other.board_; } + bool operator!=(const BitBoard& other) const { + return board_ != other.board_; + } + BitIterator begin() const { return board_; } BitIterator end() const { return 0; } @@ -237,29 +241,43 @@ class MagicBitBoards { public: MagicBitBoards(); + // Returns the rook attack bitboard for the given rook board square and the + // given occupied piece bitboard. + BitBoard GetRookAttacks(const BoardSquare rook_square, + const BitBoard pieces) const; + // Returns the bishop attack bitboard for the given rook board square and the + // given occupied piece bitboard. + BitBoard GetBishopAttacks(const BoardSquare bishop_square, + const BitBoard pieces) const; + private: - // Structure holding all relevant magic parameters per square (except magic - // number). + // Structure holding all relevant magic parameters per square (except + // magic number). struct MagicParams { // Offset into lookup table. uint32_t table_offset_; - // Number of index bits in the lookup table index. - uint8_t index_size_; + // Number of bits to shift. + uint8_t shift_bits_; // Relevant occupancy mask. BitBoard mask_; }; + // Builds rook or bishop attack table. + void BuildAttackTable(const BitBoard* kMagicNumbers, + MagicParams* magic_params, BitBoard* attacks_table, + const std::pair* directions); + // Magic numbers for each board square. static const BitBoard kRookMagicNumbers[]; static const BitBoard kBishopMagicNumbers[]; // Magic parameters for each board square. - static MagicParams kRookMagicParams[64]; - static MagicParams kBishopMagicParams[64]; + static MagicParams rook_magic_params_[]; + static MagicParams bishop_magic_params_[]; // Magic attack lookup tables. - static BitBoard kRookAttacksTable[102400]; - static BitBoard kBishopAttacksTable[5248]; + static BitBoard rook_attacks_table_[]; + static BitBoard bishop_attacks_table_[]; }; class Move { From d963790dd97f9437ee17fe1eaaf8a20b2d3abfcc Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sat, 29 Dec 2018 15:29:57 +0100 Subject: [PATCH 3/9] Use magic bitboards in move generation --- src/chess/bitboard.cc | 21 +++++++++++---------- src/chess/bitboard.h | 22 +++++++++++----------- src/chess/board.cc | 36 ++++++++++++------------------------ 3 files changed, 34 insertions(+), 45 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index e2ab38824c..5f47271e4e 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -403,11 +403,12 @@ MagicBitBoards::MagicBitBoards() { // Calculate relevant occupancy masks by subtracting the board edges from // the total attack bitboards. rook_magic_params_[square].mask_ = - kRookAttacks[square] - BoardSquare(b_sq.row(), 0) - - BoardSquare(b_sq.row(), 7) - BoardSquare(0, b_sq.col()) - - BoardSquare(7, b_sq.col()); + (kRookAttacks[square] - BoardSquare(b_sq.row(), 0) - + BoardSquare(b_sq.row(), 7) - BoardSquare(0, b_sq.col()) - + BoardSquare(7, b_sq.col())) + .as_int(); bishop_magic_params_[square].mask_ = - kBishopAttacks[square] - BitBoard(0xFF818181818181FFULL); + (kBishopAttacks[square] - BitBoard(0xFF818181818181FFULL)).as_int(); } // Build attack tables. @@ -431,7 +432,7 @@ void MagicBitBoards::BuildAttackTable(const BitBoard* magic_numbers, // Cache the relevant occupancy board squares. std::vector occupancy_squares; - for (auto temp_sq : magic_params[square].mask_) { + for (auto temp_sq : BitBoard(magic_params[square].mask_)) { occupancy_squares.emplace_back(temp_sq); } @@ -491,12 +492,12 @@ void MagicBitBoards::BuildAttackTable(const BitBoard* magic_numbers, } } -inline BitBoard MagicBitBoards::GetRookAttacks(const BoardSquare rook_square, - const BitBoard pieces) const { +BitBoard MagicBitBoards::GetRookAttacks(const BoardSquare rook_square, + const BitBoard pieces) { // Calculate magic index. uint8_t square = rook_square.as_int(); - uint64_t index = (pieces * rook_magic_params_[square].mask_).as_int(); + uint64_t index = pieces.as_int() & rook_magic_params_[square].mask_; index *= kRookMagicNumbers[square].as_int(); index >>= rook_magic_params_[square].shift_bits_; @@ -504,8 +505,8 @@ inline BitBoard MagicBitBoards::GetRookAttacks(const BoardSquare rook_square, return rook_attacks_table_[rook_magic_params_[square].table_offset_ + index]; } -inline BitBoard MagicBitBoards::GetBishopAttacks( - const BoardSquare bishop_square, const BitBoard pieces) const { +BitBoard MagicBitBoards::GetBishopAttacks(const BoardSquare bishop_square, + const BitBoard pieces) { // Calculate magic index. uint8_t square = bishop_square.as_int(); diff --git a/src/chess/bitboard.h b/src/chess/bitboard.h index 74452385c3..64241fde35 100644 --- a/src/chess/bitboard.h +++ b/src/chess/bitboard.h @@ -243,12 +243,12 @@ class MagicBitBoards { // Returns the rook attack bitboard for the given rook board square and the // given occupied piece bitboard. - BitBoard GetRookAttacks(const BoardSquare rook_square, - const BitBoard pieces) const; + static BitBoard GetRookAttacks(const BoardSquare rook_square, + const BitBoard pieces); // Returns the bishop attack bitboard for the given rook board square and the // given occupied piece bitboard. - BitBoard GetBishopAttacks(const BoardSquare bishop_square, - const BitBoard pieces) const; + static BitBoard GetBishopAttacks(const BoardSquare bishop_square, + const BitBoard pieces); private: // Structure holding all relevant magic parameters per square (except @@ -259,7 +259,7 @@ class MagicBitBoards { // Number of bits to shift. uint8_t shift_bits_; // Relevant occupancy mask. - BitBoard mask_; + uint64_t mask_; }; // Builds rook or bishop attack table. @@ -268,16 +268,16 @@ class MagicBitBoards { const std::pair* directions); // Magic numbers for each board square. - static const BitBoard kRookMagicNumbers[]; - static const BitBoard kBishopMagicNumbers[]; + static const BitBoard kRookMagicNumbers[64]; + static const BitBoard kBishopMagicNumbers[64]; // Magic parameters for each board square. - static MagicParams rook_magic_params_[]; - static MagicParams bishop_magic_params_[]; + static MagicParams rook_magic_params_[64]; + static MagicParams bishop_magic_params_[64]; // Magic attack lookup tables. - static BitBoard rook_attacks_table_[]; - static BitBoard bishop_attacks_table_[]; + static BitBoard rook_attacks_table_[102400]; + static BitBoard bishop_attacks_table_[5248]; }; class Move { diff --git a/src/chess/board.cc b/src/chess/board.cc index 4849146402..31e609be2f 100644 --- a/src/chess/board.cc +++ b/src/chess/board.cc @@ -250,35 +250,23 @@ MoveList ChessBoard::GeneratePseudolegalMoves() const { // Rook (and queen) if (rooks_.get(source)) { processed_piece = true; - for (const auto& direction : kRookDirections) { - auto dst_row = source.row(); - auto dst_col = source.col(); - while (true) { - dst_row += direction.first; - dst_col += direction.second; - if (!BoardSquare::IsValid(dst_row, dst_col)) break; - const BoardSquare destination(dst_row, dst_col); - if (our_pieces_.get(destination)) break; - result.emplace_back(source, destination); - if (their_pieces_.get(destination)) break; - } + BitBoard attacked = + MagicBitBoards::GetRookAttacks(source, our_pieces_ + their_pieces_) - + our_pieces_; + + for (const auto& destination : attacked) { + result.emplace_back(source, destination); } } // Bishop (and queen) if (bishops_.get(source)) { processed_piece = true; - for (const auto& direction : kBishopDirections) { - auto dst_row = source.row(); - auto dst_col = source.col(); - while (true) { - dst_row += direction.first; - dst_col += direction.second; - if (!BoardSquare::IsValid(dst_row, dst_col)) break; - const BoardSquare destination(dst_row, dst_col); - if (our_pieces_.get(destination)) break; - result.emplace_back(source, destination); - if (their_pieces_.get(destination)) break; - } + BitBoard attacked = MagicBitBoards::GetBishopAttacks( + source, our_pieces_ + their_pieces_) - + our_pieces_; + + for (const auto& destination : attacked) { + result.emplace_back(source, destination); } } if (processed_piece) continue; From c14d715088cc0adc7566053b598bd93504fce30a Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sat, 29 Dec 2018 18:52:54 +0100 Subject: [PATCH 4/9] Use magic bitboards in 'is under attack' detection --- src/chess/board.cc | 48 +++++++++++----------------------------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/src/chess/board.cc b/src/chess/board.cc index 31e609be2f..bc46855fe2 100644 --- a/src/chess/board.cc +++ b/src/chess/board.cc @@ -442,53 +442,27 @@ bool ChessBoard::ApplyMove(Move move) { bool ChessBoard::IsUnderAttack(BoardSquare square) const { const int row = square.row(); const int col = square.col(); - // Check king + // Check king. { const int krow = their_king_.row(); const int kcol = their_king_.col(); if (std::abs(krow - row) <= 1 && std::abs(kcol - col) <= 1) return true; } - // Check Rooks (and queen) - if (kRookAttacks[square.as_int()].intersects(their_pieces_ * rooks_)) { - for (const auto& direction : kRookDirections) { - auto dst_row = row; - auto dst_col = col; - while (true) { - dst_row += direction.first; - dst_col += direction.second; - if (!BoardSquare::IsValid(dst_row, dst_col)) break; - const BoardSquare destination(dst_row, dst_col); - if (our_pieces_.get(destination)) break; - if (their_pieces_.get(destination)) { - if (rooks_.get(destination)) return true; - break; - } - } - } + // Check rooks (and queens). + if (MagicBitBoards::GetRookAttacks(square, our_pieces_ + their_pieces_) + .intersects(their_pieces_ * rooks_)) { + return true; } - // Check Bishops - if (kBishopAttacks[square.as_int()].intersects(their_pieces_ * bishops_)) { - for (const auto& direction : kBishopDirections) { - auto dst_row = row; - auto dst_col = col; - while (true) { - dst_row += direction.first; - dst_col += direction.second; - if (!BoardSquare::IsValid(dst_row, dst_col)) break; - const BoardSquare destination(dst_row, dst_col); - if (our_pieces_.get(destination)) break; - if (their_pieces_.get(destination)) { - if (bishops_.get(destination)) return true; - break; - } - } - } + // Check bishops. + if (MagicBitBoards::GetBishopAttacks(square, our_pieces_ + their_pieces_) + .intersects(their_pieces_ * bishops_)) { + return true; } - // Check pawns + // Check pawns. if (kPawnAttacks[square.as_int()].intersects(their_pieces_ * pawns_)) { return true; } - // Check knights + // Check knights. { if (kKnightAttacks[square.as_int()].intersects(their_pieces_ - their_king_ - rooks_ - bishops_ - From 0b5310987f0c0726b95bc0812cd03cc38d3e6c76 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sat, 29 Dec 2018 19:12:51 +0100 Subject: [PATCH 5/9] Migrate functions to header files to allow inlining --- src/chess/bitboard.cc | 27 --------------------------- src/chess/bitboard.h | 43 +++++++++++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index 5f47271e4e..2c73b2a527 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -492,33 +492,6 @@ void MagicBitBoards::BuildAttackTable(const BitBoard* magic_numbers, } } -BitBoard MagicBitBoards::GetRookAttacks(const BoardSquare rook_square, - const BitBoard pieces) { - // Calculate magic index. - uint8_t square = rook_square.as_int(); - - uint64_t index = pieces.as_int() & rook_magic_params_[square].mask_; - index *= kRookMagicNumbers[square].as_int(); - index >>= rook_magic_params_[square].shift_bits_; - - // Return attack bitboard. - return rook_attacks_table_[rook_magic_params_[square].table_offset_ + index]; -} - -BitBoard MagicBitBoards::GetBishopAttacks(const BoardSquare bishop_square, - const BitBoard pieces) { - // Calculate magic index. - uint8_t square = bishop_square.as_int(); - - uint64_t index = (pieces * bishop_magic_params_[square].mask_).as_int(); - index *= kBishopMagicNumbers[square].as_int(); - index >>= bishop_magic_params_[square].shift_bits_; - - // Return attack bitboard. - return bishop_attacks_table_[bishop_magic_params_[square].table_offset_ + - index]; -} - Move::Move(const std::string& str, bool black) { if (str.size() < 4) throw Exception("Bad move: " + str); SetFrom(BoardSquare(str.substr(0, 2), black)); diff --git a/src/chess/bitboard.h b/src/chess/bitboard.h index 64241fde35..5507e0e2c3 100644 --- a/src/chess/bitboard.h +++ b/src/chess/bitboard.h @@ -241,16 +241,6 @@ class MagicBitBoards { public: MagicBitBoards(); - // Returns the rook attack bitboard for the given rook board square and the - // given occupied piece bitboard. - static BitBoard GetRookAttacks(const BoardSquare rook_square, - const BitBoard pieces); - // Returns the bishop attack bitboard for the given rook board square and the - // given occupied piece bitboard. - static BitBoard GetBishopAttacks(const BoardSquare bishop_square, - const BitBoard pieces); - - private: // Structure holding all relevant magic parameters per square (except // magic number). struct MagicParams { @@ -262,6 +252,39 @@ class MagicBitBoards { uint64_t mask_; }; + // Returns the rook attack bitboard for the given rook board square and the + // given occupied piece bitboard. + static BitBoard GetRookAttacks(const BoardSquare rook_square, + const BitBoard pieces) { + // Calculate magic index. + uint8_t square = rook_square.as_int(); + + uint64_t index = pieces.as_int() & rook_magic_params_[square].mask_; + index *= kRookMagicNumbers[square].as_int(); + index >>= rook_magic_params_[square].shift_bits_; + + // Return attack bitboard. + return rook_attacks_table_[rook_magic_params_[square].table_offset_ + + index]; + } + + // Returns the bishop attack bitboard for the given bishop board square and + // the given occupied piece bitboard. + static BitBoard GetBishopAttacks(const BoardSquare bishop_square, + const BitBoard pieces) { + // Calculate magic index. + uint8_t square = bishop_square.as_int(); + + uint64_t index = (pieces * bishop_magic_params_[square].mask_).as_int(); + index *= kBishopMagicNumbers[square].as_int(); + index >>= bishop_magic_params_[square].shift_bits_; + + // Return attack bitboard. + return bishop_attacks_table_[bishop_magic_params_[square].table_offset_ + + index]; + } + + private: // Builds rook or bishop attack table. void BuildAttackTable(const BitBoard* kMagicNumbers, MagicParams* magic_params, BitBoard* attacks_table, From c829414a46c44314641a937f9344351783de795a Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sat, 29 Dec 2018 19:15:21 +0100 Subject: [PATCH 6/9] Removed unused code --- src/chess/bitboard.cc | 1 + src/chess/board.cc | 54 ------------------------------------------- 2 files changed, 1 insertion(+), 54 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index 2c73b2a527..fa31758528 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -342,6 +342,7 @@ const int kQueenCastleIndex = static MagicBitBoards magic_bitboards; } // namespace +// Magic numbers determined via trial and error with random number generator. const BitBoard MagicBitBoards::kRookMagicNumbers[64] = { 0x088000102088C001ULL, 0x10C0200040001000ULL, 0x83001041000B2000ULL, 0x0680280080041000ULL, 0x488004000A080080ULL, 0x0100180400010002ULL, diff --git a/src/chess/board.cc b/src/chess/board.cc index bc46855fe2..09fe6fb730 100644 --- a/src/chess/board.cc +++ b/src/chess/board.cc @@ -66,64 +66,10 @@ static const BitBoard kPawnMask = 0x00FFFFFFFFFFFF00ULL; static const std::pair kKingMoves[] = { {-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}}; -static const std::pair kRookDirections[] = { - {1, 0}, {-1, 0}, {0, 1}, {0, -1}}; - -static const std::pair kBishopDirections[] = { - {1, 1}, {-1, 1}, {1, -1}, {-1, -1}}; - // If those squares are attacked, king cannot castle. static const int k00Attackers[] = {4, 5, 6}; static const int k000Attackers[] = {2, 3, 4}; -// Which squares can rook attack from every of squares. -static const BitBoard kRookAttacks[] = { - 0x01010101010101FEULL, 0x02020202020202FDULL, 0x04040404040404FBULL, - 0x08080808080808F7ULL, 0x10101010101010EFULL, 0x20202020202020DFULL, - 0x40404040404040BFULL, 0x808080808080807FULL, 0x010101010101FE01ULL, - 0x020202020202FD02ULL, 0x040404040404FB04ULL, 0x080808080808F708ULL, - 0x101010101010EF10ULL, 0x202020202020DF20ULL, 0x404040404040BF40ULL, - 0x8080808080807F80ULL, 0x0101010101FE0101ULL, 0x0202020202FD0202ULL, - 0x0404040404FB0404ULL, 0x0808080808F70808ULL, 0x1010101010EF1010ULL, - 0x2020202020DF2020ULL, 0x4040404040BF4040ULL, 0x80808080807F8080ULL, - 0x01010101FE010101ULL, 0x02020202FD020202ULL, 0x04040404FB040404ULL, - 0x08080808F7080808ULL, 0x10101010EF101010ULL, 0x20202020DF202020ULL, - 0x40404040BF404040ULL, 0x808080807F808080ULL, 0x010101FE01010101ULL, - 0x020202FD02020202ULL, 0x040404FB04040404ULL, 0x080808F708080808ULL, - 0x101010EF10101010ULL, 0x202020DF20202020ULL, 0x404040BF40404040ULL, - 0x8080807F80808080ULL, 0x0101FE0101010101ULL, 0x0202FD0202020202ULL, - 0x0404FB0404040404ULL, 0x0808F70808080808ULL, 0x1010EF1010101010ULL, - 0x2020DF2020202020ULL, 0x4040BF4040404040ULL, 0x80807F8080808080ULL, - 0x01FE010101010101ULL, 0x02FD020202020202ULL, 0x04FB040404040404ULL, - 0x08F7080808080808ULL, 0x10EF101010101010ULL, 0x20DF202020202020ULL, - 0x40BF404040404040ULL, 0x807F808080808080ULL, 0xFE01010101010101ULL, - 0xFD02020202020202ULL, 0xFB04040404040404ULL, 0xF708080808080808ULL, - 0xEF10101010101010ULL, 0xDF20202020202020ULL, 0xBF40404040404040ULL, - 0x7F80808080808080ULL}; -// Which squares can bishop attack. -static const BitBoard kBishopAttacks[] = { - 0x8040201008040200ULL, 0x0080402010080500ULL, 0x0000804020110A00ULL, - 0x0000008041221400ULL, 0x0000000182442800ULL, 0x0000010204885000ULL, - 0x000102040810A000ULL, 0x0102040810204000ULL, 0x4020100804020002ULL, - 0x8040201008050005ULL, 0x00804020110A000AULL, 0x0000804122140014ULL, - 0x0000018244280028ULL, 0x0001020488500050ULL, 0x0102040810A000A0ULL, - 0x0204081020400040ULL, 0x2010080402000204ULL, 0x4020100805000508ULL, - 0x804020110A000A11ULL, 0x0080412214001422ULL, 0x0001824428002844ULL, - 0x0102048850005088ULL, 0x02040810A000A010ULL, 0x0408102040004020ULL, - 0x1008040200020408ULL, 0x2010080500050810ULL, 0x4020110A000A1120ULL, - 0x8041221400142241ULL, 0x0182442800284482ULL, 0x0204885000508804ULL, - 0x040810A000A01008ULL, 0x0810204000402010ULL, 0x0804020002040810ULL, - 0x1008050005081020ULL, 0x20110A000A112040ULL, 0x4122140014224180ULL, - 0x8244280028448201ULL, 0x0488500050880402ULL, 0x0810A000A0100804ULL, - 0x1020400040201008ULL, 0x0402000204081020ULL, 0x0805000508102040ULL, - 0x110A000A11204080ULL, 0x2214001422418000ULL, 0x4428002844820100ULL, - 0x8850005088040201ULL, 0x10A000A010080402ULL, 0x2040004020100804ULL, - 0x0200020408102040ULL, 0x0500050810204080ULL, 0x0A000A1120408000ULL, - 0x1400142241800000ULL, 0x2800284482010000ULL, 0x5000508804020100ULL, - 0xA000A01008040201ULL, 0x4000402010080402ULL, 0x0002040810204080ULL, - 0x0005081020408000ULL, 0x000A112040800000ULL, 0x0014224180000000ULL, - 0x0028448201000000ULL, 0x0050880402010000ULL, 0x00A0100804020100ULL, - 0x0040201008040201ULL}; // Which squares can knight attack. static const BitBoard kKnightAttacks[] = { 0x0000000000020400ULL, 0x0000000000050800ULL, 0x00000000000A1100ULL, From 129cc62f3290f69f6d082a85ac8c9f30697e3e8f Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sun, 30 Dec 2018 12:03:09 +0100 Subject: [PATCH 7/9] Refactoring + comment updates --- src/chess/bitboard.cc | 28 ++++++++++++++-------------- src/chess/bitboard.h | 14 +++++++------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index fa31758528..8a4c6bd309 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -413,16 +413,16 @@ MagicBitBoards::MagicBitBoards() { } // Build attack tables. - BuildAttackTable(kRookMagicNumbers, rook_magic_params_, rook_attacks_table_, - kRookDirections); - BuildAttackTable(kBishopMagicNumbers, bishop_magic_params_, - bishop_attacks_table_, kBishopDirections); + BuildAttacksTable(kRookMagicNumbers, rook_magic_params_, rook_attacks_table_, + kRookDirections); + BuildAttacksTable(kBishopMagicNumbers, bishop_magic_params_, + bishop_attacks_table_, kBishopDirections); } -void MagicBitBoards::BuildAttackTable(const BitBoard* magic_numbers, - MagicParams* magic_params, - BitBoard* attacks_table, - const std::pair* directions) { +void MagicBitBoards::BuildAttacksTable(const BitBoard* magic_numbers, + MagicParams* magic_params, + BitBoard* attacks_table, + const std::pair* directions) { // Offset into lookup table. uint32_t table_offset = 0; @@ -430,20 +430,20 @@ void MagicBitBoards::BuildAttackTable(const BitBoard* magic_numbers, for (unsigned square = 0; square < 64; square++) { const BoardSquare b_sq(square); - // Cache the relevant occupancy board squares. + // Cache relevant occupancy board squares. std::vector occupancy_squares; for (auto temp_sq : BitBoard(magic_params[square].mask_)) { occupancy_squares.emplace_back(temp_sq); } - // Set the number of shifted bits. + // Set number of shifted bits. magic_params[square].shift_bits_ = 64 - occupancy_squares.size(); - // Set the lookup table offset. + // Set lookup table offset. magic_params[square].table_offset_ = table_offset; - // Clear the attack table. + // Clear attack table. for (int i = 0; i < (1 << occupancy_squares.size()); i++) { attacks_table[table_offset + i] = 0; } @@ -478,13 +478,13 @@ void MagicBitBoards::BuildAttackTable(const BitBoard* magic_numbers, index *= magic_numbers[square].as_int(); index >>= magic_params[square].shift_bits_; - // Update the table. + // Sanity check. if (attacks_table[table_offset + index] != 0 && attacks_table[table_offset + index] != attack) { - // Sanity check. throw Exception("Invalid magic number!"); } + // Update table. attacks_table[table_offset + index] = attack; } diff --git a/src/chess/bitboard.h b/src/chess/bitboard.h index 5507e0e2c3..8a85fa1107 100644 --- a/src/chess/bitboard.h +++ b/src/chess/bitboard.h @@ -36,8 +36,6 @@ namespace lczero { -class MagicBitBoards; - // Stores a coordinates of a single square. class BoardSquare { public: @@ -239,6 +237,8 @@ class BitBoard { // We use so-called "fancy" magic bitboards. class MagicBitBoards { public: + // Constructor is called once in bitboard.cc to initialize structures, should + // not be called again! MagicBitBoards(); // Structure holding all relevant magic parameters per square (except @@ -285,10 +285,10 @@ class MagicBitBoards { } private: - // Builds rook or bishop attack table. - void BuildAttackTable(const BitBoard* kMagicNumbers, - MagicParams* magic_params, BitBoard* attacks_table, - const std::pair* directions); + // Builds rook or bishop attacks table. + void BuildAttacksTable(const BitBoard* kMagicNumbers, + MagicParams* magic_params, BitBoard* attacks_table, + const std::pair* directions); // Magic numbers for each board square. static const BitBoard kRookMagicNumbers[64]; @@ -298,7 +298,7 @@ class MagicBitBoards { static MagicParams rook_magic_params_[64]; static MagicParams bishop_magic_params_[64]; - // Magic attack lookup tables. + // Attack bitboards lookup tables. static BitBoard rook_attacks_table_[102400]; static BitBoard bishop_attacks_table_[5248]; }; From 1f99f077e70ef424334c0a02eb182cd05229763f Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sun, 30 Dec 2018 12:43:29 +0100 Subject: [PATCH 8/9] Cache magic number in magic parameters struct to improve memory locality --- src/chess/bitboard.cc | 3 +++ src/chess/bitboard.h | 15 ++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index 8a4c6bd309..52b7c2bf12 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -437,6 +437,9 @@ void MagicBitBoards::BuildAttacksTable(const BitBoard* magic_numbers, occupancy_squares.emplace_back(temp_sq); } + // Set magic number. + magic_params[square].magic_number_ = magic_numbers[square].as_int(); + // Set number of shifted bits. magic_params[square].shift_bits_ = 64 - occupancy_squares.size(); diff --git a/src/chess/bitboard.h b/src/chess/bitboard.h index 8a85fa1107..3dfbc9078b 100644 --- a/src/chess/bitboard.h +++ b/src/chess/bitboard.h @@ -241,15 +241,16 @@ class MagicBitBoards { // not be called again! MagicBitBoards(); - // Structure holding all relevant magic parameters per square (except - // magic number). + // Structure holding all relevant magic parameters per square. struct MagicParams { + // Relevant occupancy mask. + uint64_t mask_; + // Magic number. + uint64_t magic_number_; // Offset into lookup table. uint32_t table_offset_; // Number of bits to shift. uint8_t shift_bits_; - // Relevant occupancy mask. - uint64_t mask_; }; // Returns the rook attack bitboard for the given rook board square and the @@ -260,7 +261,7 @@ class MagicBitBoards { uint8_t square = rook_square.as_int(); uint64_t index = pieces.as_int() & rook_magic_params_[square].mask_; - index *= kRookMagicNumbers[square].as_int(); + index *= rook_magic_params_[square].magic_number_; index >>= rook_magic_params_[square].shift_bits_; // Return attack bitboard. @@ -275,8 +276,8 @@ class MagicBitBoards { // Calculate magic index. uint8_t square = bishop_square.as_int(); - uint64_t index = (pieces * bishop_magic_params_[square].mask_).as_int(); - index *= kBishopMagicNumbers[square].as_int(); + uint64_t index = pieces.as_int() & bishop_magic_params_[square].mask_; + index *= bishop_magic_params_[square].magic_number_; index >>= bishop_magic_params_[square].shift_bits_; // Return attack bitboard. From 4f76d72aed6a6087b3a78ed657122c2105e8a920 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sun, 30 Dec 2018 13:12:00 +0100 Subject: [PATCH 9/9] Comment updates + minor changes --- src/chess/bitboard.cc | 31 ++++++++++++++++++------------- src/chess/bitboard.h | 22 +++++++++++----------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index 52b7c2bf12..7f470a1efa 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -402,7 +402,7 @@ MagicBitBoards::MagicBitBoards() { const BoardSquare b_sq(square); // Calculate relevant occupancy masks by subtracting the board edges from - // the total attack bitboards. + // the total attacks bitboards. rook_magic_params_[square].mask_ = (kRookAttacks[square] - BoardSquare(b_sq.row(), 0) - BoardSquare(b_sq.row(), 7) - BoardSquare(0, b_sq.col()) - @@ -412,7 +412,7 @@ MagicBitBoards::MagicBitBoards() { (kBishopAttacks[square] - BitBoard(0xFF818181818181FFULL)).as_int(); } - // Build attack tables. + // Build attacks tables. BuildAttacksTable(kRookMagicNumbers, rook_magic_params_, rook_attacks_table_, kRookDirections); BuildAttacksTable(kBishopMagicNumbers, bishop_magic_params_, @@ -433,25 +433,27 @@ void MagicBitBoards::BuildAttacksTable(const BitBoard* magic_numbers, // Cache relevant occupancy board squares. std::vector occupancy_squares; - for (auto temp_sq : BitBoard(magic_params[square].mask_)) { - occupancy_squares.emplace_back(temp_sq); + for (auto occ_sq : BitBoard(magic_params[square].mask_)) { + occupancy_squares.emplace_back(occ_sq); } // Set magic number. magic_params[square].magic_number_ = magic_numbers[square].as_int(); - // Set number of shifted bits. + // Set number of shifted bits. The magic numbers have been chosen such that + // the number of relevant occupancy bits suffice to index the attacks table. magic_params[square].shift_bits_ = 64 - occupancy_squares.size(); // Set lookup table offset. magic_params[square].table_offset_ = table_offset; - // Clear attack table. + // Clear attacks table (used for sanity check later on). for (int i = 0; i < (1 << occupancy_squares.size()); i++) { attacks_table[table_offset + i] = 0; } - // Build square attack table for every possible masked occupancy bitboard. + // Build square attacks table for every possible relevant occupancy + // bitboard. for (int i = 0; i < (1 << occupancy_squares.size()); i++) { BitBoard occupancy(0); @@ -459,8 +461,8 @@ void MagicBitBoards::BuildAttacksTable(const BitBoard* magic_numbers, occupancy.set_if(occupancy_squares[bit], (1 << bit) & i); } - // Calculate attack bitboard corresponding to this occupancy bitboard. - BitBoard attack(0); + // Calculate attacks bitboard corresponding to this occupancy bitboard. + BitBoard attacks(0); for (int j = 0; j < 4; j++) { auto direction = directions[j]; @@ -471,7 +473,7 @@ void MagicBitBoards::BuildAttacksTable(const BitBoard* magic_numbers, dst_col += direction.second; if (!BoardSquare::IsValid(dst_row, dst_col)) break; const BoardSquare destination(dst_row, dst_col); - attack.set(destination); + attacks.set(destination); if (occupancy.get(destination)) break; } } @@ -481,14 +483,17 @@ void MagicBitBoards::BuildAttacksTable(const BitBoard* magic_numbers, index *= magic_numbers[square].as_int(); index >>= magic_params[square].shift_bits_; - // Sanity check. + // Sanity check. The magic numbers have been chosen such that + // the number of relevant occupancy bits suffice to index the attacks + // table. If the table already contains an attacks bitboard, possible + // collisions should be constructive. if (attacks_table[table_offset + index] != 0 && - attacks_table[table_offset + index] != attack) { + attacks_table[table_offset + index] != attacks) { throw Exception("Invalid magic number!"); } // Update table. - attacks_table[table_offset + index] = attack; + attacks_table[table_offset + index] = attacks; } // Update table offset. diff --git a/src/chess/bitboard.h b/src/chess/bitboard.h index 3dfbc9078b..be97162056 100644 --- a/src/chess/bitboard.h +++ b/src/chess/bitboard.h @@ -253,36 +253,36 @@ class MagicBitBoards { uint8_t shift_bits_; }; - // Returns the rook attack bitboard for the given rook board square and the + // Returns the rook attacks bitboard for the given rook board square and the // given occupied piece bitboard. static BitBoard GetRookAttacks(const BoardSquare rook_square, const BitBoard pieces) { // Calculate magic index. - uint8_t square = rook_square.as_int(); + const uint8_t square = rook_square.as_int(); uint64_t index = pieces.as_int() & rook_magic_params_[square].mask_; index *= rook_magic_params_[square].magic_number_; index >>= rook_magic_params_[square].shift_bits_; + index += rook_magic_params_[square].table_offset_; - // Return attack bitboard. - return rook_attacks_table_[rook_magic_params_[square].table_offset_ + - index]; + // Return attacks bitboard. + return rook_attacks_table_[index]; } - // Returns the bishop attack bitboard for the given bishop board square and + // Returns the bishop attacks bitboard for the given bishop board square and // the given occupied piece bitboard. static BitBoard GetBishopAttacks(const BoardSquare bishop_square, const BitBoard pieces) { // Calculate magic index. - uint8_t square = bishop_square.as_int(); + const uint8_t square = bishop_square.as_int(); uint64_t index = pieces.as_int() & bishop_magic_params_[square].mask_; index *= bishop_magic_params_[square].magic_number_; index >>= bishop_magic_params_[square].shift_bits_; + index += bishop_magic_params_[square].table_offset_; - // Return attack bitboard. - return bishop_attacks_table_[bishop_magic_params_[square].table_offset_ + - index]; + // Return attacks bitboard. + return bishop_attacks_table_[index]; } private: @@ -299,7 +299,7 @@ class MagicBitBoards { static MagicParams rook_magic_params_[64]; static MagicParams bishop_magic_params_[64]; - // Attack bitboards lookup tables. + // Attacks bitboards lookup tables. static BitBoard rook_attacks_table_[102400]; static BitBoard bishop_attacks_table_[5248]; };