diff --git a/.travis.yml b/.travis.yml
index e2ae61be..d563a1e1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,5 @@
language: cpp
-dist: xenial
+dist: bionic
matrix:
include:
@@ -7,7 +7,6 @@ matrix:
compiler: gcc
addons:
apt:
- sources: ['ubuntu-toolchain-r-test']
packages: ['g++-8', 'g++-8-multilib', 'g++-multilib', 'valgrind', 'expect', 'curl']
env:
- COMPILER=g++-8
@@ -17,23 +16,23 @@ matrix:
compiler: clang
addons:
apt:
- sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-xenial-6.0']
- packages: ['clang-6.0', 'llvm-6.0-dev', 'g++-multilib', 'valgrind', 'expect', 'curl']
+ packages: ['clang-10', 'llvm-10-dev', 'g++-multilib', 'valgrind', 'expect', 'curl']
env:
- - COMPILER=clang++-6.0
+ - COMPILER=clang++-10
- COMP=clang
- - LDFLAGS=-fuse-ld=lld
- os: osx
+ osx_image: xcode12
compiler: gcc
env:
- COMPILER=g++
- COMP=gcc
- os: osx
+ osx_image: xcode12
compiler: clang
env:
- - COMPILER=clang++ V='Apple LLVM 9.4.1' # Apple LLVM version 9.1.0 (clang-902.0.39.2)
+ - COMPILER=clang++
- COMP=clang
branches:
@@ -48,26 +47,34 @@ script:
- git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig
- export benchref=$(cat git_sig)
- echo "Reference bench:" $benchref
+
+ #
+ # Compiler version string
+ - $COMPILER -v
+
#
# Verify bench number against various builds
- export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG"
- make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref
- - make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref
- - make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref
+ - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref; fi
+ - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref; fi
#
# Check perft and reproducible search
+ - export CXXFLAGS="-Werror"
+ - make clean && make -j2 ARCH=x86-64 build
- ../tests/perft.sh
- ../tests/reprosearch.sh
+
#
# Valgrind
#
- export CXXFLAGS="-O1 -fno-inline"
- if [ -x "$(command -v valgrind )" ]; then make clean && make -j2 ARCH=x86-64 debug=yes optimize=no build > /dev/null && ../tests/instrumented.sh --valgrind; fi
- if [ -x "$(command -v valgrind )" ]; then ../tests/instrumented.sh --valgrind-thread; fi
+
#
# Sanitizer
#
- # Use g++-8 as a proxy for having sanitizers, might need revision as they become available for more recent versions of clang/gcc
- - if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi
- - if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi
+ - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi
+ - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi
diff --git a/AUTHORS b/AUTHORS
index f08d71d3..07e07297 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,10 +1,17 @@
-# List of authors for Stockfish, as of March 30, 2020
+# List of authors for Stockfish, as of August 4, 2020
+# Founders of the Stockfish project and fishtest infrastructure
Tord Romstad (romstad)
Marco Costalba (mcostalba)
Joona Kiiski (zamar)
Gary Linscott (glinscott)
+# Authors and inventors of NNUE, training, NNUE port
+Yu Nasu (ynasu87)
+Motohiro Isozaki (yaneurao)
+Hisayori Noda (nodchip)
+
+# all other authors of the code in alphabetical order
Aditya (absimaldata)
Adrian Petrescu (apetresc)
Ajith Chandy Jose (ajithcj)
@@ -36,6 +43,7 @@ Dariusz Orzechowski
David Zar
Daylen Yang (daylen)
DiscanX
+Dominik Schlösser (domschl)
double-beep
Eduardo Cáceres (eduherminio)
Eelco de Groot (KingDefender)
@@ -115,7 +123,8 @@ Nick Pelling (nickpelling)
Nicklas Persson (NicklasPersson)
Niklas Fiekas (niklasf)
Nikolay Kostov (NikolayIT)
-Nguyen Pham
+Nguyen Pham (nguyenpham)
+Norman Schmidt (FireFather)
Ondrej Mosnáček (WOnder93)
Oskar Werkelin Ahlin
Pablo Vazquez
@@ -135,14 +144,17 @@ Richard Lloyd
Rodrigo Exterckötter Tjäder
Ron Britvich (Britvich)
Ronald de Man (syzygy1, syzygy)
+rqs
Ryan Schmitt
Ryan Takker
Sami Kiminki (skiminki)
Sebastian Buchwald (UniQP)
Sergei Antonov (saproj)
Sergei Ivanov (svivanov72)
+Sergio Vieri (sergiovieri)
sf-x
Shane Booth (shane31)
+Shawn Varghese (xXH4CKST3RXx)
Stefan Geschwentner (locutus2)
Stefano Cardanobile (Stefano80)
Steinar Gunderson (sesse)
@@ -155,9 +167,11 @@ Tom Vijlbrief (tomtor)
Tomasz Sobczyk (Sopel97)
Torsten Franz (torfranz, tfranzer)
Tracey Emery (basepr1me)
+tttak
Unai Corzo (unaiic)
Uri Blass (uriblass)
Vince Negri (cuddlestmonkey)
+zz4032
# Additionally, we acknowledge the authors and maintainers of fishtest,
diff --git a/Readme.md b/Readme.md
new file mode 100644
index 00000000..73eec1fb
--- /dev/null
+++ b/Readme.md
@@ -0,0 +1,63 @@
+
+
+
+
+Stockfish NNUE
+
+## Overview
+Stockfish NNUE is a port of a shogi neural network named NNUE (efficiently updateable neural network backwards) to Stockfish 11. To learn more about the Stockfish chess engine, look [here](stockfish.md) for an overview and [here](https://github.com/official-stockfish/Stockfish) for the official repository.
+
+## Training Guide
+### Generating Training Data
+Use the "no-nnue.nnue-gen-sfen-from-original-eval" binary. The given example is generation in its simplest form. There are more commands.
+```
+uci
+setoption name Threads value x
+setoption name Hash value y
+setoption name SyzygyPath value path
+isready
+gensfen depth a loop b use_draw_in_training_data_generation 1 eval_limit 32000
+```
+Specify how many threads and how much memory you would like to use with the x and y values. The option SyzygyPath is not necessary, but if you would like to use it, you must first have Syzygy endgame tablebases on your computer, which you can find [here](http://oics.olympuschess.com/tracker/index.php). You will need to have a torrent client to download these tablebases, as that is probably the fastest way to obtain them. The path is the path to the folder containing those tablebases. It does not have to be surrounded in quotes.
+
+This will save a file named "generated_kifu.bin" in the same folder as the binary. Once generation is done, rename the file to something like "1billiondepth12.bin" to remember the depth and quantity of the positions and move it to a folder named "trainingdata" in the same directory as the binaries.
+#### Generation Parameters
+- Depth is the searched depth per move, or how far the engine looks forward. This value is an integer.
+- Loop is the amount of positions generated. This value is also an integer
+### Generating Validation Data
+The process is the same as the generation of training data, except for the fact that you need to set loop to 1 million, because you don't need a lot of validation data. The depth should be the same as before or slightly higher than the depth of the training data. After generation rename the validation data file to val.bin and drop it in a folder named "validationdata" in the same directory to make it easier.
+### Training a Completely New Network
+Use the "avx2.halfkp_256x2-32-32.nnue-learn.2020-07-11" binary. Create an empty folder named "evalsave" in the same directory as the binaries.
+```
+uci
+setoption name SkipLoadingEval value true
+setoption name Threads value x
+isready
+learn targetdir trainingdata loop 100 batchsize 1000000 use_draw_in_training 1 use_draw_in_validation 1 eta 1 lambda 1 eval_limit 32000 nn_batch_size 1000 newbob_decay 0.5 eval_save_interval 250000000 loss_output_interval 1000000 mirror_percentage 50 validation_set_file_name validationdata\val.bin
+```
+Nets get saved in the "evalsave" folder.
+
+#### Training Parameters
+- eta is the learning rate
+- lambda is the amount of weight it puts to eval of learning data vs win/draw/loss results. 1 puts all weight on eval, lambda 0 puts all weight on WDL results.
+
+### Reinforcement Learning
+If you would like to do some reinforcement learning on your original network, you must first generate training data using the learn binaries. Make sure that your previously trained network is in the eval folder. Use the commands specified above. Make sure `SkipLoadingEval` is set to false so that the data generated is using the neural net's eval by typing the command `uci setoption name SkipLoadingEval value false` before typing the `isready` command. You should aim to generate less positions than the first run, around 1/10 of the number of positions generated in the first run. The depth should be higher as well. You should also do the same for validation data, with the depth being higher than the last run.
+
+After you have generated the training data, you must move it into your training data folder and delete the older data so that the binary does not accidentally train on the same data again. Do the same for the validation data and name it to val-1.bin to make it less confusing. Make sure the evalsave folder is empty. Then, using the same binary, type in the training commands shown above. Do __NOT__ set `SkipLoadingEval` to true, it must be false or you will get a completely new network, instead of a network trained with reinforcement learning. You should also set eval_save_interval to a number that is lower than the amount of positions in your training data, perhaps also 1/10 of the original value. The validation file should be set to the new validation data, not the old data.
+
+After training is finished, your new net should be located in the "final" folder under the "evalsave" directory. You should test this new network against the older network to see if there are any improvements.
+
+## Using Your Trained Net
+If you want to use your generated net, copy the net located in the "final" folder under the "evalsave" directory and move it into a new folder named "eval" under the directory with the binaries. You can then use the halfkp_256x2 binaries pertaining to your CPU with a standard chess GUI, such as Cutechess. Refer to the [releases page](https://github.com/nodchip/Stockfish/releases) to find out which binary is best for your CPU.
+
+If the engine does not load any net file, or shows "Error! *** not found or wrong format", please try to sepcify the net with the full file path with the "EvalFile" option by typing the command `setoption name EvalFile value path` where path is the full file path.
+
+## Resources
+- [Stockfish NNUE Wiki](https://www.qhapaq.org/shogi/shogiwiki/stockfish-nnue/)
+- [Training instructions](https://twitter.com/mktakizawa/status/1273042640280252416) from the creator of the Elmo shogi engine
+- [Original Talkchess thread](http://talkchess.com/forum3/viewtopic.php?t=74059) discussing Stockfish NNUE
+- [Guide to Stockfish NNUE](http://yaneuraou.yaneu.com/2020/06/19/stockfish-nnue-the-complete-guide/)
+- [Unofficial Stockfish Discord](https://discord.gg/nv8gDtt)
+
+A more updated list can be found in the #sf-nnue-resources channel in the Discord.
diff --git a/appveyor.yml b/appveyor.yml
index 21f3bbe3..d356ba2f 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -4,10 +4,9 @@ clone_depth: 50
branches:
only:
- master
- - appveyor
# Operating system (build VM template)
-os: Visual Studio 2017
+os: Visual Studio 2019
# Build platform, i.e. x86, x64, AnyCPU. This setting is optional.
platform:
@@ -36,8 +35,11 @@ before_build:
$src = $src.Replace("\", "/")
# Build CMakeLists.txt
- $t = 'cmake_minimum_required(VERSION 3.8)',
+ $t = 'cmake_minimum_required(VERSION 3.17)',
'project(Stockfish)',
+ 'set(CMAKE_CXX_STANDARD 17)',
+ 'set(CMAKE_CXX_STANDARD_REQUIRED ON)',
+ 'set (CMAKE_CXX_EXTENSIONS OFF)',
'set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src)',
'set(source_files', $src, ')',
'add_executable(stockfish ${source_files})'
@@ -51,10 +53,11 @@ before_build:
$b = git log HEAD | sls "\b[Bb]ench[ :]+[0-9]{7}" | select -first 1
$bench = $b -match '\D+(\d+)' | % { $matches[1] }
Write-Host "Reference bench:" $bench
- $g = "Visual Studio 15 2017"
- If (${env:PLATFORM} -eq 'x64') { $g = $g + ' Win64' }
- cmake -G "${g}" .
- Write-Host "Generated files for: " $g
+ $g = "Visual Studio 16 2019"
+ If (${env:PLATFORM} -eq 'x64') { $a = "x64" }
+ If (${env:PLATFORM} -eq 'x86') { $a = "Win32" }
+ cmake -G "${g}" -A ${a} .
+ Write-Host "Generated files for: " $g $a
build_script:
- cmake --build . --config %CONFIGURATION% -- /verbosity:minimal
diff --git a/src/Makefile b/src/Makefile
index 1e07bba4..79ce61a3 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -55,9 +55,9 @@ SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp
learn/learning_tools.cpp \
learn/multi_think.cpp
-OBJS = $(SRCS:.cpp=.o)
+OBJS = $(notdir $(SRCS:.cpp=.o))
-VPATH = syzygy
+VPATH = syzygy:nnue:nnue/features
### Establish the operating system name
KERNEL = $(shell uname -s)
@@ -82,12 +82,14 @@ endif
# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction
# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction
# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions
+# sse3 = yes/no --- -msse3 --- Use Intel Streaming SIMD Extensions 3
# ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3
# sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1
# sse42 = yes/no --- -msse4.2 --- Use Intel Streaming SIMD Extensions 4.2
# avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2
# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction
-# avx512 = yes/no --- -mavx512vbmi --- Use Intel Advanced Vector Extensions 512
+# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512
+# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture
#
# 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
@@ -108,6 +110,8 @@ sse42 = no
avx2 = no
pext = no
avx512 = no
+neon = no
+ARCH = x86-64-modern
### 2.2 Architecture specific
ifeq ($(ARCH),general-32)
@@ -142,16 +146,14 @@ ifeq ($(ARCH),x86-64-sse3)
prefetch = yes
sse = yes
sse3 = yes
- ssse3 = yes
endif
ifeq ($(ARCH),x86-64-sse3-popcnt)
arch = x86_64
prefetch = yes
- popcnt = yes
sse = yes
sse3 = yes
- ssse3 = yes
+ popcnt = yes
endif
ifeq ($(ARCH),x86-64-ssse3)
@@ -165,6 +167,17 @@ endif
ifeq ($(ARCH),x86-64-sse41)
arch = x86_64
prefetch = yes
+ popcnt = yes
+ sse = yes
+ sse3 = yes
+ ssse3 = yes
+ sse41 = yes
+endif
+
+ifeq ($(ARCH),x86-64-modern)
+ arch = x86_64
+ prefetch = yes
+ popcnt = yes
sse = yes
sse3 = yes
ssse3 = yes
@@ -184,7 +197,6 @@ endif
ifeq ($(ARCH),x86-64-avx2)
arch = x86_64
- bits = 64
prefetch = yes
popcnt = yes
sse = yes
@@ -210,7 +222,6 @@ endif
ifeq ($(ARCH),x86-64-avx512)
arch = x86_64
- bits = 64
prefetch = yes
popcnt = yes
sse = yes
@@ -233,6 +244,14 @@ ifeq ($(ARCH),armv8)
arch = armv8-a
prefetch = yes
popcnt = yes
+ neon = yes
+endif
+
+ifeq ($(ARCH),apple-silicon)
+ arch = arm64
+ prefetch = yes
+ popcnt = yes
+ neon = yes
endif
ifeq ($(ARCH),ppc-32)
@@ -251,7 +270,7 @@ endif
### ==========================================================================
### 3.1 Selecting compiler (default = gcc)
-CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++17 $(EXTRACXXFLAGS) $(NNUECXXFLAGS)
+CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++17 $(EXTRACXXFLAGS)
DEPENDFLAGS += -std=c++17
LDFLAGS += $(EXTRALDFLAGS)
@@ -332,28 +351,6 @@ ifeq ($(COMP),clang)
endif
endif
-ifeq ($(COMP),msys2)
- comp=gcc
- CXX=g++
- CXXFLAGS += -pedantic -Wextra -Wshadow
-
- ifeq ($(ARCH),armv7)
- ifeq ($(OS),Android)
- CXXFLAGS += -m$(bits)
- LDFLAGS += -m$(bits)
- endif
- else
- CXXFLAGS += -m$(bits)
- LDFLAGS += -m$(bits)
- endif
-
- ifneq ($(KERNEL),Darwin)
- LDFLAGS += -Wl,--no-as-needed
- endif
-
- LDFLAGS += -static -Wl,-s
-endif
-
ifeq ($(comp),icc)
profile_make = icc-profile-make
profile_use = icc-profile-use
@@ -368,8 +365,8 @@ endif
endif
ifeq ($(KERNEL),Darwin)
- CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.9
- LDFLAGS += -arch $(arch) -mmacosx-version-min=10.9
+ CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.15
+ LDFLAGS += -arch $(arch) -mmacosx-version-min=10.15
endif
### Travis CI script uses COMPILER to overwrite CXX
@@ -402,8 +399,8 @@ endif
### 3.2.2 Debugging with undefined behavior sanitizers
ifneq ($(sanitize),no)
- CXXFLAGS += -g3 -fsanitize=$(sanitize) -fuse-ld=gold
- LDFLAGS += -fsanitize=$(sanitize) -fuse-ld=gold
+ CXXFLAGS += -g3 -fsanitize=$(sanitize)
+ LDFLAGS += -fsanitize=$(sanitize)
endif
### 3.3 Optimization
@@ -441,56 +438,61 @@ endif
### 3.6 popcnt
ifeq ($(popcnt),yes)
+ ifeq ($(arch),$(filter $(arch),ppc64 armv8-a arm64))
CXXFLAGS += -DUSE_POPCNT
- ifneq ($(arch),$(filter $(arch),ppc64 armv8-a))
- ifeq ($(comp),$(filter $(comp),gcc clang mingw msys2))
- CXXFLAGS += -mpopcnt
- endif
+ else ifeq ($(comp),icc)
+ CXXFLAGS += -msse3 -DUSE_POPCNT
+ else
+ CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT
endif
endif
ifeq ($(avx2),yes)
CXXFLAGS += -DUSE_AVX2
- ifeq ($(comp),$(filter $(comp),gcc clang mingw msys2))
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -mavx2
endif
endif
ifeq ($(avx512),yes)
CXXFLAGS += -DUSE_AVX512
- ifeq ($(comp),$(filter $(comp),gcc clang mingw msys2))
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -mavx512bw
endif
endif
ifeq ($(sse42),yes)
CXXFLAGS += -DUSE_SSE42
- ifeq ($(comp),$(filter $(comp),gcc clang mingw msys2))
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -msse4.2
endif
endif
ifeq ($(sse41),yes)
CXXFLAGS += -DUSE_SSE41
- ifeq ($(comp),$(filter $(comp),gcc clang mingw msys2))
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -msse4.1
endif
endif
ifeq ($(ssse3),yes)
CXXFLAGS += -DUSE_SSSE3
- ifeq ($(comp),$(filter $(comp),gcc clang mingw msys2))
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -mssse3
endif
endif
ifeq ($(sse3),yes)
CXXFLAGS += -DUSE_SSE3
- ifeq ($(comp),$(filter $(comp),gcc clang mingw msys2))
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -msse3
endif
endif
+ifeq ($(neon),yes)
+ CXXFLAGS += -DUSE_NEON
+endif
+
ifeq ($(arch),x86_64)
CXXFLAGS += -DUSE_SSE2
endif
@@ -498,7 +500,7 @@ endif
### 3.7 pext
ifeq ($(pext),yes)
CXXFLAGS += -DUSE_PEXT
- ifeq ($(comp),$(filter $(comp),gcc clang mingw msys2))
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -mbmi2
endif
endif
@@ -544,20 +546,12 @@ help:
@echo ""
@echo "Supported targets:"
@echo ""
- @echo "build > Standard (without NNUE) build"
+ @echo "build > Standard build"
@echo "profile-build > Standard build with PGO"
- @echo "nnue > NNUE-enabled build"
- @echo "profile-nnue > NNUE-enabled build with PGO"
- @echo "nnue-learn > Produces or refines a NNUE parameter set."
- @echo " Requires training data that can be"
- @echo " generated by itself using an existing"
- @echo " parameter set, or with the next tool"
- @echo "nnue-gen-sfen-from-original-eval"
- @echo " > Produces training data for 'nnue-learn'"
- @echo " > without using a NNUE parameter set"
@echo "strip > Strip executable"
@echo "install > Install executable"
@echo "clean > Clean up"
+ @echo "net > Download the default nnue net"
@echo ""
@echo "Supported archs:"
@echo ""
@@ -565,10 +559,11 @@ help:
@echo "x86-64-bmi2 > x86 64-bit with bmi2 support"
@echo "x86-64-avx2 > x86 64-bit with avx2 support"
@echo "x86-64-sse42 > x86 64-bit with sse42 support"
+ @echo "x86-64-modern > x86 64-bit with sse41 support (x86-64-sse41)"
@echo "x86-64-sse41 > x86 64-bit with sse41 support"
@echo "x86-64-ssse3 > x86 64-bit with ssse3 support"
- @echo "x86-64-sse3-popcnt > x86 64-bit with ssse3 and popcnt support"
- @echo "x86-64-sse3 > x86 64-bit with ssse3 support"
+ @echo "x86-64-sse3-popcnt > x86 64-bit with sse3 and popcnt support"
+ @echo "x86-64-sse3 > x86 64-bit with sse3 support"
@echo "x86-64 > x86 64-bit generic"
@echo "x86-32 > x86 32-bit (also enables SSE)"
@echo "x86-32-old > x86 32-bit fall back for old hardware"
@@ -576,6 +571,7 @@ help:
@echo "ppc-32 > PPC 32-bit"
@echo "armv7 > ARMv7 32-bit"
@echo "armv8 > ARMv8 64-bit"
+ @echo "apple-silicon > Apple silicon ARM64"
@echo "general-64 > unspecified 64-bit"
@echo "general-32 > unspecified 32-bit"
@echo ""
@@ -585,21 +581,23 @@ help:
@echo "mingw > Gnu compiler with MinGW under Windows"
@echo "clang > LLVM Clang compiler"
@echo "icc > Intel compiler"
- @echo "msys2 > MSYS2"
@echo ""
@echo "Simple examples. If you don't know what to do, you likely want to run: "
@echo ""
- @echo "make build ARCH=x86-64 (This is for 64-bit systems)"
- @echo "make build ARCH=x86-32 (This is for 32-bit systems)"
+ @echo "make -j build ARCH=x86-64 (This is for 64-bit systems)"
+ @echo "make -j build ARCH=x86-32 (This is for 32-bit systems)"
@echo ""
@echo "Advanced examples, for experienced users: "
@echo ""
- @echo "make build ARCH=x86-64 COMP=clang"
- @echo "make profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-4.8"
+ @echo "make -j build ARCH=x86-64-modern COMP=clang"
+ @echo "make -j profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-4.8"
@echo ""
+ @echo "The selected architecture $(ARCH) enables the following configuration: "
+ @echo ""
+ @$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
-.PHONY: help build profile-build strip install clean objclean profileclean \
+.PHONY: help build profile-build strip install clean net objclean profileclean \
config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \
clang-profile-use clang-profile-make
@@ -633,6 +631,13 @@ install:
clean: objclean profileclean
@rm -f .depend *~ core
+net:
+ $(eval nnuenet := $(shell grep EvalFile ucioption.cpp | grep Option | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/'))
+ @echo "Default net: $(nnuenet)"
+ $(eval nnuedownloadurl := https://tests.stockfishchess.org/api/nn/$(nnuenet))
+ $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -sL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi))
+ @if test -f "$(nnuenet)"; then echo "Already available."; else echo "Downloading $(nnuedownloadurl)"; $(curl_or_wget) $(nnuedownloadurl) > $(nnuenet); fi
+
# clean binaries and objects
objclean:
@rm -f $(EXE) *.o ./syzygy/*.o ./learn/*.o ./extra/*.o ./eval/*.o ./nnue/*.o ./nnue/features/*.o
@@ -672,6 +677,7 @@ config-sanity:
@echo "avx2: '$(avx2)'"
@echo "pext: '$(pext)'"
@echo "avx512: '$(avx512)'"
+ @echo "neon: '$(neon)'"
@echo ""
@echo "Flags:"
@echo "CXX: $(CXX)"
@@ -685,7 +691,7 @@ config-sanity:
@test "$(optimize)" = "yes" || test "$(optimize)" = "no"
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || \
- test "$(arch)" = "armv7" || test "$(arch)" = "armv8-a"
+ test "$(arch)" = "armv7" || test "$(arch)" = "armv8-a" || test "$(arch)" = "arm64"
@test "$(bits)" = "32" || test "$(bits)" = "64"
@test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
@test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
@@ -697,6 +703,7 @@ config-sanity:
@test "$(avx2)" = "yes" || test "$(avx2)" = "no"
@test "$(pext)" = "yes" || test "$(pext)" = "no"
@test "$(avx512)" = "yes" || test "$(avx512)" = "no"
+ @test "$(neon)" = "yes" || test "$(neon)" = "no"
@test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang"
$(EXE): $(OBJS)
@@ -738,20 +745,10 @@ icc-profile-use:
EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \
all
-nnue: config-sanity
- $(MAKE) CXXFLAGS='$(CXXFLAGS) -DEVAL_NNUE -DENABLE_TEST_CMD -fopenmp' LDFLAGS='$(LDFLAGS) -fopenmp' build
-
-profile-nnue: export NNUECXXFLAGS = -DEVAL_NNUE -DENABLE_TEST_CMD
-profile-nnue: config-sanity
- $(MAKE) profile-build
-
-nnue-gen-sfen-from-original-eval: config-sanity
- $(MAKE) CXXFLAGS='$(CXXFLAGS) -DEVAL_LEARN -DUSE_EVAL_HASH -DENABLE_TEST_CMD -fopenmp' LDFLAGS='$(LDFLAGS) -fopenmp' build
-
nnue-learn: config-sanity
$(MAKE) CXXFLAGS='$(CXXFLAGS) -DEVAL_LEARN -DEVAL_NNUE -DUSE_EVAL_HASH -DENABLE_TEST_CMD -DUSE_BLAS -I/mingw64/include/OpenBLAS -fopenmp' LDFLAGS='$(LDFLAGS) -lopenblas -fopenmp' build
.depend:
- -@$(CXX) $(DEPENDFLAGS) -MM $(OBJS:.o=.cpp) > $@ 2> /dev/null
+ -@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null
-include .depend
diff --git a/src/benchmark.cpp b/src/benchmark.cpp
index 3299f373..6041d642 100644
--- a/src/benchmark.cpp
+++ b/src/benchmark.cpp
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
diff --git a/src/bitbase.cpp b/src/bitbase.cpp
index 7e27eb96..bbe8e9a7 100644
--- a/src/bitbase.cpp
+++ b/src/bitbase.cpp
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
diff --git a/src/bitboard.cpp b/src/bitboard.cpp
index 0bf7eef9..f531010c 100644
--- a/src/bitboard.cpp
+++ b/src/bitboard.cpp
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
diff --git a/src/bitboard.h b/src/bitboard.h
index 15ec4153..a899d879 100644
--- a/src/bitboard.h
+++ b/src/bitboard.h
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
@@ -130,12 +128,6 @@ constexpr bool more_than_one(Bitboard b) {
return b & (b - 1);
}
-/// Counts the occupation of the bitboard depending on the occupation of SQ_A1
-/// as in `b & (1ULL << SQ_A1) ? more_than_two(b) : more_than_one(b)`
-
-constexpr bool conditional_more_than_two(Bitboard b) {
- return b & (b - 1) & (b - 2);
-}
constexpr bool opposite_colors(Square s1, Square s2) {
return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1;
diff --git a/src/endgame.cpp b/src/endgame.cpp
index 40f49dce..c8be2198 100644
--- a/src/endgame.cpp
+++ b/src/endgame.cpp
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
@@ -589,8 +587,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const {
Bitboard strongPawns = pos.pieces(strongSide, PAWN);
// If all pawns are ahead of the king on a single rook file, it's a draw.
- if (!((strongPawns & ~FileABB) || (strongPawns & ~FileHBB)) &&
- !(strongPawns & ~passed_pawn_span(weakSide, weakKing)))
+ if ( !(strongPawns & ~(FileABB | FileHBB))
+ && !(strongPawns & ~passed_pawn_span(weakSide, weakKing)))
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
diff --git a/src/endgame.h b/src/endgame.h
index fd1aba2d..1351d88a 100644
--- a/src/endgame.h
+++ b/src/endgame.h
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
diff --git a/src/eval/evaluate_mir_inv_tools.cpp b/src/eval/evaluate_mir_inv_tools.cpp
index 3b5d3a36..3667b9f5 100644
--- a/src/eval/evaluate_mir_inv_tools.cpp
+++ b/src/eval/evaluate_mir_inv_tools.cpp
@@ -7,22 +7,22 @@ namespace Eval
// --- tables
- // Value when a certain BonaPiece is seen from the other side
+ // Value when a certain PieceSquare is seen from the other side
// BONA_PIECE_INIT is -1, so it must be a signed type.
- // Even if KPPT is expanded, BonaPiece will not exceed 2^15 for the time being, so int16_t is good.
- int16_t inv_piece_[Eval::fe_end];
+ // Even if KPPT is expanded, PieceSquare will not exceed 2^15 for the time being, so int16_t is good.
+ int16_t inv_piece_[PieceSquare::PS_END];
- // Returns the one at the position where a BonaPiece on the board is mirrored.
- int16_t mir_piece_[Eval::fe_end];
+ // Returns the one at the position where a PieceSquare on the board is mirrored.
+ int16_t mir_piece_[PieceSquare::PS_END];
// --- methods
-// Returns the value when a certain BonaPiece is seen from the other side
- Eval::BonaPiece inv_piece(Eval::BonaPiece p) { return (Eval::BonaPiece)inv_piece_[p]; }
+// Returns the value when a certain PieceSquare is seen from the other side
+ PieceSquare inv_piece(PieceSquare p) { return (PieceSquare)inv_piece_[p]; }
- // Returns the one at the position where a BonaPiece on the board is mirrored.
- Eval::BonaPiece mir_piece(Eval::BonaPiece p) { return (Eval::BonaPiece)mir_piece_[p]; }
+ // Returns the one at the position where a PieceSquare on the board is mirrored.
+ PieceSquare mir_piece(PieceSquare p) { return (PieceSquare)mir_piece_[p]; }
std::function mir_piece_init_function;
@@ -37,23 +37,23 @@ namespace Eval
// exchange f and e
int t[] = {
- f_pawn , e_pawn ,
- f_knight , e_knight ,
- f_bishop , e_bishop ,
- f_rook , e_rook ,
- f_queen , e_queen ,
+ PieceSquare::PS_W_PAWN , PieceSquare::PS_B_PAWN ,
+ PieceSquare::PS_W_KNIGHT , PieceSquare::PS_B_KNIGHT ,
+ PieceSquare::PS_W_BISHOP , PieceSquare::PS_B_BISHOP ,
+ PieceSquare::PS_W_ROOK , PieceSquare::PS_B_ROOK ,
+ PieceSquare::PS_W_QUEEN , PieceSquare::PS_B_QUEEN ,
};
// Insert uninitialized value.
- for (BonaPiece p = BONA_PIECE_ZERO; p < fe_end; ++p)
+ for (PieceSquare p = PieceSquare::PS_NONE; p < PieceSquare::PS_END; ++p)
{
- inv_piece_[p] = BONA_PIECE_NOT_INIT;
+ inv_piece_[p] = PieceSquare::PS_NOT_INIT;
// mirror does not work for hand pieces. Just return the original value.
- mir_piece_[p] = (p < f_pawn) ? p : BONA_PIECE_NOT_INIT;
+ mir_piece_[p] = (p < PieceSquare::PS_W_PAWN) ? p : PieceSquare::PS_NOT_INIT;
}
- for (BonaPiece p = BONA_PIECE_ZERO; p < fe_end; ++p)
+ for (PieceSquare p = PieceSquare::PS_NONE; p < PieceSquare::PS_END; ++p)
{
for (int i = 0; i < 32 /* t.size() */; i += 2)
{
@@ -62,13 +62,13 @@ namespace Eval
Square sq = (Square)(p - t[i]);
// found!!
- BonaPiece q = (p < fe_hand_end) ? BonaPiece(sq + t[i + 1]) : (BonaPiece)(Inv(sq) + t[i + 1]);
+ PieceSquare q = (p < PieceSquare::PS_W_PAWN) ? PieceSquare(sq + t[i + 1]) : (PieceSquare)(rotate180(sq) + t[i + 1]);
inv_piece_[p] = q;
inv_piece_[q] = p;
/*
It's a bit tricky, but regarding p
- p >= fe_hand_end
+ p >= PieceSquare::PS_W_PAWN
When.
For this p, let n be an integer (i in the above code can only be an even number),
@@ -76,20 +76,20 @@ namespace Eval
b) When t[2n + 1] <= p s;
+ std::unordered_set s;
vector a = {
f_hand_pawn - 1,e_hand_pawn - 1,
f_hand_lance - 1, e_hand_lance - 1,
@@ -137,7 +137,7 @@ namespace Eval
f_hand_rook - 1, e_hand_rook - 1,
};
for (auto b : a)
- s.insert((BonaPiece)b);
+ s.insert((PieceSquare)b);
// Excludes walks, incense, and katsura on the board that do not appear further (Apery also contains garbage here)
for (Rank r = RANK_1; r <= RANK_2; ++r)
@@ -146,18 +146,18 @@ namespace Eval
if (r == RANK_1)
{
// first step
- BonaPiece b1 = BonaPiece(f_pawn + (f | r));
+ PieceSquare b1 = PieceSquare(PieceSquare::PS_W_PAWN + (f | r));
s.insert(b1);
s.insert(inv_piece[b1]);
// 1st stage incense
- BonaPiece b2 = BonaPiece(f_lance + (f | r));
+ PieceSquare b2 = PieceSquare(f_lance + (f | r));
s.insert(b2);
s.insert(inv_piece[b2]);
}
// Katsura on the 1st and 2nd steps
- BonaPiece b = BonaPiece(f_knight + (f | r));
+ PieceSquare b = PieceSquare(PieceSquare::PS_W_KNIGHT + (f | r));
s.insert(b);
s.insert(inv_piece[b]);
}
@@ -166,8 +166,8 @@ namespace Eval
for (auto sq : SQ)
{
cout << sq << ' ';
- for (BonaPiece p1 = BONA_PIECE_ZERO; p1 < fe_end; ++p1)
- for (BonaPiece p2 = BONA_PIECE_ZERO; p2 < fe_end; ++p2)
+ for (PieceSquare p1 = PieceSquare::PS_NONE; p1 < PieceSquare::PS_END; ++p1)
+ for (PieceSquare p2 = PieceSquare::PS_NONE; p2 < PieceSquare::PS_END; ++p2)
if (!s.count(p1) && !s.count(p2))
kpp_write(sq, p1, p2, kpp[sq][p1][p2]);
}
@@ -177,7 +177,7 @@ namespace Eval
{
cout << sq1 << ' ';
for (auto sq2 : SQ)
- for (BonaPiece p1 = BONA_PIECE_ZERO; p1 < fe_end; ++p1)
+ for (PieceSquare p1 = PieceSquare::PS_NONE; p1 < PieceSquare::PS_END; ++p1)
if (!s.count(p1))
kkp_write(sq1, sq2, p1, kkp[sq1][sq2][p1]);
}
diff --git a/src/eval/evaluate_mir_inv_tools.h b/src/eval/evaluate_mir_inv_tools.h
index 826164bf..1f193b17 100644
--- a/src/eval/evaluate_mir_inv_tools.h
+++ b/src/eval/evaluate_mir_inv_tools.h
@@ -3,7 +3,7 @@
#if defined(EVAL_NNUE) || defined(EVAL_LEARN)
-// BonaPiece's mirror (horizontal flip) and inverse (180° on the board) tools to get pieces.
+// PieceSquare's mirror (horizontal flip) and inverse (180° on the board) tools to get pieces.
#include "../types.h"
#include "../evaluate.h"
@@ -15,18 +15,18 @@ namespace Eval
// tables
// -------------------------------------------------
- // --- Provide Mirror and Inverse to BonaPiece.
+ // --- Provide Mirror and Inverse to PieceSquare.
// These arrays are initialized by calling init() or init_mir_inv_tables();.
// If you want to use only this table from the evaluation function,
// Call init_mir_inv_tables().
// These arrays are referenced from the KK/KKP/KPP classes below.
- // Returns the value when a certain BonaPiece is seen from the other side
- extern Eval::BonaPiece inv_piece(Eval::BonaPiece p);
+ // Returns the value when a certain PieceSquare is seen from the other side
+ extern PieceSquare inv_piece(PieceSquare p);
- // Returns the one at the position where a BonaPiece on the board is mirrored.
- extern Eval::BonaPiece mir_piece(Eval::BonaPiece p);
+ // Returns the one at the position where a PieceSquare on the board is mirrored.
+ extern PieceSquare mir_piece(PieceSquare p);
// callback called when initializing mir_piece/inv_piece
@@ -35,8 +35,8 @@ namespace Eval
// At the timing when mir_piece_init_function is called, until fe_old_end
// It is guaranteed that these tables have been initialized.
extern std::function mir_piece_init_function;
- extern int16_t mir_piece_[Eval::fe_end];
- extern int16_t inv_piece_[Eval::fe_end];
+ extern int16_t mir_piece_[PieceSquare::PS_END];
+ extern int16_t inv_piece_[PieceSquare::PS_END];
// The table above will be initialized when you call this function explicitly or call init().
extern void init_mir_inv_tables();
diff --git a/src/evaluate.cpp b/src/evaluate.cpp
index 384c081d..d8daa55f 100644
--- a/src/evaluate.cpp
+++ b/src/evaluate.cpp
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
@@ -20,17 +18,51 @@
#include
#include
+#include
#include // For std::memset
#include
-#include
#include
+#include
+#include
#include "bitboard.h"
#include "evaluate.h"
#include "material.h"
#include "pawns.h"
#include "thread.h"
-#include "nnue/evaluate_nnue.h"
+#include "uci.h"
+
+namespace Eval {
+
+ bool useNNUE;
+ std::string eval_file_loaded="None";
+
+ void init_NNUE() {
+
+ useNNUE = Options["Use NNUE"];
+ std::string eval_file = std::string(Options["EvalFile"]);
+ if (useNNUE && eval_file_loaded != eval_file)
+ if (Eval::NNUE::load_eval_file(eval_file))
+ eval_file_loaded = eval_file;
+ }
+
+ void verify_NNUE() {
+
+ std::string eval_file = std::string(Options["EvalFile"]);
+ if (useNNUE && eval_file_loaded != eval_file)
+ {
+ std::cerr << "Use of NNUE evaluation, but the file " << eval_file << " was not loaded successfully. "
+ << "These network evaluation parameters must be available, compatible with this version of the code. "
+ << "The UCI option EvalFile might need to specify the full path, including the directory/folder name, to the file." << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+
+ if (useNNUE)
+ sync_cout << "info string NNUE evaluation using " << eval_file << " enabled." << sync_endl;
+ else
+ sync_cout << "info string classical evaluation enabled." << sync_endl;
+ }
+}
namespace Trace {
@@ -76,8 +108,10 @@ using namespace Trace;
namespace {
// Threshold for lazy and space evaluation
- constexpr Value LazyThreshold = Value(1400);
+ constexpr Value LazyThreshold1 = Value(1400);
+ constexpr Value LazyThreshold2 = Value(1300);
constexpr Value SpaceThreshold = Value(12222);
+ constexpr Value NNUEThreshold = Value(500);
// KingAttackWeights[PieceType] contains king attack weights by piece type
constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
@@ -148,7 +182,6 @@ namespace {
constexpr Score MinorBehindPawn = S( 18, 3);
constexpr Score PassedFile = S( 11, 8);
constexpr Score PawnlessFlank = S( 17, 95);
- constexpr Score QueenInfiltration = S( -2, 14);
constexpr Score ReachableOutpost = S( 31, 22);
constexpr Score RestrictedPiece = S( 7, 7);
constexpr Score RookOnKingRing = S( 16, 0);
@@ -311,13 +344,15 @@ namespace {
if (Pt == BISHOP || Pt == KNIGHT)
{
- // Bonus if piece is on an outpost square or can reach one
+ // Bonus if the piece is on an outpost square or can reach one
+ // Reduced bonus for knights (BadOutpost) if few relevant targets
bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them);
+ Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN);
+
if ( Pt == KNIGHT
- && bb & s & ~CenterFiles
- && !(b & pos.pieces(Them) & ~pos.pieces(PAWN))
- && !conditional_more_than_two(
- pos.pieces(Them) & ~pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide)))
+ && bb & s & ~CenterFiles // on a side outpost
+ && !(b & targets) // no relevant attacks
+ && (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide))))
score += BadOutpost;
else if (bb & s)
score += Outpost[Pt == BISHOP];
@@ -388,10 +423,6 @@ namespace {
Bitboard queenPinners;
if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners))
score -= WeakQueen;
-
- // Bonus for queen on weak square in enemy camp
- if (relative_rank(Us, s) > RANK_4 && (~pe->pawn_attacks_span(Them) & s))
- score += QueenInfiltration;
}
}
if (T)
@@ -578,17 +609,21 @@ namespace {
// Bonus for threats on the next moves against enemy queen
if (pos.count(Them) == 1)
{
+ bool queenImbalance = pos.count() == 1;
+
Square s = pos.square(Them);
- safe = mobilityArea[Us] & ~stronglyProtected;
+ safe = mobilityArea[Us]
+ & ~pos.pieces(Us, PAWN)
+ & ~stronglyProtected;
b = attackedBy[Us][KNIGHT] & attacks_bb(s);
- score += KnightOnQueen * popcount(b & safe);
+ score += KnightOnQueen * popcount(b & safe) * (1 + queenImbalance);
b = (attackedBy[Us][BISHOP] & attacks_bb(s, pos.pieces()))
| (attackedBy[Us][ROOK ] & attacks_bb(s, pos.pieces()));
- score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]);
+ score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance);
}
if (T)
@@ -788,7 +823,7 @@ namespace {
&& pos.non_pawn_material(BLACK) == RookValueMg
&& pos.count(strongSide) - pos.count(~strongSide) <= 1
&& bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN))
- && (attackedBy[~strongSide][KING] & pos.pieces(~strongSide, PAWN)))
+ && (attacks_bb(pos.square(~strongSide)) & pos.pieces(~strongSide, PAWN)))
sf = 36;
else if (pos.count() == 1)
sf = 37 + 3 * (pos.count(WHITE) == 1 ? pos.count(BLACK) + pos.count(BLACK)
@@ -839,9 +874,12 @@ namespace {
score += pe->pawn_score(WHITE) - pe->pawn_score(BLACK);
// Early exit if score is high
- Value v = (mg_value(score) + eg_value(score)) / 2;
- if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64)
- return pos.side_to_move() == WHITE ? v : -v;
+ auto lazy_skip = [&](Value lazyThreshold) {
+ return abs(mg_value(score) + eg_value(score)) / 2 > lazyThreshold + pos.non_pawn_material() / 64;
+ };
+
+ if (lazy_skip(LazyThreshold1))
+ goto make_v;
// Main evaluation begins here
initialize();
@@ -858,12 +896,17 @@ namespace {
// More complex interactions that require fully populated attack bitboards
score += king< WHITE>() - king< BLACK>()
- + threats() - threats()
- + passed< WHITE>() - passed< BLACK>()
+ + passed< WHITE>() - passed< BLACK>();
+
+ if (lazy_skip(LazyThreshold2))
+ goto make_v;
+
+ score += threats() - threats()
+ space< WHITE>() - space< BLACK>();
+make_v:
// Derive single value from mg and eg parts of score
- v = winnable(score);
+ Value v = winnable(score);
// In case of tracing add all remaining individual evaluation terms
if (T)
@@ -892,187 +935,169 @@ namespace {
/// evaluate() is the evaluator for the outer world. It returns a static
/// evaluation of the position from the point of view of the side to move.
-#if !defined(EVAL_NNUE)
Value Eval::evaluate(const Position& pos) {
+
+ if (Eval::useNNUE)
+ {
+ Value balance = pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK);
+ balance += 200 * (pos.count(WHITE) - pos.count(BLACK));
+ // Take NNUE eval only on balanced positions
+ if (abs(balance) < NNUEThreshold)
+ return NNUE::evaluate(pos) + Tempo;
+ }
return Evaluation(pos).value();
}
-#endif // defined(EVAL_NNUE)
-
/// trace() is like evaluate(), but instead of returning a value, it returns
/// a string (suitable for outputting to stdout) that contains the detailed
/// descriptions and values of each evaluation term. Useful for debugging.
+/// Trace scores are from white's point of view
std::string Eval::trace(const Position& pos) {
if (pos.checkers())
- return "Total evaluation: none (in check)";
-
- std::memset(scores, 0, sizeof(scores));
-
- pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt
-
- Value v = Evaluation(pos).value();
-
- v = pos.side_to_move() == WHITE ? v : -v; // Trace scores are from white's point of view
+ return "Final evaluation: none (in check)";
std::stringstream ss;
- ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
- << " Term | White | Black | Total \n"
- << " | MG EG | MG EG | MG EG \n"
- << " ------------+-------------+-------------+------------\n"
- << " Material | " << Term(MATERIAL)
- << " Imbalance | " << Term(IMBALANCE)
- << " Pawns | " << Term(PAWN)
- << " Knights | " << Term(KNIGHT)
- << " Bishops | " << Term(BISHOP)
- << " Rooks | " << Term(ROOK)
- << " Queens | " << Term(QUEEN)
- << " Mobility | " << Term(MOBILITY)
- << " King safety | " << Term(KING)
- << " Threats | " << Term(THREAT)
- << " Passed | " << Term(PASSED)
- << " Space | " << Term(SPACE)
- << " Winnable | " << Term(WINNABLE)
- << " ------------+-------------+-------------+------------\n"
- << " Total | " << Term(TOTAL);
+ ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2);
+
+ Value v;
+
+ if (Eval::useNNUE)
+ {
+ v = NNUE::evaluate(pos);
+ }
+ else
+ {
+ std::memset(scores, 0, sizeof(scores));
+
+ pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt
+
+ v = Evaluation(pos).value();
+
+ ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
+ << " Term | White | Black | Total \n"
+ << " | MG EG | MG EG | MG EG \n"
+ << " ------------+-------------+-------------+------------\n"
+ << " Material | " << Term(MATERIAL)
+ << " Imbalance | " << Term(IMBALANCE)
+ << " Pawns | " << Term(PAWN)
+ << " Knights | " << Term(KNIGHT)
+ << " Bishops | " << Term(BISHOP)
+ << " Rooks | " << Term(ROOK)
+ << " Queens | " << Term(QUEEN)
+ << " Mobility | " << Term(MOBILITY)
+ << " King safety | " << Term(KING)
+ << " Threats | " << Term(THREAT)
+ << " Passed | " << Term(PASSED)
+ << " Space | " << Term(SPACE)
+ << " Winnable | " << Term(WINNABLE)
+ << " ------------+-------------+-------------+------------\n"
+ << " Total | " << Term(TOTAL);
+ }
+
+ v = pos.side_to_move() == WHITE ? v : -v;
ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n";
return ss.str();
}
-#if defined(EVAL_NNUE) || defined(EVAL_LEARN)
-namespace Eval {
-ExtBonaPiece kpp_board_index[PIECE_NB] = {
- { BONA_PIECE_ZERO, BONA_PIECE_ZERO },
- { f_pawn, e_pawn },
- { f_knight, e_knight },
- { f_bishop, e_bishop },
- { f_rook, e_rook },
- { f_queen, e_queen },
- { f_king, e_king },
- { BONA_PIECE_ZERO, BONA_PIECE_ZERO },
-
- // When viewed from behind. f and e are exchanged.
- { BONA_PIECE_ZERO, BONA_PIECE_ZERO },
- { e_pawn, f_pawn },
- { e_knight, f_knight },
- { e_bishop, f_bishop },
- { e_rook, f_rook },
- { e_queen, f_queen },
- { e_king, f_king },
- { BONA_PIECE_ZERO, BONA_PIECE_ZERO }, // no money
-};
-
-// Check whether the pieceListFw[] held internally is a correct BonaPiece.
+// Check whether the pieceListFw[] held internally is a correct PieceSquare.
// Note: For debugging. slow.
bool EvalList::is_valid(const Position& pos)
{
- std::set piece_numbers;
- for (Square sq = SQ_A1; sq != SQUARE_NB; ++sq) {
- auto piece_number = piece_no_of_board(sq);
- if (piece_number == PIECE_NUMBER_NB) {
- continue;
- }
- assert(!piece_numbers.count(piece_number));
- piece_numbers.insert(piece_number);
- }
-
- for (int i = 0; i < length(); ++i)
- {
- BonaPiece fw = pieceListFw[i];
- // Go to the Position class to see if this fw really exists.
-
- if (fw == Eval::BONA_PIECE_ZERO) {
- continue;
+ std::set piece_numbers;
+ for (Square sq = SQ_A1; sq != SQUARE_NB; ++sq) {
+ auto piece_number = piece_id_list[sq];
+ if (piece_number == PieceId::PIECE_ID_NONE) {
+ continue;
+ }
+ assert(!piece_numbers.count(piece_number));
+ piece_numbers.insert(piece_number);
}
- // Out of range
- if (!(0 <= fw && fw < fe_end))
- return false;
-
- // Since it is a piece on the board, I will check if this piece really exists.
- for (Piece pc = NO_PIECE; pc < PIECE_NB; ++pc)
+ for (int i = 0; i < PieceId::PIECE_ID_KING; ++i)
{
- auto pt = type_of(pc);
- if (pt == NO_PIECE_TYPE || pt == 7) // non-existing piece
- continue;
+ PieceSquare fw = pieceListFw[i];
+ // Go to the Position class to see if this fw really exists.
- // BonaPiece start number of piece pc
- auto s = BonaPiece(kpp_board_index[pc].fw);
- if (s <= fw && fw < s + SQUARE_NB)
- {
- // Since it was found, check if this piece is at sq.
- Square sq = (Square)(fw - s);
- Piece pc2 = pos.piece_on(sq);
+ if (fw == PieceSquare::PS_NONE) {
+ continue;
+ }
- if (pc2 != pc)
- return false;
+ // Out of range
+ if (!(0 <= fw && fw < PieceSquare::PS_END))
+ return false;
- goto Found;
- }
- }
- // It was a piece that did not exist for some reason..
- return false;
- Found:;
- }
+ // Since it is a piece on the board, I will check if this piece really exists.
+ for (Piece pc = NO_PIECE; pc < PIECE_NB; ++pc)
+ {
+ auto pt = type_of(pc);
+ if (pt == NO_PIECE_TYPE || pt == 7) // non-existing piece
+ continue;
- // Validate piece_no_list_board
- for (auto sq = SQUARE_ZERO; sq < SQUARE_NB; ++sq) {
- Piece expected_piece = pos.piece_on(sq);
- PieceNumber piece_number = piece_no_list_board[sq];
- if (piece_number == PIECE_NUMBER_NB) {
- assert(expected_piece == NO_PIECE);
- if (expected_piece != NO_PIECE) {
+ // PieceSquare start number of piece pc
+ auto s = PieceSquare(kpp_board_index[pc].from[Color::WHITE]);
+ if (s <= fw && fw < s + SQUARE_NB)
+ {
+ // Since it was found, check if this piece is at sq.
+ Square sq = (Square)(fw - s);
+ Piece pc2 = pos.piece_on(sq);
+
+ if (pc2 != pc)
+ return false;
+
+ goto Found;
+ }
+ }
+ // It was a piece that did not exist for some reason..
return false;
- }
- continue;
+ Found:;
}
- BonaPiece bona_piece_white = pieceListFw[piece_number];
- Piece actual_piece;
- for (actual_piece = NO_PIECE; actual_piece < PIECE_NB; ++actual_piece) {
- if (kpp_board_index[actual_piece].fw == BONA_PIECE_ZERO) {
- continue;
- }
+ // Validate piece_id_list
+ for (auto sq = SQUARE_ZERO; sq < SQUARE_NB; ++sq) {
+ Piece expected_piece = pos.piece_on(sq);
+ PieceId piece_number = piece_id_list[sq];
+ if (piece_number == PieceId::PIECE_ID_NONE) {
+ assert(expected_piece == NO_PIECE);
+ if (expected_piece != NO_PIECE) {
+ return false;
+ }
+ continue;
+ }
- if (kpp_board_index[actual_piece].fw <= bona_piece_white
- && bona_piece_white < kpp_board_index[actual_piece].fw + SQUARE_NB) {
- break;
- }
+ PieceSquare bona_piece_white = pieceListFw[piece_number];
+ Piece actual_piece;
+ for (actual_piece = NO_PIECE; actual_piece < PIECE_NB; ++actual_piece) {
+ if (kpp_board_index[actual_piece].from[Color::WHITE] == PieceSquare::PS_NONE) {
+ continue;
+ }
+
+ if (kpp_board_index[actual_piece].from[Color::WHITE] <= bona_piece_white
+ && bona_piece_white < kpp_board_index[actual_piece].from[Color::WHITE] + SQUARE_NB) {
+ break;
+ }
+ }
+
+ assert(actual_piece != PIECE_NB);
+ if (actual_piece == PIECE_NB) {
+ return false;
+ }
+
+ assert(actual_piece == expected_piece);
+ if (actual_piece != expected_piece) {
+ return false;
+ }
+
+ Square actual_square = static_cast(
+ bona_piece_white - kpp_board_index[actual_piece].from[Color::WHITE]);
+ assert(sq == actual_square);
+ if (sq != actual_square) {
+ return false;
+ }
}
- assert(actual_piece != PIECE_NB);
- if (actual_piece == PIECE_NB) {
- return false;
- }
-
- assert(actual_piece == expected_piece);
- if (actual_piece != expected_piece) {
- return false;
- }
-
- Square actual_square = static_cast(
- bona_piece_white - kpp_board_index[actual_piece].fw);
- assert(sq == actual_square);
- if (sq != actual_square) {
- return false;
- }
- }
-
- return true;
+ return true;
}
-}
-#endif // defined(EVAL_NNUE) || defined(EVAL_LEARN)
-
-#if !defined(EVAL_NNUE)
-namespace Eval {
-void evaluate_with_no_return(const Position& pos) {}
-void update_weights(uint64_t epoch, const std::array & freeze) {}
-void init_grad(double eta1, uint64_t eta_epoch, double eta2, uint64_t eta2_epoch, double eta3) {}
-void add_grad(Position& pos, Color rootColor, double delt_grad, const std::array & freeze) {}
-void save_eval(std::string suffix) {}
-double get_eta() { return 0.0; }
-}
-#endif // defined(EVAL_NNUE)
diff --git a/src/evaluate.h b/src/evaluate.h
index 0301f455..e808068d 100644
--- a/src/evaluate.h
+++ b/src/evaluate.h
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
@@ -29,194 +27,23 @@ class Position;
namespace Eval {
-std::string trace(const Position& pos);
+ std::string trace(const Position& pos);
+ Value evaluate(const Position& pos);
-Value evaluate(const Position& pos);
+ extern bool useNNUE;
+ extern std::string eval_file_loaded;
+ void init_NNUE();
+ void verify_NNUE();
-void evaluate_with_no_return(const Position& pos);
+ namespace NNUE {
-Value compute_eval(const Position& pos);
+ Value evaluate(const Position& pos);
+ Value compute_eval(const Position& pos);
+ void update_eval(const Position& pos);
+ bool load_eval_file(const std::string& evalFile);
-#if defined(EVAL_NNUE) || defined(EVAL_LEARN)
-// Read the evaluation function file.
-// This is only called once in response to the "is_ready" command. It is not supposed to be called twice.
-// (However, if isready is sent again after EvalDir (evaluation function folder) has been changed, read it again.)
-void load_eval();
+ } // namespace NNUE
-static uint64_t calc_check_sum() {return 0;}
-
-static void print_softname(uint64_t check_sum) {}
-
-// --- enum corresponding to P of constant KPP (ball and arbitrary 2 pieces) used in evaluation function
-
-// (BonaPiece wants to define freely in experiment of evaluation function, so I don't define it here.)
-
-
-// A type that represents P(Piece) when calling KKP/KPP in Bonanza.
-// When you ask for KPP, you need a unique number for each box ~ piece type, like the step at 39 points.
-enum BonaPiece : int32_t
-{
- // Meaning of f = friend (first move). Meaning of e = enemy (rear)
-
- // Value when uninitialized
- BONA_PIECE_NOT_INIT = -1,
-
- // Invalid piece. When you drop a piece, move unnecessary pieces here.
- BONA_PIECE_ZERO = 0,
-
- fe_hand_end = BONA_PIECE_ZERO + 1,
-
- // Don't pack the numbers of unrealistic walks and incense on the board like Bonanza.
- // Reason 1) When learning, there are times when the incense is on the first stage in relative PP, and it is difficult to display it correctly in the inverse transformation.
- // Reason 2) It is difficult to convert from Square with vertical Bitboard.
-
- // --- Pieces on the board
- f_pawn = fe_hand_end,
- e_pawn = f_pawn + SQUARE_NB,
- f_knight = e_pawn + SQUARE_NB,
- e_knight = f_knight + SQUARE_NB,
- f_bishop = e_knight + SQUARE_NB,
- e_bishop = f_bishop + SQUARE_NB,
- f_rook = e_bishop + SQUARE_NB,
- e_rook = f_rook + SQUARE_NB,
- f_queen = e_rook + SQUARE_NB,
- e_queen = f_queen + SQUARE_NB,
- fe_end = e_queen + SQUARE_NB,
- f_king = fe_end,
- e_king = f_king + SQUARE_NB,
- fe_end2 = e_king + SQUARE_NB, // Last number including balls.
-};
-
-#define ENABLE_INCR_OPERATORS_ON(T) \
-inline T& operator++(T& d) { return d = T(int(d) + 1); } \
-inline T& operator--(T& d) { return d = T(int(d) - 1); }
-
-ENABLE_INCR_OPERATORS_ON(BonaPiece)
-
-#undef ENABLE_INCR_OPERATORS_ON
-
-// The number when you look at BonaPiece from the back (the number of steps from the previous 39 to the number 71 from the back)
-// Let's call the paired one the ExtBonaPiece type.
-union ExtBonaPiece
-{
- struct {
- BonaPiece fw; // from white
- BonaPiece fb; // from black
- };
- BonaPiece from[2];
-
- ExtBonaPiece() {}
- ExtBonaPiece(BonaPiece fw_, BonaPiece fb_) : fw(fw_), fb(fb_) {}
-};
-
-// Information about where the piece has moved from where to by this move.
-// Assume the piece is an ExtBonaPiece expression.
-struct ChangedBonaPiece
-{
- ExtBonaPiece old_piece;
- ExtBonaPiece new_piece;
-};
-
-// An array for finding the BonaPiece corresponding to the piece pc on the board of the KPP table.
-// example)
-// BonaPiece fb = kpp_board_index[pc].fb + sq; // BonaPiece corresponding to pc in sq seen from the front
-// BonaPiece fw = kpp_board_index[pc].fw + sq; // BonaPiece corresponding to pc in sq seen from behind
-extern ExtBonaPiece kpp_board_index[PIECE_NB];
-
-// List of pieces used in the evaluation function. A structure holding which piece (PieceNumber) is where (BonaPiece)
-struct EvalList
-{
- // List of frame numbers used in evaluation function (FV38 type)
- BonaPiece* piece_list_fw() const { return const_cast(pieceListFw); }
- BonaPiece* piece_list_fb() const { return const_cast(pieceListFb); }
-
- // Convert the specified piece_no piece to ExtBonaPiece type and return it.
- ExtBonaPiece bona_piece(PieceNumber piece_no) const
- {
- ExtBonaPiece bp;
- bp.fw = pieceListFw[piece_no];
- bp.fb = pieceListFb[piece_no];
- return bp;
- }
-
- // Place the piece_no pc piece in the sq box on the board
- void put_piece(PieceNumber piece_no, Square sq, Piece pc) {
- set_piece_on_board(piece_no, BonaPiece(kpp_board_index[pc].fw + sq), BonaPiece(kpp_board_index[pc].fb + Inv(sq)), sq);
- }
-
- // Returns the PieceNumber corresponding to a box on the board.
- PieceNumber piece_no_of_board(Square sq) const { return piece_no_list_board[sq]; }
-
- // Initialize the pieceList.
- // Set the value of unused pieces to BONA_PIECE_ZERO in case you want to deal with dropped pieces.
- // A normal evaluation function can be used as an evaluation function for missing frames.
- // piece_no_list is initialized with PIECE_NUMBER_NB to facilitate debugging.
- void clear()
- {
-
- for (auto& p: pieceListFw)
- p = BONA_PIECE_ZERO;
-
- for (auto& p: pieceListFb)
- p = BONA_PIECE_ZERO;
-
- for (auto& v :piece_no_list_board)
- v = PIECE_NUMBER_NB;
- }
-
- // Check whether the pieceListFw[] held internally is a correct BonaPiece.
- // Note: For debugging. slow.
- bool is_valid(const Position& pos);
-
- // Set that the BonaPiece of the piece_no piece on the board sq is fb,fw.
- inline void set_piece_on_board(PieceNumber piece_no, BonaPiece fw, BonaPiece fb, Square sq)
- {
- assert(is_ok(piece_no));
- pieceListFw[piece_no] = fw;
- pieceListFb[piece_no] = fb;
- piece_no_list_board[sq] = piece_no;
- }
-
- // Piece list. Piece Number Shows how many pieces are in place (Bona Piece). Used in FV38 etc.
-
- // Length of piece list
- // 38 fixed
-public:
- int length() const { return PIECE_NUMBER_KING; }
-
- // Must be a multiple of 4 to use VPGATHERDD.
- // In addition, the KPPT type evaluation function, etc. is based on the assumption that the 39th and 40th elements are zero.
- // Please note that there is a part that is accessed.
- static const int MAX_LENGTH = 32;
-
- // An array that holds the piece number (PieceNumber) for the pieces on the board
- // Hold up to +1 for when the ball is moving to SQUARE_NB,
- // SQUARE_NB balls are not moved, so this value should never be used.
- PieceNumber piece_no_list_board[SQUARE_NB_PLUS1];
-private:
-
- BonaPiece pieceListFw[MAX_LENGTH];
- BonaPiece pieceListFb[MAX_LENGTH];
-};
-
-// For management of evaluation value difference calculation
-// A structure for managing the number of pieces that have moved from the previous stage
-// Up to 2 moving pieces.
-struct DirtyPiece
-{
- // What changed from the piece with that piece number
- Eval::ChangedBonaPiece changed_piece[2];
-
- // The number of dirty pieces
- PieceNumber pieceNo[2];
-
- // The number of dirty files.
- // It can be 0 for null move.
- // Up to 2 moving pieces and taken pieces.
- int dirty_num;
-
-};
-#endif // defined(EVAL_NNUE) || defined(EVAL_LEARN)
-}
+} // namespace Eval
#endif // #ifndef EVALUATE_H_INCLUDED
diff --git a/src/extra/sfen_packer.cpp b/src/extra/sfen_packer.cpp
index b3404542..68576c82 100644
--- a/src/extra/sfen_packer.cpp
+++ b/src/extra/sfen_packer.cpp
@@ -281,7 +281,7 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre
// In updating the PieceList, we have to set which piece is where,
// A counter of how much each piece has been used
- PieceNumber next_piece_number = PIECE_NUMBER_ZERO;
+ PieceId next_piece_number = PieceId::PIECE_ID_ZERO;
pieceList[W_KING][0] = SQUARE_NB;
pieceList[B_KING][0] = SQUARE_NB;
@@ -290,7 +290,7 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre
if (mirror)
{
for (auto c : Colors)
- board[Mir((Square)stream.read_n_bit(6))] = make_piece(c, KING);
+ board[flip_file((Square)stream.read_n_bit(6))] = make_piece(c, KING);
}
else
{
@@ -305,7 +305,7 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre
{
auto sq = make_square(f, r);
if (mirror) {
- sq = Mir(sq);
+ sq = flip_file(sq);
}
// it seems there are already balls
@@ -328,9 +328,9 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre
put_piece(Piece(pc), sq);
// update evalList
- PieceNumber piece_no =
- (pc == B_KING) ?PIECE_NUMBER_BKING :// Move ball
- (pc == W_KING) ?PIECE_NUMBER_WKING :// Backing ball
+ PieceId piece_no =
+ (pc == B_KING) ?PieceId::PIECE_ID_BKING :// Move ball
+ (pc == W_KING) ?PieceId::PIECE_ID_WKING :// Backing ball
next_piece_number++; // otherwise
evalList.put_piece(piece_no, sq, pc); // Place the pc piece in the sq box
@@ -372,7 +372,7 @@ int Position::set_from_packed_sfen(const PackedSfen& sfen , StateInfo * si, Thre
if (stream.read_one_bit()) {
Square ep_square = static_cast(stream.read_n_bit(6));
if (mirror) {
- ep_square = Mir(ep_square);
+ ep_square = flip_file(ep_square);
}
st->epSquare = ep_square;
diff --git a/src/learn/learner.cpp b/src/learn/learner.cpp
index aff32ee8..eaddbb8a 100644
--- a/src/learn/learner.cpp
+++ b/src/learn/learner.cpp
@@ -627,7 +627,7 @@ void MultiThinkGenSfen::thread_worker(size_t thread_id)
// If the depth is 8 or more, it seems faster not to calculate this difference.
#if defined(EVAL_NNUE)
if (depth < 8)
- Eval::evaluate_with_no_return(pos);
+ Eval::NNUE::update_eval(pos);
#endif // defined(EVAL_NNUE)
}
@@ -825,7 +825,7 @@ void MultiThinkGenSfen::thread_worker(size_t thread_id)
pos.do_move(m, states[ply]);
// Call node evaluate() for each difference calculation.
- Eval::evaluate_with_no_return(pos);
+ Eval::NNUE::update_eval(pos);
} // for (int ply = 0; ; ++ply)
@@ -1697,7 +1697,7 @@ void LearnerThink::calc_loss(size_t thread_id, uint64_t done)
for (size_t i = 0; i < pv.size(); ++i)
{
pos.do_move(pv[i], states[i]);
- Eval::evaluate_with_no_return(pos);
+ Eval::NNUE::update_eval(pos);
}
shallow_value = (rootColor == pos.side_to_move()) ? Eval::evaluate(pos) : -Eval::evaluate(pos);
for (auto it = pv.rbegin(); it != pv.rend(); ++it)
@@ -2106,7 +2106,7 @@ void LearnerThink::thread_worker(size_t thread_id)
pos.do_move(m, state[ply++]);
// Since the value of evaluate in leaf is used, the difference is updated.
- Eval::evaluate_with_no_return(pos);
+ Eval::NNUE::update_eval(pos);
}
if (illegal_move) {
@@ -2135,9 +2135,6 @@ void LearnerThink::thread_worker(size_t thread_id)
// Write evaluation function file.
bool LearnerThink::save(bool is_final)
{
- // Calculate and output check sum before saving. (To check if it matches the next time)
- std::cout << "Check Sum = "<< std::hex << Eval::calc_check_sum() << std::dec << std::endl;
-
// Each time you save, change the extension part of the file name like "0","1","2",..
// (Because I want to compare the winning rate for each evaluation function parameter later)
@@ -3089,14 +3086,14 @@ void learn(Position&, istringstream& is)
}
if (use_convert_plain)
{
- init_nnue(true);
+ Eval::init_NNUE();
cout << "convert_plain.." << endl;
convert_plain(filenames, output_file_name);
return;
}
if (use_convert_bin)
{
- init_nnue(true);
+ Eval::init_NNUE();
cout << "convert_bin.." << endl;
convert_bin(filenames,output_file_name, ply_minimum, ply_maximum, interpolate_eval);
return;
@@ -3104,7 +3101,7 @@ void learn(Position&, istringstream& is)
}
if (use_convert_bin_from_pgn_extract)
{
- init_nnue(true);
+ Eval::init_NNUE();
cout << "convert_bin_from_pgn-extract.." << endl;
convert_bin_from_pgn_extract(filenames, output_file_name, pgn_eval_side_to_move);
return;
@@ -3170,7 +3167,7 @@ void learn(Position&, istringstream& is)
cout << "init.." << endl;
// Read evaluation function parameters
- init_nnue(true);
+ Eval::init_NNUE();
#if !defined(EVAL_NNUE)
cout << "init_grad.." << endl;
diff --git a/src/learn/learning_tools.cpp b/src/learn/learning_tools.cpp
index 4bcecab8..c97b4910 100644
--- a/src/learn/learning_tools.cpp
+++ b/src/learn/learning_tools.cpp
@@ -28,17 +28,17 @@ namespace EvalLearningTools
void init_min_index_flag()
{
// Initialization of mir_piece and inv_piece must be completed.
- assert(mir_piece(Eval::f_pawn) == Eval::e_pawn);
+ assert(Eval::mir_piece(PieceSquare::PS_W_PAWN) == PieceSquare::PS_B_PAWN);
// Initialize the flag array for dimension reduction
// Not involved in KPPP.
KK g_kk;
- g_kk.set(SQUARE_NB, Eval::fe_end, 0);
+ g_kk.set(SQUARE_NB, PieceSquare::PS_END, 0);
KKP g_kkp;
- g_kkp.set(SQUARE_NB, Eval::fe_end, g_kk.max_index());
+ g_kkp.set(SQUARE_NB, PieceSquare::PS_END, g_kk.max_index());
KPP g_kpp;
- g_kpp.set(SQUARE_NB, Eval::fe_end, g_kkp.max_index());
+ g_kpp.set(SQUARE_NB, PieceSquare::PS_END, g_kkp.max_index());
uint64_t size = g_kpp.max_index();
min_index_flag.resize(size);
@@ -123,22 +123,22 @@ namespace EvalLearningTools
// Determine if it is correct.
KK g_kk;
- g_kk.set(SQUARE_NB, Eval::fe_end, 0);
+ g_kk.set(SQUARE_NB, PieceSquare::PS_END, 0);
KKP g_kkp;
- g_kkp.set(SQUARE_NB, Eval::fe_end, g_kk.max_index());
+ g_kkp.set(SQUARE_NB, PieceSquare::PS_END, g_kk.max_index());
KPP g_kpp;
- g_kpp.set(SQUARE_NB, Eval::fe_end, g_kkp.max_index());
+ g_kpp.set(SQUARE_NB, PieceSquare::PS_END, g_kkp.max_index());
std::vector f;
f.resize(g_kpp.max_index() - g_kpp.min_index());
for(auto k = SQUARE_ZERO ; k < SQUARE_NB ; ++k)
- for(auto p0 = BonaPiece::BONA_PIECE_ZERO; p0 < fe_end ; ++p0)
- for (auto p1 = BonaPiece::BONA_PIECE_ZERO; p1 < fe_end; ++p1)
+ for(auto p0 = PieceSquare::PS_NONE; p0 < PieceSquare::PS_END ; ++p0)
+ for (auto p1 = PieceSquare::PS_NONE; p1 < PieceSquare::PS_END; ++p1)
{
KPP kpp_org = g_kpp.fromKPP(k,p0,p1);
KPP kpp0;
- KPP kpp1 = g_kpp.fromKPP(Mir(k), mir_piece(p0), mir_piece(p1));
+ KPP kpp1 = g_kpp.fromKPP(flip_file(k), mir_piece(p0), mir_piece(p1));
KPP kpp_array[2];
auto index = kpp_org.toIndex();
@@ -172,7 +172,7 @@ namespace EvalLearningTools
// Test for missing KPPP calculations
KPPP g_kppp;
- g_kppp.set(15, Eval::fe_end,0);
+ g_kppp.set(15, PieceSquare::PS_END,0);
uint64_t min_index = g_kppp.min_index();
uint64_t max_index = g_kppp.max_index();
@@ -214,7 +214,7 @@ namespace EvalLearningTools
for (int i = 0; i<10000; ++i) // As a test, assuming a large fe_end, try turning at 10000.
for (int j = 0; j < i; ++j)
{
- auto kkpp = g_kkpp.fromKKPP(k, (BonaPiece)i, (BonaPiece)j);
+ auto kkpp = g_kkpp.fromKKPP(k, (PieceSquare)i, (PieceSquare)j);
auto r = kkpp.toRawIndex();
assert(n++ == r);
auto kkpp2 = g_kkpp.fromIndex(r + g_kkpp.min_index());
diff --git a/src/learn/learning_tools.h b/src/learn/learning_tools.h
index a1de03dd..becd8db4 100644
--- a/src/learn/learning_tools.h
+++ b/src/learn/learning_tools.h
@@ -281,7 +281,7 @@ namespace EvalLearningTools
// The number of balls to support (normally SQUARE_NB)
int max_king_sq_;
- // Maximum BonaPiece value supported
+ // Maximum PieceSquare value supported
uint64_t fe_end_;
};
@@ -341,10 +341,10 @@ namespace EvalLearningTools
void toLowerDimensions(/*out*/KK kk_[KK_LOWER_COUNT]) const {
kk_[0] = fromKK(king0_, king1_,false);
#if defined(USE_KK_MIRROR_WRITE)
- kk_[1] = fromKK(Mir(king0_),Mir(king1_),false);
+ kk_[1] = fromKK(flip_file(king0_),flip_file(king1_),false);
#if defined(USE_KK_INVERSE_WRITE)
- kk_[2] = fromKK(Inv(king1_), Inv(king0_),true);
- kk_[3] = fromKK(Inv(Mir(king1_)) , Inv(Mir(king0_)),true);
+ kk_[2] = fromKK(rotate180(king1_), rotate180(king0_),true);
+ kk_[3] = fromKK(rotate180(flip_file(king1_)) , rotate180(flip_file(king0_)),true);
#endif
#endif
}
@@ -386,8 +386,8 @@ namespace EvalLearningTools
struct KKP : public SerializerBase
{
protected:
- KKP(Square king0, Square king1, Eval::BonaPiece p) : king0_(king0), king1_(king1), piece_(p), inverse_sign(false) {}
- KKP(Square king0, Square king1, Eval::BonaPiece p, bool inverse) : king0_(king0), king1_(king1), piece_(p),inverse_sign(inverse) {}
+ KKP(Square king0, Square king1, PieceSquare p) : king0_(king0), king1_(king1), piece_(p), inverse_sign(false) {}
+ KKP(Square king0, Square king1, PieceSquare p, bool inverse) : king0_(king0), king1_(king1), piece_(p),inverse_sign(inverse) {}
public:
KKP() {}
@@ -399,27 +399,27 @@ namespace EvalLearningTools
// A builder that creates a KKP object from raw_index (a number that starts from 0, not a serial number)
KKP fromRawIndex(uint64_t raw_index) const
{
- int piece = (int)(raw_index % Eval::fe_end);
- raw_index /= Eval::fe_end;
+ int piece = (int)(raw_index % PieceSquare::PS_END);
+ raw_index /= PieceSquare::PS_END;
int king1 = (int)(raw_index % SQUARE_NB);
raw_index /= SQUARE_NB;
int king0 = (int)(raw_index /* % SQUARE_NB */);
assert(king0 < SQUARE_NB);
- return fromKKP((Square)king0, (Square)king1, (Eval::BonaPiece)piece,false);
+ return fromKKP((Square)king0, (Square)king1, (PieceSquare)piece,false);
}
- KKP fromKKP(Square king0, Square king1, Eval::BonaPiece p, bool inverse) const
+ KKP fromKKP(Square king0, Square king1, PieceSquare p, bool inverse) const
{
KKP my_kkp(king0, king1, p, inverse);
my_kkp.set(max_king_sq_,fe_end_,min_index());
return my_kkp;
}
- KKP fromKKP(Square king0, Square king1, Eval::BonaPiece p) const { return fromKKP(king0, king1, p, false); }
+ KKP fromKKP(Square king0, Square king1, PieceSquare p) const { return fromKKP(king0, king1, p, false); }
// When you construct this object using fromIndex(), you can get information with the following accessors.
Square king0() const { return king0_; }
Square king1() const { return king1_; }
- Eval::BonaPiece piece() const { return piece_; }
+ PieceSquare piece() const { return piece_; }
// Number of KKP dimension reductions
#if defined(USE_KKP_INVERSE_WRITE)
@@ -442,10 +442,10 @@ namespace EvalLearningTools
void toLowerDimensions(/*out*/ KKP kkp_[KKP_LOWER_COUNT]) const {
kkp_[0] = fromKKP(king0_, king1_, piece_,false);
#if defined(USE_KKP_MIRROR_WRITE)
- kkp_[1] = fromKKP(Mir(king0_), Mir(king1_), mir_piece(piece_),false);
+ kkp_[1] = fromKKP(flip_file(king0_), flip_file(king1_), Eval::mir_piece(piece_),false);
#if defined(USE_KKP_INVERSE_WRITE)
- kkp_[2] = fromKKP( Inv(king1_), Inv(king0_), inv_piece(piece_),true);
- kkp_[3] = fromKKP( Inv(Mir(king1_)), Inv(Mir(king0_)) , inv_piece(mir_piece(piece_)),true);
+ kkp_[2] = fromKKP( rotate180(king1_), rotate180(king0_), Eval::inv_piece(piece_),true);
+ kkp_[3] = fromKKP( rotate180(flip_file(king1_)), rotate180(flip_file(king0_)) , Eval::inv_piece(Eval::mir_piece(piece_)),true);
#endif
#endif
}
@@ -473,7 +473,7 @@ namespace EvalLearningTools
private:
Square king0_, king1_;
- Eval::BonaPiece piece_;
+ PieceSquare piece_;
bool inverse_sign;
};
@@ -489,7 +489,7 @@ namespace EvalLearningTools
struct KPP : public SerializerBase
{
protected:
- KPP(Square king, Eval::BonaPiece p0, Eval::BonaPiece p1) : king_(king), piece0_(p0), piece1_(p1) {}
+ KPP(Square king, PieceSquare p0, PieceSquare p1) : king_(king), piece0_(p0), piece1_(p1) {}
public:
KPP() {}
@@ -534,7 +534,7 @@ namespace EvalLearningTools
// From the solution formula of the quadratic equation i = (sqrt(8*index2+1)-1) / 2.
// After i is converted into an integer, j can be calculated as j = index2-i * (i + 1) / 2.
- // BonaPiece assumes 32bit (may not fit in 16bit), so this multiplication must be 64bit.
+ // PieceSquare assumes 32bit (may not fit in 16bit), so this multiplication must be 64bit.
int piece1 = int(sqrt(8 * index2 + 1) - 1) / 2;
int piece0 = int(index2 - (uint64_t)piece1*((uint64_t)piece1 + 1) / 2);
@@ -546,10 +546,10 @@ namespace EvalLearningTools
#endif
int king = (int)(raw_index /* % SQUARE_NB */);
assert(king < max_king_sq_);
- return fromKPP((Square)king, (Eval::BonaPiece)piece0, (Eval::BonaPiece)piece1);
+ return fromKPP((Square)king, (PieceSquare)piece0, (PieceSquare)piece1);
}
- KPP fromKPP(Square king, Eval::BonaPiece p0, Eval::BonaPiece p1) const
+ KPP fromKPP(Square king, PieceSquare p0, PieceSquare p1) const
{
KPP my_kpp(king, p0, p1);
my_kpp.set(max_king_sq_,fe_end_,min_index());
@@ -558,8 +558,8 @@ namespace EvalLearningTools
// When you construct this object using fromIndex(), you can get information with the following accessors.
Square king() const { return king_; }
- Eval::BonaPiece piece0() const { return piece0_; }
- Eval::BonaPiece piece1() const { return piece1_; }
+ PieceSquare piece0() const { return piece0_; }
+ PieceSquare piece1() const { return piece1_; }
// number of dimension reductions
@@ -584,7 +584,7 @@ namespace EvalLearningTools
// Note that if you use a triangular array, the swapped piece0 and piece1 will not be returned.
kpp_[0] = fromKPP(king_, piece0_, piece1_);
#if defined(USE_KPP_MIRROR_WRITE)
- kpp_[1] = fromKPP(Mir(king_), mir_piece(piece0_), mir_piece(piece1_));
+ kpp_[1] = fromKPP(flip_file(king_), Eval::mir_piece(piece0_), Eval::mir_piece(piece1_));
#endif
#else
@@ -592,8 +592,8 @@ namespace EvalLearningTools
kpp_[0] = fromKPP(king_, piece0_, piece1_);
kpp_[1] = fromKPP(king_, piece1_, piece0_);
#if defined(USE_KPP_MIRROR_WRITE)
- kpp_[2] = fromKPP(Mir(king_), mir_piece(piece0_), mir_piece(piece1_));
- kpp_[3] = fromKPP(Mir(king_), mir_piece(piece1_), mir_piece(piece0_));
+ kpp_[2] = fromKPP(flip_file(king_), mir_piece(piece0_), mir_piece(piece1_));
+ kpp_[3] = fromKPP(flip_file(king_), mir_piece(piece1_), mir_piece(piece0_));
#endif
#endif
}
@@ -607,14 +607,14 @@ namespace EvalLearningTools
#else
// Macro similar to that used in Bonanza 6.0
- auto PcPcOnSq = [&](Square k, Eval::BonaPiece i, Eval::BonaPiece j)
+ auto PcPcOnSq = [&](Square k, PieceSquare i, PieceSquare j)
{
// (i,j) in this triangular array is the element in the i-th row and the j-th column.
// 1st row + 2 + ... + i = i * (i+1) / 2 because the i-th row and 0th column is the total of the elements up to that point
// The i-th row and the j-th column is j plus this. i*(i+1)/2+j
- // BonaPiece type is assumed to be 32 bits, so if you do not pay attention to multiplication, it will overflow.
+ // PieceSquare type is assumed to be 32 bits, so if you do not pay attention to multiplication, it will overflow.
return (uint64_t)k * triangle_fe_end + (uint64_t)(uint64_t(i)*(uint64_t(i)+1) / 2 + uint64_t(j));
};
@@ -646,7 +646,7 @@ namespace EvalLearningTools
private:
Square king_;
- Eval::BonaPiece piece0_, piece1_;
+ PieceSquare piece0_, piece1_;
uint64_t triangle_fe_end; // = (uint64_t)fe_end_*((uint64_t)fe_end_ + 1) / 2;
};
@@ -672,7 +672,7 @@ namespace EvalLearningTools
struct KPPP : public SerializerBase
{
protected:
- KPPP(int king, Eval::BonaPiece p0, Eval::BonaPiece p1, Eval::BonaPiece p2) :
+ KPPP(int king, PieceSquare p0, PieceSquare p1, PieceSquare p2) :
king_(king), piece0_(p0), piece1_(p1), piece2_(p2)
{
assert(piece0_ > piece1_ && piece1_ > piece2_);
@@ -716,9 +716,9 @@ namespace EvalLearningTools
kppp_[0] = fromKPPP(king_, piece0_, piece1_,piece2_);
#if KPPP_LOWER_COUNT > 1
// If mir_piece is done, it will be in a state not sorted. Need code to sort.
- Eval::BonaPiece p_list[3] = { mir_piece(piece2_), mir_piece(piece1_), mir_piece(piece0_) };
+ PieceSquare p_list[3] = { mir_piece(piece2_), mir_piece(piece1_), mir_piece(piece0_) };
my_insertion_sort(p_list, 0, 3);
- kppp_[1] = fromKPPP((int)Mir((Square)king_), p_list[2] , p_list[1], p_list[0]);
+ kppp_[1] = fromKPPP((int)flip_file((Square)king_), p_list[2] , p_list[1], p_list[0]);
#endif
}
@@ -797,12 +797,12 @@ namespace EvalLearningTools
assert(king < max_king_sq_);
// Propagate king_sq and fe_end.
- return fromKPPP((Square)king, (Eval::BonaPiece)piece0, (Eval::BonaPiece)piece1 , (Eval::BonaPiece)piece2);
+ return fromKPPP((Square)king, (PieceSquare)piece0, (PieceSquare)piece1 , (PieceSquare)piece2);
}
// Specify k,p0,p1,p2 to build KPPP instance.
// The king_sq and fe_end passed by set() which is internally retained are inherited.
- KPPP fromKPPP(int king, Eval::BonaPiece p0, Eval::BonaPiece p1, Eval::BonaPiece p2) const
+ KPPP fromKPPP(int king, PieceSquare p0, PieceSquare p1, PieceSquare p2) const
{
KPPP kppp(king, p0, p1, p2);
kppp.set(max_king_sq_, fe_end_,min_index());
@@ -815,7 +815,7 @@ namespace EvalLearningTools
// Macro similar to the one used in Bonanza 6.0
// Precondition) i> j> k.
// NG in case of i==j,j==k.
- auto PcPcPcOnSq = [this](int king, Eval::BonaPiece i, Eval::BonaPiece j , Eval::BonaPiece k)
+ auto PcPcPcOnSq = [this](int king, PieceSquare i, PieceSquare j , PieceSquare k)
{
// (i,j,k) in this triangular array is the element in the i-th row and the j-th column.
// 0th row 0th column 0th is the sum of the elements up to that point, so 0 + 0 + 1 + 3 + 6 + ... + (i)*(i-1)/2 = i*( i-1)*(i-2)/6
@@ -823,7 +823,7 @@ namespace EvalLearningTools
// i-th row, j-th column and k-th row is k plus it. + k
assert(i > j && j > k);
- // BonaPiece type is assumed to be 32 bits, so if you do not pay attention to multiplication, it will overflow.
+ // PieceSquare type is assumed to be 32 bits, so if you do not pay attention to multiplication, it will overflow.
return (uint64_t)king * triangle_fe_end + (uint64_t)(
uint64_t(i)*(uint64_t(i) - 1) * (uint64_t(i) - 2) / 6
+ uint64_t(j)*(uint64_t(j) - 1) / 2
@@ -836,9 +836,9 @@ namespace EvalLearningTools
// When you construct this object using fromIndex(), you can get information with the following accessors.
int king() const { return king_; }
- Eval::BonaPiece piece0() const { return piece0_; }
- Eval::BonaPiece piece1() const { return piece1_; }
- Eval::BonaPiece piece2() const { return piece2_; }
+ PieceSquare piece0() const { return piece0_; }
+ PieceSquare piece1() const { return piece1_; }
+ PieceSquare piece2() const { return piece2_; }
// Returns whether or not the dimension lowered with toLowerDimensions is inverse.
// Prepared to match KK, KKP and interface. This method always returns false for this KPPP class.
bool is_inverse() const {
@@ -859,14 +859,14 @@ namespace EvalLearningTools
private:
int king_;
- Eval::BonaPiece piece0_, piece1_,piece2_;
+ PieceSquare piece0_, piece1_,piece2_;
// The part of the square array of [fe_end][fe_end][fe_end] of kppp[king_sq][fe_end][fe_end][fe_end] is made into a triangular array.
// If kppp[king_sq][triangle_fe_end], the number of elements from the 0th row of this triangular array is 0,0,1,3,..., The nth row is n(n-1)/2.
// therefore,
// triangle_fe_end = Σn(n-1)/2 , n=0..fe_end-1
// = fe_end * (fe_end - 1) * (fe_end - 2) / 6
- uint64_t triangle_fe_end; // ((uint64_t)Eval::fe_end)*((uint64_t)Eval::fe_end - 1)*((uint64_t)Eval::fe_end - 2) / 6;
+ uint64_t triangle_fe_end; // ((uint64_t)PieceSquare::PS_END)*((uint64_t)PieceSquare::PS_END - 1)*((uint64_t)PieceSquare::PS_END - 2) / 6;
};
// Output for debugging.
@@ -885,12 +885,12 @@ namespace EvalLearningTools
// piece0() >piece1()
// It is, and it is necessary to keep this constraint even when passing piece0,1 in the constructor.
//
- // Due to this constraint, BonaPieceZero cannot be assigned to piece0 and piece1 at the same time and passed.
+ // Due to this constraint, PieceSquareZero cannot be assigned to piece0 and piece1 at the same time and passed.
// If you want to support learning of dropped frames, you need to devise with evaluate().
struct KKPP: SerializerBase
{
protected:
- KKPP(int king, Eval::BonaPiece p0, Eval::BonaPiece p1) :
+ KKPP(int king, PieceSquare p0, PieceSquare p1) :
king_(king), piece0_(p0), piece1_(p1)
{
assert(piece0_ > piece1_);
@@ -956,12 +956,12 @@ namespace EvalLearningTools
assert(king < max_king_sq_);
// Propagate king_sq and fe_end.
- return fromKKPP(king, (Eval::BonaPiece)piece0, (Eval::BonaPiece)piece1);
+ return fromKKPP(king, (PieceSquare)piece0, (PieceSquare)piece1);
}
// Specify k,p0,p1 to build KKPP instance.
// The king_sq and fe_end passed by set() which is internally retained are inherited.
- KKPP fromKKPP(int king, Eval::BonaPiece p0, Eval::BonaPiece p1) const
+ KKPP fromKKPP(int king, PieceSquare p0, PieceSquare p1) const
{
KKPP kkpp(king, p0, p1);
kkpp.set(max_king_sq_, fe_end_,min_index());
@@ -974,11 +974,11 @@ namespace EvalLearningTools
// Macro similar to the one used in Bonanza 6.0
// Precondition) i> j.
// NG in case of i==j,j==k.
- auto PcPcOnSq = [this](int king, Eval::BonaPiece i, Eval::BonaPiece j)
+ auto PcPcOnSq = [this](int king, PieceSquare i, PieceSquare j)
{
assert(i > j);
- // BonaPiece type is assumed to be 32 bits, so if you do not pay attention to multiplication, it will overflow.
+ // PieceSquare type is assumed to be 32 bits, so if you do not pay attention to multiplication, it will overflow.
return (uint64_t)king * triangle_fe_end + (uint64_t)(
+ uint64_t(i)*(uint64_t(i) - 1) / 2
+ uint64_t(j)
@@ -990,8 +990,8 @@ namespace EvalLearningTools
// When you construct this object using fromIndex(), fromKKPP(), you can get information with the following accessors.
int king() const { return king_; }
- Eval::BonaPiece piece0() const { return piece0_; }
- Eval::BonaPiece piece1() const { return piece1_; }
+ PieceSquare piece0() const { return piece0_; }
+ PieceSquare piece1() const { return piece1_; }
// Returns whether or not the dimension lowered with toLowerDimensions is inverse.
// Prepared to match KK, KKP and interface. In this KKPP class, this method always returns false.
@@ -1013,7 +1013,7 @@ namespace EvalLearningTools
private:
int king_;
- Eval::BonaPiece piece0_, piece1_;
+ PieceSquare piece0_, piece1_;
// Triangularize the square array part of [fe_end][fe_end] of kppp[king_sq][fe_end][fe_end].
uint64_t triangle_fe_end = 0;
diff --git a/src/learn/multi_think.cpp b/src/learn/multi_think.cpp
index ba2c47d4..82ebeabb 100644
--- a/src/learn/multi_think.cpp
+++ b/src/learn/multi_think.cpp
@@ -20,7 +20,7 @@ void MultiThink::go_think()
// Read evaluation function, etc.
// In the case of the learn command, the value of the evaluation function may be corrected after reading the evaluation function, so
// Skip memory corruption check.
- init_nnue(true);
+ Eval::init_NNUE();
// Call the derived class's init().
init();
diff --git a/src/main.cpp b/src/main.cpp
index 6001432d..e8324186 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
@@ -66,6 +64,7 @@ int main(int argc, char* argv[]) {
Endgames::init();
Threads.set(size_t(Options["Threads"]));
Search::clear(); // After threads are up
+ Eval::init_NNUE();
UCI::loop(argc, argv);
diff --git a/src/material.cpp b/src/material.cpp
index bb25d3ca..0ef9926f 100644
--- a/src/material.cpp
+++ b/src/material.cpp
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
diff --git a/src/material.h b/src/material.h
index 21647f23..80d01655 100644
--- a/src/material.h
+++ b/src/material.h
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
diff --git a/src/misc.cpp b/src/misc.cpp
index 865e21fb..0cb98e17 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
@@ -42,11 +40,11 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
#endif
#include
-#include
#include
#include
#include
#include
+#include
#if defined(__linux__) && !defined(__ANDROID__)
#include
@@ -140,7 +138,7 @@ const string engine_info(bool to_uci) {
string month, day, year;
stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008"
- ss << "Stockfish+NNUE " << Version << setfill('0');
+ ss << "Stockfish " << Version << setfill('0');
if (Version.empty())
{
@@ -148,10 +146,8 @@ const string engine_info(bool to_uci) {
ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
}
- ss << (Is64Bit ? " 64" : "")
- << (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : ""))
- << (to_uci ? "\nid author ": " by ")
- << "T. Romstad, M. Costalba, J. Kiiski, G. Linscott, H. Noda, Y. Nasu, M. Isozaki";
+ ss << (to_uci ? "\nid author ": " by ")
+ << "the Stockfish developers (see AUTHORS file)";
return ss.str();
}
@@ -216,7 +212,33 @@ const std::string compiler_info() {
compiler += " on unknown system";
#endif
- compiler += "\n __VERSION__ macro expands to: ";
+ compiler += "\nCompilation settings include: ";
+ compiler += (Is64Bit ? " 64bit" : " 32bit");
+ #if defined(USE_AVX512)
+ compiler += " AVX512";
+ #endif
+ #if defined(USE_AVX2)
+ compiler += " AVX2";
+ #endif
+ #if defined(USE_SSE42)
+ compiler += " SSE42";
+ #endif
+ #if defined(USE_SSE41)
+ compiler += " SSE41";
+ #endif
+ #if defined(USE_SSSE3)
+ compiler += " SSSE3";
+ #endif
+ #if defined(USE_SSE3)
+ compiler += " SSE3";
+ #endif
+ compiler += (HasPext ? " BMI2" : "");
+ compiler += (HasPopCnt ? " POPCNT" : "");
+ #if !defined(NDEBUG)
+ compiler += " DEBUG";
+ #endif
+
+ compiler += "\n__VERSION__ macro expands to: ";
#ifdef __VERSION__
compiler += __VERSION__;
#else
@@ -294,6 +316,29 @@ void prefetch(void* addr) {
#endif
+/// Wrappers for systems where the c++17 implementation doesn't guarantee the availability of aligned_alloc.
+/// Memory allocated with std_aligned_alloc must be freed with std_aligned_free.
+///
+
+void* std_aligned_alloc(size_t alignment, size_t size) {
+#if defined(__APPLE__)
+ return aligned_alloc(alignment, size);
+#elif defined(_WIN32)
+ return _mm_malloc(size, alignment);
+#else
+ return std::aligned_alloc(alignment, size);
+#endif
+}
+
+void std_aligned_free(void* ptr) {
+#if defined(__APPLE__)
+ free(ptr);
+#elif defined(_WIN32)
+ _mm_free(ptr);
+#else
+ free(ptr);
+#endif
+}
/// aligned_ttmem_alloc() will return suitably aligned memory, and if possible use large pages.
/// The returned pointer is the aligned one, while the mem argument is the one that needs
@@ -371,8 +416,8 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
{
if (mem)
sync_cout << "info string Hash table allocation: Windows large pages used." << sync_endl;
- //else
- //sync_cout << "info string Hash table allocation: Windows large pages not used." << sync_endl;
+ else
+ sync_cout << "info string Hash table allocation: Windows large pages not used." << sync_endl;
}
firstCall = false;
@@ -530,99 +575,99 @@ void bindThisThread(size_t idx) {
// Returns a string that represents the current time. (Used when learning evaluation functions)
std::string now_string()
{
- // Using std::ctime(), localtime() gives a warning that MSVC is not secure.
- // This shouldn't happen in the C++ standard, but...
+ // Using std::ctime(), localtime() gives a warning that MSVC is not secure.
+ // This shouldn't happen in the C++ standard, but...
#if defined(_MSC_VER)
// C4996 : 'ctime' : This function or variable may be unsafe.Consider using ctime_s instead.
#pragma warning(disable : 4996)
#endif
- auto now = std::chrono::system_clock::now();
- auto tp = std::chrono::system_clock::to_time_t(now);
- auto result = string(std::ctime(&tp));
+ auto now = std::chrono::system_clock::now();
+ auto tp = std::chrono::system_clock::to_time_t(now);
+ auto result = string(std::ctime(&tp));
- // remove line endings if they are included at the end
- while (*result.rbegin() == '\n' || (*result.rbegin() == '\r'))
- result.pop_back();
- return result;
+ // remove line endings if they are included at the end
+ while (*result.rbegin() == '\n' || (*result.rbegin() == '\r'))
+ result.pop_back();
+ return result;
}
void sleep(int ms)
{
- std::this_thread::sleep_for(std::chrono::milliseconds(ms));
+ std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
void* aligned_malloc(size_t size, size_t align)
{
- void* p = _mm_malloc(size, align);
- if (p == nullptr)
- {
- std::cout << "info string can't allocate memory. sise = " << size << std::endl;
- exit(1);
- }
- return p;
+ void* p = _mm_malloc(size, align);
+ if (p == nullptr)
+ {
+ std::cout << "info string can't allocate memory. sise = " << size << std::endl;
+ exit(1);
+ }
+ return p;
}
int read_file_to_memory(std::string filename, std::function callback_func)
{
- fstream fs(filename, ios::in | ios::binary);
- if (fs.fail())
- return 1;
-
- fs.seekg(0, fstream::end);
- uint64_t eofPos = (uint64_t)fs.tellg();
- fs.clear(); // Otherwise the next seek may fail.
- fs.seekg(0, fstream::beg);
- uint64_t begPos = (uint64_t)fs.tellg();
- uint64_t file_size = eofPos - begPos;
- //std::cout << "filename = " << filename << " , file_size = " << file_size << endl;
-
- // I know the file size, so call callback_func to get a buffer for this,
- // Get the pointer.
- void* ptr = callback_func(file_size);
-
- // If the buffer could not be secured, or if the file size is different from the expected file size,
- // It is supposed to return nullptr. At this time, reading is interrupted and an error is returned.
- if (ptr == nullptr)
- return 2;
-
- // read in pieces
-
- const uint64_t block_size = 1024 * 1024 * 1024; // number of elements to read in one read (1GB)
- for (uint64_t pos = 0; pos < file_size; pos += block_size)
- {
- // size to read this time
- uint64_t read_size = (pos + block_size < file_size) ? block_size : (file_size - pos);
- fs.read((char*)ptr + pos, read_size);
-
- // Read error occurred in the middle of the file.
+ fstream fs(filename, ios::in | ios::binary);
if (fs.fail())
- return 2;
+ return 1;
- //cout << ".";
- }
- fs.close();
+ fs.seekg(0, fstream::end);
+ uint64_t eofPos = (uint64_t)fs.tellg();
+ fs.clear(); // Otherwise the next seek may fail.
+ fs.seekg(0, fstream::beg);
+ uint64_t begPos = (uint64_t)fs.tellg();
+ uint64_t file_size = eofPos - begPos;
+ //std::cout << "filename = " << filename << " , file_size = " << file_size << endl;
- return 0;
+ // I know the file size, so call callback_func to get a buffer for this,
+ // Get the pointer.
+ void* ptr = callback_func(file_size);
+
+ // If the buffer could not be secured, or if the file size is different from the expected file size,
+ // It is supposed to return nullptr. At this time, reading is interrupted and an error is returned.
+ if (ptr == nullptr)
+ return 2;
+
+ // read in pieces
+
+ const uint64_t block_size = 1024 * 1024 * 1024; // number of elements to read in one read (1GB)
+ for (uint64_t pos = 0; pos < file_size; pos += block_size)
+ {
+ // size to read this time
+ uint64_t read_size = (pos + block_size < file_size) ? block_size : (file_size - pos);
+ fs.read((char*)ptr + pos, read_size);
+
+ // Read error occurred in the middle of the file.
+ if (fs.fail())
+ return 2;
+
+ //cout << ".";
+ }
+ fs.close();
+
+ return 0;
}
int write_memory_to_file(std::string filename, void* ptr, uint64_t size)
{
- fstream fs(filename, ios::out | ios::binary);
- if (fs.fail())
- return 1;
+ fstream fs(filename, ios::out | ios::binary);
+ if (fs.fail())
+ return 1;
- const uint64_t block_size = 1024 * 1024 * 1024; // number of elements to write in one write (1GB)
- for (uint64_t pos = 0; pos < size; pos += block_size)
- {
- // Memory size to write this time
- uint64_t write_size = (pos + block_size < size) ? block_size : (size - pos);
- fs.write((char*)ptr + pos, write_size);
- //cout << ".";
- }
- fs.close();
- return 0;
+ const uint64_t block_size = 1024 * 1024 * 1024; // number of elements to write in one write (1GB)
+ for (uint64_t pos = 0; pos < size; pos += block_size)
+ {
+ // Memory size to write this time
+ uint64_t write_size = (pos + block_size < size) ? block_size : (size - pos);
+ fs.write((char*)ptr + pos, write_size);
+ //cout << ".";
+ }
+ fs.close();
+ return 0;
}
// ----------------------------
@@ -642,22 +687,22 @@ int write_memory_to_file(std::string filename, void* ptr, uint64_t size)
#include // This is required for wstring_convert.
namespace Dependency {
- int mkdir(std::string dir_name)
- {
- std::wstring_convert, wchar_t> cv;
- return _wmkdir(cv.from_bytes(dir_name).c_str());
- // ::CreateDirectory(cv.from_bytes(dir_name).c_str(),NULL);
- }
+ int mkdir(std::string dir_name)
+ {
+ std::wstring_convert, wchar_t> cv;
+ return _wmkdir(cv.from_bytes(dir_name).c_str());
+ // ::CreateDirectory(cv.from_bytes(dir_name).c_str(),NULL);
+ }
}
#elif defined(__GNUC__)
#include
namespace Dependency {
- int mkdir(std::string dir_name)
- {
- return _mkdir(dir_name.c_str());
- }
+ int mkdir(std::string dir_name)
+ {
+ return _mkdir(dir_name.c_str());
+ }
}
#endif
@@ -669,10 +714,10 @@ namespace Dependency {
#include "sys/stat.h"
namespace Dependency {
- int mkdir(std::string dir_name)
- {
- return ::mkdir(dir_name.c_str(), 0777);
- }
+ int mkdir(std::string dir_name)
+ {
+ return ::mkdir(dir_name.c_str(), 0777);
+ }
}
#else
@@ -680,10 +725,10 @@ namespace Dependency {
// The function to dig a folder on linux is good for the time being... Only used to save the evaluation function file...
namespace Dependency {
- int mkdir(std::string dir_name)
- {
- return 0;
- }
+ int mkdir(std::string dir_name)
+ {
+ return 0;
+ }
}
#endif
diff --git a/src/misc.h b/src/misc.h
index 0e2e8403..e51d5f3f 100644
--- a/src/misc.h
+++ b/src/misc.h
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
@@ -40,6 +38,8 @@ const std::string engine_info(bool to_uci = false);
const std::string compiler_info();
void prefetch(void* addr);
void start_logger(const std::string& fname);
+void* std_aligned_alloc(size_t alignment, size_t size);
+void std_aligned_free(void* ptr);
void* aligned_ttmem_alloc(size_t size, void*& mem);
void aligned_ttmem_free(void* mem); // nop if mem == nullptr
@@ -265,9 +265,6 @@ struct Path
}
};
-extern void* aligned_malloc(size_t size, size_t align);
-static void aligned_free(void* ptr) { _mm_free(ptr); }
-
// It is ignored when new even though alignas is specified & because it is ignored when the STL container allocates memory,
// A custom allocator used for that.
template
@@ -281,8 +278,8 @@ public:
template AlignedAllocator(const AlignedAllocator&) {}
- T* allocate(std::size_t n) { return (T*)aligned_malloc(n * sizeof(T), alignof(T)); }
- void deallocate(T* p, std::size_t n) { aligned_free(p); }
+ T* allocate(std::size_t n) { return (T*)std_aligned_alloc(n * sizeof(T), alignof(T)); }
+ void deallocate(T* p, std::size_t n) { std_aligned_free(p); }
};
// --------------------
diff --git a/src/movegen.cpp b/src/movegen.cpp
index 4ff12fc6..d74df4c3 100644
--- a/src/movegen.cpp
+++ b/src/movegen.cpp
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
diff --git a/src/movegen.h b/src/movegen.h
index d5f82f16..675b7698 100644
--- a/src/movegen.h
+++ b/src/movegen.h
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
diff --git a/src/movepick.cpp b/src/movepick.cpp
index 5775f810..96a44449 100644
--- a/src/movepick.cpp
+++ b/src/movepick.cpp
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
diff --git a/src/movepick.h b/src/movepick.h
index aaff388f..f080935a 100644
--- a/src/movepick.h
+++ b/src/movepick.h
@@ -1,8 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
- Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
- Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
+ Copyright (C) 2004-2020 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
diff --git a/src/nnue/architectures/halfkp_256x2-32-32.h b/src/nnue/architectures/halfkp_256x2-32-32.h
index 467d0222..9216bd41 100644
--- a/src/nnue/architectures/halfkp_256x2-32-32.h
+++ b/src/nnue/architectures/halfkp_256x2-32-32.h
@@ -1,7 +1,25 @@
-// Definition of input features and network structure used in NNUE evaluation function
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
-#ifndef HALFKP_256X2_32_32_H
-#define HALFKP_256X2_32_32_H
+ 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 .
+*/
+
+// Definition of input features and network structure used in NNUE evaluation function
+
+#ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED
+#define NNUE_HALFKP_256X2_32_32_H_INCLUDED
#include "../features/feature_set.h"
#include "../features/half_kp.h"
@@ -10,9 +28,7 @@
#include "../layers/affine_transform.h"
#include "../layers/clipped_relu.h"
-namespace Eval {
-
-namespace NNUE {
+namespace Eval::NNUE {
// Input features used in evaluation function
using RawFeatures = Features::FeatureSet<
@@ -23,7 +39,7 @@ constexpr IndexType kTransformedFeatureDimensions = 256;
namespace Layers {
-// define network structure
+// Define network structure
using InputLayer = InputSlice;
using HiddenLayer1 = ClippedReLU>;
using HiddenLayer2 = ClippedReLU>;
@@ -33,7 +49,6 @@ using OutputLayer = AffineTransform;
using Network = Layers::OutputLayer;
-} // namespace NNUE
+} // namespace Eval::NNUE
-} // namespace Eval
-#endif // HALFKP_256X2_32_32_H
+#endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED
diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp
index 6a664907..66e5ff57 100644
--- a/src/nnue/evaluate_nnue.cpp
+++ b/src/nnue/evaluate_nnue.cpp
@@ -1,9 +1,26 @@
-// Code for calculating NNUE evaluation function
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
-#if defined(EVAL_NNUE)
+ 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 .
+*/
+
+// Code for calculating NNUE evaluation function
#include
#include
+#include
#include "../evaluate.h"
#include "../position.h"
@@ -12,315 +29,186 @@
#include "evaluate_nnue.h"
-namespace Eval {
+ExtPieceSquare kpp_board_index[PIECE_NB] = {
+ // convention: W - us, B - them
+ // viewed from other side, W and B are reversed
+ { PS_NONE, PS_NONE },
+ { PS_W_PAWN, PS_B_PAWN },
+ { PS_W_KNIGHT, PS_B_KNIGHT },
+ { PS_W_BISHOP, PS_B_BISHOP },
+ { PS_W_ROOK, PS_B_ROOK },
+ { PS_W_QUEEN, PS_B_QUEEN },
+ { PS_W_KING, PS_B_KING },
+ { PS_NONE, PS_NONE },
+ { PS_NONE, PS_NONE },
+ { PS_B_PAWN, PS_W_PAWN },
+ { PS_B_KNIGHT, PS_W_KNIGHT },
+ { PS_B_BISHOP, PS_W_BISHOP },
+ { PS_B_ROOK, PS_W_ROOK },
+ { PS_B_QUEEN, PS_W_QUEEN },
+ { PS_B_KING, PS_W_KING },
+ { PS_NONE, PS_NONE }
+};
-namespace NNUE {
-// Input feature converter
-AlignedPtr feature_transformer;
+namespace Eval::NNUE {
-// Evaluation function
-AlignedPtr network;
+ // Input feature converter
+ AlignedPtr feature_transformer;
-// Evaluation function file name
-std::string fileName = "nn.bin";
+ // Evaluation function
+ AlignedPtr network;
-// Saved evaluation function file name
-std::string savedfileName = "nn.bin";
+ // Evaluation function file name
+ std::string fileName;
-// Get a string that represents the structure of the evaluation function
-std::string GetArchitectureString() {
- return "Features=" + FeatureTransformer::GetStructureString() +
+ // Saved evaluation function file name
+ std::string savedfileName = "nn.bin";
+
+ // Get a string that represents the structure of the evaluation function
+ std::string GetArchitectureString() {
+ return "Features=" + FeatureTransformer::GetStructureString() +
",Network=" + Network::GetStructureString();
-}
+ }
-namespace {
+ namespace Detail {
-namespace Detail {
+ // Initialize the evaluation function parameters
+ template
+ void Initialize(AlignedPtr& pointer) {
-// Initialize the evaluation function parameters
-template
-void Initialize(AlignedPtr& pointer) {
- pointer.reset(reinterpret_cast(aligned_malloc(sizeof(T), alignof(T))));
- std::memset(pointer.get(), 0, sizeof(T));
-}
+ pointer.reset(reinterpret_cast(std_aligned_alloc(alignof(T), sizeof(T))));
+ std::memset(pointer.get(), 0, sizeof(T));
+ }
-// read evaluation function parameters
-template
-bool ReadParameters(std::istream& stream, const AlignedPtr& pointer) {
- std::uint32_t header;
- stream.read(reinterpret_cast(&header), sizeof(header));
- if (!stream || header != T::GetHashValue()) return false;
- return pointer->ReadParameters(stream);
-}
+ // Read evaluation function parameters
+ template
+ bool ReadParameters(std::istream& stream, const AlignedPtr& pointer) {
-// write evaluation function parameters
-template
-bool WriteParameters(std::ostream& stream, const AlignedPtr& pointer) {
- constexpr std::uint32_t header = T::GetHashValue();
- stream.write(reinterpret_cast(&header), sizeof(header));
- return pointer->WriteParameters(stream);
-}
+ std::uint32_t header;
+ stream.read(reinterpret_cast(&header), sizeof(header));
+ if (!stream || header != T::GetHashValue()) return false;
+ return pointer->ReadParameters(stream);
+ }
-} // namespace Detail
+ // write evaluation function parameters
+ template
+ bool WriteParameters(std::ostream& stream, const AlignedPtr& pointer) {
+ constexpr std::uint32_t header = T::GetHashValue();
+ stream.write(reinterpret_cast(&header), sizeof(header));
+ return pointer->WriteParameters(stream);
+ }
-// Initialize the evaluation function parameters
-void Initialize() {
- Detail::Initialize(feature_transformer);
- Detail::Initialize(network);
-}
+ } // namespace Detail
-} // namespace
+ // Initialize the evaluation function parameters
+ void Initialize() {
-// read the header
-bool ReadHeader(std::istream& stream,
- std::uint32_t* hash_value, std::string* architecture) {
- std::uint32_t version, size;
- stream.read(reinterpret_cast(&version), sizeof(version));
- stream.read(reinterpret_cast(hash_value), sizeof(*hash_value));
- stream.read(reinterpret_cast(&size), sizeof(size));
- if (!stream || version != kVersion) return false;
- architecture->resize(size);
- stream.read(&(*architecture)[0], size);
- return !stream.fail();
-}
+ Detail::Initialize(feature_transformer);
+ Detail::Initialize(network);
+ }
-// write the header
-bool WriteHeader(std::ostream& stream,
- std::uint32_t hash_value, const std::string& architecture) {
- stream.write(reinterpret_cast(&kVersion), sizeof(kVersion));
- stream.write(reinterpret_cast(&hash_value), sizeof(hash_value));
- const std::uint32_t size = static_cast(architecture.size());
- stream.write(reinterpret_cast(&size), sizeof(size));
- stream.write(architecture.data(), size);
- return !stream.fail();
-}
+ // Read network header
+ bool ReadHeader(std::istream& stream,
+ std::uint32_t* hash_value, std::string* architecture) {
-// read evaluation function parameters
-bool ReadParameters(std::istream& stream) {
- std::uint32_t hash_value;
- std::string architecture;
- if (!ReadHeader(stream, &hash_value, &architecture)) return false;
- if (hash_value != kHashValue) return false;
- if (!Detail::ReadParameters(stream, feature_transformer)) return false;
- if (!Detail::ReadParameters(stream, network)) return false;
- return stream && stream.peek() == std::ios::traits_type::eof();
-}
+ std::uint32_t version, size;
+ stream.read(reinterpret_cast(&version), sizeof(version));
+ stream.read(reinterpret_cast(hash_value), sizeof(*hash_value));
+ stream.read(reinterpret_cast(&size), sizeof(size));
+ if (!stream || version != kVersion) return false;
+ architecture->resize(size);
+ stream.read(&(*architecture)[0], size);
+ return !stream.fail();
+ }
-// write evaluation function parameters
-bool WriteParameters(std::ostream& stream) {
- if (!WriteHeader(stream, kHashValue, GetArchitectureString())) return false;
- if (!Detail::WriteParameters(stream, feature_transformer)) return false;
- if (!Detail::WriteParameters(stream, network)) return false;
- return !stream.fail();
-}
+ // write the header
+ bool WriteHeader(std::ostream& stream,
+ std::uint32_t hash_value, const std::string& architecture) {
+ stream.write(reinterpret_cast(&kVersion), sizeof(kVersion));
+ stream.write(reinterpret_cast(&hash_value), sizeof(hash_value));
+ const std::uint32_t size = static_cast(architecture.size());
+ stream.write(reinterpret_cast(&size), sizeof(size));
+ stream.write(architecture.data(), size);
+ return !stream.fail();
+ }
-// proceed if you can calculate the difference
-static void UpdateAccumulatorIfPossible(const Position& pos) {
- feature_transformer->UpdateAccumulatorIfPossible(pos);
-}
+ // Read network parameters
+ bool ReadParameters(std::istream& stream) {
-// Calculate the evaluation value
-static Value ComputeScore(const Position& pos, bool refresh = false) {
- auto& accumulator = pos.state()->accumulator;
- if (!refresh && accumulator.computed_score) {
+ std::uint32_t hash_value;
+ std::string architecture;
+ if (!ReadHeader(stream, &hash_value, &architecture)) return false;
+ if (hash_value != kHashValue) return false;
+ if (!Detail::ReadParameters(stream, feature_transformer)) return false;
+ if (!Detail::ReadParameters(stream, network)) return false;
+ return stream && stream.peek() == std::ios::traits_type::eof();
+ }
+
+ // write evaluation function parameters
+ bool WriteParameters(std::ostream& stream) {
+ if (!WriteHeader(stream, kHashValue, GetArchitectureString())) return false;
+ if (!Detail::WriteParameters(stream, feature_transformer)) return false;
+ if (!Detail::WriteParameters(stream, network)) return false;
+ return !stream.fail();
+ }
+
+ // Proceed with the difference calculation if possible
+ static void UpdateAccumulatorIfPossible(const Position& pos) {
+
+ feature_transformer->UpdateAccumulatorIfPossible(pos);
+ }
+
+ // Calculate the evaluation value
+ static Value ComputeScore(const Position& pos, bool refresh) {
+
+ auto& accumulator = pos.state()->accumulator;
+ if (!refresh && accumulator.computed_score) {
+ return accumulator.score;
+ }
+
+ alignas(kCacheLineSize) TransformedFeatureType
+ transformed_features[FeatureTransformer::kBufferSize];
+ feature_transformer->Transform(pos, transformed_features, refresh);
+ alignas(kCacheLineSize) char buffer[Network::kBufferSize];
+ const auto output = network->Propagate(transformed_features, buffer);
+
+ auto score = static_cast(output[0] / FV_SCALE);
+
+ accumulator.score = score;
+ accumulator.computed_score = true;
return accumulator.score;
}
- alignas(kCacheLineSize) TransformedFeatureType
- transformed_features[FeatureTransformer::kBufferSize];
- feature_transformer->Transform(pos, transformed_features, refresh);
- alignas(kCacheLineSize) char buffer[Network::kBufferSize];
- const auto output = network->Propagate(transformed_features, buffer);
+ // Load the evaluation function file
+ bool load_eval_file(const std::string& evalFile) {
- // When a value larger than VALUE_MAX_EVAL is returned, aspiration search fails high
- // It should be guaranteed that it is less than VALUE_MAX_EVAL because the search will not end.
+ Initialize();
+ fileName = evalFile;
- // Even if this phenomenon occurs, if the seconds are fixed when playing, the search will be aborted there, so
- // The best move in the previous iteration is pointed to as bestmove, so apparently
- // no problem. The situation in which this VALUE_MAX_EVAL is returned is almost at a dead end,
- // Since such a jamming phase often appears at the end, there is a big difference in the situation
- // Doesn't really affect the outcome.
+ std::ifstream stream(evalFile, std::ios::binary);
- // However, when searching with a fixed depth such as when creating a teacher, it will not return from the search
- // Waste the computation time for that thread. Also, it will be timed out with fixed depth game.
+ const bool result = ReadParameters(stream);
- auto score = static_cast(output[0] / FV_SCALE);
-
- // 1) I feel that if I clip too poorly, it will have an effect on my learning...
- // 2) Since accumulator.score is not used at the time of difference calculation, it can be rewritten without any problem.
- score = Math::clamp(score , -VALUE_MAX_EVAL , VALUE_MAX_EVAL);
-
- accumulator.score = score;
- accumulator.computed_score = true;
- return accumulator.score;
-}
-
-} // namespace NNUE
-
-#if defined(USE_EVAL_HASH)
-// Class used to store evaluation values in HashTable
-struct alignas(16) ScoreKeyValue {
-#if defined(USE_SSE2)
- ScoreKeyValue() = default;
- ScoreKeyValue(const ScoreKeyValue& other) {
- static_assert(sizeof(ScoreKeyValue) == sizeof(__m128i),
- "sizeof(ScoreKeyValue) should be equal to sizeof(__m128i)");
- _mm_store_si128(&as_m128i, other.as_m128i);
- }
- ScoreKeyValue& operator=(const ScoreKeyValue& other) {
- _mm_store_si128(&as_m128i, other.as_m128i);
- return *this;
- }
-#endif
-
- // It is necessary to be able to operate atomically with evaluate hash, so the manipulator for that
- void encode() {
-#if defined(USE_SSE2)
- // ScoreKeyValue is copied to atomic, so if the key matches, the data matches.
-#else
- key ^= score;
-#endif
- }
- // decode() is the reverse conversion of encode(), but since it is xor, the reverse conversion is the same.
- void decode() { encode(); }
-
- union {
- struct {
- std::uint64_t key;
- std::uint64_t score;
- };
-#if defined(USE_SSE2)
- __m128i as_m128i;
-#endif
- };
-};
-
-// Simple HashTable implementation.
-// Size is a power of 2.
-template
-struct HashTable {
- HashTable() { clear(); }
- T* operator [] (const Key k) { return entries_ + (static_cast(k) & (Size - 1)); }
- void clear() { memset(entries_, 0, sizeof(T)*Size); }
-
- // Check that Size is a power of 2
- static_assert((Size & (Size - 1)) == 0, "");
-
- private:
- T entries_[Size];
-};
-
-//HashTable to save the evaluated ones (following ehash)
-
-#if !defined(USE_LARGE_EVAL_HASH)
-// 134MB (setting other than witch's AVX2)
-struct EvaluateHashTable : HashTable {};
-#else
-// If you have prefetch, it's better to have a big one...
-// → It doesn't change much and the memory is wasteful, so is it okay to set ↑ by default?
-// 1GB (setting for witch's AVX2)
-struct EvaluateHashTable : HashTable {};
-#endif
-
-EvaluateHashTable g_evalTable;
-
-// Prepare a function to prefetch.
-void prefetch_evalhash(const Key key) {
- constexpr auto mask = ~((uint64_t)0x1f);
- prefetch((void*)((uint64_t)g_evalTable[key] & mask));
-}
-#endif
-
-// read the evaluation function file
-// Save and restore Options with bench command etc., so EvalDir is changed at this time,
-// This function may be called twice to flag that the evaluation function needs to be reloaded.
-void load_eval() {
-
- // Must be done!
- NNUE::Initialize();
-
- if (Options["SkipLoadingEval"])
- {
- std::cout << "info string SkipLoadingEval set to true, Net not loaded!" << std::endl;
- return;
+ return result;
}
- const std::string file_name = Options["EvalFile"];
- NNUE::fileName = file_name;
+ // Evaluation function. Perform differential calculation.
+ Value evaluate(const Position& pos) {
+ Value v = ComputeScore(pos, false);
+ v = Utility::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
- std::ifstream stream(file_name, std::ios::binary);
- const bool result = NNUE::ReadParameters(stream);
-
- if (!result)
- // It's a problem if it doesn't finish when there is a read error.
- std::cout << "Error! " << NNUE::fileName << " not found or wrong format" << std::endl;
-
- else
- std::cout << "info string NNUE " << NNUE::fileName << " found & loaded" << std::endl;
-}
-
-// Initialization
-void init() {
-}
-
-// Evaluation function. Perform full calculation instead of difference calculation.
-// Called only once with Position::set(). (The difference calculation after that)
-// Note that the evaluation value seen from the turn side is returned. (Design differs from other evaluation functions in this respect)
-// Since, we will not try to optimize this function.
-Value compute_eval(const Position& pos) {
- return NNUE::ComputeScore(pos, true);
-}
-
-// Evaluation function
-Value evaluate(const Position& pos) {
- const auto& accumulator = pos.state()->accumulator;
- if (accumulator.computed_score) {
- return accumulator.score;
+ return v;
}
-#if defined(USE_GLOBAL_OPTIONS)
- // If Global Options is set not to use eval hash
- // Skip the query to the eval hash.
- if (!GlobalOptions.use_eval_hash) {
- ASSERT_LV5(pos.state()->materialValue == Eval::material(pos));
- return NNUE::ComputeScore(pos);
+ // Evaluation function. Perform full calculation.
+ Value compute_eval(const Position& pos) {
+ return ComputeScore(pos, true);
}
-#endif
-#if defined(USE_EVAL_HASH)
- // May be in the evaluate hash table.
- const Key key = pos.key();
- ScoreKeyValue entry = *g_evalTable[key];
- entry.decode();
- if (entry.key == key) {
- // there were!
- return Value(entry.score);
+ // Proceed with the difference calculation if possible
+ void update_eval(const Position& pos) {
+ UpdateAccumulatorIfPossible(pos);
}
-#endif
- Value score = NNUE::ComputeScore(pos);
-#if defined(USE_EVAL_HASH)
- // Since it was calculated carefully, save it in the evaluate hash table.
- entry.key = key;
- entry.score = score;
- entry.encode();
- *g_evalTable[key] = entry;
-#endif
-
- return score;
-}
-
-// proceed if you can calculate the difference
-void evaluate_with_no_return(const Position& pos) {
- NNUE::UpdateAccumulatorIfPossible(pos);
-}
-
-// display the breakdown of the evaluation value of the current phase
-void print_eval_stat(Position& /*pos*/) {
- std::cout << "--- EVAL STAT: not implemented" << std::endl;
-}
-
-} // namespace Eval
-
-#endif // defined(EVAL_NNUE)
+} // namespace Eval::NNUE
diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h
index d474a8ae..75700d03 100644
--- a/src/nnue/evaluate_nnue.h
+++ b/src/nnue/evaluate_nnue.h
@@ -1,67 +1,77 @@
-// header used in NNUE evaluation function
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
-#ifndef _EVALUATE_NNUE_H_
-#define _EVALUATE_NNUE_H_
+ 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.
-#if defined(EVAL_NNUE)
+ 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 .
+*/
+
+// header used in NNUE evaluation function
+
+#ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
+#define NNUE_EVALUATE_NNUE_H_INCLUDED
#include "nnue_feature_transformer.h"
-#include "nnue_architecture.h"
#include
-namespace Eval {
+namespace Eval::NNUE {
-namespace NNUE {
+ // Hash value of evaluation function structure
+ constexpr std::uint32_t kHashValue =
+ FeatureTransformer::GetHashValue() ^ Network::GetHashValue();
-// hash value of evaluation function structure
-constexpr std::uint32_t kHashValue =
- FeatureTransformer::GetHashValue() ^ Network::GetHashValue();
+ // Deleter for automating release of memory area
+ template
+ struct AlignedDeleter {
+ void operator()(T* ptr) const {
+ ptr->~T();
+ std_aligned_free(ptr);
+ }
+ };
-// Deleter for automating release of memory area
-template
-struct AlignedDeleter {
- void operator()(T* ptr) const {
- ptr->~T();
- aligned_free(ptr);
- }
-};
-template
-using AlignedPtr = std::unique_ptr>;
+ template
+ using AlignedPtr = std::unique_ptr>;
-// Input feature converter
-extern AlignedPtr feature_transformer;
+ // Input feature converter
+ extern AlignedPtr feature_transformer;
-// Evaluation function
-extern AlignedPtr network;
+ // Evaluation function
+ extern AlignedPtr network;
-// Evaluation function file name
-extern std::string fileName;
+ // Evaluation function file name
+ extern std::string fileName;
-// Saved evaluation function file name
-extern std::string savedfileName;
+ // Saved evaluation function file name
+ extern std::string savedfileName;
-// Get a string that represents the structure of the evaluation function
-std::string GetArchitectureString();
+ // Get a string that represents the structure of the evaluation function
+ std::string GetArchitectureString();
-// read the header
-bool ReadHeader(std::istream& stream,
+ // read the header
+ bool ReadHeader(std::istream& stream,
std::uint32_t* hash_value, std::string* architecture);
-// write the header
-bool WriteHeader(std::ostream& stream,
+ // write the header
+ bool WriteHeader(std::ostream& stream,
std::uint32_t hash_value, const std::string& architecture);
-// read evaluation function parameters
-bool ReadParameters(std::istream& stream);
+ // read evaluation function parameters
+ bool ReadParameters(std::istream& stream);
-// write evaluation function parameters
-bool WriteParameters(std::ostream& stream);
+ // write evaluation function parameters
+ bool WriteParameters(std::ostream& stream);
-} // namespace NNUE
+} // namespace Eval::NNUE
-} // namespace Eval
-
-#endif // defined(EVAL_NNUE)
-
-#endif
+#endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
diff --git a/src/nnue/features/enpassant.cpp b/src/nnue/features/enpassant.cpp
index 82a4158e..ea70529a 100644
--- a/src/nnue/features/enpassant.cpp
+++ b/src/nnue/features/enpassant.cpp
@@ -23,7 +23,7 @@ namespace Eval {
}
if (perspective == BLACK) {
- epSquare = Inv(epSquare);
+ epSquare = rotate180(epSquare);
}
auto file = file_of(epSquare);
diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h
index 0430ebfe..c46da462 100644
--- a/src/nnue/features/feature_set.h
+++ b/src/nnue/features/feature_set.h
@@ -1,249 +1,249 @@
-// A class template that represents the input feature set of the NNUE evaluation function
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
-#ifndef _NNUE_FEATURE_SET_H_
-#define _NNUE_FEATURE_SET_H_
+ 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.
-#if defined(EVAL_NNUE)
+ 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 .
+*/
+
+// A class template that represents the input feature set of the NNUE evaluation function
+
+#ifndef NNUE_FEATURE_SET_H_INCLUDED
+#define NNUE_FEATURE_SET_H_INCLUDED
#include "features_common.h"
#include
-namespace Eval {
+namespace Eval::NNUE::Features {
-namespace NNUE {
+ // Class template that represents a list of values
+ template
+ struct CompileTimeList;
-namespace Features {
+ template
+ struct CompileTimeList {
+ static constexpr bool Contains(T value) {
+ return value == First || CompileTimeList::Contains(value);
+ }
+ static constexpr std::array
+ kValues = {{First, Remaining...}};
+ };
-// A class template that represents a list of values
-template
-struct CompileTimeList;
-template
-struct CompileTimeList {
- static constexpr bool Contains(T value) {
- return value == First || CompileTimeList::Contains(value);
- }
- static constexpr std::array
- kValues = {{First, Remaining...}};
-};
-template
-constexpr std::array
+ template
+ constexpr std::array
CompileTimeList::kValues;
-template
-struct CompileTimeList {
- static constexpr bool Contains(T /*value*/) {
- return false;
- }
- static constexpr std::array kValues = {{}};
-};
+ template
+ struct CompileTimeList {
+ static constexpr bool Contains(T /*value*/) {
+ return false;
+ }
+ static constexpr std::array kValues = { {} };
+ };
-// Class template that adds to the beginning of the list
-template
-struct AppendToList;
-template
-struct AppendToList, AnotherValue> {
- using Result = CompileTimeList;
-};
+ // Class template that adds to the beginning of the list
+ template
+ struct AppendToList;
+ template
+ struct AppendToList, AnotherValue> {
+ using Result = CompileTimeList;
+ };
-// Class template for adding to a sorted, unique list
-template
-struct InsertToSet;
-template
-struct InsertToSet, AnotherValue> {
- using Result = std::conditional_t<
+ // Class template for adding to a sorted, unique list
+ template
+ struct InsertToSet;
+ template
+ struct InsertToSet, AnotherValue> {
+ using Result = std::conditional_t<
CompileTimeList::Contains(AnotherValue),
CompileTimeList,
- std::conditional_t<(AnotherValue ,
- typename AppendToList, AnotherValue>::Result,
- First>::Result>>;
-};
-template
-struct InsertToSet, Value> {
- using Result = CompileTimeList;
-};
+ std::conditional_t<(AnotherValue < First),
+ CompileTimeList,
+ typename AppendToList, AnotherValue>::Result,
+ First>::Result>>;
+ };
+ template
+ struct InsertToSet, Value> {
+ using Result = CompileTimeList;
+ };
-// Base class of feature set
-template
-class FeatureSetBase {
- public:
- // Get a list of indices with a value of 1 among the features
- template
- static void AppendActiveIndices(
- const Position& pos, TriggerEvent trigger, IndexListType active[2]) {
- for (const auto perspective :Colors) {
- Derived::CollectActiveIndices(
- pos, trigger, perspective, &active[perspective]);
- }
- }
+ // Base class of feature set
+ template
+ class FeatureSetBase {
- // Get a list of indices whose values have changed from the previous one in the feature quantity
- template
- static void AppendChangedIndices(
- const PositionType& pos, TriggerEvent trigger,
- IndexListType removed[2], IndexListType added[2], bool reset[2]) {
- const auto& dp = pos.state()->dirtyPiece;
- if (dp.dirty_num == 0) return;
+ public:
+ // Get a list of indices for active features
+ template
+ static void AppendActiveIndices(
+ const Position& pos, TriggerEvent trigger, IndexListType active[2]) {
- for (const auto perspective :Colors) {
- reset[perspective] = false;
- switch (trigger) {
- case TriggerEvent::kNone:
- break;
- case TriggerEvent::kFriendKingMoved:
- reset[perspective] =
- dp.pieceNo[0] == PIECE_NUMBER_KING + perspective;
- break;
- case TriggerEvent::kEnemyKingMoved:
- reset[perspective] =
- dp.pieceNo[0] == PIECE_NUMBER_KING + ~perspective;
- break;
- case TriggerEvent::kAnyKingMoved:
- reset[perspective] = dp.pieceNo[0] >= PIECE_NUMBER_KING;
- break;
- case TriggerEvent::kAnyPieceMoved:
- reset[perspective] = true;
- break;
- default:
- assert(false);
- break;
- }
- if (reset[perspective]) {
+ for (Color perspective : { WHITE, BLACK }) {
Derived::CollectActiveIndices(
- pos, trigger, perspective, &added[perspective]);
- } else {
- Derived::CollectChangedIndices(
- pos, trigger, perspective,
- &removed[perspective], &added[perspective]);
+ pos, trigger, perspective, &active[perspective]);
}
}
- }
-};
-// Class template that represents the feature set
-// do internal processing in reverse order of template arguments in order to linearize the amount of calculation at runtime
-template
-class FeatureSet :
+ // Get a list of indices for recently changed features
+ template
+ static void AppendChangedIndices(
+ const PositionType& pos, TriggerEvent trigger,
+ IndexListType removed[2], IndexListType added[2], bool reset[2]) {
+
+ const auto& dp = pos.state()->dirtyPiece;
+ if (dp.dirty_num == 0) return;
+
+ for (Color perspective : { WHITE, BLACK }) {
+ reset[perspective] = false;
+ switch (trigger) {
+ case TriggerEvent::kFriendKingMoved:
+ reset[perspective] =
+ dp.pieceId[0] == PIECE_ID_KING + perspective;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ if (reset[perspective]) {
+ Derived::CollectActiveIndices(
+ pos, trigger, perspective, &added[perspective]);
+ } else {
+ Derived::CollectChangedIndices(
+ pos, trigger, perspective,
+ &removed[perspective], &added[perspective]);
+ }
+ }
+ }
+ };
+
+ // Class template that represents the feature set
+ // do internal processing in reverse order of template arguments in order to linearize the amount of calculation at runtime
+ template
+ class FeatureSet :
public FeatureSetBase<
- FeatureSet> {
- private:
- using Head = FirstFeatureType;
- using Tail = FeatureSet;
+ FeatureSet> {
+ private:
+ using Head = FirstFeatureType;
+ using Tail = FeatureSet;
- public:
- // Hash value embedded in the evaluation function file
- static constexpr std::uint32_t kHashValue =
+ public:
+ // Hash value embedded in the evaluation function file
+ static constexpr std::uint32_t kHashValue =
Head::kHashValue ^ (Tail::kHashValue << 1) ^ (Tail::kHashValue >> 31);
- // number of feature dimensions
- static constexpr IndexType kDimensions =
+ // number of feature dimensions
+ static constexpr IndexType kDimensions =
Head::kDimensions + Tail::kDimensions;
- // The maximum value of the number of indexes whose value is 1 at the same time among the feature values
- static constexpr IndexType kMaxActiveDimensions =
+ // The maximum value of the number of indexes whose value is 1 at the same time among the feature values
+ static constexpr IndexType kMaxActiveDimensions =
Head::kMaxActiveDimensions + Tail::kMaxActiveDimensions;
- // List of timings to perform all calculations instead of difference calculation
- using SortedTriggerSet = typename InsertToSet::Result;
- static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues;
+ static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues;
- // Get the feature quantity name
- static std::string GetName() {
- return std::string(Head::kName) + "+" + Tail::GetName();
- }
+ // Get the feature quantity name
+ static std::string GetName() {
+ return std::string(Head::kName) + "+" + Tail::GetName();
+ }
- private:
- // Get a list of indices with a value of 1 among the features
- template
- static void CollectActiveIndices(
+ private:
+ // Get a list of indices with a value of 1 among the features
+ template
+ static void CollectActiveIndices(
const Position& pos, const TriggerEvent trigger, const Color perspective,
IndexListType* const active) {
- Tail::CollectActiveIndices(pos, trigger, perspective, active);
- if (Head::kRefreshTrigger == trigger) {
- const auto start = active->size();
- Head::AppendActiveIndices(pos, perspective, active);
- for (auto i = start; i < active->size(); ++i) {
- (*active)[i] += Tail::kDimensions;
+ Tail::CollectActiveIndices(pos, trigger, perspective, active);
+ if (Head::kRefreshTrigger == trigger) {
+ const auto start = active->size();
+ Head::AppendActiveIndices(pos, perspective, active);
+ for (auto i = start; i < active->size(); ++i) {
+ (*active)[i] += Tail::kDimensions;
+ }
}
}
- }
- // Get a list of indices whose values have changed from the previous one in the feature quantity
- template
- static void CollectChangedIndices(
+ // Get a list of indices whose values have changed from the previous one in the feature quantity
+ template
+ static void CollectChangedIndices(
const Position& pos, const TriggerEvent trigger, const Color perspective,
IndexListType* const removed, IndexListType* const added) {
- Tail::CollectChangedIndices(pos, trigger, perspective, removed, added);
- if (Head::kRefreshTrigger == trigger) {
- const auto start_removed = removed->size();
- const auto start_added = added->size();
- Head::AppendChangedIndices(pos, perspective, removed, added);
- for (auto i = start_removed; i < removed->size(); ++i) {
- (*removed)[i] += Tail::kDimensions;
- }
- for (auto i = start_added; i < added->size(); ++i) {
- (*added)[i] += Tail::kDimensions;
+ Tail::CollectChangedIndices(pos, trigger, perspective, removed, added);
+ if (Head::kRefreshTrigger == trigger) {
+ const auto start_removed = removed->size();
+ const auto start_added = added->size();
+ Head::AppendChangedIndices(pos, perspective, removed, added);
+ for (auto i = start_removed; i < removed->size(); ++i) {
+ (*removed)[i] += Tail::kDimensions;
+ }
+ for (auto i = start_added; i < added->size(); ++i) {
+ (*added)[i] += Tail::kDimensions;
+ }
}
}
- }
- // Make the base class and the class template that recursively uses itself a friend
- friend class FeatureSetBase;
- template
- friend class FeatureSet;
-};
+ // Make the base class and the class template that recursively uses itself a friend
+ friend class FeatureSetBase;
+ template
+ friend class FeatureSet;
+ };
-// Class template that represents the feature set
-// Specialization with one template argument
-template
-class FeatureSet : public FeatureSetBase> {
- public:
- // Hash value embedded in the evaluation function file
- static constexpr std::uint32_t kHashValue = FeatureType::kHashValue;
- // number of feature dimensions
- static constexpr IndexType kDimensions = FeatureType::kDimensions;
- // The maximum value of the number of indexes whose value is 1 at the same time among the feature values
- static constexpr IndexType kMaxActiveDimensions =
- FeatureType::kMaxActiveDimensions;
- // List of timings to perform all calculations instead of difference calculation
- using SortedTriggerSet =
- CompileTimeList;
- static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues;
+ // Class template that represents the feature set
+ template
+ class FeatureSet : public FeatureSetBase> {
- // Get the feature quantity name
- static std::string GetName() {
- return FeatureType::kName;
- }
+ public:
+ // Hash value embedded in the evaluation file
+ static constexpr std::uint32_t kHashValue = FeatureType::kHashValue;
+ // Number of feature dimensions
+ static constexpr IndexType kDimensions = FeatureType::kDimensions;
+ // Maximum number of simultaneously active features
+ static constexpr IndexType kMaxActiveDimensions =
+ FeatureType::kMaxActiveDimensions;
+ // Trigger for full calculation instead of difference calculation
+ using SortedTriggerSet =
+ CompileTimeList;
+ static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues;
- private:
- // Get a list of indices with a value of 1 among the features
- static void CollectActiveIndices(
- const Position& pos, const TriggerEvent trigger, const Color perspective,
- IndexList* const active) {
- if (FeatureType::kRefreshTrigger == trigger) {
- FeatureType::AppendActiveIndices(pos, perspective, active);
+ // Get the feature quantity name
+ static std::string GetName() {
+ return FeatureType::kName;
}
- }
- // Get a list of indices whose values have changed from the previous one in the feature quantity
- static void CollectChangedIndices(
- const Position& pos, const TriggerEvent trigger, const Color perspective,
- IndexList* const removed, IndexList* const added) {
- if (FeatureType::kRefreshTrigger == trigger) {
- FeatureType::AppendChangedIndices(pos, perspective, removed, added);
+ private:
+ // Get a list of indices for active features
+ static void CollectActiveIndices(
+ const Position& pos, const TriggerEvent trigger, const Color perspective,
+ IndexList* const active) {
+ if (FeatureType::kRefreshTrigger == trigger) {
+ FeatureType::AppendActiveIndices(pos, perspective, active);
+ }
}
- }
- // Make the base class and the class template that recursively uses itself a friend
- friend class FeatureSetBase;
- template
- friend class FeatureSet;
-};
+ // Get a list of indices for recently changed features
+ static void CollectChangedIndices(
+ const Position& pos, const TriggerEvent trigger, const Color perspective,
+ IndexList* const removed, IndexList* const added) {
-} // namespace Features
+ if (FeatureType::kRefreshTrigger == trigger) {
+ FeatureType::AppendChangedIndices(pos, perspective, removed, added);
+ }
+ }
-} // namespace NNUE
+ // Make the base class and the class template that recursively uses itself a friend
+ friend class FeatureSetBase;
+ template
+ friend class FeatureSet;
+ };
-} // namespace Eval
+} // namespace Eval::NNUE::Features
-#endif // defined(EVAL_NNUE)
-
-#endif
+#endif // #ifndef NNUE_FEATURE_SET_H_INCLUDED
diff --git a/src/nnue/features/features_common.h b/src/nnue/features/features_common.h
index 2fabbd4f..3377cd8f 100644
--- a/src/nnue/features/features_common.h
+++ b/src/nnue/features/features_common.h
@@ -1,47 +1,50 @@
-//Common header of input features of NNUE evaluation function
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
-#ifndef _NNUE_FEATURES_COMMON_H_
-#define _NNUE_FEATURES_COMMON_H_
+ 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.
-#if defined(EVAL_NNUE)
+ 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 .
+*/
+
+//Common header of input features of NNUE evaluation function
+
+#ifndef NNUE_FEATURES_COMMON_H_INCLUDED
+#define NNUE_FEATURES_COMMON_H_INCLUDED
#include "../../evaluate.h"
#include "../nnue_common.h"
-namespace Eval {
+namespace Eval::NNUE::Features {
-namespace NNUE {
+ class IndexList;
-namespace Features {
+ template
+ class FeatureSet;
-// Index list type
-class IndexList;
+ // Trigger to perform full calculations instead of difference only
+ enum class TriggerEvent {
+ kNone, // Calculate the difference whenever possible
+ kFriendKingMoved, // calculate all when own ball moves
+ kEnemyKingMoved, // do all calculations when enemy balls move
+ kAnyKingMoved, // do all calculations if either ball moves
+ kAnyPieceMoved, // always do all calculations
+ };
-// Class template that represents the feature set
-template
-class FeatureSet;
+ enum class Side {
+ kFriend, // side to move
+ kEnemy, // opponent
+ };
-// Type of timing to perform all calculations instead of difference calculation
-enum class TriggerEvent {
- kNone, // Calculate the difference whenever possible
- kFriendKingMoved, // calculate all when own ball moves
- kEnemyKingMoved, // do all calculations when enemy balls move
- kAnyKingMoved, // do all calculations if either ball moves
- kAnyPieceMoved, // always do all calculations
-};
+} // namespace Eval::NNUE::Features
-// turn side or other side
-enum class Side {
- kFriend, // turn side
- kEnemy, // opponent
-};
-
-} // namespace Features
-
-} // namespace NNUE
-
-} // namespace Eval
-
-#endif // defined(EVAL_NNUE)
-
-#endif
+#endif // #ifndef NNUE_FEATURES_COMMON_H_INCLUDED
diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp
index cba2c9cd..628add6e 100644
--- a/src/nnue/features/half_kp.cpp
+++ b/src/nnue/features/half_kp.cpp
@@ -1,84 +1,92 @@
-//Definition of input features HalfKP of NNUE evaluation function
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
-#if defined(EVAL_NNUE)
+ 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 .
+*/
+
+//Definition of input features HalfKP of NNUE evaluation function
#include "half_kp.h"
#include "index_list.h"
-namespace Eval {
+namespace Eval::NNUE::Features {
-namespace NNUE {
+ // Find the index of the feature quantity from the king position and PieceSquare
+ template
+ inline IndexType HalfKP::MakeIndex(Square sq_k, PieceSquare p) {
+ return static_cast(PS_END) * static_cast(sq_k) + p;
+ }
-namespace Features {
+ // Get pieces information
+ template
+ inline void HalfKP::GetPieces(
+ const Position& pos, Color perspective,
+ PieceSquare** pieces, Square* sq_target_k) {
-// Find the index of the feature quantity from the ball position and BonaPiece
-template
-inline IndexType HalfKP::MakeIndex(Square sq_k, BonaPiece p) {
- return static_cast(fe_end) * static_cast(sq_k) + p;
-}
+ *pieces = (perspective == BLACK) ?
+ pos.eval_list()->piece_list_fb() :
+ pos.eval_list()->piece_list_fw();
+ const PieceId target = (AssociatedKing == Side::kFriend) ?
+ static_cast(PIECE_ID_KING + perspective) :
+ static_cast(PIECE_ID_KING + ~perspective);
+ *sq_target_k = static_cast(((*pieces)[target] - PS_W_KING) % SQUARE_NB);
+ }
-// Get the piece information
-template
-inline void HalfKP::GetPieces(
- const Position& pos, Color perspective,
- BonaPiece** pieces, Square* sq_target_k) {
- *pieces = (perspective == BLACK) ?
- pos.eval_list()->piece_list_fb() :
- pos.eval_list()->piece_list_fw();
- const PieceNumber target = (AssociatedKing == Side::kFriend) ?
- static_cast(PIECE_NUMBER_KING + perspective) :
- static_cast(PIECE_NUMBER_KING + ~perspective);
- *sq_target_k = static_cast(((*pieces)[target] - f_king) % SQUARE_NB);
-}
+ // Get a list of indices for active features
+ template
+ void HalfKP::AppendActiveIndices(
+ const Position& pos, Color perspective, IndexList* active) {
-// Get a list of indices with a value of 1 among the features
-template
-void HalfKP::AppendActiveIndices(
- const Position& pos, Color perspective, IndexList* active) {
- // do nothing if array size is small to avoid compiler warning
- if (RawFeatures::kMaxActiveDimensions < kMaxActiveDimensions) return;
+ // Do nothing if array size is small to avoid compiler warning
+ if (RawFeatures::kMaxActiveDimensions < kMaxActiveDimensions) return;
- BonaPiece* pieces;
- Square sq_target_k;
- GetPieces(pos, perspective, &pieces, &sq_target_k);
- for (PieceNumber i = PIECE_NUMBER_ZERO; i < PIECE_NUMBER_KING; ++i) {
- if (pieces[i] != Eval::BONA_PIECE_ZERO) {
- active->push_back(MakeIndex(sq_target_k, pieces[i]));
+ PieceSquare* pieces;
+ Square sq_target_k;
+ GetPieces(pos, perspective, &pieces, &sq_target_k);
+ for (PieceId i = PIECE_ID_ZERO; i < PIECE_ID_KING; ++i) {
+ if (pieces[i] != PS_NONE) {
+ active->push_back(MakeIndex(sq_target_k, pieces[i]));
+ }
}
}
-}
-// Get a list of indices whose values have changed from the previous one in the feature quantity
-template
-void HalfKP::AppendChangedIndices(
- const Position& pos, Color perspective,
- IndexList* removed, IndexList* added) {
- BonaPiece* pieces;
- Square sq_target_k;
- GetPieces(pos, perspective, &pieces, &sq_target_k);
- const auto& dp = pos.state()->dirtyPiece;
- for (int i = 0; i < dp.dirty_num; ++i) {
- if (dp.pieceNo[i] >= PIECE_NUMBER_KING) continue;
- const auto old_p = static_cast(
- dp.changed_piece[i].old_piece.from[perspective]);
- if (old_p != Eval::BONA_PIECE_ZERO) {
- removed->push_back(MakeIndex(sq_target_k, old_p));
- }
- const auto new_p = static_cast(
- dp.changed_piece[i].new_piece.from[perspective]);
- if (new_p != Eval::BONA_PIECE_ZERO) {
- added->push_back(MakeIndex(sq_target_k, new_p));
+ // Get a list of indices for recently changed features
+ template
+ void HalfKP::AppendChangedIndices(
+ const Position& pos, Color perspective,
+ IndexList* removed, IndexList* added) {
+
+ PieceSquare* pieces;
+ Square sq_target_k;
+ GetPieces(pos, perspective, &pieces, &sq_target_k);
+ const auto& dp = pos.state()->dirtyPiece;
+ for (int i = 0; i < dp.dirty_num; ++i) {
+ if (dp.pieceId[i] >= PIECE_ID_KING) continue;
+ const auto old_p = static_cast(
+ dp.old_piece[i].from[perspective]);
+ if (old_p != PS_NONE) {
+ removed->push_back(MakeIndex(sq_target_k, old_p));
+ }
+ const auto new_p = static_cast(
+ dp.new_piece[i].from[perspective]);
+ if (new_p != PS_NONE) {
+ added->push_back(MakeIndex(sq_target_k, new_p));
+ }
}
}
-}
-template class HalfKP;
-template class HalfKP;
+ template class HalfKP;
-} // namespace Features
-
-} // namespace NNUE
-
-} // namespace Eval
-
-#endif // defined(EVAL_NNUE)
+} // namespace Eval::NNUE::Features
diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h
index d2e8e521..99842eea 100644
--- a/src/nnue/features/half_kp.h
+++ b/src/nnue/features/half_kp.h
@@ -1,62 +1,67 @@
-//Definition of input features HalfKP of NNUE evaluation function
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
-#ifndef _NNUE_FEATURES_HALF_KP_H_
-#define _NNUE_FEATURES_HALF_KP_H_
+ 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.
-#if defined(EVAL_NNUE)
+ 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 .
+*/
+
+//Definition of input features HalfKP of NNUE evaluation function
+
+#ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED
+#define NNUE_FEATURES_HALF_KP_H_INCLUDED
#include "../../evaluate.h"
#include "features_common.h"
-namespace Eval {
+namespace Eval::NNUE::Features {
-namespace NNUE {
+ // Feature HalfKP: Combination of the position of own king
+ // and the position of pieces other than kings
+ template
+ class HalfKP {
-namespace Features {
+ public:
+ // Feature name
+ static constexpr const char* kName = "HalfKP(Friend)";
+ // Hash value embedded in the evaluation file
+ static constexpr std::uint32_t kHashValue =
+ 0x5D69D5B9u ^ (AssociatedKing == Side::kFriend);
+ // Number of feature dimensions
+ static constexpr IndexType kDimensions =
+ static_cast(SQUARE_NB) * static_cast(PS_END);
+ // Maximum number of simultaneously active features
+ static constexpr IndexType kMaxActiveDimensions = PIECE_ID_KING;
+ // Trigger for full calculation instead of difference calculation
+ static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved;
-// Feature HalfKP: Combination of the position of own ball or enemy ball and the position of pieces other than balls
-template
-class HalfKP {
- public:
- // feature quantity name
- static constexpr const char* kName =
- (AssociatedKing == Side::kFriend) ? "HalfKP(Friend)" : "HalfKP(Enemy)";
- // Hash value embedded in the evaluation function file
- static constexpr std::uint32_t kHashValue =
- 0x5D69D5B9u ^ (AssociatedKing == Side::kFriend);
- // number of feature dimensions
- static constexpr IndexType kDimensions =
- static_cast(SQUARE_NB) * static_cast(fe_end);
- // The maximum value of the number of indexes whose value is 1 at the same time among the feature values
- static constexpr IndexType kMaxActiveDimensions = PIECE_NUMBER_KING;
- // Timing of full calculation instead of difference calculation
- static constexpr TriggerEvent kRefreshTrigger =
- (AssociatedKing == Side::kFriend) ?
- TriggerEvent::kFriendKingMoved : TriggerEvent::kEnemyKingMoved;
+ // Get a list of indices for active features
+ static void AppendActiveIndices(const Position& pos, Color perspective,
+ IndexList* active);
- // Get a list of indices with a value of 1 among the features
- static void AppendActiveIndices(const Position& pos, Color perspective,
- IndexList* active);
+ // Get a list of indices for recently changed features
+ static void AppendChangedIndices(const Position& pos, Color perspective,
+ IndexList* removed, IndexList* added);
- // Get a list of indices whose values have changed from the previous one in the feature quantity
- static void AppendChangedIndices(const Position& pos, Color perspective,
- IndexList* removed, IndexList* added);
+ // Index of a feature for a given king position and another piece on some square
+ static IndexType MakeIndex(Square sq_k, PieceSquare p);
- // Find the index of the feature quantity from the ball position and BonaPiece
- static IndexType MakeIndex(Square sq_k, BonaPiece p);
+ private:
+ // Get pieces information
+ static void GetPieces(const Position& pos, Color perspective,
+ PieceSquare** pieces, Square* sq_target_k);
+ };
- private:
- // Get the piece information
- static void GetPieces(const Position& pos, Color perspective,
- BonaPiece** pieces, Square* sq_target_k);
-};
+} // namespace Eval::NNUE::Features
-} // namespace Features
-
-} // namespace NNUE
-
-} // namespace Eval
-
-#endif // defined(EVAL_NNUE)
-
-#endif
+#endif // #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED
diff --git a/src/nnue/features/half_relative_kp.cpp b/src/nnue/features/half_relative_kp.cpp
index 623b839c..7f15ff39 100644
--- a/src/nnue/features/half_relative_kp.cpp
+++ b/src/nnue/features/half_relative_kp.cpp
@@ -11,14 +11,14 @@ namespace NNUE {
namespace Features {
-// Find the index of the feature quantity from the ball position and BonaPiece
+// Find the index of the feature quantity from the ball position and PieceSquare
template
inline IndexType HalfRelativeKP::MakeIndex(
- Square sq_k, BonaPiece p) {
+ Square sq_k, PieceSquare p) {
constexpr IndexType W = kBoardWidth;
constexpr IndexType H = kBoardHeight;
- const IndexType piece_index = (p - fe_hand_end) / SQUARE_NB;
- const Square sq_p = static_cast((p - fe_hand_end) % SQUARE_NB);
+ const IndexType piece_index = (p - PieceSquare::PS_W_PAWN) / SQUARE_NB;
+ const Square sq_p = static_cast((p - PieceSquare::PS_W_PAWN) % SQUARE_NB);
const IndexType relative_file = file_of(sq_p) - file_of(sq_k) + (W / 2);
const IndexType relative_rank = rank_of(sq_p) - rank_of(sq_k) + (H / 2);
return H * W * piece_index + H * relative_file + relative_rank;
@@ -28,14 +28,14 @@ inline IndexType HalfRelativeKP