diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..4eb64ffef42045c2050fbbd616ab6ef185f1a6e1
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,1184 @@
+# Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+# Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
+#
+# Stockfish is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Stockfish is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+
+### ==========================================================================
+### Section 1. General Configuration
+### ==========================================================================
+
+### Establish the operating system name
+KERNEL := $(shell uname -s)
+ifeq ($(KERNEL),Linux)
+ OS := $(shell uname -o)
+endif
+
+### Command prefix to run the built executable (e.g. wine, sde, qemu)
+### Backward compatible alias: WINE_PATH (deprecated)
+ifneq ($(strip $(WINE_PATH)),)
+ifeq ($(strip $(RUN_PREFIX)),)
+RUN_PREFIX := $(WINE_PATH)
+endif
+ifeq ($(MAKELEVEL),0)
+ifneq ($(strip $(RUN_PREFIX)),$(strip $(WINE_PATH)))
+$(warning *** Both RUN_PREFIX and WINE_PATH are set; ignoring WINE_PATH. ***)
+else
+$(warning *** WINE_PATH is deprecated; use RUN_PREFIX instead. ***)
+endif
+endif
+endif
+
+### Target Windows OS
+ifeq ($(OS),Windows_NT)
+ ifneq ($(COMP),ndk)
+ target_windows = yes
+ endif
+else ifeq ($(COMP),mingw)
+ target_windows = yes
+ ifeq ($(RUN_PREFIX),)
+ RUN_PREFIX := $(shell which wine)
+ endif
+endif
+
+### Executable name
+ifeq ($(target_windows),yes)
+ EXE = stockfish.exe
+else
+ EXE = stockfish
+endif
+
+### Installation dir definitions
+PREFIX = /usr/local
+BINDIR = $(PREFIX)/bin
+
+### Built-in benchmark for pgo-builds
+PGOBENCH = $(RUN_PREFIX) ./$(EXE) bench
+
+### Source and object files
+SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \
+ misc.cpp movegen.cpp movepick.cpp position.cpp \
+ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
+ nnue/nnue_accumulator.cpp nnue/nnue_misc.cpp nnue/network.cpp \
+ nnue/features/half_ka_v2_hm.cpp nnue/features/full_threats.cpp \
+ engine.cpp score.cpp memory.cpp
+
+HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h history.h \
+ nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/features/full_threats.h \
+ nnue/layers/affine_transform.h nnue/layers/affine_transform_sparse_input.h \
+ nnue/layers/clipped_relu.h nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h \
+ nnue/nnue_architecture.h nnue/nnue_common.h nnue/nnue_feature_transformer.h nnue/simd.h \
+ position.h search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \
+ tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h score.h numa.h memory.h shm.h shm_linux.h
+
+OBJS = $(notdir $(SRCS:.cpp=.o))
+
+VPATH = syzygy:nnue:nnue/features
+
+### ==========================================================================
+### Section 2. High-level Configuration
+### ==========================================================================
+#
+# flag --- Comp switch --- Description
+# ----------------------------------------------------------------------------
+#
+# debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode
+# sanitize = none/ ... (-fsanitize )
+# --- ( undefined ) --- enable undefined behavior checks
+# --- ( thread ) --- enable threading error checks
+# --- ( address ) --- enable memory access checks
+# --- ...etc... --- see compiler documentation for supported sanitizers
+# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations
+# arch = (name) --- (-arch) --- Target architecture
+# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
+# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction
+# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction
+# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction
+# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions
+# mmx = yes/no --- -mmmx --- Use Intel MMX instructions
+# sse2 = yes/no --- -msse2 --- Use Intel Streaming SIMD Extensions 2
+# ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3
+# sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1
+# avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2
+# avxvnni = yes/no --- -mavxvnni --- Use Intel Vector Neural Network Instructions AVX
+# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512
+# vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512
+# avx512icl = yes/no --- ... multiple ... --- Use All AVX-512 features available on both Intel Ice Lake and AMD Zen 4
+# altivec = yes/no --- -maltivec --- Use PowerPC Altivec SIMD extension
+# vsx = yes/no --- -mvsx --- Use POWER VSX SIMD extension
+# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture
+# dotprod = yes/no --- -DUSE_NEON_DOTPROD --- Use ARM advanced SIMD Int8 dot product instructions
+# lsx = yes/no --- -mlsx --- Use Loongson SIMD eXtension
+# lasx = yes/no --- -mlasx --- use Loongson Advanced SIMD eXtension
+#
+# Note that Makefile is space sensitive, so when adding new architectures
+# or modifying existing flags, you have to make sure there are no extra spaces
+# at the end of the line for flag values.
+#
+# Example of use for these flags:
+# make build ARCH=x86-64-avx512 debug=yes sanitize="address undefined"
+
+
+### 2.1. General and architecture defaults
+
+ifeq ($(ARCH),)
+ ARCH = native
+endif
+
+ifeq ($(ARCH), native)
+ override ARCH := $(shell $(SHELL) ../scripts/get_native_properties.sh | cut -d " " -f 1)
+endif
+
+# explicitly check for the list of supported architectures (as listed with make help),
+# the user can override with `make ARCH=x86-64-avx512icl SUPPORTED_ARCH=true`
+ifeq ($(ARCH), $(filter $(ARCH), \
+ x86-64-avx512icl x86-64-vnni512 x86-64-avx512 x86-64-avxvnni \
+ x86-64-bmi2 x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \
+ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-64-altivec ppc-64-vsx ppc-32 e2k \
+ armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 \
+ loongarch64 loongarch64-lsx loongarch64-lasx))
+ SUPPORTED_ARCH=true
+else
+ SUPPORTED_ARCH=false
+endif
+
+optimize = yes
+debug = no
+sanitize = none
+bits = 64
+prefetch = no
+popcnt = no
+pext = no
+sse = no
+mmx = no
+sse2 = no
+ssse3 = no
+sse41 = no
+avx2 = no
+avxvnni = no
+avx512 = no
+vnni512 = no
+avx512icl = no
+altivec = no
+vsx = no
+neon = no
+dotprod = no
+arm_version = 0
+lsx = no
+lasx = no
+STRIP = strip
+
+ifneq ($(shell which clang-format-20 2> /dev/null),)
+ CLANG-FORMAT = clang-format-20
+else
+ CLANG-FORMAT = clang-format
+endif
+
+### 2.2 Architecture specific
+
+ifeq ($(findstring x86,$(ARCH)),x86)
+
+# x86-32/64
+
+ifeq ($(findstring x86-32,$(ARCH)),x86-32)
+ arch = i386
+ bits = 32
+ sse = no
+ mmx = yes
+else
+ arch = x86_64
+ sse = yes
+ sse2 = yes
+endif
+
+ifeq ($(findstring -sse,$(ARCH)),-sse)
+ sse = yes
+endif
+
+ifeq ($(findstring -popcnt,$(ARCH)),-popcnt)
+ popcnt = yes
+endif
+
+ifeq ($(findstring -mmx,$(ARCH)),-mmx)
+ mmx = yes
+endif
+
+ifeq ($(findstring -sse2,$(ARCH)),-sse2)
+ sse = yes
+ sse2 = yes
+endif
+
+ifeq ($(findstring -ssse3,$(ARCH)),-ssse3)
+ sse = yes
+ sse2 = yes
+ ssse3 = yes
+endif
+
+ifeq ($(findstring -sse41,$(ARCH)),-sse41)
+ sse = yes
+ sse2 = yes
+ ssse3 = yes
+ sse41 = yes
+endif
+
+ifeq ($(findstring -modern,$(ARCH)),-modern)
+ $(warning *** ARCH=$(ARCH) is deprecated, defaulting to ARCH=x86-64-sse41-popcnt. Execute `make help` for a list of available architectures. ***)
+ $(shell sleep 5)
+ popcnt = yes
+ sse = yes
+ sse2 = yes
+ ssse3 = yes
+ sse41 = yes
+endif
+
+ifeq ($(findstring -avx2,$(ARCH)),-avx2)
+ popcnt = yes
+ sse = yes
+ sse2 = yes
+ ssse3 = yes
+ sse41 = yes
+ avx2 = yes
+endif
+
+ifeq ($(findstring -avxvnni,$(ARCH)),-avxvnni)
+ popcnt = yes
+ sse = yes
+ sse2 = yes
+ ssse3 = yes
+ sse41 = yes
+ avx2 = yes
+ avxvnni = yes
+ pext = yes
+endif
+
+ifeq ($(findstring -bmi2,$(ARCH)),-bmi2)
+ popcnt = yes
+ sse = yes
+ sse2 = yes
+ ssse3 = yes
+ sse41 = yes
+ avx2 = yes
+ pext = yes
+endif
+
+ifeq ($(findstring -avx512,$(ARCH)),-avx512)
+ popcnt = yes
+ sse = yes
+ sse2 = yes
+ ssse3 = yes
+ sse41 = yes
+ avx2 = yes
+ pext = yes
+ avx512 = yes
+endif
+
+ifeq ($(findstring -vnni512,$(ARCH)),-vnni512)
+ popcnt = yes
+ sse = yes
+ sse2 = yes
+ ssse3 = yes
+ sse41 = yes
+ avx2 = yes
+ pext = yes
+ avx512 = yes
+ vnni512 = yes
+endif
+
+ifeq ($(findstring -avx512icl,$(ARCH)),-avx512icl)
+ popcnt = yes
+ sse = yes
+ sse2 = yes
+ ssse3 = yes
+ sse41 = yes
+ avx2 = yes
+ pext = yes
+ avx512 = yes
+ vnni512 = yes
+ avx512icl = yes
+endif
+
+ifeq ($(sse),yes)
+ prefetch = yes
+endif
+
+# 64-bit pext is not available on x86-32
+ifeq ($(bits),32)
+ pext = no
+endif
+
+else
+
+# all other architectures
+
+ifeq ($(ARCH),general-32)
+ arch = any
+ bits = 32
+endif
+
+ifeq ($(ARCH),general-64)
+ arch = any
+endif
+
+ifeq ($(ARCH),armv7)
+ arch = armv7
+ prefetch = yes
+ bits = 32
+ arm_version = 7
+endif
+
+ifeq ($(ARCH),armv7-neon)
+ arch = armv7
+ prefetch = yes
+ popcnt = yes
+ neon = yes
+ bits = 32
+ arm_version = 7
+endif
+
+ifeq ($(ARCH),armv8)
+ arch = armv8
+ prefetch = yes
+ popcnt = yes
+ neon = yes
+ arm_version = 8
+endif
+
+ifeq ($(ARCH),armv8-dotprod)
+ arch = armv8
+ prefetch = yes
+ popcnt = yes
+ neon = yes
+ dotprod = yes
+ arm_version = 8
+endif
+
+ifeq ($(ARCH),apple-silicon)
+ arch = arm64
+ prefetch = yes
+ popcnt = yes
+ neon = yes
+ dotprod = yes
+ arm_version = 8
+endif
+
+ifeq ($(ARCH),ppc-32)
+ arch = ppc
+ bits = 32
+endif
+
+ifeq ($(ARCH),ppc-64)
+ arch = ppc64
+ popcnt = yes
+ prefetch = yes
+endif
+
+ifeq ($(ARCH),ppc-64-altivec)
+ arch = ppc64
+ popcnt = yes
+ prefetch = yes
+ altivec = yes
+endif
+
+ifeq ($(ARCH),ppc-64-vsx)
+ arch = ppc64
+ popcnt = yes
+ prefetch = yes
+ vsx = yes
+endif
+
+ifeq ($(findstring e2k,$(ARCH)),e2k)
+ arch = e2k
+ mmx = yes
+ bits = 64
+ sse = yes
+ sse2 = yes
+ ssse3 = yes
+ sse41 = yes
+ popcnt = yes
+endif
+
+ifeq ($(ARCH),riscv64)
+ arch = riscv64
+endif
+
+ifeq ($(findstring loongarch64,$(ARCH)),loongarch64)
+ arch = loongarch64
+ prefetch = yes
+
+ifeq ($(findstring -lasx,$(ARCH)),-lasx)
+ lsx = yes
+ lasx = yes
+endif
+
+ifeq ($(findstring -lsx,$(ARCH)),-lsx)
+ lsx = yes
+endif
+
+endif
+endif
+
+
+### ==========================================================================
+### Section 3. Low-level Configuration
+### ==========================================================================
+
+### 3.1 Selecting compiler (default = gcc)
+ifeq ($(MAKELEVEL),0)
+ export ENV_CXXFLAGS := $(CXXFLAGS)
+ export ENV_DEPENDFLAGS := $(DEPENDFLAGS)
+ export ENV_LDFLAGS := $(LDFLAGS)
+endif
+
+CXXFLAGS = $(ENV_CXXFLAGS) -Wall -Wcast-qual -fno-exceptions -std=c++17 $(EXTRACXXFLAGS)
+DEPENDFLAGS = $(ENV_DEPENDFLAGS) -std=c++17
+LDFLAGS = $(ENV_LDFLAGS) $(EXTRALDFLAGS)
+
+ifeq ($(COMP),)
+ COMP=gcc
+endif
+
+ifeq ($(COMP),gcc)
+ comp=gcc
+ CXX=g++
+ CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-declarations
+
+ ifeq ($(arch),$(filter $(arch),armv7 armv8 riscv64))
+ ifeq ($(OS),Android)
+ CXXFLAGS += -m$(bits)
+ LDFLAGS += -m$(bits)
+ endif
+ ifeq ($(ARCH),riscv64)
+ CXXFLAGS += -latomic
+ endif
+ else ifeq ($(arch),loongarch64)
+ CXXFLAGS += -latomic
+ else
+ CXXFLAGS += -m$(bits)
+ LDFLAGS += -m$(bits)
+ endif
+
+ ifeq ($(arch),$(filter $(arch),armv7))
+ LDFLAGS += -latomic
+ endif
+
+ ifneq ($(KERNEL),Darwin)
+ LDFLAGS += -Wl,--no-as-needed
+ endif
+endif
+
+ifeq ($(target_windows),yes)
+ LDFLAGS += -static
+endif
+
+ifeq ($(COMP),mingw)
+ comp=mingw
+
+ ifeq ($(bits),64)
+ ifeq ($(shell which x86_64-w64-mingw32-c++-posix 2> /dev/null),)
+ CXX=x86_64-w64-mingw32-c++
+ else
+ CXX=x86_64-w64-mingw32-c++-posix
+ endif
+ else
+ ifeq ($(shell which i686-w64-mingw32-c++-posix 2> /dev/null),)
+ CXX=i686-w64-mingw32-c++
+ else
+ CXX=i686-w64-mingw32-c++-posix
+ endif
+ endif
+ CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-declarations
+endif
+
+ifeq ($(COMP),icx)
+ comp=icx
+ CXX=icpx
+ CXXFLAGS += --intel -pedantic -Wextra -Wshadow -Wmissing-prototypes \
+ -Wconditional-uninitialized -Wabi -Wdeprecated
+endif
+
+ifeq ($(COMP),clang)
+ comp=clang
+ CXX=clang++
+ ifeq ($(target_windows),yes)
+ CXX=x86_64-w64-mingw32-clang++
+ endif
+
+ CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-prototypes \
+ -Wconditional-uninitialized -flax-vector-conversions=none
+
+ ifeq ($(filter $(KERNEL),Darwin OpenBSD FreeBSD),)
+ ifeq ($(target_windows),)
+ ifneq ($(RTLIB),compiler-rt)
+ LDFLAGS += -latomic
+ endif
+ endif
+ endif
+
+ ifeq ($(arch),$(filter $(arch),armv7 armv8 riscv64))
+ ifeq ($(OS),Android)
+ CXXFLAGS += -m$(bits)
+ LDFLAGS += -m$(bits)
+ endif
+ ifeq ($(ARCH),riscv64)
+ CXXFLAGS += -latomic
+ endif
+ else ifeq ($(arch),loongarch64)
+ CXXFLAGS += -latomic
+ else
+ CXXFLAGS += -m$(bits)
+ LDFLAGS += -m$(bits)
+ endif
+endif
+
+ifeq ($(KERNEL),Darwin)
+ CXXFLAGS += -mmacosx-version-min=10.15
+ LDFLAGS += -mmacosx-version-min=10.15
+ ifneq ($(arch),any)
+ CXXFLAGS += -arch $(arch)
+ LDFLAGS += -arch $(arch)
+ endif
+ XCRUN = xcrun
+endif
+
+# To cross-compile for Android, use NDK version r27c or later.
+ifeq ($(COMP),ndk)
+ CXXFLAGS += -stdlib=libc++
+ comp=clang
+ ifeq ($(arch),armv7)
+ CXX=armv7a-linux-androideabi29-clang++
+ CXXFLAGS += -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=neon
+ ifneq ($(shell which arm-linux-androideabi-strip 2>/dev/null),)
+ STRIP=arm-linux-androideabi-strip
+ else
+ STRIP=llvm-strip
+ endif
+ endif
+ ifeq ($(arch),armv8)
+ CXX=aarch64-linux-android29-clang++
+ ifneq ($(shell which aarch64-linux-android-strip 2>/dev/null),)
+ STRIP=aarch64-linux-android-strip
+ else
+ STRIP=llvm-strip
+ endif
+ endif
+ ifeq ($(arch),x86_64)
+ CXX=x86_64-linux-android29-clang++
+ ifneq ($(shell which x86_64-linux-android-strip 2>/dev/null),)
+ STRIP=x86_64-linux-android-strip
+ else
+ STRIP=llvm-strip
+ endif
+ endif
+ LDFLAGS += -static-libstdc++
+endif
+
+### Allow overwriting CXX from command line
+ifdef COMPCXX
+ CXX=$(COMPCXX)
+endif
+
+# llvm-profdata must be version compatible with the specified CXX (be it clang, or the gcc alias)
+# make -j profile-build CXX=clang++-20 COMP=clang
+# Locate the version in the same directory as the compiler used,
+# with fallback to a generic one if it can't be located
+ LLVM_PROFDATA := $(dir $(realpath $(shell which $(CXX) 2> /dev/null)))llvm-profdata
+# for icx
+ifeq ($(wildcard $(LLVM_PROFDATA)),)
+ LLVM_PROFDATA := $(dir $(realpath $(shell which $(CXX) 2> /dev/null)))/compiler/llvm-profdata
+endif
+ifeq ($(wildcard $(LLVM_PROFDATA)),)
+ LLVM_PROFDATA := llvm-profdata
+endif
+
+ifeq ($(comp),icx)
+ profile_make = icx-profile-make
+ profile_use = icx-profile-use
+else ifeq ($(comp),clang)
+ profile_make = clang-profile-make
+ profile_use = clang-profile-use
+else
+ profile_make = gcc-profile-make
+ profile_use = gcc-profile-use
+ ifeq ($(KERNEL),Darwin)
+ EXTRAPROFILEFLAGS = -fvisibility=hidden
+ endif
+endif
+
+### Sometimes gcc is really clang
+ifeq ($(COMP),gcc)
+ gccversion := $(shell $(CXX) --version 2>/dev/null)
+ gccisclang := $(findstring clang,$(gccversion))
+ ifneq ($(gccisclang),)
+ profile_make = clang-profile-make
+ profile_use = clang-profile-use
+ else
+ CXXFLAGS += -Wstack-usage=128000
+ endif
+endif
+
+### On mingw use Windows threads, otherwise POSIX
+ifneq ($(comp),mingw)
+ CXXFLAGS += -DUSE_PTHREADS
+ # On Android Bionic's C library comes with its own pthread implementation bundled in
+ ifneq ($(OS),Android)
+ # Haiku has pthreads in its libroot, so only link it in on other platforms
+ ifneq ($(KERNEL),Haiku)
+ ifneq ($(COMP),ndk)
+ LDFLAGS += -lpthread
+
+ add_lrt = yes
+ ifeq ($(target_windows),yes)
+ add_lrt = no
+ endif
+
+ ifeq ($(KERNEL),Darwin)
+ add_lrt = no
+ endif
+
+ ifeq ($(add_lrt),yes)
+ LDFLAGS += -lrt
+ endif
+ endif
+ endif
+ endif
+endif
+
+### 3.2.1 Debugging
+ifeq ($(debug),no)
+ CXXFLAGS += -DNDEBUG
+else
+ CXXFLAGS += -g
+ CXXFLAGS += -D_GLIBCXX_ASSERTIONS -D_GLIBCXX_DEBUG
+endif
+
+### 3.2.2 Debugging with undefined behavior sanitizers
+ifneq ($(sanitize),none)
+ CXXFLAGS += -g3 $(addprefix -fsanitize=,$(sanitize))
+ LDFLAGS += $(addprefix -fsanitize=,$(sanitize))
+endif
+
+### 3.3 Optimization
+ifeq ($(optimize),yes)
+
+ CXXFLAGS += -O3 -funroll-loops
+
+ ifeq ($(comp),gcc)
+ ifeq ($(OS), Android)
+ CXXFLAGS += -fno-gcse -mthumb -march=armv7-a -mfloat-abi=softfp
+ endif
+ endif
+
+ ifeq ($(KERNEL),Darwin)
+ ifeq ($(comp),$(filter $(comp),clang icx))
+ CXXFLAGS += -mdynamic-no-pic
+ endif
+
+ ifeq ($(comp),gcc)
+ ifneq ($(arch),arm64)
+ CXXFLAGS += -mdynamic-no-pic
+ endif
+ endif
+ endif
+
+ ifeq ($(comp),clang)
+ clangmajorversion := $(shell $(CXX) -dumpversion 2>/dev/null | cut -f1 -d.)
+ ifeq ($(shell expr $(clangmajorversion) \< 16),1)
+ CXXFLAGS += -fexperimental-new-pass-manager
+ endif
+ endif
+endif
+
+### 3.4 Bits
+ifeq ($(bits),64)
+ CXXFLAGS += -DIS_64BIT
+endif
+
+### 3.5 prefetch and popcount
+ifeq ($(prefetch),yes)
+ ifeq ($(sse),yes)
+ CXXFLAGS += -msse
+ endif
+else
+ CXXFLAGS += -DNO_PREFETCH
+endif
+
+ifeq ($(popcnt),yes)
+ ifeq ($(arch),$(filter $(arch),ppc64 ppc64-altivec ppc64-vsx armv7 armv8 arm64))
+ CXXFLAGS += -DUSE_POPCNT
+ else
+ CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT
+ endif
+endif
+
+### 3.6 SIMD architectures
+ifeq ($(avx2),yes)
+ CXXFLAGS += -DUSE_AVX2
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
+ CXXFLAGS += -mavx2 -mbmi
+ endif
+endif
+
+ifeq ($(avxvnni),yes)
+ CXXFLAGS += -DUSE_VNNI -DUSE_AVXVNNI
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
+ CXXFLAGS += -mavxvnni
+ endif
+endif
+
+ifeq ($(avx512),yes)
+ CXXFLAGS += -DUSE_AVX512
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
+ CXXFLAGS += -mavx512f -mavx512bw -mavx512dq -mavx512vl
+ endif
+endif
+
+ifeq ($(vnni512),yes)
+ CXXFLAGS += -DUSE_VNNI
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
+ CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl
+ endif
+endif
+
+ifeq ($(avx512icl),yes)
+ CXXFLAGS += -DUSE_AVX512 -DUSE_VNNI -DUSE_AVX512ICL
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
+ CXXFLAGS += -mavx512f -mavx512cd -mavx512vl -mavx512dq -mavx512bw -mavx512ifma -mavx512vbmi -mavx512vbmi2 -mavx512vpopcntdq -mavx512bitalg -mavx512vnni -mvpclmulqdq -mgfni -mvaes
+ endif
+endif
+
+ifeq ($(sse41),yes)
+ CXXFLAGS += -DUSE_SSE41
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
+ CXXFLAGS += -msse4.1
+ endif
+endif
+
+ifeq ($(ssse3),yes)
+ CXXFLAGS += -DUSE_SSSE3
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
+ CXXFLAGS += -mssse3
+ endif
+endif
+
+ifeq ($(sse2),yes)
+ CXXFLAGS += -DUSE_SSE2
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
+ CXXFLAGS += -msse2
+ endif
+endif
+
+ifeq ($(mmx),yes)
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
+ CXXFLAGS += -mmmx
+ endif
+endif
+
+ifeq ($(altivec),yes)
+ CXXFLAGS += -maltivec
+ ifeq ($(COMP),gcc)
+ CXXFLAGS += -mabi=altivec
+ endif
+endif
+
+ifeq ($(vsx),yes)
+ CXXFLAGS += -mvsx
+ ifeq ($(COMP),gcc)
+ CXXFLAGS += -DNO_WARN_X86_INTRINSICS -DUSE_SSE2
+ endif
+endif
+
+ifeq ($(neon),yes)
+ CXXFLAGS += -DUSE_NEON=$(arm_version)
+ ifeq ($(KERNEL),Linux)
+ ifneq ($(COMP),ndk)
+ ifneq ($(arch),armv8)
+ CXXFLAGS += -mfpu=neon
+ endif
+ endif
+ endif
+endif
+
+ifeq ($(dotprod),yes)
+ CXXFLAGS += -march=armv8.2-a+dotprod -DUSE_NEON_DOTPROD
+endif
+
+ifeq ($(lasx),yes)
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
+ CXXFLAGS += -mlasx
+ endif
+endif
+
+ifeq ($(lsx),yes)
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
+ CXXFLAGS += -mlsx
+ endif
+endif
+
+### 3.7 pext
+ifeq ($(pext),yes)
+ CXXFLAGS += -DUSE_PEXT
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
+ CXXFLAGS += -mbmi2
+ endif
+endif
+
+### 3.8.1 Try to include git commit sha for versioning
+GIT_SHA := $(shell git rev-parse HEAD 2>/dev/null | cut -c 1-8)
+ifneq ($(GIT_SHA), )
+ CXXFLAGS += -DGIT_SHA=$(GIT_SHA)
+endif
+
+### 3.8.2 Try to include git commit date for versioning
+GIT_DATE := $(shell git show -s --date=format:'%Y%m%d' --format=%cd HEAD 2>/dev/null)
+ifneq ($(GIT_DATE), )
+ CXXFLAGS += -DGIT_DATE=$(GIT_DATE)
+endif
+
+### 3.8.3 Try to include architecture
+ifneq ($(ARCH), )
+ CXXFLAGS += -DARCH=$(ARCH)
+endif
+
+### 3.9 Link Time Optimization
+### This is a mix of compile and link time options because the lto link phase
+### needs access to the optimization flags.
+ifeq ($(optimize),yes)
+ifeq ($(debug),no)
+ ifneq ($(KERNEL),Darwin)
+ LLD_BIN := $(shell command -v ld.lld 2>/dev/null)
+ ifeq ($(LLD_BIN),)
+ LLD_BIN := $(shell command -v lld 2>/dev/null)
+ endif
+ ifneq ($(LLD_BIN),)
+ ifeq ($(comp),clang)
+ LDFLAGS += -fuse-ld=lld
+ else ifeq ($(comp),gcc)
+ ifneq ($(gccisclang),)
+ LDFLAGS += -fuse-ld=lld
+ endif
+ endif
+ endif
+ endif
+
+ ifeq ($(comp),$(filter $(comp),clang icx))
+ CXXFLAGS += -flto=full
+ ifeq ($(comp),icx)
+ CXXFLAGS += -fwhole-program-vtables
+ endif
+ LDFLAGS += $(CXXFLAGS)
+
+# GCC and CLANG use different methods for parallelizing LTO and CLANG pretends to be
+# GCC on some systems.
+ else ifeq ($(comp),gcc)
+ ifeq ($(gccisclang),)
+ CXXFLAGS += -flto -flto-partition=one
+ LDFLAGS += $(CXXFLAGS) -flto=jobserver
+ else
+ CXXFLAGS += -flto=full
+ LDFLAGS += $(CXXFLAGS)
+ endif
+
+# To use LTO and static linking on Windows,
+# the tool chain requires gcc version 10.1 or later.
+ else ifeq ($(comp),mingw)
+ CXXFLAGS += -flto -flto-partition=one
+ LDFLAGS += $(CXXFLAGS) -save-temps
+ endif
+endif
+endif
+
+### 3.10 Android 5 can only run position independent executables. Note that this
+### breaks Android 4.0 and earlier.
+ifeq ($(OS), Android)
+ CXXFLAGS += -fPIE
+ LDFLAGS += -fPIE -pie
+endif
+
+### 3.11 Inline settings
+ifeq ($(optimize), yes)
+ ifeq ($(comp), clang)
+ CXXFLAGS += -Xclang -mllvm -Xclang -inline-threshold=500
+ endif
+endif
+
+### ==========================================================================
+### Section 4. Public Targets
+### ==========================================================================
+
+help:
+ @echo "" && \
+ echo "To compile stockfish, type: " && \
+ echo "" && \
+ echo "make -j target [ARCH=arch] [COMP=compiler] [COMPCXX=cxx]" && \
+ echo "" && \
+ echo "Supported targets:" && \
+ echo "" && \
+ echo "help > Display architecture details" && \
+ echo "profile-build > standard build with profile-guided optimization" && \
+ echo "build > skip profile-guided optimization" && \
+ echo "net > Download the default nnue nets" && \
+ echo "strip > Strip executable" && \
+ echo "install > Install executable" && \
+ echo "clean > Clean up" && \
+ echo "" && \
+ echo "Supported archs:" && \
+ echo "" && \
+ echo "native > select the best architecture for the host processor (default)" && \
+ echo "x86-64-avx512icl > x86 64-bit with minimum avx512 support of Intel Ice Lake or AMD Zen 4" && \
+ echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support" && \
+ echo "x86-64-avx512 > x86 64-bit with avx512 support" && \
+ echo "x86-64-avxvnni > x86 64-bit with vnni 256bit support" && \
+ echo "x86-64-bmi2 > x86 64-bit with bmi2 support" && \
+ echo "x86-64-avx2 > x86 64-bit with avx2 support" && \
+ echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support" && \
+ echo "x86-64-modern > deprecated, currently x86-64-sse41-popcnt" && \
+ echo "x86-64-ssse3 > x86 64-bit with ssse3 support" && \
+ echo "x86-64-sse3-popcnt > x86 64-bit with sse3 compile and popcnt support" && \
+ echo "x86-64 > x86 64-bit generic (with sse2 support)" && \
+ echo "x86-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support" && \
+ echo "x86-32-sse2 > x86 32-bit with sse2 support" && \
+ echo "x86-32 > x86 32-bit generic (with mmx compile support)" && \
+ echo "ppc-64 > PPC 64-bit" && \
+ echo "ppc-64-altivec > PPC 64-bit with altivec support" && \
+ echo "ppc-64-vsx > PPC 64-bit with vsx support" && \
+ echo "ppc-32 > PPC 32-bit" && \
+ echo "armv7 > ARMv7 32-bit" && \
+ echo "armv7-neon > ARMv7 32-bit with popcnt and neon" && \
+ echo "armv8 > ARMv8 64-bit with popcnt and neon" && \
+ echo "armv8-dotprod > ARMv8 64-bit with popcnt, neon and dot product support" && \
+ echo "e2k > Elbrus 2000" && \
+ echo "apple-silicon > Apple silicon ARM64" && \
+ echo "general-64 > unspecified 64-bit" && \
+ echo "general-32 > unspecified 32-bit" && \
+ echo "riscv64 > RISC-V 64-bit" && \
+ echo "loongarch64 > LoongArch 64-bit" && \
+ echo "loongarch64-lsx > LoongArch 64-bit with SIMD eXtension" && \
+ echo "loongarch64-lasx > LoongArch 64-bit with Advanced SIMD eXtension" && \
+ echo "" && \
+ echo "Supported compilers:" && \
+ echo "" && \
+ echo "gcc > GNU compiler (default)" && \
+ echo "mingw > GNU compiler with MinGW under Windows" && \
+ echo "clang > LLVM Clang compiler" && \
+ echo "icx > Intel oneAPI DPC++/C++ Compiler" && \
+ echo "ndk > Google NDK to cross-compile for Android" && \
+ echo "" && \
+ echo "Simple examples. If you don't know what to do, you likely want to run one of: " && \
+ echo "" && \
+ echo "make -j profile-build ARCH=x86-64-avx2 # typically a fast compile for common systems " && \
+ echo "make -j profile-build ARCH=x86-64-sse41-popcnt # A more portable compile for 64-bit systems " && \
+ echo "make -j profile-build ARCH=x86-64 # A portable compile for 64-bit systems " && \
+ echo "" && \
+ echo "Advanced examples, for experienced users: " && \
+ echo "" && \
+ echo "make -j profile-build ARCH=x86-64-avxvnni" && \
+ echo "make -j profile-build ARCH=x86-64-avxvnni COMP=gcc COMPCXX=g++-12.0" && \
+ echo "make -j build ARCH=x86-64-ssse3 COMP=clang" && \
+ echo ""
+ifneq ($(SUPPORTED_ARCH), true)
+ @echo "Specify a supported architecture with the ARCH option for more details"
+ @echo ""
+endif
+
+
+.PHONY: help analyze build profile-build strip install clean net \
+ objclean profileclean config-sanity \
+ icx-profile-use icx-profile-make \
+ gcc-profile-use gcc-profile-make \
+ clang-profile-use clang-profile-make FORCE \
+ format analyze
+
+analyze: net config-sanity objclean
+ $(MAKE) -k ARCH=$(ARCH) COMP=$(COMP) $(OBJS)
+
+build: net config-sanity
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
+
+profile-build: net config-sanity objclean profileclean
+ @echo ""
+ @echo "Step 1/4. Building instrumented executable ..."
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make)
+ @echo ""
+ @echo "Step 2/4. Running benchmark for pgo-build ..."
+ $(PGOBENCH) > PGOBENCH.out 2>&1
+ tail -n 4 PGOBENCH.out
+ @echo ""
+ @echo "Step 3/4. Building optimized executable ..."
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) objclean
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use)
+ @echo ""
+ @echo "Step 4/4. Deleting profile data ..."
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) profileclean
+
+strip:
+ $(STRIP) $(EXE)
+
+install:
+ -mkdir -p -m 755 $(BINDIR)
+ -cp $(EXE) $(BINDIR)
+ $(STRIP) $(BINDIR)/$(EXE)
+
+# clean all
+clean: objclean profileclean
+ @rm -f .depend *~ core
+
+# clean binaries and objects
+objclean:
+ @rm -f stockfish stockfish.exe *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o
+
+# clean auxiliary profiling files
+profileclean:
+ @rm -rf profdir
+ @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda *.s PGOBENCH.out
+ @rm -f stockfish.profdata *.profraw
+ @rm -f stockfish.*args*
+ @rm -f stockfish.*lt*
+ @rm -f stockfish.res
+ @rm -f ./-lstdc++.res
+
+# evaluation network (nnue)
+net:
+ @$(SHELL) ../scripts/net.sh
+
+format:
+ $(CLANG-FORMAT) -i $(SRCS) $(HEADERS) -style=file
+
+### ==========================================================================
+### Section 5. Private Targets
+### ==========================================================================
+
+all: $(EXE) .depend
+
+config-sanity: net
+ @echo ""
+ @echo "Config:" && \
+ echo "debug: '$(debug)'" && \
+ echo "sanitize: '$(sanitize)'" && \
+ echo "optimize: '$(optimize)'" && \
+ echo "arch: '$(arch)'" && \
+ echo "bits: '$(bits)'" && \
+ echo "kernel: '$(KERNEL)'" && \
+ echo "os: '$(OS)'" && \
+ echo "prefetch: '$(prefetch)'" && \
+ echo "popcnt: '$(popcnt)'" && \
+ echo "pext: '$(pext)'" && \
+ echo "sse: '$(sse)'" && \
+ echo "mmx: '$(mmx)'" && \
+ echo "sse2: '$(sse2)'" && \
+ echo "ssse3: '$(ssse3)'" && \
+ echo "sse41: '$(sse41)'" && \
+ echo "avx2: '$(avx2)'" && \
+ echo "avxvnni: '$(avxvnni)'" && \
+ echo "avx512: '$(avx512)'" && \
+ echo "vnni512: '$(vnni512)'" && \
+ echo "avx512icl: '$(avx512icl)'" && \
+ echo "altivec: '$(altivec)'" && \
+ echo "vsx: '$(vsx)'" && \
+ echo "neon: '$(neon)'" && \
+ echo "dotprod: '$(dotprod)'" && \
+ echo "arm_version: '$(arm_version)'" && \
+ echo "lsx: '$(lsx)'" && \
+ echo "lasx: '$(lasx)'" && \
+ echo "target_windows: '$(target_windows)'" && \
+ echo "" && \
+ echo "Flags:" && \
+ echo "CXX: $(CXX)" && \
+ echo "CXXFLAGS: $(CXXFLAGS)" && \
+ echo "LDFLAGS: $(LDFLAGS)" && \
+ echo "" && \
+ echo "Testing config sanity. If this fails, try 'make help' ..." && \
+ echo "" && \
+ (test "$(debug)" = "yes" || test "$(debug)" = "no") && \
+ (test "$(optimize)" = "yes" || test "$(optimize)" = "no") && \
+ (test "$(SUPPORTED_ARCH)" = "true") && \
+ (test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
+ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "e2k" || \
+ test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || \
+ test "$(arch)" = "riscv64" || test "$(arch)" = "loongarch64") && \
+ (test "$(bits)" = "32" || test "$(bits)" = "64") && \
+ (test "$(prefetch)" = "yes" || test "$(prefetch)" = "no") && \
+ (test "$(popcnt)" = "yes" || test "$(popcnt)" = "no") && \
+ (test "$(pext)" = "yes" || test "$(pext)" = "no") && \
+ (test "$(sse)" = "yes" || test "$(sse)" = "no") && \
+ (test "$(mmx)" = "yes" || test "$(mmx)" = "no") && \
+ (test "$(sse2)" = "yes" || test "$(sse2)" = "no") && \
+ (test "$(ssse3)" = "yes" || test "$(ssse3)" = "no") && \
+ (test "$(sse41)" = "yes" || test "$(sse41)" = "no") && \
+ (test "$(avx2)" = "yes" || test "$(avx2)" = "no") && \
+ (test "$(avx512)" = "yes" || test "$(avx512)" = "no") && \
+ (test "$(vnni512)" = "yes" || test "$(vnni512)" = "no") && \
+ (test "$(avx512icl)" = "yes" || test "$(avx512icl)" = "no") && \
+ (test "$(altivec)" = "yes" || test "$(altivec)" = "no") && \
+ (test "$(vsx)" = "yes" || test "$(vsx)" = "no") && \
+ (test "$(neon)" = "yes" || test "$(neon)" = "no") && \
+ (test "$(lsx)" = "yes" || test "$(lsx)" = "no") && \
+ (test "$(lasx)" = "yes" || test "$(lasx)" = "no") && \
+ (test "$(comp)" = "gcc" || test "$(comp)" = "icx" || test "$(comp)" = "mingw" || \
+ test "$(comp)" = "clang" || test "$(comp)" = "armv7a-linux-androideabi16-clang" || \
+ test "$(comp)" = "aarch64-linux-android21-clang")
+
+$(EXE): $(OBJS)
+ +$(CXX) -o $@ $(OBJS) $(LDFLAGS)
+
+# Force recompilation to ensure version info is up-to-date
+misc.o: FORCE
+FORCE:
+
+clang-profile-make:
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
+ EXTRACXXFLAGS='-fprofile-generate ' \
+ EXTRALDFLAGS=' -fprofile-generate' \
+ all
+
+clang-profile-use:
+ $(XCRUN) $(LLVM_PROFDATA) merge -output=stockfish.profdata *.profraw
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
+ EXTRACXXFLAGS='-fprofile-use=stockfish.profdata' \
+ EXTRALDFLAGS='-fprofile-use ' \
+ all
+
+gcc-profile-make:
+ @mkdir -p profdir
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
+ EXTRACXXFLAGS='-fprofile-generate=profdir' \
+ EXTRACXXFLAGS+=$(EXTRAPROFILEFLAGS) \
+ EXTRALDFLAGS='-lgcov' \
+ all
+
+gcc-profile-use:
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
+ EXTRACXXFLAGS='-fprofile-use=profdir -fno-peel-loops -fno-tracer' \
+ EXTRACXXFLAGS+=$(EXTRAPROFILEFLAGS) \
+ EXTRALDFLAGS='-lgcov' \
+ all
+
+icx-profile-make:
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
+ EXTRACXXFLAGS='-fprofile-instr-generate ' \
+ EXTRALDFLAGS=' -fprofile-instr-generate' \
+ all
+
+icx-profile-use:
+ $(XCRUN) $(LLVM_PROFDATA) merge -output=stockfish.profdata *.profraw
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
+ EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \
+ EXTRALDFLAGS='-fprofile-use ' \
+ all
+
+.depend: $(SRCS)
+ -@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null
+
+ifeq (, $(filter $(MAKECMDGOALS), help strip install clean net objclean profileclean format config-sanity))
+-include .depend
+endif
diff --git a/src/benchmark.cpp b/src/benchmark.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..03bf10ae1cb3ffd95c8e087d22e5deb0f4674fdc
--- /dev/null
+++ b/src/benchmark.cpp
@@ -0,0 +1,516 @@
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
+
+ Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#include "benchmark.h"
+#include "numa.h"
+
+#include
+#include
+#include
+#include
+
+namespace {
+
+// clang-format off
+const std::vector Defaults = {
+ "setoption name UCI_Chess960 value false",
+ "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
+ "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
+ "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11",
+ "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
+ "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14 moves d4e6",
+ "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14 moves g2g4",
+ "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15",
+ "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13",
+ "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16",
+ "4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17",
+ "2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11",
+ "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16",
+ "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
+ "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
+ "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22",
+ "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26",
+ "6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1",
+ "3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1",
+ "2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1 moves g5g6 f3e3 g6g5 e3f3",
+ "8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1",
+ "7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1",
+ "8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1",
+ "8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1",
+ "8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1",
+ "8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1",
+ "5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1",
+ "6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1",
+ "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
+ "6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1",
+ "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1",
+ "5rk1/q6p/2p3bR/1pPp1rP1/1P1Pp3/P3B1Q1/1K3P2/R7 w - - 93 90",
+ "4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21",
+ "r3k2r/3nnpbp/q2pp1p1/p7/Pp1PPPP1/4BNN1/1P5P/R2Q1RK1 w kq - 0 16",
+ "3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40",
+ "4k3/3q1r2/1N2r1b1/3ppN2/2nPP3/1B1R2n1/2R1Q3/3K4 w - - 5 1",
+ "1r6/1P4bk/3qr1p1/N6p/3pp2P/6R1/3Q1PP1/1R4K1 w - - 1 42",
+
+ // Positions with high numbers of changed threats
+ "k7/2n1n3/1nbNbn2/2NbRBn1/1nbRQR2/2NBRBN1/3N1N2/7K w - - 0 1",
+ "K7/8/8/BNQNQNB1/N5N1/R1Q1q2r/n5n1/bnqnqnbk w - - 0 1",
+
+ // 5-man positions
+ "8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
+ "8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate
+ "8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw
+
+ // 6-man positions
+ "8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate
+ "8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate
+ "8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw
+
+ // 7-man positions
+ "8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw
+
+ // Mate and stalemate positions
+ "6k1/3b3r/1p1p4/p1n2p2/1PPNpP1q/P3Q1p1/1R1RB1P1/5K2 b - - 0 1",
+ "r2r1n2/pp2bk2/2p1p2p/3q4/3PN1QP/2P3R1/P4PP1/5RK1 w - - 0 1",
+ "8/8/8/8/8/6k1/6p1/6K1 w - -",
+ "7k/7P/6K1/8/3B4/8/8/8 b - -",
+
+ // Chess 960
+ "setoption name UCI_Chess960 value true",
+ "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w HFhf - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6",
+ "nqbnrkrb/pppppppp/8/8/8/8/PPPPPPPP/NQBNRKRB w KQkq - 0 1",
+ "setoption name UCI_Chess960 value false"
+};
+// clang-format on
+
+// clang-format off
+// human-randomly picked 5 games with <60 moves from
+// https://tests.stockfishchess.org/tests/view/665c71f9fd45fb0f907c21e0
+// only moves for one side
+const std::vector> BenchmarkPositions = {
+ {
+ "rnbq1k1r/ppp1bppp/4pn2/8/2B5/2NP1N2/PPP2PPP/R1BQR1K1 b - - 2 8",
+ "rnbq1k1r/pp2bppp/4pn2/2p5/2B2B2/2NP1N2/PPP2PPP/R2QR1K1 b - - 1 9",
+ "r1bq1k1r/pp2bppp/2n1pn2/2p5/2B1NB2/3P1N2/PPP2PPP/R2QR1K1 b - - 3 10",
+ "r1bq1k1r/pp2bppp/2n1p3/2p5/2B1PB2/5N2/PPP2PPP/R2QR1K1 b - - 0 11",
+ "r1b2k1r/pp2bppp/2n1p3/2p5/2B1PB2/5N2/PPP2PPP/3RR1K1 b - - 0 12",
+ "r1b1k2r/pp2bppp/2n1p3/2p5/2B1PB2/2P2N2/PP3PPP/3RR1K1 b - - 0 13",
+ "r1b1k2r/1p2bppp/p1n1p3/2p5/4PB2/2P2N2/PP2BPPP/3RR1K1 b - - 1 14",
+ "r1b1k2r/4bppp/p1n1p3/1pp5/P3PB2/2P2N2/1P2BPPP/3RR1K1 b - - 0 15",
+ "r1b1k2r/4bppp/p1n1p3/1P6/2p1PB2/2P2N2/1P2BPPP/3RR1K1 b - - 0 16",
+ "r1b1k2r/4bppp/2n1p3/1p6/2p1PB2/1PP2N2/4BPPP/3RR1K1 b - - 0 17",
+ "r3k2r/3bbppp/2n1p3/1p6/2P1PB2/2P2N2/4BPPP/3RR1K1 b - - 0 18",
+ "r3k2r/3bbppp/2n1p3/8/1pP1P3/2P2N2/3BBPPP/3RR1K1 b - - 1 19",
+ "1r2k2r/3bbppp/2n1p3/8/1pPNP3/2P5/3BBPPP/3RR1K1 b - - 3 20",
+ "1r2k2r/3bbppp/2n1p3/8/2PNP3/2B5/4BPPP/3RR1K1 b - - 0 21",
+ "1r2k2r/3bb1pp/2n1pp2/1N6/2P1P3/2B5/4BPPP/3RR1K1 b - - 1 22",
+ "1r2k2r/3b2pp/2n1pp2/1N6/1BP1P3/8/4BPPP/3RR1K1 b - - 0 23",
+ "1r2k2r/3b2pp/4pp2/1N6/1nP1P3/8/3RBPPP/4R1K1 b - - 1 24",
+ "1r5r/3bk1pp/4pp2/1N6/1nP1PP2/8/3RB1PP/4R1K1 b - - 0 25",
+ "1r5r/3bk1pp/2n1pp2/1N6/2P1PP2/8/3RBKPP/4R3 b - - 2 26",
+ "1r5r/3bk1pp/2n2p2/1N2p3/2P1PP2/6P1/3RBK1P/4R3 b - - 0 27",
+ "1r1r4/3bk1pp/2n2p2/1N2p3/2P1PP2/6P1/3RBK1P/R7 b - - 2 28",
+ "1r1r4/N3k1pp/2n1bp2/4p3/2P1PP2/6P1/3RBK1P/R7 b - - 4 29",
+ "1r1r4/3bk1pp/2N2p2/4p3/2P1PP2/6P1/3RBK1P/R7 b - - 0 30",
+ "1r1R4/4k1pp/2b2p2/4p3/2P1PP2/6P1/4BK1P/R7 b - - 0 31",
+ "3r4/4k1pp/2b2p2/4P3/2P1P3/6P1/4BK1P/R7 b - - 0 32",
+ "3r4/R3k1pp/2b5/4p3/2P1P3/6P1/4BK1P/8 b - - 1 33",
+ "8/3rk1pp/2b5/R3p3/2P1P3/6P1/4BK1P/8 b - - 3 34",
+ "8/3r2pp/2bk4/R1P1p3/4P3/6P1/4BK1P/8 b - - 0 35",
+ "8/2kr2pp/2b5/R1P1p3/4P3/4K1P1/4B2P/8 b - - 2 36",
+ "1k6/3r2pp/2b5/RBP1p3/4P3/4K1P1/7P/8 b - - 4 37",
+ "8/1k1r2pp/2b5/R1P1p3/4P3/3BK1P1/7P/8 b - - 6 38",
+ "1k6/3r2pp/2b5/2P1p3/4P3/3BK1P1/7P/R7 b - - 8 39",
+ "1k6/r5pp/2b5/2P1p3/4P3/3BK1P1/7P/5R2 b - - 10 40",
+ "1k3R2/6pp/2b5/2P1p3/4P3/r2BK1P1/7P/8 b - - 12 41",
+ "5R2/2k3pp/2b5/2P1p3/4P3/r2B2P1/3K3P/8 b - - 14 42",
+ "5R2/2k3pp/2b5/2P1p3/4P3/3BK1P1/r6P/8 b - - 16 43",
+ "5R2/2k3pp/2b5/2P1p3/4P3/r2B2P1/4K2P/8 b - - 18 44",
+ "5R2/2k3pp/2b5/2P1p3/4P3/3B1KP1/r6P/8 b - - 20 45",
+ "8/2k2Rpp/2b5/2P1p3/4P3/r2B1KP1/7P/8 b - - 22 46",
+ "3k4/5Rpp/2b5/2P1p3/4P3/r2B2P1/4K2P/8 b - - 24 47",
+ "3k4/5Rpp/2b5/2P1p3/4P3/3B1KP1/r6P/8 b - - 26 48",
+ "3k4/5Rpp/2b5/2P1p3/4P3/r2B2P1/4K2P/8 b - - 28 49",
+ "3k4/5Rpp/2b5/2P1p3/4P3/3BK1P1/r6P/8 b - - 30 50",
+ "3k4/5Rpp/2b5/2P1p3/4P3/r2B2P1/3K3P/8 b - - 32 51",
+ "3k4/5Rpp/2b5/2P1p3/4P3/2KB2P1/r6P/8 b - - 34 52",
+ "3k4/5Rpp/2b5/2P1p3/4P3/r2B2P1/2K4P/8 b - - 36 53",
+ "3k4/5Rpp/2b5/2P1p3/4P3/1K1B2P1/r6P/8 b - - 38 54",
+ "3k4/6Rp/2b5/2P1p3/4P3/1K1B2P1/7r/8 b - - 0 55",
+ "3k4/8/2b3Rp/2P1p3/4P3/1K1B2P1/7r/8 b - - 1 56",
+ "8/2k3R1/2b4p/2P1p3/4P3/1K1B2P1/7r/8 b - - 3 57",
+ "3k4/8/2b3Rp/2P1p3/4P3/1K1B2P1/7r/8 b - - 5 58",
+ "8/2k5/2b3Rp/2P1p3/1K2P3/3B2P1/7r/8 b - - 7 59",
+ "8/2k5/2b3Rp/2P1p3/4P3/2KB2P1/3r4/8 b - - 9 60",
+ "8/2k5/2b3Rp/2P1p3/1K2P3/3B2P1/6r1/8 b - - 11 61",
+ "8/2k5/2b3Rp/2P1p3/4P3/2KB2P1/3r4/8 b - - 13 62",
+ "8/2k5/2b3Rp/2P1p3/2K1P3/3B2P1/6r1/8 b - - 15 63",
+ "4b3/2k3R1/7p/2P1p3/2K1P3/3B2P1/6r1/8 b - - 17 64",
+ },
+ {
+ "r1bqkbnr/npp1pppp/p7/3P4/4pB2/2N5/PPP2PPP/R2QKBNR w KQkq - 1 6",
+ "r1bqkb1r/npp1pppp/p4n2/3P4/4pB2/2N5/PPP1QPPP/R3KBNR w KQkq - 3 7",
+ "r2qkb1r/npp1pppp/p4n2/3P1b2/4pB2/2N5/PPP1QPPP/2KR1BNR w kq - 5 8",
+ "r2qkb1r/1pp1pppp/p4n2/1n1P1b2/4pB2/2N4P/PPP1QPP1/2KR1BNR w kq - 1 9",
+ "r2qkb1r/1pp1pppp/5n2/1p1P1b2/4pB2/7P/PPP1QPP1/2KR1BNR w kq - 0 10",
+ "r2qkb1r/1ppbpppp/5n2/1Q1P4/4pB2/7P/PPP2PP1/2KR1BNR w kq - 1 11",
+ "3qkb1r/1Qpbpppp/5n2/3P4/4pB2/7P/rPP2PP1/2KR1BNR w k - 0 12",
+ "q3kb1r/1Qpbpppp/5n2/3P4/4pB2/7P/rPP2PP1/1K1R1BNR w k - 2 13",
+ "r3kb1r/2pbpppp/5n2/3P4/4pB2/7P/1PP2PP1/1K1R1BNR w k - 0 14",
+ "r3kb1r/2Bb1ppp/4pn2/3P4/4p3/7P/1PP2PP1/1K1R1BNR w k - 0 15",
+ "r3kb1r/2Bb2pp/4pn2/8/4p3/7P/1PP2PP1/1K1R1BNR w k - 0 16",
+ "r3k2r/2Bb2pp/4pn2/2b5/4p3/7P/1PP1NPP1/1K1R1B1R w k - 2 17",
+ "r6r/2Bbk1pp/4pn2/2b5/3Np3/7P/1PP2PP1/1K1R1B1R w - - 4 18",
+ "r6r/b2bk1pp/4pn2/4B3/3Np3/7P/1PP2PP1/1K1R1B1R w - - 6 19",
+ "r1r5/b2bk1pp/4pn2/4B3/2BNp3/7P/1PP2PP1/1K1R3R w - - 8 20",
+ "r7/b2bk1pp/4pn2/2r1B3/2BNp3/1P5P/2P2PP1/1K1R3R w - - 1 21",
+ "rb6/3bk1pp/4pn2/2r1B3/2BNpP2/1P5P/2P3P1/1K1R3R w - - 1 22",
+ "1r6/3bk1pp/4pn2/2r5/2BNpP2/1P5P/2P3P1/1K1R3R w - - 0 23",
+ "1r6/3bk1p1/4pn1p/2r5/2BNpP2/1P5P/2P3P1/2KR3R w - - 0 24",
+ "8/3bk1p1/1r2pn1p/2r5/2BNpP1P/1P6/2P3P1/2KR3R w - - 1 25",
+ "8/3bk3/1r2pnpp/2r5/2BNpP1P/1P6/2P3P1/2K1R2R w - - 0 26",
+ "2b5/4k3/1r2pnpp/2r5/2BNpP1P/1P4P1/2P5/2K1R2R w - - 1 27",
+ "8/1b2k3/1r2pnpp/2r5/2BNpP1P/1P4P1/2P5/2K1R1R1 w - - 3 28",
+ "8/1b1nk3/1r2p1pp/2r5/2BNpPPP/1P6/2P5/2K1R1R1 w - - 1 29",
+ "8/1b2k3/1r2p1pp/2r1nP2/2BNp1PP/1P6/2P5/2K1R1R1 w - - 1 30",
+ "8/1b2k3/1r2p1p1/2r1nPp1/2BNp2P/1P6/2P5/2K1R1R1 w - - 0 31",
+ "8/1b2k3/1r2p1n1/2r3p1/2BNp2P/1P6/2P5/2K1R1R1 w - - 0 32",
+ "8/1b2k3/1r2p1n1/6r1/2BNp2P/1P6/2P5/2K1R3 w - - 0 33",
+ "8/1b2k3/1r2p3/4n1P1/2BNp3/1P6/2P5/2K1R3 w - - 1 34",
+ "8/1b2k3/1r2p3/4n1P1/2BN4/1P2p3/2P5/2K4R w - - 0 35",
+ "8/1b2k3/1r2p2R/6P1/2nN4/1P2p3/2P5/2K5 w - - 0 36",
+ "8/1b2k3/3rp2R/6P1/2PN4/4p3/2P5/2K5 w - - 1 37",
+ "8/4k3/3rp2R/6P1/2PN4/2P1p3/6b1/2K5 w - - 1 38",
+ "8/4k3/r3p2R/2P3P1/3N4/2P1p3/6b1/2K5 w - - 1 39",
+ "8/3k4/r3p2R/2P2NP1/8/2P1p3/6b1/2K5 w - - 3 40",
+ "8/3k4/4p2R/2P3P1/8/2P1N3/6b1/r1K5 w - - 1 41",
+ "8/3k4/4p2R/2P3P1/8/2P1N3/3K2b1/6r1 w - - 3 42",
+ "8/3k4/4p2R/2P3P1/8/2PKNb2/8/6r1 w - - 5 43",
+ "8/4k3/4p1R1/2P3P1/8/2PKNb2/8/6r1 w - - 7 44",
+ "8/4k3/4p1R1/2P3P1/3K4/2P1N3/8/6rb w - - 9 45",
+ "8/3k4/4p1R1/2P1K1P1/8/2P1N3/8/6rb w - - 11 46",
+ "8/3k4/4p1R1/2P3P1/5K2/2P1N3/8/4r2b w - - 13 47",
+ "8/3k4/2b1p2R/2P3P1/5K2/2P1N3/8/4r3 w - - 15 48",
+ "8/3k4/2b1p3/2P3P1/5K2/2P1N2R/8/6r1 w - - 17 49",
+ "2k5/7R/2b1p3/2P3P1/5K2/2P1N3/8/6r1 w - - 19 50",
+ "2k5/7R/4p3/2P3P1/b1P2K2/4N3/8/6r1 w - - 1 51",
+ "2k5/3bR3/4p3/2P3P1/2P2K2/4N3/8/6r1 w - - 3 52",
+ "3k4/3b2R1/4p3/2P3P1/2P2K2/4N3/8/6r1 w - - 5 53",
+ "3kb3/6R1/4p1P1/2P5/2P2K2/4N3/8/6r1 w - - 1 54",
+ "3kb3/6R1/4p1P1/2P5/2P2KN1/8/8/2r5 w - - 3 55",
+ "3kb3/6R1/4p1P1/2P1N3/2P2K2/8/8/5r2 w - - 5 56",
+ "3kb3/6R1/4p1P1/2P1N3/2P5/4K3/8/4r3 w - - 7 57",
+ },
+ {
+ "rnbq1rk1/ppp1npb1/4p1p1/3P3p/3PP3/2N2N2/PP2BPPP/R1BQ1RK1 b - - 0 8",
+ "rnbq1rk1/ppp1npb1/6p1/3pP2p/3P4/2N2N2/PP2BPPP/R1BQ1RK1 b - - 0 9",
+ "rn1q1rk1/ppp1npb1/6p1/3pP2p/3P2b1/2N2N2/PP2BPPP/R1BQR1K1 b - - 2 10",
+ "r2q1rk1/ppp1npb1/2n3p1/3pP2p/3P2bN/2N5/PP2BPPP/R1BQR1K1 b - - 4 11",
+ "r4rk1/pppqnpb1/2n3p1/3pP2p/3P2bN/2N4P/PP2BPP1/R1BQR1K1 b - - 0 12",
+ "r4rk1/pppqnpb1/2n3p1/3pP2p/3P3N/7P/PP2NPP1/R1BQR1K1 b - - 0 13",
+ "r4rk1/pppq1pb1/2n3p1/3pPN1p/3P4/7P/PP2NPP1/R1BQR1K1 b - - 0 14",
+ "r4rk1/ppp2pb1/2n3p1/3pPq1p/3P1N2/7P/PP3PP1/R1BQR1K1 b - - 1 15",
+ "r4rk1/pppq1pb1/2n3p1/3pP2p/P2P1N2/7P/1P3PP1/R1BQR1K1 b - - 0 16",
+ "r2n1rk1/pppq1pb1/6p1/3pP2p/P2P1N2/R6P/1P3PP1/2BQR1K1 b - - 2 17",
+ "r4rk1/pppq1pb1/4N1p1/3pP2p/P2P4/R6P/1P3PP1/2BQR1K1 b - - 0 18",
+ "r4rk1/ppp2pb1/4q1p1/3pP1Bp/P2P4/R6P/1P3PP1/3QR1K1 b - - 1 19",
+ "r3r1k1/ppp2pb1/4q1p1/3pP1Bp/P2P1P2/R6P/1P4P1/3QR1K1 b - - 0 20",
+ "r3r1k1/ppp3b1/4qpp1/3pP2p/P2P1P1B/R6P/1P4P1/3QR1K1 b - - 1 21",
+ "r3r1k1/ppp3b1/4q1p1/3pP2p/P4P1B/R6P/1P4P1/3QR1K1 b - - 0 22",
+ "r4rk1/ppp3b1/4q1p1/3pP1Bp/P4P2/R6P/1P4P1/3QR1K1 b - - 2 23",
+ "r4rk1/pp4b1/4q1p1/2ppP1Bp/P4P2/3R3P/1P4P1/3QR1K1 b - - 1 24",
+ "r4rk1/pp4b1/4q1p1/2p1P1Bp/P2p1PP1/3R3P/1P6/3QR1K1 b - - 0 25",
+ "r4rk1/pp4b1/4q1p1/2p1P1B1/P2p1PP1/3R4/1P6/3QR1K1 b - - 0 26",
+ "r5k1/pp3rb1/4q1p1/2p1P1B1/P2p1PP1/6R1/1P6/3QR1K1 b - - 2 27",
+ "5rk1/pp3rb1/4q1p1/2p1P1B1/P2pRPP1/6R1/1P6/3Q2K1 b - - 4 28",
+ "5rk1/1p3rb1/p3q1p1/P1p1P1B1/3pRPP1/6R1/1P6/3Q2K1 b - - 0 29",
+ "4r1k1/1p3rb1/p3q1p1/P1p1P1B1/3pRPP1/1P4R1/8/3Q2K1 b - - 0 30",
+ "4r1k1/5rb1/pP2q1p1/2p1P1B1/3pRPP1/1P4R1/8/3Q2K1 b - - 0 31",
+ "4r1k1/5rb1/pq4p1/2p1P1B1/3pRPP1/1P4R1/4Q3/6K1 b - - 1 32",
+ "4r1k1/1r4b1/pq4p1/2p1P1B1/3pRPP1/1P4R1/2Q5/6K1 b - - 3 33",
+ "4r1k1/1r4b1/1q4p1/p1p1P1B1/3p1PP1/1P4R1/2Q5/4R1K1 b - - 1 34",
+ "4r1k1/3r2b1/1q4p1/p1p1P1B1/2Qp1PP1/1P4R1/8/4R1K1 b - - 3 35",
+ "4r1k1/3r2b1/4q1p1/p1p1P1B1/2Qp1PP1/1P4R1/5K2/4R3 b - - 5 36",
+ "4r1k1/3r2b1/6p1/p1p1P1B1/2Pp1PP1/6R1/5K2/4R3 b - - 0 37",
+ "4r1k1/3r2b1/6p1/p1p1P1B1/2P2PP1/3p2R1/5K2/3R4 b - - 1 38",
+ "5rk1/3r2b1/6p1/p1p1P1B1/2P2PP1/3p2R1/8/3RK3 b - - 3 39",
+ "5rk1/6b1/6p1/p1p1P1B1/2Pr1PP1/3R4/8/3RK3 b - - 0 40",
+ "5rk1/3R2b1/6p1/p1p1P1B1/2r2PP1/8/8/3RK3 b - - 1 41",
+ "5rk1/3R2b1/6p1/p1p1P1B1/4rPP1/8/3K4/3R4 b - - 3 42",
+ "1r4k1/3R2b1/6p1/p1p1P1B1/4rPP1/2K5/8/3R4 b - - 5 43",
+ "1r4k1/3R2b1/6p1/p1p1P1B1/2K2PP1/4r3/8/3R4 b - - 7 44",
+ "1r3bk1/8/3R2p1/p1p1P1B1/2K2PP1/4r3/8/3R4 b - - 9 45",
+ "1r3bk1/8/6R1/2p1P1B1/p1K2PP1/4r3/8/3R4 b - - 0 46",
+ "1r3b2/5k2/R7/2p1P1B1/p1K2PP1/4r3/8/3R4 b - - 2 47",
+ "5b2/1r3k2/R7/2p1P1B1/p1K2PP1/4r3/8/7R b - - 4 48",
+ "5b2/5k2/R7/2pKP1B1/pr3PP1/4r3/8/7R b - - 6 49",
+ "5b2/5k2/R1K5/2p1P1B1/p2r1PP1/4r3/8/7R b - - 8 50",
+ "8/R4kb1/2K5/2p1P1B1/p2r1PP1/4r3/8/7R b - - 10 51",
+ "8/R5b1/2K3k1/2p1PPB1/p2r2P1/4r3/8/7R b - - 0 52",
+ "8/6R1/2K5/2p1PPk1/p2r2P1/4r3/8/7R b - - 0 53",
+ "8/6R1/2K5/2p1PP2/p2r1kP1/4r3/8/5R2 b - - 2 54",
+ "8/6R1/2K2P2/2p1P3/p2r2P1/4r1k1/8/5R2 b - - 0 55",
+ "8/5PR1/2K5/2p1P3/p2r2P1/4r3/6k1/5R2 b - - 0 56",
+ },
+ {
+ "rn1qkb1r/p1pbpppp/5n2/8/2pP4/2N5/1PQ1PPPP/R1B1KBNR w KQkq - 0 7",
+ "r2qkb1r/p1pbpppp/2n2n2/8/2pP4/2N2N2/1PQ1PPPP/R1B1KB1R w KQkq - 2 8",
+ "r2qkb1r/p1pbpppp/5n2/8/1npPP3/2N2N2/1PQ2PPP/R1B1KB1R w KQkq - 1 9",
+ "r2qkb1r/p1pb1ppp/4pn2/8/1npPP3/2N2N2/1P3PPP/R1BQKB1R w KQkq - 0 10",
+ "r2qk2r/p1pbbppp/4pn2/8/1nBPP3/2N2N2/1P3PPP/R1BQK2R w KQkq - 1 11",
+ "r2q1rk1/p1pbbppp/4pn2/8/1nBPP3/2N2N2/1P3PPP/R1BQ1RK1 w - - 3 12",
+ "r2q1rk1/2pbbppp/p3pn2/8/1nBPPB2/2N2N2/1P3PPP/R2Q1RK1 w - - 0 13",
+ "r2q1rk1/2p1bppp/p3pn2/1b6/1nBPPB2/2N2N2/1P3PPP/R2QR1K1 w - - 2 14",
+ "r2q1rk1/4bppp/p1p1pn2/1b6/1nBPPB2/1PN2N2/5PPP/R2QR1K1 w - - 0 15",
+ "r4rk1/3qbppp/p1p1pn2/1b6/1nBPPB2/1PN2N2/3Q1PPP/R3R1K1 w - - 2 16",
+ "r4rk1/1q2bppp/p1p1pn2/1b6/1nBPPB2/1PN2N1P/3Q1PP1/R3R1K1 w - - 1 17",
+ "r3r1k1/1q2bppp/p1p1pn2/1b6/1nBPPB2/1PN2N1P/4QPP1/R3R1K1 w - - 3 18",
+ "r3r1k1/1q1nbppp/p1p1p3/1b6/1nBPPB2/1PN2N1P/4QPP1/3RR1K1 w - - 5 19",
+ "r3rbk1/1q1n1ppp/p1p1p3/1b6/1nBPPB2/1PN2N1P/3RQPP1/4R1K1 w - - 7 20",
+ "r3rbk1/1q3ppp/pnp1p3/1b6/1nBPPB2/1PN2N1P/3RQPP1/4R2K w - - 9 21",
+ "2r1rbk1/1q3ppp/pnp1p3/1b6/1nBPPB2/1PN2N1P/3RQPP1/1R5K w - - 11 22",
+ "2r1rbk1/1q4pp/pnp1pp2/1b6/1nBPPB2/1PN2N1P/4QPP1/1R1R3K w - - 0 23",
+ "2r1rbk1/5qpp/pnp1pp2/1b6/1nBPP3/1PN1BN1P/4QPP1/1R1R3K w - - 2 24",
+ "2r1rbk1/5qp1/pnp1pp1p/1b6/1nBPP3/1PN1BN1P/4QPP1/1R1R2K1 w - - 0 25",
+ "2r1rbk1/5qp1/pnp1pp1p/1b6/2BPP3/1P2BN1P/n3QPP1/1R1R2K1 w - - 0 26",
+ "r3rbk1/5qp1/pnp1pp1p/1b6/2BPP3/1P2BN1P/Q4PP1/1R1R2K1 w - - 1 27",
+ "rr3bk1/5qp1/pnp1pp1p/1b6/2BPP3/1P2BN1P/Q4PP1/R2R2K1 w - - 3 28",
+ "rr2qbk1/6p1/pnp1pp1p/1b6/2BPP3/1P2BN1P/4QPP1/R2R2K1 w - - 5 29",
+ "rr2qbk1/6p1/1np1pp1p/pb6/2BPP3/1P1QBN1P/5PP1/R2R2K1 w - - 0 30",
+ "rr2qbk1/6p1/1n2pp1p/pp6/3PP3/1P1QBN1P/5PP1/R2R2K1 w - - 0 31",
+ "rr2qbk1/6p1/1n2pp1p/1p1P4/p3P3/1P1QBN1P/5PP1/R2R2K1 w - - 0 32",
+ "rr2qbk1/3n2p1/3Ppp1p/1p6/p3P3/1P1QBN1P/5PP1/R2R2K1 w - - 1 33",
+ "rr3bk1/3n2p1/3Ppp1p/1p5q/pP2P3/3QBN1P/5PP1/R2R2K1 w - - 1 34",
+ "rr3bk1/3n2p1/3Ppp1p/1p5q/1P2P3/p2QBN1P/5PP1/2RR2K1 w - - 0 35",
+ "1r3bk1/3n2p1/r2Ppp1p/1p5q/1P2P3/pQ2BN1P/5PP1/2RR2K1 w - - 2 36",
+ "1r2qbk1/2Rn2p1/r2Ppp1p/1p6/1P2P3/pQ2BN1P/5PP1/3R2K1 w - - 4 37",
+ "1r2qbk1/2Rn2p1/r2Ppp1p/1pB5/1P2P3/1Q3N1P/p4PP1/3R2K1 w - - 0 38",
+ "1r2q1k1/2Rn2p1/r2bpp1p/1pB5/1P2P3/1Q3N1P/p4PP1/R5K1 w - - 0 39",
+ "1r2q1k1/2Rn2p1/3rpp1p/1p6/1P2P3/1Q3N1P/p4PP1/R5K1 w - - 0 40",
+ "2r1q1k1/2Rn2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 1 41",
+ "1r2q1k1/1R1n2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 3 42",
+ "2r1q1k1/2Rn2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 5 43",
+ "1r2q1k1/1R1n2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 7 44",
+ "1rq3k1/R2n2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 9 45",
+ "2q3k1/Rr1n2p1/3rpp1p/1p6/1P2P3/5N1P/4QPP1/R5K1 w - - 11 46",
+ "Rrq3k1/3n2p1/3rpp1p/1p6/1P2P3/5N1P/4QPP1/R5K1 w - - 13 47",
+ },
+ {
+ "rn1qkb1r/1pp2ppp/p4p2/3p1b2/5P2/1P2PN2/P1PP2PP/RN1QKB1R b KQkq - 1 6",
+ "r2qkb1r/1pp2ppp/p1n2p2/3p1b2/3P1P2/1P2PN2/P1P3PP/RN1QKB1R b KQkq - 0 7",
+ "r2qkb1r/1pp2ppp/p4p2/3p1b2/1n1P1P2/1P1BPN2/P1P3PP/RN1QK2R b KQkq - 2 8",
+ "r2qkb1r/1pp2ppp/p4p2/3p1b2/3P1P2/1P1PPN2/P5PP/RN1QK2R b KQkq - 0 9",
+ "r2qk2r/1pp2ppp/p2b1p2/3p1b2/3P1P2/1PNPPN2/P5PP/R2QK2R b KQkq - 2 10",
+ "r2qk2r/1p3ppp/p1pb1p2/3p1b2/3P1P2/1PNPPN2/P5PP/R2Q1RK1 b kq - 1 11",
+ "r2q1rk1/1p3ppp/p1pb1p2/3p1b2/3P1P2/1PNPPN2/P2Q2PP/R4RK1 b - - 3 12",
+ "r2qr1k1/1p3ppp/p1pb1p2/3p1b2/3P1P2/1P1PPN2/P2QN1PP/R4RK1 b - - 5 13",
+ "r3r1k1/1p3ppp/pqpb1p2/3p1b2/3P1P2/1P1PPNN1/P2Q2PP/R4RK1 b - - 7 14",
+ "r3r1k1/1p3ppp/pqp2p2/3p1b2/1b1P1P2/1P1PPNN1/P1Q3PP/R4RK1 b - - 9 15",
+ "r3r1k1/1p1b1ppp/pqp2p2/3p4/1b1P1P2/1P1PPNN1/P4QPP/R4RK1 b - - 11 16",
+ "2r1r1k1/1p1b1ppp/pqp2p2/3p4/1b1PPP2/1P1P1NN1/P4QPP/R4RK1 b - - 0 17",
+ "2r1r1k1/1p1b1ppp/pq3p2/2pp4/1b1PPP2/PP1P1NN1/5QPP/R4RK1 b - - 0 18",
+ "2r1r1k1/1p1b1ppp/pq3p2/2Pp4/4PP2/PPbP1NN1/5QPP/R4RK1 b - - 0 19",
+ "2r1r1k1/1p1b1ppp/p4p2/2Pp4/4PP2/PqbP1NN1/5QPP/RR4K1 b - - 1 20",
+ "2r1r1k1/1p1b1ppp/p4p2/2Pp4/q3PP2/P1bP1NN1/R4QPP/1R4K1 b - - 3 21",
+ "2r1r1k1/1p3ppp/p4p2/1bPP4/q4P2/P1bP1NN1/R4QPP/1R4K1 b - - 0 22",
+ "2r1r1k1/1p3ppp/p4p2/2PP4/q4P2/P1bb1NN1/R4QPP/2R3K1 b - - 1 23",
+ "2r1r1k1/1p3ppp/p2P1p2/2P5/2q2P2/P1bb1NN1/R4QPP/2R3K1 b - - 0 24",
+ "2rr2k1/1p3ppp/p2P1p2/2P5/2q2P2/P1bb1NN1/R4QPP/2R4K b - - 2 25",
+ "2rr2k1/1p3ppp/p2P1p2/2Q5/5P2/P1bb1NN1/R5PP/2R4K b - - 0 26",
+ "3r2k1/1p3ppp/p2P1p2/2r5/5P2/P1bb1N2/R3N1PP/2R4K b - - 1 27",
+ "3r2k1/1p3ppp/p2P1p2/2r5/5P2/P1b2N2/4R1PP/2R4K b - - 0 28",
+ "3r2k1/1p3ppp/p2P1p2/2r5/1b3P2/P4N2/4R1PP/3R3K b - - 2 29",
+ "3r2k1/1p2Rppp/p2P1p2/b1r5/5P2/P4N2/6PP/3R3K b - - 4 30",
+ "3r2k1/1R3ppp/p1rP1p2/b7/5P2/P4N2/6PP/3R3K b - - 0 31",
+ "3r2k1/1R3ppp/p2R1p2/b7/5P2/P4N2/6PP/7K b - - 0 32",
+ "6k1/1R3ppp/p2r1p2/b7/5P2/P4NP1/7P/7K b - - 0 33",
+ "6k1/1R3p1p/p2r1pp1/b7/5P1P/P4NP1/8/7K b - - 0 34",
+ "6k1/3R1p1p/pr3pp1/b7/5P1P/P4NP1/8/7K b - - 2 35",
+ "6k1/5p2/pr3pp1/b2R3p/5P1P/P4NP1/8/7K b - - 1 36",
+ "6k1/5p2/pr3pp1/7p/5P1P/P1bR1NP1/8/7K b - - 3 37",
+ "6k1/5p2/p1r2pp1/7p/5P1P/P1bR1NP1/6K1/8 b - - 5 38",
+ "6k1/5p2/p1r2pp1/b2R3p/5P1P/P4NP1/6K1/8 b - - 7 39",
+ "6k1/5p2/p4pp1/b2R3p/5P1P/P4NPK/2r5/8 b - - 9 40",
+ "6k1/2b2p2/p4pp1/7p/5P1P/P2R1NPK/2r5/8 b - - 11 41",
+ "6k1/2b2p2/5pp1/p6p/3N1P1P/P2R2PK/2r5/8 b - - 1 42",
+ "6k1/2b2p2/5pp1/p6p/3N1P1P/P1R3PK/r7/8 b - - 3 43",
+ "6k1/5p2/1b3pp1/p6p/5P1P/P1R3PK/r1N5/8 b - - 5 44",
+ "8/5pk1/1bR2pp1/p6p/5P1P/P5PK/r1N5/8 b - - 7 45",
+ "3b4/5pk1/2R2pp1/p4P1p/7P/P5PK/r1N5/8 b - - 0 46",
+ "8/4bpk1/2R2pp1/p4P1p/6PP/P6K/r1N5/8 b - - 0 47",
+ "8/5pk1/2R2pP1/p6p/6PP/b6K/r1N5/8 b - - 0 48",
+ "8/6k1/2R2pp1/p6P/7P/b6K/r1N5/8 b - - 0 49",
+ "8/6k1/2R2p2/p6p/7P/b5K1/r1N5/8 b - - 1 50",
+ "8/8/2R2pk1/p6p/7P/b4K2/r1N5/8 b - - 3 51",
+ "8/8/2R2pk1/p6p/7P/4NK2/rb6/8 b - - 5 52",
+ "2R5/8/5pk1/7p/p6P/4NK2/rb6/8 b - - 1 53",
+ "6R1/8/5pk1/7p/p6P/4NK2/1b6/r7 b - - 3 54",
+ "R7/5k2/5p2/7p/p6P/4NK2/1b6/r7 b - - 5 55",
+ "R7/5k2/5p2/7p/7P/p3N3/1b2K3/r7 b - - 1 56",
+ "8/R4k2/5p2/7p/7P/p3N3/1b2K3/7r b - - 3 57",
+ "8/8/5pk1/7p/R6P/p3N3/1b2K3/7r b - - 5 58",
+ "8/8/5pk1/7p/R6P/p7/4K3/2bN3r b - - 7 59",
+ "8/8/5pk1/7p/R6P/p7/4KN1r/2b5 b - - 9 60",
+ "8/8/5pk1/7p/R6P/p3K3/1b3N1r/8 b - - 11 61",
+ "8/8/R4pk1/7p/7P/p1b1K3/5N1r/8 b - - 13 62",
+ "8/8/5pk1/7p/7P/2b1K3/R4N1r/8 b - - 0 63",
+ "8/8/5pk1/7p/3K3P/8/R4N1r/4b3 b - - 2 64",
+ }
+};
+// clang-format on
+
+} // namespace
+
+namespace Stockfish::Benchmark {
+
+// Builds a list of UCI commands to be run by bench. There
+// are five parameters: TT size in MB, number of search threads that
+// should be used, the limit value spent for each position, a file name
+// where to look for positions in FEN format, and the type of the limit:
+// depth, perft, nodes and movetime (in milliseconds). Examples:
+//
+// bench : search default positions up to depth 13
+// bench 64 1 15 : search default positions up to depth 15 (TT = 64MB)
+// bench 64 1 100000 default nodes : search default positions for 100K nodes each
+// bench 64 4 5000 current movetime : search current position with 4 threads for 5 sec
+// bench 16 1 5 blah perft : run a perft 5 on positions in file "blah"
+std::vector setup_bench(const std::string& currentFen, std::istream& is) {
+
+ std::vector fens, list;
+ std::string go, token;
+
+ // Assign default values to missing arguments
+ std::string ttSize = (is >> token) ? token : "16";
+ std::string threads = (is >> token) ? token : "1";
+ std::string limit = (is >> token) ? token : "13";
+ std::string fenFile = (is >> token) ? token : "default";
+ std::string limitType = (is >> token) ? token : "depth";
+
+ go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit;
+
+ if (fenFile == "default")
+ fens = Defaults;
+
+ else if (fenFile == "current")
+ fens.push_back(currentFen);
+
+ else
+ {
+ std::string fen;
+ std::ifstream file(fenFile);
+
+ if (!file.is_open())
+ {
+ std::cerr << "Unable to open file " << fenFile << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ while (getline(file, fen))
+ if (!fen.empty())
+ fens.push_back(fen);
+
+ file.close();
+ }
+
+ list.emplace_back("setoption name Threads value " + threads);
+ list.emplace_back("setoption name Hash value " + ttSize);
+ list.emplace_back("ucinewgame");
+
+ for (const std::string& fen : fens)
+ if (fen.find("setoption") != std::string::npos)
+ list.emplace_back(fen);
+ else
+ {
+ list.emplace_back("position fen " + fen);
+ list.emplace_back(go);
+ }
+
+ return list;
+}
+
+BenchmarkSetup setup_benchmark(std::istream& is) {
+ // TT_SIZE_PER_THREAD is chosen such that roughly half of the hash is used all positions
+ // for the current sequence have been searched.
+ static constexpr int TT_SIZE_PER_THREAD = 128;
+
+ static constexpr int DEFAULT_DURATION_S = 150;
+
+ BenchmarkSetup setup{};
+
+ // Assign default values to missing arguments
+ int desiredTimeS;
+
+ if (!(is >> setup.threads))
+ setup.threads = int(get_hardware_concurrency());
+ else
+ setup.originalInvocation += std::to_string(setup.threads);
+
+ if (!(is >> setup.ttSize))
+ setup.ttSize = TT_SIZE_PER_THREAD * setup.threads;
+ else
+ setup.originalInvocation += " " + std::to_string(setup.ttSize);
+
+ if (!(is >> desiredTimeS))
+ desiredTimeS = DEFAULT_DURATION_S;
+ else
+ setup.originalInvocation += " " + std::to_string(desiredTimeS);
+
+ setup.filledInvocation += std::to_string(setup.threads) + " " + std::to_string(setup.ttSize)
+ + " " + std::to_string(desiredTimeS);
+
+ auto getCorrectedTime = [&](int ply) {
+ // time per move is fit roughly based on LTC games
+ // seconds = 50/{ply+15}
+ // ms = 50000/{ply+15}
+ // with this fit 10th move gets 2000ms
+ // adjust for desired 10th move time
+ return 50000.0 / (static_cast(ply) + 15.0);
+ };
+
+ float totalTime = 0;
+ for (const auto& game : BenchmarkPositions)
+ {
+ int ply = 1;
+ for (int i = 0; i < static_cast(game.size()); ++i)
+ {
+ const float correctedTime = float(getCorrectedTime(ply));
+ totalTime += correctedTime;
+ ply += 1;
+ }
+ }
+
+ float timeScaleFactor = static_cast(desiredTimeS * 1000) / totalTime;
+
+ for (const auto& game : BenchmarkPositions)
+ {
+ setup.commands.emplace_back("ucinewgame");
+ int ply = 1;
+ for (const std::string& fen : game)
+ {
+ setup.commands.emplace_back("position fen " + fen);
+
+ const int correctedTime = static_cast(getCorrectedTime(ply) * timeScaleFactor);
+ setup.commands.emplace_back("go movetime " + std::to_string(correctedTime));
+
+ ply += 1;
+ }
+ }
+
+ return setup;
+}
+
+} // namespace Stockfish
diff --git a/src/benchmark.h b/src/benchmark.h
new file mode 100644
index 0000000000000000000000000000000000000000..a6606e78cad6feb990fc4b84ddc7fe7e31d1dc5f
--- /dev/null
+++ b/src/benchmark.h
@@ -0,0 +1,42 @@
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
+
+ Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifndef BENCHMARK_H_INCLUDED
+#define BENCHMARK_H_INCLUDED
+
+#include
+#include
+#include
+
+namespace Stockfish::Benchmark {
+
+std::vector setup_bench(const std::string&, std::istream&);
+
+struct BenchmarkSetup {
+ int ttSize;
+ int threads;
+ std::vector commands;
+ std::string originalInvocation;
+ std::string filledInvocation;
+};
+
+BenchmarkSetup setup_benchmark(std::istream&);
+
+} // namespace Stockfish
+
+#endif // #ifndef BENCHMARK_H_INCLUDED
diff --git a/src/bitboard.cpp b/src/bitboard.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0861222cf0e108380ceb296f0449c37a49ce9d8b
--- /dev/null
+++ b/src/bitboard.cpp
@@ -0,0 +1,189 @@
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
+
+ Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#include "bitboard.h"
+
+#include
+#include
+#include
+
+#include "misc.h"
+
+namespace Stockfish {
+
+uint8_t PopCnt16[1 << 16];
+uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
+
+Bitboard LineBB[SQUARE_NB][SQUARE_NB];
+Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
+Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
+
+alignas(64) Magic Magics[SQUARE_NB][2];
+
+namespace {
+
+Bitboard RookTable[0x19000]; // To store rook attacks
+Bitboard BishopTable[0x1480]; // To store bishop attacks
+
+void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]);
+}
+
+// Returns an ASCII representation of a bitboard suitable
+// to be printed to standard output. Useful for debugging.
+std::string Bitboards::pretty(Bitboard b) {
+
+ std::string s = "+---+---+---+---+---+---+---+---+\n";
+
+ for (Rank r = RANK_8;; --r)
+ {
+ for (File f = FILE_A; f <= FILE_H; ++f)
+ s += b & make_square(f, r) ? "| X " : "| ";
+
+ s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n";
+
+ if (r == RANK_1)
+ break;
+ }
+ s += " a b c d e f g h\n";
+
+ return s;
+}
+
+
+// Initializes various bitboard tables. It is called at
+// startup and relies on global objects to be already zero-initialized.
+void Bitboards::init() {
+
+ for (unsigned i = 0; i < (1 << 16); ++i)
+ PopCnt16[i] = uint8_t(std::bitset<16>(i).count());
+
+ for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
+ for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
+ SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2));
+
+ init_magics(ROOK, RookTable, Magics);
+ init_magics(BISHOP, BishopTable, Magics);
+
+ for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
+ {
+ for (PieceType pt : {BISHOP, ROOK})
+ for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
+ {
+ if (PseudoAttacks[pt][s1] & s2)
+ {
+ LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
+ BetweenBB[s1][s2] =
+ (attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
+ RayPassBB[s1][s2] =
+ attacks_bb(pt, s1, 0) & (attacks_bb(pt, s2, square_bb(s1)) | s2);
+ }
+ BetweenBB[s1][s2] |= s2;
+ }
+ }
+}
+
+namespace {
+// Computes all rook and bishop attacks at startup. Magic
+// bitboards are used to look up attacks of sliding pieces. As a reference see
+// https://www.chessprogramming.org/Magic_Bitboards. In particular, here we use
+// the so called "fancy" approach.
+void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) {
+
+#ifndef USE_PEXT
+ // Optimal PRNG seeds to pick the correct magics in the shortest time
+ int seeds[][RANK_NB] = {{8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020},
+ {728, 10316, 55013, 32803, 12281, 15100, 16645, 255}};
+
+ Bitboard occupancy[4096];
+ int epoch[4096] = {}, cnt = 0;
+#endif
+ Bitboard reference[4096];
+ int size = 0;
+
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
+ {
+ // Board edges are not considered in the relevant occupancies
+ Bitboard edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
+
+ // Given a square 's', the mask is the bitboard of sliding attacks from
+ // 's' computed on an empty board. The index must be big enough to contain
+ // all the attacks for each possible subset of the mask and so is 2 power
+ // the number of 1s of the mask. Hence we deduce the size of the shift to
+ // apply to the 64 or 32 bits word to get the index.
+ Magic& m = magics[s][pt - BISHOP];
+ m.mask = Bitboards::sliding_attack(pt, s, 0) & ~edges;
+#ifndef USE_PEXT
+ m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
+#endif
+ // Set the offset for the attacks table of the square. We have individual
+ // table sizes for each square with "Fancy Magic Bitboards".
+ m.attacks = s == SQ_A1 ? table : magics[s - 1][pt - BISHOP].attacks + size;
+ size = 0;
+
+ // Use Carry-Rippler trick to enumerate all subsets of masks[s] and
+ // store the corresponding sliding attack bitboard in reference[].
+ Bitboard b = 0;
+ do
+ {
+#ifndef USE_PEXT
+ occupancy[size] = b;
+#endif
+ reference[size] = Bitboards::sliding_attack(pt, s, b);
+
+ if (HasPext)
+ m.attacks[pext(b, m.mask)] = reference[size];
+
+ size++;
+ b = (b - m.mask) & m.mask;
+ } while (b);
+
+#ifndef USE_PEXT
+ PRNG rng(seeds[Is64Bit][rank_of(s)]);
+
+ // Find a magic for square 's' picking up an (almost) random number
+ // until we find the one that passes the verification test.
+ for (int i = 0; i < size;)
+ {
+ for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6;)
+ m.magic = rng.sparse_rand();
+
+ // A good magic must map every possible occupancy to an index that
+ // looks up the correct sliding attack in the attacks[s] database.
+ // Note that we build up the database for square 's' as a side
+ // effect of verifying the magic. Keep track of the attempt count
+ // and save it in epoch[], little speed-up trick to avoid resetting
+ // m.attacks[] after every failed attempt.
+ for (++cnt, i = 0; i < size; ++i)
+ {
+ unsigned idx = m.index(occupancy[i]);
+
+ if (epoch[idx] < cnt)
+ {
+ epoch[idx] = cnt;
+ m.attacks[idx] = reference[i];
+ }
+ else if (m.attacks[idx] != reference[i])
+ break;
+ }
+ }
+#endif
+ }
+}
+}
+
+} // namespace Stockfish
diff --git a/src/bitboard.h b/src/bitboard.h
new file mode 100644
index 0000000000000000000000000000000000000000..7d36b0a62ed65ee7982e11f378c518b1a8616e2b
--- /dev/null
+++ b/src/bitboard.h
@@ -0,0 +1,458 @@
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
+
+ Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifndef BITBOARD_H_INCLUDED
+#define BITBOARD_H_INCLUDED
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "types.h"
+
+namespace Stockfish {
+
+namespace Bitboards {
+
+void init();
+std::string pretty(Bitboard b);
+
+} // namespace Stockfish::Bitboards
+
+constexpr Bitboard FileABB = 0x0101010101010101ULL;
+constexpr Bitboard FileBBB = FileABB << 1;
+constexpr Bitboard FileCBB = FileABB << 2;
+constexpr Bitboard FileDBB = FileABB << 3;
+constexpr Bitboard FileEBB = FileABB << 4;
+constexpr Bitboard FileFBB = FileABB << 5;
+constexpr Bitboard FileGBB = FileABB << 6;
+constexpr Bitboard FileHBB = FileABB << 7;
+
+constexpr Bitboard Rank1BB = 0xFF;
+constexpr Bitboard Rank2BB = Rank1BB << (8 * 1);
+constexpr Bitboard Rank3BB = Rank1BB << (8 * 2);
+constexpr Bitboard Rank4BB = Rank1BB << (8 * 3);
+constexpr Bitboard Rank5BB = Rank1BB << (8 * 4);
+constexpr Bitboard Rank6BB = Rank1BB << (8 * 5);
+constexpr Bitboard Rank7BB = Rank1BB << (8 * 6);
+constexpr Bitboard Rank8BB = Rank1BB << (8 * 7);
+
+extern uint8_t PopCnt16[1 << 16];
+extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
+
+extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
+extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
+extern Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
+
+// Magic holds all magic bitboards relevant data for a single square
+struct Magic {
+ Bitboard mask;
+ Bitboard* attacks;
+#ifndef USE_PEXT
+ Bitboard magic;
+ unsigned shift;
+#endif
+
+ // Compute the attack's index using the 'magic bitboards' approach
+ unsigned index(Bitboard occupied) const {
+
+#ifdef USE_PEXT
+ return unsigned(pext(occupied, mask));
+#else
+ if (Is64Bit)
+ return unsigned(((occupied & mask) * magic) >> shift);
+
+ unsigned lo = unsigned(occupied) & unsigned(mask);
+ unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32);
+ return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift;
+#endif
+ }
+
+ Bitboard attacks_bb(Bitboard occupied) const { return attacks[index(occupied)]; }
+};
+
+extern Magic Magics[SQUARE_NB][2];
+
+constexpr Bitboard square_bb(Square s) {
+ assert(is_ok(s));
+ return 1ULL << s;
+}
+
+
+// Overloads of bitwise operators between a Bitboard and a Square for testing
+// whether a given bit is set in a bitboard, and for setting and clearing bits.
+
+constexpr Bitboard operator&(Bitboard b, Square s) { return b & square_bb(s); }
+constexpr Bitboard operator|(Bitboard b, Square s) { return b | square_bb(s); }
+constexpr Bitboard operator^(Bitboard b, Square s) { return b ^ square_bb(s); }
+constexpr Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); }
+constexpr Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }
+
+constexpr Bitboard operator&(Square s, Bitboard b) { return b & s; }
+constexpr Bitboard operator|(Square s, Bitboard b) { return b | s; }
+constexpr Bitboard operator^(Square s, Bitboard b) { return b ^ s; }
+
+constexpr Bitboard operator|(Square s1, Square s2) { return square_bb(s1) | s2; }
+
+constexpr bool more_than_one(Bitboard b) { return b & (b - 1); }
+
+
+// rank_bb() and file_bb() return a bitboard representing all the squares on
+// the given file or rank.
+
+constexpr Bitboard rank_bb(Rank r) { return Rank1BB << (8 * r); }
+
+constexpr Bitboard rank_bb(Square s) { return rank_bb(rank_of(s)); }
+
+constexpr Bitboard file_bb(File f) { return FileABB << f; }
+
+constexpr Bitboard file_bb(Square s) { return file_bb(file_of(s)); }
+
+
+// Moves a bitboard one or two steps as specified by the direction D
+template
+constexpr Bitboard shift(Bitboard b) {
+ return D == NORTH ? b << 8
+ : D == SOUTH ? b >> 8
+ : D == NORTH + NORTH ? b << 16
+ : D == SOUTH + SOUTH ? b >> 16
+ : D == EAST ? (b & ~FileHBB) << 1
+ : D == WEST ? (b & ~FileABB) >> 1
+ : D == NORTH_EAST ? (b & ~FileHBB) << 9
+ : D == NORTH_WEST ? (b & ~FileABB) << 7
+ : D == SOUTH_EAST ? (b & ~FileHBB) >> 7
+ : D == SOUTH_WEST ? (b & ~FileABB) >> 9
+ : 0;
+}
+
+
+// Returns the squares attacked by pawns of the given color
+// from the squares in the given bitboard.
+template
+constexpr Bitboard pawn_attacks_bb(Bitboard b) {
+ return C == WHITE ? shift(b) | shift(b)
+ : shift(b) | shift(b);
+}
+
+
+// Returns a bitboard representing an entire line (from board edge
+// to board edge) that intersects the two given squares. If the given squares
+// are not on a same file/rank/diagonal, the function returns 0. For instance,
+// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal.
+inline Bitboard line_bb(Square s1, Square s2) {
+
+ assert(is_ok(s1) && is_ok(s2));
+ return LineBB[s1][s2];
+}
+
+
+// Returns a bitboard representing the squares in the semi-open
+// segment between the squares s1 and s2 (excluding s1 but including s2). If the
+// given squares are not on a same file/rank/diagonal, it returns s2. For instance,
+// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5, E6 and F7, but
+// between_bb(SQ_E6, SQ_F8) will return a bitboard with the square F8. This trick
+// allows to generate non-king evasion moves faster: the defending piece must either
+// interpose itself to cover the check or capture the checking piece.
+inline Bitboard between_bb(Square s1, Square s2) {
+
+ assert(is_ok(s1) && is_ok(s2));
+ return BetweenBB[s1][s2];
+}
+
+// distance() functions return the distance between x and y, defined as the
+// number of steps for a king in x to reach y.
+
+template
+inline int distance(Square x, Square y);
+
+template<>
+inline int distance(Square x, Square y) {
+ return std::abs(file_of(x) - file_of(y));
+}
+
+template<>
+inline int distance(Square x, Square y) {
+ return std::abs(rank_of(x) - rank_of(y));
+}
+
+template<>
+inline int distance(Square x, Square y) {
+ return SquareDistance[x][y];
+}
+
+inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
+
+
+constexpr int constexpr_popcount(Bitboard b) {
+ b = b - ((b >> 1) & 0x5555555555555555ULL);
+ b = (b & 0x3333333333333333ULL) + ((b >> 2) & 0x3333333333333333ULL);
+ b = (b + (b >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
+ return static_cast((b * 0x0101010101010101ULL) >> 56);
+}
+
+// Counts the number of non-zero bits in a bitboard.
+inline int popcount(Bitboard b) {
+
+#ifndef USE_POPCNT
+
+ std::uint16_t indices[4];
+ std::memcpy(indices, &b, sizeof(b));
+ return PopCnt16[indices[0]] + PopCnt16[indices[1]] + PopCnt16[indices[2]]
+ + PopCnt16[indices[3]];
+
+#elif defined(_MSC_VER)
+
+ return int(_mm_popcnt_u64(b));
+
+#else // Assumed gcc or compatible compiler
+
+ return __builtin_popcountll(b);
+
+#endif
+}
+
+// Returns the least significant bit in a non-zero bitboard.
+inline Square lsb(Bitboard b) {
+ assert(b);
+
+#if defined(__GNUC__) // GCC, Clang, ICX
+
+ return Square(__builtin_ctzll(b));
+
+#elif defined(_MSC_VER)
+ #ifdef _WIN64 // MSVC, WIN64
+
+ unsigned long idx;
+ _BitScanForward64(&idx, b);
+ return Square(idx);
+
+ #else // MSVC, WIN32
+ unsigned long idx;
+
+ if (b & 0xffffffff)
+ {
+ _BitScanForward(&idx, int32_t(b));
+ return Square(idx);
+ }
+ else
+ {
+ _BitScanForward(&idx, int32_t(b >> 32));
+ return Square(idx + 32);
+ }
+ #endif
+#else // Compiler is neither GCC nor MSVC compatible
+ #error "Compiler not supported."
+#endif
+}
+
+// Returns the most significant bit in a non-zero bitboard.
+inline Square msb(Bitboard b) {
+ assert(b);
+
+#if defined(__GNUC__) // GCC, Clang, ICX
+
+ return Square(63 ^ __builtin_clzll(b));
+
+#elif defined(_MSC_VER)
+ #ifdef _WIN64 // MSVC, WIN64
+
+ unsigned long idx;
+ _BitScanReverse64(&idx, b);
+ return Square(idx);
+
+ #else // MSVC, WIN32
+
+ unsigned long idx;
+
+ if (b >> 32)
+ {
+ _BitScanReverse(&idx, int32_t(b >> 32));
+ return Square(idx + 32);
+ }
+ else
+ {
+ _BitScanReverse(&idx, int32_t(b));
+ return Square(idx);
+ }
+ #endif
+#else // Compiler is neither GCC nor MSVC compatible
+ #error "Compiler not supported."
+#endif
+}
+
+// Returns the bitboard of the least significant
+// square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)).
+inline Bitboard least_significant_square_bb(Bitboard b) {
+ assert(b);
+ return b & -b;
+}
+
+// Finds and clears the least significant bit in a non-zero bitboard.
+inline Square pop_lsb(Bitboard& b) {
+ assert(b);
+ const Square s = lsb(b);
+ b &= b - 1;
+ return s;
+}
+
+namespace Bitboards {
+// Returns the bitboard of target square for the given step
+// from the given square. If the step is off the board, returns empty bitboard.
+constexpr Bitboard safe_destination(Square s, int step) {
+ constexpr auto abs = [](int v) { return v < 0 ? -v : v; };
+ Square to = Square(s + step);
+ return is_ok(to) && abs(file_of(s) - file_of(to)) <= 2 ? square_bb(to) : Bitboard(0);
+}
+
+constexpr Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
+ Bitboard attacks = 0;
+ Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
+ Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
+
+ for (Direction d : (pt == ROOK ? RookDirections : BishopDirections))
+ {
+ Square s = sq;
+ while (safe_destination(s, d))
+ {
+ attacks |= (s += d);
+ if (occupied & s)
+ {
+ break;
+ }
+ }
+ }
+
+ return attacks;
+}
+
+constexpr Bitboard knight_attack(Square sq) {
+ Bitboard b = {};
+ for (int step : {-17, -15, -10, -6, 6, 10, 15, 17})
+ b |= safe_destination(sq, step);
+ return b;
+}
+
+constexpr Bitboard king_attack(Square sq) {
+ Bitboard b = {};
+ for (int step : {-9, -8, -7, -1, 1, 7, 8, 9})
+ b |= safe_destination(sq, step);
+ return b;
+}
+
+constexpr Bitboard pseudo_attacks(PieceType pt, Square sq) {
+ switch (pt)
+ {
+ case PieceType::ROOK :
+ case PieceType::BISHOP :
+ return sliding_attack(pt, sq, 0);
+ case PieceType::QUEEN :
+ return sliding_attack(PieceType::ROOK, sq, 0) | sliding_attack(PieceType::BISHOP, sq, 0);
+ case PieceType::KNIGHT :
+ return knight_attack(sq);
+ case PieceType::KING :
+ return king_attack(sq);
+ default :
+ assert(false);
+ return 0;
+ }
+}
+
+}
+
+inline constexpr auto PseudoAttacks = []() constexpr {
+ std::array, PIECE_TYPE_NB> attacks{};
+
+ for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
+ {
+ attacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1));
+ attacks[BLACK][s1] = pawn_attacks_bb(square_bb(s1));
+
+ attacks[KING][s1] = Bitboards::pseudo_attacks(KING, s1);
+ attacks[KNIGHT][s1] = Bitboards::pseudo_attacks(KNIGHT, s1);
+ attacks[QUEEN][s1] = attacks[BISHOP][s1] = Bitboards::pseudo_attacks(BISHOP, s1);
+ attacks[QUEEN][s1] |= attacks[ROOK][s1] = Bitboards::pseudo_attacks(ROOK, s1);
+ }
+
+ return attacks;
+}();
+
+
+// Returns the pseudo attacks of the given piece type
+// assuming an empty board.
+template
+inline Bitboard attacks_bb(Square s, Color c = COLOR_NB) {
+
+ assert((Pt != PAWN || c < COLOR_NB) && is_ok(s));
+ return Pt == PAWN ? PseudoAttacks[c][s] : PseudoAttacks[Pt][s];
+}
+
+
+// Returns the attacks by the given piece
+// assuming the board is occupied according to the passed Bitboard.
+// Sliding piece attacks do not continue passed an occupied square.
+template
+inline Bitboard attacks_bb(Square s, Bitboard occupied) {
+
+ assert(Pt != PAWN && is_ok(s));
+
+ switch (Pt)
+ {
+ case BISHOP :
+ case ROOK :
+ return Magics[s][Pt - BISHOP].attacks_bb(occupied);
+ case QUEEN :
+ return attacks_bb(s, occupied) | attacks_bb(s, occupied);
+ default :
+ return PseudoAttacks[Pt][s];
+ }
+}
+
+// Returns the attacks by the given piece
+// assuming the board is occupied according to the passed Bitboard.
+// Sliding piece attacks do not continue passed an occupied square.
+inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
+
+ assert(pt != PAWN && is_ok(s));
+
+ switch (pt)
+ {
+ case BISHOP :
+ return attacks_bb(s, occupied);
+ case ROOK :
+ return attacks_bb(s, occupied);
+ case QUEEN :
+ return attacks_bb(s, occupied) | attacks_bb(s, occupied);
+ default :
+ return PseudoAttacks[pt][s];
+ }
+}
+
+inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) {
+ return type_of(pc) == PAWN ? PseudoAttacks[color_of(pc)][s]
+ : attacks_bb(type_of(pc), s, occupied);
+}
+
+} // namespace Stockfish
+
+#endif // #ifndef BITBOARD_H_INCLUDED
diff --git a/src/engine.cpp b/src/engine.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..be0fe3c4086e9aeede5707a97503679ef803d947
--- /dev/null
+++ b/src/engine.cpp
@@ -0,0 +1,411 @@
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
+
+ Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#include "engine.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "evaluate.h"
+#include "misc.h"
+#include "nnue/network.h"
+#include "nnue/nnue_common.h"
+#include "nnue/nnue_misc.h"
+#include "numa.h"
+#include "perft.h"
+#include "position.h"
+#include "search.h"
+#include "shm.h"
+#include "syzygy/tbprobe.h"
+#include "types.h"
+#include "uci.h"
+#include "ucioption.h"
+
+namespace Stockfish {
+
+namespace NN = Eval::NNUE;
+
+constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
+constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048;
+int MaxThreads = std::max(1024, 4 * int(get_hardware_concurrency()));
+
+// The default configuration will attempt to group L3 domains up to 32 threads.
+// This size was found to be a good balance between the Elo gain of increased
+// history sharing and the speed loss from more cross-cache accesses (see
+// PR#6526). The user can always explicitly override this behavior.
+constexpr NumaAutoPolicy DefaultNumaPolicy = BundledL3Policy{32};
+
+Engine::Engine(std::optional path) :
+ binaryDirectory(path ? CommandLine::get_binary_directory(*path) : ""),
+ numaContext(NumaConfig::from_system(DefaultNumaPolicy)),
+ states(new std::deque(1)),
+ threads(),
+ networks(numaContext, get_default_networks()) {
+
+ pos.set(StartFEN, false, &states->back());
+
+ options.add( //
+ "Debug Log File", Option("", [](const Option& o) {
+ start_logger(o);
+ return std::nullopt;
+ }));
+
+ options.add( //
+ "NumaPolicy", Option("auto", [this](const Option& o) {
+ set_numa_config_from_option(o);
+ return numa_config_information_as_string() + "\n"
+ + thread_allocation_information_as_string();
+ }));
+
+ options.add( //
+ "Threads", Option(1, 1, MaxThreads, [this](const Option&) {
+ resize_threads();
+ return thread_allocation_information_as_string();
+ }));
+
+ options.add( //
+ "Hash", Option(16, 1, MaxHashMB, [this](const Option& o) {
+ set_tt_size(o);
+ return std::nullopt;
+ }));
+
+ options.add( //
+ "Clear Hash", Option([this](const Option&) {
+ search_clear();
+ return std::nullopt;
+ }));
+
+ options.add( //
+ "Ponder", Option(false));
+
+ options.add( //
+ "MultiPV", Option(1, 1, MAX_MOVES));
+
+ options.add("Skill Level", Option(20, 0, 20));
+
+ options.add("Move Overhead", Option(10, 0, 5000));
+
+ options.add("nodestime", Option(0, 0, 10000));
+
+ options.add("UCI_Chess960", Option(false));
+
+ options.add("UCI_LimitStrength", Option(false));
+
+ options.add("UCI_Elo",
+ Option(Stockfish::Search::Skill::LowestElo, Stockfish::Search::Skill::LowestElo,
+ Stockfish::Search::Skill::HighestElo));
+
+ options.add("UCI_ShowWDL", Option(false));
+
+ options.add( //
+ "SyzygyPath", Option("", [](const Option& o) {
+ Tablebases::init(o);
+ return std::nullopt;
+ }));
+
+ options.add("SyzygyProbeDepth", Option(1, 1, 100));
+
+ options.add("Syzygy50MoveRule", Option(true));
+
+ options.add("SyzygyProbeLimit", Option(7, 0, 7));
+
+ options.add( //
+ "EvalFile", Option(EvalFileDefaultNameBig, [this](const Option& o) {
+ load_big_network(o);
+ return std::nullopt;
+ }));
+
+ options.add( //
+ "EvalFileSmall", Option(EvalFileDefaultNameSmall, [this](const Option& o) {
+ load_small_network(o);
+ return std::nullopt;
+ }));
+
+ threads.clear();
+ threads.ensure_network_replicated();
+ resize_threads();
+}
+
+std::uint64_t Engine::perft(const std::string& fen, Depth depth, bool isChess960) {
+ verify_networks();
+
+ return Benchmark::perft(fen, depth, isChess960);
+}
+
+void Engine::go(Search::LimitsType& limits) {
+ assert(limits.perft == 0);
+ verify_networks();
+
+ threads.start_thinking(options, pos, states, limits);
+}
+void Engine::stop() { threads.stop = true; }
+
+void Engine::search_clear() {
+ wait_for_search_finished();
+
+ tt.clear(threads);
+ threads.clear();
+
+ // @TODO wont work with multiple instances
+ Tablebases::init(options["SyzygyPath"]); // Free mapped files
+}
+
+void Engine::set_on_update_no_moves(std::function&& f) {
+ updateContext.onUpdateNoMoves = std::move(f);
+}
+
+void Engine::set_on_update_full(std::function&& f) {
+ updateContext.onUpdateFull = std::move(f);
+}
+
+void Engine::set_on_iter(std::function&& f) {
+ updateContext.onIter = std::move(f);
+}
+
+void Engine::set_on_bestmove(std::function&& f) {
+ updateContext.onBestmove = std::move(f);
+}
+
+void Engine::set_on_verify_networks(std::function&& f) {
+ onVerifyNetworks = std::move(f);
+}
+
+void Engine::wait_for_search_finished() { threads.main_thread()->wait_for_search_finished(); }
+
+void Engine::set_position(const std::string& fen, const std::vector& moves) {
+ // Drop the old state and create a new one
+ states = StateListPtr(new std::deque(1));
+ pos.set(fen, options["UCI_Chess960"], &states->back());
+
+ for (const auto& move : moves)
+ {
+ auto m = UCIEngine::to_move(pos, move);
+
+ if (m == Move::none())
+ break;
+
+ states->emplace_back();
+ pos.do_move(m, states->back());
+ }
+}
+
+// modifiers
+
+void Engine::set_numa_config_from_option(const std::string& o) {
+ if (o == "auto" || o == "system")
+ {
+ numaContext.set_numa_config(NumaConfig::from_system(DefaultNumaPolicy));
+ }
+ else if (o == "hardware")
+ {
+ // Don't respect affinity set in the system.
+ numaContext.set_numa_config(NumaConfig::from_system(DefaultNumaPolicy, false));
+ }
+ else if (o == "none")
+ {
+ numaContext.set_numa_config(NumaConfig{});
+ }
+ else
+ {
+ numaContext.set_numa_config(NumaConfig::from_string(o));
+ }
+
+ // Force reallocation of threads in case affinities need to change.
+ resize_threads();
+ threads.ensure_network_replicated();
+}
+
+void Engine::resize_threads() {
+ threads.wait_for_search_finished();
+ threads.set(numaContext.get_numa_config(), {options, threads, tt, sharedHists, networks},
+ updateContext);
+
+ // Reallocate the hash with the new threadpool size
+ set_tt_size(options["Hash"]);
+ threads.ensure_network_replicated();
+}
+
+void Engine::set_tt_size(size_t mb) {
+ wait_for_search_finished();
+ tt.resize(mb, threads);
+}
+
+void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; }
+
+// network related
+
+void Engine::verify_networks() const {
+ networks->big.verify(options["EvalFile"], onVerifyNetworks);
+ networks->small.verify(options["EvalFileSmall"], onVerifyNetworks);
+
+ auto statuses = networks.get_status_and_errors();
+ for (size_t i = 0; i < statuses.size(); ++i)
+ {
+ const auto [status, error] = statuses[i];
+ std::string message = "Network replica " + std::to_string(i + 1) + ": ";
+ if (status == SystemWideSharedConstantAllocationStatus::NoAllocation)
+ {
+ message += "No allocation.";
+ }
+ else if (status == SystemWideSharedConstantAllocationStatus::LocalMemory)
+ {
+ message += "Local memory.";
+ }
+ else if (status == SystemWideSharedConstantAllocationStatus::SharedMemory)
+ {
+ message += "Shared memory.";
+ }
+ else
+ {
+ message += "Unknown status.";
+ }
+
+ if (error.has_value())
+ {
+ message += " " + *error;
+ }
+
+ onVerifyNetworks(message);
+ }
+}
+
+std::unique_ptr Engine::get_default_networks() const {
+
+ auto networks_ =
+ std::make_unique(NN::EvalFile{EvalFileDefaultNameBig, "None", ""},
+ NN::EvalFile{EvalFileDefaultNameSmall, "None", ""});
+
+ networks_->big.load(binaryDirectory, "");
+ networks_->small.load(binaryDirectory, "");
+
+ return networks_;
+}
+
+void Engine::load_big_network(const std::string& file) {
+ networks.modify_and_replicate(
+ [this, &file](NN::Networks& networks_) { networks_.big.load(binaryDirectory, file); });
+ threads.clear();
+ threads.ensure_network_replicated();
+}
+
+void Engine::load_small_network(const std::string& file) {
+ networks.modify_and_replicate(
+ [this, &file](NN::Networks& networks_) { networks_.small.load(binaryDirectory, file); });
+ threads.clear();
+ threads.ensure_network_replicated();
+}
+
+void Engine::save_network(const std::pair, std::string> files[2]) {
+ networks.modify_and_replicate([&files](NN::Networks& networks_) {
+ networks_.big.save(files[0].first);
+ networks_.small.save(files[1].first);
+ });
+}
+
+// utility functions
+
+void Engine::trace_eval() const {
+ StateListPtr trace_states(new std::deque(1));
+ Position p;
+ p.set(pos.fen(), options["UCI_Chess960"], &trace_states->back());
+
+ verify_networks();
+
+ sync_cout << "\n" << Eval::trace(p, *networks) << sync_endl;
+}
+
+const OptionsMap& Engine::get_options() const { return options; }
+OptionsMap& Engine::get_options() { return options; }
+
+std::string Engine::fen() const { return pos.fen(); }
+
+void Engine::flip() { pos.flip(); }
+
+std::string Engine::visualize() const {
+ std::stringstream ss;
+ ss << pos;
+ return ss.str();
+}
+
+int Engine::get_hashfull(int maxAge) const { return tt.hashfull(maxAge); }
+
+std::vector> Engine::get_bound_thread_count_by_numa_node() const {
+ auto counts = threads.get_bound_thread_count_by_numa_node();
+ const NumaConfig& cfg = numaContext.get_numa_config();
+ std::vector> ratios;
+ NumaIndex n = 0;
+ for (; n < counts.size(); ++n)
+ ratios.emplace_back(counts[n], cfg.num_cpus_in_numa_node(n));
+ if (!counts.empty())
+ for (; n < cfg.num_numa_nodes(); ++n)
+ ratios.emplace_back(0, cfg.num_cpus_in_numa_node(n));
+ return ratios;
+}
+
+std::string Engine::get_numa_config_as_string() const {
+ return numaContext.get_numa_config().to_string();
+}
+
+std::string Engine::numa_config_information_as_string() const {
+ auto cfgStr = get_numa_config_as_string();
+ return "Available processors: " + cfgStr;
+}
+
+std::string Engine::thread_binding_information_as_string() const {
+ auto boundThreadsByNode = get_bound_thread_count_by_numa_node();
+ std::stringstream ss;
+ if (boundThreadsByNode.empty())
+ return ss.str();
+
+ bool isFirst = true;
+
+ for (auto&& [current, total] : boundThreadsByNode)
+ {
+ if (!isFirst)
+ ss << ":";
+ ss << current << "/" << total;
+ isFirst = false;
+ }
+
+ return ss.str();
+}
+
+std::string Engine::thread_allocation_information_as_string() const {
+ std::stringstream ss;
+
+ size_t threadsSize = threads.size();
+ ss << "Using " << threadsSize << (threadsSize > 1 ? " threads" : " thread");
+
+ auto boundThreadsByNodeStr = thread_binding_information_as_string();
+ if (boundThreadsByNodeStr.empty())
+ return ss.str();
+
+ ss << " with NUMA node thread binding: ";
+ ss << boundThreadsByNodeStr;
+
+ return ss.str();
+}
+}
diff --git a/src/engine.h b/src/engine.h
new file mode 100644
index 0000000000000000000000000000000000000000..92d6282dcd6fb56dffd59279e1bd1ee4be283b86
--- /dev/null
+++ b/src/engine.h
@@ -0,0 +1,134 @@
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
+
+ Stockfish is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Stockfish is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifndef ENGINE_H_INCLUDED
+#define ENGINE_H_INCLUDED
+
+#include
+#include
+#include
+#include