diff --git a/.github/workflows/cmake.yml b/.github/workflows/executable.yml similarity index 65% rename from .github/workflows/cmake.yml rename to .github/workflows/executable.yml index ce0e70a3..521a66d8 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/executable.yml @@ -1,8 +1,14 @@ -name: CMake +name: Executable on: push: branches: [ "master" ] + pull_request: + branches: [ "master" ] + paths-ignore: + - .github/workflows/py_binding.yml + - src/Python/** + - pyproject.toml env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) @@ -12,7 +18,7 @@ jobs: build: strategy: matrix: - arch: [ x86-64, x86-64-v2, x86-64-v3, x86-64-v4 ] + arch: [ x86-64, x86-64-v2, x86-64-v3, x86-64-v4, apple-m1, apple-m2, apple-m3, apple-m4 ] os: [ ubuntu-latest , windows-latest, macos-latest ] include: - os: windows-latest @@ -22,51 +28,65 @@ jobs: arch: apple-m1 - os: windows-latest arch: apple-m2 + - os: windows-latest + arch: apple-m3 + - os: windows-latest + arch: apple-m4 - os: ubuntu-latest arch: apple-m1 - os: ubuntu-latest arch: apple-m2 + - os: ubuntu-latest + arch: apple-m3 + - os: ubuntu-latest + arch: apple-m4 + - os: macos-latest + arch: x86-64 + - os: macos-latest + arch: x86-64-v2 + - os: macos-latest + arch: x86-64-v3 + - os: macos-latest + arch: x86-64-v4 runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - if: matrix.os == 'ubuntu-latest' name: Install Linux Dependencies run: | - sudo apt install libncurses5 ninja-build + sudo apt install ninja-build sudo apt remove python3-lldb-14 - if: matrix.os == 'windows-latest' name: Install Windows Dependencies run: | choco install ninja - - if: matrix.os == 'macos-latest' - name: Install MacOS Dependencies - run: | - brew install ninja - if: matrix.os == 'ubuntu-latest' - name: Install Clang 16 (Ubuntu) + name: Install Clang 20 (Ubuntu) run: | wget https://apt.llvm.org/llvm.sh sudo chmod +x llvm.sh - sudo ./llvm.sh 16 + sudo ./llvm.sh 20 sudo rm -rf llvm.sh - if: matrix.os == 'windows-latest' - name: Install Clang 16 (Windows) + name: Install Clang 20 (Windows) run: | - choco install llvm --version 16.0.6 -y + choco install llvm --version 20.1.0 -y - if: matrix.os == 'macos-latest' - name: Install Clang 16 (MacOS) + name: Install Clang 20 (MacOS) run: | - brew install llvm@16 + brew update-reset + brew update + brew install llvm@20 - if: matrix.os == 'ubuntu-latest' name: Set CC and CXX for Linux run: | - echo "CC=clang-16" >> $GITHUB_ENV - echo "CXX=clang++-16" >> $GITHUB_ENV + echo "CC=clang-20" >> $GITHUB_ENV + echo "CXX=clang++-20" >> $GITHUB_ENV - if: matrix.os == 'windows-latest' name: Set CC and CXX for Windows shell: powershell @@ -76,17 +96,17 @@ jobs: - if: matrix.os == 'macos-latest' name: Set CC and CXX for MacOS run: | - echo "CC=/usr/local/opt/llvm@16/bin/clang" >> $GITHUB_ENV - echo "CXX=/usr/local/opt/llvm@16/bin/clang++" >> $GITHUB_ENV - echo "PATH=/usr/local/opt/llvm@16/bin:$PATH" >> $GITHUB_ENV + echo "CC=/opt/homebrew/opt/llvm@20/bin/clang" >> $GITHUB_ENV + echo "CXX=/opt/homebrew/opt/llvm@20/bin/clang++" >> $GITHUB_ENV + echo "PATH=/opt/homebrew/opt/llvm@20/bin:$PATH" >> $GITHUB_ENV - - if: matrix.arch == 'apple-m1' || matrix.arch == 'apple-m2' - name: Configure CMake for Apple M1/M2 (ARM64) - run: cmake -B ${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_CXX_FLAGS="--target=arm64 -mcpu=${{ matrix.arch }}" -G Ninja + - if: matrix.os == 'macos-latest' + name: Configure CMake for Apple (ARM64) + run: cmake -B ${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCI=ON -DCMAKE_CXX_FLAGS="-mcpu=${{ matrix.arch }}" -G Ninja - - if: matrix.arch != 'apple-m1' && matrix.arch != 'apple-m2' + - if: matrix.os != 'macos-latest' name: Configure CMake - run: cmake -B ${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_CXX_FLAGS="-march=${{ matrix.arch }}" -G Ninja + run: cmake -B ${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCI=ON -DCMAKE_CXX_FLAGS="-march=${{ matrix.arch }}" -G Ninja - name: Build run: cmake --build ${{ github.workspace }}/build --config ${{ env.BUILD_TYPE }} @@ -109,7 +129,7 @@ jobs: mv ${{ github.workspace }}/build/StockDory${{ matrix.extension }} ${{ github.workspace }}/StockDory-${{ env.VERSION }}-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.extension }} - name: Upload Binary - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: StockDory-${{ env.VERSION }}-${{ matrix.os }}-${{ matrix.arch }} path: ${{ github.workspace }}/StockDory-${{ env.VERSION }}-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.extension }} diff --git a/.github/workflows/py_binding.yml b/.github/workflows/py_binding.yml new file mode 100644 index 00000000..a9ffc590 --- /dev/null +++ b/.github/workflows/py_binding.yml @@ -0,0 +1,236 @@ +name: PyBinding + +on: + push: + branches: [ "master" ] + paths: + - pyproject.toml + - CMakeLists.txt + pull_request: + branches: [ "master" ] + paths: + - pyproject.toml + - CMakeLists.txt + +jobs: + build: + strategy: + matrix: + arch: [ x86-64, x86-64-v2, x86-64-v3, x86-64-v4, apple-m1, apple-m2, apple-m3, apple-m4 ] + os: [ ubuntu-latest , windows-latest, macos-latest ] + python: [ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13" ] + exclude: + - os: windows-latest + arch: apple-m1 + - os: windows-latest + arch: apple-m2 + - os: windows-latest + arch: apple-m3 + - os: windows-latest + arch: apple-m4 + - os: ubuntu-latest + arch: apple-m1 + - os: ubuntu-latest + arch: apple-m2 + - os: ubuntu-latest + arch: apple-m3 + - os: ubuntu-latest + arch: apple-m4 + - os: macos-latest + arch: x86-64 + - os: macos-latest + arch: x86-64-v2 + - os: macos-latest + arch: x86-64-v3 + - os: macos-latest + arch: x86-64-v4 + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + + - if: matrix.os == 'ubuntu-latest' + name: Install Linux Dependencies + run: | + sudo apt install ninja-build + sudo apt remove python3-lldb-14 + - if: matrix.os == 'windows-latest' + name: Install Windows Dependencies + run: | + choco install ninja + + - if: matrix.os == 'ubuntu-latest' + name: Install Clang 20 (Ubuntu) + run: | + wget https://apt.llvm.org/llvm.sh + sudo chmod +x llvm.sh + sudo ./llvm.sh 20 + sudo rm -rf llvm.sh + - if: matrix.os == 'windows-latest' + name: Install Clang 20 (Windows) + run: | + choco install llvm --version 20.1.0 -y + - if: matrix.os == 'macos-latest' + name: Install Clang 20 (MacOS) + run: | + brew update-reset + brew update + brew install llvm@20 + + - if: matrix.os == 'ubuntu-latest' + name: Set CC and CXX for Linux + run: | + echo "CC=clang-20" >> $GITHUB_ENV + echo "CXX=clang++-20" >> $GITHUB_ENV + - if: matrix.os == 'windows-latest' + name: Set CC and CXX for Windows + shell: powershell + run: | + echo "CC=clang" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + echo "CXX=clang++" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + - if: matrix.os == 'macos-latest' + name: Set CC and CXX for MacOS + run: | + echo "CC=/opt/homebrew/opt/llvm@20/bin/clang" >> $GITHUB_ENV + echo "CXX=/opt/homebrew/opt/llvm@20/bin/clang++" >> $GITHUB_ENV + echo "PATH=/opt/homebrew/opt/llvm@20/bin:$PATH" >> $GITHUB_ENV + + + - name: Setup Python ${{ matrix.python }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + + - name: Python Dependencies + run: | + python -m pip install --upgrade pip + python -m pip install scikit-build-core build twine pybind11 setuptools build tomlkit + + - name: CMake Arguments (MacOS) + if: matrix.os == 'macos-latest' + run: | + echo "CMAKE_CXX_FLAGS=-mcpu=${{ matrix.arch }}" >> $GITHUB_ENV + + - name: CMake Arguments (Linux) + if: matrix.os == 'ubuntu-latest' + run: | + echo "CMAKE_CXX_FLAGS=-march=${{ matrix.arch }}" >> $GITHUB_ENV + + - name: CMake Arguments (Windows) + if: matrix.os == 'windows-latest' + shell: powershell + run: | + echo "CMAKE_CXX_FLAGS=-march=${{ matrix.arch }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Set architecture environment variable (Windows) + if: matrix.os == 'windows-latest' + run: | + echo "ARCHITECTURE=${{ matrix.arch }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Set architecture environment variable (Linux & MacOS) + if: matrix.os != 'windows-latest' + run: | + echo "ARCHITECTURE=${{ matrix.arch }}" >> $GITHUB_ENV + + - name: Extract C++ Standard Library Version (Windows) + if: matrix.os == 'windows-latest' + shell: powershell + run: | + echo "Detecting STL version using $env:CXX" + + $cxx_version = & $env:CXX -dM -E -x c++ NUL 2>$null | Select-String '_MSC_VER' | ForEach-Object { + if ($_ -match '_MSC_VER\s+(\d+)') { + "msvc$($matches[1])" + } + } + + echo "CXX_VERSION=$cxx_version" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Extract C++ Standard Library Version (Linux & MacOS) + if: matrix.os != 'windows-latest' + shell: bash + run: | + echo "Detecting STL version on ${{ matrix.os }}" + + if [[ "${{ matrix.os }}" == "ubuntu-latest" ]]; then + glibc_version=$(getconf GNU_LIBC_VERSION | awk '{print $2}') + STL_VERSION="libstdcxx${glibc_version}" + elif [[ "${{ matrix.os }}" == "macos-latest" ]]; then + clang_version=$(clang++ --version | grep -i 'clang version' | awk '{print $4}') + STL_VERSION="libcxx${clang_version}" + else + STL_VERSION="unknown" + fi + + echo "Detected STL version: $STL_VERSION" + echo "CXX_VERSION=$STL_VERSION" >> $GITHUB_ENV + + - name: Define version + shell: python + run: | + import os + import tomlkit + + architecture = os.environ.get("ARCHITECTURE", "").replace("-", "_") + cxx_version = os.environ.get("CXX_VERSION", "") + + with open("pyproject.toml", "r", encoding="utf-8") as f: + doc = tomlkit.parse(f.read()) + + doc["project"]["version"] = f"{doc['project']['version']}.arch.{architecture}.{cxx_version}" + + with open("pyproject.toml", "w", encoding="utf-8") as f: + f.write(tomlkit.dumps(doc)) + + print(f"Set project version to {doc['project']['version']}") + + - name: Build Wheel + run: | + python -m build + + - name: Upload Wheel + uses: actions/upload-artifact@v4 + with: + name: StockDory-python.${{ matrix.python }}-${{ matrix.os }}-${{ matrix.arch }}.whl + path: ./dist/*.whl + + publish: + if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') + needs: build + runs-on: ubuntu-latest + + environment: + name: Python Packages Repository + url: https://pypi.fury.io/theblackplague/stockdory + + steps: + - name: Download Wheels + uses: actions/download-artifact@v4 + with: + path: dist + merge-multiple: true + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: 3.x + + - name: Python Dependencies + run: | + python -m pip install --upgrade twine + + - name: Show downloaded Wheels + run: ls -R dist + + - name: Publish Wheels (Gemfury) + env: + FURY_TOKEN: ${{ secrets.FURY_TOKEN }} + run: | + python -m twine upload \ + --repository-url https://pypi.fury.io/theblackplague/ \ + -u theblackplague \ + -p "${FURY_TOKEN}" \ + --non-interactive \ + --skip-existing \ + dist/* diff --git a/.gitignore b/.gitignore index fa52dbbf..128fd90f 100644 --- a/.gitignore +++ b/.gitignore @@ -76,4 +76,11 @@ fabric.properties .idea/httpRequests # Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser \ No newline at end of file +.idea/caches/build_file_checksums.ser + +# Python Bindings Wheel +dist/ +.venv/ + +# Makefile +Build/ diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 00000000..086960cb --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +StockDory \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..9c799f54 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,38 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..79ee123c --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/deployment.xml b/.idea/deployment.xml new file mode 100644 index 00000000..c1975cda --- /dev/null +++ b/.idea/deployment.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/editor.xml b/.idea/editor.xml new file mode 100644 index 00000000..df886aaa --- /dev/null +++ b/.idea/editor.xml @@ -0,0 +1,280 @@ + + + + + \ No newline at end of file diff --git a/.idea/fileTemplates/internal/C Header File.h b/.idea/fileTemplates/internal/C Header File.h new file mode 100644 index 00000000..d2964585 --- /dev/null +++ b/.idea/fileTemplates/internal/C Header File.h @@ -0,0 +1,5 @@ +#parse("C File Header.h") +#[[#ifndef]]# STOCKDORY_${INCLUDE_GUARD} +#[[#define]]# STOCKDORY_${INCLUDE_GUARD} + +#[[#endif]]# //STOCKDORY_${INCLUDE_GUARD} diff --git a/.idea/fileTemplates/internal/C++ Class Header.h b/.idea/fileTemplates/internal/C++ Class Header.h new file mode 100644 index 00000000..8c41c9e7 --- /dev/null +++ b/.idea/fileTemplates/internal/C++ Class Header.h @@ -0,0 +1,13 @@ +#parse("C File Header.h") +#[[#ifndef]]# STOCKDORY_${INCLUDE_GUARD} +#[[#define]]# STOCKDORY_${INCLUDE_GUARD} + +${NAMESPACES_OPEN} + +class ${NAME} { + +}; + +${NAMESPACES_CLOSE} + +#[[#endif]]# //STOCKDORY_${INCLUDE_GUARD} diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 2a8f4e6e..d6fea122 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,8 +1,13 @@ \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 11daf914..b029fd4b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,12 +1,26 @@ + + + + + + + + + + + + + {} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7f..f38acaaf 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -3,4 +3,7 @@ + + \ No newline at end of file diff --git a/BinaryResource.cmake b/BinaryResource.cmake index c9174ef9..cf4dbbb3 100644 --- a/BinaryResource.cmake +++ b/BinaryResource.cmake @@ -3,5 +3,5 @@ function(BinaryToHeader input_file output_file target_name) string(LENGTH "${file_data}" file_size) math(EXPR file_size "${file_size} / 2") # because each byte is represented by two hex characters string(REGEX REPLACE "(..)" "0x\\1, " hex_data ${file_data}) - file(WRITE ${output_file} "constexpr unsigned char _${target_name}Data[] = { ${hex_data} };constexpr size_t _${target_name}Size = sizeof(_${target_name}Data);") + file(WRITE ${output_file} "constexpr unsigned char _${target_name}Data[] = { ${hex_data} };") endfunction() \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f7f272d..6ed0a395 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,22 +1,29 @@ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.21) project(StockDory) set(VERSION "0.1" ) set(CODENAME "Starfish") -if(CI) - set(CI TRUE) -else() - set(CI FALSE) -endif() +option(BUILD_NATIVE "Enable native CPU architecture optimizations" ON) +option(PYMODULE "Enable if building Python Bindings" OFF) +option(CI "Enable if building via Continuous Integration such as Github Actions" OFF) +option(PGO "Enable profile-guided optimization - uses existing profile or creates instrumentation executable" OFF) + +set(CMAKE_CXX_STANDARD 23) -set(CMAKE_CXX_STANDARD 20) if(CMAKE_CXX_FLAGS STREQUAL "") - set(CMAKE_CXX_FLAGS "-march=native") + set(CMAKE_CXX_FLAGS "$ENV{CMAKE_CXX_FLAGS}") +endif() + +if(BUILD_NATIVE AND NOT (PYMODULE OR CI)) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") +else() + set(NANOTHREAD_NATIVE_FLAGS "${CMAKE_CXX_FLAGS}") endif() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto") -if(PGO MATCHES "True") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto=full") + +if(PGO) message(STATUS "PGO: Enabled") if(EXISTS ${CMAKE_BINARY_DIR}/pgo.profdata) message(STATUS "PGO: Using existing profile data at: ${CMAKE_BINARY_DIR}/pgo.profdata") @@ -30,9 +37,7 @@ endif() set(CMAKE_CXX_FLAGS_DEBUG "-Wall -Wextra -g -ftime-report") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") -set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time") - -set(CPM_DOWNLOAD_VERSION 0.38.0) +set(CPM_DOWNLOAD_VERSION 0.40.8) if(CPM_SOURCE_CACHE) set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") @@ -41,7 +46,7 @@ elseif(DEFINED ENV{CPM_SOURCE_CACHE}) set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") message(STATUS "Setting CPM.cmake location to ${CPM_DOWNLOAD_LOCATION}") else() - set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") + set(CPM_DOWNLOAD_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") message(STATUS "Setting CPM.cmake location to ${CPM_DOWNLOAD_LOCATION}") endif() @@ -51,9 +56,9 @@ foreach(DOWNLOAD_ATTEMPT RANGE 1 ${CPM_DOWNLOAD_RETRY_COUNT}) message(STATUS "CPM.cmake was not found at ${CPM_DOWNLOAD_LOCATION}") message(STATUS "Attempt: ${DOWNLOAD_ATTEMPT}/${CPM_DOWNLOAD_RETRY_COUNT}: Downloading CPM.cmake...") file(DOWNLOAD - https://github.com/TheLartians/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake - ${CPM_DOWNLOAD_LOCATION} - STATUS DOWNLOAD_STAT + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} + STATUS DOWNLOAD_STAT ) list(GET DOWNLOAD_STAT 0 DOWNLOAD_RES) if(NOT DOWNLOAD_RES EQUAL 0) @@ -76,7 +81,20 @@ endforeach() include(${CPM_DOWNLOAD_LOCATION}) -CPMAddPackage("gh:TheBlackPlague/MantaRay#4038386a746ae65881e7d1f02673e1805f729cc9") +CPMAddPackage( + NAME MantaRay + GITHUB_REPOSITORY TheBlackPlague/MantaRay + GIT_TAG 1c547d179c7cdf4f24e4f8a1fea228c6178d0ae2 + OPTIONS + "BUILD_TEST OFF" + "BUILD_MB OFF" +) + +CPMAddPackage( + NAME nanothread + GITHUB_REPOSITORY TheBlackPlague/nanothread + GIT_TAG master +) include(BinaryResource.cmake) @@ -110,13 +128,56 @@ file(GLOB StockDoryFrontendType "src/Terminal/UCI/*.h" "src/Terminal/*.h") -add_executable(StockDory src/Terminal/main.cpp - ${StockDoryBackendType} ${StockDoryEngineType} ${StockDoryExternalType} ${StockDoryFrontendType}) +if(PYMODULE) + message(STATUS "Configured to build Python Module") + + if(UNIX AND NOT APPLE) + message(STATUS "Enabling Position-Independent Code") + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + set_target_properties(nanothread PROPERTIES POSITION_INDEPENDENT_CODE ON) + endif() -target_include_directories(StockDory PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") + set(PYBIND11_FINDPYTHON ON) + CPMAddPackage("gh:pybind/pybind11#a2e59f0e7065404b44dfe92a28aca47ba1378dc4") -target_link_libraries(StockDory MantaRay) + pybind11_add_module(StockDory src/Python/binding.cpp + ${StockDoryBackendType} + ${StockDoryEngineType} + ${StockDoryExternalType} + ${StockDoryFrontendType} + ) -if(NOT WIN32) - target_link_libraries(StockDory pthread) -endif() \ No newline at end of file + target_include_directories(StockDory PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") + target_link_libraries(StockDory PRIVATE MantaRay nanothread) + if(NOT WIN32) + target_link_libraries(StockDory PRIVATE pthread) + endif() + + install(TARGETS StockDory DESTINATION .) +else() + message(STATUS "Configured to build executable") + + add_executable(StockDory src/Terminal/main.cpp + ${StockDoryBackendType} + ${StockDoryEngineType} + ${StockDoryExternalType} + ${StockDoryFrontendType} + ) + + target_include_directories(StockDory PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") + target_link_libraries(StockDory MantaRay nanothread) + if(NOT WIN32) + target_link_libraries(StockDory pthread) + endif() + + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time") +endif() + +message(STATUS "NANOTHREAD COMPILATION: ${NANOTHREAD_NATIVE_FLAGS}") + +message(STATUS "CXX FLAGS: ${CMAKE_CXX_FLAGS}" ) +if(CMAKE_BUILD_TYPE MATCHES "Debug") + message(STATUS "CXX DEBUG FLAGS: ${CMAKE_CXX_FLAGS_DEBUG}" ) +elseif(CMAKE_BUILD_TYPE MATCHES "Release") + message(STATUS "CXX RELEASE FLAGS: ${CMAKE_CXX_FLAGS_RELEASE}") +endif() diff --git a/Information.cmake b/Information.cmake index 3693dbc7..0703683e 100644 --- a/Information.cmake +++ b/Information.cmake @@ -18,7 +18,7 @@ function(Information VERSION CODENAME DEV) #include const std::string NAME = \"StockDory\" \; - const std::string AUTHOR = \"the StockDory Author\" \; + const std::string AUTHOR = \"the StockDory Authors\" \; const std::string VERSION = \"${CODENAME}-${VERSION}\"\; const std::string LICENSE = \"LGPL-3.0\" \; diff --git a/Makefile b/Makefile index 1cb9a153..44cbf342 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,33 @@ +# === Configuration === + +# User-overridable variables +CC ?= clang +CXX ?= clang++ +EXE ?= StockDory + +# Detect OS and set environment-specific variables ifeq ($(OS),Windows_NT) - CP = powershell -Command "Copy-Item" - RM = powershell -Command "Remove-Item -Recurse -Force" - EXTENSION = .exe + CP = powershell -Command "Copy-Item -Force" + RM = powershell -Command "Remove-Item -Recurse -Force" + EXT = .exe LLVM_PROFDATA = llvm-profdata - SLASH = \\ + SLASH = \\ else - CP = cp - RM = rm - EXTENSION = - LLVM_PROFDATA = llvm-profdata-16 - SLASH = / + UNIX_OS := $(shell uname -s) + + CP = cp + RM = rm -rf + EXT = + LLVM_PROFDATA = llvm-profdata-20 + SLASH = / + + ifeq ($(UNIX_OS), Darwin) + LLVM_PROFDATA = llvm-profdata + endif endif +# === Targets === + all: openbench openbench: @@ -19,11 +35,39 @@ ifdef EVALFILE $(RM) src/Engine/Model/* && \ $(CP) $(EVALFILE) src/Engine/Model/ endif - cmake -B RB -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -G Ninja -DPGO=True && \ - cmake --build RB --config Release && \ - RB$(SLASH)StockDory$(EXTENSION) bench && \ - $(LLVM_PROFDATA) merge -output=RB/pgo.profdata RB/pgo.profraw && \ - $(RM) RB/StockDory$(EXTENSION) && \ - cmake -B RB -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -G Ninja -DPGO=True && \ - cmake --build RB --config Release && \ - $(CP) RB/StockDory$(EXTENSION) StockDory$(EXTENSION) + @echo "[*] Performing initial build for profiling..." + cmake -B Build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_C_COMPILER=$(CC) \ + -DCMAKE_CXX_COMPILER=$(CXX) \ + -DBUILD_NATIVE=ON \ + -DPGO=ON + cmake --build Build --config Release + + @echo "[*] Running benchmark to generate profiling data..." + Build$(SLASH)StockDory$(EXT) bench + + @echo "[*] Merging profiling data..." + $(LLVM_PROFDATA) merge -output=Build/pgo.profdata Build/pgo.profraw + $(RM) Build$(SLASH)StockDory$(EXT) + $(RM) Build$(SLASH)pgo.profraw + + @echo "[*] Performing optimized build with profiling data..." + cmake -B Build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_C_COMPILER=$(CC) \ + -DCMAKE_CXX_COMPILER=$(CXX) \ + -DBUILD_NATIVE=ON \ + -DPGO=ON + cmake --build Build --config Release + + @echo "[*] Copying final binary to root directory..." + $(CP) Build$(SLASH)StockDory$(EXT) $(EXE)$(EXT) + +# === Utility Targets === + +clean: + $(RM) Build + $(RM) $(EXE)$(EXT) + +.PHONY: all openbench clean diff --git a/README.md b/README.md index c09c3879..f7c0b403 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ instructions below. StockDory is written in C++ and uses CMake as its build system. **Requirements**: -- 🏭 CMake >= 3.15 -- 🐉 Clang (LLVM) >= 16.0.0 +- 🏭 CMake >= 3.21 +- 🐉 Clang (LLVM) >= 20.0.0 - 🥷 Ninja >= 1.10.2 **Steps**: @@ -86,7 +86,7 @@ If you would like to report a bug, please open an issue on this GitHub repositor If you would like to contribute to the codebase, please fork this repository, create a new branch (naming it appropriately for the changes you are making), make your changes. -Then, create an account on the [FindingChess Testing Framework](http://tests.findingchess.com/). +Then, create an account on [Verdict](http://verdict.shaheryarsohail.com/). Once your account is approved, appropriately set the source repository on your profile, and create a test for your branch. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..47bf070a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,29 @@ +[build-system] +requires = [ + "scikit-build-core>=0.10.0", + "pybind11" +] +build-backend = "scikit_build_core.build" + +[project] +name = "StockDory" +version = "0.1+0" +description = "Python Bindings for StockDory" +readme = "README.md" +license = { file = "LICENSE" } +requires-python = ">=3.7" +authors = [ + { name = "StockDory Authors" } +] +keywords = [ "chess", "engine", "nnue" ] +classifiers = [ + "Programming Language :: Python :: 3", + "Operating System :: Microsoft :: Windows", +] + +[tool.scikit-build] +build.verbose = true +cmake.args = ["-G", "Ninja"] +cmake.define = { "CMAKE_BUILD_TYPE" = "Release", "PYMODULE" = "ON" } + +wheel.install-dir = "" diff --git a/src/Backend/Board.h b/src/Backend/Board.h index 9e2732f0..1f3c7949 100644 --- a/src/Backend/Board.h +++ b/src/Backend/Board.h @@ -7,725 +7,724 @@ #define STOCKDORY_BOARD_H #include -#include #include +#include +#include #include "Type/BitBoard.h" -#include "Type/Piece.h" +#include "Type/CheckBitBoard.h" #include "Type/Color.h" +#include "Type/Piece.h" #include "Type/PieceColor.h" #include "Type/PinBitBoard.h" -#include "Type/CheckBitBoard.h" #include "Type/PreviousState.h" #include "Type/Zobrist.h" #include "Template/MoveType.h" #include "Move/AttackTable.h" -#include "Move/UtilityTable.h" #include "Move/BlackMagicFactory.h" +#include "Move/RayTable.h" #include "../External/strutil.h" #include "../Engine/Evaluation.h" -#include "Util.h" - namespace StockDory { class Board { - private: - std::array, 3> BB {}; - - std::array PieceAndColor {}; + constexpr static uint8_t CastlingMask = 0xF; + constexpr static uint8_t WhiteKCastleMask = 0x8; + constexpr static uint8_t WhiteQCastleMask = 0x4; + constexpr static uint8_t BlackKCastleMask = 0x2; + constexpr static uint8_t BlackQCastleMask = 0x1; - std::array ColorBB {}; + constexpr static uint8_t ColorFlipMask = 0x10; - // [COLOR TO MOVE] [WHITE KING CASTLE] [WHITE QUEEN CASTLE] [BLACK KING CASTLE] [BLACK QUEEN CASTLE] - // [ 4 BITS ] [ 1 BIT ] [ 1 BIT ] [ 1 BIT ] [ 1 BIT ] - uint8_t CastlingRightAndColorToMove = 0; + constexpr static std::array ColorCastleMask { + WhiteKCastleMask | WhiteQCastleMask, + BlackKCastleMask | BlackQCastleMask, + 0 + }; - BitBoard EnPassantTarget = BBDefault; + std::array, 3> BB {}; - ZobristHash Hash = 0; + std::array PieceAndColor {}; - constexpr static uint8_t CastlingMask = 0x0F; - constexpr static uint8_t WhiteKCastleMask = 0x08; - constexpr static uint8_t WhiteQCastleMask = 0x04; - constexpr static uint8_t BlackKCastleMask = 0x02; - constexpr static uint8_t BlackQCastleMask = 0x01; + std::array ColorBB {}; - constexpr static uint8_t ColorFlipMask = 0x10; + // [COLOR TO MOVE] [WHITE KING CASTLE] [WHITE QUEEN CASTLE] [BLACK KING CASTLE] [BLACK QUEEN CASTLE] + // [ 4 BITS ] [ 1 BIT ] [ 1 BIT ] [ 1 BIT ] [ 1 BIT ] + uint8_t CastlingRightAndColorToMove = 0; - constexpr static std::array ColorCastleMask { - WhiteKCastleMask | WhiteQCastleMask, - BlackKCastleMask | BlackQCastleMask - }; + BitBoard EnPassantTarget = BBDefault; - constexpr static std::array, 2> CastleRookSquareStart {{ - {Square::H1, Square::A1}, - {Square::H8, Square::A8} - }}; - - constexpr static std::array, 2> CastleRookSquareEnd {{ - {Square::F1, Square::D1}, - {Square::F8, Square::D8} - }}; + ZobristHash Hash = 0; public: - Board() : Board("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") {} - - explicit Board(const std::string& fen) - { - PieceColor none = PieceColor(NAP, NAC); - std::fill(std::begin(PieceAndColor), std::end(PieceAndColor), none); + Board() : Board("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") {} - for (uint8_t i = 0; i < 3; i++) - std::fill(std::begin(BB[i]), std::end(BB[i]), BBDefault); + Board(const std::string& fen) + { + constexpr auto none = PieceColor(NAP, NAC); + std::ranges::fill(PieceAndColor, none); - std::vector splitFen = strutil::split(fen, " "); + for (uint8_t i = 0; i < 3; i++) + std::ranges::fill(BB[i], BBDefault); - assert(splitFen.size() == 6); + const std::vector splitFen = strutil::split(fen, " "); - std::vector splitPosition = strutil::split(splitFen[0], "/"); - std::reverse(splitPosition.begin(), splitPosition.end()); + assert(splitFen.size() == 6); - assert(splitPosition.size() == 8); + std::vector splitPosition = strutil::split(splitFen[0], "/"); + std::ranges::reverse(splitPosition); - for (uint8_t v = 0; v < 8; v++) { - std::string& rankStr = splitPosition[v]; - uint8_t h = 0; - for (char p : rankStr) { - if (isdigit(p)) { - h += static_cast(p - 48); - continue; - } + assert(splitPosition.size() == 8); - Color color = Black; - - if (isupper(p)) color = White; - - Piece piece = NAP; - switch (tolower(p)) { - case 'p': - piece = Pawn; - break; - case 'n': - piece = Knight; - break; - case 'b': - piece = Bishop; - break; - case 'r': - piece = Rook; - break; - case 'q': - piece = Queen; - break; - case 'k': - piece = King; - break; - } - - uint8_t idx = v * 8 + h; - auto sq = static_cast(idx); - Set(BB[color][piece], sq); - PieceAndColor[idx] = PieceColor(piece, color); - if (piece == NAP) std::cout << "ERROR" << std::endl; - Hash = HashPiece(Hash, piece, color, sq); + for (uint8_t v = 0; v < 8; v++) { + std::string& rankStr = splitPosition[v]; + uint8_t h = 0; + for (const char p: rankStr) { + if (isdigit(p)) { + h += static_cast(p - 48); + continue; + } - h++; + Color color = Black; + + if (isupper(p)) color = White; + + Piece piece = NAP; + switch (tolower(p)) { + case 'p': + piece = Pawn; + break; + case 'n': + piece = Knight; + break; + case 'b': + piece = Bishop; + break; + case 'r': + piece = Rook; + break; + case 'q': + piece = Queen; + break; + case 'k': + piece = King; + break; + default:; } - } - if (splitFen[1][0] == 'w') { - CastlingRightAndColorToMove = White << 4; - Hash = HashColorFlip(Hash); - } else { - CastlingRightAndColorToMove = Black << 4; - } + uint8_t idx = v * 8 + h; + const auto sq = static_cast(idx); - std::string& castlingData = splitFen[2]; - CastlingRightAndColorToMove |= (castlingData.find('K') != std::string::npos ? 0x8 : 0x0); - CastlingRightAndColorToMove |= (castlingData.find('Q') != std::string::npos ? 0x4 : 0x0); - CastlingRightAndColorToMove |= (castlingData.find('k') != std::string::npos ? 0x2 : 0x0); - CastlingRightAndColorToMove |= (castlingData.find('q') != std::string::npos ? 0x1 : 0x0); + Set(BB[color][piece], sq); - Hash = HashCastling(Hash, CastlingRightAndColorToMove & CastlingMask); + PieceAndColor[idx] = PieceColor(piece, color); - EnPassantTarget = BBDefault; - std::string& epData = splitFen[3]; - if (epData.length() == 2) { - Square epSq = Util::StringToSquare(epData); + if (piece == NAP) std::cout << "ERROR" << std::endl; - if (AttackTable::Pawn[Opposite(ColorToMove())][epSq] & BB[ColorToMove()][Pawn]) { - EnPassantTarget = FromSquare(epSq); - Hash = HashEnPassant(Hash, epSq); - } - } + Hash = Zobrist::HashPiece(Hash, piece, color, sq); - ColorBB[Color::White] = BBDefault; - ColorBB[Color::Black] = BBDefault; - for (Piece p = Pawn; p != NAP; p = Next(p)) { - ColorBB[White] |= BB[White][p]; - ColorBB[Black] |= BB[Black][p]; + h++; } - ColorBB[NAC] = ~(ColorBB[White] | ColorBB[Black]); } - inline void LoadForEvaluation() const - { - Evaluation::ResetNetworkState(); - - for (Square sq = A1; sq < NASQ; sq = Next(sq)) { - PieceColor pc = PieceAndColor[sq]; - if (pc.Piece() == NAP || pc.Color() == NAC) continue; - - Evaluation::Activate(pc.Piece(), pc.Color(), sq); - } + if (splitFen[1][0] == 'w') { + CastlingRightAndColorToMove = White << 4; + Hash = Zobrist::HashColorFlip(Hash); + } else { + CastlingRightAndColorToMove = Black << 4; } - [[nodiscard]] - inline std::string Fen() const - { - std::array fenRank; - - for (uint8_t v = 0; v < 8; v++) { - std::stringstream rankStr; - uint8_t e = 0; - for (uint8_t h = 0; h < 8; h++) { - const PieceColor pc = PieceAndColor[v * 8 + h]; - - if (pc.Piece() == NAP) { - e++; - - if (h == 7) { - rankStr << static_cast(e); - e = 0; - } - continue; - } + const std::string& castlingData = splitFen[2]; + CastlingRightAndColorToMove |= castlingData.find('K') != std::string::npos ? WhiteKCastleMask : 0x0; + CastlingRightAndColorToMove |= castlingData.find('Q') != std::string::npos ? WhiteQCastleMask : 0x0; + CastlingRightAndColorToMove |= castlingData.find('k') != std::string::npos ? BlackKCastleMask : 0x0; + CastlingRightAndColorToMove |= castlingData.find('q') != std::string::npos ? BlackQCastleMask : 0x0; - if (e != 0) { - rankStr << static_cast(e); - e = 0; - } + Hash = Zobrist::HashCastling(Hash, CastlingRightAndColorToMove & CastlingMask); - char p; - - switch (pc.Piece()) { - case Pawn: - p = 'p'; - break; - case Knight: - p = 'n'; - break; - case Bishop: - p = 'b'; - break; - case Rook: - p = 'r'; - break; - case Queen: - p = 'q'; - break; - case King: - p = 'k'; - break; - case NAP: - break; - } - - if (pc.Color() == White) p = static_cast(toupper(p)); - - rankStr << p; - } - - fenRank[v] = rankStr.str(); + EnPassantTarget = BBDefault; + if (const std::string& epData = splitFen[3]; epData.length() == 2) { + if (const Square epSq = FromString(epData); + AttackTable::Pawn[Opposite(ColorToMove())][epSq] & BB[ColorToMove()][Pawn]) { + EnPassantTarget = FromSquare(epSq); + Hash = Zobrist::HashEnPassant(Hash, epSq); } - - std::stringstream fen; - for (uint8_t v = 0; v < 8; v++) { - fen << fenRank[7 - v]; - if (v != 7) fen << '/'; - } - - fen << ' '; - fen << (ColorToMove() == White ? 'w' : 'b'); - fen << ' '; - - if (CastlingRightAndColorToMove & CastlingMask) { - if (CastlingRightAndColorToMove & WhiteKCastleMask) fen << 'K'; - if (CastlingRightAndColorToMove & WhiteQCastleMask) fen << 'Q'; - if (CastlingRightAndColorToMove & BlackKCastleMask) fen << 'k'; - if (CastlingRightAndColorToMove & BlackQCastleMask) fen << 'q'; - } else fen << '-'; - - fen << ' '; - if (EnPassantSquare() != NASQ) fen << Util::SquareToString(ToSquare(EnPassantTarget)); - else fen << '-'; - - // Implement half and full move clocks. - fen << ' '; - fen << '0'; - fen << ' '; - fen << '1'; - - return fen.str(); } - [[nodiscard]] - constexpr inline ZobristHash Zobrist() const - { - return Hash; + ColorBB[White] = BBDefault; + ColorBB[Black] = BBDefault; + for (Piece p = Pawn; p != NAP; p = Next(p)) { + ColorBB[White] |= BB[White][p]; + ColorBB[Black] |= BB[Black][p]; } + ColorBB[NAC] = ~(ColorBB[White] | ColorBB[Black]); + } - constexpr inline PieceColor operator [](const Square sq) const - { - return PieceAndColor[sq]; - } + void LoadForEvaluation() const + { + Evaluation::ResetNetworkState(); - constexpr inline BitBoard operator [](const Color c) const - { - return ColorBB[c]; - } - - template - [[nodiscard]] - constexpr inline BitBoard PieceBoard(const Piece p) const - { - assert(p != NAP); - assert(Color != NAC); + for (Square sq = A1; sq < NASQ; sq = Next(sq)) { + PieceColor pc = PieceAndColor[sq]; + if (pc.Piece() == NAP || pc.Color() == NAC) continue; - return BB[Color][p]; + Evaluation::Activate(pc.Piece(), pc.Color(), sq); } + } - [[nodiscard]] - constexpr inline BitBoard PieceBoard(const Piece p, const Color c) const - { - assert(p != NAP); - assert(c != NAC); + std::string Fen() const + { + std::array fenRank; - return BB[c][p]; - } + for (uint8_t v = 0; v < 8; v++) { + std::stringstream rankStr; + uint8_t e = 0; + for (uint8_t h = 0; h < 8; h++) { + const PieceColor pc = PieceAndColor[v * 8 + h]; - [[nodiscard]] - constexpr inline Color ColorToMove() const - { - return static_cast(CastlingRightAndColorToMove >> 4); - } + if (pc.Piece() == NAP) { + e++; - template - [[nodiscard]] - constexpr inline bool CastlingRightK() const - { - if (Color == White) return CastlingRightAndColorToMove & WhiteKCastleMask; - if (Color == Black) return CastlingRightAndColorToMove & BlackKCastleMask; + if (h == 7) { + rankStr << static_cast(e); + e = 0; + } + continue; + } - throw std::invalid_argument("Invalid color"); - } + if (e != 0) { + rankStr << static_cast(e); + e = 0; + } - template - [[nodiscard]] - constexpr inline bool CastlingRightQ() const - { - if (Color == White) return CastlingRightAndColorToMove & WhiteQCastleMask; - if (Color == Black) return CastlingRightAndColorToMove & BlackQCastleMask; + char p = ' '; + + switch (pc.Piece()) { + case Pawn: + p = 'p'; + break; + case Knight: + p = 'n'; + break; + case Bishop: + p = 'b'; + break; + case Rook: + p = 'r'; + break; + case Queen: + p = 'q'; + break; + case King: + p = 'k'; + break; + case NAP: + break; + } - throw std::invalid_argument("Invalid color"); - } + if (pc.Color() == White) p = static_cast(toupper(p)); - [[nodiscard]] - constexpr inline BitBoard EnPassant() const - { - return EnPassantTarget; - } + rankStr << p; + } - [[nodiscard]] - constexpr inline Square EnPassantSquare() const - { - return ToSquare(EnPassantTarget); + fenRank[v] = rankStr.str(); } - template - [[nodiscard]] - constexpr inline bool Checked() const - { - constexpr Color by = Opposite(We); - - const Square king = ToSquare(BB[We][King]); + std::stringstream fen; + for (uint8_t v = 0; v < 8; v++) { + fen << fenRank[7 - v]; + if (v != 7) fen << '/'; + } - if (AttackTable::Pawn[We][king] & BB[by][Pawn]) return true; + fen << ' '; + fen << (ColorToMove() == White ? 'w' : 'b'); + fen << ' '; + + if (CastlingRightAndColorToMove & CastlingMask) { + if (CastlingRightAndColorToMove & WhiteKCastleMask) fen << 'K'; + if (CastlingRightAndColorToMove & WhiteQCastleMask) fen << 'Q'; + if (CastlingRightAndColorToMove & BlackKCastleMask) fen << 'k'; + if (CastlingRightAndColorToMove & BlackQCastleMask) fen << 'q'; + } else fen << '-'; + + fen << ' '; + if (EnPassantSquare() != NASQ) fen << ToString(ToSquare(EnPassantTarget)); + else fen << '-'; + + // Implement half and full move clocks. + fen << ' '; + fen << '0'; + fen << ' '; + fen << '1'; + + return fen.str(); + } + + ZobristHash Zobrist() const + { + return Hash; + } + + PieceColor operator [](const Square sq) const + { + return PieceAndColor[sq]; + } + + BitBoard operator [](const Color c) const + { + return ColorBB[c]; + } + + template + BitBoard PieceBoard(const Piece p) const + { + assert(p != NAP); + assert(Color != NAC); + + return BB[Color][p]; + } + + [[nodiscard]] + BitBoard PieceBoard(const Piece p, const Color c) const + { + assert(p != NAP); + assert(c != NAC); + + return BB[c][p]; + } + + Color ColorToMove() const + { + return static_cast(CastlingRightAndColorToMove >> 4); + } + + template + bool CastlingRightK() const + { + if (Color == White) return CastlingRightAndColorToMove & WhiteKCastleMask; + if (Color == Black) return CastlingRightAndColorToMove & BlackKCastleMask; + + throw std::invalid_argument("Invalid color"); + } + + template + bool CastlingRightQ() const + { + if (Color == White) return CastlingRightAndColorToMove & WhiteQCastleMask; + if (Color == Black) return CastlingRightAndColorToMove & BlackQCastleMask; + + throw std::invalid_argument("Invalid color"); + } + + BitBoard EnPassant() const + { + return EnPassantTarget; + } + + Square EnPassantSquare() const + { + return ToSquare(EnPassantTarget); + } + + template + bool Checked() const + { + constexpr Color by = Opposite(We); + + const Square king = ToSquare(BB[We][King]); + + if (AttackTable::Pawn[We][king] & BB[by][Pawn]) return true; + + if (AttackTable::Knight[king] & BB[by][Knight]) return true; + + const BitBoard occupied = ~ColorBB[NAC]; + const BitBoard queen = BB[by][Queen]; + + if (AttackTable::Sliding[BlackMagicFactory::MagicIndex(Bishop, king, occupied)] & + (queen | BB[by][Bishop])) + return true; + + if (AttackTable::Sliding[BlackMagicFactory::MagicIndex(Rook , king, occupied)] & + (queen | BB[by][ Rook ])) + return true; + + return AttackTable::King[king] & BB[by][King]; + } + + template + CheckBitBoard Check() const + { + uint8_t count = 0; + auto check = CheckBitBoard(); + + const Square sq = ToSquare(BB[Opposite(By)][King]); + + // Check if the square is under attack by opponent knights or pawns. + const BitBoard pawnCheck = AttackTable::Pawn[Opposite(By)][sq] & BB[By][Pawn]; + const BitBoard knightCheck = AttackTable::Knight[sq] & BB[By][Knight]; + + // If the square is under attack by a pawn or knight, add it our checks. + check.Check |= pawnCheck; + check.Check |= knightCheck; + + // Increment the count if there are checks. + count += static_cast(pawnCheck); + count += static_cast(knightCheck); + + // Check if the square is under attack by opponent bishops, rooks, or queens. + // For queen, we can merge with checks for bishop and rook. + const BitBoard queen = BB[By][Queen]; - if (AttackTable::Knight[king] & BB[by][Knight]) return true; + // All the occupied squares: + const BitBoard occupied = ~ColorBB[NAC]; + + // Check if the square is under attack by opponent bishops or queens (diagonally). + const BitBoard diagonalCheck = + AttackTable::Sliding[BlackMagicFactory::MagicIndex(Bishop, sq, occupied)] & + (queen | BB[By][Bishop]); - const BitBoard occupied = ~ColorBB[NAC]; - const BitBoard queen = BB[by][Queen]; + // Check if the square is under attack by opponent rooks or queens (straight). + const BitBoard straightCheck = + AttackTable::Sliding[BlackMagicFactory::MagicIndex(Rook, sq, occupied)] & + (queen | BB[By][Rook]); - if (AttackTable::Sliding[BlackMagicFactory::MagicIndex(Bishop, king, occupied)] & - (queen | BB[by][Bishop])) return true; + // For sliding attacks, we must add the square of the attack's origin and all the squares to us from the + // attack: + if (diagonalCheck) { + const Square diagonalCheckSq = ToSquare(diagonalCheck); + check.Check |= RayTable::Between[sq][diagonalCheckSq] | FromSquare(diagonalCheckSq); + } - if (AttackTable::Sliding[BlackMagicFactory::MagicIndex(Rook , king, occupied)] & - (queen | BB[by][Rook ])) return true; + if (straightCheck) { + const Square straightCheckSq = ToSquare(straightCheck); + check.Check |= RayTable::Between[sq][straightCheckSq] | FromSquare(straightCheckSq); - return AttackTable::King[king] & BB[by][King]; + // In the case where there is more than one check, we must increment the count once more, as it's a + // double check. + if (Count(straightCheck) > 1) count++; } - template - [[nodiscard]] - constexpr inline CheckBitBoard Check() const - { - uint8_t count = 0; - CheckBitBoard check = CheckBitBoard(); + count += static_cast(diagonalCheck); + count += static_cast(straightCheck); - const Square sq = ToSquare(BB[Opposite(By)][King]); + if (check.Check == BBDefault) check.Check = BBFilled; - // Check if the square is under attack by opponent knights or pawns. - const BitBoard pawnCheck = AttackTable::Pawn[Opposite(By)][sq] & BB[By][Pawn ]; - const BitBoard knightCheck = AttackTable::Knight [sq] & BB[By][Knight]; + check.DoubleCheck = count > 1; + return check; + } - // If the square is under attack by a pawn or knight, add it our checks. - check.Check |= pawnCheck; - check.Check |= knightCheck; + template + PinBitBoard Pin() const + { + auto pin = PinBitBoard(); - // Increment the count if there are checks. - count += static_cast(pawnCheck); - count += static_cast(knightCheck); + const Square sq = ToSquare(BB[We][King]); - // Check if the square is under attack by opponent bishops, rooks, or queens. - // For queen, we can merge with checks for bishop and rook. - const BitBoard queen = BB[By][Queen]; + // All the occupied squares: + // In this case, we want to let the pins pass through our pieces, since our pieces can move on the pins. + const BitBoard occupied = ColorBB[By]; - // All the occupied squares: - BitBoard occupied = ~ColorBB[NAC]; + // For queen, we can merge with checks for bishop and rook. + const BitBoard queen = BB[By][Queen]; - // Check if the square is under attack by opponent bishops or queens (diagonally). - const BitBoard diagonalCheck = - AttackTable::Sliding[BlackMagicFactory::MagicIndex(Bishop, sq, occupied)] & - (queen | BB[By][Bishop]); + // Check if the square is under attack by opponent bishops or queens (diagonally). + const BitBoard diagonalCheck = + AttackTable::Sliding[BlackMagicFactory::MagicIndex(Bishop, sq, occupied)] & + (queen | BB[By][Bishop]); - // Check if the square is under attack by opponent rooks or queens (straight). - const BitBoard straightCheck = - AttackTable::Sliding[BlackMagicFactory::MagicIndex(Rook , sq, occupied)] & - (queen | BB[By][Rook ]); + // Check if the square is under attack by opponent rooks or queens (straight). + const BitBoard straightCheck = + AttackTable::Sliding[BlackMagicFactory::MagicIndex(Rook, sq, occupied)] & + (queen | BB[By][Rook]); - // For sliding attacks, we must add the square of the attack's origin and all the squares to us from the - // attack: - if (diagonalCheck) { - const Square diagonalCheckSq = ToSquare(diagonalCheck); - check.Check |= UtilityTable::Between[sq][diagonalCheckSq] | FromSquare(diagonalCheckSq); - } + // Iterate through the attacks and check if the attack is a diagonally pinning one. + BitBoardIterator iterator(diagonalCheck); + for (Square attSq = iterator.Value(); attSq != NASQ; attSq = iterator.Value()) + if (const BitBoard possiblePin = RayTable::Between[sq][attSq] | FromSquare(attSq); + Count(possiblePin & ColorBB[We]) == 1) pin.Diagonal |= possiblePin; - if (straightCheck) { - const Square straightCheckSq = ToSquare(straightCheck); - check.Check |= UtilityTable::Between[sq][straightCheckSq] | FromSquare(straightCheckSq); + // Iterate through the attacks and check if the attack is a straight pinning one. + iterator = BitBoardIterator(straightCheck); + for (Square attSq = iterator.Value(); attSq != NASQ; attSq = iterator.Value()) + if (const BitBoard possiblePin = RayTable::Between[sq][attSq] | FromSquare(attSq); + Count(possiblePin & ColorBB[We]) == 1) pin.Straight |= possiblePin; - // In the case where there is more than one check, we must increment the count once more, as it's a - // double check. - if (Count(straightCheck) > 1) count++; - } + return pin; + } - count += static_cast(diagonalCheck); - count += static_cast(straightCheck); + BitBoard SquareAttackers(const Square sq, const BitBoard occ) const + { + BitBoard attackers = AttackTable::Pawn[White][sq] & BB[Black][ Pawn ] | + AttackTable::Pawn[Black][sq] & BB[White][ Pawn ] | + AttackTable::Knight [sq] & (BB[White][Knight] | BB[Black][Knight]) | + AttackTable::King [sq] & (BB[White][ King ] | BB[Black][ King ]) ; - if (check.Check == BBDefault) check.Check = BBFilled; + attackers |= AttackTable::Sliding[BlackMagicFactory::MagicIndex(Bishop, sq, occ)] & + (BB[White][Bishop] | BB[Black][Bishop] | BB[White][Queen] | BB[Black][Queen]); - check.DoubleCheck = count > 1; - return check; - } + attackers |= AttackTable::Sliding[BlackMagicFactory::MagicIndex(Rook, sq, occ)] & + (BB[White][ Rook ] | BB[Black][ Rook ] | BB[White][Queen] | BB[Black][Queen]); - template - [[nodiscard]] - constexpr inline PinBitBoard Pin() const - { - PinBitBoard pin = PinBitBoard(); - - const Square sq = ToSquare(BB[We][King]); + return attackers; + } - // All the occupied squares: - // In this case, we want to let the pins pass through our pieces, since our pieces can move on the pins. - const BitBoard occupied = ColorBB[By]; + PreviousStateNull Move() + { + const auto state = PreviousStateNull(EnPassantSquare()); - // For queen, we can merge with checks for bishop and rook. - const BitBoard queen = BB[By][Queen]; + Hash = Zobrist::HashEnPassant(Hash, EnPassantSquare()); + EnPassantTarget = BBDefault; - // Check if the square is under attack by opponent bishops or queens (diagonally). - const BitBoard diagonalCheck = - AttackTable::Sliding[BlackMagicFactory::MagicIndex(Bishop, sq, occupied)] & - (queen | BB[By][Bishop]); + CastlingRightAndColorToMove ^= ColorFlipMask; + Hash = Zobrist::HashColorFlip(Hash); - // Check if the square is under attack by opponent rooks or queens (straight). - const BitBoard straightCheck = - AttackTable::Sliding[BlackMagicFactory::MagicIndex(Rook , sq, occupied)] & - (queen | BB[By][Rook ]); - - // Iterate through the attacks and check if the attack is a diagonally pinning one. - BitBoardIterator iterator (diagonalCheck); - for (Square attSq = iterator.Value(); attSq != NASQ; attSq = iterator.Value()) { - const BitBoard possiblePin = UtilityTable::Between[sq][attSq] | FromSquare(attSq); - if (Count(possiblePin & ColorBB[We]) == 1) pin.Diagonal |= possiblePin; - } + return state; + } - // Iterate through the attacks and check if the attack is a straight pinning one. - iterator = BitBoardIterator(straightCheck); - for (Square attSq = iterator.Value(); attSq != NASQ; attSq = iterator.Value()) { - const BitBoard possiblePin = UtilityTable::Between[sq][attSq] | FromSquare(attSq); - if (Count(possiblePin & ColorBB[We]) == 1) pin.Straight |= possiblePin; - } - - return pin; + void UndoMove(const PreviousStateNull& state) + { + if (state.EnPassant != NASQ) { + EnPassantTarget = FromSquare(state.EnPassant); + Hash = Zobrist::HashEnPassant(Hash, state.EnPassant); } - [[nodiscard]] - constexpr inline BitBoard SquareAttackers(const Square sq, const BitBoard occ) const - { - BitBoard attackers = (AttackTable::Pawn [White][sq] & BB[Black][Pawn ]) | - (AttackTable::Pawn [Black][sq] & BB[White][Pawn ]) | - (AttackTable::Knight [sq] & (BB[White][Knight] | BB[Black][Knight])) | - (AttackTable::King [sq] & (BB[White][King ] | BB[Black][King ])) ; - - attackers |= AttackTable::Sliding[BlackMagicFactory::MagicIndex(Bishop, sq, occ)] & - (BB[White][Bishop] | BB[Black][Bishop] | BB[White][Queen] | BB[Black][Queen]); - - attackers |= AttackTable::Sliding[BlackMagicFactory::MagicIndex(Rook , sq, occ)] & - (BB[White][Rook ] | BB[Black][Rook ] | BB[White][Queen] | BB[Black][Queen]); + CastlingRightAndColorToMove ^= ColorFlipMask; + Hash = Zobrist::HashColorFlip(Hash); + } - return attackers; - } + template + PreviousState Move(const Square from, const Square to, const Piece promotion = NAP) + { + if (T & NNUE) Evaluation::PreMove(); - constexpr inline PreviousStateNull Move() - { - auto state = PreviousStateNull(EnPassantSquare()); + auto state = PreviousState(PieceAndColor[from], PieceAndColor[to], + EnPassantSquare(), CastlingRightAndColorToMove, + Hash); - Hash = HashEnPassant(Hash, EnPassantSquare()); - EnPassantTarget = BBDefault; + Hash = Zobrist::HashEnPassant(Hash, EnPassantSquare()); + EnPassantTarget = BBDefault; - CastlingRightAndColorToMove ^= ColorFlipMask; - Hash = HashColorFlip(Hash); + CastlingRightAndColorToMove ^= ColorFlipMask; + Hash = Zobrist::HashColorFlip(Hash); - return state; - } - - constexpr inline void UndoMove(const PreviousStateNull& state) - { - if (state.EnPassant != NASQ) { - EnPassantTarget = FromSquare(state.EnPassant); - Hash = HashEnPassant(Hash, state.EnPassant); - } + Hash = Zobrist::HashCastling(Hash, CastlingRightAndColorToMove & CastlingMask); - CastlingRightAndColorToMove ^= ColorFlipMask; - Hash = HashColorFlip(Hash); - } + const Piece pieceF = state.MovedPiece.Piece(); + const Color colorF = state.MovedPiece.Color(); + const Piece pieceT = state.CapturedPiece.Piece(); + const Color colorT = state.CapturedPiece.Color(); - template - constexpr inline PreviousState Move(const Square from, const Square to, const Piece promotion = NAP) + using RookCastlingHandler = std::array, 2>, 2>; + constexpr static RookCastlingHandler RookCastlingMask = + [] constexpr -> RookCastlingHandler { - if (T & NNUE) Evaluation::PreMove(); + RookCastlingHandler result = {}; - auto state = PreviousState(PieceAndColor[from], PieceAndColor[to], - EnPassantSquare(), CastlingRightAndColorToMove, - Hash); + result[0] = {{}}; - Hash = HashEnPassant(Hash, EnPassantSquare()); - EnPassantTarget = BBDefault; + result[1][0] = {}; - CastlingRightAndColorToMove ^= ColorFlipMask; - Hash = HashColorFlip(Hash); - - Hash = HashCastling(Hash, CastlingRightAndColorToMove & CastlingMask); + for (Square sq = A1; sq <= NASQ; sq = Next(sq)) { + if (sq == A1) result[1][1][sq] = WhiteQCastleMask; + else if (sq == A8) result[1][1][sq] = BlackQCastleMask; + else if (sq == H1) result[1][1][sq] = WhiteKCastleMask; + else if (sq == H8) result[1][1][sq] = BlackKCastleMask; + } - const Piece pieceF = state.MovedPiece .Piece(); - const Color colorF = state.MovedPiece .Color(); - const Piece pieceT = state.CapturedPiece.Piece(); - const Color colorT = state.CapturedPiece.Color(); + return result; + }(); - if (pieceT == Rook && (CastlingRightAndColorToMove & ColorCastleMask[colorT])) { - if (to == A1) CastlingRightAndColorToMove &= ~WhiteQCastleMask; - else if (to == A8) CastlingRightAndColorToMove &= ~BlackQCastleMask; - else if (to == H1) CastlingRightAndColorToMove &= ~WhiteKCastleMask; - else if (to == H8) CastlingRightAndColorToMove &= ~BlackKCastleMask; - } + CastlingRightAndColorToMove &= ~RookCastlingMask[pieceT == Rook][ + static_cast(CastlingRightAndColorToMove & ColorCastleMask[colorT]) + ][ to ]; + CastlingRightAndColorToMove &= ~RookCastlingMask[pieceF == Rook][ + static_cast(CastlingRightAndColorToMove & ColorCastleMask[colorF]) + ][from]; - if (pieceF == Pawn) { - if (to == state.EnPassant) { - const Color opposite = Opposite(colorF); + if (pieceF == Pawn) { + if (to == state.EnPassant) { + const Color opposite = Opposite(colorF); - const auto epPawnSq = static_cast(state.EnPassant ^ 8); - EmptyNative ( Pawn, opposite, epPawnSq); - Hash = HashPiece(Hash, Pawn, opposite, epPawnSq); + const auto epPawnSq = static_cast(state.EnPassant ^ 8); + EmptyNative(Pawn, opposite, epPawnSq); + Hash = Zobrist::HashPiece(Hash, Pawn, opposite, epPawnSq); - if (T & NNUE) Evaluation::Deactivate(Pawn, opposite, epPawnSq); + if (T & NNUE) Evaluation::Deactivate(Pawn, opposite, epPawnSq); - state.EnPassantCapture = true; - } else if (static_cast(from ^ 16) == to) { - const auto epSq = static_cast(to ^ 8); - if (T & PERFT) { + state.EnPassantCapture = true; + } else if (static_cast(from ^ 16) == to) { + const auto epSq = static_cast(to ^ 8); + if (T & PERFT) { + EnPassantTarget = FromSquare(epSq); + Hash = Zobrist::HashEnPassant(Hash, epSq); + } else { + if (AttackTable::Pawn[colorF][epSq] & BB[Opposite(colorF)][Pawn]) { EnPassantTarget = FromSquare(epSq); - Hash = HashEnPassant(Hash, epSq); - } else { - if (AttackTable::Pawn[colorF][epSq] & BB[Opposite(colorF)][Pawn]) { - EnPassantTarget = FromSquare(epSq); - Hash = HashEnPassant(Hash, epSq); - } + Hash = Zobrist::HashEnPassant(Hash, epSq); } - } else if (promotion != NAP) { - state.PromotedPiece = promotion; + } + } else if (promotion != NAP) { + state.PromotedPiece = promotion; - EmptyNative (Pawn , colorF, from); - EmptyNative (pieceT , colorT, to ); - InsertNative(promotion, colorF, to ); + EmptyNative(Pawn , colorF, from); + EmptyNative(pieceT , colorT, to); + InsertNative(promotion, colorF, to); - if (T & NNUE) { - Evaluation::Deactivate(Pawn , colorF, from); - Evaluation::Activate (promotion, colorF, to ); + if (T & NNUE) { + Evaluation::Deactivate(Pawn, colorF, from); + Evaluation::Activate(promotion, colorF, to); - if (pieceT != NAP) Evaluation::Deactivate(pieceT, colorT, to); - } + if (pieceT != NAP) Evaluation::Deactivate(pieceT, colorT, to); + } - Hash = HashPiece(Hash, Pawn , colorF, from); - Hash = HashPiece(Hash, pieceT , colorT, to ); - Hash = HashPiece(Hash, promotion, colorF, to ); + Hash = Zobrist::HashPiece(Hash, Pawn, colorF, from); + Hash = Zobrist::HashPiece(Hash, pieceT, colorT, to); + Hash = Zobrist::HashPiece(Hash, promotion, colorF, to); - Hash = HashCastling(Hash, CastlingRightAndColorToMove & CastlingMask); + Hash = Zobrist::HashCastling(Hash, CastlingRightAndColorToMove & CastlingMask); - return state; - } - } else if ((CastlingRightAndColorToMove & ColorCastleMask[colorF])) { - if (pieceF == Rook) { - if (from == A1) CastlingRightAndColorToMove &= ~WhiteQCastleMask; - else if (from == A8) CastlingRightAndColorToMove &= ~BlackQCastleMask; - else if (from == H1) CastlingRightAndColorToMove &= ~WhiteKCastleMask; - else if (from == H8) CastlingRightAndColorToMove &= ~BlackKCastleMask; - } else if (pieceF == King) { - CastlingRightAndColorToMove &= ~ColorCastleMask[colorF]; - - if (to == C1 || to == C8 || to == G1 || to == G8) { - state.CastlingFrom = CastleRookSquareStart[colorF][to < from]; - state.CastlingTo = CastleRookSquareEnd [colorF][to < from]; - - EmptyNative (King, colorF, from ); - EmptyNative (Rook, colorF, state.CastlingFrom); - InsertNative(King, colorF, to ); - InsertNative(Rook, colorF, state.CastlingTo ); - - if (T & NNUE) { - Evaluation::Transition(King, colorF, from, to); - Evaluation::Transition(Rook, colorF, - state.CastlingFrom, state.CastlingTo); - } - - Hash = HashPiece(Hash, King, colorF, from ); - Hash = HashPiece(Hash, Rook, colorF, state.CastlingFrom); - Hash = HashPiece(Hash, King, colorF, to ); - Hash = HashPiece(Hash, Rook, colorF, state.CastlingTo ); - - Hash = HashCastling(Hash, CastlingRightAndColorToMove & CastlingMask); - - return state; - } - } + return state; } + } else if (pieceF == King && CastlingRightAndColorToMove & ColorCastleMask[colorF]) { + CastlingRightAndColorToMove &= ~ColorCastleMask[colorF]; + + if (to == C1 || to == C8 || to == G1 || to == G8) { + constexpr static std::array, 2> RookCastleSquareStart {{ + {H1, A1}, + {H8, A8} + }}; + + constexpr static std::array, 2> RookCastleSquareEnd {{ + {F1, D1}, + {F8, D8} + }}; + + state.CastlingFrom = RookCastleSquareStart[colorF][to < from]; + state.CastlingTo = RookCastleSquareEnd [colorF][to < from]; + + EmptyNative(King, colorF, from); + EmptyNative(Rook, colorF, state.CastlingFrom); + InsertNative(King, colorF, to ); + InsertNative(Rook, colorF, state.CastlingTo ); + + if (T & NNUE) { + Evaluation::Transition(King, colorF, from, to); + Evaluation::Transition(Rook, colorF, state.CastlingFrom, state.CastlingTo); + } - MoveNative(pieceF, colorF, from, pieceT, colorT, to); + Hash = Zobrist::HashPiece(Hash, King, colorF, from); + Hash = Zobrist::HashPiece(Hash, Rook, colorF, state.CastlingFrom); + Hash = Zobrist::HashPiece(Hash, King, colorF, to ); + Hash = Zobrist::HashPiece(Hash, Rook, colorF, state.CastlingTo ); - if (T & NNUE) { - Evaluation::Transition(pieceF, colorF, from, to); + Hash = Zobrist::HashCastling(Hash, CastlingRightAndColorToMove & CastlingMask); - if (pieceT != NAP) Evaluation::Deactivate(pieceT, colorT, to); + return state; } + } - Hash = HashPiece(Hash, pieceF, colorF, from); - Hash = HashPiece(Hash, pieceT, colorT, to ); - Hash = HashPiece(Hash, pieceF, colorF, to ); + MoveNative(pieceF, colorF, from, pieceT, colorT, to); - Hash = HashCastling(Hash, CastlingRightAndColorToMove & CastlingMask); + if (T & NNUE) { + Evaluation::Transition(pieceF, colorF, from, to); - return state; + if (pieceT != NAP) Evaluation::Deactivate(pieceT, colorT, to); } - template - constexpr inline void UndoMove(const PreviousState& state, const Square from, const Square to) - { - if (T & NNUE) Evaluation::PreUndoMove(); + Hash = Zobrist::HashPiece(Hash, pieceF, colorF, from); + Hash = Zobrist::HashPiece(Hash, pieceT, colorT, to); + Hash = Zobrist::HashPiece(Hash, pieceF, colorF, to); - CastlingRightAndColorToMove = state.CastlingRightAndColorToMove; - if (T & ZOBRIST) Hash = state.Hash; + Hash = Zobrist::HashCastling(Hash, CastlingRightAndColorToMove & CastlingMask); - if (state.EnPassant != NASQ) EnPassantTarget = FromSquare(state.EnPassant); - else EnPassantTarget = BBDefault; + return state; + } - if (state.PromotedPiece != NAP) { - EmptyNative (state.PromotedPiece, state.MovedPiece.Color(), to ); - InsertNative(Pawn , state.MovedPiece.Color(), from); - } else { - EmptyNative (state.MovedPiece.Piece(), state.MovedPiece.Color(), to ); - InsertNative(state.MovedPiece.Piece(), state.MovedPiece.Color(), from); - } + template + void UndoMove(const PreviousState& state, const Square from, const Square to) + { + if (T & NNUE) Evaluation::PreUndoMove(); - if (state.CapturedPiece.Piece() != NAP) { - InsertNative(state.CapturedPiece.Piece(), state.CapturedPiece.Color(), to); - } else if (state.EnPassantCapture) { - auto epPieceSq = static_cast(to ^ 8); - InsertNative(Pawn, Opposite(state.MovedPiece.Color()), epPieceSq); - } else if (state.CastlingFrom != NASQ) { - EmptyNative (Rook, state.MovedPiece.Color(), state.CastlingTo ); - InsertNative(Rook, state.MovedPiece.Color(), state.CastlingFrom); - } + CastlingRightAndColorToMove = state.CastlingRightAndColorToMove; + if (T & ZOBRIST) Hash = state.Hash; + + if (state.EnPassant != NASQ) EnPassantTarget = FromSquare(state.EnPassant); + else EnPassantTarget = BBDefault; + + if (state.PromotedPiece != NAP) { + EmptyNative(state.PromotedPiece, state.MovedPiece.Color(), to); + InsertNative(Pawn , state.MovedPiece.Color(), from); + } else { + EmptyNative(state.MovedPiece.Piece(), state.MovedPiece.Color(), to); + InsertNative(state.MovedPiece.Piece(), state.MovedPiece.Color(), from); } - template - constexpr inline void MoveNative(const Piece pF, const Color cF, const Square sqF, - const Piece pT, const Color cT, const Square sqT) - { - // Capture Section: - Set(BB[cT][pT], sqT); + if (state.CapturedPiece.Piece() != NAP) { + InsertNative(state.CapturedPiece.Piece(), state.CapturedPiece.Color(), to); + } else if (state.EnPassantCapture) { + const auto epPieceSq = static_cast(to ^ 8); + InsertNative(Pawn, Opposite(state.MovedPiece.Color()), epPieceSq); + } else if (state.CastlingFrom != NASQ) { + EmptyNative(Rook, state.MovedPiece.Color(), state.CastlingTo ); + InsertNative(Rook, state.MovedPiece.Color(), state.CastlingFrom); + } + } - Set(ColorBB[cT], sqT); + void MoveNative(const Piece pF, const Color cF, const Square sqF, + const Piece pT, const Color cT, const Square sqT) + { + // Capture Section: + Set(BB[cT][pT] , sqT); - // MoveNative Section: - Set(BB[cF][pF], sqF); - Set(BB[cF][pF], sqT); + Set(ColorBB[cT], sqT); - Set(ColorBB[cF], sqF); - Set(ColorBB[cF], sqT); + // MoveNative Section: + Set(BB[cF][pF], sqF); + Set(BB[cF][pF], sqT); - UpdateNACBB(); + Set(ColorBB[cF], sqF); + Set(ColorBB[cF], sqT); - PieceAndColor[sqT] = PieceAndColor[sqF]; - PieceAndColor[sqF] = PieceColor(NAP, NAC); - } + UpdateNACBB(); - template - constexpr inline void EmptyNative(const Piece p, const Color c, const Square sq) - { - Set(BB[c][p], sq); + PieceAndColor[sqT] = PieceAndColor[sqF]; + PieceAndColor[sqF] = PieceColor(NAP, NAC); + } - Set(ColorBB[c], sq); + void EmptyNative(const Piece p, const Color c, const Square sq) + { + Set(BB[c][p] , sq); - UpdateNACBB(); + Set(ColorBB[c], sq); - PieceAndColor[sq] = PieceColor(NAP, NAC); - } + UpdateNACBB(); - template - constexpr inline void InsertNative(const Piece p, const Color c, const Square sq) - { - Set(BB[c][p], sq); + PieceAndColor[sq] = PieceColor(NAP, NAC); + } - Set(ColorBB[c], sq); + void InsertNative(const Piece p, const Color c, const Square sq) + { + Set(BB[c][p] , sq); - UpdateNACBB(); + Set(ColorBB[c], sq); - PieceAndColor[sq] = PieceColor(p, c); - } + UpdateNACBB(); - constexpr inline void UpdateNACBB() - { - ColorBB[Color::NAC] = ~(ColorBB[Color::White] | ColorBB[Color::Black]); - } + PieceAndColor[sq] = PieceColor(p, c); + } + + void UpdateNACBB() + { + ColorBB[NAC] = ~(ColorBB[White] | ColorBB[Black]); + } }; diff --git a/src/Backend/Misc.h b/src/Backend/Misc.h new file mode 100644 index 00000000..475eeead --- /dev/null +++ b/src/Backend/Misc.h @@ -0,0 +1,24 @@ +// +// Copyright (c) 2025 StockDory authors. See the list of authors for more details. +// Licensed under LGPL-3.0. +// + +#ifndef STOCKDORY_MISC_H +#define STOCKDORY_MISC_H + +#include +#include +#include + +template>> +std::string ToHex(const T v) +{ + std::stringstream ss; + + ss << std::setfill('0') << std::setw(sizeof(T) * 2); + ss << std::uppercase << std::hex << v; + + return ss.str(); +} + +#endif //STOCKDORY_MISC_H diff --git a/src/Backend/Move/AttackTable.h b/src/Backend/Move/AttackTable.h index 17d47099..454d740d 100644 --- a/src/Backend/Move/AttackTable.h +++ b/src/Backend/Move/AttackTable.h @@ -8,161 +8,94 @@ #include -#include "BlackMagicFactory.h" - #include "../Type/BitBoard.h" -#include "../Type/Piece.h" - -#include "../Util.h" -namespace StockDory +namespace StockDory::AttackTable { - class AttackTable - { - - public: - - constexpr static std::array, 2> Pawn {{ - { // BEGIN WHITE - 0x0000000000000200, 0x0000000000000500, 0x0000000000000a00, 0x0000000000001400, - 0x0000000000002800, 0x0000000000005000, 0x000000000000a000, 0x0000000000004000, - 0x0000000000020000, 0x0000000000050000, 0x00000000000a0000, 0x0000000000140000, - 0x0000000000280000, 0x0000000000500000, 0x0000000000a00000, 0x0000000000400000, - 0x0000000002000000, 0x0000000005000000, 0x000000000a000000, 0x0000000014000000, - 0x0000000028000000, 0x0000000050000000, 0x00000000a0000000, 0x0000000040000000, - 0x0000000200000000, 0x0000000500000000, 0x0000000a00000000, 0x0000001400000000, - 0x0000002800000000, 0x0000005000000000, 0x000000a000000000, 0x0000004000000000, - 0x0000020000000000, 0x0000050000000000, 0x00000a0000000000, 0x0000140000000000, - 0x0000280000000000, 0x0000500000000000, 0x0000a00000000000, 0x0000400000000000, - 0x0002000000000000, 0x0005000000000000, 0x000a000000000000, 0x0014000000000000, - 0x0028000000000000, 0x0050000000000000, 0x00a0000000000000, 0x0040000000000000, - 0x0200000000000000, 0x0500000000000000, 0x0a00000000000000, 0x1400000000000000, - 0x2800000000000000, 0x5000000000000000, 0xa000000000000000, 0x4000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 - }, // END WHITE - { // BEGIN BLACK - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000002, 0x0000000000000005, 0x000000000000000a, 0x0000000000000014, - 0x0000000000000028, 0x0000000000000050, 0x00000000000000a0, 0x0000000000000040, - 0x0000000000000200, 0x0000000000000500, 0x0000000000000a00, 0x0000000000001400, - 0x0000000000002800, 0x0000000000005000, 0x000000000000a000, 0x0000000000004000, - 0x0000000000020000, 0x0000000000050000, 0x00000000000a0000, 0x0000000000140000, - 0x0000000000280000, 0x0000000000500000, 0x0000000000a00000, 0x0000000000400000, - 0x0000000002000000, 0x0000000005000000, 0x000000000a000000, 0x0000000014000000, - 0x0000000028000000, 0x0000000050000000, 0x00000000a0000000, 0x0000000040000000, - 0x0000000200000000, 0x0000000500000000, 0x0000000a00000000, 0x0000001400000000, - 0x0000002800000000, 0x0000005000000000, 0x000000a000000000, 0x0000004000000000, - 0x0000020000000000, 0x0000050000000000, 0x00000a0000000000, 0x0000140000000000, - 0x0000280000000000, 0x0000500000000000, 0x0000a00000000000, 0x0000400000000000, - 0x0002000000000000, 0x0005000000000000, 0x000a000000000000, 0x0014000000000000, - 0x0028000000000000, 0x0050000000000000, 0x00a0000000000000, 0x0040000000000000 - } // END BLACK - }}; - - constexpr static std::array Knight { - 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 - }; - - constexpr static std::array King { - 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 - }; - - static std::array Sliding; - - }; - -} // StockDory - -std::array StockDory::AttackTable::Sliding = []() { - std::array temp = std::array(); - - const std::array, 4>, 2> deltaStride = {{ - {{{ 1, 1 },{ 1, -1 },{ -1, -1 },{ -1, 1 }}}, {{{ 1, 0 },{ 0, -1 },{ -1, 0 },{ 0, 1 }}} + constexpr std::array, 2> Pawn {{ + { + // BEGIN WHITE + 0x0000000000000200, 0x0000000000000500, 0x0000000000000a00, 0x0000000000001400, + 0x0000000000002800, 0x0000000000005000, 0x000000000000a000, 0x0000000000004000, + 0x0000000000020000, 0x0000000000050000, 0x00000000000a0000, 0x0000000000140000, + 0x0000000000280000, 0x0000000000500000, 0x0000000000a00000, 0x0000000000400000, + 0x0000000002000000, 0x0000000005000000, 0x000000000a000000, 0x0000000014000000, + 0x0000000028000000, 0x0000000050000000, 0x00000000a0000000, 0x0000000040000000, + 0x0000000200000000, 0x0000000500000000, 0x0000000a00000000, 0x0000001400000000, + 0x0000002800000000, 0x0000005000000000, 0x000000a000000000, 0x0000004000000000, + 0x0000020000000000, 0x0000050000000000, 0x00000a0000000000, 0x0000140000000000, + 0x0000280000000000, 0x0000500000000000, 0x0000a00000000000, 0x0000400000000000, + 0x0002000000000000, 0x0005000000000000, 0x000a000000000000, 0x0014000000000000, + 0x0028000000000000, 0x0050000000000000, 0x00a0000000000000, 0x0040000000000000, + 0x0200000000000000, 0x0500000000000000, 0x0a00000000000000, 0x1400000000000000, + 0x2800000000000000, 0x5000000000000000, 0xa000000000000000, 0x4000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 + // END WHITE + }, + { + // BEGIN BLACK + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000002, 0x0000000000000005, 0x000000000000000a, 0x0000000000000014, + 0x0000000000000028, 0x0000000000000050, 0x00000000000000a0, 0x0000000000000040, + 0x0000000000000200, 0x0000000000000500, 0x0000000000000a00, 0x0000000000001400, + 0x0000000000002800, 0x0000000000005000, 0x000000000000a000, 0x0000000000004000, + 0x0000000000020000, 0x0000000000050000, 0x00000000000a0000, 0x0000000000140000, + 0x0000000000280000, 0x0000000000500000, 0x0000000000a00000, 0x0000000000400000, + 0x0000000002000000, 0x0000000005000000, 0x000000000a000000, 0x0000000014000000, + 0x0000000028000000, 0x0000000050000000, 0x00000000a0000000, 0x0000000040000000, + 0x0000000200000000, 0x0000000500000000, 0x0000000a00000000, 0x0000001400000000, + 0x0000002800000000, 0x0000005000000000, 0x000000a000000000, 0x0000004000000000, + 0x0000020000000000, 0x0000050000000000, 0x00000a0000000000, 0x0000140000000000, + 0x0000280000000000, 0x0000500000000000, 0x0000a00000000000, 0x0000400000000000, + 0x0002000000000000, 0x0005000000000000, 0x000a000000000000, 0x0014000000000000, + 0x0028000000000000, 0x0050000000000000, 0x00a0000000000000, 0x0040000000000000 + // END BLACK + } }}; - using MagicPair = std::pair; - - for (uint8_t i = 0; i < 2; i++) { - const auto p = static_cast(i + 2); - const std::array, 64>& magic = StockDory::BlackMagicFactory::Magic[i]; - const std::array, 4>& delta = deltaStride[i]; - - for (uint8_t h = 0; h < 8; h++) for (uint8_t v = 0; v < 8; v++) { - auto sq = static_cast(v * 8 + h); - - // Black Magic: - BitBoard mask = ~(magic[sq].first.second); - BitBoard occ = BBDefault; - - // Enumeration: - while (true) { - BitBoard moves = BBDefault; - - for (std::pair stride : delta) { - auto hI = static_cast(h); - auto vI = static_cast(v); - - while (!Get(occ, static_cast(vI * 8 + hI))) { - auto dHI = static_cast(hI + stride.first ); - auto dVI = static_cast(vI + stride.second); - - if (dHI > 7 || dHI < 0) break; - if (dVI > 7 || dVI < 0) break; - - hI = static_cast(hI + stride.first ); - vI = static_cast(vI + stride.second); - - moves |= FromSquare(static_cast(vI * 8 + hI)); - } - } - - // List insertion: - uint32_t idx = StockDory::BlackMagicFactory::MagicIndex(p, sq, occ); - temp[idx] = moves; + constexpr std::array Knight { + 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 + }; - // Occupation Recalculation: - occ = (occ - mask) & mask; + constexpr std::array King { + 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 + }; - // Skipping count: - if (Count(occ) == 0) break; - } - } - } + extern std::array Sliding; - return temp; -}(); +} // StockDory #endif //STOCKDORY_ATTACKTABLE_H diff --git a/src/Backend/Move/BlackMagicFactory.h b/src/Backend/Move/BlackMagicFactory.h index e490db3b..36b2e01e 100644 --- a/src/Backend/Move/BlackMagicFactory.h +++ b/src/Backend/Move/BlackMagicFactory.h @@ -12,174 +12,273 @@ #include "../Type/BitBoard.h" #include "../Type/Piece.h" +#include "AttackTable.h" +#include "RayTable.h" + namespace StockDory { class BlackMagicFactory { - public: - constexpr static std::array Horizontal { - 0x0101010101010101, 0x0202020202020202, 0x0404040404040404, 0x0808080808080808, - 0x1010101010101010, 0x2020202020202020, 0x4040404040404040, 0x8080808080808080 - }; - - constexpr static std::array Vertical { - 0x00000000000000FF, 0x000000000000FF00, 0x0000000000FF0000, 0x00000000FF000000, - 0x000000FF00000000, 0x0000FF0000000000, 0x00FF000000000000, 0xFF00000000000000 - }; - - constexpr static BitBoard Edged = Horizontal[0] | Horizontal[7] | Vertical[0] | Vertical[7]; - - private: - constexpr static std::array PieceValue { 9, 12 }; - - constexpr static std::array, 64> RookMagicConst { - std::pair(0x80280013FF84FFFF, 10890), std::pair(0x5FFBFEFDFEF67FFF, 50579), - std::pair(0xFFEFFAFFEFFDFFFF, 62020), std::pair(0x003000900300008A, 67322), - std::pair(0x0050028010500023, 80251), std::pair(0x0020012120A00020, 58503), - std::pair(0x0030006000C00030, 51175), std::pair(0x0058005806B00002, 83130), - std::pair(0x7FBFF7FBFBEAFFFC, 50430), std::pair(0x0000140081050002, 21613), - std::pair(0x0000180043800048, 72625), std::pair(0x7FFFE800021FFFB8, 80755), - std::pair(0xFFFFCFFE7FCFFFAF, 69753), std::pair(0x00001800C0180060, 26973), - std::pair(0x4F8018005FD00018, 84972), std::pair(0x0000180030620018, 31958), - std::pair(0x00300018010C0003, 69272), std::pair(0x0003000C0085FFFF, 48372), - std::pair(0xFFFDFFF7FBFEFFF7, 65477), std::pair(0x7FC1FFDFFC001FFF, 43972), - std::pair(0xFFFEFFDFFDFFDFFF, 57154), std::pair(0x7C108007BEFFF81F, 53521), - std::pair(0x20408007BFE00810, 30534), std::pair(0x0400800558604100, 16548), - std::pair(0x0040200010080008, 46407), std::pair(0x0010020008040004, 11841), - std::pair(0xFFFDFEFFF7FBFFF7, 21112), std::pair(0xFEBF7DFFF8FEFFF9, 44214), - std::pair(0xC00000FFE001FFE0, 57925), std::pair(0x4AF01F00078007C3, 29574), - std::pair(0xBFFBFAFFFB683F7F, 17309), std::pair(0x0807F67FFA102040, 40143), - std::pair(0x200008E800300030, 64659), std::pair(0x0000008780180018, 70469), - std::pair(0x0000010300180018, 62917), std::pair(0x4000008180180018, 60997), - std::pair(0x008080310005FFFA, 18554), std::pair(0x4000188100060006, 14385), - std::pair(0xFFFFFF7FFFBFBFFF, 0), std::pair(0x0000802000200040, 38091), - std::pair(0x20000202EC002800, 25122), std::pair(0xFFFFF9FF7CFFF3FF, 60083), - std::pair(0x000000404B801800, 72209), std::pair(0x2000002FE03FD000, 67875), - std::pair(0xFFFFFF6FFE7FCFFD, 56290), std::pair(0xBFF7EFFFBFC00FFF, 43807), - std::pair(0x000000100800A804, 73365), std::pair(0x6054000A58005805, 76398), - std::pair(0x0829000101150028, 20024), std::pair(0x00000085008A0014, 9513), - std::pair(0x8000002B00408028, 24324), std::pair(0x4000002040790028, 22996), - std::pair(0x7800002010288028, 23213), std::pair(0x0000001800E08018, 56002), - std::pair(0xA3A80003F3A40048, 22809), std::pair(0x2003D80000500028, 44545), - std::pair(0xFFFFF37EEFEFDFBE, 36072), std::pair(0x40000280090013C1, 4750), - std::pair(0xBF7FFEFFBFFAF71F, 6014), std::pair(0xFFFDFFFF777B7D6E, 36054), - std::pair(0x48300007E8080C02, 78538), std::pair(0xAFE0000FFF780402, 28745), - std::pair(0xEE73FFFBFFBB77FE, 8555), std::pair(0x0002000308482882, 1009) - }; - - constexpr static std::array, 64> BishopMagicConst { - std::pair(0xA7020080601803D8, 60984), std::pair(0x13802040400801F1, 66046), - std::pair(0x0A0080181001F60C, 32910), std::pair(0x1840802004238008, 16369), - std::pair(0xC03FE00100000000, 42115), std::pair(0x24C00BFFFF400000, 835), - std::pair(0x0808101F40007F04, 18910), std::pair(0x100808201EC00080, 25911), - std::pair(0xFFA2FEFFBFEFB7FF, 63301), std::pair(0x083E3EE040080801, 16063), - std::pair(0xC0800080181001F8, 17481), std::pair(0x0440007FE0031000, 59361), - std::pair(0x2010007FFC000000, 18735), std::pair(0x1079FFE000FF8000, 61249), - std::pair(0x3C0708101F400080, 68938), std::pair(0x080614080FA00040, 61791), - std::pair(0x7FFE7FFF817FCFF9, 21893), std::pair(0x7FFEBFFFA01027FD, 62068), - std::pair(0x53018080C00F4001, 19829), std::pair(0x407E0001000FFB8A, 26091), - std::pair(0x201FE000FFF80010, 15815), std::pair(0xFFDFEFFFDE39FFEF, 16419), - std::pair(0xCC8808000FBF8002, 59777), std::pair(0x7FF7FBFFF8203FFF, 16288), - std::pair(0x8800013E8300C030, 33235), std::pair(0x0420009701806018, 15459), - std::pair(0x7FFEFF7F7F01F7FD, 15863), std::pair(0x8700303010C0C006, 75555), - std::pair(0xC800181810606000, 79445), std::pair(0x20002038001C8010, 15917), - std::pair(0x087FF038000FC001, 8512), std::pair(0x00080C0C00083007, 73069), - std::pair(0x00000080FC82C040, 16078), std::pair(0x000000407E416020, 19168), - std::pair(0x00600203F8008020, 11056), std::pair(0xD003FEFE04404080, 62544), - std::pair(0xA00020C018003088, 80477), std::pair(0x7FBFFE700BFFE800, 75049), - std::pair(0x107FF00FE4000F90, 32947), std::pair(0x7F8FFFCFF1D007F8, 59172), - std::pair(0x0000004100F88080, 55845), std::pair(0x00000020807C4040, 61806), - std::pair(0x00000041018700C0, 73601), std::pair(0x0010000080FC4080, 15546), - std::pair(0x1000003C80180030, 45243), std::pair(0xC10000DF80280050, 20333), - std::pair(0xFFFFFFBFEFF80FDC, 33402), std::pair(0x000000101003F812, 25917), - std::pair(0x0800001F40808200, 32875), std::pair(0x084000101F3FD208, 4639), - std::pair(0x080000000F808081, 17077), std::pair(0x0004000008003F80, 62324), - std::pair(0x08000001001FE040, 18159), std::pair(0x72DD000040900A00, 61436), - std::pair(0xFFFFFEFFBFEFF81D, 57073), std::pair(0xCD8000200FEBF209, 61025), - std::pair(0x100000101EC10082, 81259), std::pair(0x7FBAFFFFEFE0C02F, 64083), - std::pair(0x7F83FFFFFFF07F7F, 56114), std::pair(0xFFF1FFFFFFF7FFC1, 57058), - std::pair(0x0878040000FFE01F, 58912), std::pair(0x945E388000801012, 22194), - std::pair(0x0840800080200FDA, 70880), std::pair(0x100000C05F582008, 11140) - }; - - using MagicPair = std::pair; - - constexpr static auto RookOccupiedMask = - [](const Square sq) constexpr { - // Horizontal files & vertical ranks. - const BitBoard h = Horizontal[sq % 8] & ~(Vertical[0] | Vertical[7]); - const BitBoard v = Vertical [sq / 8] & ~(Horizontal[0] | Horizontal[7]); - - // Occupied inside but not the square itself. - return (h | v) & ~FromSquare(sq); - }; - - constexpr static auto BishopOccupiedMask = - [](const Square sq) constexpr { - const int h = sq % 8; - const int v = sq / 8; - - // Simple ray cast. - BitBoard ray = BBDefault; - for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++) { - int dH = i - h; - int dV = j - v; - - if (dH < 0) dH = -dH; - if (dV < 0) dV = -dV; - - if (dH == dV && dV != 0) ray |= 1ULL << (j * 8 + i); - } + constexpr static std::array PieceValue {9, 12}; + + constexpr static std::array, 64> RookMagicConst { + std::pair(0x80280013FF84FFFF, 10890), std::pair(0x5FFBFEFDFEF67FFF, 50579), + std::pair(0xFFEFFAFFEFFDFFFF, 62020), std::pair(0x003000900300008A, 67322), + std::pair(0x0050028010500023, 80251), std::pair(0x0020012120A00020, 58503), + std::pair(0x0030006000C00030, 51175), std::pair(0x0058005806B00002, 83130), + std::pair(0x7FBFF7FBFBEAFFFC, 50430), std::pair(0x0000140081050002, 21613), + std::pair(0x0000180043800048, 72625), std::pair(0x7FFFE800021FFFB8, 80755), + std::pair(0xFFFFCFFE7FCFFFAF, 69753), std::pair(0x00001800C0180060, 26973), + std::pair(0x4F8018005FD00018, 84972), std::pair(0x0000180030620018, 31958), + std::pair(0x00300018010C0003, 69272), std::pair(0x0003000C0085FFFF, 48372), + std::pair(0xFFFDFFF7FBFEFFF7, 65477), std::pair(0x7FC1FFDFFC001FFF, 43972), + std::pair(0xFFFEFFDFFDFFDFFF, 57154), std::pair(0x7C108007BEFFF81F, 53521), + std::pair(0x20408007BFE00810, 30534), std::pair(0x0400800558604100, 16548), + std::pair(0x0040200010080008, 46407), std::pair(0x0010020008040004, 11841), + std::pair(0xFFFDFEFFF7FBFFF7, 21112), std::pair(0xFEBF7DFFF8FEFFF9, 44214), + std::pair(0xC00000FFE001FFE0, 57925), std::pair(0x4AF01F00078007C3, 29574), + std::pair(0xBFFBFAFFFB683F7F, 17309), std::pair(0x0807F67FFA102040, 40143), + std::pair(0x200008E800300030, 64659), std::pair(0x0000008780180018, 70469), + std::pair(0x0000010300180018, 62917), std::pair(0x4000008180180018, 60997), + std::pair(0x008080310005FFFA, 18554), std::pair(0x4000188100060006, 14385), + std::pair(0xFFFFFF7FFFBFBFFF, 0 ), std::pair(0x0000802000200040, 38091), + std::pair(0x20000202EC002800, 25122), std::pair(0xFFFFF9FF7CFFF3FF, 60083), + std::pair(0x000000404B801800, 72209), std::pair(0x2000002FE03FD000, 67875), + std::pair(0xFFFFFF6FFE7FCFFD, 56290), std::pair(0xBFF7EFFFBFC00FFF, 43807), + std::pair(0x000000100800A804, 73365), std::pair(0x6054000A58005805, 76398), + std::pair(0x0829000101150028, 20024), std::pair(0x00000085008A0014, 9513), + std::pair(0x8000002B00408028, 24324), std::pair(0x4000002040790028, 22996), + std::pair(0x7800002010288028, 23213), std::pair(0x0000001800E08018, 56002), + std::pair(0xA3A80003F3A40048, 22809), std::pair(0x2003D80000500028, 44545), + std::pair(0xFFFFF37EEFEFDFBE, 36072), std::pair(0x40000280090013C1, 4750), + std::pair(0xBF7FFEFFBFFAF71F, 6014), std::pair(0xFFFDFFFF777B7D6E, 36054), + std::pair(0x48300007E8080C02, 78538), std::pair(0xAFE0000FFF780402, 28745), + std::pair(0xEE73FFFBFFBB77FE, 8555), std::pair(0x0002000308482882, 1009) + }; + + constexpr static std::array, 64> BishopMagicConst { + std::pair(0xA7020080601803D8, 60984), std::pair(0x13802040400801F1, 66046), + std::pair(0x0A0080181001F60C, 32910), std::pair(0x1840802004238008, 16369), + std::pair(0xC03FE00100000000, 42115), std::pair(0x24C00BFFFF400000, 835), + std::pair(0x0808101F40007F04, 18910), std::pair(0x100808201EC00080, 25911), + std::pair(0xFFA2FEFFBFEFB7FF, 63301), std::pair(0x083E3EE040080801, 16063), + std::pair(0xC0800080181001F8, 17481), std::pair(0x0440007FE0031000, 59361), + std::pair(0x2010007FFC000000, 18735), std::pair(0x1079FFE000FF8000, 61249), + std::pair(0x3C0708101F400080, 68938), std::pair(0x080614080FA00040, 61791), + std::pair(0x7FFE7FFF817FCFF9, 21893), std::pair(0x7FFEBFFFA01027FD, 62068), + std::pair(0x53018080C00F4001, 19829), std::pair(0x407E0001000FFB8A, 26091), + std::pair(0x201FE000FFF80010, 15815), std::pair(0xFFDFEFFFDE39FFEF, 16419), + std::pair(0xCC8808000FBF8002, 59777), std::pair(0x7FF7FBFFF8203FFF, 16288), + std::pair(0x8800013E8300C030, 33235), std::pair(0x0420009701806018, 15459), + std::pair(0x7FFEFF7F7F01F7FD, 15863), std::pair(0x8700303010C0C006, 75555), + std::pair(0xC800181810606000, 79445), std::pair(0x20002038001C8010, 15917), + std::pair(0x087FF038000FC001, 8512), std::pair(0x00080C0C00083007, 73069), + std::pair(0x00000080FC82C040, 16078), std::pair(0x000000407E416020, 19168), + std::pair(0x00600203F8008020, 11056), std::pair(0xD003FEFE04404080, 62544), + std::pair(0xA00020C018003088, 80477), std::pair(0x7FBFFE700BFFE800, 75049), + std::pair(0x107FF00FE4000F90, 32947), std::pair(0x7F8FFFCFF1D007F8, 59172), + std::pair(0x0000004100F88080, 55845), std::pair(0x00000020807C4040, 61806), + std::pair(0x00000041018700C0, 73601), std::pair(0x0010000080FC4080, 15546), + std::pair(0x1000003C80180030, 45243), std::pair(0xC10000DF80280050, 20333), + std::pair(0xFFFFFFBFEFF80FDC, 33402), std::pair(0x000000101003F812, 25917), + std::pair(0x0800001F40808200, 32875), std::pair(0x084000101F3FD208, 4639), + std::pair(0x080000000F808081, 17077), std::pair(0x0004000008003F80, 62324), + std::pair(0x08000001001FE040, 18159), std::pair(0x72DD000040900A00, 61436), + std::pair(0xFFFFFEFFBFEFF81D, 57073), std::pair(0xCD8000200FEBF209, 61025), + std::pair(0x100000101EC10082, 81259), std::pair(0x7FBAFFFFEFE0C02F, 64083), + std::pair(0x7F83FFFFFFF07F7F, 56114), std::pair(0xFFF1FFFFFFF7FFC1, 57058), + std::pair(0x0878040000FFE01F, 58912), std::pair(0x945E388000801012, 22194), + std::pair(0x0840800080200FDA, 70880), std::pair(0x100000C05F582008, 11140) + }; + + using MagicPair = std::pair; + + constexpr static auto RookOccupiedMask = [](const Square sq) constexpr -> BitBoard + { + // Horizontal files & vertical ranks. + const BitBoard h = RayTable::Horizontal[sq % 8] & ~(RayTable::Vertical [0] | RayTable::Vertical [7]); + const BitBoard v = RayTable::Vertical [sq / 8] & ~(RayTable::Horizontal[0] | RayTable::Horizontal[7]); + + // Occupied inside but not the square itself. + return (h | v) & ~FromSquare(sq); + }; + + constexpr static auto BishopOccupiedMask = [](const Square sq) constexpr -> BitBoard + { + const int h = sq % 8; + const int v = sq / 8; + + // Simple ray cast. + BitBoard ray = BBDefault; + for (int i = 0; i < 8; i++) + for (int j = 0; j < 8; j++) { + int dH = i - h; + int dV = j - v; + + if (dH < 0) dH = -dH; + if (dV < 0) dV = -dV; + + if (dH == dV && dV != 0) ray |= 1ULL << (j * 8 + i); + } - return ray & ~Edged; - }; + return ray & ~RayTable::Edged; + }; public: - constexpr static std::array, 64>, 2> Magic = []() constexpr { - std::array, 64>, 2> temp = {}; + constexpr static std::array, 64>, 2> Magic = + [] constexpr -> std::array, 64>, 2> + { + std::array, 64>, 2> temp = {}; + + for (int h = 0; h < 8; h++) + for (int v = 0; v < 8; v++) { + const auto sq = static_cast(v * 8 + h); + const auto& [bmf, bms] = BishopMagicConst[sq]; + + // Black magic: + temp[0][sq] = {{bmf, ~BishopOccupiedMask(sq)}, bms}; + } - for (int h = 0; h < 8; h++) for (int v = 0; v < 8; v++) { - const auto sq = static_cast(v * 8 + h); - const std::pair& bishopConst = BishopMagicConst[sq]; + for (int h = 0; h < 8; h++) + for (int v = 0; v < 8; v++) { + const auto sq = static_cast(v * 8 + h); + const auto& [rmf, rms] = RookMagicConst[sq]; - // Black magic: - temp[0][sq] = - {{bishopConst.first, ~BishopOccupiedMask(sq)}, bishopConst.second}; - } + // Black magic: + temp[1][sq] = {{rmf, ~RookOccupiedMask(sq)}, rms}; + } + + return temp; + }(); + + constexpr static uint32_t MagicIndex(const Piece p, const Square sq, const BitBoard occupied) + { + // Magic: + const auto& [magicPair, base] = Magic[p - 2][sq]; + const auto& [magic , mask] = magicPair; + + // Hash: + return base + static_cast(((occupied | mask) * magic) >> (64 - PieceValue[p - 2])); + } + + }; + +} // StockDory + +auto StockDory::AttackTable::Sliding = [] -> std::array +{ + auto temp = std::array(); + + for (uint8_t i = 0; i < 2; i++) { + constexpr std::array, 4>, 2> deltaStride = {{ + {{ + {1, 1}, {1, -1}, {-1, -1}, {-1, 1} + }}, {{ + {1, 0}, {0, -1}, {-1, 0}, {0, 1} + }} + }}; + + const auto p = static_cast(i + 2); + const auto& magic = BlackMagicFactory::Magic[i]; + const auto& delta = deltaStride[i]; + + for (uint8_t h = 0; h < 8; h++) + for (uint8_t v = 0; v < 8; v++) { + const auto sq = static_cast(v * 8 + h); + + // Black Magic: + const BitBoard mask = ~(magic[sq].first.second); + BitBoard occ = BBDefault; + + // Enumeration: + while (true) { + BitBoard moves = BBDefault; - for (int h = 0; h < 8; h++) for (int v = 0; v < 8; v++) { - const auto sq = static_cast(v * 8 + h); - const std::pair& rookConst = RookMagicConst[sq]; + for (auto [hD, vD] : delta) { + auto hI = static_cast(h); + auto vI = static_cast(v); - // Black magic: - temp[1][sq] = - {{rookConst.first, ~RookOccupiedMask(sq)}, rookConst.second}; + while (!Get(occ, static_cast(vI * 8 + hI))) { + const auto dHI = static_cast(hI + hD); + const auto dVI = static_cast(vI + vD); + + if (dHI > 7 || dHI < 0) break; + if (dVI > 7 || dVI < 0) break; + + hI = static_cast(hI + hD); + vI = static_cast(vI + vD); + + moves |= FromSquare(static_cast(vI * 8 + hI)); + } + } + + // List insertion: + temp[BlackMagicFactory::MagicIndex(p, sq, occ)] = moves; + + // Occupation Recalculation: + // ReSharper disable once CppRedundantParentheses + occ = (occ - mask) & mask; + + // Skipping count: + if (Count(occ) == 0) break; } + } + } + + return temp; +}(); + +auto StockDory::RayTable::Between = [] -> std::array, 64> +{ + std::array, 64> temp = {}; - return temp; - }(); + for (Square f = A1; f != NASQ; f = Next(f)) { + const uint8_t fH = f % 8; + const uint8_t fV = f / 8; - constexpr static inline uint32_t MagicIndex(const Piece p, const Square sq, const BitBoard occupied) - { - const std::array, 64>& pieceMagic = Magic[p - 2]; + for (Square t = A1; t != NASQ; t = Next(t)) { + temp[f][t] = BBDefault; - // Get magic: - const std::pair& pieceMagicAtSq = pieceMagic[sq]; + if (f == t) continue; - // Relevant occupied squares: - const BitBoard relevantOccupied = occupied | pieceMagicAtSq.first.second; + const uint8_t tH = t % 8; + const uint8_t tV = t / 8; - // Hash: - const BitBoard hash = relevantOccupied * pieceMagicAtSq.first.first; + BitBoard occ; - // Offset applied: - return pieceMagicAtSq.second + static_cast((hash >> (64 - PieceValue[p - 2]))); + if (fH == tH || fV == tV) { + occ = FromSquare(f) | FromSquare(t); + + const uint32_t mF = BlackMagicFactory::MagicIndex(Rook, f, occ); + const uint32_t mT = BlackMagicFactory::MagicIndex(Rook, t, occ); + + // Rook squares: + temp[f][t] = AttackTable::Sliding[mF] & AttackTable::Sliding[mT]; + + continue; } - }; + auto absH = static_cast(static_cast(fH) - static_cast(tH)); + auto absV = static_cast(static_cast(fV) - static_cast(tV)); -} // StockDory + if (absH < 0) absH = static_cast(-absH); + if (absV < 0) absV = static_cast(-absV); + + if (absH != absV) continue; + + // Bishop squares: + occ = FromSquare(f) | FromSquare(t); + + const uint32_t mF = BlackMagicFactory::MagicIndex(Bishop, f, occ); + const uint32_t mT = BlackMagicFactory::MagicIndex(Bishop, t, occ); + + temp[f][t] = AttackTable::Sliding[mF] & AttackTable::Sliding[mT]; + } + } + + return temp; +}(); #endif //STOCKDORY_BLACKMAGICFACTORY_H diff --git a/src/Backend/Move/MoveList.h b/src/Backend/Move/MoveList.h index 804f46f2..8444275a 100644 --- a/src/Backend/Move/MoveList.h +++ b/src/Backend/Move/MoveList.h @@ -9,12 +9,11 @@ #include #include "../Type/BitBoard.h" -#include "../Type/Piece.h" +#include "../Type/CheckBitBoard.h" #include "../Type/Color.h" -#include "../Type/Square.h" -#include "../Type/Move.h" +#include "../Type/Piece.h" #include "../Type/PinBitBoard.h" -#include "../Type/CheckBitBoard.h" +#include "../Type/Square.h" #include "../Board.h" @@ -28,261 +27,252 @@ namespace StockDory class MoveList { - private: - BitBoard InternalContainer; + constexpr static BitBoard WhiteQueenCastlePath = 0x0EULL; + constexpr static BitBoard WhiteKingCastlePath = 0x60ULL; + constexpr static BitBoard BlackQueenCastlePath = WhiteQueenCastlePath << 56; + constexpr static BitBoard BlackKingCastlePath = WhiteKingCastlePath << 56; - constexpr static BitBoard WhiteQueenCastlePath = 0x0EULL ; - constexpr static BitBoard WhiteKingCastlePath = 0x60ULL ; - constexpr static BitBoard BlackQueenCastlePath = WhiteQueenCastlePath << 56; - constexpr static BitBoard BlackKingCastlePath = WhiteKingCastlePath << 56; + constexpr static BitBoard QueenCastlePathMask = 0x0C0000000000000CULL; - constexpr static BitBoard QueenCastlePathMask = 0x0C0000000000000CULL; + BitBoard InternalContainer; public: - constexpr static bool Promotion(const Square sq) - { - if (Piece != Piece::Pawn) return false; - - return (Color == White && sq > H6) || (Color == Black && sq < A3); - } - - constexpr MoveList(const Board& board, const Square sq, const PinBitBoard& pin, const CheckBitBoard& check) - { - InternalContainer = BBDefault; - - if (Piece == Piece::Pawn ) Pawn (board, sq, pin, check); - if (Piece == Piece::Knight) Knight(board, sq, pin, check); - if (Piece == Piece::Bishop) Bishop(board, sq, pin, check); - if (Piece == Piece::Rook ) Rook (board, sq, pin, check); - if (Piece == Piece::Queen ) Queen (board, sq, pin, check); - if (Piece == Piece::King ) King (board, sq ); - } - - [[nodiscard]] - constexpr inline uint8_t Count() const - { - return ::Count(InternalContainer); - } - - [[nodiscard]] - constexpr inline BitBoardIterator Iterator() const - { - return BitBoardIterator(InternalContainer); - } - - constexpr inline MoveList Mask(const BitBoard mask) const - { - MoveList result = *this; - result.InternalContainer &= mask; - return result; - } - - private: - constexpr inline void Pawn (const Board& board, const Square sq , - const PinBitBoard& pin , const CheckBitBoard& check) - { - if (Get(pin.Diagonal, sq)) { - const BitBoard pawnAttack = AttackTable::Pawn[Color][sq]; - const BitBoard enPassant = board.EnPassant() & pawnAttack; - const BitBoard normal = pawnAttack & board[Opposite(Color)]; - - InternalContainer |= (enPassant & pin.Diagonal) | (normal & pin.Diagonal & check.Check); - - if (enPassant) { - const Square epTarget = board.EnPassantSquare(); - const auto epPieceSq = static_cast( - Color == White ? - epTarget - 8 : - epTarget + 8 - ); - if (!EnPassantLegal(board, sq, epPieceSq, epTarget)) - InternalContainer &= ~enPassant; - } - - return; - } else if (Get(pin.Straight, sq)) { - const BitBoard sqBoard = FromSquare(sq); - - BitBoard pushes = (Color == White ? sqBoard << 8 : sqBoard >> 8 ) & board[NAC]; - if (pushes && - (sqBoard & (Color == White ? - BlackMagicFactory::Vertical[1] : - BlackMagicFactory::Vertical[6]))) - pushes |= (Color == White ? sqBoard << 16 : sqBoard >> 16) & board[NAC]; - - InternalContainer |= pushes & pin.Straight & check.Check; - - return; - } + constexpr static bool Promotion(const Square sq) + { + if (Piece != Piece::Pawn) return false; + + return (Color == White && sq > H6) || (Color == Black && sq < A3); + } + + MoveList(const Board& board, const Square sq, const PinBitBoard& pin, const CheckBitBoard& check) + { + InternalContainer = BBDefault; + + if (Piece == ::Pawn ) Pawn (board, pin, check, sq); + if (Piece == ::Knight) Knight (board, pin, check, sq); + if (Piece == ::Bishop) Bishop (board, pin, check, sq); + if (Piece == ::Rook ) Rook (board, pin, check, sq); + if (Piece == ::Queen ) Queen (board, pin, check, sq); + if (Piece == ::King ) King (board, sq); + } + + [[nodiscard]] + uint8_t Count() const + { + return ::Count(InternalContainer); + } + + [[nodiscard]] + BitBoardIterator Iterator() const + { + return BitBoardIterator(InternalContainer); + } + + MoveList Mask(const BitBoard mask) const + { + MoveList result = *this; + result.InternalContainer &= mask; + return result; + } + private: + [[clang::always_inline]] + void Pawn (const Board& board, const PinBitBoard& pin, const CheckBitBoard& check, const Square sq) + { + if (Get(pin.Diagonal, sq)) { const BitBoard pawnAttack = AttackTable::Pawn[Color][sq]; + const BitBoard enPassant = board.EnPassant() & pawnAttack; const BitBoard normal = pawnAttack & board[Opposite(Color)]; - InternalContainer |= normal; - - const BitBoard sqBoard = FromSquare(sq); - - BitBoard pushes = (Color == White ? sqBoard << 8 : sqBoard >> 8 ) & board[NAC]; - if (pushes && - (sqBoard & (Color == White ? - BlackMagicFactory::Vertical[1] : - BlackMagicFactory::Vertical[6]))) - pushes |= (Color == White ? sqBoard << 16 : sqBoard >> 16) & board[NAC]; - - InternalContainer |= pushes; - - InternalContainer &= check.Check; - - const BitBoard enPassant = board.EnPassant() & pawnAttack; - - InternalContainer |= enPassant; + InternalContainer |= enPassant & pin.Diagonal | normal & pin.Diagonal & check.Check; if (enPassant) { const Square epTarget = board.EnPassantSquare(); const auto epPieceSq = static_cast( - Color == White ? - epTarget - 8 : - epTarget + 8 + Color == White ? epTarget - 8 : epTarget + 8 ); if (!EnPassantLegal(board, sq, epPieceSq, epTarget)) InternalContainer &= ~enPassant; } - } - - constexpr inline void Knight(const Board& board, const Square sq , - const PinBitBoard& pin , const CheckBitBoard& check) - { - if (Get(pin.Straight | pin.Diagonal, sq)) return; - - InternalContainer |= AttackTable::Knight[sq] & ~board[Color] & check.Check; - } - - constexpr inline void Bishop(const Board& board, const Square sq , - const PinBitBoard& pin , const CheckBitBoard& check) - { - if (Get(pin.Straight, sq)) return; - - const uint32_t idx = BlackMagicFactory::MagicIndex(Piece, sq, ~board[NAC]); - InternalContainer |= AttackTable::Sliding[idx] & ~board[Color] & check.Check; - if (Get(pin.Diagonal, sq)) InternalContainer &= pin.Diagonal; + return; } - constexpr inline void Rook (const Board& board, const Square sq , - const PinBitBoard& pin , const CheckBitBoard& check) - { - if (Get(pin.Diagonal, sq)) return; - - const uint32_t idx = BlackMagicFactory::MagicIndex(Piece, sq, ~board[NAC]); - InternalContainer |= AttackTable::Sliding[idx] & ~board[Color] & check.Check; - - if (Get(pin.Straight, sq)) InternalContainer &= pin.Straight; - } - - constexpr inline void Queen (const Board& board, const Square sq , - const PinBitBoard& pin , const CheckBitBoard& check) - { - const bool straight = Get(pin.Straight, sq); - const bool diagonal = Get(pin.Diagonal, sq); - - if (straight && diagonal) return; - - if (straight) { - const uint32_t idx = - BlackMagicFactory::MagicIndex(Piece::Rook , sq, ~board[NAC]); - InternalContainer |= AttackTable::Sliding[idx ] & ~board[Color] & check.Check & pin.Straight; + if (Get(pin.Straight, sq)) { + const BitBoard sqBoard = FromSquare(sq); - } else if (diagonal) { - const uint32_t idx = - BlackMagicFactory::MagicIndex(Piece::Bishop, sq, ~board[NAC]); - InternalContainer |= AttackTable::Sliding[idx ] & ~board[Color] & check.Check & pin.Diagonal; + BitBoard pushes = (Color == White ? sqBoard << 8 : sqBoard >> 8) & board[NAC]; + if (pushes && + sqBoard & (Color == White ? RayTable::Vertical[1] : RayTable::Vertical[6])) + pushes |= (Color == White ? sqBoard << 16 : sqBoard >> 16) & board[NAC]; - } else { - const uint32_t idxR = - BlackMagicFactory::MagicIndex(Piece::Rook , sq, ~board[NAC]); - InternalContainer |= AttackTable::Sliding[idxR] & ~board[Color] & check.Check; + InternalContainer |= pushes & pin.Straight & check.Check; - const uint32_t idxB = - BlackMagicFactory::MagicIndex(Piece::Bishop, sq, ~board[NAC]); - InternalContainer |= AttackTable::Sliding[idxB] & ~board[Color] & check.Check; - } + return; } - constexpr inline void King (const Board& board, const Square sq ) - { - BitBoard king = AttackTable::King[sq] & ~board[Color]; + const BitBoard pawnAttack = AttackTable::Pawn[Color][sq]; + const BitBoard normal = pawnAttack & board[Opposite(Color)]; - if (!king) return; + InternalContainer |= normal; - auto iterator = BitBoardIterator(king); - for (Square target = iterator.Value(); target != NASQ; target = iterator.Value()) { - if (!KingMoveLegal(board, target)) Set(king, target); - } + const BitBoard sqBoard = FromSquare(sq); - InternalContainer |= king; + BitBoard pushes = (Color == White ? sqBoard << 8 : sqBoard >> 8) & board[NAC]; + if (pushes && + sqBoard & (Color == White ? RayTable::Vertical[1] : RayTable::Vertical[6])) + pushes |= (Color == White ? sqBoard << 16 : sqBoard >> 16) & board[NAC]; - if (!KingMoveLegal(board, sq)) return; + InternalContainer |= pushes; - const bool kingSide = board.CastlingRightK(); - const bool queenSide = board.CastlingRightQ(); + InternalContainer &= check.Check; - if (queenSide && - Get(king, static_cast(sq - 1)) && - KingMoveLegal(board, static_cast(sq - 2))) { - const BitBoard path = Color == White ? WhiteQueenCastlePath : BlackQueenCastlePath; + const BitBoard enPassant = board.EnPassant() & pawnAttack; - if (!(path & ~board[NAC])) InternalContainer |= path & QueenCastlePathMask; - } + InternalContainer |= enPassant; - if (kingSide && - Get(king, static_cast(sq + 1)) && - KingMoveLegal(board, static_cast(sq + 2))) { - const BitBoard path = Color == White ? WhiteKingCastlePath : BlackKingCastlePath; - - if (!(path & ~board[NAC])) InternalContainer |= path; - } + if (enPassant) { + const Square epTarget = board.EnPassantSquare(); + const auto epPieceSq = static_cast( + Color == White ? epTarget - 8 : epTarget + 8 + ); + if (!EnPassantLegal(board, sq, epPieceSq, epTarget)) + InternalContainer &= ~enPassant; } + } + + [[clang::always_inline]] + void Knight(const Board& board, const PinBitBoard& pin, const CheckBitBoard& check, const Square sq) + { + if (Get(pin.Straight | pin.Diagonal, sq)) return; + + InternalContainer |= AttackTable::Knight[sq] & ~board[Color] & check.Check; + } + + [[clang::always_inline]] + void Bishop(const Board& board, const PinBitBoard& pin, const CheckBitBoard& check, const Square sq) + { + if (Get(pin.Straight, sq)) return; + + const uint32_t idx = BlackMagicFactory::MagicIndex(Piece, sq, ~board[NAC]); + InternalContainer |= AttackTable::Sliding[idx] & ~board[Color] & check.Check; + + if (Get(pin.Diagonal, sq)) InternalContainer &= pin.Diagonal; + } + + [[clang::always_inline]] + void Rook (const Board& board, const PinBitBoard& pin, const CheckBitBoard& check, const Square sq) + { + if (Get(pin.Diagonal, sq)) return; + + const uint32_t idx = BlackMagicFactory::MagicIndex(Piece, sq, ~board[NAC]); + InternalContainer |= AttackTable::Sliding[idx] & ~board[Color] & check.Check; + + if (Get(pin.Straight, sq)) InternalContainer &= pin.Straight; + } + + [[clang::always_inline]] + void Queen (const Board& board, const PinBitBoard& pin, const CheckBitBoard& check, const Square sq) + { + const bool straight = Get(pin.Straight, sq); + const bool diagonal = Get(pin.Diagonal, sq); + + if (straight && diagonal) return; + + if (straight) { + const uint32_t idx = BlackMagicFactory::MagicIndex(Piece::Rook , sq, ~board[NAC]); + InternalContainer |= AttackTable::Sliding[idx] & ~board[Color] & check.Check & pin.Straight; + } else if (diagonal) { + const uint32_t idx = BlackMagicFactory::MagicIndex(Piece::Bishop, sq, ~board[NAC]); + InternalContainer |= AttackTable::Sliding[idx] & ~board[Color] & check.Check & pin.Diagonal; + } else { + const uint32_t idxR = BlackMagicFactory::MagicIndex(Piece::Rook , sq, ~board[NAC]); + const uint32_t idxB = BlackMagicFactory::MagicIndex(Piece::Bishop, sq, ~board[NAC]); + + InternalContainer |= AttackTable::Sliding[idxR] & ~board[Color] & check.Check; + InternalContainer |= AttackTable::Sliding[idxB] & ~board[Color] & check.Check; + } + } - constexpr static inline bool KingMoveLegal (const Board& board, const Square target) { - constexpr enum Color by = Opposite(Color); - - if (AttackTable::Pawn[Color][target] & board.PieceBoard(Piece::Pawn, by)) - return false; - - if (AttackTable::Knight[target] & board.PieceBoard(Piece::Knight, by)) - return false; - - const BitBoard occupied = ~board[Color::NAC] & ~board.PieceBoard(Piece::King, Color); - - const BitBoard queen = board.PieceBoard(Piece::Queen, by); - - if (AttackTable::Sliding[BlackMagicFactory::MagicIndex(Piece::Bishop, target, occupied)] & - (queen | board.PieceBoard(Piece::Bishop, by))) return false; + [[clang::always_inline]] + void King (const Board& board, const Square sq) + { + BitBoard king = AttackTable::King[sq] & ~board[Color]; - if (AttackTable::Sliding[BlackMagicFactory::MagicIndex(Piece::Rook , target, occupied)] & - (queen | board.PieceBoard(Piece::Rook , by))) return false; + if (!king) return; - return !(AttackTable::King[target] & board.PieceBoard(Piece::King, by)); + auto iterator = BitBoardIterator(king); + for (Square target = iterator.Value(); target != NASQ; target = iterator.Value()) { + if (!KingMoveLegal(board, target)) Set(king, target); } - constexpr static inline bool EnPassantLegal(const Board& board, - const Square ourPawn, - const Square opponentPawn, - const Square enPassantTarget) - { - BitBoard occupied = ~board[Color::NAC]; - Set(occupied, ourPawn); - Set(occupied, opponentPawn); - Set(occupied, enPassantTarget); - - const BitBoard queen = board.PieceBoard(Piece::Queen, Opposite(Color)); - const Square king = ToSquare(board.PieceBoard(Piece::King , Color)); - - return !((AttackTable::Sliding[BlackMagicFactory::MagicIndex(Piece::Rook , king, occupied)] & - (queen | board.PieceBoard(Piece::Rook , Opposite(Color)))) || - (AttackTable::Sliding[BlackMagicFactory::MagicIndex(Piece::Bishop, king, occupied)] & - (queen | board.PieceBoard(Piece::Bishop, Opposite(Color))))); - } + InternalContainer |= king; + + if (!KingMoveLegal(board, sq)) return; + + const bool kingSide = board.CastlingRightK(), + queenSide = board.CastlingRightQ(); + + if (queenSide && + Get(king, static_cast(sq - 1)) && + KingMoveLegal(board, static_cast(sq - 2))) + if (const BitBoard path = Color == White ? WhiteQueenCastlePath : BlackQueenCastlePath; + !(path & ~board[NAC])) + InternalContainer |= path & QueenCastlePathMask; + + if (kingSide && + Get(king, static_cast(sq + 1)) && + KingMoveLegal(board, static_cast(sq + 2))) + if (const BitBoard path = Color == White ? WhiteKingCastlePath : BlackKingCastlePath; + !(path & ~board[NAC])) + InternalContainer |= path; + } + + [[clang::always_inline]] + static bool KingMoveLegal(const Board& board, const Square target) + { + constexpr auto by = Opposite(Color); + + if (AttackTable::Pawn[Color][target] & board.PieceBoard(Piece::Pawn, by)) + return false; + + if (AttackTable::Knight[target] & board.PieceBoard(Piece::Knight, by)) + return false; + + const BitBoard occupied = ~board[NAC] & ~board.PieceBoard(Piece::King, Color); + + const BitBoard queen = board.PieceBoard(Piece::Queen, by); + + if (AttackTable::Sliding[BlackMagicFactory::MagicIndex(Piece::Bishop, target, occupied)] & + (queen | board.PieceBoard(Piece::Bishop, by))) + return false; + + if (AttackTable::Sliding[BlackMagicFactory::MagicIndex(Piece::Rook , target, occupied)] & + (queen | board.PieceBoard(Piece::Rook , by))) + return false; + + return !(AttackTable::King[target] & board.PieceBoard(Piece::King, by)); + } + + [[clang::always_inline]] + static bool EnPassantLegal(const Board& board, + const Square x, // the square that is moving + const Square y, // the square that is being captured + const Square z) // the square that is being moved to + { + BitBoard occupied = ~board[NAC]; + Set(occupied, x); + Set(occupied, y); + Set(occupied, z); + + const BitBoard queen = board.PieceBoard(Piece::Queen, Opposite(Color)); + const Square king = ToSquare(board.PieceBoard(Piece::King, Color)); + + return !(AttackTable::Sliding[BlackMagicFactory::MagicIndex(Piece::Rook , king, occupied)] & + (queen | board.PieceBoard(Piece::Rook, Opposite(Color))) || + AttackTable::Sliding[BlackMagicFactory::MagicIndex(Piece::Bishop, king, occupied)] & + (queen | board.PieceBoard(Piece::Bishop, Opposite(Color)))); + } }; diff --git a/src/Backend/Move/RayTable.h b/src/Backend/Move/RayTable.h new file mode 100644 index 00000000..fc936f64 --- /dev/null +++ b/src/Backend/Move/RayTable.h @@ -0,0 +1,30 @@ +// +// Copyright (c) 2023 StockDory authors. See the list of authors for more details. +// Licensed under LGPL-3.0. +// + +#ifndef STOCKDORY_RAYTABLE_H +#define STOCKDORY_RAYTABLE_H + +#include "../Type/BitBoard.h" + +namespace StockDory::RayTable +{ + + constexpr std::array Horizontal { + 0x0101010101010101, 0x0202020202020202, 0x0404040404040404, 0x0808080808080808, + 0x1010101010101010, 0x2020202020202020, 0x4040404040404040, 0x8080808080808080 + }; + + constexpr std::array Vertical { + 0x00000000000000FF, 0x000000000000FF00, 0x0000000000FF0000, 0x00000000FF000000, + 0x000000FF00000000, 0x0000FF0000000000, 0x00FF000000000000, 0xFF00000000000000 + }; + + constexpr BitBoard Edged = Horizontal[0] | Horizontal[7] | Vertical[0] | Vertical[7]; + + extern std::array, 64> Between; + +} // StockDory + +#endif //STOCKDORY_RAYTABLE_H diff --git a/src/Backend/Move/UtilityTable.h b/src/Backend/Move/UtilityTable.h deleted file mode 100644 index 28dac985..00000000 --- a/src/Backend/Move/UtilityTable.h +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright (c) 2023 StockDory authors. See the list of authors for more details. -// Licensed under LGPL-3.0. -// - -#ifndef STOCKDORY_UTILITYTABLE_H -#define STOCKDORY_UTILITYTABLE_H - -#include "../Type/BitBoard.h" -#include "BlackMagicFactory.h" -#include "AttackTable.h" - -namespace StockDory -{ - - class UtilityTable - { - - public: - static std::array, 64> Between; - - }; - -} // StockDory - -std::array, 64> StockDory::UtilityTable::Between = []() { - std::array, 64> temp = {}; - - for (Square f = A1; f != NASQ; f = Next(f)) { - const uint8_t fH = f % 8; - const uint8_t fV = f / 8; - - for (Square t = A1; t != NASQ; t = Next(t)) { - temp[f][t] = BBDefault; - - if (f == t) continue; - - const uint8_t tH = t % 8; - const uint8_t tV = t / 8; - - BitBoard occ; - - if (fH == tH || fV == tV) { - occ = FromSquare(f) | FromSquare(t); - - const uint32_t mF = BlackMagicFactory::MagicIndex(Rook, f, occ); - const uint32_t mT = BlackMagicFactory::MagicIndex(Rook, t, occ); - - // Rook squares: - temp[f][t] = AttackTable::Sliding[mF] & AttackTable::Sliding[mT]; - - continue; - } - - auto absH = static_cast(static_cast(fH) - static_cast(tH)); - auto absV = static_cast(static_cast(fV) - static_cast(tV)); - if (absH < 0) absH = static_cast(-absH); - if (absV < 0) absV = static_cast(-absV); - - if (absH != absV) continue; - - // Bishop squares: - occ = FromSquare(f) | FromSquare(t); - - const uint32_t mF = BlackMagicFactory::MagicIndex(Bishop, f, occ); - const uint32_t mT = BlackMagicFactory::MagicIndex(Bishop, t, occ); - - temp[f][t] = AttackTable::Sliding[mF] & AttackTable::Sliding[mT]; - } - } - - return temp; -}(); - -#endif //STOCKDORY_UTILITYTABLE_H diff --git a/src/Backend/Template/MoveType.h b/src/Backend/Template/MoveType.h index 7cfbde02..0af6394f 100644 --- a/src/Backend/Template/MoveType.h +++ b/src/Backend/Template/MoveType.h @@ -3,8 +3,6 @@ // Licensed under LGPL-3.0. // -#pragma clang diagnostic push -#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" #ifndef STOCKDORY_MOVETYPE_H #define STOCKDORY_MOVETYPE_H @@ -16,6 +14,4 @@ constexpr MoveType PERFT = 0x00000F00; constexpr MoveType NNUE = 0x0000F000; -#endif //STOCKDORY_MOVETYPE_H - -#pragma clang diagnostic pop \ No newline at end of file +#endif //STOCKDORY_MOVETYPE_H \ No newline at end of file diff --git a/src/Backend/ThreadPool.h b/src/Backend/ThreadPool.h index 05b67d64..fc136f20 100644 --- a/src/Backend/ThreadPool.h +++ b/src/Backend/ThreadPool.h @@ -6,12 +6,56 @@ #ifndef STOCKDORY_THREADPOOL_H #define STOCKDORY_THREADPOOL_H -#include "../External/thread_pool.hpp" +#include + +class ThreadPool +{ + + public: + static size_t HardwareLimit() + { + return core_count(); + } + + private: + Pool* Internal = nullptr; + + public: + ThreadPool(const size_t n) { Internal = pool_create(n); } + + ~ThreadPool() { if (Internal != nullptr) pool_destroy(Internal); } + + size_t Size() const { return pool_size(Internal); } + + void Resize(const size_t n) + { + if (Internal != nullptr) pool_destroy(Internal); + Internal = pool_create(n); + } + + template + void Execute(F&& code) + { + Task* task = drjit::do_async(code, {}, Internal); + task_release(task); + } + + template + void For(const drjit::blocked_range& range, F&& code) + { + drjit::parallel_for( + range, + std::move(code), + Internal + ); + } + +}; namespace StockDory { - BS::thread_pool ThreadPool; + inline ThreadPool ThreadPool (1); } // StockDory diff --git a/src/Backend/TranspositionTable.h b/src/Backend/TranspositionTable.h deleted file mode 100644 index 55c07493..00000000 --- a/src/Backend/TranspositionTable.h +++ /dev/null @@ -1,81 +0,0 @@ -// -// Copyright (c) 2023 StockDory authors. See the list of authors for more details. -// Licensed under LGPL-3.0. -// - -#ifndef STOCKDORY_TRANSPOSITIONTABLE_H -#define STOCKDORY_TRANSPOSITIONTABLE_H - -#include -#include -#include - -#ifdef __x86_64__ -#include -#endif - -#include "Type/Zobrist.h" - -#include "../External/fastrange.h" - -namespace StockDory -{ - - template - class TranspositionTable - { - - private: - std::vector Internal; - uint64_t Count = 0; - - public: - explicit TranspositionTable(const uint64_t bytes) - { - Resize(bytes); - } - - void Resize(const uint64_t bytes) - { - Count = bytes / sizeof(T); - - Clear(); - } - - void Clear() - { - Internal = std::vector(Count); - } - - inline T& operator [](const ZobristHash hash) - { - return Internal[fastrange64(hash, Count)]; - } - - inline const T& operator [](const ZobristHash hash) const - { - return Internal[fastrange64(hash, Count)]; - } - - inline void Prefetch(const ZobristHash hash) const - { -#ifdef __x86_64__ - _mm_prefetch(reinterpret_cast(&Internal[fastrange64(hash, Count)]), _MM_HINT_T0); -#else -#ifdef __aarch64__ - __builtin_prefetch(reinterpret_cast(&Internal[fastrange64(hash, Count)]), 0, 3); -#endif -#endif - } - - [[nodiscard]] - inline uint64_t Size() const - { - return Internal.size(); - } - - }; - -} // StockDory - -#endif //STOCKDORY_TRANSPOSITIONTABLE_H diff --git a/src/Backend/Type/BitBoard.h b/src/Backend/Type/BitBoard.h index 8f811f83..7c771703 100644 --- a/src/Backend/Type/BitBoard.h +++ b/src/Backend/Type/BitBoard.h @@ -6,10 +6,8 @@ #ifndef STOCKDORY_BITBOARD_H #define STOCKDORY_BITBOARD_H -#include #include - -#include +#include #include "Square.h" @@ -18,29 +16,29 @@ using BitBoard = uint64_t; constexpr BitBoard BBDefault = 0x0000000000000000ULL; constexpr BitBoard BBFilled = 0xFFFFFFFFFFFFFFFFULL; -constexpr inline uint8_t Count(const BitBoard bb) +constexpr uint8_t Count(const BitBoard bb) { return std::popcount(bb); } template -constexpr inline void Set(BitBoard& bb, const Square sq) +constexpr void Set(BitBoard& bb, const Square sq) { if (Activate) bb |= 1ULL << sq ; else bb &= ~(1ULL << sq); } -constexpr inline bool Get(const BitBoard bb, const Square sq) +constexpr bool Get(const BitBoard bb, const Square sq) { return bb >> sq & 1ULL; } -constexpr inline BitBoard FromSquare(const Square sq) +constexpr BitBoard FromSquare(const Square sq) { return 1ULL << sq; } -constexpr inline Square ToSquare(const BitBoard bb) +constexpr Square ToSquare(const BitBoard bb) { return static_cast(std::countr_zero(bb)); } @@ -48,49 +46,49 @@ constexpr inline Square ToSquare(const BitBoard bb) class BitBoardIterator { - private: - BitBoard BB; + BitBoard BB; public: - constexpr explicit BitBoardIterator(BitBoard value) - { - BB = value; - } + constexpr BitBoardIterator(const BitBoard value) + { + BB = value; + } - constexpr inline Square Value() - { - uint8_t i = std::countr_zero(BB); + constexpr Square Value() + { + uint8_t i = std::countr_zero(BB); - // Subtract 1 and only hold set bits in the container. - BB &= BB - 1ULL; + // Subtract 1 and only hold set bits in the container. + BB &= BB - 1ULL; - return static_cast(i); - } + return static_cast(i); + } - template - inline uint8_t ToArray(std::array& array) - { - const uint8_t count = Count(BB); + template + uint8_t ToArray(std::array& array) + { + const uint8_t count = Count(BB); - assert(N >= count); + assert(N >= count); - for (uint8_t i = 0; i < count; i++) array[i] = Value(); + for (uint8_t i = 0; i < count; i++) array[i] = Value(); - return count; - } + return count; + } }; -constexpr inline BitBoardIterator Iterator(const BitBoard bb) +constexpr BitBoardIterator Iterator(const BitBoard bb) { return BitBoardIterator(bb); } -inline std::string ToString(const BitBoard bb) +std::string ToString(const BitBoard bb) { std::string s; - for (uint8_t v = 7; v != 255; v--) { // Using overflow-wrap to our advantage. + for (uint8_t v = 7; v != 255; v--) { + // Using overflow-wrap to our advantage. for (uint8_t h = 0; h < 8; h++) { s += Get(bb, static_cast(v * 8 + h)) ? '1' : '0'; if (h != 7) s += " "; diff --git a/src/Backend/Type/CheckBitBoard.h b/src/Backend/Type/CheckBitBoard.h index 1c683bde..ac76ec42 100644 --- a/src/Backend/Type/CheckBitBoard.h +++ b/src/Backend/Type/CheckBitBoard.h @@ -11,9 +11,8 @@ struct CheckBitBoard { - public: - BitBoard Check = BBDefault; - bool DoubleCheck = false ; + BitBoard Check = BBDefault; + bool DoubleCheck = false; }; diff --git a/src/Backend/Type/Color.h b/src/Backend/Type/Color.h index f2f2f580..8906b6c3 100644 --- a/src/Backend/Type/Color.h +++ b/src/Backend/Type/Color.h @@ -18,12 +18,12 @@ enum Color : uint8_t }; -inline constexpr Color Next(const Color c) +constexpr Color Next(const Color c) { - return static_cast(static_cast(c) + 1); + return static_cast(static_cast(c) + 1); } -inline constexpr Color Opposite(const Color c) +constexpr Color Opposite(const Color c) { return static_cast(static_cast(c) ^ 0x1); } @@ -31,10 +31,10 @@ inline constexpr Color Opposite(const Color c) std::map C_STRING = { {White, "White"}, {Black, "Black"}, - {NAC, "NAC"} + {NAC , "NAC" } }; -inline std::string ToString(const Color c) +std::string ToString(const Color c) { return C_STRING[c]; } diff --git a/src/Backend/Type/Move.h b/src/Backend/Type/Move.h index 2a9580af..8b29430c 100644 --- a/src/Backend/Type/Move.h +++ b/src/Backend/Type/Move.h @@ -8,98 +8,99 @@ #include -#include "Square.h" #include "Piece.h" -#include "../Util.h" +#include "Square.h" struct Move { private: - // [ PROMOTION ] [ TO ] [ FROM ] - // [ 4 BITS ] [ 6 BITS ] [ 6 BITS ] - uint16_t Internal; + constexpr static uint16_t SquareMask = 0x003F; + constexpr static uint8_t ToPos = 6; + constexpr static uint8_t PromotionPos = 12; - constexpr static uint16_t SquareMask = 0x003F; + // [ PROMOTION ] [ TO ] [ FROM ] + // [ 4 BITS ] [ 6 BITS ] [ 6 BITS ] + uint16_t Internal; public: - static inline Move FromString(const std::string& str) - { - Square from = StockDory::Util::StringToSquare(str.substr(0, 2)); - Square to = StockDory::Util::StringToSquare(str.substr(2, 2)); - - Piece promotion = NAP; - - if (str.size() == 5) { - switch (str[4]) { - case 'q': - promotion = Queen ; - break; - case 'r': - promotion = Rook ; - break; - case 'b': - promotion = Bishop; - break; - case 'n': - promotion = Knight; - break; - } + static Move FromString(const std::string& str) + { + const Square from = ::FromString(str.substr(0, 2)); + const Square to = ::FromString(str.substr(2, 2)); + + Piece promotion = NAP; + + if (str.size() == 5) { + switch (str[4]) { + case 'q': + promotion = Queen; + break; + case 'r': + promotion = Rook; + break; + case 'b': + promotion = Bishop; + break; + case 'n': + promotion = Knight; + break; + default: ; } - - return Move(from, to, promotion); - } - - constexpr Move() - { - Internal = 0; - } - - constexpr explicit Move(const Square from, const Square to, const Piece promotion = NAP) - { - Internal = from | (to << 6) | (promotion << 12); - } - - [[nodiscard]] - constexpr inline Square From() const - { - return static_cast(Internal & SquareMask); - } - - [[nodiscard]] - constexpr inline Square To() const - { - return static_cast((Internal >> 6) & SquareMask); - } - - [[nodiscard]] - constexpr inline Piece Promotion() const - { - return static_cast(Internal >> 12); - } - - [[nodiscard]] - constexpr inline bool operator==(const Move other) const - { - return Internal == other.Internal; } - [[nodiscard]] - inline std::string ToString() const - { - std::stringstream s; - - Square from = From (); - Square to = To (); - Piece promotion = Promotion(); - - s << StockDory::Util::SquareToString(from) << StockDory::Util::SquareToString(to); - - if (promotion != NAP) - s << static_cast(tolower(FirstLetter(promotion))); - - return s.str(); - } + return Move(from, to, promotion); + } + + constexpr Move() + { + Internal = 0; + } + + constexpr Move(const Square from, const Square to, const Piece promotion = NAP) + { + Internal = from | to << ToPos | promotion << PromotionPos; + } + + [[nodiscard]] + constexpr Square From() const + { + return static_cast(Internal & SquareMask); + } + + [[nodiscard]] + constexpr Square To() const + { + return static_cast(Internal >> ToPos & SquareMask); + } + + [[nodiscard]] + constexpr Piece Promotion() const + { + return static_cast(Internal >> PromotionPos); + } + + [[nodiscard]] + constexpr bool operator==(const Move other) const + { + return Internal == other.Internal; + } + + [[nodiscard]] + std::string ToString() const + { + std::stringstream s; + + const Square from = From(); + const Square to = To(); + const Piece promotion = Promotion(); + + s << ::ToString(from) << ::ToString(to); + + if (promotion != NAP) s << static_cast(tolower(FirstLetter(promotion))); + + return s.str(); + } }; diff --git a/src/Backend/Type/Piece.h b/src/Backend/Type/Piece.h index 78ccfaf1..50eee2cf 100644 --- a/src/Backend/Type/Piece.h +++ b/src/Backend/Type/Piece.h @@ -3,8 +3,6 @@ // Licensed under MIT. // -#pragma clang diagnostic push -#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" #ifndef STOCKDORY_PIECE_H #define STOCKDORY_PIECE_H @@ -24,12 +22,12 @@ enum Piece : uint8_t }; -inline constexpr Piece Next(const Piece p) +constexpr Piece Next(const Piece p) { return static_cast(static_cast(p) + 1); } -constexpr std::array P_CHAR = { +constexpr std::array P_CHAR = { 'P', 'N', 'B', @@ -39,26 +37,24 @@ constexpr std::array P_CHAR = { ' ' }; -constexpr inline char FirstLetter(const Piece p) +constexpr char FirstLetter(const Piece p) { return P_CHAR[p]; } std::map P_STRING = { - {Pawn, "Pawn"}, + {Pawn , "Pawn" }, {Knight, "Knight"}, {Bishop, "Bishop"}, - {Rook, "Rook"}, - {Queen, "Queen"}, - {King, "King"}, - {NAP, "NAP"} + {Rook , "Rook" }, + {Queen , "Queen" }, + {King , "King" }, + {NAP , "NAP" } }; -inline std::string ToString(const Piece p) +std::string ToString(const Piece p) { return P_STRING[p]; } #endif //STOCKDORY_PIECE_H - -#pragma clang diagnostic pop \ No newline at end of file diff --git a/src/Backend/Type/PieceColor.h b/src/Backend/Type/PieceColor.h index 0e1ff4e7..7ad46873 100644 --- a/src/Backend/Type/PieceColor.h +++ b/src/Backend/Type/PieceColor.h @@ -8,49 +8,51 @@ #include -#include "Piece.h" #include "Color.h" +#include "Piece.h" struct PieceColor { private: - // [ COLOR ] [ PIECE ] - // [ 4 BITS ] [ 4 BITS ] - uint8_t Internal; + constexpr static uint8_t PieceColorMask = 0xF; + constexpr static uint8_t ColorPos = 4; - constexpr static uint8_t PieceColorMask = 0x0F; + // [ COLOR ] [ PIECE ] + // [ 4 BITS ] [ 4 BITS ] + uint8_t Internal; public: - constexpr PieceColor() { - Internal = 0; - } - - constexpr explicit PieceColor(const Piece piece, const Color color) - { - Internal = piece | (color << 4); - } - - [[nodiscard]] - constexpr inline Piece Piece() const - { - return static_cast(Internal & PieceColorMask); - } - - [[nodiscard]] - constexpr inline Color Color() const - { - return static_cast(Internal >> 4); - } - - [[nodiscard]] - inline std::string ToString() const - { - enum Piece p = Piece(); - enum Color c = Color(); - - return ::ToString(c) + std::string(" ") + ::ToString(p); - } + constexpr PieceColor() + { + Internal = 0; + } + + constexpr PieceColor(const Piece piece, const Color color) + { + Internal = piece | color << 4; + } + + [[nodiscard]] + constexpr Piece Piece() const + { + return static_cast(Internal & PieceColorMask); + } + + [[nodiscard]] + constexpr Color Color() const + { + return static_cast(Internal >> ColorPos); + } + + [[nodiscard]] + std::string ToString() const + { + const enum Piece p = Piece(); + const enum Color c = Color(); + + return ::ToString(c) + std::string(" ") + ::ToString(p); + } }; diff --git a/src/Backend/Type/PinBitBoard.h b/src/Backend/Type/PinBitBoard.h index 52d5178c..04f328c3 100644 --- a/src/Backend/Type/PinBitBoard.h +++ b/src/Backend/Type/PinBitBoard.h @@ -11,9 +11,8 @@ struct PinBitBoard { - public: - BitBoard Straight = BBDefault; - BitBoard Diagonal = BBDefault; + BitBoard Straight = BBDefault; + BitBoard Diagonal = BBDefault; }; diff --git a/src/Backend/Type/PreviousState.h b/src/Backend/Type/PreviousState.h index bb7d9696..507f0945 100644 --- a/src/Backend/Type/PreviousState.h +++ b/src/Backend/Type/PreviousState.h @@ -1,5 +1,3 @@ -#pragma clang diagnostic push -#pragma ide diagnostic ignored "cppcoreguidelines-pro-type-member-init" // // Copyright (c) 2023 StockDory authors. See the list of authors for more details. // Licensed under LGPL-3.0. @@ -15,50 +13,46 @@ struct PreviousState { - public: - PieceColor MovedPiece ; - PieceColor CapturedPiece ; - Piece PromotedPiece ; - bool EnPassantCapture ; - Square EnPassant ; - Square CastlingFrom ; - Square CastlingTo ; - uint8_t CastlingRightAndColorToMove; - - ZobristHash Hash; - - constexpr PreviousState(const PieceColor movedPiece , const PieceColor capturedPiece , - const Square enPassant, const uint8_t castlingRightAndColorToMove, - const ZobristHash hash) - { - MovedPiece = movedPiece; - CapturedPiece = capturedPiece; - EnPassant = enPassant; - CastlingRightAndColorToMove = castlingRightAndColorToMove; - - EnPassantCapture = false; - PromotedPiece = NAP; - CastlingFrom = NASQ; - CastlingTo = NASQ; - - Hash = hash; - } + PieceColor MovedPiece ; + PieceColor CapturedPiece ; + Piece PromotedPiece ; + bool EnPassantCapture ; + Square EnPassant ; + Square CastlingFrom ; + Square CastlingTo ; + uint8_t CastlingRightAndColorToMove; + + ZobristHash Hash; + + constexpr PreviousState(const PieceColor movedPiece, const PieceColor capturedPiece, + const Square enPassant, const uint8_t castlingRightAndColorToMove, + const ZobristHash hash) + { + MovedPiece = movedPiece; + CapturedPiece = capturedPiece; + EnPassant = enPassant; + CastlingRightAndColorToMove = castlingRightAndColorToMove; + + EnPassantCapture = false; + PromotedPiece = NAP; + CastlingFrom = NASQ; + CastlingTo = NASQ; + + Hash = hash; + } }; struct PreviousStateNull { - public: - Square EnPassant; + Square EnPassant; - constexpr PreviousStateNull(const Square enPassant) - { - EnPassant = enPassant; - } + constexpr PreviousStateNull(const Square enPassant) + { + EnPassant = enPassant; + } }; #endif //STOCKDORY_PREVIOUSSTATE_H - -#pragma clang diagnostic pop \ No newline at end of file diff --git a/src/Backend/Type/Square.h b/src/Backend/Type/Square.h index 6e3fafae..c0adf130 100644 --- a/src/Backend/Type/Square.h +++ b/src/Backend/Type/Square.h @@ -3,14 +3,12 @@ // Licensed under MIT. // -#pragma clang diagnostic push -#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" #ifndef STOCKDORY_SQUARE_H #define STOCKDORY_SQUARE_H -#include #include -#include +#include +#include enum Square : uint8_t { @@ -26,12 +24,12 @@ enum Square : uint8_t }; -inline constexpr Square Next(const Square sq) +constexpr Square Next(const Square sq) { return static_cast(static_cast(sq) + 1); } -constexpr std::array FILE_CHAR { +constexpr std::array FILE_CHAR { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', @@ -39,10 +37,10 @@ constexpr std::array FILE_CHAR { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', ' ' + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'X' }; -constexpr std::array RANK { +constexpr std::array RANK_CHAR { '1', '1', '1', '1', '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3', '3', @@ -50,19 +48,34 @@ constexpr std::array RANK { '5', '5', '5', '5', '5', '5', '5', '5', '6', '6', '6', '6', '6', '6', '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', - '8', '8', '8', '8', '8', '8', '8', '8', ' ' + '8', '8', '8', '8', '8', '8', '8', '8', '0' }; -inline constexpr char File(const Square sq) +constexpr char File(const Square sq) { return FILE_CHAR[sq]; } -inline constexpr char Rank(const Square sq) +constexpr char Rank(const Square sq) { - return RANK[sq]; + return RANK_CHAR[sq]; } -#endif //STOCKDORY_SQUARE_H +std::string ToString(const Square sq) +{ + std::stringstream ss; + ss << static_cast(tolower(FILE_CHAR[sq])); + ss << RANK_CHAR[sq]; + + return ss.str(); +} + +Square FromString(const std::string& s) +{ + const uint8_t file = tolower(s[0]) - 97; + const uint8_t rank = tolower(s[1]) - 49; + + return static_cast(rank * 8 + file); +} -#pragma clang diagnostic pop \ No newline at end of file +#endif //STOCKDORY_SQUARE_H \ No newline at end of file diff --git a/src/Backend/Type/Zobrist.h b/src/Backend/Type/Zobrist.h index dc48f563..78679925 100644 --- a/src/Backend/Type/Zobrist.h +++ b/src/Backend/Type/Zobrist.h @@ -3,16 +3,14 @@ // Licensed under LGPL-3.0. // -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-braces" #ifndef STOCKDORY_ZOBRIST_H #define STOCKDORY_ZOBRIST_H -#include #include +#include -#include "Piece.h" #include "Color.h" +#include "Piece.h" #include "Square.h" #include "../Template/MoveType.h" @@ -22,412 +20,402 @@ using ZobristHash = uint64_t; namespace StockDory::Zobrist { - class ZobristKeyTable - { - - public: - std::array, 7>, 3> PieceKey { - 0xA6236FC5F3D5CB40, 0xD08B197B0AE50DDC, 0x4C1C2B8202F856DC, 0xBC471B2026C87B34, - 0x97BBE5223FC13E95, 0xFA40BA2273CCB6EF, 0x15396251A0FEBEEC, 0xC085FCA64ABE89AE, - 0xF6AE4C573C7529C6, 0x7A29A85ECB12619B, 0x326AA61C623ACD3B, 0xF2F071279891AC04, - 0xA0A30345F9AD0B31, 0x05B66E50F0040710, 0x1FA322C2D80E435F, 0x2ACF15BAAA3842BE, - 0x373530FB529A1F70, 0x96E27AA215519A29, 0x0E301862E3388BF5, 0xFAE5F9CB033A8D67, - 0x2F0964D33080DA38, 0x437D8ED128AED70E, 0x58C8ECDC2EB4A15F, 0x306B32F56EEA8CEF, - 0x91C2B1FAC8DC56B4, 0xD93D08698AD40982, 0x4C1EB64429A3F70F, 0xDC3A9B7A47412711, - 0xAD49B5B27A737E6B, 0xE7EE3D17D3AC23DE, 0xFEEB428EFE5E114E, 0xE6177B70D2B69038, - 0x5A27C394EA1EF7CF, 0xD3344A09204AF6AD, 0x294E3B3C68F85A39, 0x633C76FF21417592, - 0x8B52041E1F04C50C, 0xEDF4CBF7985004EA, 0x93AAAE821699E25B, 0x375A05811E58F5A9, - 0x34B514464314E7B9, 0xC732B5EB387E6D07, 0x5EE026DD1CC55481, 0xE40A34AB2976711B, - 0x46A6B0B610DF19A9, 0xF3A7712F150E642D, 0x925A3E3F2D9D90C2, 0xD3898B9ED528B606, - 0xD01022D6D28E9A62, 0xA92ECA1778EE7EAD, 0x6BA4DD971A6782C9, 0xA2A8D18800094CBF, - 0xA01CD5E395B33BDB, 0x3B7AE14FC07FCC80, 0x853C6E241C0C7E87, 0xDC480F198A1323F5, - 0xF4C69817DC9D4CD6, 0xDD1BE45DCA9AC765, 0x6C7E8BC50978CDD2, 0x708792C1BFA02DFF, - 0xEF249E4402E9A383, 0x112771D45DB617B8, 0x4A163F974BB73507, 0xD23206F7EADC30CE, - 0x349919FAB947293F, 0x8D9821D5AD5189A9, 0x44D93BDA8CB88F05, 0xB1F739C114349E8C, - 0xEE166E2AAA1DE3E5, 0xAC7569ED0DA1839C, 0x04F7A54D68E44E97, 0x6F7285E45B2C0250, - 0x9763EE04F1C79F91, 0x2256CB9957F0CA78, 0x3F638F55727C8729, 0x1709BB41275A18EB, - 0xA9A883A24E7B3210, 0x846AA0B583EF4CEB, 0x021900085D310AAB, 0xBD563B93EB06873B, - 0xA58008506746D8C0, 0x4A89E09F0886D3DE, 0x100EAB89377BD6AD, 0xAF15BD7D1A127818, - 0xA333035ADCC232F6, 0x5B631179357E8B89, 0x15331FBFBD3DBBFD, 0x212865F6D74E36CA, - 0xAAF0AC1D6C12D7BD, 0x0796D82DFDADD8A2, 0xD7EF587FC21DB615, 0x23AC5CB42312ADFF, - 0x503EF6FEA2775D63, 0xF1E73571E61B93CA, 0x65A2664840C2479F, 0x45F5CC9A6F6D3AE7, - 0x1E08F461D09A8A2C, 0xC966083CECF7CC77, 0xFDDA6C996D8420C5, 0xFDB6CE89C4BA12BB, - 0xAB90179AE7BAD59E, 0xDE28B56E7CC8A43F, 0xC0AC87487AAAA3D4, 0xA99444394E159DAF, - 0xAE2E7E4E674EFA15, 0x058E5EA3F52ED88F, 0x16B43C24E5EFDBE9, 0xFB714102FD38FA29, - 0x5590C16774661F0F, 0x511DB6596FEAFB75, 0xD984AF545D650F0E, 0x4DAB3146533E3745, - 0x279D1AF783E53367, 0x72BC45512870994B, 0x60A613DCF2EEE7DD, 0x440F3C82302E8D9B, - 0xF45999D026089C50, 0x00885B3FBB123D72, 0x5371B9425F76CC7A, 0x0EA56DEB69E8858D, - 0x43A1963CB4D82B8A, 0x3F20EA009E80FA8D, 0x9602AFC6B6B52E05, 0x0508EFB04B99B42B, - 0xE4C3D38313310498, 0xA253D33847D2322A, 0xDA47EEB7BD64A965, 0x9D9356FD88894129, - 0x31BC0DB9FC057AF1, 0x585EBADD948BD06A, 0xAA4D4B8B07E6F761, 0xD4E63B4A41E956BC, - 0x57E857CDBE55349F, 0x6FFDB362E75232ED, 0xA6128B70062630E3, 0x2FE77A23633F5A2D, - 0x0A8A9F64A8F4B7EA, 0x094866F9581A8E0E, 0x1464E014195D7A58, 0x723484177390AF22, - 0x430A55BEABFC224B, 0xC7ACBA70E1F71CA6, 0x091BCE1DA80A3612, 0x89F2170EB585E052, - 0x034A9C69D60CA859, 0x67A382E8F7721970, 0x7C548C7AE160C902, 0x8F3454CFDE58F207, - 0x755DD6ADC8886D29, 0xCF21235B033706EC, 0xCC0F11B645B21E3D, 0x1C346B4476CE9053, - 0x2CBADBFA7ACDE2E4, 0xE421515474F4EA6C, 0x18D9EFA1B1A088D1, 0x8031E7071D74BB5E, - 0x61D5A655781E1BD2, 0x02789B1CCBD88663, 0xF47A83CABBA4EA6E, 0x85259C663E1CB509, - 0x2B8EA83239A174B9, 0xC3FB0D171B4D3FB3, 0x4EB575421CAE7143, 0xC6FA9789A06F2AA0, - 0xC6D03EF9A8125361, 0xFBDD5F358B08795B, 0x434208EEF4FB59B9, 0x1BA1EE39089852BF, - 0x04CA53FBC3B98644, 0x4119296DC9C1E8A1, 0x59D8059B30E6734C, 0x6D8DC7A6A6E8CE11, - 0x66001AA5AF47ECD7, 0x95D679460653F68F, 0x3E807108C5BB5639, 0xD95E236A2CB260AE, - 0x329C6AE180F2545A, 0x9812D2B2A5A17F73, 0x6665C70C4F6C52F8, 0x6BFCD3674400096C, - 0xCFB45587F2AB7998, 0xDB8BDACF885289BA, 0x93CD0EB717341A4E, 0x9FCA5769D8148BEE, - 0xA2101F27390C3683, 0x9196DC7582959D9E, 0x8AB4E49F4DF6257D, 0x6366DB41FB0318AB, - 0x2EB452CE9904B2BE, 0x121D4A791A789109, 0x9E007D60435B82F5, 0xC361151B0F8E8282, - 0x2BC3FCD8C2C43136, 0x6011996C0D44C871, 0x41032280BE05F1FA, 0xF312A5C44C1765B2, - 0xF8395B8647E3B42F, 0xE832F23898D22FBF, 0x1443A20F2A142D70, 0xCC6776E57210822E, - 0x9EDAC9B6B7DD50B5, 0xBF086A4F50FF0DB7, 0x8CE3E89096DB2F33, 0x4913C74B0318CE71, - 0xA6317A15E8E306C8, 0xC3C4F2A302727552, 0xCC3BD09AB53F5179, 0xF053D6B1E745516C, - 0xB8AA20874988B6F9, 0x7FE9E0761A4435FE, 0xF7AE5E88FA89550A, 0x7CF54935C37B16CA, - 0x583C7B69ED098122, 0x8C55218C65B786A6, 0x9B68594C5CDDC667, 0xB483E6A5707E9107, - 0x8E15FBD7F5F7C73F, 0xA86B48A601533118, 0xC92F996C6E5CEEC1, 0xC0B3CBFCD8E56E19, - 0x6BB46029C4250303, 0xBFF466051B434EB0, 0xA6481489B6AFD626, 0x4C923D1669DC2E98, - 0x617FA622A3FE927E, 0xB3C313561DBEF03A, 0xEE36FC1C26B22679, 0x73554B84FB2C7FBA, - 0x7F9C5167739DF6BC, 0x844F2B56997C4E56, 0x81306C8BEFEF296B, 0xEEDC847AA3E9105B, - 0x284B50765F604A9A, 0x8F979D7FCDCF328A, 0x50E8665B5B8FA72F, 0xD205F86757171BC5, - 0x1B0EF0CA0BFE8D71, 0xBE02629C5865C7DD, 0x349381F4D3735978, 0x91C0F4395A991062, - 0x3FC58C9EF15CFC2D, 0x63B38F00937D7498, 0xEFDE31850EDF5819, 0xE9E2B456B9A9C9FA, - 0xEC201006A02AD307, 0xD233FF037F3A3394, 0xFB0CB214E040365D, 0xE4E90BE3D4F76C1E, - 0x095C13652D8C9492, 0xFAEB5EF75179F338, 0x55D401731F5BD696, 0x88084B1899812EC6, - 0x4A6D06726FED554A, 0xC004BA3821A737DA, 0x9071C9B00D3EFC80, 0xEDF0C291366637BF, - 0x90887B70A106F0EA, 0x0C1CB3593B451068, 0x81D8FB0C1B19C12A, 0x17B9379F3BB05CE4, - 0x044F63B44737BA46, 0x0C3735839FA4AE74, 0xEEE0D5A9D00C5A1A, 0x31F04DCB14BD8518, - 0xC658D57B9EF52E57, 0xAEFD39575EA71472, 0x716EE92A4420A425, 0x27393BA52E2F48DA, - 0x45909840554773BD, 0xDF97812D945D4502, 0x6E06AB2D26EF0F87, 0x372129A533C484BF, - 0x8C6EA7F1E3A080DB, 0x5843D98BE542DA14, 0x11FA124C96A650B1, 0xD8A05CD4BC95D86D, - 0xE0EDB3E0ADB82623, 0xE5E32069521262A0, 0xC9EB8A8255816B40, 0xB7B8B21118768F86, - 0xFD78962164C1693A, 0x4CA8A700E9F5C662, 0x99564ADB697BB0F5, 0xEECED53D5127EF3B, - 0xA9C47F5FFF185AA9, 0xDEC80195BBF384F2, 0xEFDC4F36612329E6, 0x8DC0266F32282813, - 0x75EDA98EA53F06F8, 0x3A039A565776D2EB, 0x5AB86CB1146A4826, 0xCC39925636EB287E, - 0x3C4F22C18EC366C3, 0x799985D96375AD3B, 0x8BDBACBB270BEE24, 0x591EB7BE6627E54E, - 0x037875D6F093AA98, 0xF88154F603D82EF8, 0x171012866812718F, 0x847EE9FBDC3F378A, - 0xAEA2A8F22FCB1C8F, 0xF994D7D38D2F0775, 0x86D3B5A54CD62E63, 0x0E089C369F657B99, - 0xE6065289DA7454AF, 0xB24482E37EF363CE, 0xD9DF3112CC7846A2, 0xD878034277954549, - 0x1170BA627FDE20FF, 0x17A254C1417EF028, 0x7EFC3B24EFFCB336, 0xCBA48FBC44919B74, - 0xE84FC4A4C37E1BEA, 0xF4179DB322A466D6, 0xC4B488078F407EA2, 0x98542B29C70A6023, - 0x7272DFD5A656B24C, 0xD7CAD996BD191363, 0x62C3144F3EA19C84, 0xAABF88BA449FBA67, - 0xC31F704700D60B11, 0x492DF66EF32CCE0B, 0x3749168A8C423395, 0xDAB72E3F1B3FF4EF, - 0x1390C9038BAC6F53, 0x70F3D0E78133DE7D, 0xFB1F334587AE558D, 0xDC83043152119EC2, - 0xCC45A24AFF9F5116, 0x1F4FC8E2AC0F2FA3, 0x20943080EA8DCE01, 0x78E23DB765C8B04F, - 0xEB26F430956A565A, 0xFA18732316E3E20F, 0xE55C99766251F24E, 0x6514E0D999AF7EBD, - 0x0DEEB95D8C51964C, 0xDCFD7E4C9DDAA2A7, 0xC1027FECC517CE98, 0x2F118D44B1D884FA, - 0x949D8C218CC97ECE, 0x3E99BEBED3BB4403, 0xCC794EC80B00D381, 0x19C83DE1D8919094, - 0x0CBA70B4CECFAB91, 0x3C032EC4A7C18330, 0x7F66068D71309AEB, 0xC6B3C5FA90F8FC04, - 0x108ACAEE1242A17B, 0x6508D18D45DAF318, 0x8B8C570D46EC558D, 0x6B112978B7CA5044, - 0x214DF0EB222CFAD0, 0x32F1212B99581F10, 0x24B96CD8A50392F4, 0xEE98D2FF9ECDB715, - 0x9D7AE5A592746FE2, 0x235FF2FFED36CA07, 0x63926C26E5C3975F, 0x3D21AF22330D0841, - 0x0E8D5EACBBB76987, 0x2224CB350E9D6618, 0x76C8B30F683FAA57, 0x587A46D89D5110DD, - 0x0076A43B9566F9A9, 0x752E84CEC0CCB40A, 0xD32EB96F78DCEAE3, 0xD236EF0C9BDE41A1, - 0xB3E78F643C6544EC, 0x59A708E65A5D3E33, 0xEFE8016AEEB9DBE2, 0x1AF3DAC5FAF492CB, - 0x5310F21E89D4FA7E, 0x99EDD35C31442802, 0xEA68483631B98599, 0x59EDED9BD271755B, - 0xEF3A1E34ECDB424B, 0xF793B7BC41EB66CB, 0x36E4C22140605BA5, 0x15B83E81A92DE991, - 0x7B6401F719E6EB3A, 0xA42E275CCF66C94B, 0x8837AD8C402D79E2, 0x8C6E90F04A5AA55D, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0xA3FF79D1D006D6AE, 0x6AD9145C58F5E5F6, 0x8D88771107C789BB, 0xBA8A1AA652BB5DB4, - 0x68D35D208621D95A, 0x492CDFB7A74A897A, 0x367B66C9678FF0CB, 0xDBFDADBE731C784E, - 0x5E4953F282FD3A5A, 0xD4D5E02D888D5FB6, 0xF4F1238AB5867231, 0xCDA95F40A738EB5E, - 0x7856A9E27D4C6E8B, 0x6D09F3D96AB131A6, 0x375147264B4247A3, 0x561DA21B07240949, - 0x87F25893BEFCC0B5, 0x6B1A8672590F0D1C, 0x43A65D1CF804CAE1, 0x045D4E85D933151D, - 0x4ADA4BF619B45A13, 0xC2C5F738182FBB55, 0x5739259D5025B8EE, 0xCD0119247EA1B819, - 0x967401D217450AB0, 0x250B8D2E61A2B9F0, 0xEE660C28CCEA7150, 0x3C265BA93DFF5C20, - 0xEDF0ADB22D6762B9, 0x8B29D882E4608F43, 0xA4A4342EB32D7CAD, 0x94EC370175F657A4, - 0xA272DD25CBA76693, 0x486C82AB9989FD80, 0x840E722E2D907433, 0x0CEC7BB75A89C3BD, - 0xD0655D301C50CCA2, 0x22F80BAB31328B09, 0x2066BFA68C41AEEB, 0x05EA1854620E2257, - 0xD5174F31DFF77C10, 0xB1D9DD40A224C00E, 0xE2665F39096F66CA, 0xF17C034E0DC42F65, - 0x9BF019142F202DA0, 0x7AE0C39BE67E6C96, 0xB0CCE1A8ED975D49, 0x0A860A8B02791416, - 0xB09DA9BC13013244, 0xA2477FE1CD73D376, 0xBF488F6BF1C3494E, 0x8B10EEA0FF197060, - 0xA0BECDC268A273E5, 0x96CFFE6ED476E6C0, 0xB49E954641B3D7CD, 0x87109D904CB242EA, - 0x81DFDEDD6DE7E7C3, 0x625173188531238B, 0x5045CE3BC83283A0, 0x2210721B807EEDD5, - 0xEEB961178611EED5, 0x769C196323B632F7, 0xF44C486A0DAB50F0, 0x19665F60EF9897A2, - 0x3E4ACA6229AF0E13, 0xBBEC221809D144CA, 0x8DB803C6CB8F0750, 0x988DC9C97FE41CA2, - 0x2614736A540F9CCC, 0x7FDB437726426C05, 0x10529AD0589FDCAA, 0x0175CBE545872156, - 0xDD5198127C3D38B6, 0x5E1692C05AC568F3, 0x31C73529EDD56441, 0x3940CEEB48FEFF4A, - 0x126D5F063E6F41F7, 0xC44FF8D56765C18A, 0x81D787F68004280A, 0x24F1099912310B70, - 0xE157D8227E628B27, 0x515C60CF6863C2F7, 0x7043C0CAC33D3AAF, 0x80785B2DFACDEE6A, - 0x32A1D195493F41CF, 0x10B16E23338922D0, 0x67592ECB5F62DFAF, 0x9CB35D0B3413F247, - 0xDEBC12208E98E24A, 0xFC4C1037A8F20B98, 0xA1B518F84D4ABC3C, 0x73266DBEA301D7E7, - 0x9CEF23D641E8EC6E, 0x3730B13CBFC78423, 0x31A466124EF717A5, 0x2A1F181148A7F72C, - 0x19250A274A397FE4, 0x3565F551732AADCD, 0x0860D875A6FDC9A7, 0x0B8BA67CDE14B748, - 0x9B0FE677BDDA9C64, 0x37E0303C6480DEF1, 0x0CB5791A47A17192, 0x269D46F67001311E, - 0xC0BDA81BB40F1E22, 0x18CCFC5797B562BA, 0xEBF043050A4CDDD6, 0xAAA49EE4469FCEE7, - 0xE91B2B211D425A41, 0x21B41DC4B88DE0B3, 0xDF2192FA189AE965, 0x1CDB77094CCD232D, - 0xAAF7AEBAFD560ABD, 0xC92738B6245662C1, 0xAEB08B04FBE07D1A, 0x3BE0CF5EFDEF6238, - 0xB667B7C7B9FA7104, 0xF528FD3C9757EFF2, 0x3217E0978B3203FC, 0xF24CF9BE57C4A75A, - 0x62016D3E441F1A82, 0x3B72193414092A15, 0x24A4559C5A00FA39, 0xF8F710F2205FC859, - 0x8EB6E3DD211564A3, 0x41FD03D66D75CF02, 0x2D56F1F9C31D8835, 0x0570E10E76EBFA76, - 0x555D5003D0701132, 0x564E37DFCC587244, 0xB2FE34F26AEA1ECA, 0x218040F8E3ABE80E, - 0xE43D2D92C43B6F5F, 0x3E4D8999877FC72A, 0xFD1305F6F0AB95AC, 0x035D7AA51BC2BE12, - 0x9DC389FC6F6C37F1, 0x841977B42B3F06E0, 0x592A909563376C9A, 0xAF3E216AD3B23D5C, - 0x2242DE1C62957B69, 0x9A49E7C6EF0F0DD1, 0x616EFA4339813DA5, 0xBF33F0D7BF12130C, - 0xBB67B6E238C18FC8, 0x74933B808791142A, 0x6F6C05A02C2E17F3, 0xE04B82CC0956330D, - 0xEEB81026362750DA, 0xD0E0E6682F0EC5D6, 0xC21E06C2BFCB9D7F, 0xA7EF5D60AE2B5123, - 0xE34E5D1330ADB0C8, 0x26073DF6DB563F5E, 0xD31E8BB72E8A8553, 0x788D09E4D4126431, - 0x21BCB038B76667D5, 0xE6CC42AF8260E74F, 0x3CEFC1D3DB5B054A, 0x54FE7A4BAEFEDA92, - 0xA3ED4FD68F05DDB1, 0xEE239B2BCFDF68C8, 0x14A5B2AEDF892C98, 0xD5D60EB15E252977, - 0xFB0F3C3DD1D10BE9, 0xF6111FE16A12B818, 0xFFA61FF01E150FB6, 0xBAF18E2AAD17F038, - 0x5BB151CA1FE60A92, 0x57323A9FF6C78E1D, 0xCAE4B7F8A1A82F18, 0x89D162254C99D165, - 0x642BF239D7D858C6, 0xFE60AF576201BA5E, 0xDAC250E5FB94BFBD, 0xBA3664B8F9BA1C25, - 0x261E5B6246303DBD, 0xB57282B63E424403, 0x75A3D5CD658E9A71, 0xF9AB32381DB1FD96, - 0x629C197F19A54683, 0xA324840EA1F76EA1, 0xE7EDD0D61E8704AA, 0xD1EE00A964B0BA9A, - 0x583558DC6FBA4999, 0x126702DE5EC42CFE, 0xB3238DAA33730DD9, 0xA0B83768C57FBE58, - 0x51C6B4C9C4072680, 0x97088F0CAA1F2FCF, 0x0C26BA8D9C8A9E53, 0xC6EF70D81E160725, - 0x2448A2FAB7DD2373, 0xC9A6786D7B642146, 0xC5131CADCA72E7B5, 0x5E79A89EC29FFB00, - 0xB09D50B1E836583F, 0xAD9EAA566C556655, 0x571924FDF0694D60, 0x9E868503FEB71810, - 0x6418BD42143B05A8, 0x556FA6C74674C14A, 0x1B60883E5062F15D, 0xD0AD9E9999D8DF3F, - 0xA26A896E51C4B6DF, 0x6990D2AF3CA60534, 0x1F60352211A161A8, 0xABCB7EE562D520A4, - 0xD4E71EE21066A414, 0x6C6B24E2391BC087, 0xAAAD9BFD37F06F4F, 0xB6BD08B34DACA455, - 0x8A867F6C0B28FA39, 0x7F3D2719628ACC7A, 0x78AE948E761368B5, 0x7E2099713DBABCF6, - 0xD0005FFCE7FF6502, 0x2E904A1130BC65E8, 0xA52746F54B982D67, 0xE11949C4AF14AF4C, - 0x55835527B35F0BAC, 0xAC677DD7288E4268, 0xBB89D50044D68B2B, 0x9F0B504B4C9B593E, - 0x1207DDEA0584DB4C, 0xCFCEFFB8B2E243A9, 0xD7E0D57238846848, 0x180936BACE75A482, - 0x4EC161F9FCDCEE37, 0x3F02D7F2BD9AC384, 0x772CD2CBE304CB8B, 0x735838674683ACFA, - 0xE7D6D122CA7ACA39, 0xABE5223AAEB336A6, 0x644C2632EE4236A1, 0x4BE67BDE4C0165C2, - 0xBA1C385C2784266B, 0xD73C7D1F953C1DF5, 0x124169F630A5D8C7, 0x2B0889D0BD07167E, - 0x684EBD9E1443674F, 0x4545B6EA14F8881A, 0x0155B5D505852928, 0x6CE95E3145A971E5, - 0x09392839B7461114, 0xC7282C716E163D89, 0x99550204A695C1D1, 0xBC3608142C2E4112, - 0x000B8D70D8008C9E, 0xB890E79F987C7BF0, 0x116842C96F74E149, 0x80F50CF9F8A4ACBB, - 0x43564F2CE5CF5A06, 0x2BFABC8688B694B6, 0x5BF3C69332E375B1, 0xE30713E9C31DEF23, - 0xFA8CAA174592FB85, 0xFE8ADF8C34B9318A, 0x3FECC328834779B2, 0xA35A93323CC17467, - 0x0614B42A8A02D28D, 0x44CFBE0C62A7E9E9, 0xC2BB83CB6F101E64, 0xF7C84FE8A3DD520E, - 0xFDD4FFDCB9A716D7, 0xA9328A1C3F9A4065, 0x09D7EA8213D87CA4, 0x991D70D6D87C4017, - 0x64681516F68AAF20, 0x84A57BE98A0A134A, 0xED75B4776BFE341F, 0xF427FB839BA19E3F, - 0x8F923CFF6D1E0122, 0x9C204CDF6FC97414, 0x6C22B172A1B8F79B, 0x4EE0CC7377B69D39, - 0xE284385CE76716EB, 0xF20D06EB02576FA2, 0x718185428C00057D, 0x481B1A866133B2DE, - 0x633F38E4C9837B93, 0x33811F5A03FFD672, 0x660B54CC6DFAC390, 0x875C2304AA09F694, - 0x450E44826F6F0E53, 0xBF167E856C199CC6, 0x3F7BAD20C2D1FF30, 0x4F5E942FED341C6F, - 0x4CB011AF4BB07879, 0x16E19F3A2FA7B4B5, 0xA5679A51047949E0, 0x3116200F66002F92, - 0x1F0FCACE0DF2C93E, 0xD0044C066B0D844F, 0xDFFD591AE50D1D3A, 0x943C4A1080B80A10, - 0x1548D4AC71F1759E, 0x469EFB8CCDB55A3A, 0xE4163013F22B302B, 0x040E8F11C3FC13CB, - 0x6623925B90CD7848, 0x155230A4C0ABF7D4, 0x6E80491EE054726B, 0xAA0385D799A60C5E, - 0x98EA0E2D1484B04B, 0x9A4056841B1DC50C, 0xFEE62D3A9F57E6C0, 0x4F8E8AEC4E325EF8, - 0x5DE83EABF37401F4, 0x08E9642C59D4BC87, 0x1E6F642DF1D90BCC, 0x82AB9BDCCE1C86CA, - 0x81EE4CE18327CE36, 0x93D28C597E17FAFE, 0x8F229DB1E1822975, 0xEC81192225285691, - 0x7F26663D5E235110, 0x3DC4F14C33F2571C, 0x98D0CA19B25989B4, 0xE55FC9248BF198AB, - 0x0E567369076D2B78, 0x9F42EFAB5AC40202, 0x46D2CEBF9971EB91, 0x535B9B74A4B28A80, - 0x40D3BC8321BA0DF7, 0xBCED760516B04B33, 0xA925C65738F95155, 0x6699B1C1DE862758, - 0xD51C21349D6271A7, 0x0A41BC1E74663117, 0x30FB22A2234FF59D, 0x817EF5355BC810E7, - 0x34874F13936F49B2, 0x9C8BB16026212435, 0x84C47661CB407CE6, 0x229FC05824B53275, - 0x4EF6AD4CD5C1E4B2, 0x36D0D5007EED1B6E, 0x3B20B3B4B9752C85, 0x75DB6399E11DEA84, - 0x384FB5EF7C73FF84, 0x9F844688465DF285, 0xFF6DE64A2C9F7F61, 0x0AB4158F625085C4, - 0xCF46546201F9783F, 0x3EB3D955C1333CDC, 0x9D2AAFC41A7F7E66, 0x70A9ED6569B77491, - 0x0D3D824C1006523E, 0xEAA9ACB30D4FCF38, 0x8F46999511FC12D2, 0xEF6ADC9CEE6F5B4D, - 0xE3F1A2D20382C293, 0xE5EA1C60F4E27140, 0xA5DB97F05296CDCA, 0x5B714F13F1C2760C, - 0x3017A629F17A5F65, 0x312F52B723D11821, 0xFCFE1746CF238971, 0xF1CFFFDF5BF7DDE7, - 0x24BBF9A2008E8600, 0xBAEDB7C6D73166B5, 0x4E15A22B89809147, 0x2150BBAF4B097D60, - 0x778E2FC33023DF49, 0x2F3527E6DF19FFCD, 0x5DC5467F4E143CEF, 0xF97470EF249F4071, - 0x17F6BD93E68E0F16, 0x396836882CBB0803, 0x03A202A6725CD948, 0x5D88698F82BFC51A, - 0x329C4F98D6C91F8A, 0x3BD5998A774AFC46, 0x1352021315DC66D8, 0x998874FECDCC8B6D, - 0xF9A0916C69A15D80, 0x81782AB7A92C293D, 0x884A728C28AA83F0, 0xD133CE820D68B5CC, - 0x46CE6CF72CF6B174, 0x425BA8D6E9D7E3C5, 0x0D0C5FCA7ADD4A12, 0x6F769328B79C1913, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 - }; - - std::array CastlingKey { - 0x84894C3406355AF6, 0x23B2609AA96363B5, 0x1407B9F037FA2659, 0x311D16CFDE18E8A4, - 0xC50E55737BF3686D, 0xB37363FC66C458C1, 0x3C7E3E99025D7867, 0x9EB4AB344B612152, - 0x04B0366C313B3145, 0x4017A1F497763418, 0x2E981BB957766475, 0x1591C2F35128B6EA, - 0x30474127CB2126A4, 0xB148E6C764EA59C7, 0x2AE55C34CFD6200A, 0x55105F0DB5470D99 - }; - - std::array EnPassantKey { - 0x33194C80CEF360A3, 0x046733315F90461D, 0x47E1C1ECD246776E, 0x672391FA1A31F640, - 0xBD317AF266C19449, 0x65F986CCEEB2A307, 0xE1693D18964743A5, 0x8B38FE1A898DAB7F, - 0xF31100729C152822, 0xB97BF20F48CB8845, 0x0B4E8C4AB774CCFD, 0x08A3A8EC47E6DF2B, - 0xA5F25C70A3CB266A, 0x8DD120728F515331, 0x9641818A4E8BD430, 0x9C07CA1992710AD9, - 0xA049D726E8DEC2E5, 0x0A98BF3BF685CFD9, 0xE87E541C1716590D, 0x678B5FEB66C935E5, - 0x5866E942B0AAB44B, 0x1A5A4AFCBA74EA8B, 0x907FEFB3C0846671, 0x79B4E20EF103F544, - 0x24C8E81586AA1D43, 0x9AECC3E0328CBEF2, 0x5CB4CBDB85F4AFDA, 0xA84A75E54C6B1934, - 0x8905D39FDE76FB36, 0x80B04D62F4CD609E, 0x789E2FEE5E166808, 0x7C3F7B7CFB524E68, - 0x06FA0E4CE5FCE473, 0xE6F9BFFD7BE76BA9, 0x06BAEB5F7C44EB68, 0x230C09815175FE5C, - 0xFA34CB517E815120, 0xA97B234882B50F6E, 0x881DC23791936B55, 0x9F85A9984E88F529, - 0x9D947A3EAB5AC45F, 0x39BE3836500E49B7, 0x5EEB7B7615FBCC82, 0xC462E5435C55F834, - 0xDB6AD0C98423158A, 0xF152CCB4EDD9F6D6, 0xE3296FADCBCC4F37, 0xD15A38254677FBD8, - 0xB26F391420C549E7, 0x67AAFEA1803F2062, 0x1A169719F32463A5, 0x9EB52458CDAC3FEB, - 0xFDDC31066A06089B, 0xD942A9308D681ADF, 0x50B86613C2A01051, 0x3E59A18DD434AB32, - 0x746040F799E4A16A, 0x42EA892EFE132786, 0x358AF383487CD2FD, 0x3D6F08FD5B37B64F, - 0x11E2A9C48E222AC0, 0xA922B952EC3B2CAF, 0x77BA1125BBD7F1C0, 0x7E7604084149AE16, - 0x0000000000000000 - }; + constexpr std::array, 7>, 3> PieceKey { + 0xA6236FC5F3D5CB40, 0xD08B197B0AE50DDC, 0x4C1C2B8202F856DC, 0xBC471B2026C87B34, + 0x97BBE5223FC13E95, 0xFA40BA2273CCB6EF, 0x15396251A0FEBEEC, 0xC085FCA64ABE89AE, + 0xF6AE4C573C7529C6, 0x7A29A85ECB12619B, 0x326AA61C623ACD3B, 0xF2F071279891AC04, + 0xA0A30345F9AD0B31, 0x05B66E50F0040710, 0x1FA322C2D80E435F, 0x2ACF15BAAA3842BE, + 0x373530FB529A1F70, 0x96E27AA215519A29, 0x0E301862E3388BF5, 0xFAE5F9CB033A8D67, + 0x2F0964D33080DA38, 0x437D8ED128AED70E, 0x58C8ECDC2EB4A15F, 0x306B32F56EEA8CEF, + 0x91C2B1FAC8DC56B4, 0xD93D08698AD40982, 0x4C1EB64429A3F70F, 0xDC3A9B7A47412711, + 0xAD49B5B27A737E6B, 0xE7EE3D17D3AC23DE, 0xFEEB428EFE5E114E, 0xE6177B70D2B69038, + 0x5A27C394EA1EF7CF, 0xD3344A09204AF6AD, 0x294E3B3C68F85A39, 0x633C76FF21417592, + 0x8B52041E1F04C50C, 0xEDF4CBF7985004EA, 0x93AAAE821699E25B, 0x375A05811E58F5A9, + 0x34B514464314E7B9, 0xC732B5EB387E6D07, 0x5EE026DD1CC55481, 0xE40A34AB2976711B, + 0x46A6B0B610DF19A9, 0xF3A7712F150E642D, 0x925A3E3F2D9D90C2, 0xD3898B9ED528B606, + 0xD01022D6D28E9A62, 0xA92ECA1778EE7EAD, 0x6BA4DD971A6782C9, 0xA2A8D18800094CBF, + 0xA01CD5E395B33BDB, 0x3B7AE14FC07FCC80, 0x853C6E241C0C7E87, 0xDC480F198A1323F5, + 0xF4C69817DC9D4CD6, 0xDD1BE45DCA9AC765, 0x6C7E8BC50978CDD2, 0x708792C1BFA02DFF, + 0xEF249E4402E9A383, 0x112771D45DB617B8, 0x4A163F974BB73507, 0xD23206F7EADC30CE, + 0x349919FAB947293F, 0x8D9821D5AD5189A9, 0x44D93BDA8CB88F05, 0xB1F739C114349E8C, + 0xEE166E2AAA1DE3E5, 0xAC7569ED0DA1839C, 0x04F7A54D68E44E97, 0x6F7285E45B2C0250, + 0x9763EE04F1C79F91, 0x2256CB9957F0CA78, 0x3F638F55727C8729, 0x1709BB41275A18EB, + 0xA9A883A24E7B3210, 0x846AA0B583EF4CEB, 0x021900085D310AAB, 0xBD563B93EB06873B, + 0xA58008506746D8C0, 0x4A89E09F0886D3DE, 0x100EAB89377BD6AD, 0xAF15BD7D1A127818, + 0xA333035ADCC232F6, 0x5B631179357E8B89, 0x15331FBFBD3DBBFD, 0x212865F6D74E36CA, + 0xAAF0AC1D6C12D7BD, 0x0796D82DFDADD8A2, 0xD7EF587FC21DB615, 0x23AC5CB42312ADFF, + 0x503EF6FEA2775D63, 0xF1E73571E61B93CA, 0x65A2664840C2479F, 0x45F5CC9A6F6D3AE7, + 0x1E08F461D09A8A2C, 0xC966083CECF7CC77, 0xFDDA6C996D8420C5, 0xFDB6CE89C4BA12BB, + 0xAB90179AE7BAD59E, 0xDE28B56E7CC8A43F, 0xC0AC87487AAAA3D4, 0xA99444394E159DAF, + 0xAE2E7E4E674EFA15, 0x058E5EA3F52ED88F, 0x16B43C24E5EFDBE9, 0xFB714102FD38FA29, + 0x5590C16774661F0F, 0x511DB6596FEAFB75, 0xD984AF545D650F0E, 0x4DAB3146533E3745, + 0x279D1AF783E53367, 0x72BC45512870994B, 0x60A613DCF2EEE7DD, 0x440F3C82302E8D9B, + 0xF45999D026089C50, 0x00885B3FBB123D72, 0x5371B9425F76CC7A, 0x0EA56DEB69E8858D, + 0x43A1963CB4D82B8A, 0x3F20EA009E80FA8D, 0x9602AFC6B6B52E05, 0x0508EFB04B99B42B, + 0xE4C3D38313310498, 0xA253D33847D2322A, 0xDA47EEB7BD64A965, 0x9D9356FD88894129, + 0x31BC0DB9FC057AF1, 0x585EBADD948BD06A, 0xAA4D4B8B07E6F761, 0xD4E63B4A41E956BC, + 0x57E857CDBE55349F, 0x6FFDB362E75232ED, 0xA6128B70062630E3, 0x2FE77A23633F5A2D, + 0x0A8A9F64A8F4B7EA, 0x094866F9581A8E0E, 0x1464E014195D7A58, 0x723484177390AF22, + 0x430A55BEABFC224B, 0xC7ACBA70E1F71CA6, 0x091BCE1DA80A3612, 0x89F2170EB585E052, + 0x034A9C69D60CA859, 0x67A382E8F7721970, 0x7C548C7AE160C902, 0x8F3454CFDE58F207, + 0x755DD6ADC8886D29, 0xCF21235B033706EC, 0xCC0F11B645B21E3D, 0x1C346B4476CE9053, + 0x2CBADBFA7ACDE2E4, 0xE421515474F4EA6C, 0x18D9EFA1B1A088D1, 0x8031E7071D74BB5E, + 0x61D5A655781E1BD2, 0x02789B1CCBD88663, 0xF47A83CABBA4EA6E, 0x85259C663E1CB509, + 0x2B8EA83239A174B9, 0xC3FB0D171B4D3FB3, 0x4EB575421CAE7143, 0xC6FA9789A06F2AA0, + 0xC6D03EF9A8125361, 0xFBDD5F358B08795B, 0x434208EEF4FB59B9, 0x1BA1EE39089852BF, + 0x04CA53FBC3B98644, 0x4119296DC9C1E8A1, 0x59D8059B30E6734C, 0x6D8DC7A6A6E8CE11, + 0x66001AA5AF47ECD7, 0x95D679460653F68F, 0x3E807108C5BB5639, 0xD95E236A2CB260AE, + 0x329C6AE180F2545A, 0x9812D2B2A5A17F73, 0x6665C70C4F6C52F8, 0x6BFCD3674400096C, + 0xCFB45587F2AB7998, 0xDB8BDACF885289BA, 0x93CD0EB717341A4E, 0x9FCA5769D8148BEE, + 0xA2101F27390C3683, 0x9196DC7582959D9E, 0x8AB4E49F4DF6257D, 0x6366DB41FB0318AB, + 0x2EB452CE9904B2BE, 0x121D4A791A789109, 0x9E007D60435B82F5, 0xC361151B0F8E8282, + 0x2BC3FCD8C2C43136, 0x6011996C0D44C871, 0x41032280BE05F1FA, 0xF312A5C44C1765B2, + 0xF8395B8647E3B42F, 0xE832F23898D22FBF, 0x1443A20F2A142D70, 0xCC6776E57210822E, + 0x9EDAC9B6B7DD50B5, 0xBF086A4F50FF0DB7, 0x8CE3E89096DB2F33, 0x4913C74B0318CE71, + 0xA6317A15E8E306C8, 0xC3C4F2A302727552, 0xCC3BD09AB53F5179, 0xF053D6B1E745516C, + 0xB8AA20874988B6F9, 0x7FE9E0761A4435FE, 0xF7AE5E88FA89550A, 0x7CF54935C37B16CA, + 0x583C7B69ED098122, 0x8C55218C65B786A6, 0x9B68594C5CDDC667, 0xB483E6A5707E9107, + 0x8E15FBD7F5F7C73F, 0xA86B48A601533118, 0xC92F996C6E5CEEC1, 0xC0B3CBFCD8E56E19, + 0x6BB46029C4250303, 0xBFF466051B434EB0, 0xA6481489B6AFD626, 0x4C923D1669DC2E98, + 0x617FA622A3FE927E, 0xB3C313561DBEF03A, 0xEE36FC1C26B22679, 0x73554B84FB2C7FBA, + 0x7F9C5167739DF6BC, 0x844F2B56997C4E56, 0x81306C8BEFEF296B, 0xEEDC847AA3E9105B, + 0x284B50765F604A9A, 0x8F979D7FCDCF328A, 0x50E8665B5B8FA72F, 0xD205F86757171BC5, + 0x1B0EF0CA0BFE8D71, 0xBE02629C5865C7DD, 0x349381F4D3735978, 0x91C0F4395A991062, + 0x3FC58C9EF15CFC2D, 0x63B38F00937D7498, 0xEFDE31850EDF5819, 0xE9E2B456B9A9C9FA, + 0xEC201006A02AD307, 0xD233FF037F3A3394, 0xFB0CB214E040365D, 0xE4E90BE3D4F76C1E, + 0x095C13652D8C9492, 0xFAEB5EF75179F338, 0x55D401731F5BD696, 0x88084B1899812EC6, + 0x4A6D06726FED554A, 0xC004BA3821A737DA, 0x9071C9B00D3EFC80, 0xEDF0C291366637BF, + 0x90887B70A106F0EA, 0x0C1CB3593B451068, 0x81D8FB0C1B19C12A, 0x17B9379F3BB05CE4, + 0x044F63B44737BA46, 0x0C3735839FA4AE74, 0xEEE0D5A9D00C5A1A, 0x31F04DCB14BD8518, + 0xC658D57B9EF52E57, 0xAEFD39575EA71472, 0x716EE92A4420A425, 0x27393BA52E2F48DA, + 0x45909840554773BD, 0xDF97812D945D4502, 0x6E06AB2D26EF0F87, 0x372129A533C484BF, + 0x8C6EA7F1E3A080DB, 0x5843D98BE542DA14, 0x11FA124C96A650B1, 0xD8A05CD4BC95D86D, + 0xE0EDB3E0ADB82623, 0xE5E32069521262A0, 0xC9EB8A8255816B40, 0xB7B8B21118768F86, + 0xFD78962164C1693A, 0x4CA8A700E9F5C662, 0x99564ADB697BB0F5, 0xEECED53D5127EF3B, + 0xA9C47F5FFF185AA9, 0xDEC80195BBF384F2, 0xEFDC4F36612329E6, 0x8DC0266F32282813, + 0x75EDA98EA53F06F8, 0x3A039A565776D2EB, 0x5AB86CB1146A4826, 0xCC39925636EB287E, + 0x3C4F22C18EC366C3, 0x799985D96375AD3B, 0x8BDBACBB270BEE24, 0x591EB7BE6627E54E, + 0x037875D6F093AA98, 0xF88154F603D82EF8, 0x171012866812718F, 0x847EE9FBDC3F378A, + 0xAEA2A8F22FCB1C8F, 0xF994D7D38D2F0775, 0x86D3B5A54CD62E63, 0x0E089C369F657B99, + 0xE6065289DA7454AF, 0xB24482E37EF363CE, 0xD9DF3112CC7846A2, 0xD878034277954549, + 0x1170BA627FDE20FF, 0x17A254C1417EF028, 0x7EFC3B24EFFCB336, 0xCBA48FBC44919B74, + 0xE84FC4A4C37E1BEA, 0xF4179DB322A466D6, 0xC4B488078F407EA2, 0x98542B29C70A6023, + 0x7272DFD5A656B24C, 0xD7CAD996BD191363, 0x62C3144F3EA19C84, 0xAABF88BA449FBA67, + 0xC31F704700D60B11, 0x492DF66EF32CCE0B, 0x3749168A8C423395, 0xDAB72E3F1B3FF4EF, + 0x1390C9038BAC6F53, 0x70F3D0E78133DE7D, 0xFB1F334587AE558D, 0xDC83043152119EC2, + 0xCC45A24AFF9F5116, 0x1F4FC8E2AC0F2FA3, 0x20943080EA8DCE01, 0x78E23DB765C8B04F, + 0xEB26F430956A565A, 0xFA18732316E3E20F, 0xE55C99766251F24E, 0x6514E0D999AF7EBD, + 0x0DEEB95D8C51964C, 0xDCFD7E4C9DDAA2A7, 0xC1027FECC517CE98, 0x2F118D44B1D884FA, + 0x949D8C218CC97ECE, 0x3E99BEBED3BB4403, 0xCC794EC80B00D381, 0x19C83DE1D8919094, + 0x0CBA70B4CECFAB91, 0x3C032EC4A7C18330, 0x7F66068D71309AEB, 0xC6B3C5FA90F8FC04, + 0x108ACAEE1242A17B, 0x6508D18D45DAF318, 0x8B8C570D46EC558D, 0x6B112978B7CA5044, + 0x214DF0EB222CFAD0, 0x32F1212B99581F10, 0x24B96CD8A50392F4, 0xEE98D2FF9ECDB715, + 0x9D7AE5A592746FE2, 0x235FF2FFED36CA07, 0x63926C26E5C3975F, 0x3D21AF22330D0841, + 0x0E8D5EACBBB76987, 0x2224CB350E9D6618, 0x76C8B30F683FAA57, 0x587A46D89D5110DD, + 0x0076A43B9566F9A9, 0x752E84CEC0CCB40A, 0xD32EB96F78DCEAE3, 0xD236EF0C9BDE41A1, + 0xB3E78F643C6544EC, 0x59A708E65A5D3E33, 0xEFE8016AEEB9DBE2, 0x1AF3DAC5FAF492CB, + 0x5310F21E89D4FA7E, 0x99EDD35C31442802, 0xEA68483631B98599, 0x59EDED9BD271755B, + 0xEF3A1E34ECDB424B, 0xF793B7BC41EB66CB, 0x36E4C22140605BA5, 0x15B83E81A92DE991, + 0x7B6401F719E6EB3A, 0xA42E275CCF66C94B, 0x8837AD8C402D79E2, 0x8C6E90F04A5AA55D, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0xA3FF79D1D006D6AE, 0x6AD9145C58F5E5F6, 0x8D88771107C789BB, 0xBA8A1AA652BB5DB4, + 0x68D35D208621D95A, 0x492CDFB7A74A897A, 0x367B66C9678FF0CB, 0xDBFDADBE731C784E, + 0x5E4953F282FD3A5A, 0xD4D5E02D888D5FB6, 0xF4F1238AB5867231, 0xCDA95F40A738EB5E, + 0x7856A9E27D4C6E8B, 0x6D09F3D96AB131A6, 0x375147264B4247A3, 0x561DA21B07240949, + 0x87F25893BEFCC0B5, 0x6B1A8672590F0D1C, 0x43A65D1CF804CAE1, 0x045D4E85D933151D, + 0x4ADA4BF619B45A13, 0xC2C5F738182FBB55, 0x5739259D5025B8EE, 0xCD0119247EA1B819, + 0x967401D217450AB0, 0x250B8D2E61A2B9F0, 0xEE660C28CCEA7150, 0x3C265BA93DFF5C20, + 0xEDF0ADB22D6762B9, 0x8B29D882E4608F43, 0xA4A4342EB32D7CAD, 0x94EC370175F657A4, + 0xA272DD25CBA76693, 0x486C82AB9989FD80, 0x840E722E2D907433, 0x0CEC7BB75A89C3BD, + 0xD0655D301C50CCA2, 0x22F80BAB31328B09, 0x2066BFA68C41AEEB, 0x05EA1854620E2257, + 0xD5174F31DFF77C10, 0xB1D9DD40A224C00E, 0xE2665F39096F66CA, 0xF17C034E0DC42F65, + 0x9BF019142F202DA0, 0x7AE0C39BE67E6C96, 0xB0CCE1A8ED975D49, 0x0A860A8B02791416, + 0xB09DA9BC13013244, 0xA2477FE1CD73D376, 0xBF488F6BF1C3494E, 0x8B10EEA0FF197060, + 0xA0BECDC268A273E5, 0x96CFFE6ED476E6C0, 0xB49E954641B3D7CD, 0x87109D904CB242EA, + 0x81DFDEDD6DE7E7C3, 0x625173188531238B, 0x5045CE3BC83283A0, 0x2210721B807EEDD5, + 0xEEB961178611EED5, 0x769C196323B632F7, 0xF44C486A0DAB50F0, 0x19665F60EF9897A2, + 0x3E4ACA6229AF0E13, 0xBBEC221809D144CA, 0x8DB803C6CB8F0750, 0x988DC9C97FE41CA2, + 0x2614736A540F9CCC, 0x7FDB437726426C05, 0x10529AD0589FDCAA, 0x0175CBE545872156, + 0xDD5198127C3D38B6, 0x5E1692C05AC568F3, 0x31C73529EDD56441, 0x3940CEEB48FEFF4A, + 0x126D5F063E6F41F7, 0xC44FF8D56765C18A, 0x81D787F68004280A, 0x24F1099912310B70, + 0xE157D8227E628B27, 0x515C60CF6863C2F7, 0x7043C0CAC33D3AAF, 0x80785B2DFACDEE6A, + 0x32A1D195493F41CF, 0x10B16E23338922D0, 0x67592ECB5F62DFAF, 0x9CB35D0B3413F247, + 0xDEBC12208E98E24A, 0xFC4C1037A8F20B98, 0xA1B518F84D4ABC3C, 0x73266DBEA301D7E7, + 0x9CEF23D641E8EC6E, 0x3730B13CBFC78423, 0x31A466124EF717A5, 0x2A1F181148A7F72C, + 0x19250A274A397FE4, 0x3565F551732AADCD, 0x0860D875A6FDC9A7, 0x0B8BA67CDE14B748, + 0x9B0FE677BDDA9C64, 0x37E0303C6480DEF1, 0x0CB5791A47A17192, 0x269D46F67001311E, + 0xC0BDA81BB40F1E22, 0x18CCFC5797B562BA, 0xEBF043050A4CDDD6, 0xAAA49EE4469FCEE7, + 0xE91B2B211D425A41, 0x21B41DC4B88DE0B3, 0xDF2192FA189AE965, 0x1CDB77094CCD232D, + 0xAAF7AEBAFD560ABD, 0xC92738B6245662C1, 0xAEB08B04FBE07D1A, 0x3BE0CF5EFDEF6238, + 0xB667B7C7B9FA7104, 0xF528FD3C9757EFF2, 0x3217E0978B3203FC, 0xF24CF9BE57C4A75A, + 0x62016D3E441F1A82, 0x3B72193414092A15, 0x24A4559C5A00FA39, 0xF8F710F2205FC859, + 0x8EB6E3DD211564A3, 0x41FD03D66D75CF02, 0x2D56F1F9C31D8835, 0x0570E10E76EBFA76, + 0x555D5003D0701132, 0x564E37DFCC587244, 0xB2FE34F26AEA1ECA, 0x218040F8E3ABE80E, + 0xE43D2D92C43B6F5F, 0x3E4D8999877FC72A, 0xFD1305F6F0AB95AC, 0x035D7AA51BC2BE12, + 0x9DC389FC6F6C37F1, 0x841977B42B3F06E0, 0x592A909563376C9A, 0xAF3E216AD3B23D5C, + 0x2242DE1C62957B69, 0x9A49E7C6EF0F0DD1, 0x616EFA4339813DA5, 0xBF33F0D7BF12130C, + 0xBB67B6E238C18FC8, 0x74933B808791142A, 0x6F6C05A02C2E17F3, 0xE04B82CC0956330D, + 0xEEB81026362750DA, 0xD0E0E6682F0EC5D6, 0xC21E06C2BFCB9D7F, 0xA7EF5D60AE2B5123, + 0xE34E5D1330ADB0C8, 0x26073DF6DB563F5E, 0xD31E8BB72E8A8553, 0x788D09E4D4126431, + 0x21BCB038B76667D5, 0xE6CC42AF8260E74F, 0x3CEFC1D3DB5B054A, 0x54FE7A4BAEFEDA92, + 0xA3ED4FD68F05DDB1, 0xEE239B2BCFDF68C8, 0x14A5B2AEDF892C98, 0xD5D60EB15E252977, + 0xFB0F3C3DD1D10BE9, 0xF6111FE16A12B818, 0xFFA61FF01E150FB6, 0xBAF18E2AAD17F038, + 0x5BB151CA1FE60A92, 0x57323A9FF6C78E1D, 0xCAE4B7F8A1A82F18, 0x89D162254C99D165, + 0x642BF239D7D858C6, 0xFE60AF576201BA5E, 0xDAC250E5FB94BFBD, 0xBA3664B8F9BA1C25, + 0x261E5B6246303DBD, 0xB57282B63E424403, 0x75A3D5CD658E9A71, 0xF9AB32381DB1FD96, + 0x629C197F19A54683, 0xA324840EA1F76EA1, 0xE7EDD0D61E8704AA, 0xD1EE00A964B0BA9A, + 0x583558DC6FBA4999, 0x126702DE5EC42CFE, 0xB3238DAA33730DD9, 0xA0B83768C57FBE58, + 0x51C6B4C9C4072680, 0x97088F0CAA1F2FCF, 0x0C26BA8D9C8A9E53, 0xC6EF70D81E160725, + 0x2448A2FAB7DD2373, 0xC9A6786D7B642146, 0xC5131CADCA72E7B5, 0x5E79A89EC29FFB00, + 0xB09D50B1E836583F, 0xAD9EAA566C556655, 0x571924FDF0694D60, 0x9E868503FEB71810, + 0x6418BD42143B05A8, 0x556FA6C74674C14A, 0x1B60883E5062F15D, 0xD0AD9E9999D8DF3F, + 0xA26A896E51C4B6DF, 0x6990D2AF3CA60534, 0x1F60352211A161A8, 0xABCB7EE562D520A4, + 0xD4E71EE21066A414, 0x6C6B24E2391BC087, 0xAAAD9BFD37F06F4F, 0xB6BD08B34DACA455, + 0x8A867F6C0B28FA39, 0x7F3D2719628ACC7A, 0x78AE948E761368B5, 0x7E2099713DBABCF6, + 0xD0005FFCE7FF6502, 0x2E904A1130BC65E8, 0xA52746F54B982D67, 0xE11949C4AF14AF4C, + 0x55835527B35F0BAC, 0xAC677DD7288E4268, 0xBB89D50044D68B2B, 0x9F0B504B4C9B593E, + 0x1207DDEA0584DB4C, 0xCFCEFFB8B2E243A9, 0xD7E0D57238846848, 0x180936BACE75A482, + 0x4EC161F9FCDCEE37, 0x3F02D7F2BD9AC384, 0x772CD2CBE304CB8B, 0x735838674683ACFA, + 0xE7D6D122CA7ACA39, 0xABE5223AAEB336A6, 0x644C2632EE4236A1, 0x4BE67BDE4C0165C2, + 0xBA1C385C2784266B, 0xD73C7D1F953C1DF5, 0x124169F630A5D8C7, 0x2B0889D0BD07167E, + 0x684EBD9E1443674F, 0x4545B6EA14F8881A, 0x0155B5D505852928, 0x6CE95E3145A971E5, + 0x09392839B7461114, 0xC7282C716E163D89, 0x99550204A695C1D1, 0xBC3608142C2E4112, + 0x000B8D70D8008C9E, 0xB890E79F987C7BF0, 0x116842C96F74E149, 0x80F50CF9F8A4ACBB, + 0x43564F2CE5CF5A06, 0x2BFABC8688B694B6, 0x5BF3C69332E375B1, 0xE30713E9C31DEF23, + 0xFA8CAA174592FB85, 0xFE8ADF8C34B9318A, 0x3FECC328834779B2, 0xA35A93323CC17467, + 0x0614B42A8A02D28D, 0x44CFBE0C62A7E9E9, 0xC2BB83CB6F101E64, 0xF7C84FE8A3DD520E, + 0xFDD4FFDCB9A716D7, 0xA9328A1C3F9A4065, 0x09D7EA8213D87CA4, 0x991D70D6D87C4017, + 0x64681516F68AAF20, 0x84A57BE98A0A134A, 0xED75B4776BFE341F, 0xF427FB839BA19E3F, + 0x8F923CFF6D1E0122, 0x9C204CDF6FC97414, 0x6C22B172A1B8F79B, 0x4EE0CC7377B69D39, + 0xE284385CE76716EB, 0xF20D06EB02576FA2, 0x718185428C00057D, 0x481B1A866133B2DE, + 0x633F38E4C9837B93, 0x33811F5A03FFD672, 0x660B54CC6DFAC390, 0x875C2304AA09F694, + 0x450E44826F6F0E53, 0xBF167E856C199CC6, 0x3F7BAD20C2D1FF30, 0x4F5E942FED341C6F, + 0x4CB011AF4BB07879, 0x16E19F3A2FA7B4B5, 0xA5679A51047949E0, 0x3116200F66002F92, + 0x1F0FCACE0DF2C93E, 0xD0044C066B0D844F, 0xDFFD591AE50D1D3A, 0x943C4A1080B80A10, + 0x1548D4AC71F1759E, 0x469EFB8CCDB55A3A, 0xE4163013F22B302B, 0x040E8F11C3FC13CB, + 0x6623925B90CD7848, 0x155230A4C0ABF7D4, 0x6E80491EE054726B, 0xAA0385D799A60C5E, + 0x98EA0E2D1484B04B, 0x9A4056841B1DC50C, 0xFEE62D3A9F57E6C0, 0x4F8E8AEC4E325EF8, + 0x5DE83EABF37401F4, 0x08E9642C59D4BC87, 0x1E6F642DF1D90BCC, 0x82AB9BDCCE1C86CA, + 0x81EE4CE18327CE36, 0x93D28C597E17FAFE, 0x8F229DB1E1822975, 0xEC81192225285691, + 0x7F26663D5E235110, 0x3DC4F14C33F2571C, 0x98D0CA19B25989B4, 0xE55FC9248BF198AB, + 0x0E567369076D2B78, 0x9F42EFAB5AC40202, 0x46D2CEBF9971EB91, 0x535B9B74A4B28A80, + 0x40D3BC8321BA0DF7, 0xBCED760516B04B33, 0xA925C65738F95155, 0x6699B1C1DE862758, + 0xD51C21349D6271A7, 0x0A41BC1E74663117, 0x30FB22A2234FF59D, 0x817EF5355BC810E7, + 0x34874F13936F49B2, 0x9C8BB16026212435, 0x84C47661CB407CE6, 0x229FC05824B53275, + 0x4EF6AD4CD5C1E4B2, 0x36D0D5007EED1B6E, 0x3B20B3B4B9752C85, 0x75DB6399E11DEA84, + 0x384FB5EF7C73FF84, 0x9F844688465DF285, 0xFF6DE64A2C9F7F61, 0x0AB4158F625085C4, + 0xCF46546201F9783F, 0x3EB3D955C1333CDC, 0x9D2AAFC41A7F7E66, 0x70A9ED6569B77491, + 0x0D3D824C1006523E, 0xEAA9ACB30D4FCF38, 0x8F46999511FC12D2, 0xEF6ADC9CEE6F5B4D, + 0xE3F1A2D20382C293, 0xE5EA1C60F4E27140, 0xA5DB97F05296CDCA, 0x5B714F13F1C2760C, + 0x3017A629F17A5F65, 0x312F52B723D11821, 0xFCFE1746CF238971, 0xF1CFFFDF5BF7DDE7, + 0x24BBF9A2008E8600, 0xBAEDB7C6D73166B5, 0x4E15A22B89809147, 0x2150BBAF4B097D60, + 0x778E2FC33023DF49, 0x2F3527E6DF19FFCD, 0x5DC5467F4E143CEF, 0xF97470EF249F4071, + 0x17F6BD93E68E0F16, 0x396836882CBB0803, 0x03A202A6725CD948, 0x5D88698F82BFC51A, + 0x329C4F98D6C91F8A, 0x3BD5998A774AFC46, 0x1352021315DC66D8, 0x998874FECDCC8B6D, + 0xF9A0916C69A15D80, 0x81782AB7A92C293D, 0x884A728C28AA83F0, 0xD133CE820D68B5CC, + 0x46CE6CF72CF6B174, 0x425BA8D6E9D7E3C5, 0x0D0C5FCA7ADD4A12, 0x6F769328B79C1913, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, + 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 + }; - ZobristHash ColorToMoveKey = 0x547E5B66033ED5B7; + constexpr std::array CastlingKey { + 0x84894C3406355AF6, 0x23B2609AA96363B5, 0x1407B9F037FA2659, 0x311D16CFDE18E8A4, + 0xC50E55737BF3686D, 0xB37363FC66C458C1, 0x3C7E3E99025D7867, 0x9EB4AB344B612152, + 0x04B0366C313B3145, 0x4017A1F497763418, 0x2E981BB957766475, 0x1591C2F35128B6EA, + 0x30474127CB2126A4, 0xB148E6C764EA59C7, 0x2AE55C34CFD6200A, 0x55105F0DB5470D99 + }; + constexpr std::array EnPassantKey { + 0x33194C80CEF360A3, 0x046733315F90461D, 0x47E1C1ECD246776E, 0x672391FA1A31F640, + 0xBD317AF266C19449, 0x65F986CCEEB2A307, 0xE1693D18964743A5, 0x8B38FE1A898DAB7F, + 0xF31100729C152822, 0xB97BF20F48CB8845, 0x0B4E8C4AB774CCFD, 0x08A3A8EC47E6DF2B, + 0xA5F25C70A3CB266A, 0x8DD120728F515331, 0x9641818A4E8BD430, 0x9C07CA1992710AD9, + 0xA049D726E8DEC2E5, 0x0A98BF3BF685CFD9, 0xE87E541C1716590D, 0x678B5FEB66C935E5, + 0x5866E942B0AAB44B, 0x1A5A4AFCBA74EA8B, 0x907FEFB3C0846671, 0x79B4E20EF103F544, + 0x24C8E81586AA1D43, 0x9AECC3E0328CBEF2, 0x5CB4CBDB85F4AFDA, 0xA84A75E54C6B1934, + 0x8905D39FDE76FB36, 0x80B04D62F4CD609E, 0x789E2FEE5E166808, 0x7C3F7B7CFB524E68, + 0x06FA0E4CE5FCE473, 0xE6F9BFFD7BE76BA9, 0x06BAEB5F7C44EB68, 0x230C09815175FE5C, + 0xFA34CB517E815120, 0xA97B234882B50F6E, 0x881DC23791936B55, 0x9F85A9984E88F529, + 0x9D947A3EAB5AC45F, 0x39BE3836500E49B7, 0x5EEB7B7615FBCC82, 0xC462E5435C55F834, + 0xDB6AD0C98423158A, 0xF152CCB4EDD9F6D6, 0xE3296FADCBCC4F37, 0xD15A38254677FBD8, + 0xB26F391420C549E7, 0x67AAFEA1803F2062, 0x1A169719F32463A5, 0x9EB52458CDAC3FEB, + 0xFDDC31066A06089B, 0xD942A9308D681ADF, 0x50B86613C2A01051, 0x3E59A18DD434AB32, + 0x746040F799E4A16A, 0x42EA892EFE132786, 0x358AF383487CD2FD, 0x3D6F08FD5B37B64F, + 0x11E2A9C48E222AC0, 0xA922B952EC3B2CAF, 0x77BA1125BBD7F1C0, 0x7E7604084149AE16, + 0x0000000000000000 }; -} + constexpr ZobristHash ColorToMoveKey = 0x547E5B66033ED5B7; -constexpr StockDory::Zobrist::ZobristKeyTable ZobristKeyTable; + template + constexpr ZobristHash HashPiece(const ZobristHash hash, const Piece p, const Color c, const Square sq) + { + if (T & ZOBRIST) return hash ^ PieceKey[c][p][sq]; + return hash; + } -template -constexpr inline ZobristHash HashPiece(const ZobristHash hash, const Piece p, const Color c, const Square sq) -{ - if (T & ZOBRIST) return hash ^ ZobristKeyTable.PieceKey[c][p][sq]; - return hash; -} + template + constexpr ZobristHash HashCastling(const ZobristHash hash, const uint8_t castlingRight) + { + if (T & ZOBRIST) return hash ^ CastlingKey[castlingRight]; + return hash; + } -template -constexpr inline ZobristHash HashCastling(const ZobristHash hash, const uint8_t castlingRight) -{ - if (T & ZOBRIST) return hash ^ ZobristKeyTable.CastlingKey[castlingRight]; - return hash; -} + template + constexpr ZobristHash HashEnPassant(const ZobristHash hash, const Square enPassantSquare) + { + if (T & ZOBRIST) return hash ^ EnPassantKey[enPassantSquare]; + return hash; + } -template -constexpr inline ZobristHash HashEnPassant(const ZobristHash hash, const Square enPassantSquare) -{ - if (T & ZOBRIST) return hash ^ ZobristKeyTable.EnPassantKey[enPassantSquare]; - return hash; -} + template + constexpr ZobristHash HashColorFlip(const ZobristHash hash) + { + if (T & ZOBRIST) return hash ^ ColorToMoveKey; + return hash; + } -template -constexpr inline ZobristHash HashColorFlip(const ZobristHash hash) -{ - if (T & ZOBRIST) return hash ^ ZobristKeyTable.ColorToMoveKey; - return hash; -} +} // StockDory::Zobrist #endif //STOCKDORY_ZOBRIST_H - -#pragma clang diagnostic pop \ No newline at end of file diff --git a/src/Backend/Util.h b/src/Backend/Util.h deleted file mode 100644 index 22ca2f4f..00000000 --- a/src/Backend/Util.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright (c) 2023 StockDory authors. See the list of authors for more details. -// Licensed under LGPL-3.0. -// - -#ifndef STOCKDORY_UTIL_H -#define STOCKDORY_UTIL_H - -#include -#include -#include - -#include "Type/Square.h" - -namespace StockDory -{ - - class Util - { - - public: - constexpr static inline Square StringToSquare(const std::string& s) - { - int file = tolower(s[0]) - 97; - int rank = tolower(s[1]) - 49; - - return static_cast(rank * 8 + file); - } - - static inline std::string SquareToString(const Square sq) - { - std::string s; - s += static_cast(tolower(File(sq))); - s += Rank(sq); - - return s; - } - - static inline std::string ToHex(const uint64_t value) - { - std::stringstream stream; - stream << std::setfill('0') << std::setw(sizeof(uint64_t) * 2); - stream << std::uppercase << std::hex << value; - return stream.str(); - } - - }; - -} // StockDory - -#endif //STOCKDORY_UTIL_H diff --git a/src/Engine/EngineParameter.h b/src/Engine/EngineParameter.h index b58a23c7..2aa76b0b 100644 --- a/src/Engine/EngineParameter.h +++ b/src/Engine/EngineParameter.h @@ -6,10 +6,8 @@ #ifndef STOCKDORY_ENGINEPARAMETER_H #define STOCKDORY_ENGINEPARAMETER_H -#include - -#include "EngineEntry.h" -#include "../Backend/TranspositionTable.h" +#include "SearchState.h" +#include "TranspositionTable.h" constexpr int32_t Infinity = 1000000 ; constexpr int32_t Mate = Infinity - 1; @@ -49,17 +47,16 @@ constexpr size_t MB = 1024 * 1024; constexpr uint8_t ReplacementThreshold = 3; -constexpr Move NoMove = Move(); +constexpr auto NoMove = Move(); -constexpr std::array, 5> BestMoveStabilityOptimisationFactor = { - {{250, 100}, - {180, 100}, - {120, 100}, - { 99, 100}, - { 97, 100}} -}; +constexpr std::array, 5> BestMoveStabilityOptimisationFactor = {{ + {250, 100}, + {180, 100}, + {120, 100}, + { 99, 100}, + { 97, 100} +}}; -StockDory::TranspositionTable TTable = - StockDory::TranspositionTable(16 * MB); +inline auto TTable = StockDory::TranspositionTable(16 * MB); #endif //STOCKDORY_ENGINEPARAMETER_H diff --git a/src/Engine/Evaluation.h b/src/Engine/Evaluation.h index 6be9dc96..c1bd9b84 100644 --- a/src/Engine/Evaluation.h +++ b/src/Engine/Evaluation.h @@ -6,10 +6,6 @@ #ifndef STOCKDORY_EVALUATION_H #define STOCKDORY_EVALUATION_H -#include -#include - -#include "../Backend/Type/PieceColor.h" #include "../Backend/Type/Square.h" #include "NetworkArchitecture.h" @@ -21,64 +17,62 @@ namespace StockDory class Evaluation { - private: - static Aurora NN; + static inline Aurora NN = [] -> Aurora + { + MantaRay::BinaryMemoryStream stream (_NeuralNetworkBinaryData, sizeof _NeuralNetworkBinaryData); + return Aurora(stream); + }(); public: - static inline std::string Name() - { - return "Aurora"; - } - - static inline void ResetNetworkState() - { - NN. ResetAccumulator(); - NN.RefreshAccumulator(); - } - - static inline void PreMove() - { - NN.PushAccumulator(); - } - - static inline void PreUndoMove() - { - NN.PullAccumulator(); - } - - static inline void Activate (const Piece piece, const Color color, const Square sq) - { - NN.EfficientlyUpdateAccumulator(piece, color, sq); - } - - static inline void Deactivate(const Piece piece, const Color color, const Square sq) - { - NN.EfficientlyUpdateAccumulator(piece, color, sq); - } - - static inline void Transition(const Piece piece, const Color color, const Square from, const Square to) - { - NN.EfficientlyUpdateAccumulator(piece, color, from, to); - } - - static inline int32_t Evaluate(Color color) - { - return NN.Evaluate(color); - } - - template - static inline int32_t Evaluate() - { - return NN.Evaluate(Color); - } + static std::string Name() + { + return "Aurora"; + } + + static void ResetNetworkState() + { + NN.Reset(); + NN.Refresh(); + } + + [[clang::always_inline]] + static void PreMove() + { + NN.Push(); + } + + [[clang::always_inline]] + static void PreUndoMove() + { + NN.Pop(); + } + + [[clang::always_inline]] + static void Activate(const Piece piece, const Color color, const Square sq) + { + NN.Insert(piece, color, sq); + } + + [[clang::always_inline]] + static void Deactivate(const Piece piece, const Color color, const Square sq) + { + NN.Remove(piece, color, sq); + } + + [[clang::always_inline]] + static void Transition(const Piece piece, const Color color, const Square from, const Square to) + { + NN.Move(piece, color, from, to); + } + + [[clang::always_inline]] + static int32_t Evaluate(const Color color) + { + return NN.Evaluate(color); + } }; } // StockDory -Aurora StockDory::Evaluation::NN = []() { - MantaRay::BinaryMemoryStream stream(_NeuralNetworkBinaryData, _NeuralNetworkBinarySize); - return Aurora (stream); -}(); - -#endif //STOCKDORY_EVALUATION_H \ No newline at end of file +#endif //STOCKDORY_EVALUATION_H diff --git a/src/Engine/LogarithmicReductionTable.h b/src/Engine/LogarithmicReductionTable.h index ba722ccb..7705b098 100644 --- a/src/Engine/LogarithmicReductionTable.h +++ b/src/Engine/LogarithmicReductionTable.h @@ -6,38 +6,28 @@ #ifndef STOCKDORY_LOGARITHMICREDUCTIONTABLE_H #define STOCKDORY_LOGARITHMICREDUCTIONTABLE_H -#include #include +#include +#include #include "EngineParameter.h" namespace StockDory { - class LogarithmicReductionTable + std::array, MaxDepth> LogarithmicReductionTable = + [] -> std::array, MaxDepth> { + std::array, MaxDepth> temp = {}; - private: - static std::array, MaxDepth> Internal; - - public: - static inline int16_t Get(const uint8_t depth, const uint8_t move) - { - return Internal[depth][move]; - } + for (uint8_t depth = 1; depth < MaxDepth; depth++) + for (uint8_t move = 1; move < MaxMove ; move++) + temp[depth] + [move ] = static_cast(std::log(depth) * std::log(move) / 2 - 0.2); - }; + return temp; + }(); } // StockDory -std::array, MaxDepth> StockDory::LogarithmicReductionTable::Internal = []() { - std::array, MaxDepth> temp = {}; - - for (uint8_t depth = 1; depth < MaxDepth; depth++) for (uint8_t move = 1; move < MaxMove; move++) { - temp[depth][move] = static_cast(log(depth) * log(move) / 2 - 0.2); - } - - return temp; -}(); - #endif //STOCKDORY_LOGARITHMICREDUCTIONTABLE_H diff --git a/src/Engine/Move/HistoryTable.h b/src/Engine/Move/HistoryTable.h index a1341324..b183a5d3 100644 --- a/src/Engine/Move/HistoryTable.h +++ b/src/Engine/Move/HistoryTable.h @@ -6,35 +6,15 @@ #ifndef STOCKDORY_HISTORYTABLE_H #define STOCKDORY_HISTORYTABLE_H -#include #include +#include -#include "../../Backend/Type/Move.h" #include "../../Backend/Type/Color.h" namespace StockDory { - class HistoryTable - { - - private: - std::array, 6>, 2> Internal = {}; - - public: - [[nodiscard]] - inline int32_t Get(const Piece piece, const Color color, const Square sq) const - { - return Internal[color][piece][sq]; - } - - [[nodiscard]] - inline int32_t& Get(const Piece piece, const Color color, const Square sq) - { - return Internal[color][piece][sq]; - } - - }; + using HistoryTable = std::array, 6>, 2>; } // StockDory diff --git a/src/Engine/Move/KillerTable.h b/src/Engine/Move/KillerTable.h index 5b9ea899..131146d3 100644 --- a/src/Engine/Move/KillerTable.h +++ b/src/Engine/Move/KillerTable.h @@ -6,8 +6,8 @@ #ifndef STOCKDORY_KILLERTABLE_H #define STOCKDORY_KILLERTABLE_H -#include #include +#include #include "../../Backend/Type/Move.h" @@ -16,38 +16,8 @@ namespace StockDory { - class KillerTable - { - - using KillerGroup = std::array; - - private: - std::array Internal = {}; - - public: - template - [[nodiscard]] - inline Move Get(const uint8_t ply) const - { - static_assert(Type == 1 || Type == 2); - - return Internal[Type - 1][ply]; - } - - template - inline void Set(const uint8_t ply, const Move move) - { - static_assert(Type == 1 || Type == 2); - - Internal[Type - 1][ply] = move; - } - - inline void Reorder(const uint8_t ply) - { - Internal[1][ply] = Internal[0][ply]; - } - - }; + using KillerGroup = std::array; + using KillerTable = std::array; } // StockDory diff --git a/src/Engine/Move/OrderedMoveList.h b/src/Engine/Move/OrderedMoveList.h index b83d64f1..326fb99b 100644 --- a/src/Engine/Move/OrderedMoveList.h +++ b/src/Engine/Move/OrderedMoveList.h @@ -9,10 +9,10 @@ #include #include -#include "../EngineParameter.h" -#include "Policy.h" -#include "KillerTable.h" #include "HistoryTable.h" +#include "KillerTable.h" +#include "Policy.h" +#include "../EngineParameter.h" #include "../../Backend/Move/MoveList.h" #include "../../Backend/Type/Move.h" @@ -24,116 +24,127 @@ namespace StockDory class OrderedMoveList { - using OrderedMove = std::pair; + struct OrderedMove + { - private: - std::array Internal = {}; - uint8_t Size = 0; + int32_t Score; + Move Move ; + + OrderedMove() = default; + + OrderedMove(const int32_t score, const ::Move move) : Score(score), Move(move) {} + + OrderedMove(const OrderedMove& other) : Score(other.Score), Move(other.Move) {} + + }; + + std::array Internal; + uint8_t Size = 0; public: - explicit OrderedMoveList(const Board& board, const uint8_t ply, - const KillerTable& kTable, const HistoryTable& hTable, - const Move ttMove) - { - const Move kOne = kTable.Get<1>(ply); - const Move kTwo = kTable.Get<2>(ply); - - const Policy policy (kOne, kTwo, ttMove); - - const PinBitBoard pin = board.Pin (); - const CheckBitBoard check = board.Check< Opposite(Color)>(); - - if (check.DoubleCheck) { - AddMoveLoop(board, hTable, policy, pin, check); - } else { - AddMoveLoop(board, hTable, policy, pin, check); - AddMoveLoop(board, hTable, policy, pin, check); - AddMoveLoop(board, hTable, policy, pin, check); - AddMoveLoop(board, hTable, policy, pin, check); - AddMoveLoop(board, hTable, policy, pin, check); - AddMoveLoop(board, hTable, policy, pin, check); - } + explicit OrderedMoveList(const Board& board, const uint8_t ply , + const KillerTable& kTable, const HistoryTable& hTable, + const Move ttMove) + { + const Move kOne = kTable[0][ply]; + const Move kTwo = kTable[1][ply]; + + const Policy policy (kOne, kTwo, ttMove); + + const PinBitBoard pin = board.Pin(); + + if (const CheckBitBoard check = board.Check(); check.DoubleCheck) { + AddMoveLoop(board, hTable, policy, pin, check); + } else { + AddMoveLoop(board, hTable, policy, pin, check); + AddMoveLoop(board, hTable, policy, pin, check); + AddMoveLoop(board, hTable, policy, pin, check); + AddMoveLoop(board, hTable, policy, pin, check); + AddMoveLoop(board, hTable, policy, pin, check); + AddMoveLoop(board, hTable, policy, pin, check); } - - template - inline void AddMoveLoop(const Board& board, const HistoryTable& hTable, - const Policy& policy, - const PinBitBoard& pin, const CheckBitBoard& check) - { - BitBoardIterator iterator (board.PieceBoard(Piece)); - - for (Square sq = iterator.Value(); sq != NASQ; sq = iterator.Value()) { - const MoveList moves (board, sq, pin, check); - BitBoardIterator moveIterator = CaptureOnly ? - (Piece == Pawn ? - moves.Mask(~board[NAC] | board.EnPassant()) : - moves.Mask(~board[NAC])).Iterator() : - moves .Iterator() ; - - for (Square m = moveIterator.Value(); m != NASQ; m = moveIterator.Value()) { - if (moves.Promotion(sq)) { - Internal[Size++] = CreateOrdered(board, hTable, policy, sq, m); - Internal[Size++] = CreateOrdered(board, hTable, policy, sq, m); - Internal[Size++] = CreateOrdered(board, hTable, policy, sq, m); - Internal[Size++] = CreateOrdered(board, hTable, policy, sq, m); - } else { - Internal[Size++] = CreateOrdered(board, hTable, policy, sq, m); - } - } + } + + template + void AddMoveLoop(const Board& board, const HistoryTable& hTable, + const Policy& policy, + const PinBitBoard& pin, const CheckBitBoard& check) + { + BitBoardIterator iterator (board.PieceBoard(Piece)); + + for (Square sq = iterator.Value(); sq != NASQ; sq = iterator.Value()) { + const MoveList moves (board, sq, pin, check); + BitBoardIterator moveIterator = + CaptureOnly ? (Piece == Pawn + ? moves.Mask(~board[NAC] | board.EnPassant()) + : moves.Mask(~board[NAC])).Iterator() + : moves.Iterator(); + + for (Square m = moveIterator.Value(); m != NASQ; m = moveIterator.Value()) { + if (moves.Promotion(sq)) { + Internal[Size++] = CreateOrdered(board, hTable, policy, sq, m); + Internal[Size++] = CreateOrdered(board, hTable, policy, sq, m); + Internal[Size++] = CreateOrdered(board, hTable, policy, sq, m); + Internal[Size++] = CreateOrdered(board, hTable, policy, sq, m); + } else + Internal[Size++] = CreateOrdered(board, hTable, policy, sq, m); } } + } private: - template - inline std::pair CreateOrdered(const Board& board, - const HistoryTable& hTable, - const Policy& policy, - const Square from, const Square to) - { - const Move move = Move(from, to, Promotion); - return {policy.template Score(board, hTable, move), move }; - } + // ReSharper disable once CppRedundantElaboratedTypeSpecifier + template + static OrderedMove CreateOrdered(const Board& board , + const HistoryTable& hTable, + const Policy& policy, + const Square from , + const Square to ) + { + const auto move = Move(from, to, Promotion); + return { policy.template Score(board, hTable, move), move }; + } public: - [[nodiscard]] - inline Move operator [](const uint8_t index) - { - assert(index < Size); + [[nodiscard]] + Move operator [](const uint8_t index) + { + assert(index < Size); - SortNext(index); - return Internal[index].second; - } + SortNext(index); + return Internal[index].Move; + } - [[nodiscard]] - inline Move UnsortedAccess(const uint8_t index) const - { - assert(index < Size); + [[nodiscard]] + Move UnsortedAccess(const uint8_t index) const + { + assert(index < Size); - return Internal[index].second; - } + return Internal[index].Move; + } - [[nodiscard]] - inline uint8_t Count() const - { - return Size; - } + [[nodiscard]] + uint8_t Count() const + { + return Size; + } private: - inline void SortNext(const uint8_t sorted) - { - uint8_t index = sorted ; - uint8_t i = sorted + 1; - - while (i < Size) { - if (Internal[i].first > Internal[index].first) index = i; - i++; - } - - const OrderedMove temp = Internal[index]; - Internal[index ] = Internal[sorted]; - Internal[sorted] = temp; + void SortNext(const uint8_t sorted) + { + uint8_t index = sorted; + uint8_t i = sorted + 1; + + while (i < Size) { + if (Internal[i].Score > Internal[index].Score) index = i; + i++; } + const OrderedMove temp = Internal[index]; + Internal[index] = Internal[sorted]; + Internal[sorted] = temp; + } + }; } // StockDory diff --git a/src/Engine/Move/Policy.h b/src/Engine/Move/Policy.h index 73d99cb0..0866205a 100644 --- a/src/Engine/Move/Policy.h +++ b/src/Engine/Move/Policy.h @@ -6,12 +6,11 @@ #ifndef STOCKDORY_POLICY_H #define STOCKDORY_POLICY_H -#include "../../Backend/Type/Move.h" #include "../../Backend/Board.h" +#include "../../Backend/Type/Move.h" -#include "../EngineParameter.h" -#include "../SEE.h" #include "HistoryTable.h" +#include "../SEE.h" namespace StockDory { @@ -20,65 +19,63 @@ namespace StockDory class Policy { - private: - constexpr static std::array, 7> MvvLva = {{ - { 2005, 2004, 2003, 2002, 2001, 2000, 0000 }, - { 3005, 3004, 3003, 3002, 3001, 3000, 0000 }, - { 4005, 4004, 4003, 4002, 4001, 4000, 0000 }, - { 5005, 5004, 5003, 5002, 5001, 5000, 0000 }, - { 6005, 6004, 6003, 6002, 6001, 6000, 0000 }, - { 7005, 7004, 7003, 7002, 7001, 7000, 0000 }, - { 0000, 0000, 0000, 0000, 0000, 0000, 0000 } - }}; + constexpr static std::array, 7> MvvLva = {{ + {2005, 2004, 2003, 2002, 2001, 2000, 0000}, + {3005, 3004, 3003, 3002, 3001, 3000, 0000}, + {4005, 4004, 4003, 4002, 4001, 4000, 0000}, + {5005, 5004, 5003, 5002, 5001, 5000, 0000}, + {6005, 6004, 6003, 6002, 6001, 6000, 0000}, + {7005, 7004, 7003, 7002, 7001, 7000, 0000}, + {0000, 0000, 0000, 0000, 0000, 0000, 0000} + }}; - constexpr static int32_t Priority = 0x7FFFFFFF; + constexpr static int32_t Priority = 0x7FFFFFFF; - constexpr static std::array PromotionPriority = { - 0 , - Priority - 8 + 3, - Priority - 8 + 1, - Priority - 8 + 2, - Priority - 8 + 4 - }; + constexpr static std::array PromotionPriority = { + 0, + Priority - 8 + 3, + Priority - 8 + 1, + Priority - 8 + 2, + Priority - 8 + 4 + }; - Move KillerOne; - Move KillerTwo; + Move KillerOne; + Move KillerTwo; - Move TranspositionTable; + Move TTMove; public: - Policy(const Move kOne, const Move kTwo, const Move tt) - { - KillerOne = kOne ; - KillerTwo = kTwo ; - TranspositionTable = tt ; - } - - template - [[nodiscard]] - inline int32_t Score(const Board& board, const HistoryTable& historyTable, const Move move) const - { - if (move == TranspositionTable) return Priority - 1; - - if (Promotion != NAP) return PromotionPriority[Promotion]; - - if (CaptureOnly || board[move.To()].Piece() != NAP) - return CaptureScore(board, move); - - if (move == KillerOne) return 900000; - if (move == KillerTwo) return 800000; - - return historyTable.Get(Piece, Color, move.To()); - } - - template - [[nodiscard]] - inline int32_t CaptureScore(const Board& board, const Move move) const - { - if (SEE::Accurate(board, move, 0)) - return MvvLva[board[move.To()].Piece()][Piece] * 1000; - else return MvvLva[board[move.To()].Piece()][Piece] * 300 ; - } + Policy(const Move kOne, const Move kTwo, const Move tt) + { + KillerOne = kOne; + KillerTwo = kTwo; + TTMove = tt; + } + + // ReSharper disable once CppRedundantElaboratedTypeSpecifier + template + int32_t Score(const Board& board, const HistoryTable& historyTable, const Move move) const + { + if (move == TTMove) return Priority - 1; + + if (Promotion != NAP) return PromotionPriority[Promotion]; + + if (CaptureOnly || board[move.To()].Piece() != NAP) return CaptureScore(board, move); + + if (move == KillerOne) return 900000; + if (move == KillerTwo) return 800000; + + return historyTable[Color][Piece][move.To()]; + } + + template + static int32_t CaptureScore(const Board& board, const Move move) + { + if (SEE::Accurate(board, move, 0)) + return MvvLva[board[move.To()].Piece()][Piece] * 1000; + + return MvvLva[board[move.To()].Piece()][Piece] * 300 ; + } }; diff --git a/src/Engine/Move/PrincipleVariationTable.h b/src/Engine/Move/PrincipleVariationTable.h index 89161b06..28699740 100644 --- a/src/Engine/Move/PrincipleVariationTable.h +++ b/src/Engine/Move/PrincipleVariationTable.h @@ -6,8 +6,8 @@ #ifndef STOCKDORY_PRINCIPLEVARIATIONTABLE_H #define STOCKDORY_PRINCIPLEVARIATIONTABLE_H -#include #include +#include #include "../../Backend/Type/Move.h" @@ -16,56 +16,18 @@ namespace StockDory { - class PrincipleVariationTable - { - - using Pv = std::array; - using PvRelation = std::pair; - - private: - std::array Internal = {}; - - public: - inline void InitializePly(const uint8_t ply) - { - Internal[ply].first = ply; - } + using PV = std::array; - [[nodiscard]] - inline bool PlyInitialized(const uint8_t currentPly, const uint8_t nextPly) const - { - return nextPly < Internal[currentPly + 1].first; - } - - inline void Insert(const uint8_t ply, const Move move) - { - Internal[ply].second[ply] = move; - } - - inline void Copy(const uint8_t currentPly, const uint8_t nextPly) - { - Internal[currentPly].second[nextPly] = Internal[currentPly + 1].second[nextPly]; - } - - inline void Update(const uint8_t ply) - { - Internal[ply].first = Internal[ply + 1].first; - } - - [[nodiscard]] - inline uint8_t Count() const - { - return Internal[0].first; - } + struct PrincipleVariationEntry + { - [[nodiscard]] - inline Move operator [](const uint8_t ply) const - { - return Internal[0].second[ply]; - } + uint8_t Ply; + PV PV ; }; + using PrincipleVariationTable = std::array; + } // StockDory #endif //STOCKDORY_PRINCIPLEVARIATIONTABLE_H diff --git a/src/Engine/NetworkArchitecture.h b/src/Engine/NetworkArchitecture.h index e8e0ded9..fd94a05d 100644 --- a/src/Engine/NetworkArchitecture.h +++ b/src/Engine/NetworkArchitecture.h @@ -6,14 +6,14 @@ #ifndef STOCKDORY_NETWORKARCHITECTURE_H #define STOCKDORY_NETWORKARCHITECTURE_H -#include -#include +#include +#include // Activation Function: -using CRelu000255 = MantaRay::ClippedReLU; +constexpr auto ClippedReLU = &MantaRay::ClippedReLU::Activate; // Architecture: -using Starshard = MantaRay::PerspectiveNetwork; -using Aurora = MantaRay::PerspectiveNetwork; +using Starshard = MantaRay::Perspective; +using Aurora = MantaRay::Perspective; #endif //STOCKDORY_NETWORKARCHITECTURE_H diff --git a/src/Engine/RepetitionHistory.h b/src/Engine/RepetitionHistory.h index 1fc5de26..ebec107a 100644 --- a/src/Engine/RepetitionHistory.h +++ b/src/Engine/RepetitionHistory.h @@ -17,43 +17,42 @@ namespace StockDory class RepetitionHistory { - private: - std::array Internal = {}; + std::array Internal = {}; - uint16_t CurrentIndex = 0; + uint16_t CurrentIndex = 0; public: - constexpr explicit RepetitionHistory(const ZobristHash hash) - { - Push(hash); - } - - constexpr inline void Push(const ZobristHash hash) - { - Internal[CurrentIndex++] = hash; - } - - constexpr inline void Pull() - { - CurrentIndex--; - } - - [[nodiscard]] - constexpr inline bool Found(const ZobristHash hash, const uint8_t halfMoveCounter) const - { - uint8_t count = 0; - for (uint16_t i = CurrentIndex - 1; i != 0xFFFF; i--) { - if (i < CurrentIndex - 1 - halfMoveCounter) return false; - - if (Internal[i] == hash) { - count++; - if (count > 2) return true; - } + explicit RepetitionHistory(const ZobristHash hash) + { + Push(hash); + } + + void Push(const ZobristHash hash) + { + Internal[CurrentIndex++] = hash; + } + + void Pull() + { + CurrentIndex--; + } + + [[nodiscard]] + bool Found(const ZobristHash hash, const uint8_t halfMoveCounter) const + { + uint8_t count = 0; + for (uint16_t i = CurrentIndex - 1; i != 0xFFFF; i--) { + if (i < CurrentIndex - 1 - halfMoveCounter) return false; + + if (Internal[i] == hash) { + count++; + if (count > 2) return true; } - - return false; } + return false; + } + }; } // StockDory diff --git a/src/Engine/SEE.h b/src/Engine/SEE.h index 28bf3877..8626c66d 100644 --- a/src/Engine/SEE.h +++ b/src/Engine/SEE.h @@ -6,8 +6,8 @@ #ifndef STOCKDORY_SEE_H #define STOCKDORY_SEE_H -#include #include +#include #include "../Backend/Board.h" #include "../Backend/Type/Move.h" @@ -18,78 +18,76 @@ namespace StockDory class SEE { - private: - constexpr static std::array Internal = { - 82, 337, 365, 477, 1025, 30000, 0 - }; + constexpr static std::array Internal = { + 82, 337, 365, 477, 1025, 30000, 0 + }; - static inline bool Unchecked(const Board& board, const Move move) - { - if (move.Promotion() != NAP) return true; + static bool Unchecked(const Board& board, const Move move) + { + if (move.Promotion() != NAP) return true; - const Piece from = board[move.From()].Piece(); - if (from == Pawn && move.To() == board.EnPassantSquare()) return true; + const Piece from = board[move.From()].Piece(); + if (from == Pawn && move.To() == board.EnPassantSquare()) return true; - return from == King && (move.To() == C1 || move.To() == C8 || move.To() == G1 || move.To() == G8); - } + return from == King && (move.To() == C1 || move.To() == C8 || move.To() == G1 || move.To() == G8); + } public: - static inline bool Accurate(const Board& board, const Move move, const int32_t threshold) - { - if (Unchecked(board, move)) return true; + static bool Accurate(const Board& board, const Move move, const int32_t threshold) + { + if (Unchecked(board, move)) return true; - const Square from = move.From(); - const Square to = move.To (); + const Square from = move.From(); + const Square to = move. To(); - int32_t value = Internal[board[to].Piece()] - threshold; - if (value < 0) return false; + int32_t value = Internal[board[to].Piece()] - threshold; + if (value < 0) return false; - value -= Internal[board[from].Piece()]; - if (value >= 0) return true; + value -= Internal[board[from].Piece()]; + if (value >= 0) return true; - const BitBoard diagonal = board.PieceBoard(Bishop) | board.PieceBoard(Bishop) | - board.PieceBoard(Queen ) | board.PieceBoard(Queen ) ; - const BitBoard straight = board.PieceBoard(Rook ) | board.PieceBoard(Rook ) | - board.PieceBoard(Queen ) | board.PieceBoard(Queen ) ; + const BitBoard diagonal = board.PieceBoard(Bishop) | board.PieceBoard(Bishop) | + board.PieceBoard(Queen ) | board.PieceBoard(Queen ) ; + const BitBoard straight = board.PieceBoard(Rook ) | board.PieceBoard(Rook ) | + board.PieceBoard(Queen ) | board.PieceBoard(Queen ) ; - BitBoard occ = ~board[NAC] ^ FromSquare(from); - BitBoard att = board.SquareAttackers(to, occ); + BitBoard occ = ~board[NAC] ^ FromSquare(from); + BitBoard att = board.SquareAttackers(to, occ); - Color ctm = Opposite(board.ColorToMove()); - while (true) { - att &= occ; + Color ctm = Opposite(board.ColorToMove()); + while (true) { + att &= occ; - const BitBoard us = att & board[ctm]; - if (!us) break; + const BitBoard us = att & board[ctm]; + if (!us) break; - Piece piece; - for ( piece = Pawn; piece < King; piece = Next(piece)) - if (us & board.PieceBoard(piece, ctm)) break; + Piece piece; + for (piece = Pawn; piece < King; piece = Next(piece)) if (us & board.PieceBoard(piece, ctm)) break; - ctm = Opposite(ctm); + ctm = Opposite(ctm); - value = -value - 1 - Internal[piece]; - if (value >= 0) { - if (piece == King && (att & board[ctm])) ctm = Opposite(ctm); + value = -value - 1 - Internal[piece]; + if (value >= 0) { + if (piece == King && att & board[ctm]) ctm = Opposite(ctm); - break; - } + break; + } - Set(occ, ToSquare(us & board.PieceBoard(piece, Opposite(ctm)))); + Set(occ, ToSquare(us & board.PieceBoard(piece, Opposite(ctm)))); - if (piece == Pawn || piece == Bishop || piece == Queen) { - const uint32_t idx = BlackMagicFactory::MagicIndex(Bishop, move.To(), occ); - att |= AttackTable::Sliding[idx] & diagonal; - } - if (piece == Rook || piece == Queen) { - const uint32_t idx = BlackMagicFactory::MagicIndex(Rook , move.To(), occ); - att |= AttackTable::Sliding[idx] & straight; - } + if (piece == Pawn || piece == Bishop || piece == Queen) { + const uint32_t idx = BlackMagicFactory::MagicIndex(Bishop, move.To(), occ); + att |= AttackTable::Sliding[idx] & diagonal; + } + if (piece == Rook || piece == Queen) { + const uint32_t idx = BlackMagicFactory::MagicIndex(Rook, move.To(), occ); + att |= AttackTable::Sliding[idx] & straight; } - - return ctm != board.ColorToMove(); } + return ctm != board.ColorToMove(); + } + }; } // StockDory diff --git a/src/Engine/Search.h b/src/Engine/Search.h index 7dbd4453..4b3d3d64 100644 --- a/src/Engine/Search.h +++ b/src/Engine/Search.h @@ -10,16 +10,16 @@ #include "../Backend/Type/Move.h" #include "../Backend/Type/Zobrist.h" -#include "Move/OrderedMoveList.h" -#include "Move/PrincipleVariationTable.h" -#include "Move/KillerTable.h" -#include "Move/HistoryTable.h" -#include "Time/TimeManager.h" -#include "Evaluation.h" #include "EngineParameter.h" +#include "Evaluation.h" #include "LogarithmicReductionTable.h" -#include "SEE.h" #include "RepetitionHistory.h" +#include "SEE.h" +#include "Move/HistoryTable.h" +#include "Move/KillerTable.h" +#include "Move/OrderedMoveList.h" +#include "Move/PrincipleVariationTable.h" +#include "Time/TimeManager.h" namespace StockDory { @@ -28,586 +28,583 @@ namespace StockDory { private: - uint64_t Nodes = 0xFFFFFFFFFFFFFFFF; - uint8_t Depth = MaxDepth / 2; + uint64_t Nodes = 0xFFFFFFFFFFFFFFFF; + uint8_t Depth = MaxDepth / 2; public: - constexpr Limit() = default; + constexpr Limit() = default; - constexpr explicit Limit(const uint64_t nodes) - { - Nodes = nodes; - } + constexpr explicit Limit(const uint64_t nodes) + { + Nodes = nodes; + } - constexpr explicit Limit(const uint8_t depth) - { - Depth = depth; - } + constexpr explicit Limit(const uint8_t depth) + { + Depth = depth; + } - [[nodiscard]] - inline bool BeyondLimit(const uint64_t nodes, const uint8_t depth) const - { - return nodes > Nodes || depth > Depth; - } + bool BeyondLimit(const uint64_t nodes, const uint8_t depth) const + { + return nodes > Nodes || depth > Depth; + } }; - class NoLogger + class DefaultHandler { + using PVEntry = PrincipleVariationEntry; + public: - static void LogDepthIteration([[maybe_unused]] const uint8_t depth, - [[maybe_unused]] const uint8_t selectiveDepth, - [[maybe_unused]] const int32_t evaluation, - [[maybe_unused]] const uint64_t nodes, - [[maybe_unused]] const uint64_t ttNodes, - [[maybe_unused]] const MS time, - [[maybe_unused]] const std::string& pv) {} + static void HandleDepthIteration([[maybe_unused]] const uint8_t depth, + [[maybe_unused]] const uint8_t selectiveDepth, + [[maybe_unused]] const int32_t evaluation, + [[maybe_unused]] const uint64_t nodes, + [[maybe_unused]] const uint64_t ttNodes, + [[maybe_unused]] const MS time, + [[maybe_unused]] const PVEntry& pv) {} - static void LogBestMove([[maybe_unused]] const Move& move) {} + static void HandleBestMove([[maybe_unused]] const Move move) {} }; - template + template class Search { - private: - class SearchStopException : public std::exception {}; + class SearchStopException final : public std::exception {}; - struct SearchStackEntry - { + struct SearchStackEntry + { - int32_t StaticEvaluation = 0; - uint8_t HalfMoveCounter = 0; + int32_t StaticEvaluation = 0; + uint8_t HalfMoveCounter = 0; - }; + }; - Board Board; - TimeControl TC ; + Board Board; + TimeControl TC; - RepetitionHistory Repetition = RepetitionHistory(0); + RepetitionHistory Repetition = RepetitionHistory(0); - PrincipleVariationTable PvTable = PrincipleVariationTable(); - KillerTable KTable = KillerTable (); - HistoryTable HTable = HistoryTable (); + PrincipleVariationTable PVTable = PrincipleVariationTable(); + KillerTable KTable = {}; + HistoryTable HTable = {}; - std::array Stack = {}; + std::array Stack = {}; - uint8_t SelectiveDepth = 0; + uint8_t SelectiveDepth = 0; - uint64_t Nodes = 0; - uint64_t TTNodes = 0; + uint64_t Nodes = 0; + uint64_t TTNodes = 0; - int32_t Evaluation = -Infinity; - Move BestMove = NoMove ; + int32_t Evaluation = -Infinity; + Move BestMove = NoMove; - uint8_t BestMoveStability = 0; + uint8_t BestMoveStability = 0; - bool Stop = false; + bool Stop = false; public: - Search() = default; - - Search(const StockDory::Board board, const StockDory::TimeControl tc, - const RepetitionHistory repetition, const uint8_t hm) - : Board(board), TC(tc), Repetition(repetition) - { - Stack[0].HalfMoveCounter = hm; + Search() = default; + + // ReSharper disable CppPassValueParameterByConstReference + Search(const StockDory::Board board, const TimeControl tc, + const RepetitionHistory repetition, const uint8_t hm) + : Board(board), TC(tc), Repetition(repetition) + { + Stack[0].HalfMoveCounter = hm; + } + // ReSharper restore CppPassValueParameterByConstReference + + void IterativeDeepening(const Limit limit) + { + Board.LoadForEvaluation(); + + TC.Start(); + + int16_t currentDepth = 1; + while (!limit.BeyondLimit(Nodes, currentDepth) && !TC.Finished()) { + const Move lastBestMove = BestMove; + + if (Board.ColorToMove() == White) + Evaluation = Aspiration(currentDepth); + else Evaluation = Aspiration(currentDepth); + + if (Stop) break; + + BestMove = PVTable[0].PV[0]; + + BestMoveStabilityOptimisation(lastBestMove); + + EventHandler::HandleDepthIteration( + currentDepth, + SelectiveDepth, + Evaluation, + Nodes, + TTNodes, + TC.Elapsed(), + PVTable[0] + ); + currentDepth++; } - void IterativeDeepening(const Limit limit) - { - Board.LoadForEvaluation(); - - TC.Start(); - - try { - int16_t currentDepth = 1; - while (!limit.BeyondLimit(Nodes, currentDepth) && !TC.Finished()) { - const Move lastBestMove = BestMove; - - if (Board.ColorToMove() == White) - Evaluation = Aspiration(currentDepth); - else Evaluation = Aspiration(currentDepth); - - BestMove = PvTable[0]; - - BestMoveStabilityOptimisation(lastBestMove); + EventHandler::HandleBestMove(BestMove); + } - Logger::LogDepthIteration(currentDepth, SelectiveDepth, - Evaluation, - Nodes, TTNodes, - TC.Elapsed(), PvLine()); - currentDepth++; - } - } catch (SearchStopException&) {} + [[nodiscard]] + uint64_t NodesSearched() const + { + return Nodes; + } - Logger::LogBestMove(BestMove); - } + void ForceStop() + { + Stop = true; + } - [[nodiscard]] - inline uint64_t NodesSearched() const - { - return Nodes; + private: + void BestMoveStabilityOptimisation(const Move lastBestMove) + { + if (lastBestMove == BestMove) BestMoveStability = std::min(BestMoveStability + 1, 4); + else BestMoveStability = 0; + + TimeManager::Optimize(TC, BestMoveStabilityOptimisationFactor[BestMoveStability]); + } + + template + int32_t Aspiration(const int16_t depth) + { + //region Window Setting + int32_t alpha = -Infinity; + int32_t beta = Infinity; + + if (depth > AspirationDepth) { + alpha = Evaluation - AspirationSize; + beta = Evaluation + AspirationSize; } + //endregion - [[nodiscard]] - inline std::string PvLine() const - { - std::stringstream line; - uint8_t ply = PvTable.Count(); - - for (uint8_t i = 0; i < ply; i++) { - line << PvTable[i].ToString(); - if (i != ply - 1) line << " "; - } + uint8_t research = 0; + while (true) { + //region Out of Time & Force Stop + if (Stop || TC.Finished()) [[unlikely]] { Stop = true; return Draw; } + //endregion - return line.str(); - } + //region Reset Window + if (alpha < -AspirationBound) alpha = -Infinity; + if (beta > AspirationBound) beta = Infinity; + //endregion - inline void ForceStop() - { - Stop = true; - } + // ReSharper disable once CppTooWideScopeInitStatement + const int32_t bestEvaluation = AlphaBeta(0, depth, alpha, beta); - private: - void BestMoveStabilityOptimisation(const Move lastBestMove) - { - if (lastBestMove == BestMove) BestMoveStability = std::min(BestMoveStability + 1, 4); - else BestMoveStability = 0; + //region Modify Window + if (bestEvaluation <= alpha) { + research++; - TimeManager::Optimise(TC, BestMoveStabilityOptimisationFactor[BestMoveStability]); - } + alpha = std::max(alpha - research * research * AspirationDelta, -Infinity); + } else if (bestEvaluation >= beta) { + research++; - template - int32_t Aspiration(const int16_t depth) - { - //region Window Setting - int32_t alpha = -Infinity; - int32_t beta = Infinity; + beta = std::min(beta + research * research * AspirationDelta, Infinity); - if (depth > AspirationDepth) { - alpha = Evaluation - AspirationSize; - beta = Evaluation + AspirationSize; - } + BestMove = PVTable[0].PV[0]; + } else return bestEvaluation; //endregion + } + } - uint8_t research = 0; - while (true) { - //region Out of Time & Force Stop - if (Stop || TC.Finished()) throw SearchStopException(); - //endregion + template + int32_t AlphaBeta(const uint8_t ply, int16_t depth, int32_t alpha, int32_t beta) + { + //region Out of Time & Force Stop + if (Stop || ((Nodes & 4095) == 0 && TC.Finished())) [[unlikely]] { Stop = true; return Draw; } + //endregion - //region Reset Window - if (alpha < -AspirationBound) alpha = -Infinity; - if (beta > AspirationBound) beta = Infinity; - //endregion + constexpr auto OColor = Opposite(Color); - int32_t bestEvaluation = AlphaBeta(0, depth, alpha, beta); + //region PV Table Ply Initialization + PVTable[ply].Ply = ply; + //endregion - //region Modify Window - if (bestEvaluation <= alpha) { - research++; + //region Selected Depth Change + if (Pv) SelectiveDepth = std::max(SelectiveDepth, ply); + //endregion - alpha = std::max(alpha - research * research * AspirationDelta, -Infinity); - } else if (bestEvaluation >= beta ) { - research++; + //region Q Jump + if (depth <= 0) return Q(ply, alpha, beta); + //endregion - beta = std::min(beta + research * research * AspirationDelta, Infinity); + //region Zobrist Hash + const ZobristHash hash = Board.Zobrist(); + //endregion - BestMove = PvTable[0]; - } else return bestEvaluation; - //endregion - } - } + if (!Root) { + //region Draw Detection + if (Stack[ply].HalfMoveCounter >= 100) return Draw; - template - int32_t AlphaBeta(const uint8_t ply, int16_t depth, int32_t alpha, int32_t beta) - { - //region Out of Time & Force Stop - if (Stop || ((Nodes & 4095) == 0 && TC.Finished())) throw SearchStopException(); - //endregion - - constexpr enum Color OColor = Opposite(Color); + if (Repetition.Found(hash, Stack[ply].HalfMoveCounter)) return Draw; - //region PV Table Ply Initialization - PvTable.InitializePly(ply); - //endregion + const uint8_t pieceCount = Count(~Board[NAC]); - //region Selected Depth Change - if (Pv) SelectiveDepth = std::max(SelectiveDepth, ply); - //endregion + if (pieceCount == 2) return Draw; - //region Q Jump - if (depth <= 0) return Q(ply, alpha, beta); + const bool knightLeft = Board.PieceBoard(Knight) | Board.PieceBoard(Knight), + bishopLeft = Board.PieceBoard(Bishop) | Board.PieceBoard(Bishop); + if (pieceCount == 3 && (knightLeft || bishopLeft)) return Draw; //endregion - //region Zobrist Hash - const ZobristHash hash = Board.Zobrist(); + //region Mate Pruning + alpha = std::max(alpha, -Mate + ply ); + beta = std::min(beta , Mate - ply - 1); + if (alpha >= beta) return alpha; //endregion + } - //region Mate Pruning & Draw Detection - if (!Root) { - if (Stack[ply].HalfMoveCounter >= 100) return Draw; + //region Transposition Table Lookup + const SearchState& ttState = TTable[hash]; + Move ttMove = NoMove; + bool ttHit = false; + + if (ttState.Type != Invalid && ttState.Hash == hash) { + ttHit = true; + ttMove = ttState.Move; + + if (!Pv && ttState.Depth >= depth && + (ttState.Type == Exact || + (ttState.Type == BetaCutoff && ttState.Evaluation >= beta ) || + (ttState.Type == AlphaUnchanged && ttState.Evaluation <= alpha))) { + TTNodes++; + return ttState.Evaluation; + } + } + //endregion - if (Repetition.Found(hash, Stack[ply].HalfMoveCounter)) return Draw; + //region Static Evaluation + const int32_t staticEvaluation = ttHit ? StaticEvaluationTT(ttState) : Evaluation::Evaluate(Color); - const uint8_t pieceCount = Count(~Board[NAC]); + Stack[ply].StaticEvaluation = staticEvaluation; + //endregion - if (pieceCount == 2) return Draw; + const bool checked = Board.Checked(); + bool improving = false; - const bool knightLeft = Board.PieceBoard(Knight) | Board.PieceBoard(Knight); - const bool bishopLeft = Board.PieceBoard(Bishop) | Board.PieceBoard(Bishop); - if (pieceCount == 3 && (knightLeft || bishopLeft)) return Draw; + if (!Pv && !checked) { + improving = ply >= 2 && staticEvaluation >= Stack[ply - 2].StaticEvaluation; - alpha = std::max(alpha, -Mate + ply ); - beta = std::min(beta , Mate - ply - 1); - if (alpha >= beta) return alpha; - } + //region Reverse Futility Pruning + if (RFP(depth, staticEvaluation, improving, beta)) return beta; //endregion - //region Transposition Table Lookup - const EngineEntry& storedEntry = TTable[hash]; - Move ttMove = NoMove; - bool ttHit = false ; - - if (storedEntry.Type != Invalid && storedEntry.Hash == hash) { - ttHit = true ; - ttMove = storedEntry.Move; - - if (!Pv && storedEntry.Depth >= depth) { - if (storedEntry.Type == Exact || - (storedEntry.Type == BetaCutoff && storedEntry.Evaluation >= beta ) || - (storedEntry.Type == AlphaUnchanged && storedEntry.Evaluation <= alpha)) { - TTNodes++; - return storedEntry.Evaluation; - } - } - } + //region Razoring + if (depth == 1 && staticEvaluation + RazoringEvaluationThreshold < alpha) + return Q(ply, alpha, beta); //endregion - //region Static Evaluation - const int32_t staticEvaluation = ttHit ? - StaticEvaluationTT(storedEntry) : Evaluation::Evaluate(); - Stack[ply].StaticEvaluation = staticEvaluation; + //region Null Move Pruning + if (NMP(ply, depth, staticEvaluation, beta)) return beta; //endregion + } else if (checked) { + //region Check Extension + depth += CheckExtension; + //endregion + } - const bool checked = Board.Checked(); - bool improving = false; - - if (!Pv && !checked) { - improving = ply >= 2 && staticEvaluation >= Stack[ply - 2].StaticEvaluation; - - //region Reverse Futility Pruning - if (RFP(depth, staticEvaluation, improving, beta)) return beta; - //endregion - - //region Razoring - if (depth == 1 && staticEvaluation + RazoringEvaluationThreshold < alpha) - return Q(ply, alpha, beta); - //endregion + //region IIR + if (depth > IIRDepthThreshold && !ttHit) depth -= IIRDepthReduction; + //endregion + + //region MoveList + using MoveList = OrderedMoveList; + MoveList moves (Board, ply, KTable, HTable, ttMove); + //endregion + + //region Checkmate & Stalemate Detection + if (moves.Count() == 0) return checked ? -Mate + ply : 0; + //endregion + + //region Fail-soft Alpha Beta Negamax + const uint8_t lmpQuietThreshold = LMPQuietThresholdBase + depth * depth; + const bool lmp = !Root && !checked && depth <= LMPDepthThreshold; + const bool lmr = depth >= LMRDepthThreshold && !checked; + const int32_t historyBonus = depth * depth; + const uint8_t historyFactor = std::max(depth / 3, 1); + + SearchState abState { + hash, + -Infinity, + ttMove, + static_cast(depth), + AlphaUnchanged + }; - //region Null Move Pruning - if (NMP(ply, depth, staticEvaluation, beta)) return beta; - //endregion - } else if (checked) { - //region Check Extension - depth += CheckExtension; - //endregion - } + uint8_t quietMoveCount = 0; + for (uint8_t i = 0; i < moves.Count(); i++) { + const Move move = moves[i]; + const bool quiet = Board[move.To()].Piece() == NAP; + quietMoveCount += quiet; - //region IIR - if (depth > IIRDepthThreshold && !ttHit) depth -= IIRDepthReduction; + //region Futility Pruning + if (i > 0 && quiet && staticEvaluation + depth * FutilityDepthFactor <= alpha) + break; //endregion - //region MoveList - using MoveList = StockDory::OrderedMoveList; - MoveList moves (Board, ply, KTable, HTable, ttMove); + //region Late Move Pruning + if (!Pv && lmp && abState.Evaluation > -Infinity && quietMoveCount > lmpQuietThreshold) + break; //endregion - //region Checkmate & Stalemate Detection - if (moves.Count() == 0) return checked ? -Mate + ply : 0; - //endregion + const PreviousState boardState = EngineMove(move, ply, quiet); - //region Fail-soft Alpha Beta Negamax - int32_t bestEvaluation = -Infinity; - Move bestMove = NoMove; - EngineEntryType ttEntryType = AlphaUnchanged; - - const uint8_t lmpQuietThreshold = LMPQuietThresholdBase + depth * depth; - const bool lmp = !Root && !checked && depth <= LMPDepthThreshold; - const bool lmr = depth >= LMRDepthThreshold && !checked; - const int32_t historyBonus = depth * depth; - const uint8_t historyFactor = std::max(depth / 3, 1); - - uint8_t quietMoveCount = 0; - for (uint8_t i = 0; i < moves.Count(); i++) { - const Move move = moves[i]; - const bool quiet = Board[move.To()].Piece() == NAP; - quietMoveCount += quiet; - - //region Futility Pruning - if (i > 0 && quiet && staticEvaluation + depth * FutilityDepthFactor <= alpha) - break; - //endregion + int32_t evaluation = 0; + if (i == 0) + evaluation = -AlphaBeta(ply + 1, depth - 1, -beta, -alpha); + else { + //region Late Move Reduction + if (i >= LMRFullSearchThreshold && lmr) { + int16_t r = LogarithmicReductionTable[depth][i]; - //region Late Move Pruning - if (!Pv && lmp && bestEvaluation > -Infinity && quietMoveCount > lmpQuietThreshold) - break; - //endregion + if (!Pv) r++; - const PreviousState state = EngineMove(move, ply, quiet); + if (!improving) r++; - int32_t evaluation = 0; - if (i == 0) evaluation = -AlphaBeta - (ply + 1, depth - 1, -beta, -alpha); - else { - //region Late Move Reduction - if (i >= LMRFullSearchThreshold && lmr) { - int16_t r = LogarithmicReductionTable::Get(depth, i); + if (Board.Checked()) r--; - if (!Pv) r++; + const int16_t reducedDepth = static_cast(std::max(depth - r, 1)); - if (!improving) r++; + evaluation = + -AlphaBeta(ply + 1, reducedDepth, -alpha - 1, -alpha); + } else evaluation = alpha + 1; + //endregion - if (Board.Checked()) r--; + //region Principal Variation Search + evaluation = PVS(evaluation, ply, depth, alpha, beta); + //endregion + } - const int16_t reducedDepth = static_cast(std::max(depth - r, 1)); + EngineUndoMove(boardState, move); - evaluation = -AlphaBeta - (ply + 1, reducedDepth, -alpha - 1, -alpha); - } else evaluation = alpha + 1; - //endregion + //region Handle Evaluation + if (evaluation <= abState.Evaluation) continue; - //region Principal Variation Search - evaluation = PVS(evaluation, ply, depth, alpha, beta); - //endregion - } + abState.Evaluation = evaluation; - EngineUndoMove(state, move); + if (evaluation <= alpha) continue; - //region Handle Evaluation - if (evaluation <= bestEvaluation) continue; + alpha = evaluation; - bestEvaluation = evaluation; + abState.Type = Exact; + abState.Move = move; - if (evaluation <= alpha ) continue; + // ReSharper disable once CppDFAConstantConditions + if (Pv && !Stop) { + PVTable[ply].PV[ply] = move; - alpha = evaluation; - bestMove = move ; - ttEntryType = Exact ; + for (uint8_t nPly = ply + 1; nPly < PVTable[ply + 1].Ply; nPly++) + PVTable[ply].PV[nPly] = PVTable[ply + 1].PV[nPly]; - if (Pv) { - PvTable.Insert(ply, move); + PVTable[ply].Ply = PVTable[ply + 1].Ply; + } - for (uint8_t nPly = ply + 1; PvTable.PlyInitialized(ply, nPly); nPly++) - PvTable.Copy(ply, nPly); + if (evaluation < beta) continue; - PvTable.Update(ply); + // ReSharper disable once CppDFAConstantConditions + if (quiet && !Stop) { + if (KTable[0][ply] != move) { + KTable[1][ply] = KTable[0][ply]; + KTable[0][ply] = move; } - if (evaluation < beta ) continue; - - if (quiet) { - if (KTable.Get<1>(ply) != move) { - KTable.Reorder(ply); - KTable.Set<1>(ply, move); - } + HTable[Color][Board[move.From()].Piece()][move.To()] += historyBonus + i * historyFactor; - HTable.Get(Board[move.From()].Piece(), Color, move.To()) - += historyBonus + i * historyFactor; - - for (uint8_t q = 1; q < quietMoveCount; q++) { - const Move other = moves.UnsortedAccess(i - q); - HTable.Get(Board[other.From()].Piece(), Color, other.To()) - -= historyBonus + (quietMoveCount - q) * historyFactor; - } + for (uint8_t q = 1; q < quietMoveCount; q++) { + const Move other = moves.UnsortedAccess(i - q); + HTable[Color][Board[other.From()].Piece()][other.To()] -= + historyBonus + (quietMoveCount - q) * historyFactor; } - - ttEntryType = BetaCutoff; - //endregion - break; } - //endregion - //region Transposition Table Insertion - auto entry = EngineEntry { - .Hash = hash, - .Evaluation = bestEvaluation, - .Move = ttEntryType != AlphaUnchanged ? bestMove : ttMove, - .Depth = static_cast(depth), - .Type = ttEntryType - }; - InsertEntry(hash, entry); + abState.Type = BetaCutoff; + break; //endregion - - return bestEvaluation; } + //endregion + + //region Transposition Table Insertion + // ReSharper disable once CppDFAConstantConditions + if (!Stop) InsertEntry(hash, abState); + //endregion + + return abState.Evaluation; + } + + template + int32_t Q(const uint8_t ply, int32_t alpha, const int32_t beta) + { + constexpr auto OColor = Opposite(Color); + + //region Selective Depth Change + if (Pv) SelectiveDepth = std::max(SelectiveDepth, ply); + //endregion + + //region Transposition Table Lookup + else { + const ZobristHash hash = Board.Zobrist(); + const SearchState& ttState = TTable[hash]; + + if (ttState.Hash == hash && + (ttState.Type == Exact || + (ttState.Type == BetaCutoff && ttState.Evaluation >= beta ) || + (ttState.Type == AlphaUnchanged && ttState.Evaluation <= alpha) )) return ttState.Evaluation; + } + //endregion - template - int32_t Q(const uint8_t ply, int32_t alpha, int32_t beta) - { - constexpr enum Color OColor = Opposite(Color); - - //region Selective Depth Change - if (Pv) SelectiveDepth = std::max(SelectiveDepth, ply); - //endregion + //region Static Evaluation + const int32_t staticEvaluation = Evaluation::Evaluate(Color); - //region Transposition Table Lookup - if (!Pv) { - const ZobristHash hash = Board.Zobrist(); - const EngineEntry& entry = TTable[hash]; + if (staticEvaluation >= beta) return beta; + if (staticEvaluation > alpha) alpha = staticEvaluation; + //endregion - if (entry.Hash == hash && - (entry.Type == Exact || - (entry.Type == BetaCutoff && entry.Evaluation >= beta ) || - (entry.Type == AlphaUnchanged && entry.Evaluation <= alpha))) - return entry.Evaluation; - } - //endregion + //region MoveList + using MoveList = OrderedMoveList; + MoveList moves (Board, ply, KTable, HTable, NoMove); + //endregion - //region Static Evaluation - const int32_t staticEvaluation = Evaluation::Evaluate(); + //region Fail-soft Alpha Beta Negamax + int32_t bestEvaluation = staticEvaluation; + for (uint8_t i = 0; i < moves.Count(); i++) { + const Move move = moves[i]; - if (staticEvaluation >= beta) return beta; - if (staticEvaluation > alpha) alpha = staticEvaluation; + //region SEE Pruning + if (!SEE::Accurate(Board, move, 0)) continue; //endregion - //region MoveList - using MoveList = StockDory::OrderedMoveList; - MoveList moves (Board, ply, KTable, HTable, NoMove); - //endregion + const PreviousState state = EngineMove(move, ply); - //region Fail-soft Alpha Beta Negamax - int32_t bestEvaluation = staticEvaluation; - for (uint8_t i = 0; i < moves.Count(); i++) { - const Move move = moves[i]; + const int32_t evaluation = -Q(ply + 1, -beta, -alpha); - //region SEE Pruning - if (!SEE::Accurate(Board, move, 0)) continue; - //endregion + EngineUndoMove(state, move); - const PreviousState state = EngineMove(move, ply); + //region Handle Evaluation + if (evaluation <= bestEvaluation) continue; - int32_t evaluation = -Q(ply + 1, -beta, -alpha); + bestEvaluation = evaluation; - EngineUndoMove(state, move); + if (evaluation <= alpha) continue; - //region Handle Evaluation - if (evaluation <= bestEvaluation) continue; - bestEvaluation = evaluation; + alpha = evaluation; - if (evaluation <= alpha ) continue; - alpha = evaluation; - - if (evaluation >= beta ) break; - //endregion - } + if (evaluation >= beta) break; //endregion - - return bestEvaluation; } + //endregion - template - inline bool NMP(const uint8_t ply, const int16_t depth, const int32_t staticEvaluation, const int32_t beta) - { - if (Root || depth < NullMoveDepth || staticEvaluation < beta) return false; + return bestEvaluation; + } - constexpr enum Color OColor = Opposite(Color); + template + bool NMP(const uint8_t ply, const int16_t depth, const int32_t staticEvaluation, const int32_t beta) + { + if (Root || depth < NullMoveDepth || staticEvaluation < beta) return false; - const auto reductionStep = static_cast(depth / NullMoveDepth); - const auto reductionFactor = static_cast((staticEvaluation - beta) / NullMoveEvaluationMargin); - const auto reduction = static_cast(NullMoveDepth + reductionStep + - std::min(NullMoveDepth, reductionFactor)); + constexpr auto OColor = Opposite(Color); - PreviousStateNull state = Board.Move(); + const auto reductionStep = static_cast(depth / NullMoveDepth); + const auto reductionFactor = static_cast((staticEvaluation - beta) / NullMoveEvaluationMargin); + const auto reduction = static_cast(NullMoveDepth + reductionStep + + std::min(NullMoveDepth, reductionFactor)); - const int32_t evaluation = -AlphaBeta - (ply + 1, depth - reduction, -beta, -beta + 1); + const PreviousStateNull state = Board.Move(); - Board.UndoMove(state); + const int32_t evaluation = -AlphaBeta + (ply + 1, depth - reduction, -beta, -beta + 1); - return evaluation >= beta; - } + Board.UndoMove(state); - template - inline int32_t PVS(int32_t evaluation, const uint8_t ply, const int16_t depth, - const int32_t alpha, const int32_t beta) - { - if (evaluation <= alpha) return evaluation; + return evaluation >= beta; + } - constexpr enum Color OColor = Opposite(Color); + template + int32_t PVS( int32_t evaluation, const uint8_t ply , const int16_t depth, + const int32_t alpha , const int32_t beta) + { + if (evaluation <= alpha) return evaluation; - evaluation = - -AlphaBeta(ply + 1, depth - 1, -alpha - 1, -alpha); + constexpr auto OColor = Opposite(Color); - if (evaluation <= alpha || evaluation >= beta) return evaluation; + evaluation = -AlphaBeta(ply + 1, depth - 1, -alpha - 1, -alpha); - return -AlphaBeta(ply + 1, depth - 1, -beta , -alpha); - } + if (evaluation <= alpha || evaluation >= beta) return evaluation; - template - inline PreviousState EngineMove(const Move move, const uint8_t ply, const bool quiet = false) - { - constexpr MoveType MT = NNUE | ZOBRIST; + return -AlphaBeta(ply + 1, depth - 1, -beta, -alpha); + } - if (!quiet || Board[move.From()].Piece() == Pawn) - Stack[ply + 1].HalfMoveCounter = 0; - else Stack[ply + 1].HalfMoveCounter = Stack[ply].HalfMoveCounter + 1; + template + PreviousState EngineMove(const Move move, const uint8_t ply, const bool quiet = false) + { + constexpr MoveType MT = NNUE | ZOBRIST; - const PreviousState state = Board.Move(move.From(), move.To(), move.Promotion()); - Nodes++; + if (!quiet || Board[move.From()].Piece() == Pawn) + Stack[ply + 1].HalfMoveCounter = 0; + else Stack[ply + 1].HalfMoveCounter = Stack[ply].HalfMoveCounter + 1; - const ZobristHash hash = Board.Zobrist(); + const PreviousState state = Board.Move(move.From(), move.To(), move.Promotion()); + Nodes++; - TTable.Prefetch(hash); + const ZobristHash hash = Board.Zobrist(); - if (UpdateHistory) Repetition.Push(hash); + TTable.Prefetch(hash); - return state; - } + if (UpdateHistory) Repetition.Push(hash); - template - inline void EngineUndoMove(const PreviousState state, const Move move) - { - constexpr MoveType MT = NNUE | ZOBRIST; + return state; + } - Board.UndoMove(state, move.From(), move.To()); + template + void EngineUndoMove(const PreviousState state, const Move move) + { + constexpr MoveType MT = NNUE | ZOBRIST; - if (UpdateHistory) Repetition.Pull(); - } + Board.UndoMove(state, move.From(), move.To()); - static inline bool RFP(const int16_t depth, const int32_t staticEvaluation, - const bool improving, const int32_t beta) - { - return depth < ReverseFutilityDepthThreshold && - abs(beta) < Mate && - staticEvaluation - ReverseFutilityD * depth + improving * ReverseFutilityI >= beta; - } + if (UpdateHistory) Repetition.Pull(); + } - static inline void InsertEntry(const ZobristHash hash, const EngineEntry& entry) - { - const EngineEntry& old = TTable[hash]; - if (entry.Type == Exact || entry.Hash != old.Hash || - (old .Type == AlphaUnchanged && - entry.Type == BetaCutoff ) || - entry.Depth > old.Depth - ReplacementThreshold) - TTable[hash] = entry; - } + static bool RFP(const int16_t depth, const int32_t staticEvaluation, + const bool improving, const int32_t beta) + { + return depth < ReverseFutilityDepthThreshold && + abs(beta) < Mate && + staticEvaluation - ReverseFutilityD * depth + improving * ReverseFutilityI >= beta; + } - template - static inline int32_t StaticEvaluationTT(const EngineEntry& entry) - { - if (entry.Type == Exact) return entry.Evaluation; + static void InsertEntry(const ZobristHash hash, const SearchState& entry) + { + // ReSharper disable once CppTooWideScopeInitStatement + const SearchState& old = TTable[hash]; + if (entry.Type == Exact || entry.Hash != old.Hash || + (old. Type == AlphaUnchanged && + entry.Type == BetaCutoff ) || + entry.Depth > old.Depth - ReplacementThreshold) + TTable[hash] = entry; + } - const int32_t staticEvaluation = Evaluation::Evaluate(); + template + static int32_t StaticEvaluationTT(const SearchState& entry) + { + if (entry.Type == Exact) return entry.Evaluation; - if ((staticEvaluation > entry.Evaluation && entry.Type == BetaCutoff ) || - (staticEvaluation < entry.Evaluation && entry.Type == AlphaUnchanged)) return staticEvaluation; + // ReSharper disable once CppTooWideScopeInitStatement + const int32_t staticEvaluation = Evaluation::Evaluate(Color); - return entry.Evaluation; - } + if ((staticEvaluation > entry.Evaluation && entry.Type == BetaCutoff ) || + (staticEvaluation < entry.Evaluation && entry.Type == AlphaUnchanged)) return staticEvaluation; + + return entry.Evaluation; + } }; diff --git a/src/Engine/EngineEntry.h b/src/Engine/SearchState.h similarity index 71% rename from src/Engine/EngineEntry.h rename to src/Engine/SearchState.h index 0fab4195..70827ab0 100644 --- a/src/Engine/EngineEntry.h +++ b/src/Engine/SearchState.h @@ -3,16 +3,16 @@ // Licensed under LGPL-3.0. // -#ifndef STOCKDORY_ENGINEENTRY_H -#define STOCKDORY_ENGINEENTRY_H +#ifndef STOCKDORY_SEARCHSTATE_H +#define STOCKDORY_SEARCHSTATE_H -#include "../Backend/Type/Zobrist.h" #include "../Backend/Type/Move.h" +#include "../Backend/Type/Zobrist.h" namespace StockDory { - enum EngineEntryType : uint8_t + enum SearchStateType : uint8_t { Exact, @@ -22,17 +22,17 @@ namespace StockDory }; - struct EngineEntry + struct SearchState { ZobristHash Hash = 0; int32_t Evaluation = 0; Move Move = ::Move(); uint8_t Depth = 0; - EngineEntryType Type = Invalid; + SearchStateType Type = Invalid; }; } // StockDory -#endif //STOCKDORY_ENGINEENTRY_H +#endif //STOCKDORY_SEARCHSTATE_H diff --git a/src/Engine/Time/TimeControl.h b/src/Engine/Time/TimeControl.h index aab7717e..c578b210 100644 --- a/src/Engine/Time/TimeControl.h +++ b/src/Engine/Time/TimeControl.h @@ -18,62 +18,62 @@ namespace StockDory { public: - constexpr static MS Zero = MS(0); + constexpr static auto Zero = MS(0); private: - TP Origin; + TP Origin; - MS OptimalTime = Zero; - MS ActualTime = Zero; + MS OptimalTime = Zero; + MS ActualTime = Zero; - bool Optimizable = false; + bool Optimizable = false; public: - TimeControl() = default; - - explicit TimeControl(const uint64_t optimal, const uint64_t actual, const bool optimizable = false) - : Origin(std::chrono::steady_clock::now()), - OptimalTime(MS(optimal)), - ActualTime(MS(actual )), - Optimizable(optimizable) {} - - void Start() - { - Origin = std::chrono::steady_clock::now(); - } - - [[nodiscard]] - bool CanBeOptimised() const - { - return Optimizable; - } - - [[nodiscard]] - uint64_t GetOptimal() const - { - return OptimalTime.count(); - } - - void SetOptimal(const uint64_t time) - { - const uint64_t adjustedTime = std::min(time, ActualTime.count()); - OptimalTime = MS(adjustedTime); - } - - template - [[nodiscard]] - inline bool Finished() const - { - if (ActualTime == Zero) return false; - - return Elapsed() > (Hard ? ActualTime : OptimalTime); - } - - [[nodiscard]] - inline MS Elapsed() const - { - return std::chrono::duration_cast(std::chrono::steady_clock::now() - Origin); - } + TimeControl() = default; + + TimeControl(const uint64_t optimal, const uint64_t actual, const bool optimizable = false) + : Origin(std::chrono::steady_clock::now()), + OptimalTime(MS(optimal)), + ActualTime(MS( actual)), + Optimizable(optimizable) {} + + void Start() + { + Origin = std::chrono::steady_clock::now(); + } + + [[nodiscard]] + bool CanBeOptimised() const + { + return Optimizable; + } + + [[nodiscard]] + uint64_t GetOptimal() const + { + return OptimalTime.count(); + } + + void SetOptimal(const uint64_t time) + { + const uint64_t adjustedTime = std::min(time, ActualTime.count()); + OptimalTime = MS(adjustedTime); + } + + template + [[nodiscard]] + bool Finished() const + { + if (ActualTime == Zero) return false; + + return Elapsed() > (Hard ? ActualTime : OptimalTime); + } + + [[nodiscard]] + MS Elapsed() const + { + return std::chrono::duration_cast(std::chrono::steady_clock::now() - Origin); + } }; diff --git a/src/Engine/Time/TimeManager.h b/src/Engine/Time/TimeManager.h index f883d8f0..4c3ce3b8 100644 --- a/src/Engine/Time/TimeManager.h +++ b/src/Engine/Time/TimeManager.h @@ -28,73 +28,67 @@ namespace StockDory class TimeManager { - private: - constexpr static uint8_t TimePartition = 20; - constexpr static uint8_t TimeOverhead = 10; - - constexpr static uint8_t IncrementPartitionNumerator = 3; - constexpr static uint8_t IncrementPartitionDenominator = 4; + constexpr static uint8_t TimePartition = 20; + constexpr static uint8_t TimeOverhead = 10; - constexpr static uint64_t MoveInstantTime = 500; + constexpr static uint8_t IncrementPartitionNumerator = 3; + constexpr static uint8_t IncrementPartitionDenominator = 4; - constexpr static KillerTable DummyKTable; - constexpr static HistoryTable DummyHTable; + constexpr static uint64_t MoveInstantTime = 500; public: - static TimeControl Default() - { - return {}; - } - - static TimeControl Fixed(const uint64_t time) - { - return TimeControl(time, time); - } + static TimeControl Default() + { + return {}; + } - static TimeControl Optimal(const Board& board, const TimeData& data) - { - const Color color = board.ColorToMove(); + static TimeControl Fixed(const uint64_t time) + { + return TimeControl(time, time); + } - const uint64_t time = color == White ? data.WhiteTime : data.BlackTime ; - const uint64_t inc = color == White ? data.WhiteIncrement : data.BlackIncrement; + static TimeControl Optimal(const Board& board, const TimeData& data) + { + const Color color = board.ColorToMove(); - uint64_t actual = time / TimePartition; + const uint64_t time = color == White ? data.WhiteTime : data.BlackTime ; + const uint64_t inc = color == White ? data.WhiteIncrement : data.BlackIncrement; - actual = data.MovesToGo > 0 ? std::max(actual, time / data.MovesToGo) : actual; + uint64_t actual = time / TimePartition; - actual += inc * IncrementPartitionNumerator / IncrementPartitionDenominator; + actual = data.MovesToGo > 0 ? std::max(actual, time / data.MovesToGo) : actual; - actual = MoveCountAdjustment(board, actual); + actual += inc * IncrementPartitionNumerator / IncrementPartitionDenominator; - actual -= TimeOverhead; + actual = MoveCountAdjustment(board, actual); - uint64_t optimal = actual; + actual -= TimeOverhead; - return TimeControl(optimal, actual, true); - } + return TimeControl(actual, actual, true); + } - static void Optimise(TimeControl& control, const std::pair optimisationFactor) - { - if (!control.CanBeOptimised()) return; + static void Optimize(TimeControl& control, const std::pair& optimizationFactor) + { + if (!control.CanBeOptimised()) return; - const uint64_t time = control.GetOptimal(); + const uint64_t time = control.GetOptimal(); - control.SetOptimal(time * optimisationFactor.first / optimisationFactor.second); - } + control.SetOptimal(time * optimizationFactor.first / optimizationFactor.second); + } private: - static uint64_t MoveCountAdjustment(const Board& board, const uint64_t actual) - { - uint8_t moveCount = board.ColorToMove() == White ? - OrderedMoveList( - board, 0, DummyKTable, DummyHTable, NoMove - ).Count() : - OrderedMoveList( - board, 0, DummyKTable, DummyHTable, NoMove - ).Count() ; - - return moveCount == 1 ? std::min(MoveInstantTime, actual) : actual; - } + static uint64_t MoveCountAdjustment(const Board& board, const uint64_t actual) + { + const uint8_t moveCount = board.ColorToMove() == White + ? OrderedMoveList( + board, 0, {}, {}, NoMove + ).Count() + : OrderedMoveList( + board, 0, {}, {}, NoMove + ).Count(); + + return moveCount == 1 ? std::min(MoveInstantTime, actual) : actual; + } }; diff --git a/src/Engine/TranspositionTable.h b/src/Engine/TranspositionTable.h new file mode 100644 index 00000000..b268b4f8 --- /dev/null +++ b/src/Engine/TranspositionTable.h @@ -0,0 +1,78 @@ +// +// Copyright (c) 2023 StockDory authors. See the list of authors for more details. +// Licensed under LGPL-3.0. +// + +#ifndef STOCKDORY_TRANSPOSITIONTABLE_H +#define STOCKDORY_TRANSPOSITIONTABLE_H + +#include + +#ifdef __x86_64__ +#include +#endif + +#include "../Backend/Type/Zobrist.h" + +#include "../External/fastrange.h" + +namespace StockDory +{ + + template + class TranspositionTable + { + + std::vector Internal; + size_t Count = 0; + + public: + explicit TranspositionTable(const size_t bytes) + { + Resize(bytes); + } + + void Resize(const size_t bytes) + { + Count = bytes / sizeof(T); + + Clear(); + } + + void Clear() + { + Internal = std::vector(Count); + } + + T& operator [](const ZobristHash hash) + { + return Internal[fastrange64(hash, Count)]; + } + + const T& operator [](const ZobristHash hash) const + { + return Internal[fastrange64(hash, Count)]; + } + + void Prefetch(const ZobristHash hash) const + { +#ifdef __x86_64__ + _mm_prefetch(reinterpret_cast(&Internal[fastrange64(hash, Count)]), _MM_HINT_T0); +#else +#ifdef __aarch64__ + __builtin_prefetch(reinterpret_cast(&Internal[fastrange64(hash, Count)]), 0, 3); +#endif +#endif + } + + [[nodiscard]] + size_t Size() const + { + return Internal.size(); + } + + }; + +} // StockDory + +#endif //STOCKDORY_TRANSPOSITIONTABLE_H diff --git a/src/External/thread_pool.hpp b/src/External/thread_pool.hpp deleted file mode 100644 index cb6fe084..00000000 --- a/src/External/thread_pool.hpp +++ /dev/null @@ -1,819 +0,0 @@ -#pragma once - -/** - * @file BS_thread_pool.hpp - * @author Barak Shoshany (baraksh@gmail.com) (http://baraksh.com) - * @version 3.5.0 - * @date 2023-05-25 - * @copyright Copyright (c) 2023 Barak Shoshany. Licensed under the MIT license. If you found this project useful, please consider starring it on GitHub! If you use this library in software of any kind, please provide a link to the GitHub repository https://github.com/bshoshany/thread-pool in the source code and documentation. If you use this library in published research, please cite it as follows: Barak Shoshany, "A C++17 Thread Pool for High-Performance Scientific Computing", doi:10.5281/zenodo.4742687, arXiv:2105.00613 (May 2021) - * - * @brief BS::thread_pool: a fast, lightweight, and easy-to-use C++17 thread pool library. This header file contains the entire library, including the main BS::thread_pool class and the helper classes BS::multi_future, BS::blocks, BS:synced_stream, and BS::timer. - */ - -#define BS_THREAD_POOL_VERSION "v3.5.0 (2023-05-25)" - -#include // std::chrono -#include // std::condition_variable -#include // std::current_exception -#include // std::bind, std::function, std::invoke -#include // std::future, std::promise -#include // std::cout, std::endl, std::flush, std::ostream -#include // std::make_shared, std::make_unique, std::shared_ptr, std::unique_ptr -#include // std::mutex, std::scoped_lock, std::unique_lock -#include // std::queue -#include // std::thread -#include // std::common_type_t, std::conditional_t, std::decay_t, std::invoke_result_t, std::is_void_v -#include // std::forward, std::move, std::swap -#include // std::vector - -namespace BS -{ -/** - * @brief A convenient shorthand for the type of std::thread::hardware_concurrency(). Should evaluate to unsigned int. - */ -using concurrency_t = std::invoke_result_t; - -// ============================================================================================= // -// Begin class multi_future // - -/** - * @brief A helper class to facilitate waiting for and/or getting the results of multiple futures at once. - * - * @tparam T The return type of the futures. - */ -template -class [[nodiscard]] multi_future -{ -public: - /** - * @brief Construct a multi_future object with the given number of futures. - * - * @param num_futures_ The desired number of futures to store. - */ - multi_future(const size_t num_futures_ = 0) : futures(num_futures_) {} - - /** - * @brief Get the results from all the futures stored in this multi_future object, rethrowing any stored exceptions. - * - * @return If the futures return void, this function returns void as well. Otherwise, it returns a vector containing the results. - */ - [[nodiscard]] std::conditional_t, void, std::vector> get() - { - if constexpr (std::is_void_v) - { - for (size_t i = 0; i < futures.size(); ++i) - futures[i].get(); - return; - } - else - { - std::vector results(futures.size()); - for (size_t i = 0; i < futures.size(); ++i) - results[i] = futures[i].get(); - return results; - } - } - - /** - * @brief Get a reference to one of the futures stored in this multi_future object. - * - * @param i The index of the desired future. - * @return The future. - */ - [[nodiscard]] std::future& operator[](const size_t i) - { - return futures[i]; - } - - /** - * @brief Append a future to this multi_future object. - * - * @param future The future to append. - */ - void push_back(std::future future) - { - futures.push_back(std::move(future)); - } - - /** - * @brief Get the number of futures stored in this multi_future object. - * - * @return The number of futures. - */ - [[nodiscard]] size_t size() const - { - return futures.size(); - } - - /** - * @brief Wait for all the futures stored in this multi_future object. - */ - void wait() const - { - for (size_t i = 0; i < futures.size(); ++i) - futures[i].wait(); - } - -private: - /** - * @brief A vector to store the futures. - */ - std::vector> futures; -}; - -// End class multi_future // -// ============================================================================================= // - -// ============================================================================================= // -// Begin class blocks // - -/** - * @brief A helper class to divide a range into blocks. Used by parallelize_loop() and push_loop(). - * - * @tparam T1 The type of the first index in the range. Should be a signed or unsigned integer. - * @tparam T2 The type of the index after the last index in the range. Should be a signed or unsigned integer. If T1 is not the same as T2, a common type will be automatically inferred. - * @tparam T The common type of T1 and T2. - */ -template > -class [[nodiscard]] blocks -{ -public: - /** - * @brief Construct a blocks object with the given specifications. - * - * @param first_index_ The first index in the range. - * @param index_after_last_ The index after the last index in the range. - * @param num_blocks_ The desired number of blocks to divide the range into. - */ - blocks(const T1 first_index_, const T2 index_after_last_, const size_t num_blocks_) : first_index(static_cast(first_index_)), index_after_last(static_cast(index_after_last_)), num_blocks(num_blocks_) - { - if (index_after_last < first_index) - std::swap(index_after_last, first_index); - total_size = static_cast(index_after_last - first_index); - block_size = static_cast(total_size / num_blocks); - if (block_size == 0) - { - block_size = 1; - num_blocks = (total_size > 1) ? total_size : 1; - } - } - - /** - * @brief Get the first index of a block. - * - * @param i The block number. - * @return The first index. - */ - [[nodiscard]] T start(const size_t i) const - { - return static_cast(i * block_size) + first_index; - } - - /** - * @brief Get the index after the last index of a block. - * - * @param i The block number. - * @return The index after the last index. - */ - [[nodiscard]] T end(const size_t i) const - { - return (i == num_blocks - 1) ? index_after_last : (static_cast((i + 1) * block_size) + first_index); - } - - /** - * @brief Get the number of blocks. Note that this may be different than the desired number of blocks that was passed to the constructor. - * - * @return The number of blocks. - */ - [[nodiscard]] size_t get_num_blocks() const - { - return num_blocks; - } - - /** - * @brief Get the total number of indices in the range. - * - * @return The total number of indices. - */ - [[nodiscard]] size_t get_total_size() const - { - return total_size; - } - -private: - /** - * @brief The size of each block (except possibly the last block). - */ - size_t block_size = 0; - - /** - * @brief The first index in the range. - */ - T first_index = 0; - - /** - * @brief The index after the last index in the range. - */ - T index_after_last = 0; - - /** - * @brief The number of blocks. - */ - size_t num_blocks = 0; - - /** - * @brief The total number of indices in the range. - */ - size_t total_size = 0; -}; - -// End class blocks // -// ============================================================================================= // - -// ============================================================================================= // -// Begin class thread_pool // - -/** - * @brief A fast, lightweight, and easy-to-use C++17 thread pool class. - */ -class [[nodiscard]] thread_pool -{ -public: - // ============================ - // Constructors and destructors - // ============================ - - /** - * @brief Construct a new thread pool. - * - * @param thread_count_ The number of threads to use. The default value is the total number of hardware threads available, as reported by the implementation. This is usually determined by the number of cores in the CPU. If a core is hyperthreaded, it will count as two threads. - */ - thread_pool(const concurrency_t thread_count_ = 0) : thread_count(determine_thread_count(thread_count_)), threads(std::make_unique(determine_thread_count(thread_count_))) - { - create_threads(); - } - - /** - * @brief Destruct the thread pool. Waits for all tasks to complete, then destroys all threads. Note that if the pool is paused, then any tasks still in the queue will never be executed. - */ - ~thread_pool() - { - wait_for_tasks(); - destroy_threads(); - } - - // ======================= - // Public member functions - // ======================= - - /** - * @brief Get the number of tasks currently waiting in the queue to be executed by the threads. - * - * @return The number of queued tasks. - */ - [[nodiscard]] size_t get_tasks_queued() const - { - const std::scoped_lock tasks_lock(tasks_mutex); - return tasks.size(); - } - - /** - * @brief Get the number of tasks currently being executed by the threads. - * - * @return The number of running tasks. - */ - [[nodiscard]] size_t get_tasks_running() const - { - const std::scoped_lock tasks_lock(tasks_mutex); - return tasks_running; - } - - /** - * @brief Get the total number of unfinished tasks: either still waiting in the queue, or running in a thread. Note that get_tasks_total() == get_tasks_queued() + get_tasks_running(). - * - * @return The total number of tasks. - */ - [[nodiscard]] size_t get_tasks_total() const - { - const std::scoped_lock tasks_lock(tasks_mutex); - return tasks_running + tasks.size(); - } - - /** - * @brief Get the number of threads in the pool. - * - * @return The number of threads. - */ - [[nodiscard]] concurrency_t get_thread_count() const - { - return thread_count; - } - - /** - * @brief Check whether the pool is currently paused. - * - * @return true if the pool is paused, false if it is not paused. - */ - [[nodiscard]] bool is_paused() const - { - const std::scoped_lock tasks_lock(tasks_mutex); - return paused; - } - - /** - * @brief Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue. Returns a multi_future object that contains the futures for all of the blocks. - * - * @tparam F The type of the function to loop through. - * @tparam T1 The type of the first index in the loop. Should be a signed or unsigned integer. - * @tparam T2 The type of the index after the last index in the loop. Should be a signed or unsigned integer. If T1 is not the same as T2, a common type will be automatically inferred. - * @tparam T The common type of T1 and T2. - * @tparam R The return value of the loop function F (can be void). - * @param first_index The first index in the loop. - * @param index_after_last The index after the last index in the loop. The loop will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to "for (T i = first_index; i < index_after_last; ++i)". Note that if index_after_last == first_index, no blocks will be submitted. - * @param loop The function to loop through. Will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. loop(start, end) should typically involve a loop of the form "for (T i = start; i < end; ++i)". - * @param num_blocks The maximum number of blocks to split the loop into. The default is to use the number of threads in the pool. - * @return A multi_future object that can be used to wait for all the blocks to finish. If the loop function returns a value, the multi_future object can also be used to obtain the values returned by each block. - */ - template , typename R = std::invoke_result_t, T, T>> - [[nodiscard]] multi_future parallelize_loop(const T1 first_index, const T2 index_after_last, F&& loop, const size_t num_blocks = 0) - { - blocks blks(first_index, index_after_last, num_blocks ? num_blocks : thread_count); - if (blks.get_total_size() > 0) - { - multi_future mf(blks.get_num_blocks()); - for (size_t i = 0; i < blks.get_num_blocks(); ++i) - mf[i] = submit(std::forward(loop), blks.start(i), blks.end(i)); - return mf; - } - else - { - return multi_future(); - } - } - - /** - * @brief Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue. Returns a multi_future object that contains the futures for all of the blocks. This overload is used for the special case where the first index is 0. - * - * @tparam F The type of the function to loop through. - * @tparam T The type of the loop indices. Should be a signed or unsigned integer. - * @tparam R The return value of the loop function F (can be void). - * @param index_after_last The index after the last index in the loop. The loop will iterate from 0 to (index_after_last - 1) inclusive. In other words, it will be equivalent to "for (T i = 0; i < index_after_last; ++i)". Note that if index_after_last == 0, no blocks will be submitted. - * @param loop The function to loop through. Will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. loop(start, end) should typically involve a loop of the form "for (T i = start; i < end; ++i)". - * @param num_blocks The maximum number of blocks to split the loop into. The default is to use the number of threads in the pool. - * @return A multi_future object that can be used to wait for all the blocks to finish. If the loop function returns a value, the multi_future object can also be used to obtain the values returned by each block. - */ - template , T, T>> - [[nodiscard]] multi_future parallelize_loop(const T index_after_last, F&& loop, const size_t num_blocks = 0) - { - return parallelize_loop(0, index_after_last, std::forward(loop), num_blocks); - } - - /** - * @brief Pause the pool. The workers will temporarily stop retrieving new tasks out of the queue, although any tasks already executed will keep running until they are finished. - */ - void pause() - { - const std::scoped_lock tasks_lock(tasks_mutex); - paused = true; - } - - /** - * @brief Purge all the tasks waiting in the queue. Tasks that are currently running will not be affected, but any tasks still waiting in the queue will be discarded, and will never be executed by the threads. Please note that there is no way to restore the purged tasks. - */ - void purge() - { - const std::scoped_lock tasks_lock(tasks_mutex); - while (!tasks.empty()) - tasks.pop(); - } - - /** - * @brief Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue. Does not return a multi_future, so the user must use wait_for_tasks() or some other method to ensure that the loop finishes executing, otherwise bad things will happen. - * - * @tparam F The type of the function to loop through. - * @tparam T1 The type of the first index in the loop. Should be a signed or unsigned integer. - * @tparam T2 The type of the index after the last index in the loop. Should be a signed or unsigned integer. If T1 is not the same as T2, a common type will be automatically inferred. - * @tparam T The common type of T1 and T2. - * @param first_index The first index in the loop. - * @param index_after_last The index after the last index in the loop. The loop will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to "for (T i = first_index; i < index_after_last; ++i)". Note that if index_after_last == first_index, no blocks will be submitted. - * @param loop The function to loop through. Will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. loop(start, end) should typically involve a loop of the form "for (T i = start; i < end; ++i)". - * @param num_blocks The maximum number of blocks to split the loop into. The default is to use the number of threads in the pool. - */ - template > - void push_loop(const T1 first_index, const T2 index_after_last, F&& loop, const size_t num_blocks = 0) - { - blocks blks(first_index, index_after_last, num_blocks ? num_blocks : thread_count); - if (blks.get_total_size() > 0) - { - for (size_t i = 0; i < blks.get_num_blocks(); ++i) - push_task(std::forward(loop), blks.start(i), blks.end(i)); - } - } - - /** - * @brief Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue. Does not return a multi_future, so the user must use wait_for_tasks() or some other method to ensure that the loop finishes executing, otherwise bad things will happen. This overload is used for the special case where the first index is 0. - * - * @tparam F The type of the function to loop through. - * @tparam T The type of the loop indices. Should be a signed or unsigned integer. - * @param index_after_last The index after the last index in the loop. The loop will iterate from 0 to (index_after_last - 1) inclusive. In other words, it will be equivalent to "for (T i = 0; i < index_after_last; ++i)". Note that if index_after_last == 0, no blocks will be submitted. - * @param loop The function to loop through. Will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. loop(start, end) should typically involve a loop of the form "for (T i = start; i < end; ++i)". - * @param num_blocks The maximum number of blocks to split the loop into. The default is to use the number of threads in the pool. - */ - template - void push_loop(const T index_after_last, F&& loop, const size_t num_blocks = 0) - { - push_loop(0, index_after_last, std::forward(loop), num_blocks); - } - - /** - * @brief Push a function with zero or more arguments, but no return value, into the task queue. Does not return a future, so the user must use wait_for_tasks() or some other method to ensure that the task finishes executing, otherwise bad things will happen. - * - * @tparam F The type of the function. - * @tparam A The types of the arguments. - * @param task The function to push. - * @param args The zero or more arguments to pass to the function. Note that if the task is a class member function, the first argument must be a pointer to the object, i.e. &object (or this), followed by the actual arguments. - */ - template - void push_task(F&& task, A&&... args) - { - { - const std::scoped_lock tasks_lock(tasks_mutex); - tasks.push(std::bind(std::forward(task), std::forward(args)...)); // cppcheck-suppress ignoredReturnValue - } - task_available_cv.notify_one(); - } - - /** - * @brief Reset the number of threads in the pool. Waits for all currently running tasks to be completed, then destroys all threads in the pool and creates a new thread pool with the new number of threads. Any tasks that were waiting in the queue before the pool was reset will then be executed by the new threads. If the pool was paused before resetting it, the new pool will be paused as well. - * - * @param thread_count_ The number of threads to use. The default value is the total number of hardware threads available, as reported by the implementation. This is usually determined by the number of cores in the CPU. If a core is hyperthreaded, it will count as two threads. - */ - void reset(const concurrency_t thread_count_ = 0) - { - std::unique_lock tasks_lock(tasks_mutex); - const bool was_paused = paused; - paused = true; - tasks_lock.unlock(); - wait_for_tasks(); - destroy_threads(); - thread_count = determine_thread_count(thread_count_); - threads = std::make_unique(thread_count); - paused = was_paused; - create_threads(); - } - - /** - * @brief Submit a function with zero or more arguments into the task queue. If the function has a return value, get a future for the eventual returned value. If the function has no return value, get an std::future which can be used to wait until the task finishes. - * - * @tparam F The type of the function. - * @tparam A The types of the zero or more arguments to pass to the function. - * @tparam R The return type of the function (can be void). - * @param task The function to submit. - * @param args The zero or more arguments to pass to the function. Note that if the task is a class member function, the first argument must be a pointer to the object, i.e. &object (or this), followed by the actual arguments. - * @return A future to be used later to wait for the function to finish executing and/or obtain its returned value if it has one. - */ - template , std::decay_t...>> - [[nodiscard]] std::future submit(F&& task, A&&... args) - { - std::shared_ptr> task_promise = std::make_shared>(); - push_task( - [task_function = std::bind(std::forward(task), std::forward(args)...), task_promise] - { - try - { - if constexpr (std::is_void_v) - { - std::invoke(task_function); - task_promise->set_value(); - } - else - { - task_promise->set_value(std::invoke(task_function)); - } - } - catch (...) - { - try - { - task_promise->set_exception(std::current_exception()); - } - catch (...) - { - } - } - }); - return task_promise->get_future(); - } - - /** - * @brief Unpause the pool. The workers will resume retrieving new tasks out of the queue. - */ - void unpause() - { - const std::scoped_lock tasks_lock(tasks_mutex); - paused = false; - } - - /** - * @brief Wait for tasks to be completed. Normally, this function waits for all tasks, both those that are currently running in the threads and those that are still waiting in the queue. However, if the pool is paused, this function only waits for the currently running tasks (otherwise it would wait forever). Note: To wait for just one specific task, use submit() instead, and call the wait() member function of the generated future. - */ - void wait_for_tasks() - { - std::unique_lock tasks_lock(tasks_mutex); - waiting = true; - tasks_done_cv.wait(tasks_lock, [this] { return !tasks_running && (paused || tasks.empty()); }); - waiting = false; - } - - /** - * @brief Wait for tasks to be completed, but stop waiting after the specified duration has passed. - * - * @tparam R An arithmetic type representing the number of ticks to wait. - * @tparam P An std::ratio representing the length of each tick in seconds. - * @param duration The time duration to wait. - * @return true if all tasks finished running, false if the duration expired but some tasks are still running. - */ - template - bool wait_for_tasks_duration(const std::chrono::duration& duration) - { - std::unique_lock tasks_lock(tasks_mutex); - waiting = true; - const bool status = tasks_done_cv.wait_for(tasks_lock, duration, [this] { return !tasks_running && (paused || tasks.empty()); }); - waiting = false; - return status; - } - - /** - * @brief Wait for tasks to be completed, but stop waiting after the specified time point has been reached. - * - * @tparam C The type of the clock used to measure time. - * @tparam D An std::chrono::duration type used to indicate the time point. - * @param timeout_time The time point at which to stop waiting. - * @return true if all tasks finished running, false if the time point was reached but some tasks are still running. - */ - template - bool wait_for_tasks_until(const std::chrono::time_point& timeout_time) - { - std::unique_lock tasks_lock(tasks_mutex); - waiting = true; - const bool status = tasks_done_cv.wait_until(tasks_lock, timeout_time, [this] { return !tasks_running && (paused || tasks.empty()); }); - waiting = false; - return status; - } - -private: - // ======================== - // Private member functions - // ======================== - - /** - * @brief Create the threads in the pool and assign a worker to each thread. - */ - void create_threads() - { - { - const std::scoped_lock tasks_lock(tasks_mutex); - workers_running = true; - } - for (concurrency_t i = 0; i < thread_count; ++i) - { - threads[i] = std::thread(&thread_pool::worker, this); - } - } - - /** - * @brief Destroy the threads in the pool. - */ - void destroy_threads() - { - { - const std::scoped_lock tasks_lock(tasks_mutex); - workers_running = false; - } - task_available_cv.notify_all(); - for (concurrency_t i = 0; i < thread_count; ++i) - { - threads[i].join(); - } - } - - /** - * @brief Determine how many threads the pool should have, based on the parameter passed to the constructor or reset(). - * - * @param thread_count_ The parameter passed to the constructor or reset(). If the parameter is a positive number, then the pool will be created with this number of threads. If the parameter is non-positive, or a parameter was not supplied (in which case it will have the default value of 0), then the pool will be created with the total number of hardware threads available, as obtained from std::thread::hardware_concurrency(). If the latter returns a non-positive number for some reason, then the pool will be created with just one thread. - * @return The number of threads to use for constructing the pool. - */ - [[nodiscard]] concurrency_t determine_thread_count(const concurrency_t thread_count_) const - { - if (thread_count_ > 0) - return thread_count_; - else - { - if (std::thread::hardware_concurrency() > 0) - return std::thread::hardware_concurrency(); - else - return 1; - } - } - - /** - * @brief A worker function to be assigned to each thread in the pool. Waits until it is notified by push_task() that a task is available, and then retrieves the task from the queue and executes it. Once the task finishes, the worker notifies wait_for_tasks() in case it is waiting. - */ - void worker() - { - std::function task; - while (true) - { - std::unique_lock tasks_lock(tasks_mutex); - task_available_cv.wait(tasks_lock, [this] { return !tasks.empty() || !workers_running; }); - if (!workers_running) - break; - if (paused) - continue; - task = std::move(tasks.front()); - tasks.pop(); - ++tasks_running; - tasks_lock.unlock(); - task(); - tasks_lock.lock(); - --tasks_running; - if (waiting && !tasks_running && (paused || tasks.empty())) - tasks_done_cv.notify_all(); - } - } - - // ============ - // Private data - // ============ - - /** - * @brief A flag indicating whether the workers should pause. When set to true, the workers temporarily stop retrieving new tasks out of the queue, although any tasks already executed will keep running until they are finished. When set to false again, the workers resume retrieving tasks. - */ - bool paused = false; - - /** - * @brief A condition variable to notify worker() that a new task has become available. - */ - std::condition_variable task_available_cv = {}; - - /** - * @brief A condition variable to notify wait_for_tasks() that the tasks are done. - */ - std::condition_variable tasks_done_cv = {}; - - /** - * @brief A queue of tasks to be executed by the threads. - */ - std::queue> tasks = {}; - - /** - * @brief A counter for the total number of currently running tasks. - */ - size_t tasks_running = 0; - - /** - * @brief A mutex to synchronize access to the task queue by different threads. - */ - mutable std::mutex tasks_mutex = {}; - - /** - * @brief The number of threads in the pool. - */ - concurrency_t thread_count = 0; - - /** - * @brief A smart pointer to manage the memory allocated for the threads. - */ - std::unique_ptr threads = nullptr; - - /** - * @brief A flag indicating that wait_for_tasks() is active and expects to be notified whenever a task is done. - */ - bool waiting = false; - - /** - * @brief A flag indicating to the workers to keep running. When set to false, the workers terminate permanently. - */ - bool workers_running = false; -}; - -// End class thread_pool // -// ============================================================================================= // - -// ============================================================================================= // -// Begin class synced_stream // - -/** - * @brief A helper class to synchronize printing to an output stream by different threads. - */ -class [[nodiscard]] synced_stream -{ -public: - /** - * @brief Construct a new synced stream. - * - * @param out_stream_ The output stream to print to. The default value is std::cout. - */ - synced_stream(std::ostream& out_stream_ = std::cout) : out_stream(out_stream_) {} - - /** - * @brief Print any number of items into the output stream. Ensures that no other threads print to this stream simultaneously, as long as they all exclusively use the same synced_stream object to print. - * - * @tparam T The types of the items - * @param items The items to print. - */ - template - void print(T&&... items) - { - const std::scoped_lock lock(stream_mutex); - (out_stream << ... << std::forward(items)); - } - - /** - * @brief Print any number of items into the output stream, followed by a newline character. Ensures that no other threads print to this stream simultaneously, as long as they all exclusively use the same synced_stream object to print. - * - * @tparam T The types of the items - * @param items The items to print. - */ - template - void println(T&&... items) - { - print(std::forward(items)..., '\n'); - } - - /** - * @brief A stream manipulator to pass to a synced_stream (an explicit cast of std::endl). Prints a newline character to the stream, and then flushes it. Should only be used if flushing is desired, otherwise '\n' should be used instead. - */ - inline static std::ostream& (&endl)(std::ostream&) = static_cast(std::endl); - - /** - * @brief A stream manipulator to pass to a synced_stream (an explicit cast of std::flush). Used to flush the stream. - */ - inline static std::ostream& (&flush)(std::ostream&) = static_cast(std::flush); - -private: - /** - * @brief The output stream to print to. - */ - std::ostream& out_stream; - - /** - * @brief A mutex to synchronize printing. - */ - mutable std::mutex stream_mutex = {}; -}; - -// End class synced_stream // -// ============================================================================================= // - -// ============================================================================================= // -// Begin class timer // - -/** - * @brief A helper class to measure execution time for benchmarking purposes. - */ -class [[nodiscard]] timer -{ -public: - /** - * @brief Start (or restart) measuring time. - */ - void start() - { - start_time = std::chrono::steady_clock::now(); - } - - /** - * @brief Stop measuring time and store the elapsed time since start(). - */ - void stop() - { - elapsed_time = std::chrono::steady_clock::now() - start_time; - } - - /** - * @brief Get the number of milliseconds that have elapsed between start() and stop(). - * - * @return The number of milliseconds. - */ - [[nodiscard]] std::chrono::milliseconds::rep ms() const - { - return (std::chrono::duration_cast(elapsed_time)).count(); - } - -private: - /** - * @brief The time point when measuring started. - */ - std::chrono::time_point start_time = std::chrono::steady_clock::now(); - - /** - * @brief The duration that has elapsed between start() and stop(). - */ - std::chrono::duration elapsed_time = std::chrono::duration::zero(); -}; - -// End class timer // -// ============================================================================================= // - -} // namespace BS diff --git a/src/Python/binding.cpp b/src/Python/binding.cpp new file mode 100644 index 00000000..f384bb2a --- /dev/null +++ b/src/Python/binding.cpp @@ -0,0 +1,906 @@ +// +// Copyright (c) 2025 StockDory authors. See the list of authors for more details. +// Licensed under LGPL-3.0. +// + +#include + +#include "Information.h" +#include "../Backend/Board.h" +#include "../Backend/Move/MoveList.h" +#include "../Backend/Type/Color.h" +#include "../Backend/Type/Move.h" +#include "../Backend/Type/Piece.h" +#include "../Backend/Type/Square.h" +#include "../Engine/EngineParameter.h" +#include "../Engine/RepetitionHistory.h" +#include "../Engine/Search.h" +#include "../Engine/Move/PrincipleVariationTable.h" +#include "../Engine/Time/TimeManager.h" +#include "../External/strutil.h" +#include "../Terminal/Perft/PerftRunner.h" + +namespace py = pybind11; + +constexpr size_t API_VERSION = 0; + +class PyMoveList +{ + + std::array Internal = {}; + + uint8_t Size = 0; + + template + void AddMoveLoop(const StockDory::Board& board, const Piece piece) + { + const PinBitBoard pin = board.Pin (); + const CheckBitBoard check = board.Check< Opposite(Color)>(); + + if (check.DoubleCheck && piece != King) return; + + switch (piece) { + case Pawn : + AddMoveLoop(board, pin, check); + break; + case Knight: + AddMoveLoop(board, pin, check); + break; + case Bishop: + AddMoveLoop(board, pin, check); + break; + case Rook : + AddMoveLoop(board, pin, check); + break; + case Queen : + AddMoveLoop(board, pin, check); + break; + case King : + AddMoveLoop(board, pin, check); + break; + default: + throw std::invalid_argument("Invalid piece."); + } + } + + template + void AddMoveLoop(const StockDory::Board& board, const Piece piece, const Square sq) + { + const PinBitBoard pin = board.Pin (); + const CheckBitBoard check = board.Check< Opposite(Color)>(); + + if (check.DoubleCheck && piece != King) return; + + switch (piece) { + case Pawn : + AddMoveLoop(board, sq, pin, check); + break; + case Knight: + AddMoveLoop(board, sq, pin, check); + break; + case Bishop: + AddMoveLoop(board, sq, pin, check); + break; + case Rook : + AddMoveLoop(board, sq, pin, check); + break; + case Queen : + AddMoveLoop(board, sq, pin, check); + break; + case King : + AddMoveLoop(board, sq, pin, check); + break; + default: + throw std::invalid_argument("Invalid piece."); + } + } + + template + void AddMoveLoop(const StockDory::Board& board, const PinBitBoard& pin, const CheckBitBoard& check) + { + BitBoardIterator iterator (board.PieceBoard(Piece)); + + for (Square sq = iterator.Value(); sq != NASQ; sq = iterator.Value()) + AddMoveLoop(board, sq, pin, check); + } + + template + void AddMoveLoop(const StockDory::Board& board, const Square from, + const PinBitBoard& pin, const CheckBitBoard& check) + { + const StockDory::MoveList moves (board, from, pin, check); + BitBoardIterator moveIterator = moves.Iterator(); + + for (Square to = moveIterator.Value(); to != NASQ; to = moveIterator.Value()) { + if (moves.Promotion(from)) { + Internal[Size++] = Move(from, to, Queen ); + Internal[Size++] = Move(from, to, Knight); + Internal[Size++] = Move(from, to, Rook ); + Internal[Size++] = Move(from, to, Bishop); + } else { + Internal[Size++] = Move(from, to, NAP ); + } + } + } + + public: + PyMoveList(const StockDory::Board& board, const Piece piece, const Color color) + { + switch (color) { + case White: + AddMoveLoop(board, piece); + break; + case Black: + AddMoveLoop(board, piece); + break; + default: + throw std::invalid_argument("Invalid color."); + } + } + + PyMoveList(const StockDory::Board& board, const Square sq) + { + switch (const PieceColor pc = board[sq]; pc.Color()) { + case White: + AddMoveLoop(board, pc.Piece(), sq); + break; + case Black: + AddMoveLoop(board, pc.Piece(), sq); + break; + default: + throw std::invalid_argument("Invalid color."); + } + } + + Move operator [](const uint8_t idx) const + { + return Internal[idx]; + } + + uint8_t Count() const + { + return Size; + } + + std::string ToString() const + { + std::stringstream ss; + ss << "["; + + for (size_t i = 0 ; i < Size; i++) { + ss << Internal[i].ToString(); + + if (i != Size - 1) ss << ", "; + } + + ss << "]"; + + return ss.str(); + } + + std::array::iterator Begin() + { + return Internal.begin(); + } + +}; + +template +class PyConstantHolder {}; + +class PySearchHandler +{ + + using PV = StockDory::PrincipleVariationEntry; + + using DepthIterationHandler = std::function; + + using BestMoveHandler = std::function; + + static inline DepthIterationHandler DepthIterationMethod = [](const uint8_t , + const uint8_t , + const int32_t, + const uint64_t, + const uint64_t, + const int64_t, + const PV& ) -> void {}; + static inline BestMoveHandler BestMoveMethod = [](const Move) -> void {}; + + public: + static void RegisterDepthIterationHandler(const DepthIterationHandler&& handler) + { + DepthIterationMethod = std::move(handler); + } + + static void RegisterBestMoveHandler(const BestMoveHandler&& handler) + { + BestMoveMethod = std::move(handler); + } + + static void HandleDepthIteration(const uint8_t a, const uint8_t b, const int32_t c, + const uint64_t d, const uint64_t e, + const StockDory::MS f, const PV& g) + { + DepthIterationMethod(a, b, c, d, e, f.count(), g); + } + + static void HandleBestMove(const Move a) + { + BestMoveMethod(a ); + } + +}; + +using PySearch = StockDory::Search; + +auto SEARCH = PySearch(); +auto SEARCH_RUNNING = false ; + +PYBIND11_MODULE(StockDory, m) +{ + m.doc() = "Python Bindings for StockDory"; + + /** -- BASE MODULES -- **/ + py::module_ core = m.def_submodule("core" , "Python Bindings for StockDory's Core" ); + py::module_ engine = m.def_submodule("engine", "Python Bindings for StockDory's Engine"); + + /** -- CONSTANTS -- **/ + { + py::class_> gc (m , "GConstant"); + py::class_> cc (core , "CConstant"); + py::class_> ec (engine, "EConstant"); + + gc.def_property_readonly_static( + "VERSION", + [](py::object) -> std::string { return VERSION; } + ); + + gc.def_property_readonly_static( + "API_VERSION", + [](py::object) -> std::string + { + return VERSION + strutil::to_string('+') + strutil::to_string(API_VERSION); + } + ); + + cc.def_property_readonly_static( + "INFINITY", + [](py::object) -> int32_t { return Infinity; } + ); + + ec.def_property_readonly_static( + "MATE", + [](py::object) -> int32_t { return Mate; } + ); + + ec.def_property_readonly_static( + "DRAW", + [](py::object) -> int32_t { return Draw; } + ); + + ec.def_property_readonly_static( + "MAX_DEPTH", + [](py::object) -> uint8_t { return MaxDepth; } + ); + } + + /** -- ENUM: core.Square -- **/ + { + py::enum_ square (core, "Square"); + + for (Square sq = A1; sq <= NASQ; sq = Next(sq)) + square.value( + strutil::capitalize(ToString(sq)).c_str(), + sq + ); + + square.def_property_readonly( + "next", + [](const Square sq) -> Square + { + return Next(sq); + } + ); + } + + /** -- ENUM: core.Piece -- **/ + { + py::enum_ piece (core, "Piece"); + + for (Piece p = Pawn; p <= NAP; p = Next(p)) + piece.value( + strutil::capitalize(ToString(p)).c_str(), + p + ); + + piece.def_property_readonly( + "next", + [](const Piece p) -> Piece + { + return Next(p); + } + ); + } + + /** -- ENUM: core.Color -- **/ + { + py::enum_ color (core, "Color"); + + for (Color c = White; c <= NAC; c = Next(c)) + color.value( + strutil::capitalize(ToString(c)).c_str(), + c + ); + + color.def_property_readonly( + "next", + [](const Color c) -> Color + { + return Next(c); + } + ); + + color.def_property_readonly("opposite", &Opposite); + } + + /** -- STRUCT: core.PreviousState & core.PreviousStateNull **/ + { + py::class_ previousState (core, "PreviousState" ); + py::class_ previousStateNull (core, "PreviousStateNull"); + } + + /** -- CLASS: core.Board -- **/ + { + py::class_ board (core, "Board"); + + board.def(py::init()); + + board.def( + py::init(), + py::arg("fen") + ); + + board.def("__str__" , &StockDory::Board::Fen ); + board.def("__hash__", &StockDory::Board::Zobrist); + + board.def("load_for_eval", &StockDory::Board::LoadForEvaluation); + + board.def( + "__getitem__", + [](const StockDory::Board& self, const Square sq) -> std::pair + { + const PieceColor pc = self[sq]; + + return std::make_pair(pc.Piece(), pc.Color()); + }, + py::arg("sq") + ); + + board.def_property_readonly("color_to_move", &StockDory::Board::ColorToMove ); + board.def_property_readonly("en_passant_sq", &StockDory::Board::EnPassantSquare); + + board.def( + "king_castling_right", + [](const StockDory::Board& self, const Color c) -> bool + { + if (c == White) return self.CastlingRightK(); + if (c == Black) return self.CastlingRightK(); + + return self.CastlingRightK(); + }, + py::arg("color") + ); + + board.def( + "queen_castling_right", + [](const StockDory::Board& self, const Color c) -> bool + { + if (c == White) return self.CastlingRightQ(); + if (c == Black) return self.CastlingRightQ(); + + return self.CastlingRightQ(); + }, + py::arg("color") + ); + + board.def( + "checked", + [](const StockDory::Board& self, const Color c) -> bool + { + if (c == White) return self.Checked(); + if (c == Black) return self.Checked(); + + return self.Checked(); + }, + py::arg("color") + ); + + board.def( + "move", + [](StockDory::Board& self) -> PreviousStateNull + { + return self.Move(); + } + ); + board.def( + "undo_move", + [](StockDory::Board& self, const PreviousStateNull& s) -> void + { + self.UndoMove(s); + }, + py::arg("previous_state") + ); + + board.def( + "move", + &StockDory::Board::Move, + py::arg("from"), + py::arg("to"), + py::arg("promotion") + ); + board.def( + "undo_move", + &StockDory::Board::UndoMove, + py::arg("previous_state"), + py::arg("from"), + py::arg("to") + ); + + board.def( + "move_nnue", + &StockDory::Board::Move, + py::arg("from"), + py::arg("to"), + py::arg("promotion") + ); + board.def( + "undo_move_nnue", + &StockDory::Board::UndoMove, + py::arg("previous_state"), + py::arg("from"), + py::arg("to") + ); + + board.def( + "move_fast", + &StockDory::Board::Move, + py::arg("from"), + py::arg("to"), + py::arg("promotion") + ); + board.def( + "undo_move_fast", + &StockDory::Board::UndoMove, + py::arg("previous_state"), + py::arg("from"), + py::arg("to") + ); + + board.def( + "move_hash", + &StockDory::Board::Move, + py::arg("from"), + py::arg("to"), + py::arg("promotion") + ); + board.def( + "undo_move_hash", + &StockDory::Board::UndoMove, + py::arg("previous_state"), + py::arg("from"), + py::arg("to") + ); + } + + /** -- STRUCT: core.Move & core.MoveList -- **/ + { + py::class_ move (core, "Move"); + + move.def(py::init()); + + move.def( + py::init(), + py::arg("from"), + py::arg("to"), + py::arg("promotion") + ); + + move.def( + py::init( + [](const std::string& s) -> Move + { + return Move::FromString(s); + } + ), + py::arg("move_str") + ); + + move.def_property_readonly("from_sq" , &Move::From ); + move.def_property_readonly("to_sq" , &Move::To ); + move.def_property_readonly("promotion", &Move::Promotion); + + move.def("__eq__", &Move::operator==); + + move.def("__str__" , &Move::ToString); + move.def("__repr__", &Move::ToString); + } + { + py::class_ moveList (core, "MoveList"); + + moveList.def( + py::init(), + py::arg("board"), + py::arg("piece"), + py::arg("color") + ); + + moveList.def( + py::init(), + py::arg("board"), + py::arg("sq") + ); + + moveList.def("__getitem__", &PyMoveList::operator[]); + + moveList.def("__len__", &PyMoveList::Count); + + moveList.def( + "__iter__", + [](PyMoveList& self) -> py::typing::Iterator + { + return py::make_iterator(self.Begin(), self.Begin() + self.Count()); + }, + py::keep_alive<0, 1>() + ); + + moveList.def("__str__" , &PyMoveList::ToString); + moveList.def("__repr__", &PyMoveList::ToString); + } + + /** -- MODULE: core.perft -- **/ + { + py::module_ perft = core.def_submodule("perft", "Python Bindings for StockDory's PerftRunner"); + + perft.def( + "set_board", + [](const std::string& s) -> void + { + StockDory::PerftRunner::SetBoard(s); + }, + py::arg("fen") + ); + perft.def( + "set_board", + [](const StockDory::Board& b) -> void + { + StockDory::PerftRunner::SetBoard(b); + }, + py::arg("board") + ); + + perft.def( + "perft", + &StockDory::PerftRunner::Perft, + py::arg("depth") + ); + perft.def( + "perft_divide", + &StockDory::PerftRunner::Perft, + py::arg("depth") + ); + + perft.def( + "__perft_white", + &StockDory::PerftRunner::Perft, + py::arg("board"), + py::arg("depth") + ); + perft.def( + "__perft_black", + &StockDory::PerftRunner::Perft, + py::arg("board"), + py::arg("depth") + ); + + perft.def( + "__perft_white_divide", + &StockDory::PerftRunner::Perft, + py::arg("board"), + py::arg("depth") + ); + perft.def( + "__perft_black_divide", + &StockDory::PerftRunner::Perft, + py::arg("board"), + py::arg("depth") + ); + + perft.def( + "__perft_white_sync", + &StockDory::PerftRunner::Perft, + py::arg("board"), + py::arg("depth") + ); + perft.def( + "__perft_black_sync", + &StockDory::PerftRunner::Perft, + py::arg("board"), + py::arg("depth") + ); + + perft.def( + "__perft_white_divide_sync", + &StockDory::PerftRunner::Perft, + py::arg("board"), + py::arg("depth") + ); + perft.def( + "__perft_black_divide_sync", + &StockDory::PerftRunner::Perft, + py::arg("board"), + py::arg("depth") + ); + } + + /** -- MODULE: engine.evaluation -- **/ + { + py::module_ evaluation = engine.def_submodule( + "evaluation", + "Python Bindings for StockDory's Engine Evaluation" + ); + + evaluation.def( + "evaluate", + [](const Color c) -> int32_t + { + return StockDory::Evaluation::Evaluate(c); + }, + py::arg("perspective") + ); + } + + /** -- MODULE: core.tp -- **/ + { + py::module_ pool = core.def_submodule("tp", "Python Bindings for StockDory's Thread Pool"); + + pool.def( + "resize", + [](const uint32_t n) -> void { StockDory::ThreadPool.Resize(n); }, + py::arg("thread_count") + ); + + pool.def( + "size", + [] -> uint32_t { return StockDory::ThreadPool.Size(); } + ); + } + + /** -- MODULE: engine.tt -- **/ + { + py::module_ tt = engine.def_submodule("tt", "Python Bindings for StockDory's Engine Transposition Table"); + + tt.def( + "resize", + [](const size_t n) -> void { TTable.Resize(n); }, + py::arg("bytes") + ); + + tt.def( + "size", + [] -> size_t { return TTable.Size(); } + ); + + tt.def( + "clear", + [] -> void { TTable.Clear(); } + ); + } + + /** -- STRUCT: core.TimeData -- **/ + { + py::class_ td (core, "TimeData"); + + td.def(py::init()); + + td.def_property( + "white_time", + [](const StockDory::TimeData& self) -> uint64_t { return self.WhiteTime; }, + [](StockDory::TimeData& self, const uint64_t v) -> void { self.WhiteTime = v; } + ); + td.def_property( + "black_time", + [](const StockDory::TimeData& self) -> uint64_t { return self.BlackTime; }, + [](StockDory::TimeData& self, const uint64_t v) -> void { self.BlackTime = v; } + ); + + td.def_property( + "white_increment", + [](const StockDory::TimeData& self) -> uint64_t { return self.WhiteIncrement; }, + [](StockDory::TimeData& self, const uint64_t v) -> void { self.WhiteIncrement = v; } + ); + td.def_property( + "black_increment", + [](const StockDory::TimeData& self) -> uint64_t { return self.BlackIncrement; }, + [](StockDory::TimeData& self, const uint64_t v) -> void { self.BlackIncrement = v; } + ); + + td.def_property( + "moves_to_go", + [](const StockDory::TimeData& self) -> uint16_t { return self.MovesToGo; }, + [](StockDory::TimeData& self, const uint16_t v) -> void { self.MovesToGo = v; } + ); + } + + /** -- CLASS: engine.TimeControl -- **/ + { + py::class_ tc (engine, "TimeControl"); + } + + /** -- MODULE: engine.tm -- **/ + { + py::module_ tm = engine.def_submodule("tm", "Python Bindings for StockDory's Engine Time Manager"); + + tm.def( + "default", + &StockDory::TimeManager::Default + ); + + tm.def( + "fixed" , + &StockDory::TimeManager::Fixed, + py::arg("milliseconds") + ); + + tm.def( + "optimal", + &StockDory::TimeManager::Optimal, + py::arg("board"), + py::arg("time_data") + ); + } + + /** -- CLASS: engine.RepetitionHistory -- **/ + { + py::class_ rh (engine, "RepetitionHistory"); + + rh.def( + py::init(), + py::arg("hash") + ); + + rh.def( + "push" , + &StockDory::RepetitionHistory::Push, + py::arg("hash") + ); + + rh.def( + "pull" , + &StockDory::RepetitionHistory::Pull + ); + + rh.def( + "found", + &StockDory::RepetitionHistory::Found, + py::arg("hash"), + py::arg("half_move_count") + ); + } + + /** -- CLASS: engine.PrincipleVariationTable -- **/ + { + py::class_ pv (engine, "PrincipleVariationEntry"); + + pv.def( + "__len__", + [](const StockDory::PrincipleVariationEntry& self) -> size_t { return self.Ply; } + ); + + pv.def( + "__getitem__", + [](const StockDory::PrincipleVariationEntry& self, const size_t idx) -> Move { return self.PV[idx]; } + ); + } + + /** -- MODULE: engine.event -- **/ + { + py::module_ event = engine.def_submodule( + "event", + "Python Bindings for StockDory's Engine Event System" + ); + + event.def( + "set_depth_iteration_event_handler", + [](py::function& callback) -> void + { + PySearchHandler::RegisterDepthIterationHandler( + [callback = std::move(callback)](const uint8_t a, const uint8_t b, const int32_t c, + const uint64_t d, const uint64_t e, const int64_t f, + const StockDory::PrincipleVariationEntry& g) -> void + { + py::gil_scoped_acquire _; + + // ReSharper disable once CppExpressionWithoutSideEffects + callback(a, b, c, d, e, f, g); + } + ); + }, + py::arg("handler") + ); + + event.def( + "set_best_move_event_handler", + [](py::function& callback) -> void + { + PySearchHandler:: RegisterBestMoveHandler( + [callback = std::move(callback)](const Move a) -> void + { + py::gil_scoped_acquire _; + + // ReSharper disable once CppExpressionWithoutSideEffects + callback(a ); + } + ); + }, + py::arg("handler") + ); + } + + /** -- STRUCT: engine.Limit -- **/ + { + py::class_ limit (engine, "Limit"); + + limit.def(py::init()); + + limit.def(py::init(), py::arg("nodes")); + + limit.def(py::init(), py::arg("depth")); + } + + /** -- MODULE: engine.search -- **/ + { + py::module_ search = engine.def_submodule( + "search", + "Python Bindings for StockDory's Asynchronous Search System" + ); + + search.def( + "configure", + [](const StockDory::Board& a, const StockDory::TimeControl& b, + const StockDory::RepetitionHistory& c, const uint8_t d) -> void + { + SEARCH = PySearch(a, b, c, d); + }, + py::arg("board"), + py::arg("time_control"), + py::arg("repetition_history"), + py::arg("half_move_count") + ); + + search.def( + "start_async", + [](const StockDory::Limit& limit = StockDory::Limit()) -> void + { + StockDory::ThreadPool.Execute([limit] -> void + { + SEARCH_RUNNING = true ; + SEARCH.IterativeDeepening(limit); + SEARCH_RUNNING = false; + }); + }, + py::arg("limit") = StockDory::Limit() + ); + + search.def("stop_async", [] -> void { SEARCH.ForceStop(); }); + + search.def("is_running", [] -> bool { return SEARCH_RUNNING; }); + } +} diff --git a/src/Terminal/BenchHash.h b/src/Terminal/BenchHash.h index 16b750b9..745fabc1 100644 --- a/src/Terminal/BenchHash.h +++ b/src/Terminal/BenchHash.h @@ -6,11 +6,12 @@ #ifndef STOCKDORY_BENCHHASH_H #define STOCKDORY_BENCHHASH_H +#include #include #include -#include "../Engine/Time/TimeManager.h" #include "../Engine/Search.h" +#include "../Engine/Time/TimeManager.h" namespace StockDory { @@ -18,46 +19,49 @@ namespace StockDory class BenchHash { - private: - constexpr static uint8_t BenchLength = 50; - constexpr static Limit BenchLimit = Limit(static_cast(13)); + constexpr static uint8_t BenchLength = 50; + constexpr static auto BenchLimit = Limit(static_cast(13)); - static std::array Positions; + static std::array Positions; public: - static void Run() - { - using BTP = std::chrono::time_point; - - uint64_t nodes = 0; - TimeControl infinite = TimeManager::Default(); + static void Run() + { + using BTP = std::chrono::time_point; - MS time (0); + std::array nodes; + std::array< MS , BenchLength> times; - for (uint8_t i = 0; i < BenchLength; i++) { - std::cout << "Position (" << std::setw(2) << std::setfill('0') - << static_cast(i + 1) << "/"; - std::cout << static_cast(BenchLength) << "): "; - std::cout << Positions[i] << std::endl; + for (size_t i = 0; i < BenchLength; i++) { + std::cout << "Position (" << std::setw(2) << std::setfill('0') + << static_cast(i + 1) << "/" << static_cast(BenchLength) << "): "; + std::cout << Positions[i] << std::endl; - const Board board (Positions[i]); - const RepetitionHistory history (board.Zobrist()); - Search search (board, infinite, history, 0); + const Board board (Positions[i]); + const RepetitionHistory history (board.Zobrist()); - const BTP start = std::chrono::high_resolution_clock::now(); - search.IterativeDeepening(BenchLimit); - const BTP stop = std::chrono::high_resolution_clock::now(); + const TimeControl infinite = TimeManager::Default(); - nodes += search.NodesSearched(); - time += std::chrono::duration_cast(stop - start); - } + Search search (board, infinite, history, 0); - const auto nps = static_cast(static_cast(nodes) / - (static_cast(time.count()) / static_cast(1000 ))); + const BTP t0 = std::chrono::high_resolution_clock::now(); + search.IterativeDeepening(BenchLimit); + const BTP t1 = std::chrono::high_resolution_clock::now(); - std::cout << nodes << " nodes " << nps << " nps" << std::endl; + nodes[i] = search.NodesSearched(); + times[i] = std::chrono::duration_cast(t1 - t0); } + const uint64_t nodeC = std::accumulate(nodes.begin(), nodes.end(), 0ULL); + const MS timeC = std::accumulate(times.begin(), times.end(), MS(0)); + + const auto nps = static_cast( + static_cast(nodeC) / (static_cast(timeC.count()) / 1000.0) + ); + + std::cout << nodeC << " nodes " << nps << " nps" << std::endl; + } + }; } // StockDory diff --git a/src/Terminal/NetworkConverter.h b/src/Terminal/NetworkConverter.h deleted file mode 100644 index 8b4840e6..00000000 --- a/src/Terminal/NetworkConverter.h +++ /dev/null @@ -1,114 +0,0 @@ -// -// Copyright (c) 2023 StockDory authors. See the list of authors for more details. -// Licensed under LGPL-3.0. -// - -#ifndef STOCKDORY_NETWORKCONVERTER_H -#define STOCKDORY_NETWORKCONVERTER_H - -#include - -#include "../Engine/NetworkArchitecture.h" - -#include "../External/strutil.h" -#include "../External/picosha2.h" -#include "../External/emoji.h" - -namespace StockDory -{ - - class NetworkConverter - { - - private: - constexpr static char Separator = -#ifdef _WIN32 - '\\'; -#else - '/'; -#endif - - static std::string JoinPath(const std::vector& paths) - { - std::stringstream ss; - uint8_t i = 0; - for (const std::string& path : paths) { - ss << path; - if (i != paths.size() - 1) ss << Separator; - i++; - } - - return ss.str(); - } - - template - static std::shared_ptr ReadFromFile(const std::string& path) - { - std::cout << emojicpp::emojize(":brain: Reading network from file... "); - MantaRay::MarlinflowStream stream (path); - std::cout << emojicpp::emojize(":white_check_mark:") << std::endl; - return std::make_shared(stream); - } - - template - static void WriteToFile(const std::string& directory, const std::shared_ptr& network) - { - const std::string path = JoinPath({directory, "temp.nnue"}); - MantaRay::BinaryFileStream stream (path); - network->WriteTo(stream); - } - - static void GenerateHash(const std::string& directory, const std::string& architecture) - { - const std::string path = JoinPath({directory, "temp.nnue"}); - - std::cout << emojicpp::emojize(":magic_wand: Generating network hash... "); - std::ifstream hashStream (path, std::ios::binary); - std::vector hashBlock (picosha2::k_digest_size); - picosha2::hash256(hashStream, hashBlock.begin(), hashBlock.end()); - hashStream.close(); - std::string hash = picosha2::bytes_to_hex_string(hashBlock.begin(), hashBlock.end()) - .substr(0, 10); - std::cout << emojicpp::emojize(":white_check_mark:") << std::endl; - - std::cout << emojicpp::emojize(":floppy_disk: Saving network... "); - const std::string arch = strutil::capitalize_first_char(strutil::to_lower(architecture)); - const std::string name = arch + "-" + hash + ".nnue"; - const std::string newPath = JoinPath({directory, name}); - std::rename(path.c_str(), newPath.c_str()); - std::cout << emojicpp::emojize(":white_check_mark: - Network saved as: " + name) << std::endl; - } - - public: - static void Launch() - { - std::cout << emojicpp::emojize(":dna: Neural Network Converter :dna:") << std::endl; - - std::cout << emojicpp::emojize(":open_folder: JSON Path: "); - std::string inputPath; - std::getline(std::cin, inputPath); - - std::cout << emojicpp::emojize(":open_folder: Architecture: "); - std::string architecture; - std::getline(std::cin, architecture); - - std::cout << emojicpp::emojize(":open_folder: Output Directory: "); - std::string outputDirectory; - std::getline(std::cin, outputDirectory); - - if (strutil::compare_ignore_case(architecture, "Starshard")) { - std::shared_ptr network = ReadFromFile(inputPath); - WriteToFile(outputDirectory, network); - } else if (strutil::compare_ignore_case(architecture, "Aurora" )) { - std::shared_ptr network = ReadFromFile(inputPath); - WriteToFile(outputDirectory, network); - } - - GenerateHash(outputDirectory, architecture); - } - - }; - -} // StockDory - -#endif //STOCKDORY_NETWORKCONVERTER_H diff --git a/src/Terminal/Perft/PerftEntry.h b/src/Terminal/Perft/PerftEntry.h index 5940898f..27da9db3 100644 --- a/src/Terminal/Perft/PerftEntry.h +++ b/src/Terminal/Perft/PerftEntry.h @@ -6,10 +6,9 @@ #ifndef STOCKDORY_PERFTENTRY_H #define STOCKDORY_PERFTENTRY_H -#include +#include #include -#include "../../Backend/TranspositionTable.h" #include "../../Backend/Type/Zobrist.h" namespace StockDory @@ -21,23 +20,26 @@ namespace StockDory static_assert(Depth <= 64, "Depth must be less than or equal to 64."); - ZobristHash Hash = 0; - std::array Internal {}; - uint64_t Utilization = 0; - std::shared_mutex ThreadLock; + ZobristHash Hash = 0; + std::array Internal{}; + uint64_t Utilization = 0; + std::shared_mutex ThreadLock; inline std::pair Nodes(const ZobristHash hash, const uint8_t depth) { std::lock_guard lock(ThreadLock); - return {Hash == hash && (Utilization & (1ULL << (depth - 1))), Internal[depth - 1]}; + + // ReSharper disable once CppRedundantParentheses + return {Hash == hash && Utilization & 1ULL << (depth - 1), Internal[depth - 1]}; } inline void Insert(const ZobristHash hash, const uint8_t depth, const uint64_t nodes) { - const uint8_t idx = depth - 1 ; + const uint8_t idx = depth - 1; const uint64_t mask = 1ULL << idx; - std::lock_guard lock(ThreadLock); + std::lock_guard lock (ThreadLock); + if (Utilization == 0) Hash = hash; if (hash != Hash) return; Utilization |= mask; diff --git a/src/Terminal/Perft/PerftRunner.h b/src/Terminal/Perft/PerftRunner.h index dbe0c8e5..9d8af578 100644 --- a/src/Terminal/Perft/PerftRunner.h +++ b/src/Terminal/Perft/PerftRunner.h @@ -6,18 +6,19 @@ #ifndef STOCKDORY_PERFTRUNNER_H #define STOCKDORY_PERFTRUNNER_H -#include #include +#include +#include + +#include #include "../../Backend/Board.h" -#include "../../Backend/TranspositionTable.h" -#include "../../Backend/Util.h" #include "../../Backend/ThreadPool.h" #include "../../Backend/Move/MoveList.h" #include "PerftEntry.h" -//using PEntry = StockDory::Perft::PerftEntry<9>; +// using PEntry = StockDory::Perft::PerftEntry<9>; namespace StockDory { @@ -25,305 +26,371 @@ namespace StockDory class PerftRunner { - private: - static Board PerftBoard; -// static TranspositionTable TranspositionTable; + static Board PerftBoard; + // static TranspositionTable TranspositionTable; - template<::Color Color, bool Divide, bool Sync = false, bool TT = false> - struct PerftLayer - { + template + struct PerftLayer + { - public: - static inline uint64_t Perft(Board& board, const uint8_t depth) - { - return PerftRunner::Perft(board, depth); - } - - template - static inline uint64_t PerftLoop(Board& board, const uint8_t depth, - const PinBitBoard& pin, const CheckBitBoard& check, - const BitBoardIterator& iterator) - { - return PerftRunner::PerftLoop - (board, depth, pin, check, iterator); - } - - }; - - template - struct BoardLayer + static inline uint64_t Perft(Board& board, const uint8_t depth) { + return PerftRunner::Perft(board, depth); + } - public: - static inline PreviousState Move(Board& board, - const Square from, const Square to, - const Piece promotion = NAP) - { - return board.Move(from, to, promotion); - } + template + static inline uint64_t PerftLoop( Board& board, const uint8_t depth, + const PinBitBoard& pin, const CheckBitBoard& check, + const BitBoardIterator& iterator) + { + return PerftRunner::PerftLoop(board, depth, pin, check, iterator); + } - static inline void UndoMove(Board& board, const PreviousState& state, - const Square from, const Square to) - { - board.UndoMove(state, from, to); - } + }; - }; + template + struct BoardLayer + { - template<::Color Color, bool Divide, bool Sync = false, bool TT = false> - static inline uint64_t Perft(Board& board, const uint8_t depth) + static inline PreviousState Move(Board& board, + const Square from, const Square to, + const Piece promotion = NAP) { - uint64_t nodes = 0; - using PLayer = PerftLayer; + return board.Move(from, to, promotion); + } -// if (TT) { -// const ZobristHash hash = board.Zobrist(); -// PEntry& entry = TranspositionTable[hash]; -// std::pair result = entry.Nodes(hash, depth); -// -// if (result.first) return result.second; -// } + static inline void UndoMove(Board& board, const PreviousState& state, + const Square from, const Square to) + { + board.UndoMove(state, from, to); + } - const PinBitBoard pin = board.Pin (); - const CheckBitBoard check = board.Check< Opposite(Color)>(); - - if (check.DoubleCheck) { - const BitBoardIterator kings (board.PieceBoard(King)); - nodes += PLayer::template PerftLoop(board, depth, pin, check, kings); - } else { - const BitBoardIterator pawns (board.PieceBoard(Pawn )); - const BitBoardIterator knights (board.PieceBoard(Knight)); - const BitBoardIterator bishops (board.PieceBoard(Bishop)); - const BitBoardIterator rooks (board.PieceBoard(Rook )); - const BitBoardIterator queens (board.PieceBoard(Queen )); - const BitBoardIterator kings (board.PieceBoard(King )); + }; + public: + template + static inline uint64_t Perft(Board& board, const uint8_t depth) + { + uint64_t nodes = 0; + using PLayer = PerftLayer; + + // if (TT) { + // const ZobristHash hash = board.Zobrist(); + // PEntry& entry = TranspositionTable[hash]; + // std::pair result = entry.Nodes(hash, depth); + // + // if (result.first) return result.second; + // } + + const PinBitBoard pin = board.Pin(); + + if (const CheckBitBoard check = board.Check(); check.DoubleCheck) { + const BitBoardIterator kings (board.PieceBoard(King )); + nodes += PLayer::template PerftLoop(board, depth, pin, check, kings); + } else { + const BitBoardIterator pawns (board.PieceBoard(Pawn )); + const BitBoardIterator knights (board.PieceBoard(Knight)); + const BitBoardIterator bishops (board.PieceBoard(Bishop)); + const BitBoardIterator rooks (board.PieceBoard(Rook )); + const BitBoardIterator queens (board.PieceBoard(Queen )); + const BitBoardIterator kings (board.PieceBoard(King )); + + if (Sync || depth < 5) { nodes += PLayer::template PerftLoop(board, depth, pin, check, pawns ); nodes += PLayer::template PerftLoop(board, depth, pin, check, knights); nodes += PLayer::template PerftLoop(board, depth, pin, check, bishops); nodes += PLayer::template PerftLoop(board, depth, pin, check, rooks ); nodes += PLayer::template PerftLoop(board, depth, pin, check, queens ); nodes += PLayer::template PerftLoop(board, depth, pin, check, kings ); - } + } else { + std::array result = {}; + std::array, 6> perftLoops = { + [pawns , depth, &board, &pin, &check, &result] -> void + { + Board b = board; + result[Pawn ] = PLayer::template PerftLoop(b, depth, pin, check, pawns ); + }, + [knights, depth, &board, &pin, &check, &result] -> void + { + Board b = board; + result[Knight] = PLayer::template PerftLoop(b, depth, pin, check, knights); + }, + [bishops, depth, &board, &pin, &check, &result] -> void + { + Board b = board; + result[Bishop] = PLayer::template PerftLoop(b, depth, pin, check, bishops); + }, + [rooks , depth, &board, &pin, &check, &result] -> void + { + Board b = board; + result[Rook ] = PLayer::template PerftLoop(b, depth, pin, check, rooks ); + }, + [queens , depth, &board, &pin, &check, &result] -> void + { + Board b = board; + result[Queen ] = PLayer::template PerftLoop(b, depth, pin, check, queens ); + }, + [kings , depth, &board, &pin, &check, &result] -> void + { + Board b = board; + result[King ] = PLayer::template PerftLoop(b, depth, pin, check, kings ); + } + }; -// if (TT) { -// const ZobristHash hash = board.Zobrist(); -// PEntry& entry = TranspositionTable[hash]; -// entry.Insert(hash, depth, nodes); -// } + using Block = drjit::blocked_range; + ThreadPool.For( + Block(0, 6), + [&perftLoops](const Block block) -> void + { + perftLoops[block.begin()](); + } + ); - return nodes; + for (size_t i = 0; i < 6; i++) nodes += result[i]; + } } - template - static inline uint64_t PerftLoop(Board& board, const uint8_t depth, - const PinBitBoard& pin, const CheckBitBoard& check, - BitBoardIterator pIterator) - { - uint64_t nodes = 0; - using PLayer = PerftLayer; - using BLayer = BoardLayer; + // if (TT) { + // const ZobristHash hash = board.Zobrist(); + // PEntry& entry = TranspositionTable[hash]; + // entry.Insert(hash, depth, nodes); + // } + + return nodes; + } - if (depth == 1) for (Square sq = pIterator.Value(); sq != Square::NASQ; sq = pIterator.Value()) { + private: + template + static inline uint64_t PerftLoop(Board& board, const uint8_t depth, + const PinBitBoard& pin, const CheckBitBoard& check, + BitBoardIterator pIterator) + { + uint64_t nodes = 0; + + using PLayer = PerftLayer; + using BLayer = BoardLayer; + + if (depth == 1) + for (Square sq = pIterator.Value(); sq != NASQ; sq = pIterator.Value()) { const MoveList moves (board, sq, pin, check); - uint8_t count = moves.Count(); + const uint8_t count = moves.Count(); if (moves.Promotion(sq)) nodes += count * 4; - else nodes += count ; + else nodes += count; if (Divide && count) { BitBoardIterator mIterator = moves.Iterator(); - for (Square m = mIterator.Value(); m != Square::NASQ; m = mIterator.Value()) { + for (Square m = mIterator.Value(); m != NASQ; m = mIterator.Value()) { if (moves.Promotion(sq)) { - LogMove(sq, m, 1); - LogMove(sq, m, 1); - LogMove(sq, m, 1); - LogMove(sq, m, 1); - } - else LogMove (sq, m, 1); + LogMove(sq, m, 1); + LogMove(sq, m, 1); + LogMove(sq, m, 1); + LogMove(sq, m, 1); + } else LogMove(sq, m, 1); } } - } else if (Sync || depth < 5) - for (Square sq = pIterator.Value(); sq != Square::NASQ; sq = pIterator.Value()) { + } + else if (Sync || depth < 5) + for (Square sq = pIterator.Value(); sq != NASQ; sq = pIterator.Value()) { const MoveList moves (board, sq, pin, check); BitBoardIterator mIterator = moves.Iterator(); - for (Square m = mIterator.Value(); m != Square::NASQ; m = mIterator.Value()) { + for (Square m = mIterator.Value(); m != NASQ; m = mIterator.Value()) { if (moves.Promotion(sq)) { - PreviousState state = - BLayer::Move(board, sq, m, Queen ); - const uint64_t queenNodes = PLayer::Perft(board, depth - 1); + PreviousState state = BLayer::Move (board, sq, m, Queen ); + const uint64_t queenNodes = PLayer::Perft(board, depth - 1); BLayer::UndoMove(board, state, sq, m); nodes += queenNodes; - if (Divide) LogMove(sq, m, queenNodes); + if (Divide) LogMove(sq, m, queenNodes); - state = BLayer::Move(board, sq, m, Rook ); - const uint64_t rookNodes = PLayer::Perft(board, depth - 1); + state = BLayer::Move (board, sq, m, Rook ); + const uint64_t rookNodes = PLayer::Perft(board, depth - 1); BLayer::UndoMove(board, state, sq, m); nodes += rookNodes; - if (Divide) LogMove(sq, m, rookNodes); + if (Divide) LogMove(sq, m, rookNodes); - state = BLayer::Move(board, sq, m, Bishop); + state = BLayer::Move (board, sq, m, Bishop); const uint64_t bishopNodes = PLayer::Perft(board, depth - 1); BLayer::UndoMove(board, state, sq, m); nodes += bishopNodes; if (Divide) LogMove(sq, m, bishopNodes); - state = BLayer::Move(board, sq, m, Knight); + state = BLayer::Move (board, sq, m, Knight); const uint64_t knightNodes = PLayer::Perft(board, depth - 1); BLayer::UndoMove(board, state, sq, m); nodes += knightNodes; if (Divide) LogMove(sq, m, knightNodes); } else { - const PreviousState state = BLayer::Move (board, sq, m); - const uint64_t perftNodes = PLayer::Perft(board, depth - 1); + const PreviousState state = BLayer::Move (board, sq, m); + const uint64_t perftNodes = PLayer::Perft(board, depth - 1); BLayer::UndoMove(board, state, sq, m); nodes += perftNodes; if (Divide) LogMove(sq, m, perftNodes); } } - } else { - std::array psq = {}; - std::array, 64> futures = {}; - uint8_t count = pIterator.ToArray(psq); - - BS::blocks blocks(0, count, std::thread::hardware_concurrency()); - - auto ParallelComputation = - [depth, &board, &pin, &check, &psq, &blocks](const size_t b) -> uint64_t - { - const uint8_t start = blocks.start(b); - const uint8_t end = blocks. end(b); + } + else { + std::array psq = {}; + std::array result = {}; - uint64_t parallelNodes = 0 ; - uint8_t nextDepth = depth - 1; + const uint8_t count = pIterator.ToArray(psq); - Board parallelBoard = board; + using Block = drjit::blocked_range; - for (uint8_t i = start; i < end; i++) { - const Square sq = psq[i]; + auto Loop = [depth, &board, &pin, &check, &psq](const Block block) -> uint64_t + { + const uint8_t start = block.begin(); + const uint8_t end = block. end(); - MoveList moves (parallelBoard, sq, pin, check); - if (moves.Count() < 1) return 0; + const uint8_t nextDepth = depth - 1; - BitBoardIterator mIterator = moves.Iterator(); + Board parallelBoard = board; - for (Square m = mIterator.Value(); m != NASQ; m = mIterator.Value()) { - if (moves.Promotion(sq)) { - PreviousState state = - BLayer::Move(parallelBoard, sq, m, Queen); - const uint64_t queenNodes = PLayer::Perft(parallelBoard, nextDepth); - BLayer::UndoMove(parallelBoard, state, sq, m); - parallelNodes += queenNodes; + uint64_t parallelNodes = 0; - if (Divide) LogMove(sq, m, queenNodes); + for (uint8_t i = start; i < end; i++) { + const Square sq = psq[i]; - state = BLayer::Move(parallelBoard, sq, m, Rook); - const uint64_t rookNodes = PLayer::Perft(parallelBoard, nextDepth); - BLayer::UndoMove(parallelBoard, state, sq, m); - parallelNodes += rookNodes; + MoveList moves (parallelBoard, sq, pin, check); + if (moves.Count() < 1) continue; - if (Divide) LogMove(sq, m, rookNodes); + BitBoardIterator mIterator = moves.Iterator(); - state = BLayer::Move(parallelBoard, sq, m, Bishop); - const uint64_t bishopNodes = PLayer::Perft(parallelBoard, nextDepth); - BLayer::UndoMove(parallelBoard, state, sq, m); - parallelNodes += bishopNodes; + for (Square m = mIterator.Value(); m != NASQ; m = mIterator.Value()) { + if (moves.Promotion(sq)) { + PreviousState state = BLayer::Move (parallelBoard, sq, m, Queen ); + const uint64_t queenNodes = PLayer::Perft(parallelBoard, nextDepth); + BLayer::UndoMove(parallelBoard, state, sq, m); + parallelNodes += queenNodes; - if (Divide) LogMove(sq, m, bishopNodes); + if (Divide) LogMove(sq, m, queenNodes); - state = BLayer::Move(parallelBoard, sq, m, Knight); - const uint64_t knightNodes = PLayer::Perft(parallelBoard, nextDepth); - BLayer::UndoMove(parallelBoard, state, sq, m); - parallelNodes += knightNodes; + state = BLayer::Move (parallelBoard, sq, m, Rook ); + const uint64_t rookNodes = PLayer::Perft(parallelBoard, nextDepth); + BLayer::UndoMove(parallelBoard, state, sq, m); + parallelNodes += rookNodes; - if (Divide) LogMove(sq, m, knightNodes); - } else { - const PreviousState state = BLayer::Move(parallelBoard, sq, m); - const uint64_t perftNodes = PLayer::Perft(parallelBoard, nextDepth); - BLayer::UndoMove(parallelBoard, state, sq, m); - parallelNodes += perftNodes; + if (Divide) LogMove(sq, m, rookNodes); - if (Divide) LogMove(sq, m, perftNodes); - } - } - } + state = BLayer::Move (parallelBoard, sq, m, Bishop); + const uint64_t bishopNodes = PLayer::Perft(parallelBoard, nextDepth); + BLayer::UndoMove(parallelBoard, state, sq, m); + parallelNodes += bishopNodes; - return parallelNodes; - }; + if (Divide) LogMove(sq, m, bishopNodes); - size_t b = 0; - while (b < blocks.get_num_blocks()) { - futures[b] = std::async(std::launch::async, ParallelComputation, b); + state = BLayer::Move (parallelBoard, sq, m, Knight); + const uint64_t knightNodes = PLayer::Perft(parallelBoard, nextDepth); + BLayer::UndoMove(parallelBoard, state, sq, m); + parallelNodes += knightNodes; - b++; - } + if (Divide) LogMove(sq, m, knightNodes); + } else { + const PreviousState state = BLayer::Move (parallelBoard, sq, m); + const uint64_t perftNodes = PLayer::Perft(parallelBoard, nextDepth); + BLayer::UndoMove(parallelBoard, state, sq, m); + parallelNodes += perftNodes; - for (size_t f = 0; f < b; f++) { - nodes += futures[f].get(); + if (Divide) LogMove(sq, m, perftNodes); + } + } } - } - - return nodes; - } - template - static void LogMove(const Square from, const Square to, const uint64_t nodes) - { - std::string logEntry = Util::SquareToString(from) + Util::SquareToString(to); - if (Promotion != NAP) logEntry += static_cast(tolower(FirstLetter(Promotion))); - logEntry += ": " + std::to_string(nodes) + "\n"; - std::cout << logEntry; - } + return parallelNodes; + }; - public: - static void SetBoard(const std::string& fen) - { - PerftBoard = Board(fen); - } + ThreadPool.For( + Block(0, count), + [&Loop, &result](const Block block) -> void + { + result[block.begin()] = Loop(block); + } + ); - static void SetBoard(const Board& board) - { - PerftBoard = board; + for (size_t i = 0; i < 8; i++) nodes += result[i]; } -// static void SetTranspositionTable(const uint64_t bytes) -// { -// std::cout << "Allocating table using defined bytes (" << bytes << ")\n"; -// TranspositionTable = StockDory::TranspositionTable(bytes); -// std::cout << "Table: " << TranspositionTable.Size() << " entries\n"; -// std::cout << "Table: " << TranspositionTable.Size() * sizeof(PEntry) << " bytes\n"; -// } + return nodes; + } - template - static void Perft(const uint8_t depth) - { - std::cout << "Running PERFT @ depth " << static_cast(depth) << ":" << std::endl; - - auto start = std::chrono::high_resolution_clock::now(); - const uint64_t nodes = - PerftBoard.ColorToMove() == White ? - Perft(PerftBoard, depth) : - Perft(PerftBoard, depth) ; - auto stop = std::chrono::high_resolution_clock::now(); - auto time = std::chrono::duration_cast(stop - start).count(); + template + static void LogMove(const Square from, const Square to, const uint64_t nodes) + { + std::string logEntry = ToString(from) + ToString(to); + if (Promotion != NAP) logEntry += static_cast(tolower(FirstLetter(Promotion))); + logEntry += ": " + std::to_string(nodes) + "\n"; + std::cout << logEntry; + } - std::cout << "Searched " << nodes << " nodes. (" << time << "µs)" << std::endl; - } + public: + static void SetBoard(const std::string& fen) + { + PerftBoard = Board(fen); + } + + static void SetBoard(const Board& board) + { + PerftBoard = board; + } + + // static void SetTranspositionTable(const uint64_t bytes) + // { + // std::cout << "Allocating table using defined bytes (" << bytes << ")\n"; + // TranspositionTable = StockDory::TranspositionTable(bytes); + // std::cout << "Table: " << TranspositionTable.Size() << " entries\n"; + // std::cout << "Table: " << TranspositionTable.Size() * sizeof(PEntry) << " bytes\n"; + // } + + template + static void Perft(const uint8_t depth) + { + static const std::regex comma ("(\\d)(?=(\\d{3})+(?!\\d))"); + + std::cout << "Running PERFT @ depth " << static_cast(depth) << " "; + std::cout << "[Maximum Concurrency: " << ThreadPool.Size() << "t]:"; + std::cout << std::endl; + + const auto start = std::chrono::high_resolution_clock::now(); + uint64_t nodes = 0; + + if (ThreadPool.Size() > 1) + nodes = PerftBoard.ColorToMove() == White + ? Perft(PerftBoard, depth) + : Perft(PerftBoard, depth); + else + nodes = PerftBoard.ColorToMove() == White + ? Perft(PerftBoard, depth) + : Perft(PerftBoard, depth); + + const auto stop = std::chrono::high_resolution_clock::now(); + const auto time = std::chrono::duration_cast(stop - start).count(); + + const double_t t = static_cast(time) / 1000000; + const uint64_t nps = static_cast(nodes / t ); + + std::cout << std::endl; + + std::cout << "Nodes searched: " << std::regex_replace(std::to_string(nodes), comma, "$1,"); + std::cout << std::endl; + std::cout << "Time taken: " << t << "s"; + std::cout << std::endl; + std::cout << "Speed: " << std::regex_replace(std::to_string(nps), comma, "$1,") << " nps"; + std::cout << std::endl; + } }; } // Perft -StockDory::Board StockDory::PerftRunner::PerftBoard = StockDory::Board(); -//StockDory::TranspositionTable StockDory::Perft::PerftRunner::TranspositionTable = -// StockDory::TranspositionTable(0); +StockDory::Board StockDory::PerftRunner::PerftBoard = Board(); +// StockDory::TranspositionTable StockDory::Perft::PerftRunner::TranspositionTable = +// StockDory::TranspositionTable(0); #endif //STOCKDORY_PERFTRUNNER_H diff --git a/src/Terminal/UCI/UCIInterface.h b/src/Terminal/UCI/UCIInterface.h index 4acdf255..aa2ce556 100644 --- a/src/Terminal/UCI/UCIInterface.h +++ b/src/Terminal/UCI/UCIInterface.h @@ -6,26 +6,27 @@ #ifndef STOCKDORY_UCIINTERFACE_H #define STOCKDORY_UCIINTERFACE_H -#include -#include +#include #include +#include #include -#include +#include #include "Information.h" #include "../../External/strutil.h" #include "../../Backend/Board.h" -#include "../../Backend/Util.h" +#include "../../Backend/Misc.h" -#include "../../Engine/Time/TimeManager.h" #include "../../Engine/Search.h" +#include "../../Engine/Time/TimeManager.h" -#include "UCISearch.h" -#include "UCIOption.h" #include "../Perft/PerftRunner.h" +#include "UCIOption.h" +#include "UCISearch.h" + namespace StockDory { @@ -35,280 +36,283 @@ namespace StockDory using Arguments = std::vector; using CommandHandler = std::function; using CommandSwitch = std::unordered_map; - using OptionSwitch = std::unordered_map>; + using OptionSwitch = std::unordered_map >; - private: - static bool Running; + static bool Running; - static bool UciPrompted; + static bool UciPrompted; - static CommandSwitch UCICommandSwitch; - static OptionSwitch UCIOptionSwitch; + static CommandSwitch UCICommandSwitch; + static OptionSwitch UCIOptionSwitch; - static Board MainBoard ; - static RepetitionHistory MainHistory ; - static uint8_t HalfMoveCounter; + static Board MainBoard; + static RepetitionHistory MainHistory; + static uint8_t HalfMoveCounter; - static UCISearch Search; + static UCISearch Search; public: - static void Launch() - { - RegisterOptions(); - RegisterCommands(); + static void Launch() + { + RegisterOptions(); + RegisterCommands(); - std::string input; - while (Running && std::getline(std::cin, input)) HandleInput(input); - } + std::string input; + while (Running && std::getline(std::cin, input)) HandleInput(input); + } private: - static void RegisterCommands() - { - UCICommandSwitch.emplace("uci" , [](const Arguments& ) { Uci ( ); }); - UCICommandSwitch.emplace("setoption" , [](const Arguments& args) { SetOption (args); }); - UCICommandSwitch.emplace("quit" , [](const Arguments& ) { Quit ( ); }); - UCICommandSwitch.emplace("ucinewgame", [](const Arguments& ) { UciNewGame ( ); }); - UCICommandSwitch.emplace("isready" , [](const Arguments& ) { IsReady ( ); }); - UCICommandSwitch.emplace("info" , [](const Arguments& args) { Info (args); }); - UCICommandSwitch.emplace("position" , [](const Arguments& args) { HandlePosition(args); }); - UCICommandSwitch.emplace("go" , [](const Arguments& args) { HandleGo (args); }); - UCICommandSwitch.emplace("stop" , [](const Arguments& ) { HandleStop ( ); }); - } - - static void RegisterOptions() - { - std::shared_ptr > hash = - std::make_shared> - ("Hash", 16, 1, 16384, [](const uint64_t& value) { - if (value < 1 ) { - std::cerr << "ERROR: Hash must be at least 1 MB" << std::endl; - return; - } else if (value > 16384) { - std::cerr << "ERROR: Hash must be at most 16 GB" << std::endl; - return; + static void RegisterCommands() + { + UCICommandSwitch.emplace("uci", [](const Arguments& ) { Uci(); }); + UCICommandSwitch.emplace("setoption", [](const Arguments& args) { SetOption(args); }); + UCICommandSwitch.emplace("quit", [](const Arguments& ) { Quit(); }); + UCICommandSwitch.emplace("ucinewgame", [](const Arguments& ) { UciNewGame(); }); + UCICommandSwitch.emplace("isready", [](const Arguments& ) { IsReady(); }); + UCICommandSwitch.emplace("info", [](const Arguments& args) { Info(args); }); + UCICommandSwitch.emplace("position", [](const Arguments& args) { HandlePosition(args); }); + UCICommandSwitch.emplace("go", [](const Arguments& args) { HandleGo(args); }); + UCICommandSwitch.emplace("stop", [](const Arguments& ) { HandleStop(); }); + } + + static void RegisterOptions() + { + auto hash = + std::make_shared> + ("Hash", 16, 1, 16384, [](const size_t& value) -> void + { + if (value < 1) { + std::cerr << "ERROR: Hash must be at least 1 MB" << std::endl; + return; + } + if (value > 16384) { + std::cerr << "ERROR: Hash must be at most 16 GB" << std::endl; + return; + } + + TTable.Resize(value * MB); } - - TTable.Resize(value * MB); - }); - - std::shared_ptr > threads = - std::make_shared> - ("Threads", 1, 1, 128, [](const uint8_t& value) { - if (value != 1) { - std::cerr << "ERROR: Multithreading is not supported yet" << std::endl; - return; + ); + + auto threads = + std::make_shared> + ("Threads", 1, 1, ThreadPool::HardwareLimit(), [](const size_t& value) -> void + { + if (value < 1) { + std::cerr << "ERROR: Maximum thread count must be at least 1" << std::endl; + return; + } + + if (value > ThreadPool::HardwareLimit()) { + std::cerr << "ERROR: Maximum thread count exceeds number of logical processors" << std::endl; + return; + } + + ThreadPool.Resize(value); } - }); + ); - UCIOptionSwitch.emplace(hash ->GetName(), hash ); - UCIOptionSwitch.emplace(threads->GetName(), threads); - } + UCIOptionSwitch.emplace( hash->GetName(), hash ); + UCIOptionSwitch.emplace(threads->GetName(), threads); + } - static void HandleInput(const std::string& input) - { - const Arguments tokens = strutil::split(input, ' '); - const std::string command = strutil::to_lower(tokens[0]); - if (!UCICommandSwitch.contains(command)) return; + static void HandleInput(const std::string& input) + { + const Arguments tokens = strutil::split(input, ' '); + const std::string command = strutil::to_lower(tokens[0]); + if (!UCICommandSwitch.contains(command)) return; - const Arguments args = {tokens.begin() + 1, tokens.end()}; - UCICommandSwitch[command](args); - } + const Arguments args = {tokens.begin() + 1, tokens.end()}; + UCICommandSwitch[command](args); + } - static void Uci() - { - if (UciPrompted) return; + static void Uci() + { + if (UciPrompted) return; - std::stringstream ss; - ss << "id name " << NAME << " " << VERSION << "\n"; - ss << "id nnue " << StockDory::Evaluation::Name() << "\n"; - ss << "id author " << AUTHOR << "\n"; - ss << "id license " << LICENSE << "\n"; + std::stringstream ss; + ss << "id name " << NAME << " " << VERSION << "\n"; + ss << "id nnue " << Evaluation::Name() << "\n"; + ss << "id author " << AUTHOR << "\n"; + ss << "id license " << LICENSE << "\n"; - for (const auto& [_, option] : UCIOptionSwitch) - ss << option->Log() << "\n"; + for (const auto& option: UCIOptionSwitch | std::views::values) + ss << option->Log() << "\n"; - ss << "uciok"; + ss << "uciok"; - std::cout << ss.str() << std::endl; - UciPrompted = true; - } + std::cout << ss.str() << std::endl; + UciPrompted = true; + } - static void SetOption(const Arguments& args) - { - if (!UciPrompted || args.size() < 4) return; + static void SetOption(const Arguments& args) + { + if (!UciPrompted || args.size() < 4) return; - if (!UCIOptionSwitch.contains(args[1])) return; + if (!UCIOptionSwitch.contains(args[1])) return; - const std::vector parameterLeading = {args.begin() + 3, args.end()}; - const std::string parameter = strutil::join(parameterLeading, ""); + const std::vector parameterLeading = {args.begin() + 3, args.end()}; + const std::string parameter = strutil::join(parameterLeading, ""); - UCIOptionSwitch[args[1]]->Set(parameter); - } + UCIOptionSwitch[args[1]]->Set(parameter); + } - static void UciNewGame() - { - if (!UciPrompted) return; + static void UciNewGame() + { + if (!UciPrompted) return; - Search.Stop(); + Search.Stop(); - MainBoard = Board(); - MainHistory = RepetitionHistory(MainBoard.Zobrist()); - TTable.Clear(); - } + MainBoard = Board(); + MainHistory = RepetitionHistory(MainBoard.Zobrist()); + TTable.Clear(); + } - static void IsReady() - { - if (!UciPrompted) return; + static void IsReady() + { + if (!UciPrompted) return; - std::cout << "readyok" << std::endl; - } + std::cout << "readyok" << std::endl; + } - static void Quit() - { - Search.Stop(); - Running = false; - } + static void Quit() + { + Search.Stop(); + Running = false; + } - static void Info(const Arguments& args) - { - if (!UciPrompted || Search.IsRunning()) return; + static void Info(const Arguments& args) + { + if (!UciPrompted || Search.IsRunning()) return; - MainBoard.LoadForEvaluation(); - const int32_t evaluation = StockDory::Evaluation::Evaluate(MainBoard.ColorToMove()); + MainBoard.LoadForEvaluation(); + const int32_t evaluation = Evaluation::Evaluate(MainBoard.ColorToMove()); - std::stringstream ss; - ss << "FEN: " << MainBoard.Fen() << "\n"; - ss << "Hash: " << Util::ToHex(MainBoard.Zobrist()) << "\n"; - ss << "Evaluation: " << evaluation; + std::stringstream ss; + ss << "FEN: " << MainBoard.Fen() << "\n"; + ss << "Hash: " << ToHex(MainBoard.Zobrist()) << "\n"; + ss << "Evaluation: " << evaluation; - if (!args.empty() && strutil::compare_ignore_case(args[0], "moves")) { - ss << "\nMoves: "; - if (MainBoard.ColorToMove() == White) { - OrderedMoveList moves(MainBoard, 0, - KillerTable(), HistoryTable(), Move()); + if (!args.empty() && strutil::compare_ignore_case(args[0], "moves")) { + ss << "\nMoves: "; + if (MainBoard.ColorToMove() == White) { + OrderedMoveList moves (MainBoard, 0, {}, {}, {}); - for (uint8_t i = 0; i < moves.Count(); i++) ss << "\n" << moves[i].ToString(); - } else { - OrderedMoveList moves(MainBoard, 0, - KillerTable(), HistoryTable(), Move()); + for (uint8_t i = 0; i < moves.Count(); i++) ss << "\n" << moves[i].ToString(); + } else { + OrderedMoveList moves (MainBoard, 0, {}, {}, {}); - for (uint8_t i = 0; i < moves.Count(); i++) ss << "\n" << moves[i].ToString(); - } + for (uint8_t i = 0; i < moves.Count(); i++) ss << "\n" << moves[i].ToString(); } - - std::cout << ss.str() << std::endl; } - static void HandlePosition(const Arguments& args) - { - if (!UciPrompted) return; - - uint8_t moveStrIndex = 2; - if (strutil::compare_ignore_case(args[0], "fen")) { - const Arguments fenToken = {args.begin() + 1, args.begin() + 7}; - const std::string fen = strutil::join(fenToken, " "); - MainBoard = Board(fen); - MainHistory = RepetitionHistory(MainBoard.Zobrist()); - HalfMoveCounter = std::stoi(fenToken[4]); - moveStrIndex = 8; - } else if (strutil::compare_ignore_case(args[0], "startpos")) { - MainBoard = Board(); - MainHistory = RepetitionHistory(MainBoard.Zobrist()); - HalfMoveCounter = 0; - } else return; - - if (args.size() >= moveStrIndex && - strutil::compare_ignore_case(args[moveStrIndex - 1], "moves")) { - const Arguments movesToken = {args.begin() + moveStrIndex, args.end()}; - - for (const std::string& moveStr : movesToken) { - const Move move = Move::FromString(moveStr); - - if (MainBoard[move.To()].Piece() != NAP || MainBoard[move.From()].Piece() == Pawn) - HalfMoveCounter = 0; - else HalfMoveCounter++ ; - - MainBoard.Move(move.From(), move.To(), move.Promotion()); - MainHistory.Push(MainBoard.Zobrist()); - } + std::cout << ss.str() << std::endl; + } + + static void HandlePosition(const Arguments& args) + { + if (!UciPrompted) return; + + uint8_t moveStrIndex = 2; + if (strutil::compare_ignore_case(args[0], "fen")) { + const Arguments fenToken = {args.begin() + 1, args.begin() + 7}; + const std::string fen = strutil::join(fenToken, " "); + MainBoard = Board(fen); + MainHistory = RepetitionHistory(MainBoard.Zobrist()); + HalfMoveCounter = std::stoi(fenToken[4]); + moveStrIndex = 8; + } else if (strutil::compare_ignore_case(args[0], "startpos")) { + MainBoard = Board(); + MainHistory = RepetitionHistory(MainBoard.Zobrist()); + HalfMoveCounter = 0; + } else return; + + if (args.size() >= moveStrIndex && + strutil::compare_ignore_case(args[moveStrIndex - 1], "moves")) + for (const Arguments movesToken = {args.begin() + moveStrIndex, args.end()}; + const std::string& moveStr: movesToken) { + const Move move = Move::FromString(moveStr); + + if (MainBoard[move.To()].Piece() != NAP || MainBoard[move.From()].Piece() == Pawn) + HalfMoveCounter = 0; + else HalfMoveCounter++; + + MainBoard.Move(move.From(), move.To(), move.Promotion()); + MainHistory.Push(MainBoard.Zobrist()); } + } + + template + [[nodiscard]] + // ReSharper disable once CppDFAConstantParameter + static T TokenToValue(const Arguments& args, const std::string& token, const T defaultValue) + { + for (size_t i = 0; i < args.size(); i++) + if (strutil::compare_ignore_case(args[i], token) && i + 1 < args.size()) + return static_cast(std::stoull(args[i + 1])); + + return defaultValue; + } + + static void HandleGo(const Arguments& args) + { + if (!UciPrompted || Search.IsRunning()) return; + + if (args.size() > 1 && strutil::compare_ignore_case(args[0], "perft")) { + const auto depth = static_cast(std::stoull(args[1])); + PerftRunner::SetBoard(MainBoard); + PerftRunner::Perft(depth); + return; } - template - [[nodiscard]] - static T TokenToValue(const Arguments& args, const std::string& token, const T defaultValue) - { - for (size_t i = 0; i < args.size(); i++) - if (strutil::compare_ignore_case(args[i], token) && i + 1 < args.size()) - return static_cast(std::stoull(args[i + 1])); - - return defaultValue; + TimeControl timeControl = TimeManager::Default(); + auto limit = Limit(); + + if (args.size() == 2) { + if ( strutil::compare_ignore_case(args[0], "movetime")) + timeControl = TimeManager::Fixed(std::stoull(args[1])); + else if (strutil::compare_ignore_case(args[0], "depth")) + limit = Limit(static_cast(std::stoull(args[1]))); + else if (strutil::compare_ignore_case(args[0], "nodes")) + limit = Limit(static_cast(std::stoull(args[1]))); + } else if (args.size() > 2) { + const TimeData timeData{ + .WhiteTime = TokenToValue(args, "wtime" , 0), + .BlackTime = TokenToValue(args, "btime" , 0), + .WhiteIncrement = TokenToValue(args, "winc" , 0), + .BlackIncrement = TokenToValue(args, "binc" , 0), + .MovesToGo = TokenToValue(args, "movestogo", 0) + }; + + timeControl = TimeManager::Optimal(MainBoard, timeData); } - static void HandleGo(const Arguments& args) - { - if (!UciPrompted || Search.IsRunning()) return; + Search.Stop(); + Search = UCISearch(MainBoard, timeControl, MainHistory, HalfMoveCounter); + Search.Start(limit); + } - if (args.size() > 1 && strutil::compare_ignore_case(args[0], "perft")) { - const auto depth = static_cast(std::stoull(args[1])); - StockDory::PerftRunner::SetBoard(MainBoard); - StockDory::PerftRunner::Perft(depth); - return; - } - - TimeControl timeControl = TimeManager::Default(); - Limit limit = Limit() ; - - if (args.size() == 2) { - if (strutil::compare_ignore_case(args[0], "movetime")) - timeControl = TimeManager::Fixed(std::stoull(args[1])); - else if (strutil::compare_ignore_case(args[0], "depth" )) - limit = Limit(static_cast< uint8_t>(std::stoull(args[1]))); - else if (strutil::compare_ignore_case(args[0], "nodes" )) - limit = Limit(static_cast(std::stoull(args[1]))); - } else if (args.size() > 2) { - const TimeData timeData { - .WhiteTime = TokenToValue(args, "wtime" , 0), - .BlackTime = TokenToValue(args, "btime" , 0), - .WhiteIncrement = TokenToValue(args, "winc" , 0), - .BlackIncrement = TokenToValue(args, "binc" , 0), - .MovesToGo = TokenToValue(args, "movestogo", 0) - }; - - timeControl = TimeManager::Optimal(MainBoard, timeData); - } - - Search.Stop(); - Search = UCISearch(MainBoard, timeControl, MainHistory, HalfMoveCounter); - Search.Start(limit); - } + static void HandleStop() + { + if (!UciPrompted || !Search.IsRunning()) return; - static void HandleStop() - { - if (!UciPrompted || !Search.IsRunning()) return; - - Search.Stop(); - } + Search.Stop(); + } }; } // StockDory -bool StockDory::UCIInterface::Running = true; - +bool StockDory::UCIInterface::Running = true ; bool StockDory::UCIInterface::UciPrompted = false; -StockDory::UCIInterface::CommandSwitch StockDory::UCIInterface::UCICommandSwitch = - StockDory::UCIInterface::CommandSwitch(); - -StockDory::UCIInterface::OptionSwitch StockDory::UCIInterface::UCIOptionSwitch = - StockDory::UCIInterface::OptionSwitch(); +StockDory::UCIInterface::CommandSwitch StockDory::UCIInterface::UCICommandSwitch = CommandSwitch(); +StockDory::UCIInterface::OptionSwitch StockDory::UCIInterface::UCIOptionSwitch = OptionSwitch(); -StockDory::Board StockDory::UCIInterface::MainBoard = StockDory::Board(); -StockDory::RepetitionHistory StockDory::UCIInterface::MainHistory = - StockDory::RepetitionHistory(MainBoard.Zobrist()); -uint8_t StockDory::UCIInterface::HalfMoveCounter = 0; +StockDory::Board StockDory::UCIInterface::MainBoard = Board(); +StockDory::RepetitionHistory StockDory::UCIInterface::MainHistory = RepetitionHistory(MainBoard.Zobrist()); -StockDory::UCISearch StockDory::UCIInterface::Search = StockDory::UCISearch(); +uint8_t StockDory::UCIInterface::HalfMoveCounter = 0 ; +StockDory::UCISearch StockDory::UCIInterface::Search = UCISearch(); #endif //STOCKDORY_UCIINTERFACE_H diff --git a/src/Terminal/UCI/UCIOption.h b/src/Terminal/UCI/UCIOption.h index 44f14104..57accbe9 100644 --- a/src/Terminal/UCI/UCIOption.h +++ b/src/Terminal/UCI/UCIOption.h @@ -7,10 +7,10 @@ #define STOCKDORY_UCIOPTION_H #include -#include -#include #include #include +#include +#include namespace StockDory { @@ -19,94 +19,93 @@ namespace StockDory { public: - virtual ~UCIOptionBase() = default; + virtual ~UCIOptionBase() = default; - [[nodiscard]] - virtual std::string Log() const = 0; + [[nodiscard]] + virtual std::string Log() const = 0; - virtual void Set(const std::string& value) = 0; + virtual void Set(const std::string& value) = 0; }; template - class UCIOption : public UCIOptionBase + class UCIOption final : public UCIOptionBase { - private: - using Handler = std::function; + using Handler = std::function; - std::string Name; + std::string Name; - T Default; + T Default; - typename std::enable_if, T>::type Min; - typename std::enable_if, T>::type Max; + std::enable_if_t, T> Min; + std::enable_if_t, T> Max; - Handler OptionHandler; + Handler OptionHandler; public: - UCIOption(const std::string& name, const T& defaultValue, const Handler& handler) - { - static_assert(std::is_integral::value, "Min and Max must be defined for integral types."); - - Name = name; - Default = defaultValue; - OptionHandler = handler; - } - - UCIOption(const std::string& name, const T& defaultValue, const T& min, const T& max, const Handler& handler) - { - static_assert(std::is_integral_v, "Min and Max can only be used with integral types."); - - Name = name; - Default = defaultValue; - Min = min; - Max = max; - OptionHandler = handler; - } - - [[nodiscard]] - std::string GetName() const - { - return Name; + UCIOption(const std::string& name, const T& defaultValue, const Handler& handler) + { + static_assert(std::is_integral_v, "Min and Max must be defined for integral types."); + + Name = name; + Default = defaultValue; + OptionHandler = handler; + } + + UCIOption(const std::string& name, const T& defaultValue, const T& min, const T& max, const Handler& handler) + { + static_assert(std::is_integral_v, "Min and Max can only be used with integral types."); + + Name = name; + Default = defaultValue; + Min = min; + Max = max; + OptionHandler = handler; + } + + [[nodiscard]] + std::string GetName() const + { + return Name; + } + + [[nodiscard]] + std::string Log() const override + { + std::stringstream output; + + output << "option name " << Name << " "; + + if (std::is_integral_v) + output << "type spin "; + else if (std::is_same_v) + output << "type check "; + else if (std::is_same_v) + output << "type string "; + else throw std::runtime_error("Unsupported type."); + + if (std::is_same_v) + output << "default " << static_cast(Default); + else if (std::is_same_v) + output << "default " << static_cast< int16_t>(Default); + else output << "default " << Default; + + if (std::is_integral_v) { + if (std::is_same_v) + output << " min " << static_cast(Min) << " max " << static_cast(Max); + else if (std::is_same_v) + output << " min " << static_cast< int16_t>(Min) << " max " << static_cast< int16_t>(Max); + else output << " min " << Min << " max " << Max; } - [[nodiscard]] - std::string Log() const override - { - std::stringstream output; - - output << "option name " << Name << " "; - - if (std::is_integral_v) - output << "type spin "; - else if (std::is_same_v) - output << "type check "; - else if (std::is_same_v) - output << "type string "; - else throw std::runtime_error("Unsupported type."); - - if (std::is_same_v) - output << "default " << static_cast(Default); - else if (std::is_same_v) - output << "default " << static_cast< int16_t>(Default); - else output << "default " << Default; - - if (std::is_integral_v) { - if (std::is_same_v) - output << " min " << static_cast(Min) << " max " << static_cast(Max); - else if (std::is_same_v) - output << " min " << static_cast< int16_t>(Min) << " max " << static_cast< int16_t>(Max); - else output << " min " << Min << " max " << Max; - } - - return output.str(); - } + return output.str(); + } - void Set(const std::string& value) override - { - OptionHandler(strutil::parse_string(value)); - } + void Set(const std::string& value) override + { + OptionHandler(strutil::parse_string(value)); + } }; diff --git a/src/Terminal/UCI/UCISearch.h b/src/Terminal/UCI/UCISearch.h index 56919ee1..9ec2e48e 100644 --- a/src/Terminal/UCI/UCISearch.h +++ b/src/Terminal/UCI/UCISearch.h @@ -13,95 +13,103 @@ namespace StockDory { - class UCISearchLogger + class UCIHandler { + using PVEntry = PrincipleVariationEntry; + + static std::string PvLine(const PVEntry& pv) + { + std::stringstream line; + + for (uint8_t i = 0; i < pv.Ply; i++) { + line << pv.PV[i].ToString(); + if (i != pv.Ply - 1) line << " "; + } + + return line.str(); + } + public: - static void LogDepthIteration(const uint8_t depth, const uint8_t selectiveDepth, const int32_t evaluation, - const uint64_t nodes, const uint64_t ttNodes, - const MS time, const std::string& pv) - { - std::stringstream output; + static void HandleDepthIteration(const uint8_t depth, const uint8_t selectiveDepth, const int32_t evaluation, + const uint64_t nodes, const uint64_t _ , + const MS time, const PVEntry& pv) + { + std::stringstream output; - int64_t displayedTime = time.count(); - displayedTime = std::max(displayedTime, static_cast(1)); + int64_t displayedTime = time.count(); + displayedTime = std::max(displayedTime, static_cast(1)); - const auto nps = static_cast(static_cast(nodes) / - (static_cast(displayedTime) / static_cast(1000))); + const auto nps = static_cast(static_cast(nodes) / + (static_cast(displayedTime) / static_cast(1000))); - output << "info "; - output << "depth " << static_cast(depth) << " "; - output << "seldepth " << static_cast(selectiveDepth) << " "; - output << "score "; + output << "info "; + output << "depth " << static_cast( depth) << " "; + output << "seldepth " << static_cast(selectiveDepth) << " "; + output << "score "; - if (abs(evaluation) > Infinity - MaxDepth) - output << "mate " << (evaluation > 0 ? Infinity - evaluation : -Infinity - evaluation) / 2 << " "; - else - output << "cp " << evaluation << " "; + if (abs(evaluation) > Infinity - MaxDepth) + output << "mate " << (evaluation > 0 ? Infinity - evaluation : -Infinity - evaluation) / 2 << " "; + else + output << "cp " << evaluation << " "; - output << "nodes " << nodes << " "; - output << "ttnodes " << ttNodes << " "; - output << "time " << displayedTime << " "; - output << "nps " << nps << " "; - output << "pv " << pv; + output << "nodes " << nodes << " "; - std::cout << output.str() << std::endl; - } + output << "nps " << nps << " "; + output << "time " << displayedTime << " "; + output << "pv " << PvLine(pv); - static void LogBestMove(const Move& move) - { - std::stringstream output; - output << "bestmove " << move.ToString(); + std::cout << output.str() << std::endl; + } - std::cout << output.str() << std::endl; - } + static void HandleBestMove(const Move move) + { + std::stringstream output; + output << "bestmove " << move.ToString(); + + std::cout << output.str() << std::endl; + } }; -#pragma clang diagnostic push -#pragma ide diagnostic ignored "UnusedValue" class UCISearch { - using Search = Search; + using Search = Search; - private: - Search EngineSearch; + Search EngineSearch; - bool Running = false; + bool Running = false; public: - UCISearch() = default; - - UCISearch(const Board& board, const StockDory::TimeControl& timeControl, - const RepetitionHistory& repetitionHistory, const uint8_t halfMoveCounter) - : EngineSearch(Search(board, timeControl, repetitionHistory, halfMoveCounter)) {} - - void Start(const Limit limit) - { - std::future _ = ThreadPool.submit( - [this](const Limit limit) { - Running = true; - EngineSearch.IterativeDeepening(limit); - Running = false; - }, - limit - ); - } + UCISearch() = default; - void Stop() - { - EngineSearch.ForceStop(); - } + UCISearch(const Board& board, const TimeControl& timeControl, + const RepetitionHistory& repetitionHistory, const uint8_t halfMoveCounter) + : EngineSearch(Search(board, timeControl, repetitionHistory, halfMoveCounter)) {} - [[nodiscard]] - bool IsRunning() const + void Start(const Limit limit) + { + ThreadPool.Execute([this, limit] -> void { - return Running; - } + Running = true ; + EngineSearch.IterativeDeepening(limit); + Running = false; + }); + } + + void Stop() + { + EngineSearch.ForceStop(); + } + + [[nodiscard]] + bool IsRunning() const + { + return Running; + } }; -#pragma clang diagnostic pop } // StockDory diff --git a/src/Terminal/main.cpp b/src/Terminal/main.cpp index 748faf69..e49a3327 100644 --- a/src/Terminal/main.cpp +++ b/src/Terminal/main.cpp @@ -7,9 +7,8 @@ #include "Information.h" -#include "UCI/UCIInterface.h" #include "BenchHash.h" -#include "NetworkConverter.h" +#include "UCI/UCIInterface.h" void DisplayTitle() { @@ -20,25 +19,17 @@ void DisplayTitle() std::cerr << ss.str() << std::endl; } -int main(int argc, char* argv[]) +int main(const int argc, const char* argv[]) { DisplayTitle(); if (argc > 1) { - if (strutil::compare_ignore_case(argv[1], "bench")) { + if (strutil::compare_ignore_case(argv[1], "bench" )) { StockDory::BenchHash::Run(); return EXIT_SUCCESS; - } else if (strutil::compare_ignore_case(argv[1], "convert")) { - StockDory::NetworkConverter::Launch(); - return EXIT_SUCCESS; } } - if (argc > 1 && strutil::compare_ignore_case(argv[1], "bench")) { - StockDory::BenchHash::Run(); - return EXIT_SUCCESS; - } - StockDory::UCIInterface::Launch(); return EXIT_SUCCESS;