diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 8a2ceca..83af1a3 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -42,32 +42,15 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest]
build_type: [Debug, Release]
- c_compiler: [gcc, clang, cl]
-
- include:
- # Windows
- - os: windows-latest
- c_compiler: cl
- cpp_compiler: cl
- - os: windows-latest
- c_compiler: clang
- cpp_compiler: clang++
- - os: windows-latest
- c_compiler: gcc
- cpp_compiler: g++
-
- # Linux
- - os: ubuntu-latest
- c_compiler: gcc
- cpp_compiler: g++
- - os: ubuntu-latest
- c_compiler: clang
- cpp_compiler: clang++
+ cpp_compiler: [g++, clang++, cl]
+ error_mode: [ASSERT, THROW, NONE]
exclude:
- os: ubuntu-latest
- c_compiler: cl
-
+ cpp_compiler: cl
+ - build_type: Release
+ error_mode: ASSERT
+
steps:
- uses: actions/checkout@v4
@@ -87,10 +70,10 @@ jobs:
fi
fi
cmake -B "${{ steps.vars.outputs.dir }}" \
- -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} \
-DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DSANITIZERS=${SANITIZERS} \
+ -DCHESSLIB_ERROR_MODE=${{ matrix.error_mode }} \
-DDART_TESTING_TIMEOUT=0 \
-S "${{ github.workspace }}"
diff --git a/.github/workflows/try_compile.yml b/.github/workflows/try_compile.yml
deleted file mode 100644
index be6f456..0000000
--- a/.github/workflows/try_compile.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-name: Compilation
-
-on:
- push:
- branches-ignore: main
-jobs:
- build:
- runs-on: ${{ matrix.os }}
-
- strategy:
- fail-fast: true
- matrix:
- os: [ubuntu-latest, windows-latest]
- build_type: [Debug]
- c_compiler: [gcc, clang, cl]
-
- include:
- # Windows
- - os: windows-latest
- c_compiler: cl
- cpp_compiler: cl
- - os: windows-latest
- c_compiler: clang
- cpp_compiler: clang++
- - os: windows-latest
- c_compiler: gcc
- cpp_compiler: g++
-
- # Linux
- - os: ubuntu-latest
- c_compiler: gcc
- cpp_compiler: g++
- - os: ubuntu-latest
- c_compiler: clang
- cpp_compiler: clang++
-
- exclude:
- - os: ubuntu-latest
- c_compiler: cl
-
- steps:
- - uses: actions/checkout@v4
-
- - name: Set build dir
- id: vars
- shell: bash
- run: echo "dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
-
- - name: Configure CMake
- shell: bash
- run: |
- cmake -B "${{ steps.vars.outputs.dir }}" \
- -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} \
- -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \
- -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
- -S "${{ github.workspace }}"
-
- - name: Build
- run: cmake --build ${{ steps.vars.outputs.dir }} --config ${{ matrix.build_type }}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1f9eebb..d2c8b5d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,45 +21,43 @@ project(chesslib LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
+# --- Core Library ---
+set(SOURCES
+ position.cpp
+ attacks.cpp "zobrist.cpp"
+ "moves_io.cpp" "printers.cpp" "movegen.cpp")
+add_library(chesslib STATIC ${SOURCES})
+target_include_directories(chesslib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+target_compile_definitions(chesslib PRIVATE GENERATE_AT_RUNTIME)
+set(CHESSLIB_ERROR_MODE "THROW" CACHE STRING "ASSERT|THROW|NONE")
+set_property(CACHE CHESSLIB_ERROR_MODE PROPERTY STRINGS ASSERT THROW NONE)
+
+if(NOT CHESSLIB_ERROR_MODE MATCHES "^(ASSERT|THROW|NONE)$")
+ message(FATAL_ERROR "Invalid ERROR_MODE: ${CHESSLIB_ERROR_MODE}")
+endif()
# --- Compiler tuning ---
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
# clang-cl: forward GCC/Clang style constexpr flags via /clang:
- add_compile_options(
- /clang:-fconstexpr-steps=2000000000
- /clang:-fconstexpr-depth=1024
+ target_compile_options(chesslib PRIVATE
/clang:-march=native
/clang:-mtune=native
- /clang:-ftemplate-backtrace-limit=0
)
else()
# native clang++ on *nix or Windows
- add_compile_options(
- -fconstexpr-steps=2000000000
- -fconstexpr-depth=1024
+ target_compile_options(chesslib PRIVATE
-march=native -mtune=native
- -ftemplate-backtrace-limit=0 -static
)
endif()
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
- add_compile_options(-fconstexpr-ops-limit=2000000000 -fconstexpr-depth=1024 -march=native -mtune=native -ftemplate-backtrace-limit=0 -static)
+ target_compile_options(chesslib PRIVATE -march=native -mtune=native)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(ARCH_FLAG "/arch:AVX2" CACHE STRING "MSVC architecture flag (/arch:SSE2, /arch:AVX, /arch:AVX2, /arch:AVX512)")
- add_compile_options(/constexpr:steps2000000000 /constexpr:depth1024 ${ARCH_FLAG})
+ target_compile_options(chesslib PRIVATE ${ARCH_FLAG})
endif()
-add_compile_definitions(GENERATE_AT_RUNTIME)
-if(CMAKE_BUILD_TYPE MATCHES "Debug")
- add_compile_definitions(_DEBUG)
-endif()
-# --- Core Library ---
-set(SOURCES
- position.cpp
- attacks.cpp "zobrist.cpp"
- "moves_io.cpp" "printers.cpp" "movegen.cpp")
-add_library(chesslib STATIC ${SOURCES})
-target_include_directories(chesslib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+target_compile_definitions(chesslib PUBLIC _CHESSLIB_ERROR_MODE_${CHESSLIB_ERROR_MODE})
# --- Enable CTest integration ---
include(CTest)
@@ -81,9 +79,9 @@ if(BUILD_TESTING)
target_link_libraries(NonImportantTests PRIVATE chesslib doctest::doctest)
target_link_libraries(test_chess960 PRIVATE chesslib doctest::doctest)
- add_test(NAME test_normal COMMAND test_normal)
- add_test(NAME test_api COMMAND NonImportantTests)
- add_test(NAME test_chess960 COMMAND test_chess960)
+ add_test(NAME test_normal COMMAND test_normal --abort-after=1)
+ add_test(NAME test_api COMMAND NonImportantTests --abort-after=1)
+ add_test(NAME test_chess960 COMMAND test_chess960 --abort-after=1)
if (UNIX AND CMAKE_BUILD_TYPE MATCHES "Debug")
set(SANITIZERS "" CACHE STRING "sanitizers such as undefined,address")
diff --git a/attacks.cpp b/attacks.cpp
index ee5a025..a7abe64 100644
--- a/attacks.cpp
+++ b/attacks.cpp
@@ -16,12 +16,17 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
+
+/// @file attacks.cpp
+/// @brief Magic-bitboard generation, hyperbola-quintessence helpers, and between-square table.
+
#include "attacks.h"
namespace chess::_chess {
// [INTERNAL]
-// Reverse bits horizontally in 64-bit integer
+/// @brief Reverse bits horizontally in a 64-bit integer.
+/// @details Used by the hyperbola quintessence algorithm.
static constexpr Bitboard reverse(Bitboard b) {
b = (b & 0x5555555555555555ULL) << 1 | ((b >> 1) & 0x5555555555555555ULL);
b = (b & 0x3333333333333333ULL) << 2 | ((b >> 2) & 0x3333333333333333ULL);
@@ -32,6 +37,11 @@ static constexpr Bitboard reverse(Bitboard b) {
return b;
}
+/// @brief Hyperbola quintessence attack computation for a single line.
+/// @param sliderBB Bitboard with the slider's square set.
+/// @param occ Occupancy bitboard.
+/// @param mask Line mask (rank, file, or diagonal).
+/// @return Attacks along the masked line.
static constexpr Bitboard hyp_quint(Bitboard sliderBB, Bitboard occ, Bitboard mask) {
Bitboard occ_masked = occ & mask;
Bitboard left = occ_masked - 2 * sliderBB;
@@ -39,7 +49,7 @@ static constexpr Bitboard hyp_quint(Bitboard sliderBB, Bitboard occ, Bitboard ma
return (left ^ reverse(right)) & mask;
}
-// For Bishop: Mask for diagonal and anti-diagonal
+/// @brief Compute the diagonal mask through a square.
static constexpr Bitboard diag_mask(Square sq) {
int r = rank_of(sq);
int f = file_of(sq);
@@ -53,6 +63,7 @@ static constexpr Bitboard diag_mask(Square sq) {
}
return mask;
}
+/// @brief Compute the anti-diagonal mask through a square.
static constexpr Bitboard antidiag_mask(Square sq) {
int r = rank_of(sq);
int f = file_of(sq);
@@ -68,7 +79,7 @@ static constexpr Bitboard antidiag_mask(Square sq) {
return mask;
}
-// Hyperbola Quintessence for Bishop
+/// @brief Bishop attacks via hyperbola quintessence.
static constexpr Bitboard _HyperbolaBishopAttacks(Square sq, Bitboard occ) {
Bitboard slider = 1ULL << sq;
Bitboard d_mask = diag_mask(sq);
@@ -76,12 +87,13 @@ static constexpr Bitboard _HyperbolaBishopAttacks(Square sq, Bitboard occ) {
return hyp_quint(slider, occ, d_mask) | hyp_quint(slider, occ, ad_mask);
}
-// For Rook: Rank and File Masks
+/// @brief Rank mask for a square.
static constexpr Bitboard rank_mask(Square sq) { return attacks::MASK_RANK[rank_of(sq)]; }
+/// @brief File mask for a square.
static constexpr Bitboard file_mask(Square sq) { return attacks::MASK_FILE[file_of(sq)]; }
-// Hyperbola Quintessence for Rook
+/// @brief Rook attacks via hyperbola quintessence.
static constexpr Bitboard _HyperbolaRookAttacks(Square sq, Bitboard occ) {
Bitboard slider = 1ULL << sq;
Bitboard r_mask = rank_mask(sq);
@@ -134,6 +146,12 @@ _POSSIBLY_CONSTEXPR std::array BishopMagics = {
};
// clang-format on
+
+/// @brief Generate magic-bitboard lookup tables.
+/// @tparam AttackFunc The hyperbola attack function to use.
+/// @tparam TableSize Total number of attack entries.
+/// @tparam IsBishop true for bishop, false for rook.
+/// @return Pair of (magic table, attack table).
template
_POSSIBLY_CONSTEXPR std::pair, std::array> generate_magic_table() {
std::array table{};
@@ -184,34 +202,27 @@ _POSSIBLY_CONSTEXPR std::pair, std::array();
_POSSIBLY_CONSTEXPR std::array RookTable = rookData.first;
_POSSIBLY_CONSTEXPR std::array RookAttacks = rookData.second;
-/**
- * @brief Returns the bishop attacks for a given square
- * @param sq
- * @param occupied
- * @return
- */
+
+/// @brief Look up bishop attacks from the precomputed magic table.
[[nodiscard]] Bitboard bishop(Square sq, Bitboard occupied) {
return BishopAttacks[BishopTable[(int)sq].index + BishopTable[(int)sq](occupied)];
}
-/**
- * @brief Returns the rook attacks for a given square
- * @param sq
- * @param occupied
- * @return
- */
+/// @brief Look up rook attacks from the precomputed magic table.
[[nodiscard]] Bitboard rook(Square sq, Bitboard occupied) {
return RookAttacks[RookTable[(int)sq].index + RookTable[(int)sq](occupied)];
}
-
} // namespace chess::attacks
namespace chess::movegen {
+
+/// @brief Hyperbola attack for bishop or rook (used for between-table generation).
inline static Bitboard att(PieceType pt, Square sq, Bitboard occ) {
return (pt == BISHOP) ? chess::_chess::_HyperbolaBishopAttacks(sq, occ) : chess::_chess::_HyperbolaRookAttacks(sq, occ);
}
-inline static std::array, SQ_NONE + 1> generate_between() {
- std::array, SQ_NONE + 1> squares_between_bb{};
+/// @brief Generate the between-square table at program startup.
+inline static std::array, 64> generate_between() {
+ std::array, 64> squares_between_bb{};
for (int sq1 = 0; sq1 < 64; ++sq1) {
for (PieceType pt : { BISHOP, ROOK }) {
@@ -226,5 +237,5 @@ inline static std::array, SQ_NONE + 1> generat
return squares_between_bb;
}
-std::array, SQ_NONE + 1> SQUARES_BETWEEN_BB = generate_between();
+std::array, 64> SQUARES_BETWEEN_BB = generate_between();
} // namespace chess::movegen
diff --git a/attacks.h b/attacks.h
index c3e1654..8028ca3 100644
--- a/attacks.h
+++ b/attacks.h
@@ -24,95 +24,103 @@
#include
#include
#include
+
+/// @file attacks.h
+/// @brief Precomputed attack tables and magic-bitboard lookup functions.
+
namespace chess::attacks {
-// clang-format off
- // pre-calculated lookup table for pawn attacks
- constexpr Bitboard PawnAttacks[2][64] = {
- // 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 },
- // 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
- }
- };
+/// @brief Precomputed pawn-attack bitboards.
+/// @details Indexed as PawnAttacks[color][square].
+constexpr Bitboard PawnAttacks[2][64] = {
+ // clang-format off
+ // 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 },
+
+ // 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
+ }
+ // clang-format on
+};
- // pre-calculated lookup table for knight attacks
- constexpr Bitboard KnightAttacks[64] = {
- 0x0000000000020400, 0x0000000000050800, 0x00000000000A1100, 0x0000000000142200,
- 0x0000000000284400, 0x0000000000508800, 0x0000000000A01000, 0x0000000000402000,
- 0x0000000002040004, 0x0000000005080008, 0x000000000A110011, 0x0000000014220022,
- 0x0000000028440044, 0x0000000050880088, 0x00000000A0100010, 0x0000000040200020,
- 0x0000000204000402, 0x0000000508000805, 0x0000000A1100110A, 0x0000001422002214,
- 0x0000002844004428, 0x0000005088008850, 0x000000A0100010A0, 0x0000004020002040,
- 0x0000020400040200, 0x0000050800080500, 0x00000A1100110A00, 0x0000142200221400,
- 0x0000284400442800, 0x0000508800885000, 0x0000A0100010A000, 0x0000402000204000,
- 0x0002040004020000, 0x0005080008050000, 0x000A1100110A0000, 0x0014220022140000,
- 0x0028440044280000, 0x0050880088500000, 0x00A0100010A00000, 0x0040200020400000,
- 0x0204000402000000, 0x0508000805000000, 0x0A1100110A000000, 0x1422002214000000,
- 0x2844004428000000, 0x5088008850000000, 0xA0100010A0000000, 0x4020002040000000,
- 0x0400040200000000, 0x0800080500000000, 0x1100110A00000000, 0x2200221400000000,
- 0x4400442800000000, 0x8800885000000000, 0x100010A000000000, 0x2000204000000000,
- 0x0004020000000000, 0x0008050000000000, 0x00110A0000000000, 0x0022140000000000,
- 0x0044280000000000, 0x0088500000000000, 0x0010A00000000000, 0x0020400000000000};
+/// @brief Precomputed knight-attack bitboards.
+/// @details Indexed by square.
+constexpr Bitboard KnightAttacks[64] = {
+ 0x0000000000020400, 0x0000000000050800, 0x00000000000A1100, 0x0000000000142200, 0x0000000000284400, 0x0000000000508800,
+ 0x0000000000A01000, 0x0000000000402000, 0x0000000002040004, 0x0000000005080008, 0x000000000A110011, 0x0000000014220022,
+ 0x0000000028440044, 0x0000000050880088, 0x00000000A0100010, 0x0000000040200020, 0x0000000204000402, 0x0000000508000805,
+ 0x0000000A1100110A, 0x0000001422002214, 0x0000002844004428, 0x0000005088008850, 0x000000A0100010A0, 0x0000004020002040,
+ 0x0000020400040200, 0x0000050800080500, 0x00000A1100110A00, 0x0000142200221400, 0x0000284400442800, 0x0000508800885000,
+ 0x0000A0100010A000, 0x0000402000204000, 0x0002040004020000, 0x0005080008050000, 0x000A1100110A0000, 0x0014220022140000,
+ 0x0028440044280000, 0x0050880088500000, 0x00A0100010A00000, 0x0040200020400000, 0x0204000402000000, 0x0508000805000000,
+ 0x0A1100110A000000, 0x1422002214000000, 0x2844004428000000, 0x5088008850000000, 0xA0100010A0000000, 0x4020002040000000,
+ 0x0400040200000000, 0x0800080500000000, 0x1100110A00000000, 0x2200221400000000, 0x4400442800000000, 0x8800885000000000,
+ 0x100010A000000000, 0x2000204000000000, 0x0004020000000000, 0x0008050000000000, 0x00110A0000000000, 0x0022140000000000,
+ 0x0044280000000000, 0x0088500000000000, 0x0010A00000000000, 0x0020400000000000
+};
+
+/// @brief Precomputed king-attack bitboards.
+/// @details Indexed by square.
+constexpr Bitboard KingAttacks[64] = {
+ 0x0000000000000302, 0x0000000000000705, 0x0000000000000E0A, 0x0000000000001C14, 0x0000000000003828, 0x0000000000007050,
+ 0x000000000000E0A0, 0x000000000000C040, 0x0000000000030203, 0x0000000000070507, 0x00000000000E0A0E, 0x00000000001C141C,
+ 0x0000000000382838, 0x0000000000705070, 0x0000000000E0A0E0, 0x0000000000C040C0, 0x0000000003020300, 0x0000000007050700,
+ 0x000000000E0A0E00, 0x000000001C141C00, 0x0000000038283800, 0x0000000070507000, 0x00000000E0A0E000, 0x00000000C040C000,
+ 0x0000000302030000, 0x0000000705070000, 0x0000000E0A0E0000, 0x0000001C141C0000, 0x0000003828380000, 0x0000007050700000,
+ 0x000000E0A0E00000, 0x000000C040C00000, 0x0000030203000000, 0x0000070507000000, 0x00000E0A0E000000, 0x00001C141C000000,
+ 0x0000382838000000, 0x0000705070000000, 0x0000E0A0E0000000, 0x0000C040C0000000, 0x0003020300000000, 0x0007050700000000,
+ 0x000E0A0E00000000, 0x001C141C00000000, 0x0038283800000000, 0x0070507000000000, 0x00E0A0E000000000, 0x00C040C000000000,
+ 0x0302030000000000, 0x0705070000000000, 0x0E0A0E0000000000, 0x1C141C0000000000, 0x3828380000000000, 0x7050700000000000,
+ 0xE0A0E00000000000, 0xC040C00000000000, 0x0203000000000000, 0x0507000000000000, 0x0A0E000000000000, 0x141C000000000000,
+ 0x2838000000000000, 0x5070000000000000, 0xA0E0000000000000, 0x40C0000000000000
+};
- // pre-calculated lookup table for king attacks
- constexpr Bitboard KingAttacks[64] = {
- 0x0000000000000302, 0x0000000000000705, 0x0000000000000E0A, 0x0000000000001C14,
- 0x0000000000003828, 0x0000000000007050, 0x000000000000E0A0, 0x000000000000C040,
- 0x0000000000030203, 0x0000000000070507, 0x00000000000E0A0E, 0x00000000001C141C,
- 0x0000000000382838, 0x0000000000705070, 0x0000000000E0A0E0, 0x0000000000C040C0,
- 0x0000000003020300, 0x0000000007050700, 0x000000000E0A0E00, 0x000000001C141C00,
- 0x0000000038283800, 0x0000000070507000, 0x00000000E0A0E000, 0x00000000C040C000,
- 0x0000000302030000, 0x0000000705070000, 0x0000000E0A0E0000, 0x0000001C141C0000,
- 0x0000003828380000, 0x0000007050700000, 0x000000E0A0E00000, 0x000000C040C00000,
- 0x0000030203000000, 0x0000070507000000, 0x00000E0A0E000000, 0x00001C141C000000,
- 0x0000382838000000, 0x0000705070000000, 0x0000E0A0E0000000, 0x0000C040C0000000,
- 0x0003020300000000, 0x0007050700000000, 0x000E0A0E00000000, 0x001C141C00000000,
- 0x0038283800000000, 0x0070507000000000, 0x00E0A0E000000000, 0x00C040C000000000,
- 0x0302030000000000, 0x0705070000000000, 0x0E0A0E0000000000, 0x1C141C0000000000,
- 0x3828380000000000, 0x7050700000000000, 0xE0A0E00000000000, 0xC040C00000000000,
- 0x0203000000000000, 0x0507000000000000, 0x0A0E000000000000, 0x141C000000000000,
- 0x2838000000000000, 0x5070000000000000, 0xA0E0000000000000, 0x40C0000000000000};
- constexpr Bitboard MASK_RANK[8] = {
- 0xff, 0xff00, 0xff0000, 0xff000000,
- 0xff00000000, 0xff0000000000, 0xff000000000000, 0xff00000000000000};
+/// @brief Per-rank mask (rank 0-7).
+constexpr Bitboard MASK_RANK[8] = { 0xff, 0xff00, 0xff0000, 0xff000000,
+ 0xff00000000, 0xff0000000000, 0xff000000000000, 0xff00000000000000 };
+
+/// @brief Per-file mask (file 0-7).
+constexpr Bitboard MASK_FILE[8] = {
+ 0x101010101010101, 0x202020202020202, 0x404040404040404, 0x808080808080808,
+ 0x1010101010101010, 0x2020202020202020, 0x4040404040404040, 0x8080808080808080,
+};
- constexpr Bitboard MASK_FILE[8] = {
- 0x101010101010101, 0x202020202020202, 0x404040404040404, 0x808080808080808,
- 0x1010101010101010, 0x2020202020202020, 0x4040404040404040, 0x8080808080808080,
- };
-// clang-format on
#ifdef __BMI2__
+/// @brief Software fallback for the PEXT instruction.
+/// @details Used during constant evaluation when BMI2 is unavailable.
+/// @param val The value to compress.
+/// @param mask The bit mask.
+/// @return Compressed bits.
constexpr uint64_t software_pext_u64(uint64_t val, uint64_t mask) {
uint64_t result = 0;
uint64_t bit_position = 0;
@@ -127,9 +135,11 @@ constexpr uint64_t software_pext_u64(uint64_t val, uint64_t mask) {
}
return result;
}
+
+/// @brief Magic structure for PEXT-based magic bitboards (BMI2 path).
struct Magic {
- Bitboard mask;
- int index;
+ Bitboard mask; ///< Relevant occupancy mask.
+ int index; ///< Starting index into the attack table.
constexpr Bitboard operator()(Bitboard b) const {
if (is_constant_evaluated()) {
return software_pext_u64(b, mask);
@@ -139,23 +149,23 @@ struct Magic {
}
};
#else
+/// @brief Magic structure for classical (multiply-and-shift) magic bitboards.
struct Magic {
- Bitboard mask;
- Bitboard magic;
- size_t index;
- Bitboard shift;
+ Bitboard mask; ///< Relevant occupancy mask.
+ Bitboard magic; ///< Magic multiplier.
+ size_t index; ///< Starting index into the attack table.
+ Bitboard shift; ///< Right-shift amount.
constexpr Bitboard operator()(Bitboard b) const { return (((b & mask)) * magic) >> shift; }
};
#endif
} // namespace chess::attacks
namespace chess::attacks {
-/**
- * @brief Shifts a bitboard in a given direction
- * @tparam direction
- * @param b
- * @return
- */
+
+/// @brief Shift a bitboard in the given direction.
+/// @param b The bitboard.
+/// @param direction Direction to shift.
+/// @return Shifted bitboard.
[[nodiscard]] static constexpr Bitboard shift(const Bitboard b, Direction direction) {
switch (direction) {
case Direction::NORTH:
@@ -176,31 +186,24 @@ namespace chess::attacks {
return (b & ~MASK_FILE[7]) >> 7;
case DOUBLE_NORTH:
return b << 16;
-
case DOUBLE_SOUTH:
return b >> 16;
-
case DOUBLE_EAST:
return (b & ~MASK_FILE[7] & ~(MASK_FILE[7] >> 1)) << 2;
-
case DOUBLE_WEST:
return (b & ~MASK_FILE[0] & ~(MASK_FILE[0] << 1)) >> 2;
-
case DOUBLE_NORTH_EAST: {
Bitboard t = (b & ~MASK_FILE[7]) << 9;
return (t & ~MASK_FILE[7]) << 9;
}
-
case DOUBLE_NORTH_WEST: {
Bitboard t = (b & ~MASK_FILE[0]) << 7;
return (t & ~MASK_FILE[0]) << 7;
}
-
case DOUBLE_SOUTH_EAST: {
Bitboard t = (b & ~MASK_FILE[7]) >> 7;
return (t & ~MASK_FILE[7]) >> 7;
}
-
case DOUBLE_SOUTH_WEST: {
Bitboard t = (b & ~MASK_FILE[0]) >> 9;
return (t & ~MASK_FILE[0]) >> 9;
@@ -212,117 +215,93 @@ namespace chess::attacks {
return 0;
}
}
-/**
- * @brief Shifts a bitboard in a given direction
- * @tparam direction
- * @param b
- * @return
- */
+
+/// @brief Template wrapper for shift in a compile-time-known direction.
template [[nodiscard]] static constexpr Bitboard shift(const Bitboard b) { return shift(b, direction); }
-/**
- * @brief
- * @tparam c
- * @param pawns
- * @return
- */
+/// @brief Generate left-side pawn attacks for the given colour.
+/// @tparam c Colour.
+/// @param pawns Bitboard of pawns.
+/// @return Bitboard of left-capture target squares.
template [[nodiscard]] constexpr Bitboard pawnLeftAttacks(const Bitboard pawns) {
ASSUME(c == WHITE || c == BLACK);
return c == WHITE ? (pawns << 7) & ~MASK_FILE[7] : (pawns >> 7) & ~MASK_FILE[0];
}
-/**
- * @brief Generate the right side pawn attacks.
- * @tparam c
- * @param pawns
- * @return
- */
+/// @brief Generate right-side pawn attacks for the given colour.
+/// @tparam c Colour.
+/// @param pawns Bitboard of pawns.
+/// @return Bitboard of right-capture target squares.
template [[nodiscard]] constexpr Bitboard pawnRightAttacks(const Bitboard pawns) {
ASSUME(c == WHITE || c == BLACK);
return c == WHITE ? (pawns << 9) & ~MASK_FILE[0] : (pawns >> 9) & ~MASK_FILE[7];
}
-/**
- * @brief Generate the right side pawn attacks.
- * @tparam c
- * @param pawns
- * @return
- */
+/// @brief Generate all pawn attacks from a bitboard of pawns.
+/// @tparam c Colour.
+/// @param pawns Bitboard of pawns.
+/// @return Bitboard of all squares attacked by the pawns.
template [[nodiscard]] constexpr Bitboard pawn(const Bitboard pawns) {
ASSUME(c == WHITE || c == BLACK);
if constexpr (c == WHITE) {
- return ((pawns & ~MASK_FILE[FILE_A]) << 7) | // left captures
- ((pawns & ~MASK_FILE[FILE_H]) << 9); // right captures
+ return ((pawns & ~MASK_FILE[FILE_A]) << 7) | ((pawns & ~MASK_FILE[FILE_H]) << 9);
} else {
return ((pawns & ~MASK_FILE[FILE_H]) >> 7) | ((pawns & ~MASK_FILE[FILE_A]) >> 9);
}
}
-/**
- * @brief Returns the pawn attacks for a given color and square
- * @param c
- * @param sq
- * @return
- */
+/// @brief Look up pawn attacks for a single square.
+/// @param c Colour.
+/// @param sq Square.
+/// @return Bitboard of squares attacked.
[[nodiscard]] constexpr Bitboard pawn(Color c, Square sq) { return PawnAttacks[(int)c][(int)sq]; }
-/**
- * @brief Returns the knight attacks for a given square
- * @param sq
- * @return
- */
+/// @brief Look up knight attacks for a single square.
+/// @param sq Square.
+/// @return Bitboard of squares attacked.
[[nodiscard]] constexpr Bitboard knight(Square sq) { return KnightAttacks[(int)sq]; }
-/**
- * @brief Returns the knight attacks for given knights
- * @param sq
- * @return
- */
+
+/// @brief Compute knight attacks for a bitboard of knights.
+/// @param knights Bitboard of knights.
+/// @return Bitboard of all squares attacked.
[[nodiscard]] constexpr Bitboard knight(Bitboard knights) {
- Bitboard l1 = (knights >> 1) & 0x7f7f7f7f7f7f7f7fULL; // shift left by 1, mask out file A
- Bitboard l2 = (knights >> 2) & 0x3f3f3f3f3f3f3f3fULL; // shift left by 2, mask out files A+B
- Bitboard r1 = (knights << 1) & 0xfefefefefefefefeULL; // shift right by 1, mask out file H
- Bitboard r2 = (knights << 2) & 0xfcfcfcfcfcfcfcfcULL; // shift right by 2, mask out files G+H
- Bitboard h1 = l1 | r1; // 1-square horizontal shifts
- Bitboard h2 = l2 | r2; // 2-square horizontal shifts
- return (h1 << 16) | (h1 >> 16) | (h2 << 8) | (h2 >> 8); // vertical shifts: +2,+1,-2,-1
+ Bitboard l1 = (knights >> 1) & 0x7f7f7f7f7f7f7f7fULL;
+ Bitboard l2 = (knights >> 2) & 0x3f3f3f3f3f3f3f3fULL;
+ Bitboard r1 = (knights << 1) & 0xfefefefefefefefeULL;
+ Bitboard r2 = (knights << 2) & 0xfcfcfcfcfcfcfcfcULL;
+ Bitboard h1 = l1 | r1;
+ Bitboard h2 = l2 | r2;
+ return (h1 << 16) | (h1 >> 16) | (h2 << 8) | (h2 >> 8);
}
-/**
- * @brief Returns the bishop attacks for a given square
- * @param sq
- * @param occupied
- * @return
- */
+
+/// @brief Look up bishop attacks via magic bitboard (defined in attacks.cpp).
+/// @param sq Bishop square.
+/// @param occupied Occupancy bitboard.
+/// @return Bitboard of squares attacked.
[[nodiscard]] Bitboard bishop(Square sq, Bitboard occupied);
-/**
- * @brief Returns the rook attacks for a given square
- * @param sq
- * @param occupied
- * @return
- */
+/// @brief Look up rook attacks via magic bitboard (defined in attacks.cpp).
+/// @param sq Rook square.
+/// @param occupied Occupancy bitboard.
+/// @return Bitboard of squares attacked.
[[nodiscard]] Bitboard rook(Square sq, Bitboard occupied);
-/**
- * @brief Returns the queen attacks for a given square
- * @param sq
- * @param occupied
- * @return
- */
+
+/// @brief Compute queen attacks (bishop | rook).
+/// @param sq Queen square.
+/// @param occupied Occupancy bitboard.
+/// @return Bitboard of squares attacked.
[[nodiscard]] inline Bitboard queen(Square sq, Bitboard occupied) { return bishop(sq, occupied) | rook(sq, occupied); }
-/**
- * @brief Returns the king attacks for a given square
- * @param sq
- * @return
- */
+/// @brief Look up king attacks for a single square.
+/// @param sq Square.
+/// @return Bitboard of squares attacked.
[[nodiscard]] constexpr Bitboard king(Square sq) { return KingAttacks[(int)sq]; }
-/**
- * @brief Returns the slider attacks for a given square
- * @param sq
- * @param occupied
- * @tparam pt
- * @return
- */
+/// @brief Template dispatcher for slider attacks (bishop / rook / queen).
+/// @tparam pt Piece type (must be a slider).
+/// @param sq Square.
+/// @param occupied Occupancy bitboard.
+/// @return Bitboard of squares attacked.
template [[nodiscard]] inline Bitboard slider(Square sq, Bitboard occupied) {
static_assert(pt == PieceType::BISHOP || pt == PieceType::ROOK || pt == PieceType::QUEEN, "PieceType must be a slider!");
diff --git a/bitboard.h b/bitboard.h
index 0aa7eae..7a6002b 100644
--- a/bitboard.h
+++ b/bitboard.h
@@ -23,10 +23,15 @@
#include
#endif
#include
+
+/// @file bitboard.h
+/// @brief Bitboard utility functions (popcount, LSB, MSB, etc.).
+
namespace chess {
-// -------------------------------
-// constexpr fallbacks
-// -------------------------------
+
+/// @brief constexpr fallback for population count.
+/// @param x Input bitboard.
+/// @return Number of set bits.
constexpr int popcount_constexpr(Bitboard x) noexcept {
int count = 0;
while (x) {
@@ -36,6 +41,9 @@ constexpr int popcount_constexpr(Bitboard x) noexcept {
return count;
}
+/// @brief constexpr fallback for least-significant bit index.
+/// @param x Input bitboard.
+/// @return Index of the lowest set bit (0-based).
constexpr int lsb_constexpr(Bitboard x) noexcept {
int pos = 0;
while ((x & 1) == 0) {
@@ -45,6 +53,9 @@ constexpr int lsb_constexpr(Bitboard x) noexcept {
return pos;
}
+/// @brief constexpr fallback for most-significant bit index.
+/// @param x Input bitboard.
+/// @return Index of the highest set bit (0-based).
constexpr int msb_constexpr(Bitboard x) noexcept {
int pos = 63;
Bitboard mask = 1ULL << 63;
@@ -55,9 +66,9 @@ constexpr int msb_constexpr(Bitboard x) noexcept {
return pos;
}
-// -------------------------------
-// runtime + constexpr aware
-// -------------------------------
+/// @brief Population count (uses hardware POPCNT when available).
+/// @param x Input bitboard.
+/// @return Number of set bits.
#if defined(__GNUG__) || defined(__clang__)
[[gnu::const]]
#endif
@@ -72,6 +83,9 @@ inline constexpr int popcount(Bitboard x) noexcept {
return popcount_constexpr(x);
}
+/// @brief Least-significant bit index (uses hardware BSF when available).
+/// @param x Input bitboard (must be non-zero).
+/// @return Index of the lowest set bit.
#if defined(__GNUG__) || defined(__clang__)
[[gnu::const]]
#endif
@@ -89,6 +103,9 @@ inline constexpr int lsb(Bitboard x) noexcept {
return lsb_constexpr(x);
}
+/// @brief Most-significant bit index (uses hardware BSR when available).
+/// @param x Input bitboard (must be non-zero).
+/// @return Index of the highest set bit.
#if defined(__GNUG__) || defined(__clang__)
[[gnu::const]]
#endif
@@ -106,9 +123,9 @@ inline constexpr int msb(Bitboard x) noexcept {
return msb_constexpr(x);
}
-// -------------------------------
-// destructive variants
-// -------------------------------
+/// @brief Extract and pop the least-significant bit (destructive).
+/// @param b Bitboard reference; modified in place.
+/// @return Index of the lowest set bit before removal.
inline int pop_lsb(Bitboard &b) noexcept {
int c = lsb(b);
#ifndef __BMI2__
@@ -119,6 +136,9 @@ inline int pop_lsb(Bitboard &b) noexcept {
return c;
}
+/// @brief Extract and pop the most-significant bit (destructive).
+/// @param b Bitboard reference; modified in place.
+/// @return Index of the highest set bit before removal.
inline int pop_msb(Bitboard &b) noexcept {
int c = msb(b);
b &= ~(1ULL << c);
diff --git a/chess960_tests.cpp b/chess960_tests.cpp
index 8730c99..25c3b5c 100644
--- a/chess960_tests.cpp
+++ b/chess960_tests.cpp
@@ -16,8 +16,10 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-#define DOCTEST_CONFIG_IMPLEMENT
+#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
+#if !defined(__cpp_exceptions) && !defined(_CPPUNWIND) && !defined(__EXCEPTIONS) && !defined(_CHESSLIB_ERROR_MODE_THROW)
#define DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+#endif
#include "moves_io.h"
#include "position.h"
#include "printers.h"
@@ -25,7 +27,7 @@
#include
#include
using namespace chess;
-#if defined(_DEBUG) || !defined(NDEBUG)
+#if !defined(NDEBUG)
#define IS_RELEASE 0
#else
#define IS_RELEASE 1
@@ -38,17 +40,21 @@ template struct TestEntry {
InputT input;
CheckInfo info;
};
-template uint64_t perft(_Position &pos, int depth) {
+template uint64_t perft(_Position &pos, int depth) {
if (depth == 0) {
return 1;
} else if (depth == 1) {
- Movelist moves;
- pos.template legals(moves);
- if constexpr (EnableDiv)
- for (const Move &m : moves) {
+ if constexpr (EnableDiv) {
+ Movelist moves;
+ pos.template legals(moves);
+ for (const Move &m : moves)
std::cout << m << ": 1\n";
- }
- return moves.size();
+ return moves.size();
+ } else {
+ CountOnlyList moves;
+ pos.template legals(moves);
+ return moves.size_;
+ }
} else {
Movelist moves;
pos.template legals(moves);
@@ -57,32 +63,27 @@ template uint64_t perft(_Po
pos.template doMove(m);
#if !IS_RELEASE
{
- const auto pre_nm_hash_1 = pos.hash();
- const auto pre_nm_fen_1 = pos.fen();
- if (pos.zobrist() != pos.hash())
- REQUIRE(pos.zobrist() == pos.hash());
+ const auto pre_nm_hash = pos.hash();
pos.doNullMove();
pos.undoMove();
- if (!(pos.hash() == pre_nm_hash_1 && pos.fen() == pre_nm_fen_1 && pos.zobrist() == pre_nm_hash_1)) {
- REQUIRE(pos.hash() == pre_nm_hash_1);
- REQUIRE(pos.fen() == pre_nm_fen_1);
- REQUIRE(pos.zobrist() == pre_nm_hash_1);
+ if (pos.hash() != pre_nm_hash || pos.zobrist() != pre_nm_hash) {
+ // Compute fen() only on failure (extremely rare)
+ const auto post_fen = pos.fen();
+ REQUIRE(!"Hash changed after null move");
+ REQUIRE(pos.zobrist() == pre_nm_hash);
}
}
#endif
- const uint64_t nodes = perft(pos, depth - 1);
+ const uint64_t nodes = perft(pos, depth - 1);
#if !IS_RELEASE
{
- const auto pre_nm_hash_1 = pos.hash();
- const auto pre_nm_fen_1 = pos.fen();
- if (pos.zobrist() != pos.hash())
- REQUIRE(pos.zobrist() == pos.hash());
+ const auto pre_nm_hash = pos.hash();
pos.doNullMove();
pos.undoMove();
- if (!(pos.hash() == pre_nm_hash_1 && pos.fen() == pre_nm_fen_1 && pos.zobrist() == pre_nm_hash_1)) {
- REQUIRE(pos.hash() == pre_nm_hash_1);
- REQUIRE(pos.fen() == pre_nm_fen_1);
- REQUIRE(pos.zobrist() == pre_nm_hash_1);
+ if (pos.hash() != pre_nm_hash || pos.zobrist() != pre_nm_hash) {
+ const auto post_fen = pos.fen();
+ REQUIRE(!"Hash changed after null move");
+ REQUIRE(pos.zobrist() == pre_nm_hash);
}
}
#endif
@@ -135,63 +136,51 @@ auto split_testcases(std::vector> &entries) {
return optimized;
}
#endif
+template
+void check_perft_type(TestEntry &entry, uint64_t &nodes, double &elapsed) {
+ using namespace std::chrono;
+ _Position pos(entry.input, true);
+ auto start_time = high_resolution_clock::now();
+ if (pos.side_to_move() == WHITE)
+ REQUIRE(perft(pos, entry.info.depth) == entry.info.nodes);
+ else
+ REQUIRE(perft(pos, entry.info.depth) == entry.info.nodes);
+ auto end_time = high_resolution_clock::now();
+ elapsed += duration(end_time - start_time).count();
+ nodes += entry.info.nodes;
+ if (entry.info.nodes < 5e6) {
+ _Position pos2 = pos;
+ REQUIRE(pos.fen() == pos2.fen());
+ start_time = high_resolution_clock::now();
+ if (pos2.side_to_move() == WHITE)
+ REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes);
+ else
+ REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes);
+ end_time = high_resolution_clock::now();
+ elapsed += duration(end_time - start_time).count();
+ nodes += entry.info.nodes;
+ } else {
+ std::cerr << "\n(skipped copying test)\n";
+ }
+}
template
void check_perfts(std::vector> &entries) {
uint64_t nodes = 0;
double elapsed = 0;
- using namespace std::chrono;
#if !IS_RELEASE
entries = split_testcases(entries);
#endif
- auto start_time = high_resolution_clock::now();
for (auto &entry : entries) {
std::cerr << entry.input << " (chess960=true) " << entry.info.depth;
std::cerr << '\n';
- {
- _Position pos(entry.input, true);
- REQUIRE(perft(pos, entry.info.depth) == entry.info.nodes);
- nodes += entry.info.nodes;
- if (entry.info.nodes < 5e6) {
- _Position pos2 = pos;
- REQUIRE(pos.fen() == pos2.fen());
- REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes);
- nodes += entry.info.nodes;
- } else {
- std::cerr << "\n(skipped copying test)\n";
- }
- }
- {
- _Position pos(entry.input, true);
- REQUIRE(perft(pos, entry.info.depth) == entry.info.nodes);
- nodes += entry.info.nodes;
- if (entry.info.nodes < 5e6) {
- _Position pos2 = pos;
- REQUIRE(pos.fen() == pos2.fen());
- REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes);
- nodes += entry.info.nodes;
- } else {
- std::cerr << "\n(skipped copying test)\n";
- }
- }
- {
- _Position pos(entry.input, true);
- REQUIRE(perft(pos, entry.info.depth) == entry.info.nodes);
- nodes += entry.info.nodes;
- if (entry.info.nodes < 5e6) {
- _Position pos2 = pos;
- REQUIRE(pos.fen() == pos2.fen());
- REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes);
- nodes += entry.info.nodes;
- } else {
- std::cerr << "\n(skipped copying test)\n";
- }
- }
+ check_perft_type(entry, nodes, elapsed);
+ check_perft_type(entry, nodes, elapsed);
+ check_perft_type(entry, nodes, elapsed);
}
- auto end_time = high_resolution_clock::now();
- elapsed = duration(end_time - start_time).count();
double mnps = (nodes / elapsed) / 1'000'000.0;
std::cout << "Speed: " << mnps << "Mnps\n";
}
+
TEST_CASE("Chess960" * doctest::timeout(36000)) {
std::vector> tests = {
{ "bqnb1rkr/pp3ppp/3ppn2/2p5/5P2/P2P4/NPP1P1PP/BQ1BNRKR w HFhf - 2 9", 1, 21 },
@@ -5957,10 +5946,95 @@ TEST_CASE("Chess960" * doctest::timeout(36000)) {
};
check_perfts(tests);
}
-int main(int argc, char **argv) {
- doctest::Context ctx;
- ctx.setOption("success", true);
- ctx.setOption("no-breaks", true);
- ctx.setOption("abort-after", 1);
- return ctx.run();
+TEST_CASE("Chess960 double setFEN reinitializes castling metadata") {
+ {
+ // Chess960 position → Chess960 position (different castling)
+ // GEge: G/kingside rook on G file, E/queenside rook on E file; king at F1/F8
+ Position p("n1bqrkrb/pppppppp/8/8/8/8/PPPPPPPP/N1BQRKRB w GEge - 0 1", true);
+ auto meta_w = p.getCastlingMetadata(WHITE);
+ auto meta_b = p.getCastlingMetadata(BLACK);
+ REQUIRE(meta_w.king_start == SQ_F1);
+ REQUIRE(meta_w.rook_start_ks == SQ_G1);
+ REQUIRE(meta_w.rook_start_qs == SQ_E1);
+ REQUIRE(meta_b.king_start == SQ_F8);
+ REQUIRE(meta_b.rook_start_ks == SQ_G8);
+ REQUIRE(meta_b.rook_start_qs == SQ_E8);
+
+ // Q1NBBRKR: Q A1, . B1, N C1, B D1, B E1, R F1, K G1, R H1 → HFhf → king=G1, rook_ks=H1, rook_qs=F1
+ p.setFEN("qnnbbrkr/1p2ppp1/2pp3p/p7/1P5P/2NP4/P1P1PPP1/Q1NBBRKR w HFhf - 0 9", true);
+ meta_w = p.getCastlingMetadata(WHITE);
+ meta_b = p.getCastlingMetadata(BLACK);
+ REQUIRE(meta_w.king_start == SQ_G1);
+ REQUIRE(meta_w.rook_start_ks == SQ_H1);
+ REQUIRE(meta_w.rook_start_qs == SQ_F1);
+ REQUIRE(meta_b.king_start == SQ_G8);
+ REQUIRE(meta_b.rook_start_ks == SQ_H8);
+ REQUIRE(meta_b.rook_start_qs == SQ_F8);
+ }
+ {
+ // Standard → Chess960 → Standard
+ // BQ1BNRKR: B A1, Q B1, . C1, B D1, N E1, R F1, K G1, R H1 → HFhf → king=G1, rook_ks=H1, rook_qs=F1
+ Position p("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
+ p.setFEN("bqnb1rkr/pp3ppp/3ppn2/2p5/5P2/P2P4/NPP1P1PP/BQ1BNRKR w HFhf - 2 9", true);
+ auto meta_w = p.getCastlingMetadata(WHITE);
+ REQUIRE(meta_w.king_start == SQ_G1);
+ REQUIRE(meta_w.rook_start_ks == SQ_H1);
+ REQUIRE(meta_w.rook_start_qs == SQ_F1);
+
+ p.setFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
+ meta_w = p.getCastlingMetadata(WHITE);
+ REQUIRE(meta_w.king_start == SQ_E1);
+ REQUIRE(meta_w.rook_start_ks == SQ_H1);
+ REQUIRE(meta_w.rook_start_qs == SQ_A1);
+ }
+ {
+ // Chess960 → no castling → Chess960
+ // QNBBNRKR: Q A1, N B1, B C1, B D1, N E1, R F1, K G1, R H1 → HFhf → king=G1
+ Position p("1nbbnrkr/p1p1ppp1/3p4/1p3P1p/3Pq2P/8/PPP1P1P1/QNBBNRKR w HFhf - 0 9", true);
+ REQUIRE(p.getCastlingMetadata(WHITE).king_start == SQ_G1);
+
+ p.setFEN("4k3/8/8/8/8/8/8/4K3 w - - 0 1");
+ REQUIRE(p.getCastlingMetadata(WHITE).king_start == SQ_NONE);
+ REQUIRE(p.getCastlingMetadata(BLACK).king_start == SQ_NONE);
+
+ // 1BNNBRKR: . A1, B B1, N C1, N D1, B E1, R F1, K G1, R H1 → HFhf → king=G1, rook_ks=H1, rook_qs=F1
+ p.setFEN("qbn1brkr/ppp1p1p1/2n4p/3p1p2/P7/6PP/QPPPPP2/1BNNBRKR w HFhf - 0 9", true);
+ auto meta_w = p.getCastlingMetadata(WHITE);
+ REQUIRE(meta_w.king_start == SQ_G1);
+ REQUIRE(meta_w.rook_start_ks == SQ_H1);
+ REQUIRE(meta_w.rook_start_qs == SQ_F1);
+ }
+ {
+ // Chess960 with "Kk": white kingside + black kingside only
+ Position p("r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1");
+ p.setFEN("r3k2r/8/8/8/8/8/8/R3K2R w Kk - 0 1", true);
+ auto meta_w = p.getCastlingMetadata(WHITE);
+ REQUIRE(meta_w.king_start == SQ_E1);
+ REQUIRE(meta_w.rook_start_ks == SQ_H1);
+ REQUIRE(meta_w.rook_start_qs == SQ_NONE);
+ auto meta_b = p.getCastlingMetadata(BLACK);
+ REQUIRE(meta_b.king_start == SQ_E8);
+ REQUIRE(meta_b.rook_start_ks == SQ_H8);
+ REQUIRE(meta_b.rook_start_qs == SQ_NONE);
+ }
+ {
+ // Chess960 with non-standard rook placements
+ Position p("r1bqkb1r/pppppppp/2n2n2/8/8/2N2N2/PPPPPPPP/R1BQKB1R w KQkq - 0 1");
+ p.setFEN("n1bqrkrb/pppppppp/8/8/8/8/PPPPPPPP/N1BQRKRB w GEge - 0 1", true);
+ auto meta_w = p.getCastlingMetadata(WHITE);
+ REQUIRE(meta_w.king_start == SQ_F1);
+ REQUIRE(meta_w.rook_start_ks == SQ_G1);
+ REQUIRE(meta_w.rook_start_qs == SQ_E1);
+ auto meta_b = p.getCastlingMetadata(BLACK);
+ REQUIRE(meta_b.king_start == SQ_F8);
+ REQUIRE(meta_b.rook_start_ks == SQ_G8);
+ REQUIRE(meta_b.rook_start_qs == SQ_E8);
+
+ // 1BNNBRKR: . A1, B B1, N C1, N D1, B E1, R F1, K G1, R H1 → HFhf → king=G1, rook_ks=H1, rook_qs=F1
+ p.setFEN("qbn1brkr/ppp1p1p1/2n4p/3p1p2/P7/6PP/QPPPPP2/1BNNBRKR w HFhf - 0 9", true);
+ meta_w = p.getCastlingMetadata(WHITE);
+ REQUIRE(meta_w.king_start == SQ_G1);
+ REQUIRE(meta_w.rook_start_ks == SQ_H1);
+ REQUIRE(meta_w.rook_start_qs == SQ_F1);
+ }
}
diff --git a/fwd_decl.h b/fwd_decl.h
index 9f822ca..153346d 100644
--- a/fwd_decl.h
+++ b/fwd_decl.h
@@ -19,31 +19,90 @@
#pragma once
#include
#include
+
+/// @file fwd_decl.h
+/// @brief Forward declarations for all major chess types.
+
namespace chess {
+
+/// @enum Color
+/// @brief Side to move or piece color.
enum Color : uint8_t;
+
+/// @enum PieceType
+/// @brief Basic piece type without color information.
enum PieceType : std::int8_t;
+/// @brief Trait to detect piece-enum types (PolyglotPiece, EnginePiece, ContiguousMappingPiece).
template struct is_piece_enum : std::false_type {};
template struct is_piece_enum> : std::true_type {};
+
+/// @enum CastlingRights
+/// @brief Bitmask of available castling rights.
enum CastlingRights : int8_t;
+/// @enum Square
+/// @brief Board square index (0-63, A1-H8).
enum Square : int8_t;
+/// @enum Direction
+/// @brief Compass direction offsets for board traversal.
enum Direction : int8_t;
+/// @enum MoveType
+/// @brief Move-type flags (normal, promotion, en-passant, castling).
enum MoveType : uint16_t;
+/// @enum File
+/// @brief File index (0-7, A-H).
enum File : int8_t;
+/// @enum Rank
+/// @brief Rank index (0-7, 1-8).
enum Rank : int8_t;
+
+/// @class Move
+/// @brief Compact 16-bit move representation.
class Move;
+
+/// @enum MoveGenType
+/// @brief Move-generation filter flags for piece type and move kind.
enum class MoveGenType : uint16_t;
+
+/// @class _Position
+/// @brief Templated chess position class parameterised by the piece enum.
template class _Position;
+
+/// @typedef Bitboard
+/// @brief 64-bit bitboard representing a set of squares.
using Bitboard = uint64_t;
+
+/// @typedef Key
+/// @brief 64-bit Zobrist hash key.
using Key = uint64_t;
+
+/// @class ValueList
+/// @brief Stack-allocated fixed-capacity container.
template class ValueList;
-using Movelist = ValueList;
-// bonus: define the piece enums here
+
+/// @typedef Movelist
+/// @brief Fixed-capacity list of up to 300 moves.
+using Movelist = ValueList;
+
+/// @enum PolyglotPiece
+/// @brief Piece encoding used by Polyglot opening books.
enum class PolyglotPiece : uint8_t;
+
+/// @enum EnginePiece
+/// @brief Default engine piece encoding (8 values per colour).
enum class EnginePiece : uint8_t;
+
+/// @enum ContiguousMappingPiece
+/// @brief Compact piece encoding (0-5 white, 6-11 black).
enum class ContiguousMappingPiece : uint8_t;
+
+/// @typedef Position
+/// @brief Default chess position type (uses EnginePiece).
using Position = _Position;
-using Board = Position;
+
+/// @typedef Board
+/// @brief Alias for Position.
+using Board [[deprecated("Use Position instead")]] = Position;
} // namespace chess
diff --git a/movegen.cpp b/movegen.cpp
index 60f5155..db06f73 100644
--- a/movegen.cpp
+++ b/movegen.cpp
@@ -22,6 +22,10 @@
// movegen references
// License: https://github.com/Disservin/chess-library/blob/master/LICENSE
+
+/// @file movegen.cpp
+/// @brief Move generator: AVX-512 accelerated splatting and per-piece-type move generation.
+
#include "movegen.h"
#include "position.h"
@@ -122,11 +126,23 @@ inline Move *splat_moves(Move *moveList, Square from, Bitboard to_bb) {
}
#endif
} // namespace _chess
+
+// Count-only dispatch helpers — splat_moves/splat_pawn_moves when storing is needed, no-op when counting.
+template inline void record_moves(ListT &list, Square from, Bitboard targets) {
+ if constexpr (std::is_same_v) {
+ _chess::splat_moves(list.data() + list.size_, from, targets);
+ }
+}
+template inline void record_pawn_moves(ListT &list, Bitboard targets) {
+ if constexpr (std::is_same_v) {
+ _chess::splat_pawn_moves(list.data() + list.size_, targets);
+ }
+}
} // namespace chess
namespace chess {
-template void movegen::genEP(const _Position &pos, Movelist &mv) {
+template [[gnu::hot]] void movegen::genEP(const _Position &pos, ListT &mv) {
- const Square king_sq = pos.kingSq(c);
+ const Square king_sq = pos.king_sq(c);
const Square ep_sq = pos.ep_square();
if (ep_sq == SQ_NONE)
return;
@@ -139,12 +155,12 @@ template void movegen::genEP(const _Position &pos
const Bitboard ep_mask = (1ULL << ep_pawn_sq) | (1ULL << ep_sq);
// ASSUME(popcount(candidates) <= 32);
+ Bitboard occ_all = pos.occ();
while (candidates) {
Square from = static_cast(pop_lsb(candidates));
// Remove the EP pawn and this attacker from occupancy
- Bitboard occ_temp = pos.occ();
- occ_temp ^= (1ULL << from) | ep_mask;
+ Bitboard occ_temp = occ_all ^ ((1ULL << from) | ep_mask);
// attackers check
Bitboard atks = 0;
@@ -156,8 +172,9 @@ template void movegen::genEP(const _Position &pos
}
}
}
-template
-void movegen::genPawnDoubleMoves(const _Position &pos, Movelist &moves, Bitboard pin_mask, Bitboard check_mask) {
+template
+[[gnu::hot]] void
+movegen::genPawnDoubleMoves(const _Position &pos, ListT &moves, Bitboard pin_mask, Bitboard check_mask) {
constexpr Bitboard RANK_2 = (c == WHITE) ? attacks::MASK_RANK[1] : attacks::MASK_RANK[6];
constexpr Direction UP = pawn_push(c);
@@ -165,7 +182,7 @@ void movegen::genPawnDoubleMoves(const _Position &pos, Movelist &moves,
Bitboard pawns = pos.template pieces() & RANK_2;
// Split pin types
- Bitboard pin_file = pin_mask & attacks::MASK_FILE[file_of(pos.kingSq(c))];
+ Bitboard pin_file = pin_mask & attacks::MASK_FILE[file_of(pos.king_sq(c))];
Bitboard unpinned = pawns & ~pin_mask;
Bitboard file_pinned = pawns & pin_file;
@@ -180,12 +197,12 @@ void movegen::genPawnDoubleMoves(const _Position &pos, Movelist &moves,
Bitboard destinations = (step2_unpinned | step2_pinned) & check_mask;
- _chess::splat_pawn_moves<2 * UP>(moves.data() + moves.size_, destinations);
+ record_pawn_moves<2 * UP>(moves, destinations);
moves.size_ += popcount(destinations);
}
-template
-void movegen::genPawnSingleMoves(
- const _Position &pos, Movelist &moves, Bitboard _rook_pin, Bitboard _bishop_pin, Bitboard _check_mask) {
+template
+[[gnu::hot]] void movegen::genPawnSingleMoves(
+ const _Position &pos, ListT &moves, Bitboard _rook_pin, Bitboard _bishop_pin, Bitboard _check_mask) {
constexpr auto UP = relative_direction(c, NORTH);
constexpr auto UP_LEFT = relative_direction(c, NORTH_WEST);
constexpr auto UP_RIGHT = relative_direction(c, NORTH_EAST);
@@ -259,16 +276,17 @@ void movegen::genPawnSingleMoves(
l_pawns &= ~RANK_PROMO;
r_pawns &= ~RANK_PROMO;
if constexpr (!capturesOnly) {
- _chess::splat_pawn_moves(moves.data() + moves.size_, single_push);
+ record_pawn_moves(moves, single_push);
moves.size_ += popcount(single_push);
}
- _chess::splat_pawn_moves(moves.data() + moves.size_, l_pawns);
+ record_pawn_moves(moves, l_pawns);
moves.size_ += popcount(l_pawns);
- _chess::splat_pawn_moves(moves.data() + moves.size_, r_pawns);
+ record_pawn_moves(moves, r_pawns);
moves.size_ += popcount(r_pawns);
}
-template
-void movegen::genKnightMoves(const _Position &pos, Movelist &list, Bitboard _pin_mask, Bitboard _check_mask) {
+template
+[[gnu::hot]] void
+movegen::genKnightMoves(const _Position &pos, ListT &list, Bitboard _pin_mask, Bitboard _check_mask) {
Bitboard knights = pos.template pieces() & ~_pin_mask;
while (knights) {
Square x = static_cast(pop_lsb(knights));
@@ -276,43 +294,50 @@ void movegen::genKnightMoves(const _Position &pos, Movelist &list, Bitb
moves &= _check_mask;
if constexpr (capturesOnly)
moves &= pos.occ(~c);
- _chess::splat_moves(list.data() + list.size(), x, moves);
+ record_moves(list, x, moves);
list.size_ += popcount(moves);
}
}
-template
-void movegen::genKingMoves(const _Position &pos, Movelist &out, Bitboard _pin_mask) {
+template
+[[gnu::hot]] void movegen::genKingMoves(const _Position &pos, ListT &out, Bitboard _pin_mask) {
constexpr Color them = ~c;
- const Square kingSq = pos.kingSq(c);
- const Bitboard occAll = pos.occ();
+ const Square kingSq = pos.king_sq(c);
const Bitboard myOcc = pos.occ(c);
+ const Bitboard occ_opp = pos.occ(~c);
- // Remove king from board when computing enemy attacks
+ if constexpr (capturesOnly) {
+ Bitboard targets = attacks::king(kingSq) & occ_opp;
+ if (!targets) {
+ out.size_ += 0;
+ return;
+ }
+ }
+
+ const Bitboard occAll = pos.occ();
const Bitboard occWithoutKing = occAll ^ 1ULL << kingSq;
Bitboard enemyAttacks = 0ULL;
// Sliding pieces
- Bitboard bLike = pos.template pieces() | pos.template pieces();
- while (bLike)
- enemyAttacks |= attacks::bishop(static_cast(pop_lsb(bLike)), occWithoutKing);
-
- Bitboard rLike = pos.template pieces() | pos.template pieces();
- while (rLike)
- enemyAttacks |= attacks::rook(static_cast(pop_lsb(rLike)), occWithoutKing);
+ {
+ Bitboard bLike = pos.template pieces() | pos.template pieces();
+ while (bLike)
+ enemyAttacks |= attacks::bishop(static_cast(pop_lsb(bLike)), occWithoutKing);
+ }
+ {
+ Bitboard rLike = pos.template pieces() | pos.template pieces();
+ while (rLike)
+ enemyAttacks |= attacks::rook(static_cast(pop_lsb(rLike)), occWithoutKing);
+ }
- // Knights
+ // Knights, pawns, enemy king (precomputed tables)
enemyAttacks |= attacks::knight(pos.template pieces());
-
- // Pawns
enemyAttacks |= attacks::pawn(pos.template pieces());
-
- // Enemy king (adjacent control squares)
- enemyAttacks |= attacks::king(pos.kingSq(them));
+ enemyAttacks |= attacks::king(pos.king_sq(them));
Bitboard moves = attacks::king(kingSq) & ~myOcc & ~enemyAttacks;
if constexpr (capturesOnly)
- moves &= pos.occ(~c);
- _chess::splat_moves(out.data() + out.size(), kingSq, moves);
+ moves &= occ_opp;
+ record_moves(out, kingSq, moves);
out.size_ += popcount(moves);
if constexpr (!capturesOnly) {
if (pos.checkers())
@@ -322,11 +347,11 @@ void movegen::genKingMoves(const _Position &pos, Movelist &out, Bitboar
Bitboard enemy_attacks = enemyAttacks;
constexpr CastlingRights kingRights = KING_SIDE & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING),
queenRights = QUEEN_SIDE & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING);
- Bitboard OO_EMPTY = pos.getCastlingPath(c, true);
+ Bitboard OO_EMPTY = pos.get_castling_path(c, true);
Bitboard OO_SAFE = between(kingSq, castling_king_square(c, true));
- Bitboard OOO_EMPTY = pos.getCastlingPath(c, false);
+ Bitboard OOO_EMPTY = pos.get_castling_path(c, false);
Bitboard OOO_SAFE = between(kingSq, castling_king_square(c, false));
- Square rookKing = pos.getCastlingMetadata(c).rook_start_ks, rookQueen = pos.getCastlingMetadata(c).rook_start_qs;
+ Square rookKing = pos.get_castling_metadata(c).rook_start_ks, rookQueen = pos.get_castling_metadata(c).rook_start_qs;
if (pos.castlingRights() & kingRights &&
!(occupancy & OO_EMPTY || enemy_attacks & OO_SAFE || _pin_mask & 1ULL << rookKing)) {
@@ -338,9 +363,9 @@ void movegen::genKingMoves(const _Position &pos, Movelist &out, Bitboar
}
}
}
-template
-void movegen::genSlidingMoves(
- const _Position &pos, Movelist &moves, Bitboard _rook_pin, Bitboard _bishop_pin, Bitboard _check_mask) {
+template
+[[gnu::hot]] void movegen::genSlidingMoves(
+ const _Position &pos, ListT &moves, Bitboard _rook_pin, Bitboard _bishop_pin, Bitboard _check_mask) {
static_assert(pt == BISHOP || pt == ROOK || pt == QUEEN, "Sliding pieces only.");
Bitboard sliders = pos.template pieces();
Bitboard occ_all = pos.occ();
@@ -350,6 +375,7 @@ void movegen::genSlidingMoves(
sliders &= ~rook_pinners;
if constexpr (pt == ROOK)
sliders &= ~bishop_pinners;
+ Bitboard occ_opp = pos.occ(~c);
Bitboard filter_list = ~pos.occ(c) & _check_mask;
while (sliders) {
Square from = static_cast(pop_lsb(sliders));
@@ -359,140 +385,148 @@ void movegen::genSlidingMoves(
Bitboard bishop_hit = bishop_pinners & from_bb;
Bitboard pin_mask = rook_hit ? rook_pinners : bishop_hit ? bishop_pinners : ~0ULL;
- auto func = attacks::queen;
- if constexpr (pt == BISHOP)
- func = attacks::bishop;
- else if constexpr (pt == ROOK)
- func = attacks::rook;
- func = rook_hit ? attacks::rook : bishop_hit ? attacks::bishop : func;
Bitboard filtered_pin = pin_mask & filter_list;
- Bitboard targets = func(from, occ_all) & filtered_pin;
+ Bitboard targets;
+ if (rook_hit) {
+ targets = attacks::rook(from, occ_all) & filtered_pin;
+ } else if (bishop_hit) {
+ targets = attacks::bishop(from, occ_all) & filtered_pin;
+ } else if constexpr (pt == BISHOP) {
+ targets = attacks::bishop(from, occ_all) & filtered_pin;
+ } else if constexpr (pt == ROOK) {
+ targets = attacks::rook(from, occ_all) & filtered_pin;
+ } else {
+ targets = attacks::queen(from, occ_all) & filtered_pin;
+ }
if constexpr (capturesOnly)
- targets &= pos.occ(~c);
- _chess::splat_moves(moves.data() + moves.size_, from, targets);
+ targets &= occ_opp;
+ record_moves(moves, from, targets);
moves.size_ += popcount(targets);
}
}
-#define INSTANTIATE(PieceC) \
- template void chess::movegen::genEP(const _Position &, Movelist &); \
- template void chess::movegen::genEP(const _Position &, Movelist &); \
- template void chess::movegen::genPawnDoubleMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genPawnDoubleMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genPawnSingleMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genPawnSingleMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
+#define INSTANTIATE(PieceC, ListT) \
+ template void chess::movegen::genEP(const _Position &, ListT &); \
+ template void chess::movegen::genEP(const _Position &, ListT &); \
+ template void chess::movegen::genPawnDoubleMoves(const _Position &, \
+ ListT &, \
Bitboard, \
Bitboard); \
- template void chess::movegen::genPawnSingleMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genPawnSingleMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
+ template void chess::movegen::genPawnDoubleMoves(const _Position &, \
+ ListT &, \
Bitboard, \
Bitboard); \
- template void chess::movegen::genSlidingMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genSlidingMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
+ template void chess::movegen::genPawnSingleMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genPawnSingleMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genPawnSingleMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genPawnSingleMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genSlidingMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genSlidingMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genSlidingMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genSlidingMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genSlidingMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genSlidingMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genSlidingMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genSlidingMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genSlidingMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genSlidingMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genSlidingMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genSlidingMoves(const _Position &, \
+ ListT &, \
+ Bitboard, \
+ Bitboard, \
+ Bitboard); \
+ template void chess::movegen::genKnightMoves(const _Position &, \
+ ListT &, \
Bitboard, \
Bitboard); \
- template void chess::movegen::genSlidingMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genSlidingMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genSlidingMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genSlidingMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genSlidingMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genSlidingMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
+ template void chess::movegen::genKnightMoves(const _Position &, \
+ ListT &, \
Bitboard, \
Bitboard); \
- template void chess::movegen::genSlidingMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
+ template void chess::movegen::genKnightMoves(const _Position &, \
+ ListT &, \
Bitboard, \
Bitboard); \
- template void chess::movegen::genSlidingMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genSlidingMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
+ template void chess::movegen::genKnightMoves(const _Position &, \
+ ListT &, \
Bitboard, \
Bitboard); \
- template void chess::movegen::genSlidingMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genKnightMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genKnightMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genKnightMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genKnightMoves(const _Position &, \
- Movelist &, \
- Bitboard, \
- Bitboard); \
- template void chess::movegen::genKingMoves(const _Position &, \
- Movelist &, \
- Bitboard); \
- template void chess::movegen::genKingMoves(const _Position &, \
- Movelist &, \
- Bitboard); \
- template void chess::movegen::genKingMoves(const _Position &, \
- Movelist &, \
- Bitboard); \
- template void chess::movegen::genKingMoves(const _Position &, \
- Movelist &, \
- Bitboard);
-INSTANTIATE(EnginePiece)
-INSTANTIATE(PolyglotPiece)
-INSTANTIATE(ContiguousMappingPiece)
+ template void chess::movegen::genKingMoves(const _Position &, \
+ ListT &, \
+ Bitboard); \
+ template void chess::movegen::genKingMoves(const _Position &, \
+ ListT &, \
+ Bitboard); \
+ template void chess::movegen::genKingMoves