From 691c42812901bfc7e8ee777c6eaf210bbfe54e62 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Tue, 1 Jan 2019 11:55:02 +0100 Subject: [PATCH 01/16] Movegen speedup via magic bitboards --- src/chess/bitboard.cc | 219 ++++++++++++++++++++++++++++++++++++++++++ src/chess/bitboard.h | 75 +++++++++++++++ src/chess/board.cc | 78 ++++----------- 3 files changed, 314 insertions(+), 58 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index bbee6bdaa9..7f470a1efa 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,61 @@ 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}; + +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) { @@ -280,8 +337,170 @@ 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 +// Magic numbers determined via trial and error with random number generator. +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]; + +BitBoard MagicBitBoards::rook_attacks_table_[102400]; +BitBoard MagicBitBoards::bishop_attacks_table_[5248]; + +MagicBitBoards::MagicBitBoards() { + // Initialize masks for all board squares. + for (unsigned square = 0; square < 64; square++) { + const BoardSquare b_sq(square); + + // Calculate relevant occupancy masks by subtracting the board edges from + // 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()) - + BoardSquare(7, b_sq.col())) + .as_int(); + bishop_magic_params_[square].mask_ = + (kBishopAttacks[square] - BitBoard(0xFF818181818181FFULL)).as_int(); + } + + // Build attacks tables. + BuildAttacksTable(kRookMagicNumbers, rook_magic_params_, rook_attacks_table_, + kRookDirections); + BuildAttacksTable(kBishopMagicNumbers, bishop_magic_params_, + bishop_attacks_table_, kBishopDirections); +} + +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; + + // Initialize for all board squares. + for (unsigned square = 0; square < 64; square++) { + const BoardSquare b_sq(square); + + // Cache relevant occupancy board squares. + std::vector occupancy_squares; + + 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. 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 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 attacks table for every possible relevant 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 attacks bitboard corresponding to this occupancy bitboard. + BitBoard attacks(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); + attacks.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_; + + // 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] != attacks) { + throw Exception("Invalid magic number!"); + } + + // Update table. + attacks_table[table_offset + index] = attacks; + } + + // Update table offset. + table_offset += (1 << occupancy_squares.size()); + } +} + 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..be97162056 100644 --- a/src/chess/bitboard.h +++ b/src/chess/bitboard.h @@ -177,6 +177,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; } @@ -229,6 +233,77 @@ class BitBoard { std::uint64_t board_ = 0; }; +// Holds magic bitboard routines and performs initialization. +// 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. + 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_; + }; + + // 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. + 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 attacks bitboard. + return rook_attacks_table_[index]; + } + + // 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. + 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 attacks bitboard. + return bishop_attacks_table_[index]; + } + + private: + // 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]; + static const BitBoard kBishopMagicNumbers[64]; + + // Magic parameters for each board square. + static MagicParams rook_magic_params_[64]; + static MagicParams bishop_magic_params_[64]; + + // Attacks bitboards lookup tables. + static BitBoard rook_attacks_table_[102400]; + static BitBoard bishop_attacks_table_[5248]; +}; + class Move { public: enum class Promotion : std::uint8_t { None, Queen, Rook, Bishop, Knight }; diff --git a/src/chess/board.cc b/src/chess/board.cc index 4849146402..965a5d41a8 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; @@ -460,41 +448,15 @@ bool ChessBoard::IsUnderAttack(BoardSquare square) const { 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 if (kPawnAttacks[square.as_int()].intersects(their_pieces_ * pawns_)) { From ab779cf34aebcc93b03e8a73fa801b6108978604 Mon Sep 17 00:00:00 2001 From: borg323 Date: Sun, 6 Jan 2019 03:34:07 +0200 Subject: [PATCH 02/16] build system support for PEXT instruction --- meson.build | 4 ++++ meson_options.txt | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/meson.build b/meson.build index 8881e0eaab..160e2d2b74 100644 --- a/meson.build +++ b/meson.build @@ -436,6 +436,10 @@ if not get_option('popcnt') add_project_arguments('-DNO_POPCNT', language : 'cpp') endif +if not get_option('pext') + add_project_arguments('-DNO_PEXT', language : 'cpp') +endif + executable('lc0', 'src/main.cc', files, include_directories: includes, dependencies: deps, install: true) diff --git a/meson_options.txt b/meson_options.txt index 04993f626f..8116f6e36e 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -113,6 +113,11 @@ option('popcnt', value: true, description: 'Use the popcnt instruction') +option('pext', + type: 'boolean', + value: false, + description: 'Use the pext instruction') + option('gtest', type: 'boolean', value: true, From 6f86c24ebaf11d858295ab8b092394bdc495ff82 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sun, 6 Jan 2019 09:21:04 +0100 Subject: [PATCH 03/16] Removed dependency from legacy rook/bishop attack tables in initialization code --- src/chess/bitboard.cc | 88 ++++++++++++------------------------------- 1 file changed, 24 insertions(+), 64 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index 7f470a1efa..fb0837b45e 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -269,55 +269,6 @@ 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}; - static const std::pair kRookDirections[] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1}}; @@ -397,21 +348,6 @@ BitBoard MagicBitBoards::rook_attacks_table_[102400]; BitBoard MagicBitBoards::bishop_attacks_table_[5248]; MagicBitBoards::MagicBitBoards() { - // Initialize masks for all board squares. - for (unsigned square = 0; square < 64; square++) { - const BoardSquare b_sq(square); - - // Calculate relevant occupancy masks by subtracting the board edges from - // 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()) - - BoardSquare(7, b_sq.col())) - .as_int(); - bishop_magic_params_[square].mask_ = - (kBishopAttacks[square] - BitBoard(0xFF818181818181FFULL)).as_int(); - } - // Build attacks tables. BuildAttacksTable(kRookMagicNumbers, rook_magic_params_, rook_attacks_table_, kRookDirections); @@ -430,6 +366,30 @@ void MagicBitBoards::BuildAttacksTable(const BitBoard* magic_numbers, for (unsigned square = 0; square < 64; square++) { const BoardSquare b_sq(square); + // Calculate relevant occupancy masks by subtracting the board edges from + // the total attacks bitboards. + BitBoard mask = {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 the next square in this direction is invalid, the current square + // is at the board's edge and should not be added. + if (!BoardSquare::IsValid(dst_row + direction.first, + dst_col + direction.second)) + break; + const BoardSquare destination(dst_row, dst_col); + mask.set(destination); + } + } + + // Set mask. + magic_params[square].mask_ = mask.as_int(); + // Cache relevant occupancy board squares. std::vector occupancy_squares; From d246bc3d4ab0d20a21318f53325487a521005583 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sun, 6 Jan 2019 09:29:48 +0100 Subject: [PATCH 04/16] Optimization: use pointer to square attacks table instead of offset to save some instructions --- src/chess/bitboard.cc | 4 ++-- src/chess/bitboard.h | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index fb0837b45e..adab9b3503 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -404,8 +404,8 @@ void MagicBitBoards::BuildAttacksTable(const BitBoard* magic_numbers, // 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; + // Set pointer to lookup table. + magic_params[square].attacks_table_ = &attacks_table[table_offset]; // Clear attacks table (used for sanity check later on). for (int i = 0; i < (1 << occupancy_squares.size()); i++) { diff --git a/src/chess/bitboard.h b/src/chess/bitboard.h index be97162056..1284a9569e 100644 --- a/src/chess/bitboard.h +++ b/src/chess/bitboard.h @@ -247,8 +247,8 @@ class MagicBitBoards { uint64_t mask_; // Magic number. uint64_t magic_number_; - // Offset into lookup table. - uint32_t table_offset_; + // Pointer to lookup table. + BitBoard* attacks_table_; // Number of bits to shift. uint8_t shift_bits_; }; @@ -263,10 +263,9 @@ class MagicBitBoards { 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 attacks bitboard. - return rook_attacks_table_[index]; + return rook_magic_params_[square].attacks_table_[index]; } // Returns the bishop attacks bitboard for the given bishop board square and @@ -279,10 +278,9 @@ class MagicBitBoards { 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 attacks bitboard. - return bishop_attacks_table_[index]; + return bishop_magic_params_[square].attacks_table_[index]; } private: From 94684d90fbf51ca244f5055e911d246644bfd0ec Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sun, 6 Jan 2019 09:35:27 +0100 Subject: [PATCH 05/16] Consistently use term 'attack line' everywhere --- src/chess/board.cc | 20 ++++++++++---------- src/chess/board.h | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/chess/board.cc b/src/chess/board.cc index 9be4a4f932..567ecaa68d 100644 --- a/src/chess/board.cc +++ b/src/chess/board.cc @@ -514,9 +514,9 @@ KingAttackInfo ChessBoard::GenerateKingAttackInfo() const { // Store the pinned piece. king_attack_info.pinned_pieces_.set(possible_pinned_piece); } else { - // Update attacking lines. - king_attack_info.attacking_lines_ = - king_attack_info.attacking_lines_ + attack_line; + // Update attack lines. + king_attack_info.attack_lines_ = + king_attack_info.attack_lines_ + attack_line; num_king_attackers++; } } @@ -557,9 +557,9 @@ KingAttackInfo ChessBoard::GenerateKingAttackInfo() const { // Store the pinned piece. king_attack_info.pinned_pieces_.set(possible_pinned_piece); } else { - // Update attacking lines. - king_attack_info.attacking_lines_ = - king_attack_info.attacking_lines_ + attack_line; + // Update attack lines. + king_attack_info.attack_lines_ = + king_attack_info.attack_lines_ + attack_line; num_king_attackers++; } } @@ -571,8 +571,8 @@ KingAttackInfo ChessBoard::GenerateKingAttackInfo() const { // Check pawns. const BitBoard attacking_pawns = kPawnAttacks[our_king_.as_int()] * their_pieces_ * pawns_; - king_attack_info.attacking_lines_ = - king_attack_info.attacking_lines_ + attacking_pawns; + king_attack_info.attack_lines_ = + king_attack_info.attack_lines_ + attacking_pawns; if (attacking_pawns.as_int()) { // No more than one pawn can give check. @@ -583,8 +583,8 @@ KingAttackInfo ChessBoard::GenerateKingAttackInfo() const { const BitBoard attacking_knights = kKnightAttacks[our_king_.as_int()] * (their_pieces_ - their_king_ - rooks_ - bishops_ - (pawns_ * kPawnMask)); - king_attack_info.attacking_lines_ = - king_attack_info.attacking_lines_ + attacking_knights; + king_attack_info.attack_lines_ = + king_attack_info.attack_lines_ + attacking_knights; if (attacking_knights.as_int()) { // No more than one knight can give check. diff --git a/src/chess/board.h b/src/chess/board.h index 9188c5f6f3..c2431a1fae 100644 --- a/src/chess/board.h +++ b/src/chess/board.h @@ -36,18 +36,18 @@ namespace lczero { // Represents king attack info used during legal move detection. class KingAttackInfo { public: - bool in_check() const { return attacking_lines_.as_int(); } + bool in_check() const { return attack_lines_.as_int(); } bool in_double_check() const { return double_check_; } bool is_pinned(const BoardSquare square) const { return pinned_pieces_.get(square); } bool is_on_attack_line(const BoardSquare square) const { - return attacking_lines_.get(square); + return attack_lines_.get(square); } bool double_check_ = 0; BitBoard pinned_pieces_ = {0}; - BitBoard attacking_lines_ = {0}; + BitBoard attack_lines_ = {0}; }; // Represents a board position. From 8dfdb9c3bb1072848f9da743aa95045e60ac3b8a Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sun, 6 Jan 2019 10:06:56 +0100 Subject: [PATCH 06/16] Add PEXT support path --- src/chess/bitboard.cc | 32 ++++++++++++++++++++++---------- src/chess/bitboard.h | 24 ++++++++++++++++++++---- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index adab9b3503..afb596e332 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -348,15 +348,23 @@ BitBoard MagicBitBoards::rook_attacks_table_[102400]; BitBoard MagicBitBoards::bishop_attacks_table_[5248]; MagicBitBoards::MagicBitBoards() { +#if defined(NO_PEXT) + // Set magic numbers for all board squares. + for (unsigned square = 0; square < 64; square++) { + rook_magic_params_[square].magic_number_ = + kRookMagicNumbers[square].as_int(); + bishop_magic_params_[square].magic_number_ = + kBishopMagicNumbers[square].as_int(); + } +#endif + // Build attacks tables. - BuildAttacksTable(kRookMagicNumbers, rook_magic_params_, rook_attacks_table_, - kRookDirections); - BuildAttacksTable(kBishopMagicNumbers, bishop_magic_params_, - bishop_attacks_table_, kBishopDirections); + BuildAttacksTable(rook_magic_params_, rook_attacks_table_, kRookDirections); + BuildAttacksTable(bishop_magic_params_, bishop_attacks_table_, + kBishopDirections); } -void MagicBitBoards::BuildAttacksTable(const BitBoard* magic_numbers, - MagicParams* magic_params, +void MagicBitBoards::BuildAttacksTable(MagicParams* magic_params, BitBoard* attacks_table, const std::pair* directions) { // Offset into lookup table. @@ -397,12 +405,11 @@ void MagicBitBoards::BuildAttacksTable(const BitBoard* magic_numbers, occupancy_squares.emplace_back(occ_sq); } - // Set magic number. - magic_params[square].magic_number_ = magic_numbers[square].as_int(); - +#if defined(NO_PEXT) // 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(); +#endif // Set pointer to lookup table. magic_params[square].attacks_table_ = &attacks_table[table_offset]; @@ -438,9 +445,10 @@ void MagicBitBoards::BuildAttacksTable(const BitBoard* magic_numbers, } } +#if defined(NO_PEXT) // Calculate magic index. uint64_t index = occupancy.as_int(); - index *= magic_numbers[square].as_int(); + index *= magic_params[square].magic_number_; index >>= magic_params[square].shift_bits_; // Sanity check. The magic numbers have been chosen such that @@ -451,6 +459,10 @@ void MagicBitBoards::BuildAttacksTable(const BitBoard* magic_numbers, attacks_table[table_offset + index] != attacks) { throw Exception("Invalid magic number!"); } +#else + uint64_t index = + _pext_u64(occupancy.as_int(), magic_params[square].mask_); +#endif // Update table. attacks_table[table_offset + index] = attacks; diff --git a/src/chess/bitboard.h b/src/chess/bitboard.h index 1284a9569e..34ecf95c40 100644 --- a/src/chess/bitboard.h +++ b/src/chess/bitboard.h @@ -32,6 +32,11 @@ #include #include +#if not defined(NO_PEXT) +// Include header for pext instruction. +#include +#endif + #include "utils/bititer.h" namespace lczero { @@ -245,12 +250,14 @@ class MagicBitBoards { struct MagicParams { // Relevant occupancy mask. uint64_t mask_; - // Magic number. - uint64_t magic_number_; // Pointer to lookup table. BitBoard* attacks_table_; +#if defined(NO_PEXT) + // Magic number. + uint64_t magic_number_; // Number of bits to shift. uint8_t shift_bits_; +#endif }; // Returns the rook attacks bitboard for the given rook board square and the @@ -260,9 +267,14 @@ class MagicBitBoards { // Calculate magic index. const uint8_t square = rook_square.as_int(); +#if defined(NO_PEXT) 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_; +#else + uint64_t index = + _pext_u64(pieces.as_int(), rook_magic_params_[square].mask_); +#endif // Return attacks bitboard. return rook_magic_params_[square].attacks_table_[index]; @@ -275,9 +287,14 @@ class MagicBitBoards { // Calculate magic index. const uint8_t square = bishop_square.as_int(); +#if defined(NO_PEXT) 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_; +#else + uint64_t index = + _pext_u64(pieces.as_int(), bishop_magic_params_[square].mask_); +#endif // Return attacks bitboard. return bishop_magic_params_[square].attacks_table_[index]; @@ -285,8 +302,7 @@ class MagicBitBoards { private: // Builds rook or bishop attacks table. - void BuildAttacksTable(const BitBoard* kMagicNumbers, - MagicParams* magic_params, BitBoard* attacks_table, + void BuildAttacksTable(MagicParams* magic_params, BitBoard* attacks_table, const std::pair* directions); // Magic numbers for each board square. From b28f4930ae45fa09adde1b917def44cd623728db Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sun, 6 Jan 2019 10:27:29 +0100 Subject: [PATCH 07/16] Comment improvement --- src/chess/bitboard.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index afb596e332..ed609bb4a1 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -293,7 +293,9 @@ const int kQueenCastleIndex = static MagicBitBoards magic_bitboards; } // namespace -// Magic numbers determined via trial and error with random number generator. +// Magic numbers determined via trial and error with random number generator +// such that the number of relevant occupancy bits suffice to index the attacks +// tables without non-constructive collisions. const BitBoard MagicBitBoards::kRookMagicNumbers[64] = { 0x088000102088C001ULL, 0x10C0200040001000ULL, 0x83001041000B2000ULL, 0x0680280080041000ULL, 0x488004000A080080ULL, 0x0100180400010002ULL, From 7f1374d3944125c9c18cf927ba0794ae8f81d0e0 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sun, 6 Jan 2019 10:38:16 +0100 Subject: [PATCH 08/16] More comment improvements --- src/chess/bitboard.cc | 5 ++--- src/chess/board_test.cc | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index ed609bb4a1..96d1b4359a 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -295,7 +295,7 @@ static MagicBitBoards magic_bitboards; // Magic numbers determined via trial and error with random number generator // such that the number of relevant occupancy bits suffice to index the attacks -// tables without non-constructive collisions. +// tables with only constructive collisions. const BitBoard MagicBitBoards::kRookMagicNumbers[64] = { 0x088000102088C001ULL, 0x10C0200040001000ULL, 0x83001041000B2000ULL, 0x0680280080041000ULL, 0x488004000A080080ULL, 0x0100180400010002ULL, @@ -376,8 +376,7 @@ void MagicBitBoards::BuildAttacksTable(MagicParams* magic_params, for (unsigned square = 0; square < 64; square++) { const BoardSquare b_sq(square); - // Calculate relevant occupancy masks by subtracting the board edges from - // the total attacks bitboards. + // Calculate relevant occupancy masks. BitBoard mask = {0}; for (int j = 0; j < 4; j++) { diff --git a/src/chess/board_test.cc b/src/chess/board_test.cc index 65ed51dc82..c770b3c087 100644 --- a/src/chess/board_test.cc +++ b/src/chess/board_test.cc @@ -94,7 +94,7 @@ int Perft(const ChessBoard& board, int max_depth, bool dump = false, } } // namespace -/* TEST(ChessBoard, MoveGenStartingPos) { +TEST(ChessBoard, MoveGenStartingPos) { ChessBoard board; board.SetFromFen(ChessBoard::kStartposFen); @@ -105,7 +105,7 @@ int Perft(const ChessBoard& board, int max_depth, bool dump = false, EXPECT_EQ(Perft(board, 4), 197281); EXPECT_EQ(Perft(board, 5), 4865609); EXPECT_EQ(Perft(board, 6), 119060324); -} */ +} TEST(ChessBoard, MoveGenKiwipete) { ChessBoard board; @@ -116,7 +116,7 @@ TEST(ChessBoard, MoveGenKiwipete) { EXPECT_EQ(Perft(board, 2), 2039); EXPECT_EQ(Perft(board, 3), 97862); EXPECT_EQ(Perft(board, 4), 4085603); -// EXPECT_EQ(Perft(board, 5), 193690690); + EXPECT_EQ(Perft(board, 5), 193690690); } TEST(ChessBoard, MoveGenPosition3) { @@ -151,7 +151,7 @@ TEST(ChessBoard, MoveGenPosition5) { EXPECT_EQ(Perft(board, 2), 1486); EXPECT_EQ(Perft(board, 3), 62379); EXPECT_EQ(Perft(board, 4), 2103487); -// EXPECT_EQ(Perft(board, 5), 89941194); + EXPECT_EQ(Perft(board, 5), 89941194); } TEST(ChessBoard, MoveGenPosition6) { From 7d68bc221e2737291361e3727d856518ea176d73 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sun, 6 Jan 2019 10:55:11 +0100 Subject: [PATCH 09/16] Revert accidental board_test changes This reverts commit 7f1374d3944125c9c18cf927ba0794ae8f81d0e0. --- src/chess/bitboard.cc | 5 +++-- src/chess/board_test.cc | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index 96d1b4359a..ed609bb4a1 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -295,7 +295,7 @@ static MagicBitBoards magic_bitboards; // Magic numbers determined via trial and error with random number generator // such that the number of relevant occupancy bits suffice to index the attacks -// tables with only constructive collisions. +// tables without non-constructive collisions. const BitBoard MagicBitBoards::kRookMagicNumbers[64] = { 0x088000102088C001ULL, 0x10C0200040001000ULL, 0x83001041000B2000ULL, 0x0680280080041000ULL, 0x488004000A080080ULL, 0x0100180400010002ULL, @@ -376,7 +376,8 @@ void MagicBitBoards::BuildAttacksTable(MagicParams* magic_params, for (unsigned square = 0; square < 64; square++) { const BoardSquare b_sq(square); - // Calculate relevant occupancy masks. + // Calculate relevant occupancy masks by subtracting the board edges from + // the total attacks bitboards. BitBoard mask = {0}; for (int j = 0; j < 4; j++) { diff --git a/src/chess/board_test.cc b/src/chess/board_test.cc index c770b3c087..65ed51dc82 100644 --- a/src/chess/board_test.cc +++ b/src/chess/board_test.cc @@ -94,7 +94,7 @@ int Perft(const ChessBoard& board, int max_depth, bool dump = false, } } // namespace -TEST(ChessBoard, MoveGenStartingPos) { +/* TEST(ChessBoard, MoveGenStartingPos) { ChessBoard board; board.SetFromFen(ChessBoard::kStartposFen); @@ -105,7 +105,7 @@ TEST(ChessBoard, MoveGenStartingPos) { EXPECT_EQ(Perft(board, 4), 197281); EXPECT_EQ(Perft(board, 5), 4865609); EXPECT_EQ(Perft(board, 6), 119060324); -} +} */ TEST(ChessBoard, MoveGenKiwipete) { ChessBoard board; @@ -116,7 +116,7 @@ TEST(ChessBoard, MoveGenKiwipete) { EXPECT_EQ(Perft(board, 2), 2039); EXPECT_EQ(Perft(board, 3), 97862); EXPECT_EQ(Perft(board, 4), 4085603); - EXPECT_EQ(Perft(board, 5), 193690690); +// EXPECT_EQ(Perft(board, 5), 193690690); } TEST(ChessBoard, MoveGenPosition3) { @@ -151,7 +151,7 @@ TEST(ChessBoard, MoveGenPosition5) { EXPECT_EQ(Perft(board, 2), 1486); EXPECT_EQ(Perft(board, 3), 62379); EXPECT_EQ(Perft(board, 4), 2103487); - EXPECT_EQ(Perft(board, 5), 89941194); +// EXPECT_EQ(Perft(board, 5), 89941194); } TEST(ChessBoard, MoveGenPosition6) { From 3087bb839b167e619df1f8d7035aed6fa40c49c7 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sun, 6 Jan 2019 10:58:07 +0100 Subject: [PATCH 10/16] More comment improvements --- src/chess/bitboard.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index ed609bb4a1..96d1b4359a 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -295,7 +295,7 @@ static MagicBitBoards magic_bitboards; // Magic numbers determined via trial and error with random number generator // such that the number of relevant occupancy bits suffice to index the attacks -// tables without non-constructive collisions. +// tables with only constructive collisions. const BitBoard MagicBitBoards::kRookMagicNumbers[64] = { 0x088000102088C001ULL, 0x10C0200040001000ULL, 0x83001041000B2000ULL, 0x0680280080041000ULL, 0x488004000A080080ULL, 0x0100180400010002ULL, @@ -376,8 +376,7 @@ void MagicBitBoards::BuildAttacksTable(MagicParams* magic_params, for (unsigned square = 0; square < 64; square++) { const BoardSquare b_sq(square); - // Calculate relevant occupancy masks by subtracting the board edges from - // the total attacks bitboards. + // Calculate relevant occupancy masks. BitBoard mask = {0}; for (int j = 0; j < 4; j++) { From 26c3c8241a38720c5452ac963a86a2c4ee9edbb4 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Mon, 7 Jan 2019 09:14:39 +0100 Subject: [PATCH 11/16] Address review comments --- src/chess/bitboard.cc | 183 ----------------------------- src/chess/bitboard.h | 85 -------------- src/chess/board.cc | 259 ++++++++++++++++++++++++++++++++++++++++-- src/chess/board.h | 3 + src/main.cc | 6 +- 5 files changed, 260 insertions(+), 276 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index 96d1b4359a..a4bf6a8dcc 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -289,191 +289,8 @@ const int kKingCastleIndex = 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 -// Magic numbers determined via trial and error with random number generator -// such that the number of relevant occupancy bits suffice to index the attacks -// tables with only constructive collisions. -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]; - -BitBoard MagicBitBoards::rook_attacks_table_[102400]; -BitBoard MagicBitBoards::bishop_attacks_table_[5248]; - -MagicBitBoards::MagicBitBoards() { -#if defined(NO_PEXT) - // Set magic numbers for all board squares. - for (unsigned square = 0; square < 64; square++) { - rook_magic_params_[square].magic_number_ = - kRookMagicNumbers[square].as_int(); - bishop_magic_params_[square].magic_number_ = - kBishopMagicNumbers[square].as_int(); - } -#endif - - // Build attacks tables. - BuildAttacksTable(rook_magic_params_, rook_attacks_table_, kRookDirections); - BuildAttacksTable(bishop_magic_params_, bishop_attacks_table_, - kBishopDirections); -} - -void MagicBitBoards::BuildAttacksTable(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); - - // Calculate relevant occupancy masks. - BitBoard mask = {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 the next square in this direction is invalid, the current square - // is at the board's edge and should not be added. - if (!BoardSquare::IsValid(dst_row + direction.first, - dst_col + direction.second)) - break; - const BoardSquare destination(dst_row, dst_col); - mask.set(destination); - } - } - - // Set mask. - magic_params[square].mask_ = mask.as_int(); - - // Cache relevant occupancy board squares. - std::vector occupancy_squares; - - for (auto occ_sq : BitBoard(magic_params[square].mask_)) { - occupancy_squares.emplace_back(occ_sq); - } - -#if defined(NO_PEXT) - // 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(); -#endif - - // Set pointer to lookup table. - magic_params[square].attacks_table_ = &attacks_table[table_offset]; - - // 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 attacks table for every possible relevant 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 attacks bitboard corresponding to this occupancy bitboard. - BitBoard attacks(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); - attacks.set(destination); - if (occupancy.get(destination)) break; - } - } - -#if defined(NO_PEXT) - // Calculate magic index. - uint64_t index = occupancy.as_int(); - index *= magic_params[square].magic_number_; - index >>= magic_params[square].shift_bits_; - - // 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] != attacks) { - throw Exception("Invalid magic number!"); - } -#else - uint64_t index = - _pext_u64(occupancy.as_int(), magic_params[square].mask_); -#endif - - // Update table. - attacks_table[table_offset + index] = attacks; - } - - // Update table offset. - table_offset += (1 << occupancy_squares.size()); - } -} - 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 34ecf95c40..0cde790b10 100644 --- a/src/chess/bitboard.h +++ b/src/chess/bitboard.h @@ -32,11 +32,6 @@ #include #include -#if not defined(NO_PEXT) -// Include header for pext instruction. -#include -#endif - #include "utils/bititer.h" namespace lczero { @@ -238,86 +233,6 @@ class BitBoard { std::uint64_t board_ = 0; }; -// Holds magic bitboard routines and performs initialization. -// 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. - struct MagicParams { - // Relevant occupancy mask. - uint64_t mask_; - // Pointer to lookup table. - BitBoard* attacks_table_; -#if defined(NO_PEXT) - // Magic number. - uint64_t magic_number_; - // Number of bits to shift. - uint8_t shift_bits_; -#endif - }; - - // 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. - const uint8_t square = rook_square.as_int(); - -#if defined(NO_PEXT) - 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_; -#else - uint64_t index = - _pext_u64(pieces.as_int(), rook_magic_params_[square].mask_); -#endif - - // Return attacks bitboard. - return rook_magic_params_[square].attacks_table_[index]; - } - - // 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. - const uint8_t square = bishop_square.as_int(); - -#if defined(NO_PEXT) - 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_; -#else - uint64_t index = - _pext_u64(pieces.as_int(), bishop_magic_params_[square].mask_); -#endif - - // Return attacks bitboard. - return bishop_magic_params_[square].attacks_table_[index]; - } - - private: - // Builds rook or bishop attacks table. - void BuildAttacksTable(MagicParams* magic_params, BitBoard* attacks_table, - const std::pair* directions); - - // Magic numbers for each board square. - static const BitBoard kRookMagicNumbers[64]; - static const BitBoard kBishopMagicNumbers[64]; - - // Magic parameters for each board square. - static MagicParams rook_magic_params_[64]; - static MagicParams bishop_magic_params_[64]; - - // Attacks bitboards lookup tables. - static BitBoard rook_attacks_table_[102400]; - static BitBoard bishop_attacks_table_[5248]; -}; - class Move { public: enum class Promotion : std::uint8_t { None, Queen, Rook, Bishop, Knight }; diff --git a/src/chess/board.cc b/src/chess/board.cc index 567ecaa68d..be471ab3f9 100644 --- a/src/chess/board.cc +++ b/src/chess/board.cc @@ -33,6 +33,11 @@ #include #include "utils/exception.h" +#if not defined(NO_PEXT) +// Include header for pext instruction. +#include +#endif + namespace lczero { using std::string; @@ -180,8 +185,250 @@ static const Move::Promotion kPromotions[] = { Move::Promotion::Knight, }; +// Magic bitboard routines and structures. +// We use so-called "fancy" magic bitboards. + +// Structure holding all relevant magic parameters per square. +struct MagicParams { + // Relevant occupancy mask. + uint64_t mask_; + // Pointer to lookup table. + BitBoard* attacks_table_; +#if defined(NO_PEXT) + // Magic number. + uint64_t magic_number_; + // Number of bits to shift. + uint8_t shift_bits_; +#endif +}; + +#if defined(NO_PEXT) +// Magic numbers determined via trial and error with random number generator +// such that the number of relevant occupancy bits suffice to index the attacks +// tables with only constructive collisions. +static const BitBoard kRookMagicNumbers[] = { + 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}; +static const BitBoard kBishopMagicNumbers[] = { + 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}; +#endif + +// Magic parameters for rooks/bishops. +static MagicParams rook_magic_params[64]; +static MagicParams bishop_magic_params[64]; + +// Precomputed attacks bitboard tables. +static BitBoard rook_attacks_table[102400]; +static BitBoard bishop_attacks_table[5248]; + +// Builds rook or bishop attacks table. +static void BuildAttacksTable(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); + + // Calculate relevant occupancy masks. + BitBoard mask = {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 the next square in this direction is invalid, the current square + // is at the board's edge and should not be added. + if (!BoardSquare::IsValid(dst_row + direction.first, + dst_col + direction.second)) + break; + const BoardSquare destination(dst_row, dst_col); + mask.set(destination); + } + } + + // Set mask. + magic_params[square].mask_ = mask.as_int(); + + // Cache relevant occupancy board squares. + std::vector occupancy_squares; + + for (auto occ_sq : BitBoard(magic_params[square].mask_)) { + occupancy_squares.emplace_back(occ_sq); + } + +#if defined(NO_PEXT) + // 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(); +#endif + + // Set pointer to lookup table. + magic_params[square].attacks_table_ = &attacks_table[table_offset]; + + // 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 attacks table for every possible relevant 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 attacks bitboard corresponding to this occupancy bitboard. + BitBoard attacks(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); + attacks.set(destination); + if (occupancy.get(destination)) break; + } + } + +#if defined(NO_PEXT) + // Calculate magic index. + uint64_t index = occupancy.as_int(); + index *= magic_params[square].magic_number_; + index >>= magic_params[square].shift_bits_; + + // 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] != attacks) { + throw Exception("Invalid magic number!"); + } +#else + uint64_t index = + _pext_u64(occupancy.as_int(), magic_params[square].mask_); +#endif + + // Update table. + attacks_table[table_offset + index] = attacks; + } + + // Update table offset. + table_offset += (1 << occupancy_squares.size()); + } +} + +// 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. + const uint8_t square = rook_square.as_int(); + +#if defined(NO_PEXT) + 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_; +#else + uint64_t index = _pext_u64(pieces.as_int(), rook_magic_params[square].mask_); +#endif + + // Return attacks bitboard. + return rook_magic_params[square].attacks_table_[index]; +} + +// 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. + const uint8_t square = bishop_square.as_int(); + +#if defined(NO_PEXT) + 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_; +#else + uint64_t index = + _pext_u64(pieces.as_int(), bishop_magic_params[square].mask_); +#endif + + // Return attacks bitboard. + return bishop_magic_params[square].attacks_table_[index]; +} + } // namespace +void InitializeMagicBitboards() { +#if defined(NO_PEXT) + // Set magic numbers for all board squares. + for (unsigned square = 0; square < 64; square++) { + rook_magic_params[square].magic_number_ = + kRookMagicNumbers[square].as_int(); + bishop_magic_params[square].magic_number_ = + kBishopMagicNumbers[square].as_int(); + } +#endif + + // Build attacks tables. + BuildAttacksTable(rook_magic_params, rook_attacks_table, kRookDirections); + BuildAttacksTable(bishop_magic_params, bishop_attacks_table, + kBishopDirections); +} + BitBoard ChessBoard::pawns() const { return pawns_ * kPawnMask; } BitBoard ChessBoard::en_passant() const { return pawns_ - pawns(); } @@ -251,8 +498,7 @@ MoveList ChessBoard::GeneratePseudolegalMoves() const { if (rooks_.get(source)) { processed_piece = true; BitBoard attacked = - MagicBitBoards::GetRookAttacks(source, our_pieces_ + their_pieces_) - - our_pieces_; + GetRookAttacks(source, our_pieces_ + their_pieces_) - our_pieces_; for (const auto& destination : attacked) { result.emplace_back(source, destination); @@ -261,9 +507,8 @@ MoveList ChessBoard::GeneratePseudolegalMoves() const { // Bishop (and queen) if (bishops_.get(source)) { processed_piece = true; - BitBoard attacked = MagicBitBoards::GetBishopAttacks( - source, our_pieces_ + their_pieces_) - - our_pieces_; + BitBoard attacked = + GetBishopAttacks(source, our_pieces_ + their_pieces_) - our_pieces_; for (const auto& destination : attacked) { result.emplace_back(source, destination); @@ -449,12 +694,12 @@ bool ChessBoard::IsUnderAttack(BoardSquare square) const { if (std::abs(krow - row) <= 1 && std::abs(kcol - col) <= 1) return true; } // Check rooks (and queens). - if (MagicBitBoards::GetRookAttacks(square, our_pieces_ + their_pieces_) + if (GetRookAttacks(square, our_pieces_ + their_pieces_) .intersects(their_pieces_ * rooks_)) { return true; } // Check bishops. - if (MagicBitBoards::GetBishopAttacks(square, our_pieces_ + their_pieces_) + if (GetBishopAttacks(square, our_pieces_ + their_pieces_) .intersects(their_pieces_ * bishops_)) { return true; } diff --git a/src/chess/board.h b/src/chess/board.h index c2431a1fae..3c54202faf 100644 --- a/src/chess/board.h +++ b/src/chess/board.h @@ -33,6 +33,9 @@ namespace lczero { +// Initializes internal magic bitboard structures. +void InitializeMagicBitboards(); + // Represents king attack info used during legal move detection. class KingAttackInfo { public: diff --git a/src/main.cc b/src/main.cc index c31924ff0d..cc32ef3ff3 100644 --- a/src/main.cc +++ b/src/main.cc @@ -25,8 +25,9 @@ Program grant you additional permission to convey the resulting work. */ -#include "engine.h" #include "benchmark/benchmark.h" +#include "chess/board.h" +#include "engine.h" #include "selfplay/loop.h" #include "utils/commandline.h" #include "utils/logging.h" @@ -38,6 +39,9 @@ int main(int argc, const char** argv) { CERR << "| _ | |"; CERR << "|_ |_ |_| v" << GetVersionStr() << " built " << __DATE__; using namespace lczero; + + InitializeMagicBitboards(); + CommandLine::Init(argc, argv); CommandLine::RegisterMode("uci", "(default) Act as UCI engine"); CommandLine::RegisterMode("selfplay", "Play games with itself"); From ec40a038a91c9644ff99aa35a16166343fe91249 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Mon, 7 Jan 2019 09:18:31 +0100 Subject: [PATCH 12/16] Initialize magic bitboards in board_test --- src/chess/board_test.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/chess/board_test.cc b/src/chess/board_test.cc index 65ed51dc82..374ce92516 100644 --- a/src/chess/board_test.cc +++ b/src/chess/board_test.cc @@ -116,7 +116,7 @@ TEST(ChessBoard, MoveGenKiwipete) { EXPECT_EQ(Perft(board, 2), 2039); EXPECT_EQ(Perft(board, 3), 97862); EXPECT_EQ(Perft(board, 4), 4085603); -// EXPECT_EQ(Perft(board, 5), 193690690); + // EXPECT_EQ(Perft(board, 5), 193690690); } TEST(ChessBoard, MoveGenPosition3) { @@ -151,7 +151,7 @@ TEST(ChessBoard, MoveGenPosition5) { EXPECT_EQ(Perft(board, 2), 1486); EXPECT_EQ(Perft(board, 3), 62379); EXPECT_EQ(Perft(board, 4), 2103487); -// EXPECT_EQ(Perft(board, 5), 89941194); + // EXPECT_EQ(Perft(board, 5), 89941194); } TEST(ChessBoard, MoveGenPosition6) { @@ -240,5 +240,6 @@ TEST(ChessBoard, HasMatingMaterialMultipleBishopsNotSameColor) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + lczero::InitializeMagicBitboards(); return RUN_ALL_TESTS(); } From a28129bca878eccecec1c83ea6403df211f49ceb Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Mon, 7 Jan 2019 18:10:21 +0100 Subject: [PATCH 13/16] Cleanup of unused code --- src/chess/bitboard.cc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/chess/bitboard.cc b/src/chess/bitboard.cc index a4bf6a8dcc..bbee6bdaa9 100644 --- a/src/chess/bitboard.cc +++ b/src/chess/bitboard.cc @@ -28,8 +28,6 @@ #include "chess/bitboard.h" #include "utils/exception.h" -#include - namespace lczero { namespace { @@ -269,12 +267,6 @@ const Move kIdxToMove[] = { "g7g8b", "g7h8q", "g7h8r", "g7h8b", "h7g8q", "h7g8r", "h7g8b", "h7h8q", "h7h8r", "h7h8b"}; -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) { @@ -288,7 +280,6 @@ 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()]; - } // namespace Move::Move(const std::string& str, bool black) { From 389c4d30e6b39f53eb6f68b163452e8fe3f0cfa4 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Mon, 7 Jan 2019 18:16:24 +0100 Subject: [PATCH 14/16] Initialize magic bitboards in tests --- src/chess/position_test.cc | 1 + src/neural/encoder_test.cc | 23 ++++++++++++----------- src/syzygy/syzygy_test.cc | 1 + 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/chess/position_test.cc b/src/chess/position_test.cc index 6e6e021e08..de5b146588 100644 --- a/src/chess/position_test.cc +++ b/src/chess/position_test.cc @@ -132,5 +132,6 @@ TEST(PositionHistory, DidRepeatSinceLastZeroingMoveNeverRepeated) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + lczero::InitializeMagicBitboards(); return RUN_ALL_TESTS(); } diff --git a/src/neural/encoder_test.cc b/src/neural/encoder_test.cc index 6b84f3d753..d922b1c38a 100644 --- a/src/neural/encoder_test.cc +++ b/src/neural/encoder_test.cc @@ -30,7 +30,6 @@ TEST(EncodePositionForNN, EncodeStartPosition) { board.SetFromFen(ChessBoard::kStartposFen); history.Reset(board, 0, 1); - InputPlanes encoded_planes = EncodePositionForNN(history, 8, FillEmptyHistory::NO); @@ -68,30 +67,31 @@ TEST(EncodePositionForNN, EncodeStartPosition) { InputPlane their_king_plane = encoded_planes[11]; auto their_king_row = 7; auto their_king_col = 4; - EXPECT_EQ(their_king_plane.mask, 1ull << (8*their_king_row + their_king_col)); + EXPECT_EQ(their_king_plane.mask, + 1ull << (8 * their_king_row + their_king_col)); EXPECT_EQ(their_king_plane.value, 1.0f); // Auxiliary planes // It's the start of the game, so all castlings should be allowed. for (auto i = 0; i < 4; i++) { - InputPlane can_castle_plane = encoded_planes[13*8 + i]; + InputPlane can_castle_plane = encoded_planes[13 * 8 + i]; EXPECT_EQ(can_castle_plane.mask, kAllSquaresMask); EXPECT_EQ(can_castle_plane.value, 1.0f); } - InputPlane we_are_black_plane = encoded_planes[13*8 + 4]; + InputPlane we_are_black_plane = encoded_planes[13 * 8 + 4]; EXPECT_EQ(we_are_black_plane.mask, 0ull); - InputPlane fifty_move_counter_plane = encoded_planes[13*8 + 5]; + InputPlane fifty_move_counter_plane = encoded_planes[13 * 8 + 5]; EXPECT_EQ(fifty_move_counter_plane.mask, kAllSquaresMask); EXPECT_EQ(fifty_move_counter_plane.value, 0.0f); // We no longer encode the move count, so that plane should be all zeros - InputPlane zeroed_move_count_plane = encoded_planes[13*8 + 6]; + InputPlane zeroed_move_count_plane = encoded_planes[13 * 8 + 6]; EXPECT_EQ(zeroed_move_count_plane.mask, 0ull); - InputPlane all_ones_plane = encoded_planes[13*8 + 7]; + InputPlane all_ones_plane = encoded_planes[13 * 8 + 7]; EXPECT_EQ(all_ones_plane.mask, kAllSquaresMask); EXPECT_EQ(all_ones_plane.value, 1.0f); } @@ -108,11 +108,11 @@ TEST(EncodePositionForNN, EncodeFiftyMoveCounter) { InputPlanes encoded_planes = EncodePositionForNN(history, 8, FillEmptyHistory::NO); - InputPlane we_are_black_plane = encoded_planes[13*8 + 4]; + InputPlane we_are_black_plane = encoded_planes[13 * 8 + 4]; EXPECT_EQ(we_are_black_plane.mask, kAllSquaresMask); EXPECT_EQ(we_are_black_plane.value, 1.0f); - InputPlane fifty_move_counter_plane = encoded_planes[13*8 + 5]; + InputPlane fifty_move_counter_plane = encoded_planes[13 * 8 + 5]; EXPECT_EQ(fifty_move_counter_plane.mask, kAllSquaresMask); EXPECT_EQ(fifty_move_counter_plane.value, 1.0f); @@ -121,10 +121,10 @@ TEST(EncodePositionForNN, EncodeFiftyMoveCounter) { encoded_planes = EncodePositionForNN(history, 8, FillEmptyHistory::NO); - we_are_black_plane = encoded_planes[13*8 + 4]; + we_are_black_plane = encoded_planes[13 * 8 + 4]; EXPECT_EQ(we_are_black_plane.mask, 0ull); - fifty_move_counter_plane = encoded_planes[13*8 + 5]; + fifty_move_counter_plane = encoded_planes[13 * 8 + 5]; EXPECT_EQ(fifty_move_counter_plane.mask, kAllSquaresMask); EXPECT_EQ(fifty_move_counter_plane.value, 2.0f); } @@ -133,5 +133,6 @@ TEST(EncodePositionForNN, EncodeFiftyMoveCounter) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + lczero::InitializeMagicBitboards(); return RUN_ALL_TESTS(); } diff --git a/src/syzygy/syzygy_test.cc b/src/syzygy/syzygy_test.cc index ac5a3e9578..cd4d287abd 100644 --- a/src/syzygy/syzygy_test.cc +++ b/src/syzygy/syzygy_test.cc @@ -244,5 +244,6 @@ TEST(Syzygy, Root5PieceProbes) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + lczero::InitializeMagicBitboards(); return RUN_ALL_TESTS(); } From 85e95a612187b51ebed3388919d8abe7d2cfdd49 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Mon, 7 Jan 2019 18:34:17 +0100 Subject: [PATCH 15/16] Rename bitboard operators +,* to |,& --- src/chess/bitboard.h | 14 ++++++------- src/chess/board.cc | 46 +++++++++++++++++++++---------------------- src/chess/board.h | 2 +- src/mcts/search.cc | 7 ++++--- src/neural/encoder.cc | 18 ++++++++--------- src/syzygy/syzygy.cc | 16 +++++++-------- 6 files changed, 52 insertions(+), 51 deletions(-) diff --git a/src/chess/bitboard.h b/src/chess/bitboard.h index 0cde790b10..9f09825b1f 100644 --- a/src/chess/bitboard.h +++ b/src/chess/bitboard.h @@ -199,7 +199,7 @@ class BitBoard { } // Applies a mask to the bitboard (intersects). - BitBoard& operator*=(const BitBoard& a) { + BitBoard& operator&=(const BitBoard& a) { board_ &= a.board_; return *this; } @@ -210,10 +210,15 @@ class BitBoard { } // Returns union (bitwise OR) of two boards. - friend BitBoard operator+(const BitBoard& a, const BitBoard& b) { + friend BitBoard operator|(const BitBoard& a, const BitBoard& b) { return {a.board_ | b.board_}; } + // Returns intersection (bitwise AND) of two boards. + friend BitBoard operator&(const BitBoard& a, const BitBoard& b) { + return {a.board_ & b.board_}; + } + // Returns bitboard with one bit reset. friend BitBoard operator-(const BitBoard& a, const BoardSquare& b) { return {a.board_ & ~(1ULL << b.as_int())}; @@ -224,11 +229,6 @@ class BitBoard { return {a.board_ & ~b.board_}; } - // Returns intersection (bitwise AND) of two boards. - friend BitBoard operator*(const BitBoard& a, const BitBoard& b) { - return {a.board_ & b.board_}; - } - private: std::uint64_t board_ = 0; }; diff --git a/src/chess/board.cc b/src/chess/board.cc index be471ab3f9..11b239ef97 100644 --- a/src/chess/board.cc +++ b/src/chess/board.cc @@ -429,7 +429,7 @@ void InitializeMagicBitboards() { kBishopDirections); } -BitBoard ChessBoard::pawns() const { return pawns_ * kPawnMask; } +BitBoard ChessBoard::pawns() const { return pawns_ & kPawnMask; } BitBoard ChessBoard::en_passant() const { return pawns_ - pawns(); } @@ -498,7 +498,7 @@ MoveList ChessBoard::GeneratePseudolegalMoves() const { if (rooks_.get(source)) { processed_piece = true; BitBoard attacked = - GetRookAttacks(source, our_pieces_ + their_pieces_) - our_pieces_; + GetRookAttacks(source, our_pieces_ | their_pieces_) - our_pieces_; for (const auto& destination : attacked) { result.emplace_back(source, destination); @@ -508,7 +508,7 @@ MoveList ChessBoard::GeneratePseudolegalMoves() const { if (bishops_.get(source)) { processed_piece = true; BitBoard attacked = - GetBishopAttacks(source, our_pieces_ + their_pieces_) - our_pieces_; + GetBishopAttacks(source, our_pieces_ | their_pieces_) - our_pieces_; for (const auto& destination : attacked) { result.emplace_back(source, destination); @@ -516,7 +516,7 @@ MoveList ChessBoard::GeneratePseudolegalMoves() const { } if (processed_piece) continue; // Pawns. - if ((pawns_ * kPawnMask).get(source)) { + if ((pawns_ & kPawnMask).get(source)) { // Moves forward. { const auto dst_row = source.row() + 1; @@ -612,7 +612,7 @@ bool ChessBoard::ApplyMove(Move move) { } // Remove en passant flags. - pawns_ *= kPawnMask; + pawns_ &= kPawnMask; // If pawn was moved, reset 50 move draw counter. reset_50_moves |= pawns_.get(from); @@ -677,7 +677,7 @@ bool ChessBoard::ApplyMove(Move move) { // Set en passant flag. if (to_row - from_row == 2 && pawns_.get(to)) { BoardSquare ep_sq(to_row - 1, to_col); - if (kPawnAttacks[ep_sq.as_int()].intersects(their_pieces_ * pawns_)) { + if (kPawnAttacks[ep_sq.as_int()].intersects(their_pieces_ & pawns_)) { pawns_.set(0, to_col); } } @@ -694,24 +694,24 @@ bool ChessBoard::IsUnderAttack(BoardSquare square) const { if (std::abs(krow - row) <= 1 && std::abs(kcol - col) <= 1) return true; } // Check rooks (and queens). - if (GetRookAttacks(square, our_pieces_ + their_pieces_) - .intersects(their_pieces_ * rooks_)) { + if (GetRookAttacks(square, our_pieces_ | their_pieces_) + .intersects(their_pieces_ & rooks_)) { return true; } // Check bishops. - if (GetBishopAttacks(square, our_pieces_ + their_pieces_) - .intersects(their_pieces_ * bishops_)) { + if (GetBishopAttacks(square, our_pieces_ | their_pieces_) + .intersects(their_pieces_ & bishops_)) { return true; } // Check pawns. - if (kPawnAttacks[square.as_int()].intersects(their_pieces_ * pawns_)) { + if (kPawnAttacks[square.as_int()].intersects(their_pieces_ & pawns_)) { return true; } // Check knights. { if (kKnightAttacks[square.as_int()].intersects(their_pieces_ - their_king_ - rooks_ - bishops_ - - (pawns_ * kPawnMask))) { + (pawns_ & kPawnMask))) { return true; } } @@ -728,7 +728,7 @@ KingAttackInfo ChessBoard::GenerateKingAttackInfo() const { const int col = our_king_.col(); // King checks are unnecessary, as kings cannot give check. // Check rooks (and queens). - if (kRookAttacks[our_king_.as_int()].intersects(their_pieces_ * rooks_)) { + if (kRookAttacks[our_king_.as_int()].intersects(their_pieces_ & rooks_)) { for (const auto& direction : kRookDirections) { auto dst_row = row; auto dst_col = col; @@ -761,7 +761,7 @@ KingAttackInfo ChessBoard::GenerateKingAttackInfo() const { } else { // Update attack lines. king_attack_info.attack_lines_ = - king_attack_info.attack_lines_ + attack_line; + king_attack_info.attack_lines_ | attack_line; num_king_attackers++; } } @@ -771,7 +771,7 @@ KingAttackInfo ChessBoard::GenerateKingAttackInfo() const { } } // Check bishops. - if (kBishopAttacks[our_king_.as_int()].intersects(their_pieces_ * bishops_)) { + if (kBishopAttacks[our_king_.as_int()].intersects(their_pieces_ & bishops_)) { for (const auto& direction : kBishopDirections) { auto dst_row = row; auto dst_col = col; @@ -804,7 +804,7 @@ KingAttackInfo ChessBoard::GenerateKingAttackInfo() const { } else { // Update attack lines. king_attack_info.attack_lines_ = - king_attack_info.attack_lines_ + attack_line; + king_attack_info.attack_lines_ | attack_line; num_king_attackers++; } } @@ -815,9 +815,9 @@ KingAttackInfo ChessBoard::GenerateKingAttackInfo() const { } // Check pawns. const BitBoard attacking_pawns = - kPawnAttacks[our_king_.as_int()] * their_pieces_ * pawns_; + kPawnAttacks[our_king_.as_int()] & their_pieces_ & pawns_; king_attack_info.attack_lines_ = - king_attack_info.attack_lines_ + attacking_pawns; + king_attack_info.attack_lines_ | attacking_pawns; if (attacking_pawns.as_int()) { // No more than one pawn can give check. @@ -826,10 +826,10 @@ KingAttackInfo ChessBoard::GenerateKingAttackInfo() const { // Check knights. const BitBoard attacking_knights = - kKnightAttacks[our_king_.as_int()] * - (their_pieces_ - their_king_ - rooks_ - bishops_ - (pawns_ * kPawnMask)); + kKnightAttacks[our_king_.as_int()] & + (their_pieces_ - their_king_ - rooks_ - bishops_ - (pawns_ & kPawnMask)); king_attack_info.attack_lines_ = - king_attack_info.attack_lines_ + attacking_knights; + king_attack_info.attack_lines_ | attacking_knights; if (attacking_knights.as_int()) { // No more than one knight can give check. @@ -1020,7 +1020,7 @@ bool ChessBoard::HasMatingMaterial() const { return true; } - if ((our_pieces_ + their_pieces_).count() < 4) { + if ((our_pieces_ | their_pieces_).count() < 4) { // K v K, K+B v K, K+N v K. return false; } @@ -1060,7 +1060,7 @@ string ChessBoard::DebugString() const { continue; } char c = '?'; - if ((pawns_ * kPawnMask).get(i, j)) { + if ((pawns_ & kPawnMask).get(i, j)) { c = 'p'; } else if (bishops_.get(i, j)) { if (rooks_.get(i, j)) diff --git a/src/chess/board.h b/src/chess/board.h index 3c54202faf..46c2fc4207 100644 --- a/src/chess/board.h +++ b/src/chess/board.h @@ -153,7 +153,7 @@ class ChessBoard { BitBoard en_passant() const; BitBoard bishops() const { return bishops_ - rooks_; } BitBoard rooks() const { return rooks_ - bishops_; } - BitBoard queens() const { return rooks_ * bishops_; } + BitBoard queens() const { return rooks_ & bishops_; } BitBoard our_knights() const { return our_pieces_ - pawns() - our_king_ - rooks_ - bishops_; } diff --git a/src/mcts/search.cc b/src/mcts/search.cc index d946741f08..8e5d82b781 100644 --- a/src/mcts/search.cc +++ b/src/mcts/search.cc @@ -450,7 +450,7 @@ bool Search::PopulateRootMoveLimit(MoveList* root_moves) const { } auto board = played_history_.Last().GetBoard(); if (!syzygy_tb_ || !board.castlings().no_legal_castle() || - (board.ours() + board.theirs()).count() > syzygy_tb_->max_cardinality()) { + (board.ours() | board.theirs()).count() > syzygy_tb_->max_cardinality()) { return false; } return syzygy_tb_->root_probe(played_history_.Last(), @@ -850,7 +850,8 @@ SearchWorker::NodeToProcess SearchWorker::PickNodeToExtend( // a search collision, and this node is already being expanded. if (!node->TryStartScoreUpdate()) { if (!is_root_node) { - IncrementNInFlight(node->GetParent(), search_->root_node_, collision_limit - 1); + IncrementNInFlight(node->GetParent(), search_->root_node_, + collision_limit - 1); } return NodeToProcess::Collision(node, depth, collision_limit); } @@ -996,7 +997,7 @@ void SearchWorker::ExtendNode(Node* node) { // Neither by-position or by-rule termination, but maybe it's a TB position. if (search_->syzygy_tb_ && board.castlings().no_legal_castle() && history_.Last().GetNoCaptureNoPawnPly() == 0 && - (board.ours() + board.theirs()).count() <= + (board.ours() | board.theirs()).count() <= search_->syzygy_tb_->max_cardinality()) { ProbeState state; WDLScore wdl = search_->syzygy_tb_->probe_wdl(history_.Last(), &state); diff --git a/src/neural/encoder.cc b/src/neural/encoder.cc index 77e9a5178f..9e4569dda2 100644 --- a/src/neural/encoder.cc +++ b/src/neural/encoder.cc @@ -26,8 +26,8 @@ */ #include "neural/encoder.h" -#include "utils/optional.h" #include +#include "utils/optional.h" namespace lczero { @@ -72,18 +72,18 @@ InputPlanes EncodePositionForNN(const PositionHistory& history, } const int base = i * kPlanesPerBoard; - result[base + 0].mask = (board.ours() * board.pawns()).as_int(); + result[base + 0].mask = (board.ours() & board.pawns()).as_int(); result[base + 1].mask = (board.our_knights()).as_int(); - result[base + 2].mask = (board.ours() * board.bishops()).as_int(); - result[base + 3].mask = (board.ours() * board.rooks()).as_int(); - result[base + 4].mask = (board.ours() * board.queens()).as_int(); + result[base + 2].mask = (board.ours() & board.bishops()).as_int(); + result[base + 3].mask = (board.ours() & board.rooks()).as_int(); + result[base + 4].mask = (board.ours() & board.queens()).as_int(); result[base + 5].mask = (board.our_king()).as_int(); - result[base + 6].mask = (board.theirs() * board.pawns()).as_int(); + result[base + 6].mask = (board.theirs() & board.pawns()).as_int(); result[base + 7].mask = (board.their_knights()).as_int(); - result[base + 8].mask = (board.theirs() * board.bishops()).as_int(); - result[base + 9].mask = (board.theirs() * board.rooks()).as_int(); - result[base + 10].mask = (board.theirs() * board.queens()).as_int(); + result[base + 8].mask = (board.theirs() & board.bishops()).as_int(); + result[base + 9].mask = (board.theirs() & board.rooks()).as_int(); + result[base + 10].mask = (board.theirs() & board.queens()).as_int(); result[base + 11].mask = (board.their_king()).as_int(); const int repetitions = position.GetRepetitions(); diff --git a/src/syzygy/syzygy.cc b/src/syzygy/syzygy.cc index 77d4948b06..72a69b3b73 100644 --- a/src/syzygy/syzygy.cc +++ b/src/syzygy/syzygy.cc @@ -187,15 +187,15 @@ int count_pieces(const ChessBoard& pos, int type, bool theirs) { case KING: return 1; case QUEEN: - return (all * pos.queens()).count_few(); + return (all & pos.queens()).count_few(); case ROOK: - return (all * pos.rooks()).count_few(); + return (all & pos.rooks()).count_few(); case BISHOP: - return (all * pos.bishops()).count_few(); + return (all & pos.bishops()).count_few(); case KNIGHT: return (theirs ? pos.their_knights() : pos.our_knights()).count_few(); case PAWN: - return (all * pos.pawns()).count_few(); + return (all & pos.pawns()).count_few(); default: assert(false); } @@ -208,15 +208,15 @@ BitBoard pieces(const ChessBoard& pos, int type, bool theirs) { case KING: return theirs ? pos.their_king() : pos.our_king(); case QUEEN: - return all * pos.queens(); + return all & pos.queens(); case ROOK: - return all * pos.rooks(); + return all & pos.rooks(); case BISHOP: - return all * pos.bishops(); + return all & pos.bishops(); case KNIGHT: return theirs ? pos.their_knights() : pos.our_knights(); case PAWN: - return all * pos.pawns(); + return all & pos.pawns(); default: assert(false); } From 0c45ed1330cb2a5deb47136eb7c50ab8178e43e3 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Mon, 7 Jan 2019 18:38:22 +0100 Subject: [PATCH 16/16] Added inline qualifiers --- src/chess/board.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/chess/board.cc b/src/chess/board.cc index 11b239ef97..7fceb42ecd 100644 --- a/src/chess/board.cc +++ b/src/chess/board.cc @@ -373,8 +373,8 @@ static void BuildAttacksTable(MagicParams* magic_params, // 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) { +static inline BitBoard GetRookAttacks(const BoardSquare rook_square, + const BitBoard pieces) { // Calculate magic index. const uint8_t square = rook_square.as_int(); @@ -392,8 +392,8 @@ static BitBoard GetRookAttacks(const BoardSquare rook_square, // 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) { +static inline BitBoard GetBishopAttacks(const BoardSquare bishop_square, + const BitBoard pieces) { // Calculate magic index. const uint8_t square = bishop_square.as_int();