Last active
May 19, 2023 03:24
-
-
Save archishou/f48649e13a87f024b55aaab409c62e30 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <cstdint> | |
#include <string> | |
#include <array> | |
#include <vector> | |
#include <algorithm> | |
#include <sstream> | |
#include "iostream" | |
namespace MidnightMoveGen { | |
/* ------------------------ DEFINE UNIVERSAL TYPES ------------------------ */ | |
using u8 = std::uint8_t; | |
using u16 = std::uint16_t; | |
using u32 = std::uint32_t; | |
using u64 = std::uint64_t; | |
using i8 = std::int8_t; | |
using i16 = std::int16_t; | |
using i32 = std::int32_t; | |
using i64 = std::int64_t; | |
using f32 = float; | |
using f64 = double; | |
using usize = std::size_t; | |
using std::string; | |
using std::array; | |
using std::vector; | |
/* ------------------------ DEFINE HELPER SPLIT FUNCTION ------------------------ */ | |
inline vector<std::string> split(const string& s, const string& delimiter) { | |
usize pos_start = 0, pos_end, delim_len = delimiter.length(); | |
string token; | |
vector<string> res; | |
while ((pos_end = s.find (delimiter, pos_start)) != std::string::npos) { | |
token = s.substr (pos_start, pos_end - pos_start); | |
pos_start = pos_end + delim_len; | |
res.push_back(token); | |
} | |
res.push_back(s.substr (pos_start)); | |
return res; | |
} | |
/* ------------------------ DEFINE HELPER STACK CLASS ------------------------ */ | |
template<typename T, usize Capacity> | |
class Stack { | |
public: | |
Stack() = default; | |
~Stack() = default; | |
inline void push(const T& element) { | |
data[length++] = element; | |
} | |
inline void push(const T&& constructed_element) { | |
data[length++] = std::move(constructed_element); | |
} | |
[[nodiscard]] inline T pop() { return data[length-- -1]; } | |
[[nodiscard]] inline T peek() const { return data[length - 1]; } | |
[[nodiscard]] inline T& top() { return data[length - 1]; } | |
inline void clear() { | |
for (usize i = 0; i < length; i++) data[i].~T(); | |
length = 0; | |
} | |
[[nodiscard]] inline auto size() const { return length; } | |
[[nodiscard]] inline auto empty() const { return length == 0; } | |
[[nodiscard]] inline auto operator[](usize i) const { return data[i]; } | |
[[nodiscard]] inline auto begin() { return data.begin(); } | |
[[nodiscard]] inline auto end() { return data.begin() + static_cast<std::ptrdiff_t>(length); } | |
[[nodiscard]] inline auto &operator[](usize i) { return data[i]; } | |
[[nodiscard]] inline auto begin() const { return data.begin(); } | |
[[nodiscard]] inline auto end() const { return data.begin() + static_cast<std::ptrdiff_t>(length); } | |
private: | |
std::array<T, Capacity> data{}; | |
usize length = 0; | |
}; | |
/* ------------------------ DEFINE BOARD TYPES ------------------------ */ | |
using ZobristHash = u64; | |
using Bitboard = u64; | |
constexpr i32 NCOLORS = 2; | |
enum Color : i32 { | |
WHITE, | |
BLACK, | |
}; | |
constexpr Color operator~(Color c) { | |
return Color(c ^ BLACK); | |
} | |
constexpr i32 NDIRS = 8; | |
enum Direction : i32 { | |
NORTH = 8, NORTH_EAST = 9, EAST = 1, SOUTH_EAST = -7, | |
SOUTH = -8, SOUTH_WEST = -9, WEST = -1, NORTH_WEST = 7, | |
NORTH_NORTH = 16, SOUTH_SOUTH = -16 | |
}; | |
template<Color C, Direction D> | |
consteval Direction relative_dir() { | |
if constexpr (C == WHITE) return D; | |
return Direction(-D); | |
} | |
constexpr i32 NFILES = 8; | |
using File = i32; | |
constexpr File AFILE = 0; | |
constexpr File BFILE = 1; | |
constexpr File CFILE = 2; | |
constexpr File DFILE = 3; | |
constexpr File EFILE = 4; | |
constexpr File FFILE = 5; | |
constexpr File GFILE = 6; | |
constexpr File HFILE = 7; | |
constexpr i32 NRANKS = 8; | |
using Rank = i32; | |
constexpr Rank RANK1 = 0; | |
constexpr Rank RANK2 = 1; | |
constexpr Rank RANK3 = 2; | |
constexpr Rank RANK4 = 3; | |
constexpr Rank RANK5 = 4; | |
constexpr Rank RANK6 = 5; | |
constexpr Rank RANK7 = 6; | |
constexpr Rank RANK8 = 7; | |
template<Color color> | |
constexpr Rank relative_rank(Rank r) { | |
if constexpr (color == WHITE) return r; | |
return RANK8 - r; | |
} | |
constexpr i32 NCASTLING_RIGHTS = 4; | |
using CastleRight = i32; | |
constexpr CastleRight BLACK_OOO = 0b0001; | |
constexpr CastleRight BLACK_OO = 0b0010; | |
constexpr CastleRight WHITE_OOO = 0b0100; | |
constexpr CastleRight WHITE_OO = 0b1000; | |
/* ------------------------ DEFINE SQUARE TYPES ------------------------ */ | |
constexpr i32 NSQUARES = 64; | |
enum Square : i32 { | |
a1, b1, c1, d1, e1, f1, g1, h1, | |
a2, b2, c2, d2, e2, f2, g2, h2, | |
a3, b3, c3, d3, e3, f3, g3, h3, | |
a4, b4, c4, d4, e4, f4, g4, h4, | |
a5, b5, c5, d5, e5, f5, g5, h5, | |
a6, b6, c6, d6, e6, f6, g6, h6, | |
a7, b7, c7, d7, e7, f7, g7, h7, | |
a8, b8, c8, d8, e8, f8, g8, h8, | |
NO_SQUARE | |
}; | |
inline constexpr Square operator ++(Square& orig, i32) { | |
Square r_val = orig; | |
orig = static_cast<Square>(orig + 1); | |
return r_val; | |
} | |
constexpr Square operator +(Square s, Direction d) { | |
return Square(static_cast<i32>(s) + static_cast<i32>(d)); | |
} | |
constexpr Square operator -(Square s, Direction d) { | |
return Square(static_cast<i32>(s) - static_cast<i32>(d)); | |
} | |
inline Square& operator +=(Square& s, Direction d) { | |
return s = s + d; | |
} | |
inline Square& operator +=(Square& s, i32 i) { | |
return s = static_cast<Square>(s + i); | |
} | |
inline Square& operator -=(Square& s, Direction d) { | |
return s = s - d; | |
} | |
inline Square flip(Square s) { | |
return static_cast<Square>(s ^ 0b111000); | |
} | |
constexpr Rank rank_of(Square s) { | |
return Rank(s >> 3); | |
} | |
constexpr File file_of(Square s) { | |
return File(s & 0b111); | |
} | |
constexpr i32 diagonal_of(Square s) { | |
return 7 + rank_of(s) - file_of(s); | |
} | |
constexpr i32 anti_diagonal_of(Square s) { | |
return rank_of(s) + file_of(s); | |
} | |
constexpr Square create_square(File f, Rank r) { | |
return Square(r << 3 | f); | |
} | |
inline const string SQ_TO_STRING[NSQUARES + 1] = { | |
"a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1", | |
"a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2", | |
"a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3", | |
"a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4", | |
"a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5", | |
"a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6", | |
"a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7", | |
"a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8", | |
"None" | |
}; | |
/* ------------------------ DEFINE PIECE TYPE ------------------------ */ | |
constexpr u32 NPIECE_TYPES = 7; | |
enum PieceType : u32 { | |
PAWN, | |
KNIGHT, | |
BISHOP, | |
ROOK, | |
QUEEN, | |
KING, | |
NO_PIECE_TYPE | |
}; | |
constexpr i8 NPIECES = 15; | |
enum Piece : u32 { | |
WHITE_PAWN, | |
WHITE_KNIGHT, | |
WHITE_BISHOP, | |
WHITE_ROOK, | |
WHITE_QUEEN, | |
WHITE_KING, | |
BLACK_PAWN = 8, | |
BLACK_KNIGHT, | |
BLACK_BISHOP, | |
BLACK_ROOK, | |
BLACK_QUEEN, | |
BLACK_KING, | |
NO_PIECE, | |
}; | |
template<Color color, PieceType piece_type> | |
consteval Piece make_piece() { | |
return static_cast<Piece>((color << 3) | piece_type); | |
} | |
template<Piece piece> | |
consteval PieceType type_of() { | |
return static_cast<PieceType>(piece & 0b000111); | |
} | |
inline PieceType type_of(Piece piece) { | |
return static_cast<PieceType>(piece & 0b000111); | |
} | |
inline Piece piece_from_char(char c) { | |
switch (c) { | |
case 'P': return WHITE_PAWN; | |
case 'N': return WHITE_KNIGHT; | |
case 'B': return WHITE_BISHOP; | |
case 'R': return WHITE_ROOK; | |
case 'Q': return WHITE_QUEEN; | |
case 'K': return WHITE_KING; | |
case 'p': return BLACK_PAWN; | |
case 'n': return BLACK_KNIGHT; | |
case 'b': return BLACK_BISHOP; | |
case 'r': return BLACK_ROOK; | |
case 'q': return BLACK_QUEEN; | |
case 'k': return BLACK_KING; | |
default: return NO_PIECE; | |
} | |
} | |
constexpr char PIECE_MATCHER[NPIECES] = {'P', 'N', 'B', 'R', 'Q', 'K', '-', '-', 'p', 'n', 'b', 'r', 'q', 'k', '-'}; | |
/* ------------------------ DEFINE BOARD MASKS ------------------------ */ | |
constexpr array<Bitboard, NFILES> MASK_FILE = { | |
0x101010101010101, 0x202020202020202, 0x404040404040404, 0x808080808080808, | |
0x1010101010101010, 0x2020202020202020, 0x4040404040404040, 0x8080808080808080, | |
}; | |
constexpr array<Bitboard, NFILES> MASK_RANK = { | |
0xff, 0xff00, 0xff0000, 0xff000000, 0xff00000000, | |
0xff0000000000, 0xff000000000000, 0xff00000000000000 | |
}; | |
constexpr array<Bitboard, 15> MASK_DIAGONAL = { | |
0x80, 0x8040, 0x804020, | |
0x80402010, 0x8040201008, 0x804020100804, | |
0x80402010080402, 0x8040201008040201, 0x4020100804020100, | |
0x2010080402010000, 0x1008040201000000, 0x804020100000000, | |
0x402010000000000, 0x201000000000000, 0x100000000000000, | |
}; | |
constexpr array<Bitboard, 15> MASK_ANTI_DIAGONAL= { | |
0x1, 0x102, 0x10204, | |
0x1020408, 0x102040810, 0x10204081020, | |
0x1020408102040, 0x102040810204080, 0x204081020408000, | |
0x408102040800000, 0x810204080000000, 0x1020408000000000, | |
0x2040800000000000, 0x4080000000000000, 0x8000000000000000, | |
}; | |
/* ------------------------ DEFINE BITBOARD TYPE ------------------------ */ | |
[[nodiscard]] constexpr Bitboard square_to_bitboard(Square square) { | |
return 1ULL << square; | |
} | |
[[nodiscard]] constexpr Bitboard shift(Direction D, Bitboard b) { | |
if (D == NORTH) return b << 8; | |
else if (D == SOUTH) return b >> 8; | |
else if (D == NORTH + NORTH) return b << 16; | |
else if (D == SOUTH + SOUTH) return b >> 16; | |
else if (D == EAST) return (b & ~MASK_FILE[HFILE]) << 1; | |
else if (D == WEST) return (b & ~MASK_FILE[AFILE]) >> 1; | |
else if (D == NORTH_EAST) return (b & ~MASK_FILE[HFILE]) << 9; | |
else if (D == NORTH_WEST) return (b & ~MASK_FILE[AFILE]) << 7; | |
else if (D == SOUTH_EAST) return (b & ~MASK_FILE[HFILE]) >> 7; | |
else if (D == SOUTH_WEST) return (b & ~MASK_FILE[AFILE]) >> 9; | |
return 0; | |
} | |
template<Direction D> | |
[[nodiscard]] constexpr Bitboard shift(Bitboard b) { | |
return shift(D, b); | |
} | |
template<Color C, Direction D> | |
[[nodiscard]] constexpr Bitboard shift_relative(Bitboard b) { | |
return shift<relative_dir<C, D>()>(b); | |
} | |
inline void print_bitboard(Bitboard bitboard) { | |
std::bitset<64> b(bitboard); | |
std::string str_bitset = b.to_string(); | |
for (i32 i = 0; i < 64; i += 8) { | |
std::string x = str_bitset.substr(i, 8); | |
reverse(x.begin(), x.end()); | |
for (auto c : x) std::cout << c << " "; | |
std::cout << std::endl; | |
} | |
std::cout << std::endl; | |
} | |
// Compiler specific functions, taken from Stockfish https://github.com/official-stockfish/Stockfish | |
#if defined(__GNUC__) // GCC, Clang, ICC | |
[[nodiscard]] Square lsb(Bitboard bitboard) { | |
assert(bitboard); | |
return static_cast<Square>(__builtin_ctzll(bitboard)); | |
} | |
[[nodiscard]] Square msb(Bitboard bitboard) { | |
assert(bitboard); | |
return static_cast<Square>(63 ^ __builtin_clzll(bitboard)); | |
} | |
#elif defined(_MSC_VER) // MSVC | |
#ifdef _WIN64 // MSVC, WIN64 | |
#include <intrin.h> | |
Square lsb(U64 b) { | |
unsigned long idx; | |
_BitScanForward64(&idx, b); | |
return (Square)idx; | |
} | |
Square msb(U64 b) { | |
unsigned long idx; | |
_BitScanReverse64(&idx, b); | |
return (Square)idx; | |
} | |
#else // MSVC, WIN32 | |
#include <intrin.h> | |
Square lsb(U64 b) { | |
unsigned long idx; | |
if (b & 0xffffffff) { | |
_BitScanForward(&idx, int32_t(b)); | |
return Square(idx); | |
} | |
else { | |
_BitScanForward(&idx, int32_t(b >> 32)); | |
return Square(idx + 32); | |
} | |
} | |
Square msb(U64 b) { | |
unsigned long idx; | |
if (b >> 32) { | |
_BitScanReverse(&idx, int32_t(b >> 32)); | |
return Square(idx + 32); | |
} | |
else { | |
_BitScanReverse(&idx, int32_t(b)); | |
return Square(idx); | |
} | |
} | |
#endif | |
#else // Compiler is neither GCC nor MSVC compatible | |
#error "Compiler not supported." | |
#endif | |
[[nodiscard]] uint32_t pop_count(Bitboard bitboard) { | |
#if defined(_MSC_VER) || defined(__INTEL_COMPILER) | |
return (uint8_t)_mm_popcnt_u64(mask); | |
#else // Assumed gcc or compatible compiler | |
return __builtin_popcountll(bitboard); | |
#endif | |
} | |
[[nodiscard]] Square pop_lsb(Bitboard& bitboard) { | |
Square s = lsb(bitboard); | |
bitboard &= bitboard - 1; // compiler optimizes this to _blsr_u64 | |
return static_cast<Square>(s); | |
} | |
/* ------------------------ DEFINE FEN CONSTANTS ------------------------ */ | |
inline const string START_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; | |
inline const string INITIAL_BOARD_FEN = START_FEN; | |
inline const string KIWIPETE_FEN = "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1"; | |
inline const string TALKCHESS_FEN = "rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8"; | |
/* ------------------------ DEFINE ZOBRIST CONSTANTS ------------------------ */ | |
constexpr u64 ZOBRIST_PIECE_SQUARE[NPIECES][NSQUARES] = { | |
{ 0xb03f6a710560ca4, 0xed3975060c14ba86, 0x27d593c487210854, 0x354213d12a653685, 0x719746ce7c9e11a1, 0xd81b188d476c1ec, 0x9755d6539624742e, 0xae9c591477522543, 0x163054c3ee901c90, 0xe417020a2fceaae0, 0xce01388387e94026, 0xf6efadaeb35fab79, 0x6b72e713795a1ea5, 0x3e43b1ee0ac3ef08, 0x604b708b2f1a388b, 0x91418f2a99de6db7, 0xc5aac196f5de021a, 0xe8c7242deec7736c, 0x4659e971c74da2f2, 0xa4294430b4394e9f, 0xb19886ce52cc7a33, 0x7c8e785e8461a3b8, 0x51f60093ccd643b4, 0x184364435dfe7124, 0x7ac5d5dad6cb1d1e, 0xb8315e0269e8b2f1, 0x858fdcc85a9ae75d, 0xa917c6f01c4d592c, 0x209339334907c0c5, 0x61ec79e911a79dbd, 0x1c1b4d0145aaa62a, 0xfd3b6865557ccf04, 0xd634731b3d65727d, 0xe703ec0e37eacf4b, 0xa7b36760c91c9b19, 0x2873f58c847aadb5, 0x3fdf3c33e5a66df1, 0x5303275757fe98bc, 0x2e2d4cdd3966564, 0x9a18303d77379612, 0x81c32eaa8ee6ce9, 0xc7cc8bbb7b72ddd6, 0x277735f53a906bdc, 0x7eff27fda480edb1, 0xc35a6b278c74708e, 0xf2573ea43c3d442d, 0xce5111564718ea6c, 0xc1d3f3d4181a8c68, 0x401ffcb80673e805, 0x30c58602cb359113, 0x62d20e1734a15920, 0xab8738ab8aa27199, 0x47ee70afb0f14d1e, 0xcb7cfd08ceaca8d2, 0x1a972359321807bd, 0xef480e9aa10007d1, 0x53a6f438449bd2ba, 0xe1e3674dbe26e551, 0x22372768f7ed554a, 0x1167b49bae3047d5, 0x8b401744c17b5247, 0xb043da8455e26d0c, 0x63ab9f5e26f7fa12, 0xd4e6e5a7a8569316, }, | |
{ 0x8c14c80df61984ae, 0x20900df0c5610944, 0xc648dcff7bcb5b92, 0x7b657daff8a877e9, 0x8202e7294ee2facf, 0xcd2a34e764d7f515, 0x6e33e0fc59d2573, 0x4ff70942ed0765a6, 0xde4517f673399deb, 0x10347ab44c32b95a, 0xde38323e8d7d06ab, 0xa3cd23bf912c36e, 0x94d3ae5f57e84814, 0xa62043fb10ab6190, 0xfd3c2948e9b52b8d, 0x4015e4d9d94bc044, 0x5dd26f5981963277, 0xd775c3e7b94154d, 0xa7d5aad01eab6d52, 0xd2c674797d2a09be, 0x930be917bd9173c, 0x7c45acdb3ae1061f, 0x33b93da0b9ba66ec, 0x7ded0e1d5b6b39f9, 0x9ba11f4484b0d399, 0x452c0b3fbe70a356, 0x79551f6c68696e0d, 0x6917cf75317f4aee, 0xe597319c533190b8, 0x308119154126ba47, 0x53d3fce42679e38e, 0x3066a3355a6cb4bb, 0xaf02e0646f0ff735, 0x8ed970a295f2b46e, 0xdbd4efbd64b2a4d3, 0x59595259c695b49a, 0x594137a23edcee6d, 0x1ac7667deb21e56, 0xf62d377133e4feb, 0xe6a41cdf58d73da5, 0x7ae6495b8b0a7fca, 0x12c6a5a76ba73889, 0x626f942b6334692e, 0xf469bd55266c067a, 0xc8b842715590616, 0xe57b902e0a8bfd3a, 0x16c7490fac8829c0, 0xe018d648b236ab2d, 0x9de26143f6b3a6c2, 0x54b706b99d1c5662, 0xa07d75c271c5fb51, 0xce9253d668cb5bbd, 0x6e9c94145bff2471, 0x21cd008031d45b38, 0xe4f752a4957a1d44, 0xb55ed32471e99bcf, 0x9a77660dc85befe6, 0x1421d48ab93d3223, 0x2c02392591e6fa03, 0x36b3264d72743167, 0xa8cf8fd543a12c85, 0x5a40e65a711f6ed1, 0x77a1b315dbf7d01b, 0x980e1295cddeab83, }, | |
{ 0xe1d47c102f741898, 0xd01ea3a2e8669aa5, 0xc53cdbdc3d2ee918, 0xb386bd1e3e6a0432, 0xf560b7d94e230317, 0xe71fc7e1cea2d5ad, 0xd8125e9a5ead2c44, 0xe5731d44835ceda2, 0x5208b918db651c2c, 0x282c09558052c15a, 0x5ab97421f73f20d2, 0x4d90affd9ad36a64, 0x447ce26f8b69b395, 0x1854fc9f93688a46, 0x679158c785dfb70e, 0xac94c76820e442da, 0x967bbd1a61bc0985, 0x919379cb173a27d2, 0x8aeacc3dd89e4de4, 0x1a344dbc51db743d, 0x40880229ef1c0330, 0xbf8c5f916b26ca8e, 0x28a43f574894073b, 0x6bd34d202d1f4a7f, 0x16ef3c35e7bcc962, 0x5187f1df1441af82, 0xe9483b7ba7d9d9aa, 0x481d5a89b4dd1acc, 0x98a19302d6e30aa, 0x469993835958e999, 0x5c907bdf50414143, 0x31c1f68ff0f55f3d, 0xdd520c59df4a2819, 0xda2ca3237e7b1087, 0xa7a4f9c0b68f1c29, 0x9bbecdf17474bda6, 0xdf32eeeac3ee4b8a, 0x87d97732c8dbb4f9, 0x5918720c830bcea9, 0xccb8a2c0eebd4d01, 0x4c39f8fab8520eea, 0x583c356021ba529c, 0x23587cca4426028d, 0x3ae857686d70a094, 0x4728659cbd66e7d4, 0x7aac3d9c02701261, 0xfff4f2d0616b699e, 0x77c8bd64d02573b5, 0x1cbec04652ff2936, 0xaa1a1931e998e4e4, 0x247ba9854d31f392, 0x3bb4c6ae5c729566, 0x3016b484c818b402, 0x2a13419416697752, 0xedf16dafb4c4ef71, 0x974f56c3304f4c76, 0x42bfc9ec8de6f22b, 0x969c98b09e752488, 0xcd1b7de1f69d5a97, 0x80b8c28a14d28be0, 0xfde0750d0b843fff, 0x6bf2e95795e4331e, 0x8a831d65c90bf545, 0xf1a432965b2d0981, }, | |
{ 0x5a60fc9f9d45bf5, 0x71304ab6397960f9, 0xf1e4cfce5b3b0100, 0x31503b84441ef18a, 0xb83c23d504e819b5, 0x6b6472680fa28dfe, 0x44c4c144c076d448, 0x1bdf77d1176d2998, 0x204846092758ff47, 0x3a93e17261835364, 0xba8986384d297e8c, 0x118f1588a1be9075, 0x4f85a627be03c1fa, 0xde8cbd28b96cd8cd, 0x72b127f4a3fa7189, 0xd6888becac0332e7, 0x3a86ecf11a31d092, 0xca26d87ca7789119, 0xb77021e978803238, 0xafbcad730dfef0dc, 0xdcb9a2e0546c0dc9, 0xd0781e94274af57d, 0x2dba1b54e3445390, 0xbe0a8eaf06402d40, 0x543c93f70f43d036, 0x4bed8679a143eec5, 0xb454039bcadb604, 0xccd313aaa9d71e2f, 0x122e1dd43b71e66c, 0xe7f40001c46921f, 0xfd16fb27c52daf5e, 0xeb54a71cf19a4925, 0xfc4cdfd5aa67d68a, 0x9e6cb0d9ec616b96, 0x3cc0f62b9b008e7f, 0x96934876b46ce81d, 0xa2bdc4e85cefba84, 0x1a92b13b41040a01, 0xc7175202f94ff16d, 0xe647d2ffe7bdf17c, 0x530b78d019a81332, 0xd27532aae26507b3, 0xfb3ed429135609ce, 0xb4c70e47ea378778, 0x32b84276d086d16b, 0xfb3af5a09d38a723, 0x4280778421408e3f, 0xe3abac7e62d8dca8, 0x17dd6e3bc0c81d24, 0x45fce77b8ca9bf20, 0x4f7f80b29fb1de99, 0xd2383e5331470f44, 0x6b065c1adcf43387, 0x75604233fb8ba2f2, 0xde6b370cea1c7cd2, 0x3d2b4eeea0fe1e52, 0x885dbe0e4e7c668d, 0xd3a8088bcdb489ad, 0xcbc7a18e0c2784d9, 0x4594ffccf2451f9b, 0xb01b33deb024d975, 0x5c17ab2e72d45e0, 0x5c4fe989c79822a8, 0x8d5a82b96f253542, }, | |
{ 0xdfab88d179a5fbd6, 0x431448ed546d6544, 0x1a4b96f06e673d2f, 0xc37fc61a2368d70f, 0x8938516c99b34e8d, 0xcdbda4f4ff0b9cb7, 0xa08fda8de44c4ee5, 0xd6fb5c57fc7324db, 0xdb894d99a2646e76, 0x258ee35e3db6de4f, 0x56b20527acf6fd31, 0xa666e0497d3bd6d1, 0x6bc1ecaacd2b78c1, 0xc4c701883b63108a, 0x841197fe5685dbf2, 0xa931bc646960a13a, 0xab19858757a40d51, 0xcdf045011561fc8, 0x6a36d52c9b3dfaf6, 0x883f88569ed5bbd, 0xfd51f30d3c8947b6, 0x5446ccb8e57c422b, 0x84673095e08bc898, 0x9602daf04e2acf91, 0x64743e9954f0ee71, 0x3235e9942f09db15, 0xdbd8a542c8e49e4c, 0x340107157bf86980, 0x6cb12d7bf273047b, 0xa4205641b3673306, 0xc8aad9ebd661873a, 0x9a2a64fb0dcdec44, 0xc884b75d78674495, 0xd5f79dfffebfc229, 0x94dbb14cf6f28c96, 0xee6c7675d426855, 0x7131dc1ff56e14b7, 0xc359a61220084209, 0x9cf9b99860827779, 0xb9ade6285d1f71f0, 0xe9eda21587079f1c, 0xc1e7a13f534a9527, 0xb3c277f5596cd98d, 0x3efc83d0045405d, 0x91a059cf029793cd, 0x1e9df3571e7c6b25, 0x399638b7bc1f35a6, 0x74156f5559cf818f, 0x709f3a623c0dac92, 0x5b83474ffdf8474b, 0x8dc7c79d5d093205, 0x57837ca03c43d291, 0x22c8a6dce618a80e, 0x545093555a948d36, 0x1b70c0d99a5d98d5, 0x3168c8ecc03b4133, 0x8ac39f7bbe901db2, 0x30ebf2aa8d5b504, 0xf89a25b0d15e89fa, 0xcd7bfd42ac621ac6, 0xed400d6d00c4a2ff, 0x38f9880b29305c3d, 0xe6f77ebc21c75ced, 0x5112d5745255be21, }, | |
{ 0xc5ddbf553e010dc6, 0x73d87df583b1dc83, 0x2f6b8a9becd6090d, 0x46f811582120ba76, 0xdf59ea6fda0c431b, 0xbc04a2ae80044d48, 0xb0ad9e4783354a37, 0xea4eebd90f6fa7d3, 0x734ffc346b7750e1, 0xc0074d2de22918ad, 0x73b9502cc20473c6, 0x576634d13c4c023d, 0xc1027f121aa428d2, 0x5c29220ff27b42b8, 0x69196c460e1a870d, 0xb2023b10fb1bb329, 0x6048686400161049, 0xaae160b02acc444b, 0x8b9829d1a6bff459, 0xf8841ee61847b8b, 0xda3e9830adb28f8f, 0x234661a6d850316f, 0xf894bd743dd7d949, 0xcf847fe30627b07f, 0xa065d79e79438f6f, 0x7e870507ec7c09fc, 0x4ea5ca89da2e3bc4, 0x118a42b6b21102ba, 0xc22fb3dd07408e60, 0x943c34df13884287, 0x8b7a235e74b2ae8b, 0x805ec78e1f2b55e6, 0xf2a2c2392374127f, 0xc2a6db9b58b7ad8f, 0xcd31aae73eb249ee, 0x79a993e49c9e81ce, 0xf2d99c4b09b702bc, 0x3292db3c02eb2172, 0xfa885e1a9a02f908, 0xc527947efc56696f, 0xd2a51eaa54b53878, 0xefccbb90f06fa951, 0xa4496b15776b1afe, 0x3c462edea7c1d951, 0x892aeda94ee332e7, 0x216225afeba70edd, 0x57f4093322c6ad36, 0x7f1f6c39bf05d26, 0x29fdd1fb65561c59, 0xa1b4268fb24b5763, 0x6d429da742a473a6, 0x773f6c336feecd64, 0x5e99133c2c5dc1b7, 0x4e8352d46ece2f0f, 0x82ce4eadbc71e3d9, 0xa4a8319529e40855, 0x972e6d56d2547cea, 0x9e9e759c4c5c6ee, 0x1382dfef23756a16, 0x5e7456de18aa6fd9, 0x29c57093b197f3fa, 0x5efa70a8bfe4c2af, 0x6cbe5dcf6962b815, 0x9a25feb53003b104, }, | |
{ 0x985e09f19b32185, 0x79fedb0bbe2027d1, 0x9dbdb8136778f47f, 0x91846e20b2ac0444, 0xbbe052b9b70b9f9c, 0x4b3f7e5d6b63ac18, 0xd271d1ef80104ed8, 0xdf2d71eb5200ffb0, 0xace82b1bd60c4437, 0x888c58366d63c7a2, 0xb4a37da09186e527, 0xcb749bbb051f8ed1, 0x811af27f2d13d014, 0x7cf08aacd0e7a0cc, 0xc9071b77694507d3, 0xde41ab41e3322cd3, 0xe813a429e980e050, 0x2b4328b00d3c210b, 0xe748b912c4b769dd, 0x6e07a72391b315dc, 0x8b1dba5dacf0008a, 0x5612e096ef8c2892, 0x572a76ae405671d6, 0x7ff09110d7e0a9b4, 0xce77cec695569b67, 0xb1913c97750448a1, 0x4f7cbb4836e8df26, 0x7beb69d8883d524f, 0xac56b60660b73fc6, 0xc48553cb5014483b, 0x35fea3e715bd2f5, 0xd49f5e2ef43068cd, 0x9a1f68dd3d4b6be9, 0x853df1bdc39bc9cb, 0x8e1d15625196cc1b, 0x9a562a35414795a6, 0x1ec6efa42da3c396, 0xd83cdcd4d16f863f, 0xd38f25dcf51b65cb, 0x2d8ea4387d339f1a, 0xaf003a62656c7c2d, 0xb3cdf22c281e5a7c, 0x68fb22f7e7233852, 0x5e1e1bb64fd32e85, 0xe6b05d2ecba26517, 0xa057bd953f88c650, 0x61037f9af6e56204, 0xbce74b4ba7a1558b, 0x413c6ff8042eccf2, 0xeb0dfb531552085d, 0xbb2f229582ca0a43, 0x3949222f928825fa, 0x5ce14eed5f883384, 0x1f1f284a9fed73ff, 0x511bb6bcee5ccf61, 0x2bd4b07332490626, 0x5ac05465123548f3, 0xad539e9e8f277396, 0x40c6388946e15630, 0x11e49a4e26478d40, 0xed7e7c761f13af21, 0xec75616ed568d6a5, 0xe54d41cf8be8aa8f, 0xe0d5310e7a2bec33, }, | |
{ 0x708fee35573d06da, 0x262f1538e693e02a, 0xa5790774eb111c39, 0xf137fb5ba2417f17, 0xc137ba6ef49f8620, 0xd93d053fcb738636, 0x9e441589dae7ca11, 0x1931d37401c211b8, 0x857700812827d3de, 0x2f43fea5202beade, 0xa4e659ae3c87bbbd, 0xeeabcb3e7f969d6f, 0x979665e6d7c8eeb2, 0xae71067e245ccff2, 0x4453eb11cc3e219, 0xbd8c9dd271e52598, 0x976b8dd4758ac9f1, 0xa452e0fc1d884433, 0x1fa5c182bff42a30, 0x160d639a6e998960, 0x7920ccccd0310279, 0x122c06375c0bdaab, 0x951f4d9b2a02a3e5, 0x52e1996303b3b58a, 0x15958b32096b90ba, 0xe1688953c6ab4945, 0x4fc520c00c038c6c, 0x2dbb5ba0e43f14e7, 0x477b91e8bb678a29, 0x8c2a7bd10f2da1bb, 0xbe7a62d177fb927, 0x6cf4ca5811ec27eb, 0x192cb5af94d4ab91, 0xff959647d4852a14, 0x6c3a67d4e82b96fb, 0xf7d57b35a1644a4a, 0x45c4e732680289e5, 0x618d187bb2423255, 0x61ab6a4f39b819cd, 0x1655fcec329727a4, 0x895823e7ac61073f, 0x7dbd607e70e3c1d7, 0x19f95fedfcd71a99, 0xcb79166a166459fb, 0xb5826479d9120394, 0x8b74b2f87aaccd1a, 0xe7c6936d785d2fe0, 0x56c78dd765862d5a, 0x10931fc941ee9a43, 0xa699c72ab619c53c, 0x3ce4d67fd32ada54, 0xa12a590617e810c6, 0xe739c7aeb77d0695, 0x990904d39e2d9dd1, 0x778762fa6e1cdb01, 0x75337373987e4de4, 0x2e5cd0db85e1126a, 0x4809728a2e474b09, 0xd5af7082ea94b57d, 0x2ceefc58e738b01a, 0xa185ab49d19b1429, 0xb049fd06275accc5, 0x49c8c6c3239dfa60, 0xefe30afa5d41b39d, }, | |
{ 0x7c8d35ea653b028c, 0xd27fcd2438b8fd70, 0x69e91c2677c1e3d2, 0xde615c95fd04e6fd, 0xea5baf5b2554b353, 0xe865552c46769bef, 0x3574c8fe4e7044e0, 0xf2c67c732e1b3ac9, 0x36de14e8a5a0646c, 0x5b51a5d8d36a010a, 0xf953f7fd65098d7b, 0xfff35c5bfe094cb9, 0xa2e3fdfe73f7f92b, 0x67a4849cb1426f73, 0x1ad46d0d53d01b74, 0xa14219a8928b8be6, 0xee4d983985fa2032, 0xad96d3c3cd2e91b5, 0x8605499b92f440c0, 0x4f9dbb14bb585366, 0x444ae2298aafff19, 0xea8e472ee1b879f4, 0x1518b2636d9f1663, 0x8eebd5bcf6aa2fb5, 0x16ae679ac8ab0ea1, 0x826060451d423adc, 0x4e0db5d3ff9cf24e, 0xee477f4ed4ea9c34, 0xfb1f88d17c584202, 0x4fb01ef24ad9341d, 0x2d19eb0b4e8ba64e, 0x487562af34c306a1, 0x7fc9240f26fede15, 0x178162d2838ca36a, 0x2992a49d63a99ca7, 0x58d91e324ef3d036, 0x3f06df8f13e0660f, 0x999aef3f89f06fb9, 0x83c1c1c97a08a755, 0xa2244a72b9c8a, 0x82740bf5748d3a1, 0xe7cb861472a3a05, 0x3616b9bfcc5bad3a, 0xde5093191a5a7081, 0x67cfa38907e41013, 0x54754fd2f5eee2c9, 0x9474a3a46404a4f4, 0x53770f3c4bd19c58, 0x1bece7e7eef4d807, 0x9706dfbf68443dc1, 0xa1996c4666847e3c, 0x36cf6ff59b11c931, 0xf62bd07a29258fb8, 0x1a69e33d1f187d3e, 0xbce859dfbf59c0fd, 0xe018fc51764fb8c9, 0xf3c9ccf79d812ba, 0xa9c9bc6f0e539957, 0xec67987c9c5266c8, 0xb06ec0bb084bdb2c, 0xcdc3976cecbeb44f, 0x9f67d3a374512d67, 0x56f19a17568958c8, 0xbf010c645d014797, }, | |
{ 0xf49414eacfe0141a, 0xbd51eca5ea37f165, 0x5a7692329da41afa, 0x2f0d87b33ed5f153, 0xfa4972b3390f4df8, 0x93b3e130ca8bc4e8, 0x458e02565fcc17de, 0x235cdeeaeb4993fb, 0xba7acd78654a07c3, 0x16aeb28d321983be, 0x2607c70efbd9be9a, 0x974a01b53f0ab949, 0x476eabb1464375a8, 0x4a2dddfb8d4827b9, 0xafbc95874516756b, 0x65cdfccb65ba0f19, 0xf757b4c42e13788, 0xd9218865a56e5871, 0x15f5ed4a2001ad6b, 0x47739a5980dc5eab, 0x6814bd661394deb, 0x80f41377d8430b23, 0x44b223fbe854a328, 0x4b69d41b5e9f870, 0xc6d9b53e3e84788a, 0x5f542ee82825052, 0x44843edc1e4a802f, 0x15a905f67cb55342, 0x1623a310395ced83, 0xe87e92c5fdcb76c3, 0x178b88ce32235df9, 0xe17bfab0edbb9997, 0xd16a2ef81613ba4d, 0xfe0e2a26ffe70ede, 0x58321bb2ebb4f9a1, 0xbac1bb62b996296e, 0x80d2068973391e5f, 0x5371f5afca3ad0d5, 0x47ffc81a87e2a11b, 0xa7ff95b953e0be6d, 0x325b715b86c40621, 0xca4874dcae30cfde, 0x448a4217ea35762, 0xef96f9a82964272a, 0xff1a20f4ce0df576, 0xafb2a8e824511402, 0x990a901ea0beeaff, 0x6fb505dd89af8788, 0xfcfe7d3acfa8ad5, 0x73e5ff12a3e251c2, 0xb2a435324e459a22, 0x84da675fb3cff5c, 0xfdbbb57504cd6f2e, 0xc4826d505a36e1ca, 0xf405fab48b4d561a, 0x55422375bf241958, 0x4d95690146d2bc34, 0x795018fc54dd05cc, 0x983ff9ab2e31c1e7, 0xed7fd97abec953cb, 0x5b3c6c3215ce1877, 0x63bc327010c5a593, 0x354c08d04590a008, 0x4828ecc848061395, }, | |
{ 0x8e033b03966ac833, 0xb17fc47e477444e6, 0xbfed64641e6f9bed, 0xf8730345fd3f0ec6, 0x1506f7250d4369ab, 0xc6bb9058e134f1b0, 0xc5f453f715f1b5a0, 0xd55991085b036dd9, 0x3b72873ed27b95a4, 0x7a0c53e23661a1c8, 0x78dfc8a5cab509b3, 0xf83e5c0a0573915e, 0xd687c6460b653460, 0xe0ebff8ab21bab5f, 0x4a285ce94e03a59e, 0x4979dc209e5d053c, 0x8767987f06ecad79, 0x75c7935bc65197c, 0xe7faffdb5396b00d, 0x3f93bb2a4a322943, 0x6e6715d272f7585b, 0xeb1bc0151c9b6644, 0x1c631d95e7a1dd2b, 0xee15b5848080f201, 0xd7fcee69bf278fb, 0x45d5e99e513a8e7d, 0x3ded716c660625c1, 0xadd1574140df0ab5, 0xef575030cb1cbe52, 0xfa274ca6557e0c85, 0x87d8f2ef062bb242, 0xecc8b5ef0320d4f7, 0xaadd3f82f181d605, 0x8090810ce247d761, 0x8f3893f0482a771e, 0x9280fd02187285c2, 0x99dddc48aeb67c8d, 0x98dea536c71fc319, 0x9d6b2a37e912aced, 0x7a8b49d3fdbf767e, 0x1d531630daee75a7, 0xcfbbcf9579b45443, 0x97d2e7e212ba83b4, 0x6be6f5e40b0d2471, 0xa5c3f8f56a2ff928, 0x556f02765db5d75a, 0x8a016e8f59923a26, 0x66494d0f6a66d242, 0xcefcf09b80950834, 0x93d78a5d3e949682, 0x73040c64d33e5874, 0xe4bdd8e851a95b89, 0xfa144738146405b6, 0x28704783a8dae845, 0xf960331788dfdada, 0xd43ac58991469198, 0x3e97031d5fc1650d, 0x8a2e234c5969043f, 0x28c58847b91193f9, 0x4763a3865cd32d07, 0xfe9a6aa213daf553, 0xce857e6d712dcf51, 0x4f9f6dbb1a6571d1, 0x1771e1ca7e572fd3, }, | |
{ 0x272f082368938347, 0x8791b73ae37f81ac, 0xcd617856de561865, 0xed3ec9e0b4debf12, 0x8cc4bb6a1b94d240, 0xe963b571b64d70b5, 0xeed80b6ed7cb9dee, 0x498cc22c22e3755d, 0x7002a2db8ad20858, 0xe14754611e49977d, 0xd91f0a6ceb67c612, 0x389c878dc99ac755, 0x6d474c106eea58ac, 0x44a3b2a720f7c700, 0x848586f6768e9fd3, 0x8a93e1e37b0cd88c, 0xbb474e32fb19716c, 0x5aef286083cb0b40, 0x93db71a63c1ea19d, 0x56b22048640d1dae, 0xca1be64422c38ac2, 0x6623d75caf443b41, 0x38079e0c6956c10d, 0x328ac89bc47e3f68, 0xa9a666b255cf73f8, 0xdfc35fef0c059ff8, 0x49adb7ae0a4b451c, 0x9aa621b31e4e8f93, 0xbb00c0935471e58e, 0x8378f22b10059c2a, 0x28803786db345350, 0xc0bbfb3005b2f7db, 0xd7936166d74e453d, 0x1eb909a8f891307f, 0xbc68e3a2ae0636ae, 0x7a0d812f788517fd, 0x71d4df0d2a661e21, 0x7db46fc4677c3a3d, 0x60d9da0401af51bf, 0x91174e53987e21a1, 0x7ae552da602d96f3, 0x5bce4d6d377a85ee, 0xa38c1f660effe103, 0xa3434424c3c47a0, 0xcc1d2e570215fb7e, 0xf628a67b95ec663b, 0xa88e5c0a12b9d02e, 0xb3e0742152084374, 0xa2696c8fe996c9fd, 0x8c3d08e8e0836ffb, 0xa7608c316215ad8b, 0xd0ec57ed959db05a, 0x77b5e30e8638587c, 0x5091b9b4841cd7d6, 0x3744e23adcbaa54e, 0xa4688281ba1c268b, 0xab332c6eb6a3e0ba, 0x94b161d5fb21dcaf, 0x94c3c0e33925b931, 0xc86d0700b7a37491, 0x20156e56257e489d, 0xc25d4d4ea5036e40, 0xcea86bc417e69eb, 0xd9890895a353f558, }, | |
{ 0x4600ca890e39b338, 0xe20a2861897baa29, 0x892057743af6581e, 0x79c9c16d26f41da3, 0x303f1fa01df8c96e, 0x4b2b9d8cfbcd6e90, 0x454a99af8cb9537e, 0x6c0c571edf358b6f, 0x4231658c78110198, 0xfb4af6c1b004199a, 0x4f88b67ec9d78f10, 0x8440dbe42a2a840, 0x96c948a8e8bb86b1, 0x75d339ba46a2e1e7, 0x4648428ce88875cf, 0x15602960d8ab6f6d, 0x1aecd8b2d5bd44f, 0xa7a31a7b1dca85fc, 0xc281c8936f137f11, 0xcdc74643230f501a, 0x46e61a3fddccf5f5, 0x7439dc8762178e27, 0x71956fb3fecd7828, 0x60c37bc724759878, 0xa6199423b215f03e, 0xbe7c90c32ac3d85d, 0x4e739145a465ba0d, 0x5697c4a51f2835fb, 0xa119809125b91184, 0x3725b2cab89e149a, 0x7b7ecdec85392207, 0x88d1eb900087b996, 0x7dfddb3f204f8f6b, 0x816d4c6829ac22e3, 0xd8941c571c759fcf, 0xacf8d003c31d9a48, 0x580fc7e877fe398b, 0x1decfb5e8b27f0e3, 0x296bb72666dcf5ca, 0xeb92a4d0a6f40398, 0xb925bb8013d8e2cb, 0x9842e298157c92d0, 0x8d4adbf152302e3, 0xc2550242342d189f, 0xdca5582004ba4d73, 0xf003d546de8acfaa, 0x9c238423469a7955, 0xd93618f591a8df69, 0x466fcba4acb78ed5, 0x53cd8cf1e3c3d9b5, 0xdb18ff96e5933c6e, 0xbb9a0a3731c168bf, 0xdc113c2da8faa04e, 0x35800a8a30097db, 0xf4d7b57d05e58425, 0x6cf985ed6a0a7d02, 0x3bd4b90f26ddf04b, 0xe8a89d366bed1604, 0xab0849bd2633a834, 0xd8b82fb0388912c8, 0x5846698ceae1b318, 0x3d2494bceddbbec3, 0x987de44bc67c032c, 0x4c6ae1c15745dfb3, }, | |
{ 0x2b4a0d99924f167d, 0x96eed71797a7bad3, 0xfec0d2d065b509dc, 0xaadf733ca5845223, 0xe2d7d7038fc52714, 0x44586874811e2bfc, 0x1c6520c32d050224, 0x3ea62a00e8d38477, 0x3e0f5ce23ac1101, 0xebf809b753bf249c, 0x65247be6f0610bdc, 0x9a1014b017270d87, 0x2ce66baded768af5, 0x3d34c11622c880f6, 0x5ff22bb92dd50f16, 0xf6201da4b9a2604e, 0x44569ad2f9d64db2, 0xebdfc59238df93e, 0xb07c9e708434d24, 0x53d26bd38c9b28af, 0x4fbcf7a03848ed6a, 0xe731a2c7f592f2af, 0x4ec930ff112bcb28, 0xca10bb2a7cb3e351, 0xb96183e36841ceb3, 0xe8f763ac723214ae, 0xbee8a3de68c02f1, 0x2d7eede8267040b5, 0xe4d47a85dbbfb69a, 0xc4186e3cc244edc2, 0xc95e67824d4f2610, 0xba315a14a666951b, 0xf64f25d592545543, 0xd32560d08cd80704, 0xc40cbb1afc619ce5, 0x198b86bfd6af4702, 0x9bc19ce0b31e541f, 0xcf6316da906a56ed, 0x45066aec95a1a6b1, 0x7abb8c63cb04d508, 0x1fd1fa5a082e3c8d, 0x19761349a4309744, 0x3bb6a025176c3139, 0x1708ffd4a3d35077, 0x97c23ca0521b42d7, 0x2ae855c93166dada, 0x6bceb341895ffb4d, 0xf2e18458e33ce813, 0x38d413ab85e3d4fa, 0xc069b1825592687d, 0x94d57793180a88ad, 0x34fb11c184d954fa, 0x4b4cd48a4a9ea21c, 0xfe0f283eaecef120, 0x1bda5f527943d38d, 0x83419a710054cf8f, 0x1b138d7adfac774c, 0xd434b8437c5d86e0, 0x1572427cd58116ba, 0x89bf3bf09247ba27, 0xe1d5d808b2ce13dc, 0x16b60ff40961ade8, 0x3d1ad518138e688e, 0xbcb1559614a45fe8, }, | |
{ 0xb463fb4253e6087b, 0x85cd5e060ecd1786, 0x21f35a35e289a5d3, 0x912d826d6bbafc2b, 0xb38bf976b9c26b0c, 0x964db517ef2a1476, 0x3e7033cf06429b46, 0xb71ed67db3095985, 0xaec26d210fda4160, 0xf6e51c3404e4e758, 0xea37bb6c51ef803b, 0xcfb70678bd794e5, 0x507c891b07501e44, 0xb095f43071712b32, 0xf099ef63d7a80858, 0xb7f809ef0f5e6073, 0xd58ebdb3c19d3f07, 0x8fb490a439530c7, 0x598839f37f5d9a59, 0xc8ef56f0d99cd604, 0x78b3ae9f183f2cce, 0xd418821cd6d0df03, 0x7950e85348504fe9, 0xac11f1e9ebae24d2, 0x1120df6c9194216f, 0xfd7165163597cd0b, 0x13a45381a073f944, 0xd4e62404170841e6, 0xe9d22c5fd4a647f, 0x5116347a9f81e22, 0x9b99e9110610423, 0xb30f3bcb52a98c37, 0x604fc0542a2fdc32, 0xe6cc61a80d714d1e, 0x31e78bf0626a8c40, 0x672c713b45c99df6, 0x5dd41a47c033c2ed, 0xa3444a2e60b7e6da, 0xb7b849b7f6589fd0, 0xfe25a713bb649d22, 0xe54119581fce4679, 0x4a2808ee0c9dd2a7, 0x5b3a23ebb892b921, 0x1d109708b713a320, 0xe9c304cdc8b7cf18, 0x95be961919ec17de, 0x7f766b667d4b41e6, 0x67b9e4c861781157, 0x2d1893dad92ce12, 0x8e007c606e516209, 0xe963548821c23893, 0x51e7a5499dcc514f, 0x4920fa023bbb3f44, 0xc867cbc92ee1e2fa, 0xf66b481318cb083, 0xc6a74ed599bc1a7, 0xc97d66687819388d, 0xda92d340d2ff3f5f, 0xca20a6162643045d, 0x1cd64b6e1d2d2ac8, 0xa51a0b0965608ce2, 0xe8a80a35f4a76155, 0x688592ea54556d0c, 0x165bc54a808ccc23, }, | |
}; | |
constexpr u64 ZOBRIST_CASTLING_RIGHTS[NCASTLING_RIGHTS * NCASTLING_RIGHTS] = { | |
0x27a6232b09e17a7c, 0x446ec80dd510973, 0xc714579ab1aa53e0, 0x169cc5508ae0ac1b, | |
0xf05aeca1e54c3c8e, 0xbb0c11bd2c9152e7, 0x9a43e5c630ff989a, 0xd2d00416fe8e7c37, | |
0xd306d760f5457c26, 0x53196a88ebded707, 0x38c7b1e83ec50f39, 0xdc089734a7f12b07, | |
0xc1b5e792f0ff738b, 0x4b4c7e6a10d96839, 0x4aca9be5bcf07993, 0x85540db6f23f66f2, | |
}; | |
// NSQUARES + 1 for no ep square. | |
constexpr u64 ZOBRIST_EP_SQUARE[NSQUARES + 1] = { | |
0x6a83bc36f0495ef4, 0xc85f640b09faf6ab, 0x6147944e9b24699d, 0x6291014eeab4c810, 0x385e9ce5bad2aa07, 0x9289dd2f3b369ac, 0xffc65340107e5d5, 0x8a27e0fd0a838aad, | |
0xdc2544201e621658, 0xb00daea48e827644, 0xd92ddac513d63745, 0x6065868b8679daf9, 0x55b2dfb01c995ebd, 0x4cf075e6b943a18, 0x23bb15b9907bb3d7, 0x2168b7ccb834744c, | |
0x9a57b96a6b7a7e55, 0xea71efae40d96a26, 0xd1cb1ba6537ec782, 0xc259a753f4f4fef4, 0x17e9af670c158ee3, 0xabad004cd9dfc2e1, 0xd4a373084b08f146, 0x5609f6df88be155c, | |
0x85580595488cd7bb, 0xf027b3e84bcaf972, 0xe0ed76e236aa49f4, 0x81025b98ee8a206c, 0xec9fbebc926d9588, 0xc5dd03ccc6b2420b, 0x66d7200d9e9cf62d, 0x7421b83194233d2a, | |
0x37fb96fff362370, 0xbe90de574440c594, 0xd75cab6b9892862f, 0x3c27926b182d2c6d, 0x139d5e59bdd6e548, 0x840daad34fbd20e3, 0x81232d93327fbdc1, 0xb31beca25e05068c, | |
0xf5baf2cef1d12b2b, 0x8c0866f295dfbebd, 0xbeaea688a8168559, 0xfff3784ab68dfe0, 0x63ddf8e753c95933, 0xb39e1e6e3b68f89c, 0xf9e230b290e70a38, 0xf05c05fe9bab000b, | |
0xd41d68cd1f789e21, 0xe39eb9e76b90810c, 0xf628ceb34f8aa125, 0x92c71291b5ce8350, 0x29fcec256d7203a, 0x7cfd4d800b133e5, 0x63d994163c375b63, 0xf2a2452977c6ed20, | |
0x580a92cdae7327d8, 0x2b2265f5f534e9b8, 0x1bfc55cf26a7baab, 0x39634286cc2534ce, 0xaaf196335586d1c9, 0x261b66fbdbfbd7e1, 0x68f248327514d19e, 0x9e6a49c027a27b3, | |
0x1026375f03408983, | |
}; | |
constexpr u64 ZOBRIST_COLOR[NCOLORS]= {0x750ee814dc0e551c, 0x1026375f03408983}; | |
/* ------------------------ DEFINE MAGIC ATTACK TABLE CONSTANTS ------------------------ */ | |
namespace tables { | |
namespace { | |
constexpr usize ROOK_TABLE_SIZE = 4096; | |
constexpr usize BISHOP_TABLE_SIZE = 512; | |
constexpr array<i32, NSQUARES> ROOK_SHIFTS = { | |
52, 53, 53, 53, 53, 53, 53, 52, | |
53, 54, 54, 54, 54, 54, 54, 53, | |
53, 54, 54, 54, 54, 54, 54, 53, | |
53, 54, 54, 54, 54, 54, 54, 53, | |
53, 54, 54, 54, 54, 54, 54, 53, | |
53, 54, 54, 54, 54, 54, 54, 53, | |
53, 54, 54, 54, 54, 54, 54, 53, | |
52, 53, 53, 53, 53, 53, 53, 52, | |
}; | |
constexpr array<i32, NSQUARES> BISHOP_SHIFTS = { | |
58, 59, 59, 59, 59, 59, 59, 58, | |
59, 59, 59, 59, 59, 59, 59, 59, | |
59, 59, 57, 57, 57, 57, 59, 59, | |
59, 59, 57, 55, 55, 57, 59, 59, | |
59, 59, 57, 55, 55, 57, 59, 59, | |
59, 59, 57, 57, 57, 57, 59, 59, | |
59, 59, 59, 59, 59, 59, 59, 59, | |
58, 59, 59, 59, 59, 59, 59, 58, | |
}; | |
constexpr array<Bitboard, NSQUARES> KING_ATTACKS = { | |
0x302, 0x705, 0xe0a, 0x1c14, | |
0x3828, 0x7050, 0xe0a0, 0xc040, | |
0x30203, 0x70507, 0xe0a0e, 0x1c141c, | |
0x382838, 0x705070, 0xe0a0e0, 0xc040c0, | |
0x3020300, 0x7050700, 0xe0a0e00, 0x1c141c00, | |
0x38283800, 0x70507000, 0xe0a0e000, 0xc040c000, | |
0x302030000, 0x705070000, 0xe0a0e0000, 0x1c141c0000, | |
0x3828380000, 0x7050700000, 0xe0a0e00000, 0xc040c00000, | |
0x30203000000, 0x70507000000, 0xe0a0e000000, 0x1c141c000000, | |
0x382838000000, 0x705070000000, 0xe0a0e0000000, 0xc040c0000000, | |
0x3020300000000, 0x7050700000000, 0xe0a0e00000000, 0x1c141c00000000, | |
0x38283800000000, 0x70507000000000, 0xe0a0e000000000, 0xc040c000000000, | |
0x302030000000000, 0x705070000000000, 0xe0a0e0000000000, 0x1c141c0000000000, | |
0x3828380000000000, 0x7050700000000000, 0xe0a0e00000000000, 0xc040c00000000000, | |
0x203000000000000, 0x507000000000000, 0xa0e000000000000, 0x141c000000000000, | |
0x2838000000000000, 0x5070000000000000, 0xa0e0000000000000, 0x40c0000000000000, | |
}; | |
constexpr array<Bitboard, NSQUARES> KNIGHT_ATTACKS = { | |
0x20400, 0x50800, 0xa1100, 0x142200, | |
0x284400, 0x508800, 0xa01000, 0x402000, | |
0x2040004, 0x5080008, 0xa110011, 0x14220022, | |
0x28440044, 0x50880088, 0xa0100010, 0x40200020, | |
0x204000402, 0x508000805, 0xa1100110a, 0x1422002214, | |
0x2844004428, 0x5088008850, 0xa0100010a0, 0x4020002040, | |
0x20400040200, 0x50800080500, 0xa1100110a00, 0x142200221400, | |
0x284400442800, 0x508800885000, 0xa0100010a000, 0x402000204000, | |
0x2040004020000, 0x5080008050000, 0xa1100110a0000, 0x14220022140000, | |
0x28440044280000, 0x50880088500000, 0xa0100010a00000, 0x40200020400000, | |
0x204000402000000, 0x508000805000000, 0xa1100110a000000, 0x1422002214000000, | |
0x2844004428000000, 0x5088008850000000, 0xa0100010a0000000, 0x4020002040000000, | |
0x400040200000000, 0x800080500000000, 0x1100110a00000000, 0x2200221400000000, | |
0x4400442800000000, 0x8800885000000000, 0x100010a000000000, 0x2000204000000000, | |
0x4020000000000, 0x8050000000000, 0x110a0000000000, 0x22140000000000, | |
0x44280000000000, 0x0088500000000000, 0x0010a00000000000, 0x20400000000000 | |
}; | |
constexpr array<Bitboard, NSQUARES> WHITE_PAWN_ATTACKS = { | |
0x200, 0x500, 0xa00, 0x1400, | |
0x2800, 0x5000, 0xa000, 0x4000, | |
0x20000, 0x50000, 0xa0000, 0x140000, | |
0x280000, 0x500000, 0xa00000, 0x400000, | |
0x2000000, 0x5000000, 0xa000000, 0x14000000, | |
0x28000000, 0x50000000, 0xa0000000, 0x40000000, | |
0x200000000, 0x500000000, 0xa00000000, 0x1400000000, | |
0x2800000000, 0x5000000000, 0xa000000000, 0x4000000000, | |
0x20000000000, 0x50000000000, 0xa0000000000, 0x140000000000, | |
0x280000000000, 0x500000000000, 0xa00000000000, 0x400000000000, | |
0x2000000000000, 0x5000000000000, 0xa000000000000, 0x14000000000000, | |
0x28000000000000, 0x50000000000000, 0xa0000000000000, 0x40000000000000, | |
0x200000000000000, 0x500000000000000, 0xa00000000000000, 0x1400000000000000, | |
0x2800000000000000, 0x5000000000000000, 0xa000000000000000, 0x4000000000000000, | |
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | |
}; | |
constexpr array<Bitboard, NSQUARES> BLACK_PAWN_ATTACKS = { | |
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | |
0x2, 0x5, 0xa, 0x14, 0x28, 0x50, 0xa0, 0x40, | |
0x200, 0x500, 0xa00, 0x1400, | |
0x2800, 0x5000, 0xa000, 0x4000, | |
0x20000, 0x50000, 0xa0000, 0x140000, | |
0x280000, 0x500000, 0xa00000, 0x400000, | |
0x2000000, 0x5000000, 0xa000000, 0x14000000, | |
0x28000000, 0x50000000, 0xa0000000, 0x40000000, | |
0x200000000, 0x500000000, 0xa00000000, 0x1400000000, | |
0x2800000000, 0x5000000000, 0xa000000000, 0x4000000000, | |
0x20000000000, 0x50000000000, 0xa0000000000, 0x140000000000, | |
0x280000000000, 0x500000000000, 0xa00000000000, 0x400000000000, | |
0x2000000000000, 0x5000000000000, 0xa000000000000, 0x14000000000000, | |
0x28000000000000, 0x50000000000000, 0xa0000000000000, 0x40000000000000, | |
}; | |
constexpr array<Bitboard, NSQUARES> BISHOP_MAGICS = { | |
0x0002020202020200, 0x0002020202020000, 0x0004010202000000, 0x0004040080000000, | |
0x0001104000000000, 0x0000821040000000, 0x0000410410400000, 0x0000104104104000, | |
0x0000040404040400, 0x0000020202020200, 0x0000040102020000, 0x0000040400800000, | |
0x0000011040000000, 0x0000008210400000, 0x0000004104104000, 0x0000002082082000, | |
0x0004000808080800, 0x0002000404040400, 0x0001000202020200, 0x0000800802004000, | |
0x0000800400A00000, 0x0000200100884000, 0x0000400082082000, 0x0000200041041000, | |
0x0002080010101000, 0x0001040008080800, 0x0000208004010400, 0x0000404004010200, | |
0x0000840000802000, 0x0000404002011000, 0x0000808001041000, 0x0000404000820800, | |
0x0001041000202000, 0x0000820800101000, 0x0000104400080800, 0x0000020080080080, | |
0x0000404040040100, 0x0000808100020100, 0x0001010100020800, 0x0000808080010400, | |
0x0000820820004000, 0x0000410410002000, 0x0000082088001000, 0x0000002011000800, | |
0x0000080100400400, 0x0001010101000200, 0x0002020202000400, 0x0001010101000200, | |
0x0000410410400000, 0x0000208208200000, 0x0000002084100000, 0x0000000020880000, | |
0x0000001002020000, 0x0000040408020000, 0x0004040404040000, 0x0002020202020000, | |
0x0000104104104000, 0x0000002082082000, 0x0000000020841000, 0x0000000000208800, | |
0x0000000010020200, 0x0000000404080200, 0x0000040404040400, 0x0002020202020200 | |
}; | |
constexpr array<Bitboard, NSQUARES> ROOK_MAGICS = { | |
0x0080001020400080, 0x0040001000200040, 0x0080081000200080, 0x0080040800100080, | |
0x0080020400080080, 0x0080010200040080, 0x0080008001000200, 0x0080002040800100, | |
0x0000800020400080, 0x0000400020005000, 0x0000801000200080, 0x0000800800100080, | |
0x0000800400080080, 0x0000800200040080, 0x0000800100020080, 0x0000800040800100, | |
0x0000208000400080, 0x0000404000201000, 0x0000808010002000, 0x0000808008001000, | |
0x0000808004000800, 0x0000808002000400, 0x0000010100020004, 0x0000020000408104, | |
0x0000208080004000, 0x0000200040005000, 0x0000100080200080, 0x0000080080100080, | |
0x0000040080080080, 0x0000020080040080, 0x0000010080800200, 0x0000800080004100, | |
0x0000204000800080, 0x0000200040401000, 0x0000100080802000, 0x0000080080801000, | |
0x0000040080800800, 0x0000020080800400, 0x0000020001010004, 0x0000800040800100, | |
0x0000204000808000, 0x0000200040008080, 0x0000100020008080, 0x0000080010008080, | |
0x0000040008008080, 0x0000020004008080, 0x0000010002008080, 0x0000004081020004, | |
0x0000204000800080, 0x0000200040008080, 0x0000100020008080, 0x0000080010008080, | |
0x0000040008008080, 0x0000020004008080, 0x0000800100020080, 0x0000800041000080, | |
0x00FFFCDDFCED714A, 0x007FFCDDFCED714A, 0x003FFFCDFFD88096, 0x0000040810002101, | |
0x0001000204080011, 0x0001000204000801, 0x0001000082000401, 0x0001FFFAABFAD1A2 | |
}; | |
[[nodiscard]] constexpr Bitboard board_edge(Direction D) { | |
if (D == NORTH) return MASK_RANK[RANK8]; | |
else if (D == SOUTH) return MASK_RANK[RANK1]; | |
else if (D == EAST) return MASK_FILE[HFILE]; | |
else if (D == WEST) return MASK_FILE[AFILE]; | |
else if (D == NORTH_EAST) return MASK_RANK[RANK8] | MASK_FILE[HFILE]; | |
else if (D == NORTH_WEST) return MASK_RANK[RANK8] | MASK_FILE[AFILE]; | |
else if (D == SOUTH_EAST) return MASK_RANK[RANK1] | MASK_FILE[HFILE]; | |
else if (D == SOUTH_WEST) return MASK_RANK[RANK1] | MASK_FILE[AFILE]; | |
return 0; | |
} | |
[[nodiscard]] consteval Bitboard empty_board_rook_attacks(Square square) { | |
return MASK_RANK[rank_of(square)] ^ MASK_FILE[file_of(square)]; | |
} | |
[[nodiscard]] consteval Bitboard empty_board_bishop_attacks(Square square) { | |
return MASK_DIAGONAL[diagonal_of(square)] ^ MASK_ANTI_DIAGONAL[anti_diagonal_of(square)]; | |
} | |
[[nodiscard]] consteval array<Bitboard, NSQUARES> generate_rook_attack_masks() { | |
array<Bitboard, NSQUARES> rook_attack_masks{}; | |
for (Square sq = a1; sq < NSQUARES; sq++) { | |
Bitboard edges = ((board_edge(NORTH) | board_edge(SOUTH)) & ~MASK_RANK[rank_of(sq)]) | | |
((board_edge(EAST) | board_edge(WEST)) & ~MASK_FILE[file_of(sq)]); | |
rook_attack_masks[sq] = empty_board_rook_attacks(sq) & ~edges; | |
} | |
return rook_attack_masks; | |
} | |
[[nodiscard]] consteval array<Bitboard, NSQUARES> generate_bishop_attack_masks() { | |
array<Bitboard, NSQUARES> bishop_attack_masks{}; | |
for (Square sq = a1; sq < NSQUARES; sq++) { | |
Bitboard edges = board_edge(NORTH) | board_edge(SOUTH) | board_edge(EAST) | board_edge(WEST); | |
bishop_attack_masks[sq] = empty_board_bishop_attacks(sq) & ~edges; | |
} | |
return bishop_attack_masks; | |
} | |
constexpr array<Bitboard, NSQUARES> rook_attack_masks = generate_rook_attack_masks(); | |
constexpr array<Bitboard, NSQUARES> bishop_attack_masks = generate_bishop_attack_masks(); | |
[[nodiscard]] consteval Bitboard generate_slow_sliding_attacks(Square sq, Direction direction, Bitboard occupancy) { | |
Bitboard attacks{}; | |
Bitboard blockers = board_edge(direction); | |
Bitboard square_bb = square_to_bitboard(sq); | |
if ((blockers & square_bb) != 0) return attacks; | |
blockers |= occupancy; | |
do { | |
square_bb = shift(direction, square_bb); | |
attacks |= square_bb; | |
} while ((blockers & square_bb) == 0); | |
return attacks; | |
} | |
[[nodiscard]] consteval Bitboard generate_slow_rook_attacks(Square sq, Bitboard occupancy) { | |
return generate_slow_sliding_attacks(sq, NORTH, occupancy) | | |
generate_slow_sliding_attacks(sq, SOUTH, occupancy) | | |
generate_slow_sliding_attacks(sq, EAST, occupancy) | | |
generate_slow_sliding_attacks(sq, WEST, occupancy); | |
} | |
[[nodiscard]] consteval Bitboard generate_slow_bishop_attacks(Square sq, Bitboard occupancy) { | |
return generate_slow_sliding_attacks(sq, NORTH_EAST, occupancy) | | |
generate_slow_sliding_attacks(sq, NORTH_WEST, occupancy) | | |
generate_slow_sliding_attacks(sq, SOUTH_EAST, occupancy) | | |
generate_slow_sliding_attacks(sq, SOUTH_WEST, occupancy); | |
} | |
[[nodiscard]] consteval array<array<Bitboard, ROOK_TABLE_SIZE>, NSQUARES> generate_rook_attack_table() { | |
array<array<Bitboard, ROOK_TABLE_SIZE>, NSQUARES> rook_attack_table{}; | |
Bitboard subset{}, index{}; | |
for (Square sq = a1; sq < NSQUARES; sq++) { | |
subset = 0; | |
do { | |
index = subset; | |
index = index * ROOK_MAGICS[sq]; | |
index = index >> ROOK_SHIFTS[sq]; | |
rook_attack_table[sq][index] = generate_slow_rook_attacks(sq, subset); | |
subset = (subset - rook_attack_masks[sq]) & rook_attack_masks[sq]; | |
} while (subset); | |
} | |
return rook_attack_table; | |
} | |
[[nodiscard]] consteval array<array<Bitboard, BISHOP_TABLE_SIZE>, NSQUARES> generate_bishop_attack_table() { | |
array<array<Bitboard, BISHOP_TABLE_SIZE>, NSQUARES> bishop_attack_table{}; | |
Bitboard subset{}, index{}; | |
for (Square sq = a1; sq < NSQUARES; sq++) { | |
subset = 0; | |
do { | |
index = subset; | |
index = index * BISHOP_MAGICS[sq]; | |
index = index >> BISHOP_SHIFTS[sq]; | |
bishop_attack_table[sq][index] = generate_slow_bishop_attacks(sq, subset); | |
subset = (subset - bishop_attack_masks[sq]) & bishop_attack_masks[sq]; | |
} while (subset); | |
} | |
return bishop_attack_table; | |
} | |
constexpr array<array<Bitboard, ROOK_TABLE_SIZE>, NSQUARES> rook_attack_table = generate_rook_attack_table(); | |
constexpr Bitboard get_rook_attacks(Square square, Bitboard occ) { | |
usize index = ((occ & rook_attack_masks[square]) * ROOK_MAGICS[square]) >> ROOK_SHIFTS[square]; | |
return rook_attack_table[square][index]; | |
} | |
constexpr array<array<Bitboard, BISHOP_TABLE_SIZE>, NSQUARES> bishop_attack_table = generate_bishop_attack_table(); | |
constexpr Bitboard get_bishop_attacks(Square square, Bitboard occ) { | |
usize index = ((occ & bishop_attack_masks[square]) * BISHOP_MAGICS[square]) >> BISHOP_SHIFTS[square]; | |
return bishop_attack_table[square][index]; | |
} | |
consteval array<array<Bitboard, NSQUARES>, NSQUARES> generate_squares_in_between() { | |
array<array<Bitboard, NSQUARES>, NSQUARES> squares_in_between{}; | |
for (Square sq1 = a1; sq1 < NSQUARES; sq1++) { | |
for (Square sq2 = a1; sq2 < NSQUARES; sq2++) { | |
Bitboard sqs = square_to_bitboard(sq1) | square_to_bitboard(sq2); | |
if (file_of(sq1) == file_of(sq2) || rank_of(sq1) == rank_of(sq2)) | |
squares_in_between[sq1][sq2] = generate_slow_rook_attacks(sq1, sqs) & generate_slow_rook_attacks(sq2, sqs); | |
else if (diagonal_of(sq1) == diagonal_of(sq2) || anti_diagonal_of(sq1) == anti_diagonal_of(sq2)) | |
squares_in_between[sq1][sq2] = generate_slow_bishop_attacks(sq1, sqs) & generate_slow_bishop_attacks(sq2, sqs); | |
} | |
} | |
return squares_in_between; | |
} | |
constexpr array<array<Bitboard, NSQUARES>, NSQUARES> squares_in_between = generate_squares_in_between(); | |
consteval array<array<Bitboard, NSQUARES>, NSQUARES> generate_square_line() { | |
array<array<Bitboard, NSQUARES>, NSQUARES> square_line{}; | |
for (Square sq1 = a1; sq1 < NSQUARES; sq1++) { | |
for (Square sq2 = a1; sq2 < NSQUARES; sq2++) { | |
if (file_of(sq1) == file_of(sq2) || rank_of(sq1) == rank_of(sq2)) | |
square_line[sq1][sq2] = generate_slow_rook_attacks(sq1, 0) & generate_slow_rook_attacks(sq2, 0); | |
else if (diagonal_of(sq1) == diagonal_of(sq2) || anti_diagonal_of(sq1) == anti_diagonal_of(sq2)) | |
square_line[sq1][sq2] = generate_slow_bishop_attacks(sq1, 0) & generate_slow_bishop_attacks(sq2, 0); | |
} | |
} | |
return square_line; | |
} | |
constexpr array<array<Bitboard, NSQUARES>, NSQUARES> square_line = generate_square_line(); | |
} // anon namespace | |
template<PieceType piece_type, Color color = WHITE> | |
constexpr Bitboard attacks(Square sq, Bitboard occupancy = 0) { | |
if constexpr (piece_type == PAWN) { | |
if constexpr (color == WHITE) return WHITE_PAWN_ATTACKS[sq]; | |
return BLACK_PAWN_ATTACKS[sq]; | |
} | |
else if constexpr (piece_type == KNIGHT) return KNIGHT_ATTACKS[sq]; | |
else if constexpr (piece_type == BISHOP) return get_bishop_attacks(sq, occupancy); | |
else if constexpr (piece_type == ROOK) return get_rook_attacks(sq, occupancy); | |
else if constexpr (piece_type == QUEEN) return get_bishop_attacks(sq, occupancy) | get_rook_attacks(sq, occupancy); | |
else if constexpr (piece_type == KING) return KING_ATTACKS[sq]; | |
return 0; | |
} | |
inline Bitboard attacks(PieceType piece_type, Square sq, Bitboard occupancy) { | |
if (piece_type == PAWN) return 0; | |
else if (piece_type == KNIGHT) return KNIGHT_ATTACKS[sq]; | |
else if (piece_type == BISHOP) return get_bishop_attacks(sq, occupancy); | |
else if (piece_type == ROOK) return get_rook_attacks(sq, occupancy); | |
else if (piece_type == QUEEN) return get_bishop_attacks(sq, occupancy) | get_rook_attacks(sq, occupancy); | |
else if (piece_type == KING) return KING_ATTACKS[sq]; | |
return 0; | |
} | |
inline constexpr Bitboard square_in_between(Square sq1, Square sq2) { | |
return squares_in_between[sq1][sq2]; | |
} | |
inline constexpr Bitboard line_of(Square sq1, Square sq2) { | |
return square_line[sq1][sq2]; | |
} | |
} // table namespace | |
/* ------------------------ DEFINE MOVE TYPE ------------------------ */ | |
using MoveType = u8; | |
constexpr MoveType QUIET = 0b0000; | |
constexpr MoveType OO = 0b0001; | |
constexpr MoveType OOO = 0b0010; | |
constexpr MoveType DOUBLE_PUSH = 0b0011; | |
// Captures have the 4th bit set. | |
constexpr MoveType CAPTURE_TYPE = 0b1000; | |
constexpr MoveType ENPASSANT = 0b1001; | |
// Promotions have the 3rd bit set. | |
constexpr MoveType PROMOTION_TYPE = 0b0100; | |
constexpr MoveType PR_KNIGHT = 0b0100; | |
constexpr MoveType PR_BISHOP = 0b0101; | |
constexpr MoveType PR_ROOK = 0b0110; | |
constexpr MoveType PR_QUEEN = 0b0111; | |
class Move { | |
private: | |
// Bits are arranged as follows | |
// | 4 bits for type | 6 bits for from | 6 bits for to | |
uint16_t move; | |
static constexpr u8 TO_SHIFT = 0; | |
static constexpr u8 FROM_SHIFT = 6; | |
static constexpr u8 TYPE_SHIFT = 12; | |
static constexpr u8 TO_BITMASK = 0b111111; | |
static constexpr u8 FROM_BITMASK = 0b111111; | |
static constexpr u8 TYPE_BITMASK = 0b1111; | |
static constexpr u8 CAPTURE_BITMASK = 0b1000; | |
static constexpr u8 PROMOTION_BITMASK = 0b0100; | |
public: | |
consteval Move() : move(0) {} | |
constexpr explicit Move(uint16_t m) { move = m; } | |
constexpr Move(Square from, Square to) : move(0) { | |
move = (from << 6) | to; | |
} | |
constexpr Move(Square from, Square to, MoveType type) : move(0) { | |
move = (type << TYPE_SHIFT) | (from << FROM_SHIFT) | to << TO_SHIFT; | |
} | |
[[nodiscard]] inline Square to() const { return Square(move & TO_BITMASK); } | |
[[nodiscard]] inline Square from() const { return Square((move >> FROM_SHIFT) & FROM_BITMASK); } | |
[[nodiscard]] inline MoveType type() const { return (move >> TYPE_SHIFT) & TYPE_BITMASK; } | |
[[nodiscard]] inline bool is_capture() const { return (move >> TYPE_SHIFT) & CAPTURE_BITMASK; } | |
[[nodiscard]] inline bool is_promotion() const { return (move >> TYPE_SHIFT) & PROMOTION_BITMASK; } | |
[[nodiscard]] inline bool is_quiet() const { return !is_capture() && !is_promotion(); } | |
bool operator==(Move a) const { return move == a.move; } | |
bool operator!=(Move a) const { return move != a.move; } | |
}; | |
constexpr Move EMPTY_MOVE = Move(); | |
inline array<string , 16> MOVE_TYPE_UCI = { | |
"", "", "", "", "N", "B", "R", "Q", | |
"", "", "", "", "N", "B", "R", "Q" | |
}; | |
inline std::ostream& operator<<(std::ostream& os, const Move& m) { | |
os << SQ_TO_STRING[m.from()] << SQ_TO_STRING[m.to()] << MOVE_TYPE_UCI[m.type()]; | |
return os; | |
} | |
/* ------------------------ DEFINE POSITION TYPE ------------------------ */ | |
class Position; | |
class PositionState { | |
friend Position; | |
private: | |
static constexpr Bitboard WHITE_OO_BANNED_MASK = 0x90; | |
static constexpr Bitboard WHITE_OOO_BANNED_MASK = 0x11; | |
static constexpr Bitboard BLACK_OO_BANNED_MASK = 0x9000000000000000; | |
static constexpr Bitboard BLACK_OOO_BANNED_MASK = 0x1100000000000000; | |
static constexpr Bitboard NO_CASTLING_MASK = 0x9100000000000091; | |
Bitboard from_to{}; | |
Piece captured{}; | |
Square ep_square = NO_SQUARE; | |
u16 fifty_move_rule{}; | |
ZobristHash hash{}; | |
public: | |
PositionState() = default; | |
~PositionState() = default; | |
}; | |
class Position { | |
private: | |
Color side = WHITE; | |
array<Bitboard, NPIECES> pieces{}; | |
array<Piece, NSQUARES> board{}; | |
static constexpr i16 POSITION_STATE_SIZE = 1000; | |
Stack<PositionState, POSITION_STATE_SIZE> state_history{}; | |
static constexpr bool ENABLE_HASH_UPDATE = true; | |
static constexpr bool DISABLE_HASH_UPDATE = false; | |
template<bool update_hash> | |
inline void place_piece(Piece piece, Square square); | |
template<bool update_hash> | |
inline void remove_piece(Square square); | |
template<bool update_hash> | |
inline void move_piece(Square from, Square to); | |
inline void reset(); | |
[[nodiscard]] u8 castling_state(Bitboard from_to) const; | |
public: | |
Position() = default; | |
explicit Position(const std::string& fen); | |
[[nodiscard]] inline u16 fifty_move_rule() const { return state_history.peek().fifty_move_rule; } | |
[[nodiscard]] inline Square ep_square() const { return state_history.peek().ep_square; } | |
[[nodiscard]] inline ZobristHash hash() const { return state_history.peek().hash; } | |
[[nodiscard]] inline Bitboard from_to() const { return state_history.peek().from_to; } | |
[[nodiscard]] inline Color turn() const { return side; } | |
enum Repetition : i32 { | |
TWO_FOLD, | |
THREE_FOLD | |
}; | |
[[nodiscard]] inline bool has_repetition(Repetition fold = TWO_FOLD); | |
template<Color color> | |
[[nodiscard]] inline bool king_and_oo_rook_not_moved() const { | |
if constexpr (color == WHITE) return !(from_to() & PositionState::WHITE_OO_BANNED_MASK); | |
return !(from_to() & PositionState::BLACK_OO_BANNED_MASK); | |
} | |
template<Color color> | |
[[nodiscard]] inline bool king_and_ooo_rook_not_moved() const { | |
if constexpr (color == WHITE) return !(from_to() & PositionState::WHITE_OOO_BANNED_MASK); | |
return !(from_to() & PositionState::BLACK_OOO_BANNED_MASK); | |
} | |
[[nodiscard]] inline Piece piece_at(Square square) const { return board[square]; } | |
template<Color color, PieceType piece_type> | |
[[nodiscard]] constexpr Bitboard occupancy() const { return pieces[make_piece<color, piece_type>()]; } | |
template<Piece piece> | |
[[nodiscard]] constexpr Bitboard occupancy() const { return pieces[piece]; } | |
template<Color color> | |
[[nodiscard]] constexpr Bitboard occupancy() const { | |
return pieces[make_piece<color, PAWN>()] | | |
pieces[make_piece<color, KNIGHT>()] | | |
pieces[make_piece<color, BISHOP>()] | | |
pieces[make_piece<color, ROOK>()] | | |
pieces[make_piece<color, QUEEN>()] | | |
pieces[make_piece<color, KING>()]; | |
} | |
[[nodiscard]] inline Bitboard occupancy() { | |
return occupancy<WHITE>() | occupancy<BLACK>(); | |
} | |
template<Color color> | |
[[nodiscard]] constexpr Bitboard diagonal_sliders() const { | |
return occupancy<color, BISHOP>() | occupancy<color, QUEEN>(); | |
} | |
template<Color color> | |
[[nodiscard]] constexpr Bitboard orthogonal_sliders() const { | |
return occupancy<color, ROOK>() | occupancy<color, QUEEN>(); | |
} | |
template<Color color> | |
[[nodiscard]] constexpr Bitboard attackers_of(Square s, Bitboard occ) const { | |
return (tables::attacks<PAWN, ~color>(s) & pieces[make_piece<color, PAWN>()]) | | |
(tables::attacks<KNIGHT>(s, occ) & pieces[make_piece<color, KNIGHT>()]) | | |
(tables::attacks<BISHOP>(s, occ) & (pieces[make_piece<color, BISHOP>()] | pieces[make_piece<color, QUEEN>()])) | | |
(tables::attacks<ROOK>(s, occ) & (pieces[make_piece<color, ROOK>()] | pieces[make_piece<color, QUEEN>()])); | |
} | |
[[nodiscard]] constexpr Bitboard attackers_of(Square s, Bitboard occ) const { | |
return attackers_of<BLACK>(s, occ) | attackers_of<WHITE>(s, occ); | |
} | |
inline void set_fen(const std::string& fen); | |
[[nodiscard]] inline std::string fen() const; | |
inline friend std::ostream& operator<<(std::ostream& os, const Position& p); | |
template<Color color> | |
inline void play(Move move); | |
template<Color color> | |
inline void undo(Move move); | |
template<Color color> | |
inline void play_null(); | |
template<Color color> | |
inline void undo_null(); | |
}; | |
inline Position::Position(const std::string& fen) { | |
set_fen(fen); | |
} | |
inline void Position::reset() { | |
state_history.clear(); | |
pieces.fill(0); | |
board.fill(NO_PIECE); | |
side = WHITE; | |
} | |
template<bool update_hash> | |
inline void Position::place_piece(Piece piece, Square square) { | |
pieces[piece] |= square_to_bitboard(square); | |
board[square] = piece; | |
if constexpr (update_hash) { | |
state_history.top().hash ^= ZOBRIST_PIECE_SQUARE[piece][square]; | |
} | |
} | |
template<bool update_hash> | |
inline void Position::remove_piece(Square square) { | |
if constexpr (update_hash) { | |
state_history.top().hash ^= ZOBRIST_PIECE_SQUARE[piece_at(square)][square]; | |
} | |
pieces[piece_at(square)] &= ~square_to_bitboard(square); | |
board[square] = NO_PIECE; | |
} | |
template<bool update_hash> | |
inline void Position::move_piece(Square from, Square to) { | |
Piece piece = piece_at(from); | |
remove_piece<update_hash>(from); | |
place_piece<update_hash>(piece, to); | |
} | |
inline u8 Position::castling_state(Bitboard from_to) const { | |
i32 white_oo = !(from_to & PositionState::WHITE_OO_BANNED_MASK) << 3; | |
i32 white_ooo = !(from_to & PositionState::WHITE_OOO_BANNED_MASK) << 2; | |
i32 black_oo = !(from_to & PositionState::BLACK_OO_BANNED_MASK) << 1; | |
i32 black_ooo = !(from_to & PositionState::BLACK_OOO_BANNED_MASK); | |
return white_oo | white_ooo | black_oo | black_ooo; | |
} | |
inline std::string Position::fen() const { | |
std::ostringstream fen; | |
i32 empty; | |
for (i32 i = 56; i >= 0; i -= 8) { | |
empty = 0; | |
for (i32 j = 0; j < 8; j++) { | |
Piece p = board[i + j]; | |
if (p == NO_PIECE) empty++; | |
else { | |
fen << (empty == 0 ? "" : std::to_string(empty)) << PIECE_MATCHER[p]; | |
empty = 0; | |
} | |
} | |
if (empty != 0) fen << empty; | |
if (i > 0) fen << '/'; | |
} | |
std::string castling_rights; | |
const Bitboard set_castling_state = castling_state(state_history.peek().from_to); | |
if ((set_castling_state >> 3) & 0b1) castling_rights += "K"; | |
if ((set_castling_state >> 2) & 0b1) castling_rights += "Q"; | |
if ((set_castling_state >> 1) & 0b1) castling_rights += "k"; | |
if (set_castling_state & 0b1) castling_rights += "q"; | |
if (set_castling_state == 0) castling_rights = "-"; | |
fen << (side == WHITE ? " w " : " b ") | |
<< castling_rights | |
<< (ep_square() == NO_SQUARE ? " -" : " " + std::string(SQ_TO_STRING[ep_square()])) | |
<< " " | |
<< "0" << " " | |
<< "1"; | |
return fen.str(); | |
} | |
inline void Position::set_fen(const std::string& fen_string) { | |
reset(); | |
// Push empty state to state history. | |
state_history.push({}); | |
std::vector<std::string> fen_tokens = split(fen_string, " "); | |
if (fen_tokens.size() < 4) { | |
throw std::invalid_argument("Fen is missing fields. "); | |
} | |
const std::string position = fen_tokens[0]; | |
const std::string player = fen_tokens[1]; | |
const std::string castling = fen_tokens[2]; | |
const std::string en_passant = fen_tokens[3]; | |
const std::string half_move_clock = fen_tokens.size() > 4 ? fen_tokens[4] : "0"; | |
const std::string full_move_counter = fen_tokens.size() > 4 ? fen_tokens[5] : "1"; | |
side = player == "w" ? WHITE : BLACK; | |
state_history.top().hash ^= ZOBRIST_COLOR[side]; | |
Square square = a8; | |
for (char ch : position) { | |
if (isdigit(ch)) square += std::stoi(std::string(1, ch)) * EAST; | |
else if (ch == '/') square += SOUTH_SOUTH; | |
else place_piece<ENABLE_HASH_UPDATE>(piece_from_char(ch), square++); | |
} | |
state_history.top().from_to = PositionState::NO_CASTLING_MASK; | |
for (char c : castling) { | |
if (c == 'K') state_history.top().from_to &= ~PositionState::WHITE_OO_BANNED_MASK; | |
else if (c == 'Q') state_history.top().from_to &= ~PositionState::WHITE_OOO_BANNED_MASK; | |
else if (c == 'k') state_history.top().from_to &= ~PositionState::BLACK_OO_BANNED_MASK; | |
else if (c == 'q') state_history.top().from_to &= ~PositionState::BLACK_OOO_BANNED_MASK; | |
} | |
state_history.top().hash ^= ZOBRIST_CASTLING_RIGHTS[castling_state(state_history.top().from_to)]; | |
if (en_passant.size() > 1) { | |
auto s = create_square(File(en_passant[0] - 'a'), Rank(en_passant[1] - '1')); | |
state_history.top().ep_square = s; | |
} else { | |
state_history.top().ep_square = NO_SQUARE; | |
} | |
state_history.top().hash ^= ZOBRIST_EP_SQUARE[state_history.top().ep_square]; | |
} | |
inline std::ostream& operator << (std::ostream& os, const Position& p) { | |
const std::string s = " +---+---+---+---+---+---+---+---+\n"; | |
const std::string t = " A B C D E F G H\n"; | |
os << t; | |
for (i32 i = 56; i >= 0; i -= 8) { | |
os << s << " " << i / 8 + 1 << " "; | |
for (i32 j = 0; j < 8; j++) | |
os << "| " << PIECE_MATCHER[p.board[i + j]] << " "; | |
os << "| " << i / 8 + 1 << "\n"; | |
} | |
os << s; | |
os << t << "\n"; | |
os << "FEN: " << p.fen() << "\n"; | |
os << "Hash: 0x" << std::hex << p.hash() << std::dec << "\n"; | |
return os; | |
} | |
inline bool Position::has_repetition(Repetition fold) { | |
int count = fold == THREE_FOLD ? 0 : 1; | |
const i32 hash_hist_size = static_cast<int>(state_history.size()); | |
const u64 current_hash = hash(); | |
for (i32 idx = hash_hist_size - 3; | |
idx >= 0 && idx >= hash_hist_size - fifty_move_rule(); | |
idx -= 2) { | |
ZobristHash stack_hash = state_history[idx].hash; | |
if (stack_hash == current_hash) count += 1; | |
if (count >= 2) return true; | |
} | |
return false; | |
} | |
template<Color color> | |
inline void Position::play(Move move) { | |
PositionState next_state = {}; | |
next_state.from_to = state_history.peek().from_to | square_to_bitboard(move.from()) | square_to_bitboard(move.to()); | |
next_state.captured = piece_at(move.to()); | |
next_state.hash = state_history.peek().hash; | |
next_state.fifty_move_rule = state_history.peek().fifty_move_rule + 1; | |
next_state.ep_square = NO_SQUARE; | |
if (move.is_capture() || type_of(piece_at(move.from())) == PAWN) | |
next_state.fifty_move_rule = 0; | |
next_state.hash ^= ZOBRIST_COLOR[~color]; | |
next_state.hash ^= ZOBRIST_COLOR[color]; | |
next_state.hash ^= ZOBRIST_CASTLING_RIGHTS[castling_state(state_history.peek().from_to)]; | |
next_state.hash ^= ZOBRIST_CASTLING_RIGHTS[castling_state(next_state.from_to)]; | |
next_state.hash ^= ZOBRIST_EP_SQUARE[state_history.peek().ep_square]; | |
state_history.push(next_state); | |
if (move.type() & CAPTURE_TYPE && move.type() != ENPASSANT) remove_piece<ENABLE_HASH_UPDATE>(move.to()); | |
move_piece<ENABLE_HASH_UPDATE>(move.from(), move.to()); | |
MoveType type = move.type(); | |
switch (type) { | |
case DOUBLE_PUSH: | |
state_history.top().ep_square = move.from() + relative_dir<color, NORTH>(); | |
break; | |
case OO: | |
if constexpr (color == WHITE) move_piece<ENABLE_HASH_UPDATE>(h1, f1); | |
else move_piece<ENABLE_HASH_UPDATE>(h8, f8); | |
break; | |
case OOO: | |
if constexpr (color == WHITE) move_piece<ENABLE_HASH_UPDATE>(a1, d1); | |
else move_piece<ENABLE_HASH_UPDATE>(a8, d8); | |
break; | |
case ENPASSANT: | |
remove_piece<ENABLE_HASH_UPDATE>(move.to() + relative_dir<color, SOUTH>()); | |
state_history.top().captured = make_piece<~color, PAWN>(); | |
break; | |
case PR_KNIGHT | CAPTURE_TYPE: | |
case PR_KNIGHT: | |
remove_piece<ENABLE_HASH_UPDATE>(move.to()); | |
place_piece<ENABLE_HASH_UPDATE>(make_piece<color, KNIGHT>(), move.to()); | |
break; | |
case PR_BISHOP | CAPTURE_TYPE: | |
case PR_BISHOP: | |
remove_piece<ENABLE_HASH_UPDATE>(move.to()); | |
place_piece<ENABLE_HASH_UPDATE>(make_piece<color, BISHOP>(), move.to()); | |
break; | |
case PR_ROOK | CAPTURE_TYPE: | |
case PR_ROOK: | |
remove_piece<ENABLE_HASH_UPDATE>(move.to()); | |
place_piece<ENABLE_HASH_UPDATE>(make_piece<color, ROOK>(), move.to()); | |
break; | |
case PR_QUEEN | CAPTURE_TYPE: | |
case PR_QUEEN: | |
remove_piece<ENABLE_HASH_UPDATE>(move.to()); | |
place_piece<ENABLE_HASH_UPDATE>(make_piece<color, QUEEN>(), move.to()); | |
break; | |
default: break; | |
} | |
state_history.top().hash ^= ZOBRIST_EP_SQUARE[state_history.peek().ep_square]; | |
side = ~side; | |
} | |
template<Color color> | |
inline void Position::undo(Move move) { | |
PositionState old_state = state_history.pop(); | |
move_piece<DISABLE_HASH_UPDATE>(move.to(), move.from()); | |
place_piece<DISABLE_HASH_UPDATE>(old_state.captured, move.to()); | |
MoveType type = move.type(); | |
switch (type) { | |
case OO: | |
if constexpr (color == WHITE) move_piece<DISABLE_HASH_UPDATE>(f1, h1); | |
else move_piece<DISABLE_HASH_UPDATE>(f8, h8); | |
break; | |
case OOO: | |
if constexpr (color == WHITE) move_piece<DISABLE_HASH_UPDATE>(d1, a1); | |
else move_piece<DISABLE_HASH_UPDATE>(d8, a8); | |
break; | |
case ENPASSANT: | |
remove_piece<DISABLE_HASH_UPDATE>(move.to()); | |
place_piece<DISABLE_HASH_UPDATE>(make_piece<~color, PAWN>(), move.to() + relative_dir<color, SOUTH>()); | |
break; | |
case PR_KNIGHT | CAPTURE_TYPE: | |
case PR_KNIGHT: | |
case PR_BISHOP | CAPTURE_TYPE: | |
case PR_BISHOP: | |
case PR_ROOK | CAPTURE_TYPE: | |
case PR_ROOK: | |
case PR_QUEEN | CAPTURE_TYPE: | |
case PR_QUEEN: | |
remove_piece<DISABLE_HASH_UPDATE>(move.from()); | |
place_piece<DISABLE_HASH_UPDATE>(make_piece<color, PAWN>(), move.from()); | |
break; | |
default: break; | |
} | |
side = ~side; | |
} | |
template<Color color> | |
inline void Position::play_null() { | |
PositionState next_state = {}; | |
next_state.from_to = state_history.peek().from_to; | |
next_state.captured = NO_PIECE; | |
next_state.hash = state_history.peek().hash; | |
next_state.fifty_move_rule = state_history.peek().fifty_move_rule + 1; | |
next_state.ep_square = NO_SQUARE; | |
next_state.hash ^= ZOBRIST_COLOR[~color]; | |
next_state.hash ^= ZOBRIST_COLOR[color]; | |
state_history.push(next_state); | |
} | |
template<Color color> | |
inline void Position::undo_null() { | |
state_history.pop(); | |
} | |
/* ------------------------ DEFINE MOVE GENERATION TYPE ------------------------ */ | |
enum MoveGenerationType : i32 { | |
ALL, | |
CAPTURES | |
}; | |
/* ------------------------ DEFINE CASTLING RIGHTS MASKS ------------------------ */ | |
constexpr Bitboard WHITE_OO_BLOCKERS_MASK = 0x60; | |
constexpr Bitboard WHITE_OOO_DANGER_MASK = 0xC; | |
constexpr Bitboard WHITE_OOO_BLOCKERS_MASK = 0xE; | |
constexpr Bitboard BLACK_OO_BLOCKERS_MASK = 0x6000000000000000; | |
constexpr Bitboard BLACK_OOO_DANGER_MASK = 0xC00000000000000; | |
constexpr Bitboard BLACK_OOO_BLOCKERS_MASK = 0xE00000000000000; | |
template<Color C> | |
Bitboard oo_blockers_mask() { | |
if constexpr (C == WHITE) return WHITE_OO_BLOCKERS_MASK; | |
return BLACK_OO_BLOCKERS_MASK; | |
} | |
template<Color C> | |
Bitboard ooo_danger_mask() { | |
if constexpr (C == WHITE) return WHITE_OOO_DANGER_MASK; | |
return BLACK_OOO_DANGER_MASK; | |
} | |
template<Color C> | |
Bitboard ooo_blockers_mask() { | |
if constexpr (C == WHITE) return WHITE_OOO_BLOCKERS_MASK; | |
return BLACK_OOO_BLOCKERS_MASK; | |
} | |
/* ------------------------ DEFINE MOVE GENERATOR ------------------------ */ | |
template<Color color, MoveGenerationType move_gen_type = ALL> | |
class MoveList { | |
private: | |
Stack<Move, 218> move_list{}; | |
Position& board_; | |
struct SharedData { | |
Bitboard us_occupancy, them_occupancy, all; | |
Square them_king_square, us_king_square; | |
Bitboard us_king, them_king; | |
Bitboard us_ortho_sliders, us_diag_sliders; | |
Bitboard them_ortho_sliders, them_diag_sliders; | |
explicit SharedData(Position& board) { | |
us_occupancy = board.occupancy<color>(); | |
them_occupancy = board.occupancy<~color>(); | |
all = us_occupancy | them_occupancy; | |
us_king = board.occupancy<color, KING>(); | |
them_king = board.occupancy<~color, KING>(); | |
us_king_square = lsb(us_king); | |
them_king_square = lsb(them_king); | |
us_diag_sliders = board.diagonal_sliders<color>(); | |
them_diag_sliders = board.diagonal_sliders<~color>(); | |
us_ortho_sliders = board.orthogonal_sliders<color>(); | |
them_ortho_sliders = board.orthogonal_sliders<~color>(); | |
} | |
}; | |
template<MoveType move_type> | |
void push(Square from, Bitboard to) { | |
while (to) { | |
Square to_square = pop_lsb(to); | |
if constexpr (move_type & PROMOTION_TYPE) { | |
constexpr MoveType is_capture = move_type & CAPTURE_TYPE; | |
move_list.push(Move(from, to_square, PR_KNIGHT | is_capture)); | |
move_list.push(Move(from, to_square, PR_BISHOP | is_capture)); | |
move_list.push(Move(from, to_square, PR_ROOK | is_capture)); | |
move_list.push(Move(from, to_square, PR_QUEEN | is_capture)); | |
} else { | |
move_list.push(Move(from, to_square, move_type)); | |
} | |
} | |
} | |
template<MoveType move_type> | |
void push_single(Square from, Square to) { | |
if constexpr (move_type & PROMOTION_TYPE) { | |
constexpr MoveType is_capture = move_type & CAPTURE_TYPE; | |
move_list.push(Move(from, to, PR_KNIGHT | is_capture)); | |
move_list.push(Move(from, to, PR_BISHOP | is_capture)); | |
move_list.push(Move(from, to, PR_ROOK | is_capture)); | |
move_list.push(Move(from, to, PR_QUEEN | is_capture)); | |
} else { | |
move_list.push(Move(from, to, move_type)); | |
} | |
} | |
constexpr Bitboard generate_danger(const SharedData& data); | |
constexpr std::pair<Bitboard, Bitboard> generate_checkers_and_pinned(const SharedData& data); | |
constexpr void push_check_evasions(const SharedData& data, Bitboard danger); | |
constexpr bool push_pawn_knight_check_captures(const SharedData& data, Bitboard checker, Bitboard pinned); | |
constexpr void push_en_passant(const SharedData& data, Bitboard pinned); | |
constexpr void push_castle(const SharedData& data, Bitboard danger); | |
constexpr void push_pinned(const SharedData& data, Bitboard pinned, Bitboard quiet_mask, Bitboard capture_mask); | |
constexpr void push_non_pinned_pieces(const SharedData& data, Bitboard pinned, Bitboard quiet_mask, Bitboard capture_mask); | |
constexpr void push_non_pinned_pawns(const SharedData& data, Bitboard pinned, Bitboard quiet_mask, Bitboard capture_mask); | |
constexpr void push_promotions(Bitboard pinned, Bitboard quiet_mask, Bitboard capture_mask); | |
public: | |
explicit MoveList(Position& board); | |
[[nodiscard]] inline auto begin() const { return move_list.begin(); } | |
[[nodiscard]] inline auto end() const { return move_list.end(); } | |
[[nodiscard]] inline auto size() const { return move_list.size(); } | |
}; | |
template<Color color, MoveGenerationType move_gen_type> | |
constexpr void | |
MoveList<color, move_gen_type>::push_promotions(Bitboard pinned, Bitboard quiet_mask, Bitboard capture_mask) { | |
Bitboard promotion_candidates = board_.occupancy<color, PAWN>() & ~pinned & MASK_RANK[relative_rank<color>(RANK7)]; | |
if (!promotion_candidates) return; | |
Bitboard west_promo_capture = shift_relative<color, NORTH_WEST>(promotion_candidates) & capture_mask; | |
Bitboard east_promo_capture = shift_relative<color, NORTH_EAST>(promotion_candidates) & capture_mask; | |
while (west_promo_capture) { | |
Square s = pop_lsb(west_promo_capture); | |
push_single<PROMOTION_TYPE | CAPTURE_TYPE>(s - relative_dir<color, NORTH_WEST>(), s); | |
} | |
while (east_promo_capture) { | |
Square s = pop_lsb(east_promo_capture); | |
push_single<PROMOTION_TYPE | CAPTURE_TYPE>(s - relative_dir<color, NORTH_EAST>(), s); | |
} | |
if constexpr (move_gen_type == CAPTURES) return; | |
Bitboard quiet_promos = shift_relative<color, NORTH>(promotion_candidates) & quiet_mask; | |
while (quiet_promos) { | |
Square s = pop_lsb(quiet_promos); | |
push_single<PROMOTION_TYPE>(s - relative_dir<color, NORTH>(), s); | |
} | |
} | |
template<Color color, MoveGenerationType move_gen_type> | |
constexpr void MoveList<color, move_gen_type>::push_check_evasions(const MoveList::SharedData &data, Bitboard danger) { | |
Bitboard evasions = tables::attacks<KING>(data.us_king_square, data.all) & ~(data.us_occupancy | danger); | |
if constexpr (move_gen_type == ALL) push<QUIET>(data.us_king_square, evasions & ~data.them_occupancy); | |
push<CAPTURE_TYPE>(data.us_king_square, evasions & data.them_occupancy); | |
} | |
template<Color color, MoveGenerationType move_gen_type> | |
constexpr std::pair<Bitboard, Bitboard> | |
MoveList<color, move_gen_type>::generate_checkers_and_pinned(const MoveList::SharedData &data) { | |
Bitboard checkers{}, pinned{}; | |
checkers = (tables::attacks<KNIGHT>(data.us_king_square, data.all) & board_.occupancy<~color, KNIGHT>()) | | |
(tables::attacks<PAWN, color>(data.us_king_square) & board_.occupancy<~color, PAWN>()); | |
Bitboard candidates = (tables::attacks<ROOK>(data.us_king_square, data.them_occupancy) & data.them_ortho_sliders) | | |
(tables::attacks<BISHOP>(data.us_king_square, data.them_occupancy) & data.them_diag_sliders); | |
while (candidates) { | |
Square candidate_square = pop_lsb(candidates); | |
Bitboard potentially_pinned = tables::square_in_between(data.us_king_square, candidate_square) & data.us_occupancy; | |
if (potentially_pinned == 0) checkers ^= square_to_bitboard(candidate_square); | |
else if (pop_count(potentially_pinned) == 1) pinned ^= potentially_pinned; | |
} | |
return {checkers, pinned}; | |
} | |
template<Color color, MoveGenerationType move_gen_type> | |
constexpr Bitboard MoveList<color, move_gen_type>::generate_danger(const MoveList::SharedData& data) { | |
Bitboard danger = board_.occupancy<~color, PAWN>(); | |
danger = shift_relative<~color, NORTH_WEST>(danger) | shift_relative<~color, NORTH_EAST>(danger); | |
danger |= tables::attacks<KING>(data.them_king_square, data.all); | |
Bitboard them_knights = board_.occupancy<~color, KNIGHT>(); | |
while (them_knights) { | |
danger |= tables::attacks<KNIGHT>(pop_lsb(them_knights), data.all); | |
} | |
Bitboard them_diag_sliders_ = data.them_diag_sliders; | |
while (them_diag_sliders_) { | |
danger |= tables::attacks<BISHOP>(pop_lsb(them_diag_sliders_), data.all ^ data.us_king); | |
} | |
Bitboard them_ortho_sliders_ = data.them_ortho_sliders; | |
while (them_ortho_sliders_) { | |
danger |= tables::attacks<ROOK>(pop_lsb(them_ortho_sliders_), data.all ^ data.us_king); | |
} | |
return danger; | |
} | |
template<Color color, MoveGenerationType move_gen_type> | |
constexpr void MoveList<color, move_gen_type>::push_pinned(const MoveList::SharedData &data, Bitboard pinned, | |
Bitboard quiet_mask, Bitboard capture_mask) { | |
Bitboard pinned_pieces = pinned & ~board_.occupancy<color, KNIGHT>() & ~board_.occupancy<color, PAWN>(); | |
Bitboard pinned_pawns = pinned & board_.occupancy<color, PAWN>(); | |
while (pinned_pieces) { | |
const Square s = pop_lsb(pinned_pieces); | |
Bitboard pinned_to = tables::attacks(type_of(board_.piece_at(s)), s, data.all) & tables::line_of(data.us_king_square, s); | |
if constexpr (move_gen_type == ALL) push<QUIET>(s, pinned_to & quiet_mask); | |
push<CAPTURE_TYPE>(s, pinned_to & capture_mask); | |
} | |
while (pinned_pawns) { | |
const Square s = pop_lsb(pinned_pawns); | |
const Bitboard line_of_s_king = tables::line_of(data.us_king_square, s); | |
if (rank_of(s) == relative_rank<color>(RANK7)) { | |
Bitboard pinned_pawn_to = tables::attacks<PAWN, color>(s) & capture_mask & line_of_s_king; | |
push<PROMOTION_TYPE | CAPTURE_TYPE>(s, pinned_pawn_to); | |
continue; | |
} | |
Bitboard pinned_pawn_to = tables::attacks<PAWN, color>(s) & data.them_occupancy & line_of_s_king; | |
push<CAPTURE_TYPE>(s, pinned_pawn_to); | |
if constexpr (move_gen_type == CAPTURES) continue; | |
pinned_pawn_to = shift_relative<color, NORTH>(square_to_bitboard(s)) & ~data.all & line_of_s_king; | |
Bitboard double_push_to = pinned_pawn_to & MASK_RANK[relative_rank<color>(RANK3)]; | |
Bitboard pinned_pawn_double_push = shift_relative<color, NORTH>(double_push_to) & ~data.all & line_of_s_king; | |
push<QUIET>(s, pinned_pawn_to); | |
push<DOUBLE_PUSH>(s, pinned_pawn_double_push); | |
} | |
} | |
template<Color color, MoveGenerationType move_gen_type> | |
constexpr void MoveList<color, move_gen_type>::push_castle(const MoveList::SharedData &data, Bitboard danger) { | |
if constexpr (move_gen_type == CAPTURES) return; | |
Bitboard oo_path_in_danger = (data.all | danger) & oo_blockers_mask<color>(); | |
if (board_.king_and_oo_rook_not_moved<color>() && !oo_path_in_danger) { | |
if constexpr (color == WHITE) push_single<OO>(e1, g1); | |
else push_single<OO>(e8, g8); | |
} | |
Bitboard ooo_path_in_danger = (data.all | (danger & ooo_danger_mask<color>())) & ooo_blockers_mask<color>(); | |
if (board_.king_and_ooo_rook_not_moved<color>() && !ooo_path_in_danger) { | |
if constexpr (color == WHITE) push_single<OOO>(e1, c1); | |
else push_single<OOO>(e8, c8); | |
} | |
} | |
template<Color color, MoveGenerationType move_gen_type> | |
constexpr void MoveList<color, move_gen_type>::push_en_passant(const MoveList::SharedData &data, Bitboard pinned) { | |
if (board_.ep_square() == NO_SQUARE) return; | |
const Bitboard ep_attackers = tables::attacks<PAWN, ~color>(board_.ep_square()) & board_.occupancy<color, PAWN>(); | |
Bitboard unpinned_ep_attackers = ep_attackers & ~pinned; | |
while (unpinned_ep_attackers) { | |
const Square s = pop_lsb(unpinned_ep_attackers); | |
const Bitboard ep_captured_pawn = shift_relative<color, SOUTH>(square_to_bitboard(board_.ep_square())); | |
const Bitboard masked_all = data.all ^ square_to_bitboard(s) ^ ep_captured_pawn; | |
const Bitboard attacking_ortho_sliders = tables::attacks<ROOK>(data.us_king_square, masked_all); | |
const Bitboard pseudo_pinned_attackers = attacking_ortho_sliders & MASK_RANK[rank_of(data.us_king_square)] & data.them_ortho_sliders; | |
if (pseudo_pinned_attackers != 0) continue; | |
push_single<ENPASSANT>(s, board_.ep_square()); | |
} | |
const Bitboard diagonal_pinned_ep_attackers = ep_attackers & pinned & tables::line_of(board_.ep_square(), data.us_king_square); | |
if (diagonal_pinned_ep_attackers) { | |
push_single<ENPASSANT>(lsb(diagonal_pinned_ep_attackers), board_.ep_square()); | |
} | |
} | |
template<Color color, MoveGenerationType move_gen_type> | |
constexpr bool MoveList<color, move_gen_type>::push_pawn_knight_check_captures(const MoveList::SharedData &data, Bitboard checker, Bitboard pinned) { | |
Square checker_square = lsb(checker); | |
Bitboard ep_checker_captures, attacking_checker; | |
Square epsq = board_.ep_square(); | |
// For pawn and knight checks we must capture or evade, we cannot block them. | |
switch (board_.piece_at(checker_square)) { | |
case make_piece<~color, PAWN>(): | |
// The checker was a double pushed pawn | |
if (checker == shift_relative<color, SOUTH>(square_to_bitboard(epsq))) { | |
// We can ep capture the double pushed pawn as it is not pinned. | |
ep_checker_captures = tables::attacks<PAWN, ~color>(epsq) & board_.occupancy<color, PAWN>() & ~pinned; | |
while (ep_checker_captures) { | |
push<ENPASSANT>(pop_lsb(ep_checker_captures), square_to_bitboard(epsq)); | |
} | |
} | |
[[fallthrough]]; | |
case make_piece<~color, KNIGHT>(): | |
// Checker was a pawn or knight, we must capture (evasions assumed to be handled already) | |
attacking_checker = board_.attackers_of<color>(checker_square, data.all) & ~pinned; | |
while (attacking_checker) { | |
Square s = pop_lsb(attacking_checker); | |
// If they promoted to a knight, and we can capture and promote, do that. | |
if (type_of(board_.piece_at(s)) == PAWN && rank_of(s) == relative_rank<color>(RANK7)) { | |
push<PROMOTION_TYPE | CAPTURE_TYPE>(s, checker); | |
} | |
else push<CAPTURE_TYPE>(s, checker); | |
} | |
return true; | |
default: return false; | |
} | |
} | |
template<Color color, MoveGenerationType move_gen_type> | |
constexpr void MoveList<color, move_gen_type>::push_non_pinned_pieces(const MoveList::SharedData &data, Bitboard pinned, | |
Bitboard quiet_mask, Bitboard capture_mask) { | |
Bitboard un_pinned_knights = board_.occupancy<color, KNIGHT>() & ~pinned; | |
while (un_pinned_knights) { | |
Square s = pop_lsb(un_pinned_knights); | |
Bitboard knight_attacks = tables::attacks<KNIGHT>(s, data.all); | |
if constexpr (move_gen_type == ALL) push<QUIET>(s, knight_attacks & quiet_mask); | |
push<CAPTURE_TYPE>(s, knight_attacks & capture_mask); | |
} | |
Bitboard non_pinned_diag = data.us_diag_sliders & ~pinned; | |
while (non_pinned_diag) { | |
Square s = pop_lsb(non_pinned_diag); | |
Bitboard non_pinned_diag_attacks = tables::attacks<BISHOP>(s, data.all); | |
if constexpr (move_gen_type == ALL) push<QUIET>(s, non_pinned_diag_attacks & quiet_mask); | |
push<CAPTURE_TYPE>(s, non_pinned_diag_attacks & capture_mask); | |
} | |
Bitboard non_pinned_ortho = data.us_ortho_sliders & ~pinned; | |
while (non_pinned_ortho) { | |
Square s = pop_lsb(non_pinned_ortho); | |
Bitboard non_pinned_ortho_attacks = tables::attacks<ROOK>(s, data.all); | |
if constexpr (move_gen_type == ALL) push<QUIET>(s, non_pinned_ortho_attacks & quiet_mask); | |
push<CAPTURE_TYPE>(s, non_pinned_ortho_attacks & capture_mask); | |
} | |
} | |
template<Color color, MoveGenerationType move_gen_type> | |
constexpr void MoveList<color, move_gen_type>::push_non_pinned_pawns(const MoveList::SharedData &data, Bitboard pinned, | |
Bitboard quiet_mask, Bitboard capture_mask) { | |
Bitboard non_pinned_pawns = board_.occupancy<color, PAWN>() & ~pinned & ~MASK_RANK[relative_rank<color>(RANK7)]; | |
Bitboard left_pawn_captures = shift_relative<color, NORTH_WEST>(non_pinned_pawns) & capture_mask; | |
Bitboard right_pawn_captures = shift_relative<color, NORTH_EAST>(non_pinned_pawns) & capture_mask; | |
while (left_pawn_captures) { | |
Square s = pop_lsb(left_pawn_captures); | |
push_single<CAPTURE_TYPE>(s - relative_dir<color, NORTH_WEST>(), s); | |
} | |
while (right_pawn_captures) { | |
Square s = pop_lsb(right_pawn_captures); | |
push_single<CAPTURE_TYPE>(s - relative_dir<color, NORTH_EAST>(), s); | |
} | |
if constexpr (move_gen_type == CAPTURES) return; | |
Bitboard single_pawn_pushes = shift_relative<color, NORTH>(non_pinned_pawns) & ~data.all; | |
Bitboard double_pawn_pushes = single_pawn_pushes & MASK_RANK[relative_rank<color>(RANK3)]; | |
double_pawn_pushes = shift_relative<color, NORTH>(double_pawn_pushes) & quiet_mask; | |
single_pawn_pushes &= quiet_mask; | |
while (single_pawn_pushes) { | |
Square s = pop_lsb(single_pawn_pushes); | |
push_single<QUIET>(s - relative_dir<color, NORTH>(), s); | |
} | |
while (double_pawn_pushes) { | |
Square s = pop_lsb(double_pawn_pushes); | |
push_single<DOUBLE_PUSH>(s - relative_dir<color, NORTH_NORTH>(), s); | |
} | |
} | |
template<Color color, MoveGenerationType move_gen_type> | |
MoveList<color, move_gen_type>::MoveList(Position &board) : board_{board} { | |
const SharedData data(board_); | |
Bitboard danger = generate_danger(data); | |
push_check_evasions(data, danger); | |
Bitboard checkers{}, pinned{}; | |
std::tie(checkers, pinned) = generate_checkers_and_pinned(data); | |
Bitboard capture_mask, quiet_mask; | |
switch (pop_count(checkers)) { | |
case 2: return; | |
case 1: | |
if (push_pawn_knight_check_captures(data, checkers, pinned)) return; | |
capture_mask = checkers; | |
quiet_mask = tables::square_in_between(data.us_king_square, lsb(checkers)); | |
break; | |
default: | |
capture_mask = data.them_occupancy; | |
quiet_mask = ~data.all; | |
push_en_passant(data, pinned); | |
push_castle(data, danger); | |
push_pinned(data, pinned, quiet_mask, capture_mask); | |
break; | |
} | |
push_non_pinned_pieces(data, pinned, quiet_mask, capture_mask); | |
push_non_pinned_pawns(data, pinned, quiet_mask, capture_mask); | |
push_promotions(pinned, quiet_mask, capture_mask); | |
} | |
} // Namespace MidnightMoveGen |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment