mikereations commited on
Commit
bda2ed7
1 Parent(s): c4088d6

Upload 57 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
src/.DS_Store ADDED
Binary file (6.15 kB). View file
 
src/Makefile ADDED
@@ -0,0 +1,1009 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Stockfish, a UCI chess playing engine derived from Glaurung 2.1
2
+ # Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
3
+ #
4
+ # Stockfish is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # Stockfish is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+
18
+ ### ==========================================================================
19
+ ### Section 1. General Configuration
20
+ ### ==========================================================================
21
+
22
+ ### Establish the operating system name
23
+ KERNEL = $(shell uname -s)
24
+ ifeq ($(KERNEL),Linux)
25
+ OS = $(shell uname -o)
26
+ endif
27
+
28
+ ### Target Windows OS
29
+ ifeq ($(OS),Windows_NT)
30
+ ifneq ($(COMP),ndk)
31
+ target_windows = yes
32
+ endif
33
+ else ifeq ($(COMP),mingw)
34
+ target_windows = yes
35
+ ifeq ($(WINE_PATH),)
36
+ WINE_PATH = $(shell which wine)
37
+ endif
38
+ endif
39
+
40
+ ### Executable name
41
+ ifeq ($(target_windows),yes)
42
+ EXE = stockfish.exe
43
+ else
44
+ EXE = stockfish
45
+ endif
46
+
47
+ ### Installation dir definitions
48
+ PREFIX = /usr/local
49
+ BINDIR = $(PREFIX)/bin
50
+
51
+ ### Built-in benchmark for pgo-builds
52
+ ifeq ($(SDE_PATH),)
53
+ PGOBENCH = $(WINE_PATH) ./$(EXE) bench
54
+ else
55
+ PGOBENCH = $(SDE_PATH) -- $(WINE_PATH) ./$(EXE) bench
56
+ endif
57
+
58
+ ### Source and object files
59
+ SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \
60
+ material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \
61
+ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
62
+ nnue/evaluate_nnue.cpp nnue/features/half_ka_v2_hm.cpp
63
+
64
+ OBJS = $(notdir $(SRCS:.cpp=.o))
65
+
66
+ VPATH = syzygy:nnue:nnue/features
67
+
68
+ ### ==========================================================================
69
+ ### Section 2. High-level Configuration
70
+ ### ==========================================================================
71
+ #
72
+ # flag --- Comp switch --- Description
73
+ # ----------------------------------------------------------------------------
74
+ #
75
+ # debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode
76
+ # sanitize = none/<sanitizer> ... (-fsanitize )
77
+ # --- ( undefined ) --- enable undefined behavior checks
78
+ # --- ( thread ) --- enable threading error checks
79
+ # --- ( address ) --- enable memory access checks
80
+ # --- ...etc... --- see compiler documentation for supported sanitizers
81
+ # optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations
82
+ # arch = (name) --- (-arch) --- Target architecture
83
+ # bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
84
+ # prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction
85
+ # popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction
86
+ # pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction
87
+ # sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions
88
+ # mmx = yes/no --- -mmmx --- Use Intel MMX instructions
89
+ # sse2 = yes/no --- -msse2 --- Use Intel Streaming SIMD Extensions 2
90
+ # ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3
91
+ # sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1
92
+ # avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2
93
+ # avxvnni = yes/no --- -mavxvnni --- Use Intel Vector Neural Network Instructions AVX
94
+ # avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512
95
+ # vnni256 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 256
96
+ # vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512
97
+ # neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture
98
+ #
99
+ # Note that Makefile is space sensitive, so when adding new architectures
100
+ # or modifying existing flags, you have to make sure there are no extra spaces
101
+ # at the end of the line for flag values.
102
+ #
103
+ # Example of use for these flags:
104
+ # make build ARCH=x86-64-avx512 debug=yes sanitize="address undefined"
105
+
106
+
107
+ ### 2.1. General and architecture defaults
108
+
109
+ ifeq ($(ARCH),)
110
+ ARCH = x86-64-modern
111
+ help_skip_sanity = yes
112
+ endif
113
+ # explicitly check for the list of supported architectures (as listed with make help),
114
+ # the user can override with `make ARCH=x86-32-vnni256 SUPPORTED_ARCH=true`
115
+ ifeq ($(ARCH), $(filter $(ARCH), \
116
+ x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \
117
+ x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \
118
+ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \
119
+ armv7 armv7-neon armv8 apple-silicon general-64 general-32 riscv64))
120
+ SUPPORTED_ARCH=true
121
+ else
122
+ SUPPORTED_ARCH=false
123
+ endif
124
+
125
+ optimize = yes
126
+ debug = no
127
+ sanitize = none
128
+ bits = 64
129
+ prefetch = no
130
+ popcnt = no
131
+ pext = no
132
+ sse = no
133
+ mmx = no
134
+ sse2 = no
135
+ ssse3 = no
136
+ sse41 = no
137
+ avx2 = no
138
+ avxvnni = no
139
+ avx512 = no
140
+ vnni256 = no
141
+ vnni512 = no
142
+ neon = no
143
+ arm_version = 0
144
+ STRIP = strip
145
+
146
+ ### 2.2 Architecture specific
147
+
148
+ ifeq ($(findstring x86,$(ARCH)),x86)
149
+
150
+ # x86-32/64
151
+
152
+ ifeq ($(findstring x86-32,$(ARCH)),x86-32)
153
+ arch = i386
154
+ bits = 32
155
+ sse = no
156
+ mmx = yes
157
+ else
158
+ arch = x86_64
159
+ sse = yes
160
+ sse2 = yes
161
+ endif
162
+
163
+ ifeq ($(findstring -sse,$(ARCH)),-sse)
164
+ sse = yes
165
+ endif
166
+
167
+ ifeq ($(findstring -popcnt,$(ARCH)),-popcnt)
168
+ popcnt = yes
169
+ endif
170
+
171
+ ifeq ($(findstring -mmx,$(ARCH)),-mmx)
172
+ mmx = yes
173
+ endif
174
+
175
+ ifeq ($(findstring -sse2,$(ARCH)),-sse2)
176
+ sse = yes
177
+ sse2 = yes
178
+ endif
179
+
180
+ ifeq ($(findstring -ssse3,$(ARCH)),-ssse3)
181
+ sse = yes
182
+ sse2 = yes
183
+ ssse3 = yes
184
+ endif
185
+
186
+ ifeq ($(findstring -sse41,$(ARCH)),-sse41)
187
+ sse = yes
188
+ sse2 = yes
189
+ ssse3 = yes
190
+ sse41 = yes
191
+ endif
192
+
193
+ ifeq ($(findstring -modern,$(ARCH)),-modern)
194
+ popcnt = yes
195
+ sse = yes
196
+ sse2 = yes
197
+ ssse3 = yes
198
+ sse41 = yes
199
+ endif
200
+
201
+ ifeq ($(findstring -avx2,$(ARCH)),-avx2)
202
+ popcnt = yes
203
+ sse = yes
204
+ sse2 = yes
205
+ ssse3 = yes
206
+ sse41 = yes
207
+ avx2 = yes
208
+ endif
209
+
210
+ ifeq ($(findstring -avxvnni,$(ARCH)),-avxvnni)
211
+ popcnt = yes
212
+ sse = yes
213
+ sse2 = yes
214
+ ssse3 = yes
215
+ sse41 = yes
216
+ avx2 = yes
217
+ avxvnni = yes
218
+ pext = yes
219
+ endif
220
+
221
+ ifeq ($(findstring -bmi2,$(ARCH)),-bmi2)
222
+ popcnt = yes
223
+ sse = yes
224
+ sse2 = yes
225
+ ssse3 = yes
226
+ sse41 = yes
227
+ avx2 = yes
228
+ pext = yes
229
+ endif
230
+
231
+ ifeq ($(findstring -avx512,$(ARCH)),-avx512)
232
+ popcnt = yes
233
+ sse = yes
234
+ sse2 = yes
235
+ ssse3 = yes
236
+ sse41 = yes
237
+ avx2 = yes
238
+ pext = yes
239
+ avx512 = yes
240
+ endif
241
+
242
+ ifeq ($(findstring -vnni256,$(ARCH)),-vnni256)
243
+ popcnt = yes
244
+ sse = yes
245
+ sse2 = yes
246
+ ssse3 = yes
247
+ sse41 = yes
248
+ avx2 = yes
249
+ pext = yes
250
+ vnni256 = yes
251
+ endif
252
+
253
+ ifeq ($(findstring -vnni512,$(ARCH)),-vnni512)
254
+ popcnt = yes
255
+ sse = yes
256
+ sse2 = yes
257
+ ssse3 = yes
258
+ sse41 = yes
259
+ avx2 = yes
260
+ pext = yes
261
+ avx512 = yes
262
+ vnni512 = yes
263
+ endif
264
+
265
+ ifeq ($(sse),yes)
266
+ prefetch = yes
267
+ endif
268
+
269
+ # 64-bit pext is not available on x86-32
270
+ ifeq ($(bits),32)
271
+ pext = no
272
+ endif
273
+
274
+ else
275
+
276
+ # all other architectures
277
+
278
+ ifeq ($(ARCH),general-32)
279
+ arch = any
280
+ bits = 32
281
+ endif
282
+
283
+ ifeq ($(ARCH),general-64)
284
+ arch = any
285
+ endif
286
+
287
+ ifeq ($(ARCH),armv7)
288
+ arch = armv7
289
+ prefetch = yes
290
+ bits = 32
291
+ arm_version = 7
292
+ endif
293
+
294
+ ifeq ($(ARCH),armv7-neon)
295
+ arch = armv7
296
+ prefetch = yes
297
+ popcnt = yes
298
+ neon = yes
299
+ bits = 32
300
+ arm_version = 7
301
+ endif
302
+
303
+ ifeq ($(ARCH),armv8)
304
+ arch = armv8
305
+ prefetch = yes
306
+ popcnt = yes
307
+ neon = yes
308
+ arm_version = 8
309
+ endif
310
+
311
+ ifeq ($(ARCH),apple-silicon)
312
+ arch = arm64
313
+ prefetch = yes
314
+ popcnt = yes
315
+ neon = yes
316
+ arm_version = 8
317
+ endif
318
+
319
+ ifeq ($(ARCH),ppc-32)
320
+ arch = ppc
321
+ bits = 32
322
+ endif
323
+
324
+ ifeq ($(ARCH),ppc-64)
325
+ arch = ppc64
326
+ popcnt = yes
327
+ prefetch = yes
328
+ endif
329
+
330
+ ifeq ($(findstring e2k,$(ARCH)),e2k)
331
+ arch = e2k
332
+ mmx = yes
333
+ bits = 64
334
+ sse = yes
335
+ sse2 = yes
336
+ ssse3 = yes
337
+ sse41 = yes
338
+ popcnt = yes
339
+ endif
340
+
341
+ ifeq ($(ARCH),riscv64)
342
+ arch = riscv64
343
+ endif
344
+ endif
345
+
346
+
347
+ ### ==========================================================================
348
+ ### Section 3. Low-level Configuration
349
+ ### ==========================================================================
350
+
351
+ ### 3.1 Selecting compiler (default = gcc)
352
+ ifeq ($(MAKELEVEL),0)
353
+ export ENV_CXXFLAGS := $(CXXFLAGS)
354
+ export ENV_DEPENDFLAGS := $(DEPENDFLAGS)
355
+ export ENV_LDFLAGS := $(LDFLAGS)
356
+ endif
357
+
358
+ CXXFLAGS = $(ENV_CXXFLAGS) -Wall -Wcast-qual -fno-exceptions -std=c++17 $(EXTRACXXFLAGS)
359
+ DEPENDFLAGS = $(ENV_DEPENDFLAGS) -std=c++17
360
+ LDFLAGS = $(ENV_LDFLAGS) $(EXTRALDFLAGS)
361
+
362
+ ifeq ($(COMP),)
363
+ COMP=gcc
364
+ endif
365
+
366
+ ifeq ($(COMP),gcc)
367
+ comp=gcc
368
+ CXX=g++
369
+ CXXFLAGS += -pedantic -Wextra -Wshadow
370
+
371
+ ifeq ($(arch),$(filter $(arch),armv7 armv8 riscv64))
372
+ ifeq ($(OS),Android)
373
+ CXXFLAGS += -m$(bits)
374
+ LDFLAGS += -m$(bits)
375
+ endif
376
+ ifeq ($(ARCH),riscv64)
377
+ CXXFLAGS += -latomic
378
+ endif
379
+ else
380
+ CXXFLAGS += -m$(bits)
381
+ LDFLAGS += -m$(bits)
382
+ endif
383
+
384
+ ifeq ($(arch),$(filter $(arch),armv7))
385
+ LDFLAGS += -latomic
386
+ endif
387
+
388
+ ifneq ($(KERNEL),Darwin)
389
+ LDFLAGS += -Wl,--no-as-needed
390
+ endif
391
+ endif
392
+
393
+ ifeq ($(target_windows),yes)
394
+ LDFLAGS += -static
395
+ endif
396
+
397
+ ifeq ($(COMP),mingw)
398
+ comp=mingw
399
+
400
+ ifeq ($(bits),64)
401
+ ifeq ($(shell which x86_64-w64-mingw32-c++-posix 2> /dev/null),)
402
+ CXX=x86_64-w64-mingw32-c++
403
+ else
404
+ CXX=x86_64-w64-mingw32-c++-posix
405
+ endif
406
+ else
407
+ ifeq ($(shell which i686-w64-mingw32-c++-posix 2> /dev/null),)
408
+ CXX=i686-w64-mingw32-c++
409
+ else
410
+ CXX=i686-w64-mingw32-c++-posix
411
+ endif
412
+ endif
413
+ CXXFLAGS += -pedantic -Wextra -Wshadow
414
+ endif
415
+
416
+ ifeq ($(COMP),icc)
417
+ comp=icc
418
+ CXX=icpc
419
+ CXXFLAGS += -diag-disable 1476,10120 -Wcheck -Wabi -Wdeprecated -strict-ansi
420
+ endif
421
+
422
+ ifeq ($(COMP),clang)
423
+ comp=clang
424
+ CXX=clang++
425
+ ifeq ($(target_windows),yes)
426
+ CXX=x86_64-w64-mingw32-clang++
427
+ endif
428
+
429
+ CXXFLAGS += -pedantic -Wextra -Wshadow
430
+
431
+ ifeq ($(filter $(KERNEL),Darwin OpenBSD FreeBSD),)
432
+ ifeq ($(target_windows),)
433
+ ifneq ($(RTLIB),compiler-rt)
434
+ LDFLAGS += -latomic
435
+ endif
436
+ endif
437
+ endif
438
+
439
+ ifeq ($(arch),$(filter $(arch),armv7 armv8 riscv64))
440
+ ifeq ($(OS),Android)
441
+ CXXFLAGS += -m$(bits)
442
+ LDFLAGS += -m$(bits)
443
+ endif
444
+ ifeq ($(ARCH),riscv64)
445
+ CXXFLAGS += -latomic
446
+ endif
447
+ else
448
+ CXXFLAGS += -m$(bits)
449
+ LDFLAGS += -m$(bits)
450
+ endif
451
+ endif
452
+
453
+ ifeq ($(KERNEL),Darwin)
454
+ CXXFLAGS += -mmacosx-version-min=10.14
455
+ LDFLAGS += -mmacosx-version-min=10.14
456
+ ifneq ($(arch),any)
457
+ CXXFLAGS += -arch $(arch)
458
+ LDFLAGS += -arch $(arch)
459
+ endif
460
+ XCRUN = xcrun
461
+ endif
462
+
463
+ # To cross-compile for Android, NDK version r21 or later is recommended.
464
+ # In earlier NDK versions, you'll need to pass -fno-addrsig if using GNU binutils.
465
+ # Currently we don't know how to make PGO builds with the NDK yet.
466
+ ifeq ($(COMP),ndk)
467
+ CXXFLAGS += -stdlib=libc++ -fPIE
468
+ comp=clang
469
+ ifeq ($(arch),armv7)
470
+ CXX=armv7a-linux-androideabi16-clang++
471
+ CXXFLAGS += -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=neon
472
+ ifneq ($(shell which arm-linux-androideabi-strip 2>/dev/null),)
473
+ STRIP=arm-linux-androideabi-strip
474
+ else
475
+ STRIP=llvm-strip
476
+ endif
477
+ endif
478
+ ifeq ($(arch),armv8)
479
+ CXX=aarch64-linux-android21-clang++
480
+ ifneq ($(shell which aarch64-linux-android-strip 2>/dev/null),)
481
+ STRIP=aarch64-linux-android-strip
482
+ else
483
+ STRIP=llvm-strip
484
+ endif
485
+ endif
486
+ LDFLAGS += -static-libstdc++ -pie -lm -latomic
487
+ endif
488
+
489
+ ifeq ($(comp),icc)
490
+ profile_make = icc-profile-make
491
+ profile_use = icc-profile-use
492
+ else ifeq ($(comp),clang)
493
+ profile_make = clang-profile-make
494
+ profile_use = clang-profile-use
495
+ else
496
+ profile_make = gcc-profile-make
497
+ profile_use = gcc-profile-use
498
+ ifeq ($(KERNEL),Darwin)
499
+ EXTRAPROFILEFLAGS = -fvisibility=hidden
500
+ endif
501
+ endif
502
+
503
+ ### Travis CI script uses COMPILER to overwrite CXX
504
+ ifdef COMPILER
505
+ COMPCXX=$(COMPILER)
506
+ endif
507
+
508
+ ### Allow overwriting CXX from command line
509
+ ifdef COMPCXX
510
+ CXX=$(COMPCXX)
511
+ endif
512
+
513
+ ### Sometimes gcc is really clang
514
+ ifeq ($(COMP),gcc)
515
+ gccversion = $(shell $(CXX) --version)
516
+ gccisclang = $(findstring clang,$(gccversion))
517
+ ifneq ($(gccisclang),)
518
+ profile_make = clang-profile-make
519
+ profile_use = clang-profile-use
520
+ endif
521
+ endif
522
+
523
+ ### On mingw use Windows threads, otherwise POSIX
524
+ ifneq ($(comp),mingw)
525
+ CXXFLAGS += -DUSE_PTHREADS
526
+ # On Android Bionic's C library comes with its own pthread implementation bundled in
527
+ ifneq ($(OS),Android)
528
+ # Haiku has pthreads in its libroot, so only link it in on other platforms
529
+ ifneq ($(KERNEL),Haiku)
530
+ ifneq ($(COMP),ndk)
531
+ LDFLAGS += -lpthread
532
+ endif
533
+ endif
534
+ endif
535
+ endif
536
+
537
+ ### 3.2.1 Debugging
538
+ ifeq ($(debug),no)
539
+ CXXFLAGS += -DNDEBUG
540
+ else
541
+ CXXFLAGS += -g
542
+ endif
543
+
544
+ ### 3.2.2 Debugging with undefined behavior sanitizers
545
+ ifneq ($(sanitize),none)
546
+ CXXFLAGS += -g3 $(addprefix -fsanitize=,$(sanitize))
547
+ LDFLAGS += $(addprefix -fsanitize=,$(sanitize))
548
+ endif
549
+
550
+ ### 3.3 Optimization
551
+ ifeq ($(optimize),yes)
552
+
553
+ CXXFLAGS += -O3
554
+
555
+ ifeq ($(comp),gcc)
556
+ ifeq ($(OS), Android)
557
+ CXXFLAGS += -fno-gcse -mthumb -march=armv7-a -mfloat-abi=softfp
558
+ endif
559
+ endif
560
+
561
+ ifeq ($(KERNEL),Darwin)
562
+ ifeq ($(comp),$(filter $(comp),clang icc))
563
+ CXXFLAGS += -mdynamic-no-pic
564
+ endif
565
+
566
+ ifeq ($(comp),gcc)
567
+ ifneq ($(arch),arm64)
568
+ CXXFLAGS += -mdynamic-no-pic
569
+ endif
570
+ endif
571
+ endif
572
+
573
+ ifeq ($(comp),clang)
574
+ CXXFLAGS += -fexperimental-new-pass-manager
575
+ endif
576
+ endif
577
+
578
+ ### 3.4 Bits
579
+ ifeq ($(bits),64)
580
+ CXXFLAGS += -DIS_64BIT
581
+ endif
582
+
583
+ ### 3.5 prefetch and popcount
584
+ ifeq ($(prefetch),yes)
585
+ ifeq ($(sse),yes)
586
+ CXXFLAGS += -msse
587
+ endif
588
+ else
589
+ CXXFLAGS += -DNO_PREFETCH
590
+ endif
591
+
592
+ ifeq ($(popcnt),yes)
593
+ ifeq ($(arch),$(filter $(arch),ppc64 armv7 armv8 arm64))
594
+ CXXFLAGS += -DUSE_POPCNT
595
+ else ifeq ($(comp),icc)
596
+ CXXFLAGS += -msse3 -DUSE_POPCNT
597
+ else
598
+ CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT
599
+ endif
600
+ endif
601
+
602
+ ### 3.6 SIMD architectures
603
+ ifeq ($(avx2),yes)
604
+ CXXFLAGS += -DUSE_AVX2
605
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
606
+ CXXFLAGS += -mavx2 -mbmi
607
+ endif
608
+ endif
609
+
610
+ ifeq ($(avxvnni),yes)
611
+ CXXFLAGS += -DUSE_VNNI -DUSE_AVXVNNI
612
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
613
+ CXXFLAGS += -mavxvnni
614
+ endif
615
+ endif
616
+
617
+ ifeq ($(avx512),yes)
618
+ CXXFLAGS += -DUSE_AVX512
619
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
620
+ CXXFLAGS += -mavx512f -mavx512bw
621
+ endif
622
+ endif
623
+
624
+ ifeq ($(vnni256),yes)
625
+ CXXFLAGS += -DUSE_VNNI
626
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
627
+ CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl -mprefer-vector-width=256
628
+ endif
629
+ endif
630
+
631
+ ifeq ($(vnni512),yes)
632
+ CXXFLAGS += -DUSE_VNNI
633
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
634
+ CXXFLAGS += -mavx512vnni -mavx512dq -mavx512vl
635
+ endif
636
+ endif
637
+
638
+ ifeq ($(sse41),yes)
639
+ CXXFLAGS += -DUSE_SSE41
640
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
641
+ CXXFLAGS += -msse4.1
642
+ endif
643
+ endif
644
+
645
+ ifeq ($(ssse3),yes)
646
+ CXXFLAGS += -DUSE_SSSE3
647
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
648
+ CXXFLAGS += -mssse3
649
+ endif
650
+ endif
651
+
652
+ ifeq ($(sse2),yes)
653
+ CXXFLAGS += -DUSE_SSE2
654
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
655
+ CXXFLAGS += -msse2
656
+ endif
657
+ endif
658
+
659
+ ifeq ($(mmx),yes)
660
+ CXXFLAGS += -DUSE_MMX
661
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
662
+ CXXFLAGS += -mmmx
663
+ endif
664
+ endif
665
+
666
+ ifeq ($(neon),yes)
667
+ CXXFLAGS += -DUSE_NEON=$(arm_version)
668
+ ifeq ($(KERNEL),Linux)
669
+ ifneq ($(COMP),ndk)
670
+ ifneq ($(arch),armv8)
671
+ CXXFLAGS += -mfpu=neon
672
+ endif
673
+ endif
674
+ endif
675
+ endif
676
+
677
+ ### 3.7 pext
678
+ ifeq ($(pext),yes)
679
+ CXXFLAGS += -DUSE_PEXT
680
+ ifeq ($(comp),$(filter $(comp),gcc clang mingw))
681
+ CXXFLAGS += -mbmi2
682
+ endif
683
+ endif
684
+
685
+ ### 3.7.1 Try to include git commit sha for versioning
686
+ GIT_SHA = $(shell git rev-parse --short HEAD 2>/dev/null)
687
+ ifneq ($(GIT_SHA), )
688
+ CXXFLAGS += -DGIT_SHA=\"$(GIT_SHA)\"
689
+ endif
690
+
691
+ ### 3.7.2 Try to include git commit date for versioning
692
+ GIT_DATE = $(shell git show -s --date=format:'%Y%m%d' --format=%cd HEAD 2>/dev/null)
693
+ ifneq ($(GIT_DATE), )
694
+ CXXFLAGS += -DGIT_DATE=\"$(GIT_DATE)\"
695
+ endif
696
+
697
+ ### 3.8 Link Time Optimization
698
+ ### This is a mix of compile and link time options because the lto link phase
699
+ ### needs access to the optimization flags.
700
+ ifeq ($(optimize),yes)
701
+ ifeq ($(debug), no)
702
+ ifeq ($(comp),clang)
703
+ CXXFLAGS += -flto=full
704
+ ifeq ($(target_windows),yes)
705
+ CXXFLAGS += -fuse-ld=lld
706
+ endif
707
+ LDFLAGS += $(CXXFLAGS)
708
+
709
+ # GCC and CLANG use different methods for parallelizing LTO and CLANG pretends to be
710
+ # GCC on some systems.
711
+ else ifeq ($(comp),gcc)
712
+ ifeq ($(gccisclang),)
713
+ CXXFLAGS += -flto -flto-partition=one
714
+ LDFLAGS += $(CXXFLAGS) -flto=jobserver
715
+ else
716
+ CXXFLAGS += -flto=full
717
+ LDFLAGS += $(CXXFLAGS)
718
+ endif
719
+
720
+ # To use LTO and static linking on Windows,
721
+ # the tool chain requires gcc version 10.1 or later.
722
+ else ifeq ($(comp),mingw)
723
+ CXXFLAGS += -flto -flto-partition=one
724
+ LDFLAGS += $(CXXFLAGS) -save-temps
725
+ endif
726
+ endif
727
+ endif
728
+
729
+ ### 3.9 Android 5 can only run position independent executables. Note that this
730
+ ### breaks Android 4.0 and earlier.
731
+ ifeq ($(OS), Android)
732
+ CXXFLAGS += -fPIE
733
+ LDFLAGS += -fPIE -pie
734
+ endif
735
+
736
+ ### ==========================================================================
737
+ ### Section 4. Public Targets
738
+ ### ==========================================================================
739
+
740
+
741
+ help:
742
+ @echo ""
743
+ @echo "To compile stockfish, type: "
744
+ @echo ""
745
+ @echo "make target ARCH=arch [COMP=compiler] [COMPCXX=cxx]"
746
+ @echo ""
747
+ @echo "Supported targets:"
748
+ @echo ""
749
+ @echo "help > Display architecture details"
750
+ @echo "build > Standard build"
751
+ @echo "net > Download the default nnue net"
752
+ @echo "profile-build > Faster build (with profile-guided optimization)"
753
+ @echo "strip > Strip executable"
754
+ @echo "install > Install executable"
755
+ @echo "clean > Clean up"
756
+ @echo ""
757
+ @echo "Supported archs:"
758
+ @echo ""
759
+ @echo "x86-64-vnni512 > x86 64-bit with vnni support 512bit wide"
760
+ @echo "x86-64-vnni256 > x86 64-bit with vnni support 256bit wide"
761
+ @echo "x86-64-avx512 > x86 64-bit with avx512 support"
762
+ @echo "x86-64-avxvnni > x86 64-bit with avxvnni support"
763
+ @echo "x86-64-bmi2 > x86 64-bit with bmi2 support"
764
+ @echo "x86-64-avx2 > x86 64-bit with avx2 support"
765
+ @echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support"
766
+ @echo "x86-64-modern > common modern CPU, currently x86-64-sse41-popcnt"
767
+ @echo "x86-64-ssse3 > x86 64-bit with ssse3 support"
768
+ @echo "x86-64-sse3-popcnt > x86 64-bit with sse3 and popcnt support"
769
+ @echo "x86-64 > x86 64-bit generic (with sse2 support)"
770
+ @echo "x86-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support"
771
+ @echo "x86-32-sse2 > x86 32-bit with sse2 support"
772
+ @echo "x86-32 > x86 32-bit generic (with mmx and sse support)"
773
+ @echo "ppc-64 > PPC 64-bit"
774
+ @echo "ppc-32 > PPC 32-bit"
775
+ @echo "armv7 > ARMv7 32-bit"
776
+ @echo "armv7-neon > ARMv7 32-bit with popcnt and neon"
777
+ @echo "armv8 > ARMv8 64-bit with popcnt and neon"
778
+ @echo "e2k > Elbrus 2000"
779
+ @echo "apple-silicon > Apple silicon ARM64"
780
+ @echo "general-64 > unspecified 64-bit"
781
+ @echo "general-32 > unspecified 32-bit"
782
+ @echo "riscv64 > RISC-V 64-bit"
783
+ @echo ""
784
+ @echo "Supported compilers:"
785
+ @echo ""
786
+ @echo "gcc > Gnu compiler (default)"
787
+ @echo "mingw > Gnu compiler with MinGW under Windows"
788
+ @echo "clang > LLVM Clang compiler"
789
+ @echo "icc > Intel compiler"
790
+ @echo "ndk > Google NDK to cross-compile for Android"
791
+ @echo ""
792
+ @echo "Simple examples. If you don't know what to do, you likely want to run: "
793
+ @echo ""
794
+ @echo "make -j build ARCH=x86-64 (A portable, slow compile for 64-bit systems)"
795
+ @echo "make -j build ARCH=x86-32 (A portable, slow compile for 32-bit systems)"
796
+ @echo ""
797
+ @echo "Advanced examples, for experienced users looking for performance: "
798
+ @echo ""
799
+ @echo "make help ARCH=x86-64-bmi2"
800
+ @echo "make -j profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-9.0"
801
+ @echo "make -j build ARCH=x86-64-ssse3 COMP=clang"
802
+ @echo ""
803
+ @echo "-------------------------------"
804
+ ifeq ($(SUPPORTED_ARCH)$(help_skip_sanity), true)
805
+ @echo "The selected architecture $(ARCH) will enable the following configuration: "
806
+ @$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
807
+ else
808
+ @echo "Specify a supported architecture with the ARCH option for more details"
809
+ @echo ""
810
+ endif
811
+
812
+
813
+ .PHONY: help build profile-build strip install clean net objclean profileclean \
814
+ config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \
815
+ clang-profile-use clang-profile-make FORCE
816
+
817
+ build: net config-sanity
818
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
819
+
820
+ profile-build: net config-sanity objclean profileclean
821
+ @echo ""
822
+ @echo "Step 1/4. Building instrumented executable ..."
823
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make)
824
+ @echo ""
825
+ @echo "Step 2/4. Running benchmark for pgo-build ..."
826
+ $(PGOBENCH) 2>&1 | tail -n 4
827
+ @echo ""
828
+ @echo "Step 3/4. Building optimized executable ..."
829
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) objclean
830
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use)
831
+ @echo ""
832
+ @echo "Step 4/4. Deleting profile data ..."
833
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) profileclean
834
+
835
+ strip:
836
+ $(STRIP) $(EXE)
837
+
838
+ install:
839
+ -mkdir -p -m 755 $(BINDIR)
840
+ -cp $(EXE) $(BINDIR)
841
+ $(STRIP) $(BINDIR)/$(EXE)
842
+
843
+ # clean all
844
+ clean: objclean profileclean
845
+ @rm -f .depend *~ core
846
+
847
+ # evaluation network (nnue)
848
+ net:
849
+ $(eval nnuenet := $(shell grep EvalFileDefaultName evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/'))
850
+ @echo "Default net: $(nnuenet)"
851
+ $(eval nnuedownloadurl1 := https://tests.stockfishchess.org/api/nn/$(nnuenet))
852
+ $(eval nnuedownloadurl2 := https://github.com/official-stockfish/networks/raw/master/$(nnuenet))
853
+ $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi))
854
+ @if [ "x$(curl_or_wget)" = "x" ]; then \
855
+ echo "Automatic download failed: neither curl nor wget is installed. Install one of these tools or download the net manually"; exit 1; \
856
+ fi
857
+ $(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi))
858
+ @if [ "x$(shasum_command)" = "x" ]; then \
859
+ echo "shasum / sha256sum not found, skipping net validation"; \
860
+ fi
861
+ @for nnuedownloadurl in "$(nnuedownloadurl1)" "$(nnuedownloadurl2)"; do \
862
+ if test -f "$(nnuenet)"; then \
863
+ echo "$(nnuenet) available."; \
864
+ else \
865
+ if [ "x$(curl_or_wget)" != "x" ]; then \
866
+ echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\
867
+ fi; \
868
+ fi; \
869
+ if [ "x$(shasum_command)" != "x" ]; then \
870
+ if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \
871
+ echo "Removing failed download"; rm -f $(nnuenet); \
872
+ else \
873
+ echo "Network validated"; break; \
874
+ fi; \
875
+ fi; \
876
+ done
877
+ @if ! test -f "$(nnuenet)"; then \
878
+ echo "Failed to download $(nnuenet)."; \
879
+ fi
880
+
881
+ # clean binaries and objects
882
+ objclean:
883
+ @rm -f stockfish stockfish.exe *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o
884
+
885
+ # clean auxiliary profiling files
886
+ profileclean:
887
+ @rm -rf profdir
888
+ @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda *.s
889
+ @rm -f stockfish.profdata *.profraw
890
+ @rm -f stockfish.*args*
891
+ @rm -f stockfish.*lt*
892
+ @rm -f stockfish.res
893
+ @rm -f ./-lstdc++.res
894
+
895
+ default:
896
+ help
897
+
898
+ ### ==========================================================================
899
+ ### Section 5. Private Targets
900
+ ### ==========================================================================
901
+
902
+ all: $(EXE) .depend
903
+
904
+ config-sanity: net
905
+ @echo ""
906
+ @echo "Config:"
907
+ @echo "debug: '$(debug)'"
908
+ @echo "sanitize: '$(sanitize)'"
909
+ @echo "optimize: '$(optimize)'"
910
+ @echo "arch: '$(arch)'"
911
+ @echo "bits: '$(bits)'"
912
+ @echo "kernel: '$(KERNEL)'"
913
+ @echo "os: '$(OS)'"
914
+ @echo "prefetch: '$(prefetch)'"
915
+ @echo "popcnt: '$(popcnt)'"
916
+ @echo "pext: '$(pext)'"
917
+ @echo "sse: '$(sse)'"
918
+ @echo "mmx: '$(mmx)'"
919
+ @echo "sse2: '$(sse2)'"
920
+ @echo "ssse3: '$(ssse3)'"
921
+ @echo "sse41: '$(sse41)'"
922
+ @echo "avx2: '$(avx2)'"
923
+ @echo "avxvnni: '$(avxvnni)'"
924
+ @echo "avx512: '$(avx512)'"
925
+ @echo "vnni256: '$(vnni256)'"
926
+ @echo "vnni512: '$(vnni512)'"
927
+ @echo "neon: '$(neon)'"
928
+ @echo "arm_version: '$(arm_version)'"
929
+ @echo ""
930
+ @echo "Flags:"
931
+ @echo "CXX: $(CXX)"
932
+ @echo "CXXFLAGS: $(CXXFLAGS)"
933
+ @echo "LDFLAGS: $(LDFLAGS)"
934
+ @echo ""
935
+ @echo "Testing config sanity. If this fails, try 'make help' ..."
936
+ @echo ""
937
+ @test "$(debug)" = "yes" || test "$(debug)" = "no"
938
+ @test "$(optimize)" = "yes" || test "$(optimize)" = "no"
939
+ @test "$(SUPPORTED_ARCH)" = "true"
940
+ @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
941
+ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "e2k" || \
942
+ test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || test "$(arch)" = "riscv64"
943
+ @test "$(bits)" = "32" || test "$(bits)" = "64"
944
+ @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
945
+ @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
946
+ @test "$(pext)" = "yes" || test "$(pext)" = "no"
947
+ @test "$(sse)" = "yes" || test "$(sse)" = "no"
948
+ @test "$(mmx)" = "yes" || test "$(mmx)" = "no"
949
+ @test "$(sse2)" = "yes" || test "$(sse2)" = "no"
950
+ @test "$(ssse3)" = "yes" || test "$(ssse3)" = "no"
951
+ @test "$(sse41)" = "yes" || test "$(sse41)" = "no"
952
+ @test "$(avx2)" = "yes" || test "$(avx2)" = "no"
953
+ @test "$(avx512)" = "yes" || test "$(avx512)" = "no"
954
+ @test "$(vnni256)" = "yes" || test "$(vnni256)" = "no"
955
+ @test "$(vnni512)" = "yes" || test "$(vnni512)" = "no"
956
+ @test "$(neon)" = "yes" || test "$(neon)" = "no"
957
+ @test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" \
958
+ || test "$(comp)" = "armv7a-linux-androideabi16-clang" || test "$(comp)" = "aarch64-linux-android21-clang"
959
+
960
+ $(EXE): $(OBJS)
961
+ +$(CXX) -o $@ $(OBJS) $(LDFLAGS)
962
+
963
+ # Force recompilation to ensure version info is up-to-date
964
+ misc.o: FORCE
965
+ FORCE:
966
+
967
+ clang-profile-make:
968
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
969
+ EXTRACXXFLAGS='-fprofile-instr-generate ' \
970
+ EXTRALDFLAGS=' -fprofile-instr-generate' \
971
+ all
972
+
973
+ clang-profile-use:
974
+ $(XCRUN) llvm-profdata merge -output=stockfish.profdata *.profraw
975
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
976
+ EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \
977
+ EXTRALDFLAGS='-fprofile-use ' \
978
+ all
979
+
980
+ gcc-profile-make:
981
+ @mkdir -p profdir
982
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
983
+ EXTRACXXFLAGS='-fprofile-generate=profdir' \
984
+ EXTRACXXFLAGS+=$(EXTRAPROFILEFLAGS) \
985
+ EXTRALDFLAGS='-lgcov' \
986
+ all
987
+
988
+ gcc-profile-use:
989
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
990
+ EXTRACXXFLAGS='-fprofile-use=profdir -fno-peel-loops -fno-tracer' \
991
+ EXTRACXXFLAGS+=$(EXTRAPROFILEFLAGS) \
992
+ EXTRALDFLAGS='-lgcov' \
993
+ all
994
+
995
+ icc-profile-make:
996
+ @mkdir -p profdir
997
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
998
+ EXTRACXXFLAGS='-prof-gen=srcpos -prof_dir ./profdir' \
999
+ all
1000
+
1001
+ icc-profile-use:
1002
+ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
1003
+ EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \
1004
+ all
1005
+
1006
+ .depend: $(SRCS)
1007
+ -@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null
1008
+
1009
+ -include .depend
src/benchmark.cpp ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <fstream>
20
+ #include <iostream>
21
+ #include <istream>
22
+ #include <vector>
23
+
24
+ #include "position.h"
25
+
26
+ using namespace std;
27
+
28
+ namespace {
29
+
30
+ const vector<string> Defaults = {
31
+ "setoption name UCI_Chess960 value false",
32
+ "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
33
+ "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
34
+ "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11",
35
+ "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
36
+ "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14 moves d4e6",
37
+ "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14 moves g2g4",
38
+ "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15",
39
+ "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13",
40
+ "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16",
41
+ "4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17",
42
+ "2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11",
43
+ "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16",
44
+ "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
45
+ "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
46
+ "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22",
47
+ "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26",
48
+ "6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1",
49
+ "3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1",
50
+ "2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1 moves g5g6 f3e3 g6g5 e3f3",
51
+ "8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1",
52
+ "7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1",
53
+ "8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1",
54
+ "8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1",
55
+ "8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1",
56
+ "8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1",
57
+ "5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1",
58
+ "6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1",
59
+ "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
60
+ "6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1",
61
+ "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1",
62
+ "5rk1/q6p/2p3bR/1pPp1rP1/1P1Pp3/P3B1Q1/1K3P2/R7 w - - 93 90",
63
+ "4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21",
64
+ "r3k2r/3nnpbp/q2pp1p1/p7/Pp1PPPP1/4BNN1/1P5P/R2Q1RK1 w kq - 0 16",
65
+ "3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40",
66
+ "4k3/3q1r2/1N2r1b1/3ppN2/2nPP3/1B1R2n1/2R1Q3/3K4 w - - 5 1",
67
+
68
+ // 5-man positions
69
+ "8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
70
+ "8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate
71
+ "8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw
72
+
73
+ // 6-man positions
74
+ "8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate
75
+ "8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate
76
+ "8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw
77
+
78
+ // 7-man positions
79
+ "8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw
80
+
81
+ // Mate and stalemate positions
82
+ "6k1/3b3r/1p1p4/p1n2p2/1PPNpP1q/P3Q1p1/1R1RB1P1/5K2 b - - 0 1",
83
+ "r2r1n2/pp2bk2/2p1p2p/3q4/3PN1QP/2P3R1/P4PP1/5RK1 w - - 0 1",
84
+ "8/8/8/8/8/6k1/6p1/6K1 w - -",
85
+ "7k/7P/6K1/8/3B4/8/8/8 b - -",
86
+
87
+ // Chess 960
88
+ "setoption name UCI_Chess960 value true",
89
+ "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w HFhf - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6",
90
+ "nqbnrkrb/pppppppp/8/8/8/8/PPPPPPPP/NQBNRKRB w KQkq - 0 1",
91
+ "setoption name UCI_Chess960 value false"
92
+ };
93
+
94
+ } // namespace
95
+
96
+ namespace Stockfish {
97
+
98
+ /// setup_bench() builds a list of UCI commands to be run by bench. There
99
+ /// are five parameters: TT size in MB, number of search threads that
100
+ /// should be used, the limit value spent for each position, a file name
101
+ /// where to look for positions in FEN format, the type of the limit:
102
+ /// depth, perft, nodes and movetime (in millisecs), and evaluation type
103
+ /// mixed (default), classical, NNUE.
104
+ ///
105
+ /// bench -> search default positions up to depth 13
106
+ /// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB)
107
+ /// bench 64 4 5000 current movetime -> search current position with 4 threads for 5 sec
108
+ /// bench 64 1 100000 default nodes -> search default positions for 100K nodes each
109
+ /// bench 16 1 5 default perft -> run a perft 5 on default positions
110
+
111
+ vector<string> setup_bench(const Position& current, istream& is) {
112
+
113
+ vector<string> fens, list;
114
+ string go, token;
115
+
116
+ // Assign default values to missing arguments
117
+ string ttSize = (is >> token) ? token : "16";
118
+ string threads = (is >> token) ? token : "1";
119
+ string limit = (is >> token) ? token : "13";
120
+ string fenFile = (is >> token) ? token : "default";
121
+ string limitType = (is >> token) ? token : "depth";
122
+ string evalType = (is >> token) ? token : "mixed";
123
+
124
+ go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit;
125
+
126
+ if (fenFile == "default")
127
+ fens = Defaults;
128
+
129
+ else if (fenFile == "current")
130
+ fens.push_back(current.fen());
131
+
132
+ else
133
+ {
134
+ string fen;
135
+ ifstream file(fenFile);
136
+
137
+ if (!file.is_open())
138
+ {
139
+ cerr << "Unable to open file " << fenFile << endl;
140
+ exit(EXIT_FAILURE);
141
+ }
142
+
143
+ while (getline(file, fen))
144
+ if (!fen.empty())
145
+ fens.push_back(fen);
146
+
147
+ file.close();
148
+ }
149
+
150
+ list.emplace_back("setoption name Threads value " + threads);
151
+ list.emplace_back("setoption name Hash value " + ttSize);
152
+ list.emplace_back("ucinewgame");
153
+
154
+ size_t posCounter = 0;
155
+
156
+ for (const string& fen : fens)
157
+ if (fen.find("setoption") != string::npos)
158
+ list.emplace_back(fen);
159
+ else
160
+ {
161
+ if (evalType == "classical" || (evalType == "mixed" && posCounter % 2 == 0))
162
+ list.emplace_back("setoption name Use NNUE value false");
163
+ else if (evalType == "NNUE" || (evalType == "mixed" && posCounter % 2 != 0))
164
+ list.emplace_back("setoption name Use NNUE value true");
165
+ list.emplace_back("position fen " + fen);
166
+ list.emplace_back(go);
167
+ ++posCounter;
168
+ }
169
+
170
+ list.emplace_back("setoption name Use NNUE value true");
171
+
172
+ return list;
173
+ }
174
+
175
+ } // namespace Stockfish
src/bitbase.cpp ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <cassert>
20
+ #include <vector>
21
+ #include <bitset>
22
+
23
+ #include "bitboard.h"
24
+ #include "types.h"
25
+
26
+ namespace Stockfish {
27
+
28
+ namespace {
29
+
30
+ // There are 24 possible pawn squares: files A to D and ranks from 2 to 7.
31
+ // Positions with the pawn on files E to H will be mirrored before probing.
32
+ constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608
33
+
34
+ std::bitset<MAX_INDEX> KPKBitbase;
35
+
36
+ // A KPK bitbase index is an integer in [0, IndexMax] range
37
+ //
38
+ // Information is mapped in a way that minimizes the number of iterations:
39
+ //
40
+ // bit 0- 5: white king square (from SQ_A1 to SQ_H8)
41
+ // bit 6-11: black king square (from SQ_A1 to SQ_H8)
42
+ // bit 12: side to move (WHITE or BLACK)
43
+ // bit 13-14: white pawn file (from FILE_A to FILE_D)
44
+ // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2)
45
+ unsigned index(Color stm, Square bksq, Square wksq, Square psq) {
46
+ return int(wksq) | (bksq << 6) | (stm << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15);
47
+ }
48
+
49
+ enum Result {
50
+ INVALID = 0,
51
+ UNKNOWN = 1,
52
+ DRAW = 2,
53
+ WIN = 4
54
+ };
55
+
56
+ Result& operator|=(Result& r, Result v) { return r = Result(r | v); }
57
+
58
+ struct KPKPosition {
59
+ KPKPosition() = default;
60
+ explicit KPKPosition(unsigned idx);
61
+ operator Result() const { return result; }
62
+ Result classify(const std::vector<KPKPosition>& db);
63
+
64
+ Color stm;
65
+ Square ksq[COLOR_NB], psq;
66
+ Result result;
67
+ };
68
+
69
+ } // namespace
70
+
71
+ bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) {
72
+
73
+ assert(file_of(wpsq) <= FILE_D);
74
+
75
+ return KPKBitbase[index(stm, bksq, wksq, wpsq)];
76
+ }
77
+
78
+
79
+ void Bitbases::init() {
80
+
81
+ std::vector<KPKPosition> db(MAX_INDEX);
82
+ unsigned idx, repeat = 1;
83
+
84
+ // Initialize db with known win / draw positions
85
+ for (idx = 0; idx < MAX_INDEX; ++idx)
86
+ db[idx] = KPKPosition(idx);
87
+
88
+ // Iterate through the positions until none of the unknown positions can be
89
+ // changed to either wins or draws (15 cycles needed).
90
+ while (repeat)
91
+ for (repeat = idx = 0; idx < MAX_INDEX; ++idx)
92
+ repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN);
93
+
94
+ // Fill the bitbase with the decisive results
95
+ for (idx = 0; idx < MAX_INDEX; ++idx)
96
+ if (db[idx] == WIN)
97
+ KPKBitbase.set(idx);
98
+ }
99
+
100
+ namespace {
101
+
102
+ KPKPosition::KPKPosition(unsigned idx) {
103
+
104
+ ksq[WHITE] = Square((idx >> 0) & 0x3F);
105
+ ksq[BLACK] = Square((idx >> 6) & 0x3F);
106
+ stm = Color ((idx >> 12) & 0x01);
107
+ psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7)));
108
+
109
+ // Invalid if two pieces are on the same square or if a king can be captured
110
+ if ( distance(ksq[WHITE], ksq[BLACK]) <= 1
111
+ || ksq[WHITE] == psq
112
+ || ksq[BLACK] == psq
113
+ || (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK])))
114
+ result = INVALID;
115
+
116
+ // Win if the pawn can be promoted without getting captured
117
+ else if ( stm == WHITE
118
+ && rank_of(psq) == RANK_7
119
+ && ksq[WHITE] != psq + NORTH
120
+ && ( distance(ksq[BLACK], psq + NORTH) > 1
121
+ || (distance(ksq[WHITE], psq + NORTH) == 1)))
122
+ result = WIN;
123
+
124
+ // Draw if it is stalemate or the black king can capture the pawn
125
+ else if ( stm == BLACK
126
+ && ( !(attacks_bb<KING>(ksq[BLACK]) & ~(attacks_bb<KING>(ksq[WHITE]) | pawn_attacks_bb(WHITE, psq)))
127
+ || (attacks_bb<KING>(ksq[BLACK]) & ~attacks_bb<KING>(ksq[WHITE]) & psq)))
128
+ result = DRAW;
129
+
130
+ // Position will be classified later
131
+ else
132
+ result = UNKNOWN;
133
+ }
134
+
135
+ Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
136
+
137
+ // White to move: If one move leads to a position classified as WIN, the result
138
+ // of the current position is WIN. If all moves lead to positions classified
139
+ // as DRAW, the current position is classified as DRAW, otherwise the current
140
+ // position is classified as UNKNOWN.
141
+ //
142
+ // Black to move: If one move leads to a position classified as DRAW, the result
143
+ // of the current position is DRAW. If all moves lead to positions classified
144
+ // as WIN, the position is classified as WIN, otherwise the current position is
145
+ // classified as UNKNOWN.
146
+ const Result Good = (stm == WHITE ? WIN : DRAW);
147
+ const Result Bad = (stm == WHITE ? DRAW : WIN);
148
+
149
+ Result r = INVALID;
150
+ Bitboard b = attacks_bb<KING>(ksq[stm]);
151
+
152
+ while (b)
153
+ r |= stm == WHITE ? db[index(BLACK, ksq[BLACK], pop_lsb(b), psq)]
154
+ : db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)];
155
+
156
+ if (stm == WHITE)
157
+ {
158
+ if (rank_of(psq) < RANK_7) // Single push
159
+ r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH)];
160
+
161
+ if ( rank_of(psq) == RANK_2 // Double push
162
+ && psq + NORTH != ksq[WHITE]
163
+ && psq + NORTH != ksq[BLACK])
164
+ r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH + NORTH)];
165
+ }
166
+
167
+ return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad;
168
+ }
169
+
170
+ } // namespace
171
+
172
+ } // namespace Stockfish
src/bitboard.cpp ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <algorithm>
20
+ #include <bitset>
21
+
22
+ #include "bitboard.h"
23
+ #include "misc.h"
24
+
25
+ namespace Stockfish {
26
+
27
+ uint8_t PopCnt16[1 << 16];
28
+ uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
29
+
30
+ Bitboard SquareBB[SQUARE_NB];
31
+ Bitboard LineBB[SQUARE_NB][SQUARE_NB];
32
+ Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
33
+ Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
34
+ Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
35
+
36
+ Magic RookMagics[SQUARE_NB];
37
+ Magic BishopMagics[SQUARE_NB];
38
+
39
+ namespace {
40
+
41
+ Bitboard RookTable[0x19000]; // To store rook attacks
42
+ Bitboard BishopTable[0x1480]; // To store bishop attacks
43
+
44
+ void init_magics(PieceType pt, Bitboard table[], Magic magics[]);
45
+
46
+ }
47
+
48
+ /// safe_destination() returns the bitboard of target square for the given step
49
+ /// from the given square. If the step is off the board, returns empty bitboard.
50
+
51
+ inline Bitboard safe_destination(Square s, int step) {
52
+ Square to = Square(s + step);
53
+ return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
54
+ }
55
+
56
+
57
+ /// Bitboards::pretty() returns an ASCII representation of a bitboard suitable
58
+ /// to be printed to standard output. Useful for debugging.
59
+
60
+ std::string Bitboards::pretty(Bitboard b) {
61
+
62
+ std::string s = "+---+---+---+---+---+---+---+---+\n";
63
+
64
+ for (Rank r = RANK_8; r >= RANK_1; --r)
65
+ {
66
+ for (File f = FILE_A; f <= FILE_H; ++f)
67
+ s += b & make_square(f, r) ? "| X " : "| ";
68
+
69
+ s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n";
70
+ }
71
+ s += " a b c d e f g h\n";
72
+
73
+ return s;
74
+ }
75
+
76
+
77
+ /// Bitboards::init() initializes various bitboard tables. It is called at
78
+ /// startup and relies on global objects to be already zero-initialized.
79
+
80
+ void Bitboards::init() {
81
+
82
+ for (unsigned i = 0; i < (1 << 16); ++i)
83
+ PopCnt16[i] = uint8_t(std::bitset<16>(i).count());
84
+
85
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
86
+ SquareBB[s] = (1ULL << s);
87
+
88
+ for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
89
+ for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
90
+ SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
91
+
92
+ init_magics(ROOK, RookTable, RookMagics);
93
+ init_magics(BISHOP, BishopTable, BishopMagics);
94
+
95
+ for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
96
+ {
97
+ PawnAttacks[WHITE][s1] = pawn_attacks_bb<WHITE>(square_bb(s1));
98
+ PawnAttacks[BLACK][s1] = pawn_attacks_bb<BLACK>(square_bb(s1));
99
+
100
+ for (int step : {-9, -8, -7, -1, 1, 7, 8, 9} )
101
+ PseudoAttacks[KING][s1] |= safe_destination(s1, step);
102
+
103
+ for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} )
104
+ PseudoAttacks[KNIGHT][s1] |= safe_destination(s1, step);
105
+
106
+ PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb<BISHOP>(s1, 0);
107
+ PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0);
108
+
109
+ for (PieceType pt : { BISHOP, ROOK })
110
+ for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
111
+ {
112
+ if (PseudoAttacks[pt][s1] & s2)
113
+ {
114
+ LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
115
+ BetweenBB[s1][s2] = (attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
116
+ }
117
+ BetweenBB[s1][s2] |= s2;
118
+ }
119
+ }
120
+ }
121
+
122
+ namespace {
123
+
124
+ Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
125
+
126
+ Bitboard attacks = 0;
127
+ Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
128
+ Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
129
+
130
+ for (Direction d : (pt == ROOK ? RookDirections : BishopDirections))
131
+ {
132
+ Square s = sq;
133
+ while (safe_destination(s, d) && !(occupied & s))
134
+ attacks |= (s += d);
135
+ }
136
+
137
+ return attacks;
138
+ }
139
+
140
+
141
+ // init_magics() computes all rook and bishop attacks at startup. Magic
142
+ // bitboards are used to look up attacks of sliding pieces. As a reference see
143
+ // www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so
144
+ // called "fancy" approach.
145
+
146
+ void init_magics(PieceType pt, Bitboard table[], Magic magics[]) {
147
+
148
+ // Optimal PRNG seeds to pick the correct magics in the shortest time
149
+ int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 },
150
+ { 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } };
151
+
152
+ Bitboard occupancy[4096], reference[4096], edges, b;
153
+ int epoch[4096] = {}, cnt = 0, size = 0;
154
+
155
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
156
+ {
157
+ // Board edges are not considered in the relevant occupancies
158
+ edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
159
+
160
+ // Given a square 's', the mask is the bitboard of sliding attacks from
161
+ // 's' computed on an empty board. The index must be big enough to contain
162
+ // all the attacks for each possible subset of the mask and so is 2 power
163
+ // the number of 1s of the mask. Hence we deduce the size of the shift to
164
+ // apply to the 64 or 32 bits word to get the index.
165
+ Magic& m = magics[s];
166
+ m.mask = sliding_attack(pt, s, 0) & ~edges;
167
+ m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
168
+
169
+ // Set the offset for the attacks table of the square. We have individual
170
+ // table sizes for each square with "Fancy Magic Bitboards".
171
+ m.attacks = s == SQ_A1 ? table : magics[s - 1].attacks + size;
172
+
173
+ // Use Carry-Rippler trick to enumerate all subsets of masks[s] and
174
+ // store the corresponding sliding attack bitboard in reference[].
175
+ b = size = 0;
176
+ do {
177
+ occupancy[size] = b;
178
+ reference[size] = sliding_attack(pt, s, b);
179
+
180
+ if (HasPext)
181
+ m.attacks[pext(b, m.mask)] = reference[size];
182
+
183
+ size++;
184
+ b = (b - m.mask) & m.mask;
185
+ } while (b);
186
+
187
+ if (HasPext)
188
+ continue;
189
+
190
+ PRNG rng(seeds[Is64Bit][rank_of(s)]);
191
+
192
+ // Find a magic for square 's' picking up an (almost) random number
193
+ // until we find the one that passes the verification test.
194
+ for (int i = 0; i < size; )
195
+ {
196
+ for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6; )
197
+ m.magic = rng.sparse_rand<Bitboard>();
198
+
199
+ // A good magic must map every possible occupancy to an index that
200
+ // looks up the correct sliding attack in the attacks[s] database.
201
+ // Note that we build up the database for square 's' as a side
202
+ // effect of verifying the magic. Keep track of the attempt count
203
+ // and save it in epoch[], little speed-up trick to avoid resetting
204
+ // m.attacks[] after every failed attempt.
205
+ for (++cnt, i = 0; i < size; ++i)
206
+ {
207
+ unsigned idx = m.index(occupancy[i]);
208
+
209
+ if (epoch[idx] < cnt)
210
+ {
211
+ epoch[idx] = cnt;
212
+ m.attacks[idx] = reference[i];
213
+ }
214
+ else if (m.attacks[idx] != reference[i])
215
+ break;
216
+ }
217
+ }
218
+ }
219
+ }
220
+ }
221
+
222
+ } // namespace Stockfish
src/bitboard.h ADDED
@@ -0,0 +1,451 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef BITBOARD_H_INCLUDED
20
+ #define BITBOARD_H_INCLUDED
21
+
22
+ #include <string>
23
+
24
+ #include "types.h"
25
+
26
+ namespace Stockfish {
27
+
28
+ namespace Bitbases {
29
+
30
+ void init();
31
+ bool probe(Square wksq, Square wpsq, Square bksq, Color us);
32
+
33
+ } // namespace Stockfish::Bitbases
34
+
35
+ namespace Bitboards {
36
+
37
+ void init();
38
+ std::string pretty(Bitboard b);
39
+
40
+ } // namespace Stockfish::Bitboards
41
+
42
+ constexpr Bitboard AllSquares = ~Bitboard(0);
43
+ constexpr Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
44
+
45
+ constexpr Bitboard FileABB = 0x0101010101010101ULL;
46
+ constexpr Bitboard FileBBB = FileABB << 1;
47
+ constexpr Bitboard FileCBB = FileABB << 2;
48
+ constexpr Bitboard FileDBB = FileABB << 3;
49
+ constexpr Bitboard FileEBB = FileABB << 4;
50
+ constexpr Bitboard FileFBB = FileABB << 5;
51
+ constexpr Bitboard FileGBB = FileABB << 6;
52
+ constexpr Bitboard FileHBB = FileABB << 7;
53
+
54
+ constexpr Bitboard Rank1BB = 0xFF;
55
+ constexpr Bitboard Rank2BB = Rank1BB << (8 * 1);
56
+ constexpr Bitboard Rank3BB = Rank1BB << (8 * 2);
57
+ constexpr Bitboard Rank4BB = Rank1BB << (8 * 3);
58
+ constexpr Bitboard Rank5BB = Rank1BB << (8 * 4);
59
+ constexpr Bitboard Rank6BB = Rank1BB << (8 * 5);
60
+ constexpr Bitboard Rank7BB = Rank1BB << (8 * 6);
61
+ constexpr Bitboard Rank8BB = Rank1BB << (8 * 7);
62
+
63
+ constexpr Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB;
64
+ constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
65
+ constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
66
+ constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);
67
+
68
+ constexpr Bitboard KingFlank[FILE_NB] = {
69
+ QueenSide ^ FileDBB, QueenSide, QueenSide,
70
+ CenterFiles, CenterFiles,
71
+ KingSide, KingSide, KingSide ^ FileEBB
72
+ };
73
+
74
+ extern uint8_t PopCnt16[1 << 16];
75
+ extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
76
+
77
+ extern Bitboard SquareBB[SQUARE_NB];
78
+ extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
79
+ extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
80
+ extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
81
+ extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
82
+
83
+
84
+ /// Magic holds all magic bitboards relevant data for a single square
85
+ struct Magic {
86
+ Bitboard mask;
87
+ Bitboard magic;
88
+ Bitboard* attacks;
89
+ unsigned shift;
90
+
91
+ // Compute the attack's index using the 'magic bitboards' approach
92
+ unsigned index(Bitboard occupied) const {
93
+
94
+ if (HasPext)
95
+ return unsigned(pext(occupied, mask));
96
+
97
+ if (Is64Bit)
98
+ return unsigned(((occupied & mask) * magic) >> shift);
99
+
100
+ unsigned lo = unsigned(occupied) & unsigned(mask);
101
+ unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32);
102
+ return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift;
103
+ }
104
+ };
105
+
106
+ extern Magic RookMagics[SQUARE_NB];
107
+ extern Magic BishopMagics[SQUARE_NB];
108
+
109
+ inline Bitboard square_bb(Square s) {
110
+ assert(is_ok(s));
111
+ return SquareBB[s];
112
+ }
113
+
114
+
115
+ /// Overloads of bitwise operators between a Bitboard and a Square for testing
116
+ /// whether a given bit is set in a bitboard, and for setting and clearing bits.
117
+
118
+ inline Bitboard operator&( Bitboard b, Square s) { return b & square_bb(s); }
119
+ inline Bitboard operator|( Bitboard b, Square s) { return b | square_bb(s); }
120
+ inline Bitboard operator^( Bitboard b, Square s) { return b ^ square_bb(s); }
121
+ inline Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); }
122
+ inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }
123
+
124
+ inline Bitboard operator&(Square s, Bitboard b) { return b & s; }
125
+ inline Bitboard operator|(Square s, Bitboard b) { return b | s; }
126
+ inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; }
127
+
128
+ inline Bitboard operator|(Square s1, Square s2) { return square_bb(s1) | s2; }
129
+
130
+ constexpr bool more_than_one(Bitboard b) {
131
+ return b & (b - 1);
132
+ }
133
+
134
+
135
+ constexpr bool opposite_colors(Square s1, Square s2) {
136
+ return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1;
137
+ }
138
+
139
+
140
+ /// rank_bb() and file_bb() return a bitboard representing all the squares on
141
+ /// the given file or rank.
142
+
143
+ constexpr Bitboard rank_bb(Rank r) {
144
+ return Rank1BB << (8 * r);
145
+ }
146
+
147
+ constexpr Bitboard rank_bb(Square s) {
148
+ return rank_bb(rank_of(s));
149
+ }
150
+
151
+ constexpr Bitboard file_bb(File f) {
152
+ return FileABB << f;
153
+ }
154
+
155
+ constexpr Bitboard file_bb(Square s) {
156
+ return file_bb(file_of(s));
157
+ }
158
+
159
+
160
+ /// shift() moves a bitboard one or two steps as specified by the direction D
161
+
162
+ template<Direction D>
163
+ constexpr Bitboard shift(Bitboard b) {
164
+ return D == NORTH ? b << 8 : D == SOUTH ? b >> 8
165
+ : D == NORTH+NORTH? b <<16 : D == SOUTH+SOUTH? b >>16
166
+ : D == EAST ? (b & ~FileHBB) << 1 : D == WEST ? (b & ~FileABB) >> 1
167
+ : D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7
168
+ : D == SOUTH_EAST ? (b & ~FileHBB) >> 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9
169
+ : 0;
170
+ }
171
+
172
+
173
+ /// pawn_attacks_bb() returns the squares attacked by pawns of the given color
174
+ /// from the squares in the given bitboard.
175
+
176
+ template<Color C>
177
+ constexpr Bitboard pawn_attacks_bb(Bitboard b) {
178
+ return C == WHITE ? shift<NORTH_WEST>(b) | shift<NORTH_EAST>(b)
179
+ : shift<SOUTH_WEST>(b) | shift<SOUTH_EAST>(b);
180
+ }
181
+
182
+ inline Bitboard pawn_attacks_bb(Color c, Square s) {
183
+
184
+ assert(is_ok(s));
185
+ return PawnAttacks[c][s];
186
+ }
187
+
188
+
189
+ /// pawn_double_attacks_bb() returns the squares doubly attacked by pawns of the
190
+ /// given color from the squares in the given bitboard.
191
+
192
+ template<Color C>
193
+ constexpr Bitboard pawn_double_attacks_bb(Bitboard b) {
194
+ return C == WHITE ? shift<NORTH_WEST>(b) & shift<NORTH_EAST>(b)
195
+ : shift<SOUTH_WEST>(b) & shift<SOUTH_EAST>(b);
196
+ }
197
+
198
+
199
+ /// adjacent_files_bb() returns a bitboard representing all the squares on the
200
+ /// adjacent files of a given square.
201
+
202
+ constexpr Bitboard adjacent_files_bb(Square s) {
203
+ return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
204
+ }
205
+
206
+
207
+ /// line_bb() returns a bitboard representing an entire line (from board edge
208
+ /// to board edge) that intersects the two given squares. If the given squares
209
+ /// are not on a same file/rank/diagonal, the function returns 0. For instance,
210
+ /// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal.
211
+
212
+ inline Bitboard line_bb(Square s1, Square s2) {
213
+
214
+ assert(is_ok(s1) && is_ok(s2));
215
+
216
+ return LineBB[s1][s2];
217
+ }
218
+
219
+
220
+ /// between_bb(s1, s2) returns a bitboard representing the squares in the semi-open
221
+ /// segment between the squares s1 and s2 (excluding s1 but including s2). If the
222
+ /// given squares are not on a same file/rank/diagonal, it returns s2. For instance,
223
+ /// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5, E6 and F7, but
224
+ /// between_bb(SQ_E6, SQ_F8) will return a bitboard with the square F8. This trick
225
+ /// allows to generate non-king evasion moves faster: the defending piece must either
226
+ /// interpose itself to cover the check or capture the checking piece.
227
+
228
+ inline Bitboard between_bb(Square s1, Square s2) {
229
+
230
+ assert(is_ok(s1) && is_ok(s2));
231
+
232
+ return BetweenBB[s1][s2];
233
+ }
234
+
235
+
236
+ /// forward_ranks_bb() returns a bitboard representing the squares on the ranks in
237
+ /// front of the given one, from the point of view of the given color. For instance,
238
+ /// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2.
239
+
240
+ constexpr Bitboard forward_ranks_bb(Color c, Square s) {
241
+ return c == WHITE ? ~Rank1BB << 8 * relative_rank(WHITE, s)
242
+ : ~Rank8BB >> 8 * relative_rank(BLACK, s);
243
+ }
244
+
245
+
246
+ /// forward_file_bb() returns a bitboard representing all the squares along the
247
+ /// line in front of the given one, from the point of view of the given color.
248
+
249
+ constexpr Bitboard forward_file_bb(Color c, Square s) {
250
+ return forward_ranks_bb(c, s) & file_bb(s);
251
+ }
252
+
253
+
254
+ /// pawn_attack_span() returns a bitboard representing all the squares that can
255
+ /// be attacked by a pawn of the given color when it moves along its file, starting
256
+ /// from the given square.
257
+
258
+ constexpr Bitboard pawn_attack_span(Color c, Square s) {
259
+ return forward_ranks_bb(c, s) & adjacent_files_bb(s);
260
+ }
261
+
262
+
263
+ /// passed_pawn_span() returns a bitboard which can be used to test if a pawn of
264
+ /// the given color and on the given square is a passed pawn.
265
+
266
+ constexpr Bitboard passed_pawn_span(Color c, Square s) {
267
+ return pawn_attack_span(c, s) | forward_file_bb(c, s);
268
+ }
269
+
270
+
271
+ /// aligned() returns true if the squares s1, s2 and s3 are aligned either on a
272
+ /// straight or on a diagonal line.
273
+
274
+ inline bool aligned(Square s1, Square s2, Square s3) {
275
+ return line_bb(s1, s2) & s3;
276
+ }
277
+
278
+
279
+ /// distance() functions return the distance between x and y, defined as the
280
+ /// number of steps for a king in x to reach y.
281
+
282
+ template<typename T1 = Square> inline int distance(Square x, Square y);
283
+ template<> inline int distance<File>(Square x, Square y) { return std::abs(file_of(x) - file_of(y)); }
284
+ template<> inline int distance<Rank>(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); }
285
+ template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; }
286
+
287
+ inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
288
+ inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); }
289
+
290
+
291
+ /// attacks_bb(Square) returns the pseudo attacks of the give piece type
292
+ /// assuming an empty board.
293
+
294
+ template<PieceType Pt>
295
+ inline Bitboard attacks_bb(Square s) {
296
+
297
+ assert((Pt != PAWN) && (is_ok(s)));
298
+
299
+ return PseudoAttacks[Pt][s];
300
+ }
301
+
302
+
303
+ /// attacks_bb(Square, Bitboard) returns the attacks by the given piece
304
+ /// assuming the board is occupied according to the passed Bitboard.
305
+ /// Sliding piece attacks do not continue passed an occupied square.
306
+
307
+ template<PieceType Pt>
308
+ inline Bitboard attacks_bb(Square s, Bitboard occupied) {
309
+
310
+ assert((Pt != PAWN) && (is_ok(s)));
311
+
312
+ switch (Pt)
313
+ {
314
+ case BISHOP: return BishopMagics[s].attacks[BishopMagics[s].index(occupied)];
315
+ case ROOK : return RookMagics[s].attacks[ RookMagics[s].index(occupied)];
316
+ case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
317
+ default : return PseudoAttacks[Pt][s];
318
+ }
319
+ }
320
+
321
+ inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
322
+
323
+ assert((pt != PAWN) && (is_ok(s)));
324
+
325
+ switch (pt)
326
+ {
327
+ case BISHOP: return attacks_bb<BISHOP>(s, occupied);
328
+ case ROOK : return attacks_bb< ROOK>(s, occupied);
329
+ case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
330
+ default : return PseudoAttacks[pt][s];
331
+ }
332
+ }
333
+
334
+
335
+ /// popcount() counts the number of non-zero bits in a bitboard
336
+
337
+ inline int popcount(Bitboard b) {
338
+
339
+ #ifndef USE_POPCNT
340
+
341
+ union { Bitboard bb; uint16_t u[4]; } v = { b };
342
+ return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]];
343
+
344
+ #elif defined(_MSC_VER) || defined(__INTEL_COMPILER)
345
+
346
+ return (int)_mm_popcnt_u64(b);
347
+
348
+ #else // Assumed gcc or compatible compiler
349
+
350
+ return __builtin_popcountll(b);
351
+
352
+ #endif
353
+ }
354
+
355
+
356
+ /// lsb() and msb() return the least/most significant bit in a non-zero bitboard
357
+
358
+ #if defined(__GNUC__) // GCC, Clang, ICC
359
+
360
+ inline Square lsb(Bitboard b) {
361
+ assert(b);
362
+ return Square(__builtin_ctzll(b));
363
+ }
364
+
365
+ inline Square msb(Bitboard b) {
366
+ assert(b);
367
+ return Square(63 ^ __builtin_clzll(b));
368
+ }
369
+
370
+ #elif defined(_MSC_VER) // MSVC
371
+
372
+ #ifdef _WIN64 // MSVC, WIN64
373
+
374
+ inline Square lsb(Bitboard b) {
375
+ assert(b);
376
+ unsigned long idx;
377
+ _BitScanForward64(&idx, b);
378
+ return (Square) idx;
379
+ }
380
+
381
+ inline Square msb(Bitboard b) {
382
+ assert(b);
383
+ unsigned long idx;
384
+ _BitScanReverse64(&idx, b);
385
+ return (Square) idx;
386
+ }
387
+
388
+ #else // MSVC, WIN32
389
+
390
+ inline Square lsb(Bitboard b) {
391
+ assert(b);
392
+ unsigned long idx;
393
+
394
+ if (b & 0xffffffff) {
395
+ _BitScanForward(&idx, int32_t(b));
396
+ return Square(idx);
397
+ } else {
398
+ _BitScanForward(&idx, int32_t(b >> 32));
399
+ return Square(idx + 32);
400
+ }
401
+ }
402
+
403
+ inline Square msb(Bitboard b) {
404
+ assert(b);
405
+ unsigned long idx;
406
+
407
+ if (b >> 32) {
408
+ _BitScanReverse(&idx, int32_t(b >> 32));
409
+ return Square(idx + 32);
410
+ } else {
411
+ _BitScanReverse(&idx, int32_t(b));
412
+ return Square(idx);
413
+ }
414
+ }
415
+
416
+ #endif
417
+
418
+ #else // Compiler is neither GCC nor MSVC compatible
419
+
420
+ #error "Compiler not supported."
421
+
422
+ #endif
423
+
424
+ /// least_significant_square_bb() returns the bitboard of the least significant
425
+ /// square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)).
426
+
427
+ inline Bitboard least_significant_square_bb(Bitboard b) {
428
+ assert(b);
429
+ return b & -b;
430
+ }
431
+
432
+ /// pop_lsb() finds and clears the least significant bit in a non-zero bitboard
433
+
434
+ inline Square pop_lsb(Bitboard& b) {
435
+ assert(b);
436
+ const Square s = lsb(b);
437
+ b &= b - 1;
438
+ return s;
439
+ }
440
+
441
+
442
+ /// frontmost_sq() returns the most advanced square for the given color,
443
+ /// requires a non-zero bitboard.
444
+ inline Square frontmost_sq(Color c, Bitboard b) {
445
+ assert(b);
446
+ return c == WHITE ? msb(b) : lsb(b);
447
+ }
448
+
449
+ } // namespace Stockfish
450
+
451
+ #endif // #ifndef BITBOARD_H_INCLUDED
src/endgame.cpp ADDED
@@ -0,0 +1,747 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <cassert>
20
+
21
+ #include "bitboard.h"
22
+ #include "endgame.h"
23
+ #include "movegen.h"
24
+
25
+ namespace Stockfish {
26
+
27
+ namespace {
28
+
29
+ // Used to drive the king towards the edge of the board
30
+ // in KX vs K and KQ vs KR endgames.
31
+ // Values range from 27 (center squares) to 90 (in the corners)
32
+ inline int push_to_edge(Square s) {
33
+ int rd = edge_distance(rank_of(s)), fd = edge_distance(file_of(s));
34
+ return 90 - (7 * fd * fd / 2 + 7 * rd * rd / 2);
35
+ }
36
+
37
+ // Used to drive the king towards A1H8 corners in KBN vs K endgames.
38
+ // Values range from 0 on A8H1 diagonal to 7 in A1H8 corners
39
+ inline int push_to_corner(Square s) {
40
+ return abs(7 - rank_of(s) - file_of(s));
41
+ }
42
+
43
+ // Drive a piece close to or away from another piece
44
+ inline int push_close(Square s1, Square s2) { return 140 - 20 * distance(s1, s2); }
45
+ inline int push_away(Square s1, Square s2) { return 120 - push_close(s1, s2); }
46
+
47
+ #ifndef NDEBUG
48
+ bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) {
49
+ return pos.non_pawn_material(c) == npm && pos.count<PAWN>(c) == pawnsCnt;
50
+ }
51
+ #endif
52
+
53
+ // Map the square as if strongSide is white and strongSide's only pawn
54
+ // is on the left half of the board.
55
+ Square normalize(const Position& pos, Color strongSide, Square sq) {
56
+
57
+ assert(pos.count<PAWN>(strongSide) == 1);
58
+
59
+ if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E)
60
+ sq = flip_file(sq);
61
+
62
+ return strongSide == WHITE ? sq : flip_rank(sq);
63
+ }
64
+
65
+ } // namespace
66
+
67
+
68
+ namespace Endgames {
69
+
70
+ std::pair<Map<Value>, Map<ScaleFactor>> maps;
71
+
72
+ void init() {
73
+
74
+ add<KPK>("KPK");
75
+ add<KNNK>("KNNK");
76
+ add<KBNK>("KBNK");
77
+ add<KRKP>("KRKP");
78
+ add<KRKB>("KRKB");
79
+ add<KRKN>("KRKN");
80
+ add<KQKP>("KQKP");
81
+ add<KQKR>("KQKR");
82
+ add<KNNKP>("KNNKP");
83
+
84
+ add<KRPKR>("KRPKR");
85
+ add<KRPKB>("KRPKB");
86
+ add<KBPKB>("KBPKB");
87
+ add<KBPKN>("KBPKN");
88
+ add<KBPPKB>("KBPPKB");
89
+ add<KRPPKRP>("KRPPKRP");
90
+ }
91
+ }
92
+
93
+
94
+ /// Mate with KX vs K. This function is used to evaluate positions with
95
+ /// king and plenty of material vs a lone king. It simply gives the
96
+ /// attacking side a bonus for driving the defending king towards the edge
97
+ /// of the board, and for keeping the distance between the two kings small.
98
+ template<>
99
+ Value Endgame<KXK>::operator()(const Position& pos) const {
100
+
101
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
102
+ assert(!pos.checkers()); // Eval is never called when in check
103
+
104
+ // Stalemate detection with lone king
105
+ if (pos.side_to_move() == weakSide && !MoveList<LEGAL>(pos).size())
106
+ return VALUE_DRAW;
107
+
108
+ Square strongKing = pos.square<KING>(strongSide);
109
+ Square weakKing = pos.square<KING>(weakSide);
110
+
111
+ Value result = pos.non_pawn_material(strongSide)
112
+ + pos.count<PAWN>(strongSide) * PawnValueEg
113
+ + push_to_edge(weakKing)
114
+ + push_close(strongKing, weakKing);
115
+
116
+ if ( pos.count<QUEEN>(strongSide)
117
+ || pos.count<ROOK>(strongSide)
118
+ ||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide))
119
+ || ( (pos.pieces(strongSide, BISHOP) & ~DarkSquares)
120
+ && (pos.pieces(strongSide, BISHOP) & DarkSquares)))
121
+ result = std::min(result + VALUE_KNOWN_WIN, VALUE_TB_WIN_IN_MAX_PLY - 1);
122
+
123
+ return strongSide == pos.side_to_move() ? result : -result;
124
+ }
125
+
126
+
127
+ /// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
128
+ /// defending king towards a corner square that our bishop attacks.
129
+ template<>
130
+ Value Endgame<KBNK>::operator()(const Position& pos) const {
131
+
132
+ assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0));
133
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
134
+
135
+ Square strongKing = pos.square<KING>(strongSide);
136
+ Square strongBishop = pos.square<BISHOP>(strongSide);
137
+ Square weakKing = pos.square<KING>(weakSide);
138
+
139
+ // If our bishop does not attack A1/H8, we flip the enemy king square
140
+ // to drive to opposite corners (A8/H1).
141
+
142
+ Value result = (VALUE_KNOWN_WIN + 3520)
143
+ + push_close(strongKing, weakKing)
144
+ + 420 * push_to_corner(opposite_colors(strongBishop, SQ_A1) ? flip_file(weakKing) : weakKing);
145
+
146
+ assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY);
147
+ return strongSide == pos.side_to_move() ? result : -result;
148
+ }
149
+
150
+
151
+ /// KP vs K. This endgame is evaluated with the help of a bitbase
152
+ template<>
153
+ Value Endgame<KPK>::operator()(const Position& pos) const {
154
+
155
+ assert(verify_material(pos, strongSide, VALUE_ZERO, 1));
156
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
157
+
158
+ // Assume strongSide is white and the pawn is on files A-D
159
+ Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
160
+ Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
161
+ Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
162
+
163
+ Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
164
+
165
+ if (!Bitbases::probe(strongKing, strongPawn, weakKing, us))
166
+ return VALUE_DRAW;
167
+
168
+ Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(strongPawn));
169
+
170
+ return strongSide == pos.side_to_move() ? result : -result;
171
+ }
172
+
173
+
174
+ /// KR vs KP. This is a somewhat tricky endgame to evaluate precisely without
175
+ /// a bitbase. The function below returns drawish scores when the pawn is
176
+ /// far advanced with support of the king, while the attacking king is far
177
+ /// away.
178
+ template<>
179
+ Value Endgame<KRKP>::operator()(const Position& pos) const {
180
+
181
+ assert(verify_material(pos, strongSide, RookValueMg, 0));
182
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
183
+
184
+ Square strongKing = pos.square<KING>(strongSide);
185
+ Square weakKing = pos.square<KING>(weakSide);
186
+ Square strongRook = pos.square<ROOK>(strongSide);
187
+ Square weakPawn = pos.square<PAWN>(weakSide);
188
+ Square queeningSquare = make_square(file_of(weakPawn), relative_rank(weakSide, RANK_8));
189
+ Value result;
190
+
191
+ // If the stronger side's king is in front of the pawn, it's a win
192
+ if (forward_file_bb(strongSide, strongKing) & weakPawn)
193
+ result = RookValueEg - distance(strongKing, weakPawn);
194
+
195
+ // If the weaker side's king is too far from the pawn and the rook,
196
+ // it's a win.
197
+ else if ( distance(weakKing, weakPawn) >= 3 + (pos.side_to_move() == weakSide)
198
+ && distance(weakKing, strongRook) >= 3)
199
+ result = RookValueEg - distance(strongKing, weakPawn);
200
+
201
+ // If the pawn is far advanced and supported by the defending king,
202
+ // the position is drawish
203
+ else if ( relative_rank(strongSide, weakKing) <= RANK_3
204
+ && distance(weakKing, weakPawn) == 1
205
+ && relative_rank(strongSide, strongKing) >= RANK_4
206
+ && distance(strongKing, weakPawn) > 2 + (pos.side_to_move() == strongSide))
207
+ result = Value(80) - 8 * distance(strongKing, weakPawn);
208
+
209
+ else
210
+ result = Value(200) - 8 * ( distance(strongKing, weakPawn + pawn_push(weakSide))
211
+ - distance(weakKing, weakPawn + pawn_push(weakSide))
212
+ - distance(weakPawn, queeningSquare));
213
+
214
+ return strongSide == pos.side_to_move() ? result : -result;
215
+ }
216
+
217
+
218
+ /// KR vs KB. This is very simple, and always returns drawish scores. The
219
+ /// score is slightly bigger when the defending king is close to the edge.
220
+ template<>
221
+ Value Endgame<KRKB>::operator()(const Position& pos) const {
222
+
223
+ assert(verify_material(pos, strongSide, RookValueMg, 0));
224
+ assert(verify_material(pos, weakSide, BishopValueMg, 0));
225
+
226
+ Value result = Value(push_to_edge(pos.square<KING>(weakSide)));
227
+ return strongSide == pos.side_to_move() ? result : -result;
228
+ }
229
+
230
+
231
+ /// KR vs KN. The attacking side has slightly better winning chances than
232
+ /// in KR vs KB, particularly if the king and the knight are far apart.
233
+ template<>
234
+ Value Endgame<KRKN>::operator()(const Position& pos) const {
235
+
236
+ assert(verify_material(pos, strongSide, RookValueMg, 0));
237
+ assert(verify_material(pos, weakSide, KnightValueMg, 0));
238
+
239
+ Square weakKing = pos.square<KING>(weakSide);
240
+ Square weakKnight = pos.square<KNIGHT>(weakSide);
241
+ Value result = Value(push_to_edge(weakKing) + push_away(weakKing, weakKnight));
242
+ return strongSide == pos.side_to_move() ? result : -result;
243
+ }
244
+
245
+
246
+ /// KQ vs KP. In general, this is a win for the stronger side, but there are a
247
+ /// few important exceptions. A pawn on 7th rank and on the A,C,F or H files
248
+ /// with a king positioned next to it can be a draw, so in that case, we only
249
+ /// use the distance between the kings.
250
+ template<>
251
+ Value Endgame<KQKP>::operator()(const Position& pos) const {
252
+
253
+ assert(verify_material(pos, strongSide, QueenValueMg, 0));
254
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
255
+
256
+ Square strongKing = pos.square<KING>(strongSide);
257
+ Square weakKing = pos.square<KING>(weakSide);
258
+ Square weakPawn = pos.square<PAWN>(weakSide);
259
+
260
+ Value result = Value(push_close(strongKing, weakKing));
261
+
262
+ if ( relative_rank(weakSide, weakPawn) != RANK_7
263
+ || distance(weakKing, weakPawn) != 1
264
+ || ((FileBBB | FileDBB | FileEBB | FileGBB) & weakPawn))
265
+ result += QueenValueEg - PawnValueEg;
266
+
267
+ return strongSide == pos.side_to_move() ? result : -result;
268
+ }
269
+
270
+
271
+ /// KQ vs KR. This is almost identical to KX vs K: we give the attacking
272
+ /// king a bonus for having the kings close together, and for forcing the
273
+ /// defending king towards the edge. If we also take care to avoid null move for
274
+ /// the defending side in the search, this is usually sufficient to win KQ vs KR.
275
+ template<>
276
+ Value Endgame<KQKR>::operator()(const Position& pos) const {
277
+
278
+ assert(verify_material(pos, strongSide, QueenValueMg, 0));
279
+ assert(verify_material(pos, weakSide, RookValueMg, 0));
280
+
281
+ Square strongKing = pos.square<KING>(strongSide);
282
+ Square weakKing = pos.square<KING>(weakSide);
283
+
284
+ Value result = QueenValueEg
285
+ - RookValueEg
286
+ + push_to_edge(weakKing)
287
+ + push_close(strongKing, weakKing);
288
+
289
+ return strongSide == pos.side_to_move() ? result : -result;
290
+ }
291
+
292
+
293
+ /// KNN vs KP. Very drawish, but there are some mate opportunities if we can
294
+ /// press the weakSide King to a corner before the pawn advances too much.
295
+ template<>
296
+ Value Endgame<KNNKP>::operator()(const Position& pos) const {
297
+
298
+ assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0));
299
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
300
+
301
+ Square weakKing = pos.square<KING>(weakSide);
302
+ Square weakPawn = pos.square<PAWN>(weakSide);
303
+
304
+ Value result = PawnValueEg
305
+ + 2 * push_to_edge(weakKing)
306
+ - 10 * relative_rank(weakSide, weakPawn);
307
+
308
+ return strongSide == pos.side_to_move() ? result : -result;
309
+ }
310
+
311
+
312
+ /// Some cases of trivial draws
313
+ template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; }
314
+
315
+
316
+ /// KB and one or more pawns vs K. It checks for draws with rook pawns and
317
+ /// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW
318
+ /// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
319
+ /// will be used.
320
+ template<>
321
+ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
322
+
323
+ assert(pos.non_pawn_material(strongSide) == BishopValueMg);
324
+ assert(pos.count<PAWN>(strongSide) >= 1);
325
+
326
+ // No assertions about the material of weakSide, because we want draws to
327
+ // be detected even when the weaker side has some pawns.
328
+
329
+ Bitboard strongPawns = pos.pieces(strongSide, PAWN);
330
+ Bitboard allPawns = pos.pieces(PAWN);
331
+
332
+ Square strongBishop = pos.square<BISHOP>(strongSide);
333
+ Square weakKing = pos.square<KING>(weakSide);
334
+ Square strongKing = pos.square<KING>(strongSide);
335
+
336
+ // All strongSide pawns are on a single rook file?
337
+ if (!(strongPawns & ~FileABB) || !(strongPawns & ~FileHBB))
338
+ {
339
+ Square queeningSquare = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8));
340
+
341
+ if ( opposite_colors(queeningSquare, strongBishop)
342
+ && distance(queeningSquare, weakKing) <= 1)
343
+ return SCALE_FACTOR_DRAW;
344
+ }
345
+
346
+ // If all the pawns are on the same B or G file, then it's potentially a draw
347
+ if ((!(allPawns & ~FileBBB) || !(allPawns & ~FileGBB))
348
+ && pos.non_pawn_material(weakSide) == 0
349
+ && pos.count<PAWN>(weakSide) >= 1)
350
+ {
351
+ // Get the least advanced weakSide pawn
352
+ Square weakPawn = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN));
353
+
354
+ // There's potential for a draw if our pawn is blocked on the 7th rank,
355
+ // the bishop cannot attack it or they only have one pawn left.
356
+ if ( relative_rank(strongSide, weakPawn) == RANK_7
357
+ && (strongPawns & (weakPawn + pawn_push(weakSide)))
358
+ && (opposite_colors(strongBishop, weakPawn) || !more_than_one(strongPawns)))
359
+ {
360
+ int strongKingDist = distance(weakPawn, strongKing);
361
+ int weakKingDist = distance(weakPawn, weakKing);
362
+
363
+ // It's a draw if the weak king is on its back two ranks, within 2
364
+ // squares of the blocking pawn and the strong king is not
365
+ // closer. (I think this rule only fails in practically
366
+ // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w
367
+ // and positions where qsearch will immediately correct the
368
+ // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w).
369
+ if ( relative_rank(strongSide, weakKing) >= RANK_7
370
+ && weakKingDist <= 2
371
+ && weakKingDist <= strongKingDist)
372
+ return SCALE_FACTOR_DRAW;
373
+ }
374
+ }
375
+
376
+ return SCALE_FACTOR_NONE;
377
+ }
378
+
379
+
380
+ /// KQ vs KR and one or more pawns. It tests for fortress draws with a rook on
381
+ /// the third rank defended by a pawn.
382
+ template<>
383
+ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
384
+
385
+ assert(verify_material(pos, strongSide, QueenValueMg, 0));
386
+ assert(pos.count<ROOK>(weakSide) == 1);
387
+ assert(pos.count<PAWN>(weakSide) >= 1);
388
+
389
+ Square strongKing = pos.square<KING>(strongSide);
390
+ Square weakKing = pos.square<KING>(weakSide);
391
+ Square weakRook = pos.square<ROOK>(weakSide);
392
+
393
+ if ( relative_rank(weakSide, weakKing) <= RANK_2
394
+ && relative_rank(weakSide, strongKing) >= RANK_4
395
+ && relative_rank(weakSide, weakRook) == RANK_3
396
+ && ( pos.pieces(weakSide, PAWN)
397
+ & attacks_bb<KING>(weakKing)
398
+ & pawn_attacks_bb(strongSide, weakRook)))
399
+ return SCALE_FACTOR_DRAW;
400
+
401
+ return SCALE_FACTOR_NONE;
402
+ }
403
+
404
+
405
+ /// KRP vs KR. This function knows a handful of the most important classes of
406
+ /// drawn positions, but is far from perfect. It would probably be a good idea
407
+ /// to add more knowledge in the future.
408
+ ///
409
+ /// It would also be nice to rewrite the actual code for this function,
410
+ /// which is mostly copied from Glaurung 1.x, and isn't very pretty.
411
+ template<>
412
+ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
413
+
414
+ assert(verify_material(pos, strongSide, RookValueMg, 1));
415
+ assert(verify_material(pos, weakSide, RookValueMg, 0));
416
+
417
+ // Assume strongSide is white and the pawn is on files A-D
418
+ Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
419
+ Square strongRook = normalize(pos, strongSide, pos.square<ROOK>(strongSide));
420
+ Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
421
+ Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
422
+ Square weakRook = normalize(pos, strongSide, pos.square<ROOK>(weakSide));
423
+
424
+ File pawnFile = file_of(strongPawn);
425
+ Rank pawnRank = rank_of(strongPawn);
426
+ Square queeningSquare = make_square(pawnFile, RANK_8);
427
+ int tempo = (pos.side_to_move() == strongSide);
428
+
429
+ // If the pawn is not too far advanced and the defending king defends the
430
+ // queening square, use the third-rank defence.
431
+ if ( pawnRank <= RANK_5
432
+ && distance(weakKing, queeningSquare) <= 1
433
+ && strongKing <= SQ_H5
434
+ && (rank_of(weakRook) == RANK_6 || (pawnRank <= RANK_3 && rank_of(strongRook) != RANK_6)))
435
+ return SCALE_FACTOR_DRAW;
436
+
437
+ // The defending side saves a draw by checking from behind in case the pawn
438
+ // has advanced to the 6th rank with the king behind.
439
+ if ( pawnRank == RANK_6
440
+ && distance(weakKing, queeningSquare) <= 1
441
+ && rank_of(strongKing) + tempo <= RANK_6
442
+ && (rank_of(weakRook) == RANK_1 || (!tempo && distance<File>(weakRook, strongPawn) >= 3)))
443
+ return SCALE_FACTOR_DRAW;
444
+
445
+ if ( pawnRank >= RANK_6
446
+ && weakKing == queeningSquare
447
+ && rank_of(weakRook) == RANK_1
448
+ && (!tempo || distance(strongKing, strongPawn) >= 2))
449
+ return SCALE_FACTOR_DRAW;
450
+
451
+ // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7
452
+ // and the black rook is behind the pawn.
453
+ if ( strongPawn == SQ_A7
454
+ && strongRook == SQ_A8
455
+ && (weakKing == SQ_H7 || weakKing == SQ_G7)
456
+ && file_of(weakRook) == FILE_A
457
+ && (rank_of(weakRook) <= RANK_3 || file_of(strongKing) >= FILE_D || rank_of(strongKing) <= RANK_5))
458
+ return SCALE_FACTOR_DRAW;
459
+
460
+ // If the defending king blocks the pawn and the attacking king is too far
461
+ // away, it's a draw.
462
+ if ( pawnRank <= RANK_5
463
+ && weakKing == strongPawn + NORTH
464
+ && distance(strongKing, strongPawn) - tempo >= 2
465
+ && distance(strongKing, weakRook) - tempo >= 2)
466
+ return SCALE_FACTOR_DRAW;
467
+
468
+ // Pawn on the 7th rank supported by the rook from behind usually wins if the
469
+ // attacking king is closer to the queening square than the defending king,
470
+ // and the defending king cannot gain tempi by threatening the attacking rook.
471
+ if ( pawnRank == RANK_7
472
+ && pawnFile != FILE_A
473
+ && file_of(strongRook) == pawnFile
474
+ && strongRook != queeningSquare
475
+ && (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo)
476
+ && (distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo))
477
+ return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(strongKing, queeningSquare));
478
+
479
+ // Similar to the above, but with the pawn further back
480
+ if ( pawnFile != FILE_A
481
+ && file_of(strongRook) == pawnFile
482
+ && strongRook < strongPawn
483
+ && (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo)
484
+ && (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn + NORTH) - 2 + tempo)
485
+ && ( distance(weakKing, strongRook) + tempo >= 3
486
+ || ( distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo
487
+ && (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn) + tempo))))
488
+ return ScaleFactor( SCALE_FACTOR_MAX
489
+ - 8 * distance(strongPawn, queeningSquare)
490
+ - 2 * distance(strongKing, queeningSquare));
491
+
492
+ // If the pawn is not far advanced and the defending king is somewhere in
493
+ // the pawn's path, it's probably a draw.
494
+ if (pawnRank <= RANK_4 && weakKing > strongPawn)
495
+ {
496
+ if (file_of(weakKing) == file_of(strongPawn))
497
+ return ScaleFactor(10);
498
+ if ( distance<File>(weakKing, strongPawn) == 1
499
+ && distance(strongKing, weakKing) > 2)
500
+ return ScaleFactor(24 - 2 * distance(strongKing, weakKing));
501
+ }
502
+ return SCALE_FACTOR_NONE;
503
+ }
504
+
505
+ template<>
506
+ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
507
+
508
+ assert(verify_material(pos, strongSide, RookValueMg, 1));
509
+ assert(verify_material(pos, weakSide, BishopValueMg, 0));
510
+
511
+ // Test for a rook pawn
512
+ if (pos.pieces(PAWN) & (FileABB | FileHBB))
513
+ {
514
+ Square weakKing = pos.square<KING>(weakSide);
515
+ Square weakBishop = pos.square<BISHOP>(weakSide);
516
+ Square strongKing = pos.square<KING>(strongSide);
517
+ Square strongPawn = pos.square<PAWN>(strongSide);
518
+ Rank pawnRank = relative_rank(strongSide, strongPawn);
519
+ Direction push = pawn_push(strongSide);
520
+
521
+ // If the pawn is on the 5th rank and the pawn (currently) is on
522
+ // the same color square as the bishop then there is a chance of
523
+ // a fortress. Depending on the king position give a moderate
524
+ // reduction or a stronger one if the defending king is near the
525
+ // corner but not trapped there.
526
+ if (pawnRank == RANK_5 && !opposite_colors(weakBishop, strongPawn))
527
+ {
528
+ int d = distance(strongPawn + 3 * push, weakKing);
529
+
530
+ if (d <= 2 && !(d == 0 && weakKing == strongKing + 2 * push))
531
+ return ScaleFactor(24);
532
+ else
533
+ return ScaleFactor(48);
534
+ }
535
+
536
+ // When the pawn has moved to the 6th rank we can be fairly sure
537
+ // it's drawn if the bishop attacks the square in front of the
538
+ // pawn from a reasonable distance and the defending king is near
539
+ // the corner
540
+ if ( pawnRank == RANK_6
541
+ && distance(strongPawn + 2 * push, weakKing) <= 1
542
+ && (attacks_bb<BISHOP>(weakBishop) & (strongPawn + push))
543
+ && distance<File>(weakBishop, strongPawn) >= 2)
544
+ return ScaleFactor(8);
545
+ }
546
+
547
+ return SCALE_FACTOR_NONE;
548
+ }
549
+
550
+ /// KRPP vs KRP. There is just a single rule: if the stronger side has no passed
551
+ /// pawns and the defending king is actively placed, the position is drawish.
552
+ template<>
553
+ ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
554
+
555
+ assert(verify_material(pos, strongSide, RookValueMg, 2));
556
+ assert(verify_material(pos, weakSide, RookValueMg, 1));
557
+
558
+ Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN));
559
+ Square strongPawn2 = msb(pos.pieces(strongSide, PAWN));
560
+ Square weakKing = pos.square<KING>(weakSide);
561
+
562
+ // Does the stronger side have a passed pawn?
563
+ if (pos.pawn_passed(strongSide, strongPawn1) || pos.pawn_passed(strongSide, strongPawn2))
564
+ return SCALE_FACTOR_NONE;
565
+
566
+ Rank pawnRank = std::max(relative_rank(strongSide, strongPawn1), relative_rank(strongSide, strongPawn2));
567
+
568
+ if ( distance<File>(weakKing, strongPawn1) <= 1
569
+ && distance<File>(weakKing, strongPawn2) <= 1
570
+ && relative_rank(strongSide, weakKing) > pawnRank)
571
+ {
572
+ assert(pawnRank > RANK_1 && pawnRank < RANK_7);
573
+ return ScaleFactor(7 * pawnRank);
574
+ }
575
+ return SCALE_FACTOR_NONE;
576
+ }
577
+
578
+
579
+ /// K and two or more pawns vs K. There is just a single rule here: if all pawns
580
+ /// are on the same rook file and are blocked by the defending king, it's a draw.
581
+ template<>
582
+ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
583
+
584
+ assert(pos.non_pawn_material(strongSide) == VALUE_ZERO);
585
+ assert(pos.count<PAWN>(strongSide) >= 2);
586
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
587
+
588
+ Square weakKing = pos.square<KING>(weakSide);
589
+ Bitboard strongPawns = pos.pieces(strongSide, PAWN);
590
+
591
+ // If all pawns are ahead of the king on a single rook file, it's a draw.
592
+ if ( !(strongPawns & ~(FileABB | FileHBB))
593
+ && !(strongPawns & ~passed_pawn_span(weakSide, weakKing)))
594
+ return SCALE_FACTOR_DRAW;
595
+
596
+ return SCALE_FACTOR_NONE;
597
+ }
598
+
599
+
600
+ /// KBP vs KB. There are two rules: if the defending king is somewhere along the
601
+ /// path of the pawn, and the square of the king is not of the same color as the
602
+ /// stronger side's bishop, it's a draw. If the two bishops have opposite color,
603
+ /// it's almost always a draw.
604
+ template<>
605
+ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
606
+
607
+ assert(verify_material(pos, strongSide, BishopValueMg, 1));
608
+ assert(verify_material(pos, weakSide, BishopValueMg, 0));
609
+
610
+ Square strongPawn = pos.square<PAWN>(strongSide);
611
+ Square strongBishop = pos.square<BISHOP>(strongSide);
612
+ Square weakBishop = pos.square<BISHOP>(weakSide);
613
+ Square weakKing = pos.square<KING>(weakSide);
614
+
615
+ // Case 1: Defending king blocks the pawn, and cannot be driven away
616
+ if ( (forward_file_bb(strongSide, strongPawn) & weakKing)
617
+ && ( opposite_colors(weakKing, strongBishop)
618
+ || relative_rank(strongSide, weakKing) <= RANK_6))
619
+ return SCALE_FACTOR_DRAW;
620
+
621
+ // Case 2: Opposite colored bishops
622
+ if (opposite_colors(strongBishop, weakBishop))
623
+ return SCALE_FACTOR_DRAW;
624
+
625
+ return SCALE_FACTOR_NONE;
626
+ }
627
+
628
+
629
+ /// KBPP vs KB. It detects a few basic draws with opposite-colored bishops
630
+ template<>
631
+ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
632
+
633
+ assert(verify_material(pos, strongSide, BishopValueMg, 2));
634
+ assert(verify_material(pos, weakSide, BishopValueMg, 0));
635
+
636
+ Square strongBishop = pos.square<BISHOP>(strongSide);
637
+ Square weakBishop = pos.square<BISHOP>(weakSide);
638
+
639
+ if (!opposite_colors(strongBishop, weakBishop))
640
+ return SCALE_FACTOR_NONE;
641
+
642
+ Square weakKing = pos.square<KING>(weakSide);
643
+ Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN));
644
+ Square strongPawn2 = msb(pos.pieces(strongSide, PAWN));
645
+ Square blockSq1, blockSq2;
646
+
647
+ if (relative_rank(strongSide, strongPawn1) > relative_rank(strongSide, strongPawn2))
648
+ {
649
+ blockSq1 = strongPawn1 + pawn_push(strongSide);
650
+ blockSq2 = make_square(file_of(strongPawn2), rank_of(strongPawn1));
651
+ }
652
+ else
653
+ {
654
+ blockSq1 = strongPawn2 + pawn_push(strongSide);
655
+ blockSq2 = make_square(file_of(strongPawn1), rank_of(strongPawn2));
656
+ }
657
+
658
+ switch (distance<File>(strongPawn1, strongPawn2))
659
+ {
660
+ case 0:
661
+ // Both pawns are on the same file. It's an easy draw if the defender firmly
662
+ // controls some square in the frontmost pawn's path.
663
+ if ( file_of(weakKing) == file_of(blockSq1)
664
+ && relative_rank(strongSide, weakKing) >= relative_rank(strongSide, blockSq1)
665
+ && opposite_colors(weakKing, strongBishop))
666
+ return SCALE_FACTOR_DRAW;
667
+ else
668
+ return SCALE_FACTOR_NONE;
669
+
670
+ case 1:
671
+ // Pawns on adjacent files. It's a draw if the defender firmly controls the
672
+ // square in front of the frontmost pawn's path, and the square diagonally
673
+ // behind this square on the file of the other pawn.
674
+ if ( weakKing == blockSq1
675
+ && opposite_colors(weakKing, strongBishop)
676
+ && ( weakBishop == blockSq2
677
+ || (attacks_bb<BISHOP>(blockSq2, pos.pieces()) & pos.pieces(weakSide, BISHOP))
678
+ || distance<Rank>(strongPawn1, strongPawn2) >= 2))
679
+ return SCALE_FACTOR_DRAW;
680
+
681
+ else if ( weakKing == blockSq2
682
+ && opposite_colors(weakKing, strongBishop)
683
+ && ( weakBishop == blockSq1
684
+ || (attacks_bb<BISHOP>(blockSq1, pos.pieces()) & pos.pieces(weakSide, BISHOP))))
685
+ return SCALE_FACTOR_DRAW;
686
+ else
687
+ return SCALE_FACTOR_NONE;
688
+
689
+ default:
690
+ // The pawns are not on the same file or adjacent files. No scaling.
691
+ return SCALE_FACTOR_NONE;
692
+ }
693
+ }
694
+
695
+
696
+ /// KBP vs KN. There is a single rule: if the defending king is somewhere along
697
+ /// the path of the pawn, and the square of the king is not of the same color as
698
+ /// the stronger side's bishop, it's a draw.
699
+ template<>
700
+ ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
701
+
702
+ assert(verify_material(pos, strongSide, BishopValueMg, 1));
703
+ assert(verify_material(pos, weakSide, KnightValueMg, 0));
704
+
705
+ Square strongPawn = pos.square<PAWN>(strongSide);
706
+ Square strongBishop = pos.square<BISHOP>(strongSide);
707
+ Square weakKing = pos.square<KING>(weakSide);
708
+
709
+ if ( file_of(weakKing) == file_of(strongPawn)
710
+ && relative_rank(strongSide, strongPawn) < relative_rank(strongSide, weakKing)
711
+ && ( opposite_colors(weakKing, strongBishop)
712
+ || relative_rank(strongSide, weakKing) <= RANK_6))
713
+ return SCALE_FACTOR_DRAW;
714
+
715
+ return SCALE_FACTOR_NONE;
716
+ }
717
+
718
+
719
+ /// KP vs KP. This is done by removing the weakest side's pawn and probing the
720
+ /// KP vs K bitbase: if the weakest side has a draw without the pawn, it probably
721
+ /// has at least a draw with the pawn as well. The exception is when the stronger
722
+ /// side's pawn is far advanced and not on a rook file; in this case it is often
723
+ /// possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
724
+ template<>
725
+ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
726
+
727
+ assert(verify_material(pos, strongSide, VALUE_ZERO, 1));
728
+ assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
729
+
730
+ // Assume strongSide is white and the pawn is on files A-D
731
+ Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
732
+ Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
733
+ Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
734
+
735
+ Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
736
+
737
+ // If the pawn has advanced to the fifth rank or further, and is not a
738
+ // rook pawn, it's too dangerous to assume that it's at least a draw.
739
+ if (rank_of(strongPawn) >= RANK_5 && file_of(strongPawn) != FILE_A)
740
+ return SCALE_FACTOR_NONE;
741
+
742
+ // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
743
+ // it's probably at least a draw even with the pawn.
744
+ return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
745
+ }
746
+
747
+ } // namespace Stockfish
src/endgame.h ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef ENDGAME_H_INCLUDED
20
+ #define ENDGAME_H_INCLUDED
21
+
22
+ #include <memory>
23
+ #include <string>
24
+ #include <type_traits>
25
+ #include <unordered_map>
26
+ #include <utility>
27
+
28
+ #include "position.h"
29
+ #include "types.h"
30
+
31
+ namespace Stockfish {
32
+
33
+ /// EndgameCode lists all supported endgame functions by corresponding codes
34
+
35
+ enum EndgameCode {
36
+
37
+ EVALUATION_FUNCTIONS,
38
+ KNNK, // KNN vs K
39
+ KNNKP, // KNN vs KP
40
+ KXK, // Generic "mate lone king" eval
41
+ KBNK, // KBN vs K
42
+ KPK, // KP vs K
43
+ KRKP, // KR vs KP
44
+ KRKB, // KR vs KB
45
+ KRKN, // KR vs KN
46
+ KQKP, // KQ vs KP
47
+ KQKR, // KQ vs KR
48
+
49
+ SCALING_FUNCTIONS,
50
+ KBPsK, // KB and pawns vs K
51
+ KQKRPs, // KQ vs KR and pawns
52
+ KRPKR, // KRP vs KR
53
+ KRPKB, // KRP vs KB
54
+ KRPPKRP, // KRPP vs KRP
55
+ KPsK, // K and pawns vs K
56
+ KBPKB, // KBP vs KB
57
+ KBPPKB, // KBPP vs KB
58
+ KBPKN, // KBP vs KN
59
+ KPKP // KP vs KP
60
+ };
61
+
62
+
63
+ /// Endgame functions can be of two types depending on whether they return a
64
+ /// Value or a ScaleFactor.
65
+
66
+ template<EndgameCode E> using
67
+ eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type;
68
+
69
+
70
+ /// Base and derived functors for endgame evaluation and scaling functions
71
+
72
+ template<typename T>
73
+ struct EndgameBase {
74
+
75
+ explicit EndgameBase(Color c) : strongSide(c), weakSide(~c) {}
76
+ virtual ~EndgameBase() = default;
77
+ virtual T operator()(const Position&) const = 0;
78
+
79
+ const Color strongSide, weakSide;
80
+ };
81
+
82
+
83
+ template<EndgameCode E, typename T = eg_type<E>>
84
+ struct Endgame : public EndgameBase<T> {
85
+
86
+ explicit Endgame(Color c) : EndgameBase<T>(c) {}
87
+ T operator()(const Position&) const override;
88
+ };
89
+
90
+
91
+ /// The Endgames namespace handles the pointers to endgame evaluation and scaling
92
+ /// base objects in two std::map. We use polymorphism to invoke the actual
93
+ /// endgame function by calling its virtual operator().
94
+
95
+ namespace Endgames {
96
+
97
+ template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>;
98
+ template<typename T> using Map = std::unordered_map<Key, Ptr<T>>;
99
+
100
+ extern std::pair<Map<Value>, Map<ScaleFactor>> maps;
101
+
102
+ void init();
103
+
104
+ template<typename T>
105
+ Map<T>& map() {
106
+ return std::get<std::is_same<T, ScaleFactor>::value>(maps);
107
+ }
108
+
109
+ template<EndgameCode E, typename T = eg_type<E>>
110
+ void add(const std::string& code) {
111
+
112
+ StateInfo st;
113
+ map<T>()[Position().set(code, WHITE, &st).material_key()] = Ptr<T>(new Endgame<E>(WHITE));
114
+ map<T>()[Position().set(code, BLACK, &st).material_key()] = Ptr<T>(new Endgame<E>(BLACK));
115
+ }
116
+
117
+ template<typename T>
118
+ const EndgameBase<T>* probe(Key key) {
119
+ auto it = map<T>().find(key);
120
+ return it != map<T>().end() ? it->second.get() : nullptr;
121
+ }
122
+ }
123
+
124
+ } // namespace Stockfish
125
+
126
+ #endif // #ifndef ENDGAME_H_INCLUDED
src/evaluate.cpp ADDED
@@ -0,0 +1,1171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <algorithm>
20
+ #include <cassert>
21
+ #include <cstdlib>
22
+ #include <cstring> // For std::memset
23
+ #include <fstream>
24
+ #include <iomanip>
25
+ #include <sstream>
26
+ #include <iostream>
27
+ #include <streambuf>
28
+ #include <vector>
29
+
30
+ #include "bitboard.h"
31
+ #include "evaluate.h"
32
+ #include "material.h"
33
+ #include "misc.h"
34
+ #include "pawns.h"
35
+ #include "thread.h"
36
+ #include "timeman.h"
37
+ #include "uci.h"
38
+ #include "incbin/incbin.h"
39
+
40
+
41
+ // Macro to embed the default efficiently updatable neural network (NNUE) file
42
+ // data in the engine binary (using incbin.h, by Dale Weiler).
43
+ // This macro invocation will declare the following three variables
44
+ // const unsigned char gEmbeddedNNUEData[]; // a pointer to the embedded data
45
+ // const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end
46
+ // const unsigned int gEmbeddedNNUESize; // the size of the embedded file
47
+ // Note that this does not work in Microsoft Visual Studio.
48
+ #if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF)
49
+ INCBIN(EmbeddedNNUE, EvalFileDefaultName);
50
+ #else
51
+ const unsigned char gEmbeddedNNUEData[1] = {0x0};
52
+ const unsigned char *const gEmbeddedNNUEEnd = &gEmbeddedNNUEData[1];
53
+ const unsigned int gEmbeddedNNUESize = 1;
54
+ #endif
55
+
56
+
57
+ using namespace std;
58
+
59
+ namespace Stockfish {
60
+
61
+ namespace Eval {
62
+
63
+ bool useNNUE;
64
+ string currentEvalFileName = "None";
65
+
66
+ /// NNUE::init() tries to load a NNUE network at startup time, or when the engine
67
+ /// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue"
68
+ /// The name of the NNUE network is always retrieved from the EvalFile option.
69
+ /// We search the given network in three locations: internally (the default
70
+ /// network may be embedded in the binary), in the active working directory and
71
+ /// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY
72
+ /// variable to have the engine search in a special directory in their distro.
73
+
74
+ void NNUE::init() {
75
+
76
+ useNNUE = Options["Use NNUE"];
77
+ if (!useNNUE)
78
+ return;
79
+
80
+ string eval_file = string(Options["EvalFile"]);
81
+ if (eval_file.empty())
82
+ eval_file = EvalFileDefaultName;
83
+
84
+ #if defined(DEFAULT_NNUE_DIRECTORY)
85
+ #define stringify2(x) #x
86
+ #define stringify(x) stringify2(x)
87
+ vector<string> dirs = { "<internal>" , "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) };
88
+ #else
89
+ vector<string> dirs = { "<internal>" , "" , CommandLine::binaryDirectory };
90
+ #endif
91
+
92
+ for (string directory : dirs)
93
+ if (currentEvalFileName != eval_file)
94
+ {
95
+ if (directory != "<internal>")
96
+ {
97
+ ifstream stream(directory + eval_file, ios::binary);
98
+ if (load_eval(eval_file, stream))
99
+ currentEvalFileName = eval_file;
100
+ }
101
+
102
+ if (directory == "<internal>" && eval_file == EvalFileDefaultName)
103
+ {
104
+ // C++ way to prepare a buffer for a memory stream
105
+ class MemoryBuffer : public basic_streambuf<char> {
106
+ public: MemoryBuffer(char* p, size_t n) { setg(p, p, p + n); setp(p, p + n); }
107
+ };
108
+
109
+ MemoryBuffer buffer(const_cast<char*>(reinterpret_cast<const char*>(gEmbeddedNNUEData)),
110
+ size_t(gEmbeddedNNUESize));
111
+ (void) gEmbeddedNNUEEnd; // Silence warning on unused variable
112
+
113
+ istream stream(&buffer);
114
+ if (load_eval(eval_file, stream))
115
+ currentEvalFileName = eval_file;
116
+ }
117
+ }
118
+ }
119
+
120
+ /// NNUE::verify() verifies that the last net used was loaded successfully
121
+ void NNUE::verify() {
122
+
123
+ string eval_file = string(Options["EvalFile"]);
124
+ if (eval_file.empty())
125
+ eval_file = EvalFileDefaultName;
126
+
127
+ if (useNNUE && currentEvalFileName != eval_file)
128
+ {
129
+
130
+ string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available.";
131
+ string msg2 = "The option is set to true, but the network file " + eval_file + " was not loaded successfully.";
132
+ string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file.";
133
+ string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + std::string(EvalFileDefaultName);
134
+ string msg5 = "The engine will be terminated now.";
135
+
136
+ sync_cout << "info string ERROR: " << msg1 << sync_endl;
137
+ sync_cout << "info string ERROR: " << msg2 << sync_endl;
138
+ sync_cout << "info string ERROR: " << msg3 << sync_endl;
139
+ sync_cout << "info string ERROR: " << msg4 << sync_endl;
140
+ sync_cout << "info string ERROR: " << msg5 << sync_endl;
141
+
142
+ exit(EXIT_FAILURE);
143
+ }
144
+
145
+ if (useNNUE)
146
+ sync_cout << "info string NNUE evaluation using " << eval_file << " enabled" << sync_endl;
147
+ else
148
+ sync_cout << "info string classical evaluation enabled" << sync_endl;
149
+ }
150
+ }
151
+
152
+ namespace Trace {
153
+
154
+ enum Tracing { NO_TRACE, TRACE };
155
+
156
+ enum Term { // The first 8 entries are reserved for PieceType
157
+ MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, WINNABLE, TOTAL, TERM_NB
158
+ };
159
+
160
+ Score scores[TERM_NB][COLOR_NB];
161
+
162
+ double to_cp(Value v) { return double(v) / UCI::NormalizeToPawnValue; }
163
+
164
+ void add(int idx, Color c, Score s) {
165
+ scores[idx][c] = s;
166
+ }
167
+
168
+ void add(int idx, Score w, Score b = SCORE_ZERO) {
169
+ scores[idx][WHITE] = w;
170
+ scores[idx][BLACK] = b;
171
+ }
172
+
173
+ std::ostream& operator<<(std::ostream& os, Score s) {
174
+ os << std::setw(5) << to_cp(mg_value(s)) << " "
175
+ << std::setw(5) << to_cp(eg_value(s));
176
+ return os;
177
+ }
178
+
179
+ std::ostream& operator<<(std::ostream& os, Term t) {
180
+
181
+ if (t == MATERIAL || t == IMBALANCE || t == WINNABLE || t == TOTAL)
182
+ os << " ---- ----" << " | " << " ---- ----";
183
+ else
184
+ os << scores[t][WHITE] << " | " << scores[t][BLACK];
185
+
186
+ os << " | " << scores[t][WHITE] - scores[t][BLACK] << " |\n";
187
+ return os;
188
+ }
189
+ }
190
+
191
+ using namespace Trace;
192
+
193
+ namespace {
194
+
195
+ // Threshold for lazy and space evaluation
196
+ constexpr Value LazyThreshold1 = Value(3631);
197
+ constexpr Value LazyThreshold2 = Value(2084);
198
+ constexpr Value SpaceThreshold = Value(11551);
199
+
200
+ // KingAttackWeights[PieceType] contains king attack weights by piece type
201
+ constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 76, 46, 45, 14 };
202
+
203
+ // SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type,
204
+ // higher if multiple safe checks are possible for that piece type.
205
+ constexpr int SafeCheck[][2] = {
206
+ {}, {}, {805, 1292}, {650, 984}, {1071, 1886}, {730, 1128}
207
+ };
208
+
209
+ #define S(mg, eg) make_score(mg, eg)
210
+
211
+ // MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game,
212
+ // indexed by piece type and number of attacked squares in the mobility area.
213
+ constexpr Score MobilityBonus[][32] = {
214
+ { S(-62,-79), S(-53,-57), S(-12,-31), S( -3,-17), S( 3, 7), S( 12, 13), // Knight
215
+ S( 21, 16), S( 28, 21), S( 37, 26) },
216
+ { S(-47,-59), S(-20,-25), S( 14, -8), S( 29, 12), S( 39, 21), S( 53, 40), // Bishop
217
+ S( 53, 56), S( 60, 58), S( 62, 65), S( 69, 72), S( 78, 78), S( 83, 87),
218
+ S( 91, 88), S( 96, 98) },
219
+ { S(-60,-82), S(-24,-15), S( 0, 17) ,S( 3, 43), S( 4, 72), S( 14,100), // Rook
220
+ S( 20,102), S( 30,122), S( 41,133), S(41 ,139), S( 41,153), S( 45,160),
221
+ S( 57,165), S( 58,170), S( 67,175) },
222
+ { S(-29,-49), S(-16,-29), S( -8, -8), S( -8, 17), S( 18, 39), S( 25, 54), // Queen
223
+ S( 23, 59), S( 37, 73), S( 41, 76), S( 54, 95), S( 65, 95) ,S( 68,101),
224
+ S( 69,124), S( 70,128), S( 70,132), S( 70,133) ,S( 71,136), S( 72,140),
225
+ S( 74,147), S( 76,149), S( 90,153), S(104,169), S(105,171), S(106,171),
226
+ S(112,178), S(114,185), S(114,187), S(119,221) }
227
+ };
228
+
229
+ // BishopPawns[distance from edge] contains a file-dependent penalty for pawns on
230
+ // squares of the same color as our bishop.
231
+ constexpr Score BishopPawns[int(FILE_NB) / 2] = {
232
+ S(3, 8), S(3, 9), S(2, 7), S(3, 7)
233
+ };
234
+
235
+ // KingProtector[knight/bishop] contains penalty for each distance unit to own king
236
+ constexpr Score KingProtector[] = { S(9, 9), S(7, 9) };
237
+
238
+ // Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a
239
+ // pawn protected square on rank 4 to 6 which is also safe from a pawn attack.
240
+ constexpr Score Outpost[] = { S(54, 34), S(31, 25) };
241
+
242
+ // PassedRank[Rank] contains a bonus according to the rank of a passed pawn
243
+ constexpr Score PassedRank[RANK_NB] = {
244
+ S(0, 0), S(2, 38), S(15, 36), S(22, 50), S(64, 81), S(166, 184), S(284, 269)
245
+ };
246
+
247
+ constexpr Score RookOnClosedFile = S(10, 5);
248
+ constexpr Score RookOnOpenFile[] = { S(18, 8), S(49, 26) };
249
+
250
+ // ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
251
+ // which piece type attacks which one. Attacks on lesser pieces which are
252
+ // pawn-defended are not considered.
253
+ constexpr Score ThreatByMinor[PIECE_TYPE_NB] = {
254
+ S(0, 0), S(6, 37), S(64, 50), S(82, 57), S(103, 130), S(81, 163)
255
+ };
256
+
257
+ constexpr Score ThreatByRook[PIECE_TYPE_NB] = {
258
+ S(0, 0), S(3, 44), S(36, 71), S(44, 59), S(0, 39), S(60, 39)
259
+ };
260
+
261
+ constexpr Value CorneredBishop = Value(50);
262
+
263
+ // Assorted bonuses and penalties
264
+ constexpr Score UncontestedOutpost = S( 0, 10);
265
+ constexpr Score BishopOnKingRing = S( 24, 0);
266
+ constexpr Score BishopXRayPawns = S( 4, 5);
267
+ constexpr Score FlankAttacks = S( 8, 0);
268
+ constexpr Score Hanging = S( 72, 40);
269
+ constexpr Score KnightOnQueen = S( 16, 11);
270
+ constexpr Score LongDiagonalBishop = S( 45, 0);
271
+ constexpr Score MinorBehindPawn = S( 18, 3);
272
+ constexpr Score PassedFile = S( 13, 8);
273
+ constexpr Score PawnlessFlank = S( 19, 97);
274
+ constexpr Score ReachableOutpost = S( 33, 19);
275
+ constexpr Score RestrictedPiece = S( 6, 7);
276
+ constexpr Score RookOnKingRing = S( 16, 0);
277
+ constexpr Score SliderOnQueen = S( 62, 21);
278
+ constexpr Score ThreatByKing = S( 24, 87);
279
+ constexpr Score ThreatByPawnPush = S( 48, 39);
280
+ constexpr Score ThreatBySafePawn = S(167, 99);
281
+ constexpr Score TrappedRook = S( 55, 13);
282
+ constexpr Score WeakQueenProtection = S( 14, 0);
283
+ constexpr Score WeakQueen = S( 57, 19);
284
+
285
+
286
+ #undef S
287
+
288
+ // Evaluation class computes and stores attacks tables and other working data
289
+ template<Tracing T>
290
+ class Evaluation {
291
+
292
+ public:
293
+ Evaluation() = delete;
294
+ explicit Evaluation(const Position& p) : pos(p) {}
295
+ Evaluation& operator=(const Evaluation&) = delete;
296
+ Value value();
297
+
298
+ private:
299
+ template<Color Us> void initialize();
300
+ template<Color Us, PieceType Pt> Score pieces();
301
+ template<Color Us> Score king() const;
302
+ template<Color Us> Score threats() const;
303
+ template<Color Us> Score passed() const;
304
+ template<Color Us> Score space() const;
305
+ Value winnable(Score score) const;
306
+
307
+ const Position& pos;
308
+ Material::Entry* me;
309
+ Pawns::Entry* pe;
310
+ Bitboard mobilityArea[COLOR_NB];
311
+ Score mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO };
312
+
313
+ // attackedBy[color][piece type] is a bitboard representing all squares
314
+ // attacked by a given color and piece type. Special "piece types" which
315
+ // is also calculated is ALL_PIECES.
316
+ Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
317
+
318
+ // attackedBy2[color] are the squares attacked by at least 2 units of a given
319
+ // color, including x-rays. But diagonal x-rays through pawns are not computed.
320
+ Bitboard attackedBy2[COLOR_NB];
321
+
322
+ // kingRing[color] are the squares adjacent to the king plus some other
323
+ // very near squares, depending on king position.
324
+ Bitboard kingRing[COLOR_NB];
325
+
326
+ // kingAttackersCount[color] is the number of pieces of the given color
327
+ // which attack a square in the kingRing of the enemy king.
328
+ int kingAttackersCount[COLOR_NB];
329
+
330
+ // kingAttackersWeight[color] is the sum of the "weights" of the pieces of
331
+ // the given color which attack a square in the kingRing of the enemy king.
332
+ // The weights of the individual piece types are given by the elements in
333
+ // the KingAttackWeights array.
334
+ int kingAttackersWeight[COLOR_NB];
335
+
336
+ // kingAttacksCount[color] is the number of attacks by the given color to
337
+ // squares directly adjacent to the enemy king. Pieces which attack more
338
+ // than one square are counted multiple times. For instance, if there is
339
+ // a white knight on g5 and black's king is on g8, this white knight adds 2
340
+ // to kingAttacksCount[WHITE].
341
+ int kingAttacksCount[COLOR_NB];
342
+ };
343
+
344
+
345
+ // Evaluation::initialize() computes king and pawn attacks, and the king ring
346
+ // bitboard for a given color. This is done at the beginning of the evaluation.
347
+
348
+ template<Tracing T> template<Color Us>
349
+ void Evaluation<T>::initialize() {
350
+
351
+ constexpr Color Them = ~Us;
352
+ constexpr Direction Up = pawn_push(Us);
353
+ constexpr Direction Down = -Up;
354
+ constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB);
355
+
356
+ const Square ksq = pos.square<KING>(Us);
357
+
358
+ Bitboard dblAttackByPawn = pawn_double_attacks_bb<Us>(pos.pieces(Us, PAWN));
359
+
360
+ // Find our pawns that are blocked or on the first two ranks
361
+ Bitboard b = pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | LowRanks);
362
+
363
+ // Squares occupied by those pawns, by our king or queen, by blockers to attacks on our king
364
+ // or controlled by enemy pawns are excluded from the mobility area.
365
+ mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pos.blockers_for_king(Us) | pe->pawn_attacks(Them));
366
+
367
+ // Initialize attackedBy[] for king and pawns
368
+ attackedBy[Us][KING] = attacks_bb<KING>(ksq);
369
+ attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
370
+ attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
371
+ attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
372
+
373
+ // Init our king safety tables
374
+ Square s = make_square(std::clamp(file_of(ksq), FILE_B, FILE_G),
375
+ std::clamp(rank_of(ksq), RANK_2, RANK_7));
376
+ kingRing[Us] = attacks_bb<KING>(s) | s;
377
+
378
+ kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
379
+ kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
380
+
381
+ // Remove from kingRing[] the squares defended by two pawns
382
+ kingRing[Us] &= ~dblAttackByPawn;
383
+ }
384
+
385
+
386
+ // Evaluation::pieces() scores pieces of a given color and type
387
+
388
+ template<Tracing T> template<Color Us, PieceType Pt>
389
+ Score Evaluation<T>::pieces() {
390
+
391
+ constexpr Color Them = ~Us;
392
+ constexpr Direction Down = -pawn_push(Us);
393
+ constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
394
+ : Rank5BB | Rank4BB | Rank3BB);
395
+ Bitboard b1 = pos.pieces(Us, Pt);
396
+ Bitboard b, bb;
397
+ Score score = SCORE_ZERO;
398
+
399
+ attackedBy[Us][Pt] = 0;
400
+
401
+ while (b1)
402
+ {
403
+ Square s = pop_lsb(b1);
404
+
405
+ // Find attacked squares, including x-ray attacks for bishops and rooks
406
+ b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN))
407
+ : Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK))
408
+ : attacks_bb<Pt>(s, pos.pieces());
409
+
410
+ if (pos.blockers_for_king(Us) & s)
411
+ b &= line_bb(pos.square<KING>(Us), s);
412
+
413
+ attackedBy2[Us] |= attackedBy[Us][ALL_PIECES] & b;
414
+ attackedBy[Us][Pt] |= b;
415
+ attackedBy[Us][ALL_PIECES] |= b;
416
+
417
+ if (b & kingRing[Them])
418
+ {
419
+ kingAttackersCount[Us]++;
420
+ kingAttackersWeight[Us] += KingAttackWeights[Pt];
421
+ kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]);
422
+ }
423
+
424
+ else if (Pt == ROOK && (file_bb(s) & kingRing[Them]))
425
+ score += RookOnKingRing;
426
+
427
+ else if (Pt == BISHOP && (attacks_bb<BISHOP>(s, pos.pieces(PAWN)) & kingRing[Them]))
428
+ score += BishopOnKingRing;
429
+
430
+ int mob = popcount(b & mobilityArea[Us]);
431
+ mobility[Us] += MobilityBonus[Pt - 2][mob];
432
+
433
+ if (Pt == BISHOP || Pt == KNIGHT)
434
+ {
435
+ // Bonus if the piece is on an outpost square or can reach one
436
+ // Bonus for knights (UncontestedOutpost) if few relevant targets
437
+ bb = OutpostRanks & (attackedBy[Us][PAWN] | shift<Down>(pos.pieces(PAWN)))
438
+ & ~pe->pawn_attacks_span(Them);
439
+ Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN);
440
+
441
+ if ( Pt == KNIGHT
442
+ && bb & s & ~CenterFiles // on a side outpost
443
+ && !(b & targets) // no relevant attacks
444
+ && (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide))))
445
+ score += UncontestedOutpost * popcount(pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide));
446
+ else if (bb & s)
447
+ score += Outpost[Pt == BISHOP];
448
+ else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
449
+ score += ReachableOutpost;
450
+
451
+ // Bonus for a knight or bishop shielded by pawn
452
+ if (shift<Down>(pos.pieces(PAWN)) & s)
453
+ score += MinorBehindPawn;
454
+
455
+ // Penalty if the piece is far from the king
456
+ score -= KingProtector[Pt == BISHOP] * distance(pos.square<KING>(Us), s);
457
+
458
+ if constexpr (Pt == BISHOP)
459
+ {
460
+ // Penalty according to the number of our pawns on the same color square as the
461
+ // bishop, bigger when the center files are blocked with pawns and smaller
462
+ // when the bishop is outside the pawn chain.
463
+ Bitboard blocked = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces());
464
+
465
+ score -= BishopPawns[edge_distance(file_of(s))] * pos.pawns_on_same_color_squares(Us, s)
466
+ * (!(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles));
467
+
468
+ // Penalty for all enemy pawns x-rayed
469
+ score -= BishopXRayPawns * popcount(attacks_bb<BISHOP>(s) & pos.pieces(Them, PAWN));
470
+
471
+ // Bonus for bishop on a long diagonal which can "see" both center squares
472
+ if (more_than_one(attacks_bb<BISHOP>(s, pos.pieces(PAWN)) & Center))
473
+ score += LongDiagonalBishop;
474
+
475
+ // An important Chess960 pattern: a cornered bishop blocked by a friendly
476
+ // pawn diagonally in front of it is a very serious problem, especially
477
+ // when that pawn is also blocked.
478
+ if ( pos.is_chess960()
479
+ && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
480
+ {
481
+ Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
482
+ if (pos.piece_on(s + d) == make_piece(Us, PAWN))
483
+ score -= !pos.empty(s + d + pawn_push(Us)) ? 4 * make_score(CorneredBishop, CorneredBishop)
484
+ : 3 * make_score(CorneredBishop, CorneredBishop);
485
+ }
486
+ }
487
+ }
488
+
489
+ if constexpr (Pt == ROOK)
490
+ {
491
+ // Bonuses for rook on a (semi-)open or closed file
492
+ if (pos.is_on_semiopen_file(Us, s))
493
+ {
494
+ score += RookOnOpenFile[pos.is_on_semiopen_file(Them, s)];
495
+ }
496
+ else
497
+ {
498
+ // If our pawn on this file is blocked, increase penalty
499
+ if ( pos.pieces(Us, PAWN)
500
+ & shift<Down>(pos.pieces())
501
+ & file_bb(s))
502
+ {
503
+ score -= RookOnClosedFile;
504
+ }
505
+
506
+ // Penalty when trapped by the king, even more if the king cannot castle
507
+ if (mob <= 3)
508
+ {
509
+ File kf = file_of(pos.square<KING>(Us));
510
+ if ((kf < FILE_E) == (file_of(s) < kf))
511
+ score -= TrappedRook * (1 + !pos.castling_rights(Us));
512
+ }
513
+ }
514
+ }
515
+
516
+ if constexpr (Pt == QUEEN)
517
+ {
518
+ // Penalty if any relative pin or discovered attack against the queen
519
+ Bitboard queenPinners;
520
+ if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners))
521
+ score -= WeakQueen;
522
+ }
523
+ }
524
+ if constexpr (T)
525
+ Trace::add(Pt, Us, score);
526
+
527
+ return score;
528
+ }
529
+
530
+
531
+ // Evaluation::king() assigns bonuses and penalties to a king of a given color
532
+
533
+ template<Tracing T> template<Color Us>
534
+ Score Evaluation<T>::king() const {
535
+
536
+ constexpr Color Them = ~Us;
537
+ constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB
538
+ : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB);
539
+
540
+ Bitboard weak, b1, b2, b3, safe, unsafeChecks = 0;
541
+ Bitboard rookChecks, queenChecks, bishopChecks, knightChecks;
542
+ int kingDanger = 0;
543
+ const Square ksq = pos.square<KING>(Us);
544
+
545
+ // Init the score with king shelter and enemy pawns storm
546
+ Score score = pe->king_safety<Us>(pos);
547
+
548
+ // Attacked squares defended at most once by our queen or king
549
+ weak = attackedBy[Them][ALL_PIECES]
550
+ & ~attackedBy2[Us]
551
+ & (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]);
552
+
553
+ // Analyse the safe enemy's checks which are possible on next move
554
+ safe = ~pos.pieces(Them);
555
+ safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]);
556
+
557
+ b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
558
+ b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
559
+
560
+ // Enemy rooks checks
561
+ rookChecks = b1 & attackedBy[Them][ROOK] & safe;
562
+ if (rookChecks)
563
+ kingDanger += SafeCheck[ROOK][more_than_one(rookChecks)];
564
+ else
565
+ unsafeChecks |= b1 & attackedBy[Them][ROOK];
566
+
567
+ // Enemy queen safe checks: count them only if the checks are from squares from
568
+ // which opponent cannot give a rook check, because rook checks are more valuable.
569
+ queenChecks = (b1 | b2) & attackedBy[Them][QUEEN] & safe
570
+ & ~(attackedBy[Us][QUEEN] | rookChecks);
571
+ if (queenChecks)
572
+ kingDanger += SafeCheck[QUEEN][more_than_one(queenChecks)];
573
+
574
+ // Enemy bishops checks: count them only if they are from squares from which
575
+ // opponent cannot give a queen check, because queen checks are more valuable.
576
+ bishopChecks = b2 & attackedBy[Them][BISHOP] & safe
577
+ & ~queenChecks;
578
+ if (bishopChecks)
579
+ kingDanger += SafeCheck[BISHOP][more_than_one(bishopChecks)];
580
+
581
+ else
582
+ unsafeChecks |= b2 & attackedBy[Them][BISHOP];
583
+
584
+ // Enemy knights checks
585
+ knightChecks = attacks_bb<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
586
+ if (knightChecks & safe)
587
+ kingDanger += SafeCheck[KNIGHT][more_than_one(knightChecks & safe)];
588
+ else
589
+ unsafeChecks |= knightChecks;
590
+
591
+ // Find the squares that opponent attacks in our king flank, the squares
592
+ // which they attack twice in that flank, and the squares that we defend.
593
+ b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
594
+ b2 = b1 & attackedBy2[Them];
595
+ b3 = attackedBy[Us][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
596
+
597
+ int kingFlankAttack = popcount(b1) + popcount(b2);
598
+ int kingFlankDefense = popcount(b3);
599
+
600
+ kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] // (~10 Elo)
601
+ + 183 * popcount(kingRing[Us] & weak) // (~15 Elo)
602
+ + 148 * popcount(unsafeChecks) // (~4 Elo)
603
+ + 98 * popcount(pos.blockers_for_king(Us)) // (~2 Elo)
604
+ + 69 * kingAttacksCount[Them] // (~0.5 Elo)
605
+ + 3 * kingFlankAttack * kingFlankAttack / 8 // (~0.5 Elo)
606
+ + mg_value(mobility[Them] - mobility[Us]) // (~0.5 Elo)
607
+ - 873 * !pos.count<QUEEN>(Them) // (~24 Elo)
608
+ - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) // (~5 Elo)
609
+ - 6 * mg_value(score) / 8 // (~8 Elo)
610
+ - 4 * kingFlankDefense // (~5 Elo)
611
+ + 37; // (~0.5 Elo)
612
+
613
+ // Transform the kingDanger units into a Score, and subtract it from the evaluation
614
+ if (kingDanger > 100)
615
+ score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);
616
+
617
+ // Penalty when our king is on a pawnless flank
618
+ if (!(pos.pieces(PAWN) & KingFlank[file_of(ksq)]))
619
+ score -= PawnlessFlank;
620
+
621
+ // Penalty if king flank is under attack, potentially moving toward the king
622
+ score -= FlankAttacks * kingFlankAttack;
623
+
624
+ if constexpr (T)
625
+ Trace::add(KING, Us, score);
626
+
627
+ return score;
628
+ }
629
+
630
+
631
+ // Evaluation::threats() assigns bonuses according to the types of the
632
+ // attacking and the attacked pieces.
633
+
634
+ template<Tracing T> template<Color Us>
635
+ Score Evaluation<T>::threats() const {
636
+
637
+ constexpr Color Them = ~Us;
638
+ constexpr Direction Up = pawn_push(Us);
639
+ constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
640
+
641
+ Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe;
642
+ Score score = SCORE_ZERO;
643
+
644
+ // Non-pawn enemies
645
+ nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(PAWN);
646
+
647
+ // Squares strongly protected by the enemy, either because they defend the
648
+ // square with a pawn, or because they defend the square twice and we don't.
649
+ stronglyProtected = attackedBy[Them][PAWN]
650
+ | (attackedBy2[Them] & ~attackedBy2[Us]);
651
+
652
+ // Non-pawn enemies, strongly protected
653
+ defended = nonPawnEnemies & stronglyProtected;
654
+
655
+ // Enemies not strongly protected and under our attack
656
+ weak = pos.pieces(Them) & ~stronglyProtected & attackedBy[Us][ALL_PIECES];
657
+
658
+ // Bonus according to the kind of attacking pieces
659
+ if (defended | weak)
660
+ {
661
+ b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]);
662
+ while (b)
663
+ score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(b)))];
664
+
665
+ b = weak & attackedBy[Us][ROOK];
666
+ while (b)
667
+ score += ThreatByRook[type_of(pos.piece_on(pop_lsb(b)))];
668
+
669
+ if (weak & attackedBy[Us][KING])
670
+ score += ThreatByKing;
671
+
672
+ b = ~attackedBy[Them][ALL_PIECES]
673
+ | (nonPawnEnemies & attackedBy2[Us]);
674
+ score += Hanging * popcount(weak & b);
675
+
676
+ // Additional bonus if weak piece is only protected by a queen
677
+ score += WeakQueenProtection * popcount(weak & attackedBy[Them][QUEEN]);
678
+ }
679
+
680
+ // Bonus for restricting their piece moves
681
+ b = attackedBy[Them][ALL_PIECES]
682
+ & ~stronglyProtected
683
+ & attackedBy[Us][ALL_PIECES];
684
+ score += RestrictedPiece * popcount(b);
685
+
686
+ // Protected or unattacked squares
687
+ safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES];
688
+
689
+ // Bonus for attacking enemy pieces with our relatively safe pawns
690
+ b = pos.pieces(Us, PAWN) & safe;
691
+ b = pawn_attacks_bb<Us>(b) & nonPawnEnemies;
692
+ score += ThreatBySafePawn * popcount(b);
693
+
694
+ // Find squares where our pawns can push on the next move
695
+ b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces();
696
+ b |= shift<Up>(b & TRank3BB) & ~pos.pieces();
697
+
698
+ // Keep only the squares which are relatively safe
699
+ b &= ~attackedBy[Them][PAWN] & safe;
700
+
701
+ // Bonus for safe pawn threats on the next move
702
+ b = pawn_attacks_bb<Us>(b) & nonPawnEnemies;
703
+ score += ThreatByPawnPush * popcount(b);
704
+
705
+ // Bonus for threats on the next moves against enemy queen
706
+ if (pos.count<QUEEN>(Them) == 1)
707
+ {
708
+ bool queenImbalance = pos.count<QUEEN>() == 1;
709
+
710
+ Square s = pos.square<QUEEN>(Them);
711
+ safe = mobilityArea[Us]
712
+ & ~pos.pieces(Us, PAWN)
713
+ & ~stronglyProtected;
714
+
715
+ b = attackedBy[Us][KNIGHT] & attacks_bb<KNIGHT>(s);
716
+
717
+ score += KnightOnQueen * popcount(b & safe) * (1 + queenImbalance);
718
+
719
+ b = (attackedBy[Us][BISHOP] & attacks_bb<BISHOP>(s, pos.pieces()))
720
+ | (attackedBy[Us][ROOK ] & attacks_bb<ROOK >(s, pos.pieces()));
721
+
722
+ score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance);
723
+ }
724
+
725
+ if constexpr (T)
726
+ Trace::add(THREAT, Us, score);
727
+
728
+ return score;
729
+ }
730
+
731
+ // Evaluation::passed() evaluates the passed pawns and candidate passed
732
+ // pawns of the given color.
733
+
734
+ template<Tracing T> template<Color Us>
735
+ Score Evaluation<T>::passed() const {
736
+
737
+ constexpr Color Them = ~Us;
738
+ constexpr Direction Up = pawn_push(Us);
739
+ constexpr Direction Down = -Up;
740
+
741
+ auto king_proximity = [&](Color c, Square s) {
742
+ return std::min(distance(pos.square<KING>(c), s), 5);
743
+ };
744
+
745
+ Bitboard b, bb, squaresToQueen, unsafeSquares, blockedPassers, helpers;
746
+ Score score = SCORE_ZERO;
747
+
748
+ b = pe->passed_pawns(Us);
749
+
750
+ blockedPassers = b & shift<Down>(pos.pieces(Them, PAWN));
751
+ if (blockedPassers)
752
+ {
753
+ helpers = shift<Up>(pos.pieces(Us, PAWN))
754
+ & ~pos.pieces(Them)
755
+ & (~attackedBy2[Them] | attackedBy[Us][ALL_PIECES]);
756
+
757
+ // Remove blocked candidate passers that don't have help to pass
758
+ b &= ~blockedPassers
759
+ | shift<WEST>(helpers)
760
+ | shift<EAST>(helpers);
761
+ }
762
+
763
+ while (b)
764
+ {
765
+ Square s = pop_lsb(b);
766
+
767
+ assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up)));
768
+
769
+ int r = relative_rank(Us, s);
770
+
771
+ Score bonus = PassedRank[r];
772
+
773
+ if (r > RANK_3)
774
+ {
775
+ int w = 5 * r - 13;
776
+ Square blockSq = s + Up;
777
+
778
+ // Adjust bonus based on the king's proximity
779
+ bonus += make_score(0, ( king_proximity(Them, blockSq) * 19 / 4
780
+ - king_proximity(Us, blockSq) * 2) * w);
781
+
782
+ // If blockSq is not the queening square then consider also a second push
783
+ if (r != RANK_7)
784
+ bonus -= make_score(0, king_proximity(Us, blockSq + Up) * w);
785
+
786
+ // If the pawn is free to advance, then increase the bonus
787
+ if (pos.empty(blockSq))
788
+ {
789
+ squaresToQueen = forward_file_bb(Us, s);
790
+ unsafeSquares = passed_pawn_span(Us, s);
791
+
792
+ bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
793
+
794
+ if (!(pos.pieces(Them) & bb))
795
+ unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
796
+
797
+ // If there are no enemy pieces or attacks on passed pawn span, assign a big bonus.
798
+ // Or if there is some, but they are all attacked by our pawns, assign a bit smaller bonus.
799
+ // Otherwise assign a smaller bonus if the path to queen is not attacked
800
+ // and even smaller bonus if it is attacked but block square is not.
801
+ int k = !unsafeSquares ? 36 :
802
+ !(unsafeSquares & ~attackedBy[Us][PAWN]) ? 30 :
803
+ !(unsafeSquares & squaresToQueen) ? 17 :
804
+ !(unsafeSquares & blockSq) ? 7 :
805
+ 0 ;
806
+
807
+ // Assign a larger bonus if the block square is defended
808
+ if ((pos.pieces(Us) & bb) || (attackedBy[Us][ALL_PIECES] & blockSq))
809
+ k += 5;
810
+
811
+ bonus += make_score(k * w, k * w);
812
+ }
813
+ } // r > RANK_3
814
+
815
+ score += bonus - PassedFile * edge_distance(file_of(s));
816
+ }
817
+
818
+ if constexpr (T)
819
+ Trace::add(PASSED, Us, score);
820
+
821
+ return score;
822
+ }
823
+
824
+
825
+ // Evaluation::space() computes a space evaluation for a given side, aiming to improve game
826
+ // play in the opening. It is based on the number of safe squares on the four central files
827
+ // on ranks 2 to 4. Completely safe squares behind a friendly pawn are counted twice.
828
+ // Finally, the space bonus is multiplied by a weight which decreases according to occupancy.
829
+
830
+ template<Tracing T> template<Color Us>
831
+ Score Evaluation<T>::space() const {
832
+
833
+ // Early exit if, for example, both queens or 6 minor pieces have been exchanged
834
+ if (pos.non_pawn_material() < SpaceThreshold)
835
+ return SCORE_ZERO;
836
+
837
+ constexpr Color Them = ~Us;
838
+ constexpr Direction Down = -pawn_push(Us);
839
+ constexpr Bitboard SpaceMask =
840
+ Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB)
841
+ : CenterFiles & (Rank7BB | Rank6BB | Rank5BB);
842
+
843
+ // Find the available squares for our pieces inside the area defined by SpaceMask
844
+ Bitboard safe = SpaceMask
845
+ & ~pos.pieces(Us, PAWN)
846
+ & ~attackedBy[Them][PAWN];
847
+
848
+ // Find all squares which are at most three squares behind some friendly pawn
849
+ Bitboard behind = pos.pieces(Us, PAWN);
850
+ behind |= shift<Down>(behind);
851
+ behind |= shift<Down+Down>(behind);
852
+
853
+ // Compute space score based on the number of safe squares and number of our pieces
854
+ // increased with number of total blocked pawns in position.
855
+ int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]);
856
+ int weight = pos.count<ALL_PIECES>(Us) - 3 + std::min(pe->blocked_count(), 9);
857
+ Score score = make_score(bonus * weight * weight / 16, 0);
858
+
859
+ if constexpr (T)
860
+ Trace::add(SPACE, Us, score);
861
+
862
+ return score;
863
+ }
864
+
865
+
866
+ // Evaluation::winnable() adjusts the midgame and endgame score components, based on
867
+ // the known attacking/defending status of the players. The final value is derived
868
+ // by interpolation from the midgame and endgame values.
869
+
870
+ template<Tracing T>
871
+ Value Evaluation<T>::winnable(Score score) const {
872
+
873
+ int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
874
+ + int(rank_of(pos.square<KING>(WHITE)) - rank_of(pos.square<KING>(BLACK)));
875
+
876
+ bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide)
877
+ && (pos.pieces(PAWN) & KingSide);
878
+
879
+ bool almostUnwinnable = outflanking < 0
880
+ && !pawnsOnBothFlanks;
881
+
882
+ bool infiltration = rank_of(pos.square<KING>(WHITE)) > RANK_4
883
+ || rank_of(pos.square<KING>(BLACK)) < RANK_5;
884
+
885
+ // Compute the initiative bonus for the attacking side
886
+ int complexity = 9 * pe->passed_count()
887
+ + 12 * pos.count<PAWN>()
888
+ + 9 * outflanking
889
+ + 21 * pawnsOnBothFlanks
890
+ + 24 * infiltration
891
+ + 51 * !pos.non_pawn_material()
892
+ - 43 * almostUnwinnable
893
+ -110 ;
894
+
895
+ Value mg = mg_value(score);
896
+ Value eg = eg_value(score);
897
+
898
+ // Now apply the bonus: note that we find the attacking side by extracting the
899
+ // sign of the midgame or endgame values, and that we carefully cap the bonus
900
+ // so that the midgame and endgame scores do not change sign after the bonus.
901
+ int u = ((mg > 0) - (mg < 0)) * std::clamp(complexity + 50, -abs(mg), 0);
902
+ int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg));
903
+
904
+ mg += u;
905
+ eg += v;
906
+
907
+ // Compute the scale factor for the winning side
908
+ Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
909
+ int sf = me->scale_factor(pos, strongSide);
910
+
911
+ // If scale factor is not already specific, scale up/down via general heuristics
912
+ if (sf == SCALE_FACTOR_NORMAL)
913
+ {
914
+ if (pos.opposite_bishops())
915
+ {
916
+ // For pure opposite colored bishops endgames use scale factor
917
+ // based on the number of passed pawns of the strong side.
918
+ if ( pos.non_pawn_material(WHITE) == BishopValueMg
919
+ && pos.non_pawn_material(BLACK) == BishopValueMg)
920
+ sf = 18 + 4 * popcount(pe->passed_pawns(strongSide));
921
+ // For every other opposite colored bishops endgames use scale factor
922
+ // based on the number of all pieces of the strong side.
923
+ else
924
+ sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide);
925
+ }
926
+ // For rook endgames with strong side not having overwhelming pawn number advantage
927
+ // and its pawns being on one flank and weak side protecting its pieces with a king
928
+ // use lower scale factor.
929
+ else if ( pos.non_pawn_material(WHITE) == RookValueMg
930
+ && pos.non_pawn_material(BLACK) == RookValueMg
931
+ && pos.count<PAWN>(strongSide) - pos.count<PAWN>(~strongSide) <= 1
932
+ && bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN))
933
+ && (attacks_bb<KING>(pos.square<KING>(~strongSide)) & pos.pieces(~strongSide, PAWN)))
934
+ sf = 36;
935
+ // For queen vs no queen endgames use scale factor
936
+ // based on number of minors of side that doesn't have queen.
937
+ else if (pos.count<QUEEN>() == 1)
938
+ sf = 37 + 3 * (pos.count<QUEEN>(WHITE) == 1 ? pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK)
939
+ : pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE));
940
+ // In every other case use scale factor based on
941
+ // the number of pawns of the strong side reduced if pawns are on a single flank.
942
+ else
943
+ sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide)) - 4 * !pawnsOnBothFlanks;
944
+
945
+ // Reduce scale factor in case of pawns being on a single flank
946
+ sf -= 4 * !pawnsOnBothFlanks;
947
+ }
948
+
949
+ // Interpolate between the middlegame and (scaled by 'sf') endgame score
950
+ v = mg * int(me->game_phase())
951
+ + eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL;
952
+ v /= PHASE_MIDGAME;
953
+
954
+ if constexpr (T)
955
+ {
956
+ Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score)));
957
+ Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL));
958
+ }
959
+
960
+ return Value(v);
961
+ }
962
+
963
+
964
+ // Evaluation::value() is the main function of the class. It computes the various
965
+ // parts of the evaluation and returns the value of the position from the point
966
+ // of view of the side to move.
967
+
968
+ template<Tracing T>
969
+ Value Evaluation<T>::value() {
970
+
971
+ assert(!pos.checkers());
972
+
973
+ // Probe the material hash table
974
+ me = Material::probe(pos);
975
+
976
+ // If we have a specialized evaluation function for the current material
977
+ // configuration, call it and return.
978
+ if (me->specialized_eval_exists())
979
+ return me->evaluate(pos);
980
+
981
+ // Initialize score by reading the incrementally updated scores included in
982
+ // the position object (material + piece square tables) and the material
983
+ // imbalance. Score is computed internally from the white point of view.
984
+ Score score = pos.psq_score() + me->imbalance();
985
+
986
+ // Probe the pawn hash table
987
+ pe = Pawns::probe(pos);
988
+ score += pe->pawn_score(WHITE) - pe->pawn_score(BLACK);
989
+
990
+ // Early exit if score is high
991
+ auto lazy_skip = [&](Value lazyThreshold) {
992
+ return abs(mg_value(score) + eg_value(score)) > lazyThreshold
993
+ + std::abs(pos.this_thread()->bestValue) * 5 / 4
994
+ + pos.non_pawn_material() / 32;
995
+ };
996
+
997
+ if (lazy_skip(LazyThreshold1))
998
+ goto make_v;
999
+
1000
+ // Main evaluation begins here
1001
+ initialize<WHITE>();
1002
+ initialize<BLACK>();
1003
+
1004
+ // Pieces evaluated first (also populates attackedBy, attackedBy2).
1005
+ // Note that the order of evaluation of the terms is left unspecified.
1006
+ score += pieces<WHITE, KNIGHT>() - pieces<BLACK, KNIGHT>()
1007
+ + pieces<WHITE, BISHOP>() - pieces<BLACK, BISHOP>()
1008
+ + pieces<WHITE, ROOK >() - pieces<BLACK, ROOK >()
1009
+ + pieces<WHITE, QUEEN >() - pieces<BLACK, QUEEN >();
1010
+
1011
+ score += mobility[WHITE] - mobility[BLACK];
1012
+
1013
+ // More complex interactions that require fully populated attack bitboards
1014
+ score += king< WHITE>() - king< BLACK>()
1015
+ + passed< WHITE>() - passed< BLACK>();
1016
+
1017
+ if (lazy_skip(LazyThreshold2))
1018
+ goto make_v;
1019
+
1020
+ score += threats<WHITE>() - threats<BLACK>()
1021
+ + space< WHITE>() - space< BLACK>();
1022
+
1023
+ make_v:
1024
+ // Derive single value from mg and eg parts of score
1025
+ Value v = winnable(score);
1026
+
1027
+ // In case of tracing add all remaining individual evaluation terms
1028
+ if constexpr (T)
1029
+ {
1030
+ Trace::add(MATERIAL, pos.psq_score());
1031
+ Trace::add(IMBALANCE, me->imbalance());
1032
+ Trace::add(PAWN, pe->pawn_score(WHITE), pe->pawn_score(BLACK));
1033
+ Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]);
1034
+ }
1035
+
1036
+ // Evaluation grain
1037
+ v = (v / 16) * 16;
1038
+
1039
+ // Side to move point of view
1040
+ v = (pos.side_to_move() == WHITE ? v : -v);
1041
+
1042
+ return v;
1043
+ }
1044
+
1045
+ } // namespace Eval
1046
+
1047
+
1048
+ /// evaluate() is the evaluator for the outer world. It returns a static
1049
+ /// evaluation of the position from the point of view of the side to move.
1050
+
1051
+ Value Eval::evaluate(const Position& pos, int* complexity) {
1052
+
1053
+ Value v;
1054
+ Value psq = pos.psq_eg_stm();
1055
+
1056
+ // We use the much less accurate but faster Classical eval when the NNUE
1057
+ // option is set to false. Otherwise we use the NNUE eval unless the
1058
+ // PSQ advantage is decisive and several pieces remain. (~3 Elo)
1059
+ bool useClassical = !useNNUE || (pos.count<ALL_PIECES>() > 7 && abs(psq) > 1760);
1060
+
1061
+ if (useClassical)
1062
+ v = Evaluation<NO_TRACE>(pos).value();
1063
+ else
1064
+ {
1065
+ int nnueComplexity;
1066
+ int scale = 1064 + 106 * pos.non_pawn_material() / 5120;
1067
+
1068
+ Color stm = pos.side_to_move();
1069
+ Value optimism = pos.this_thread()->optimism[stm];
1070
+
1071
+ Value nnue = NNUE::evaluate(pos, true, &nnueComplexity);
1072
+
1073
+ // Blend nnue complexity with (semi)classical complexity
1074
+ nnueComplexity = ( 416 * nnueComplexity
1075
+ + 424 * abs(psq - nnue)
1076
+ + (optimism > 0 ? int(optimism) * int(psq - nnue) : 0)
1077
+ ) / 1024;
1078
+
1079
+ // Return hybrid NNUE complexity to caller
1080
+ if (complexity)
1081
+ *complexity = nnueComplexity;
1082
+
1083
+ optimism = optimism * (269 + nnueComplexity) / 256;
1084
+ v = (nnue * scale + optimism * (scale - 754)) / 1024;
1085
+ }
1086
+
1087
+ // Damp down the evaluation linearly when shuffling
1088
+ v = v * (195 - pos.rule50_count()) / 211;
1089
+
1090
+ // Guarantee evaluation does not hit the tablebase range
1091
+ v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
1092
+
1093
+ // When not using NNUE, return classical complexity to caller
1094
+ if (complexity && (!useNNUE || useClassical))
1095
+ *complexity = abs(v - psq);
1096
+
1097
+ return v;
1098
+ }
1099
+
1100
+ /// trace() is like evaluate(), but instead of returning a value, it returns
1101
+ /// a string (suitable for outputting to stdout) that contains the detailed
1102
+ /// descriptions and values of each evaluation term. Useful for debugging.
1103
+ /// Trace scores are from white's point of view
1104
+
1105
+ std::string Eval::trace(Position& pos) {
1106
+
1107
+ if (pos.checkers())
1108
+ return "Final evaluation: none (in check)";
1109
+
1110
+ std::stringstream ss;
1111
+ ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2);
1112
+
1113
+ Value v;
1114
+
1115
+ std::memset(scores, 0, sizeof(scores));
1116
+
1117
+ // Reset any global variable used in eval
1118
+ pos.this_thread()->bestValue = VALUE_ZERO;
1119
+ pos.this_thread()->optimism[WHITE] = VALUE_ZERO;
1120
+ pos.this_thread()->optimism[BLACK] = VALUE_ZERO;
1121
+
1122
+ v = Evaluation<TRACE>(pos).value();
1123
+
1124
+ ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
1125
+ << " Contributing terms for the classical eval:\n"
1126
+ << "+------------+-------------+-------------+-------------+\n"
1127
+ << "| Term | White | Black | Total |\n"
1128
+ << "| | MG EG | MG EG | MG EG |\n"
1129
+ << "+------------+-------------+-------------+-------------+\n"
1130
+ << "| Material | " << Term(MATERIAL)
1131
+ << "| Imbalance | " << Term(IMBALANCE)
1132
+ << "| Pawns | " << Term(PAWN)
1133
+ << "| Knights | " << Term(KNIGHT)
1134
+ << "| Bishops | " << Term(BISHOP)
1135
+ << "| Rooks | " << Term(ROOK)
1136
+ << "| Queens | " << Term(QUEEN)
1137
+ << "| Mobility | " << Term(MOBILITY)
1138
+ << "|King safety | " << Term(KING)
1139
+ << "| Threats | " << Term(THREAT)
1140
+ << "| Passed | " << Term(PASSED)
1141
+ << "| Space | " << Term(SPACE)
1142
+ << "| Winnable | " << Term(WINNABLE)
1143
+ << "+------------+-------------+-------------+-------------+\n"
1144
+ << "| Total | " << Term(TOTAL)
1145
+ << "+------------+-------------+-------------+-------------+\n";
1146
+
1147
+ if (Eval::useNNUE)
1148
+ ss << '\n' << NNUE::trace(pos) << '\n';
1149
+
1150
+ ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15);
1151
+
1152
+ v = pos.side_to_move() == WHITE ? v : -v;
1153
+ ss << "\nClassical evaluation " << to_cp(v) << " (white side)\n";
1154
+ if (Eval::useNNUE)
1155
+ {
1156
+ v = NNUE::evaluate(pos, false);
1157
+ v = pos.side_to_move() == WHITE ? v : -v;
1158
+ ss << "NNUE evaluation " << to_cp(v) << " (white side)\n";
1159
+ }
1160
+
1161
+ v = evaluate(pos);
1162
+ v = pos.side_to_move() == WHITE ? v : -v;
1163
+ ss << "Final evaluation " << to_cp(v) << " (white side)";
1164
+ if (Eval::useNNUE)
1165
+ ss << " [with scaled NNUE, hybrid, ...]";
1166
+ ss << "\n";
1167
+
1168
+ return ss.str();
1169
+ }
1170
+
1171
+ } // namespace Stockfish
src/evaluate.h ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef EVALUATE_H_INCLUDED
20
+ #define EVALUATE_H_INCLUDED
21
+
22
+ #include <string>
23
+ #include <optional>
24
+
25
+ #include "types.h"
26
+
27
+ namespace Stockfish {
28
+
29
+ class Position;
30
+
31
+ namespace Eval {
32
+
33
+ std::string trace(Position& pos);
34
+ Value evaluate(const Position& pos, int* complexity = nullptr);
35
+
36
+ extern bool useNNUE;
37
+ extern std::string currentEvalFileName;
38
+
39
+ // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue
40
+ // for the build process (profile-build and fishtest) to work. Do not change the
41
+ // name of the macro, as it is used in the Makefile.
42
+ #define EvalFileDefaultName "nn-ad9b42354671.nnue"
43
+
44
+ namespace NNUE {
45
+
46
+ std::string trace(Position& pos);
47
+ Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr);
48
+
49
+ void init();
50
+ void verify();
51
+
52
+ bool load_eval(std::string name, std::istream& stream);
53
+ bool save_eval(std::ostream& stream);
54
+ bool save_eval(const std::optional<std::string>& filename);
55
+
56
+ } // namespace NNUE
57
+
58
+ } // namespace Eval
59
+
60
+ } // namespace Stockfish
61
+
62
+ #endif // #ifndef EVALUATE_H_INCLUDED
src/incbin/UNLICENCE ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The file "incbin.h" is free and unencumbered software released into
2
+ the public domain by Dale Weiler, see:
3
+ <https://github.com/graphitemaster/incbin>
4
+
5
+ Anyone is free to copy, modify, publish, use, compile, sell, or
6
+ distribute this software, either in source code form or as a compiled
7
+ binary, for any purpose, commercial or non-commercial, and by any
8
+ means.
9
+
10
+ In jurisdictions that recognize copyright laws, the author or authors
11
+ of this software dedicate any and all copyright interest in the
12
+ software to the public domain. We make this dedication for the benefit
13
+ of the public at large and to the detriment of our heirs and
14
+ successors. We intend this dedication to be an overt act of
15
+ relinquishment in perpetuity of all present and future rights to this
16
+ software under copyright law.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ For more information, please refer to <http://unlicense.org/>
src/incbin/incbin.h ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @file incbin.h
3
+ * @author Dale Weiler
4
+ * @brief Utility for including binary files
5
+ *
6
+ * Facilities for including binary files into the current translation unit and
7
+ * making use from them externally in other translation units.
8
+ */
9
+ #ifndef INCBIN_HDR
10
+ #define INCBIN_HDR
11
+ #include <limits.h>
12
+ #if defined(__AVX512BW__) || \
13
+ defined(__AVX512CD__) || \
14
+ defined(__AVX512DQ__) || \
15
+ defined(__AVX512ER__) || \
16
+ defined(__AVX512PF__) || \
17
+ defined(__AVX512VL__) || \
18
+ defined(__AVX512F__)
19
+ # define INCBIN_ALIGNMENT_INDEX 6
20
+ #elif defined(__AVX__) || \
21
+ defined(__AVX2__)
22
+ # define INCBIN_ALIGNMENT_INDEX 5
23
+ #elif defined(__SSE__) || \
24
+ defined(__SSE2__) || \
25
+ defined(__SSE3__) || \
26
+ defined(__SSSE3__) || \
27
+ defined(__SSE4_1__) || \
28
+ defined(__SSE4_2__) || \
29
+ defined(__neon__)
30
+ # define INCBIN_ALIGNMENT_INDEX 4
31
+ #elif ULONG_MAX != 0xffffffffu
32
+ # define INCBIN_ALIGNMENT_INDEX 3
33
+ # else
34
+ # define INCBIN_ALIGNMENT_INDEX 2
35
+ #endif
36
+
37
+ /* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */
38
+ #define INCBIN_ALIGN_SHIFT_0 1
39
+ #define INCBIN_ALIGN_SHIFT_1 2
40
+ #define INCBIN_ALIGN_SHIFT_2 4
41
+ #define INCBIN_ALIGN_SHIFT_3 8
42
+ #define INCBIN_ALIGN_SHIFT_4 16
43
+ #define INCBIN_ALIGN_SHIFT_5 32
44
+ #define INCBIN_ALIGN_SHIFT_6 64
45
+
46
+ /* Actual alignment value */
47
+ #define INCBIN_ALIGNMENT \
48
+ INCBIN_CONCATENATE( \
49
+ INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), \
50
+ INCBIN_ALIGNMENT_INDEX)
51
+
52
+ /* Stringize */
53
+ #define INCBIN_STR(X) \
54
+ #X
55
+ #define INCBIN_STRINGIZE(X) \
56
+ INCBIN_STR(X)
57
+ /* Concatenate */
58
+ #define INCBIN_CAT(X, Y) \
59
+ X ## Y
60
+ #define INCBIN_CONCATENATE(X, Y) \
61
+ INCBIN_CAT(X, Y)
62
+ /* Deferred macro expansion */
63
+ #define INCBIN_EVAL(X) \
64
+ X
65
+ #define INCBIN_INVOKE(N, ...) \
66
+ INCBIN_EVAL(N(__VA_ARGS__))
67
+
68
+ /* Green Hills uses a different directive for including binary data */
69
+ #if defined(__ghs__)
70
+ # if (__ghs_asm == 2)
71
+ # define INCBIN_MACRO ".file"
72
+ /* Or consider the ".myrawdata" entry in the ld file */
73
+ # else
74
+ # define INCBIN_MACRO "\tINCBIN"
75
+ # endif
76
+ #else
77
+ # define INCBIN_MACRO ".incbin"
78
+ #endif
79
+
80
+ #ifndef _MSC_VER
81
+ # define INCBIN_ALIGN \
82
+ __attribute__((aligned(INCBIN_ALIGNMENT)))
83
+ #else
84
+ # define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT))
85
+ #endif
86
+
87
+ #if defined(__arm__) || /* GNU C and RealView */ \
88
+ defined(__arm) || /* Diab */ \
89
+ defined(_ARM) /* ImageCraft */
90
+ # define INCBIN_ARM
91
+ #endif
92
+
93
+ #ifdef __GNUC__
94
+ /* Utilize .balign where supported */
95
+ # define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
96
+ # define INCBIN_ALIGN_BYTE ".balign 1\n"
97
+ #elif defined(INCBIN_ARM)
98
+ /*
99
+ * On arm assemblers, the alignment value is calculated as (1 << n) where `n' is
100
+ * the shift count. This is the value passed to `.align'
101
+ */
102
+ # define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n"
103
+ # define INCBIN_ALIGN_BYTE ".align 0\n"
104
+ #else
105
+ /* We assume other inline assembler's treat `.align' as `.balign' */
106
+ # define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
107
+ # define INCBIN_ALIGN_BYTE ".align 1\n"
108
+ #endif
109
+
110
+ /* INCBIN_CONST is used by incbin.c generated files */
111
+ #if defined(__cplusplus)
112
+ # define INCBIN_EXTERNAL extern "C"
113
+ # define INCBIN_CONST extern const
114
+ #else
115
+ # define INCBIN_EXTERNAL extern
116
+ # define INCBIN_CONST const
117
+ #endif
118
+
119
+ /**
120
+ * @brief Optionally override the linker section into which data is emitted.
121
+ *
122
+ * @warning If you use this facility, you'll have to deal with platform-specific linker output
123
+ * section naming on your own
124
+ *
125
+ * Overriding the default linker output section, e.g for esp8266/Arduino:
126
+ * @code
127
+ * #define INCBIN_OUTPUT_SECTION ".irom.text"
128
+ * #include "incbin.h"
129
+ * INCBIN(Foo, "foo.txt");
130
+ * // Data is emitted into program memory that never gets copied to RAM
131
+ * @endcode
132
+ */
133
+ #if !defined(INCBIN_OUTPUT_SECTION)
134
+ # if defined(__APPLE__)
135
+ # define INCBIN_OUTPUT_SECTION ".const_data"
136
+ # else
137
+ # define INCBIN_OUTPUT_SECTION ".rodata"
138
+ # endif
139
+ #endif
140
+
141
+ #if defined(__APPLE__)
142
+ /* The directives are different for Apple branded compilers */
143
+ # define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n"
144
+ # define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
145
+ # define INCBIN_INT ".long "
146
+ # define INCBIN_MANGLE "_"
147
+ # define INCBIN_BYTE ".byte "
148
+ # define INCBIN_TYPE(...)
149
+ #else
150
+ # define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n"
151
+ # define INCBIN_GLOBAL(NAME) ".global " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
152
+ # if defined(__ghs__)
153
+ # define INCBIN_INT ".word "
154
+ # else
155
+ # define INCBIN_INT ".int "
156
+ # endif
157
+ # if defined(__USER_LABEL_PREFIX__)
158
+ # define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__)
159
+ # else
160
+ # define INCBIN_MANGLE ""
161
+ # endif
162
+ # if defined(INCBIN_ARM)
163
+ /* On arm assemblers, `@' is used as a line comment token */
164
+ # define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n"
165
+ # elif defined(__MINGW32__) || defined(__MINGW64__)
166
+ /* Mingw doesn't support this directive either */
167
+ # define INCBIN_TYPE(NAME)
168
+ # else
169
+ /* It's safe to use `@' on other architectures */
170
+ # define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n"
171
+ # endif
172
+ # define INCBIN_BYTE ".byte "
173
+ #endif
174
+
175
+ /* List of style types used for symbol names */
176
+ #define INCBIN_STYLE_CAMEL 0
177
+ #define INCBIN_STYLE_SNAKE 1
178
+
179
+ /**
180
+ * @brief Specify the prefix to use for symbol names.
181
+ *
182
+ * By default this is `g', producing symbols of the form:
183
+ * @code
184
+ * #include "incbin.h"
185
+ * INCBIN(Foo, "foo.txt");
186
+ *
187
+ * // Now you have the following symbols:
188
+ * // const unsigned char gFooData[];
189
+ * // const unsigned char *const gFooEnd;
190
+ * // const unsigned int gFooSize;
191
+ * @endcode
192
+ *
193
+ * If however you specify a prefix before including: e.g:
194
+ * @code
195
+ * #define INCBIN_PREFIX incbin
196
+ * #include "incbin.h"
197
+ * INCBIN(Foo, "foo.txt");
198
+ *
199
+ * // Now you have the following symbols instead:
200
+ * // const unsigned char incbinFooData[];
201
+ * // const unsigned char *const incbinFooEnd;
202
+ * // const unsigned int incbinFooSize;
203
+ * @endcode
204
+ */
205
+ #if !defined(INCBIN_PREFIX)
206
+ # define INCBIN_PREFIX g
207
+ #endif
208
+
209
+ /**
210
+ * @brief Specify the style used for symbol names.
211
+ *
212
+ * Possible options are
213
+ * - INCBIN_STYLE_CAMEL "CamelCase"
214
+ * - INCBIN_STYLE_SNAKE "snake_case"
215
+ *
216
+ * Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form:
217
+ * @code
218
+ * #include "incbin.h"
219
+ * INCBIN(Foo, "foo.txt");
220
+ *
221
+ * // Now you have the following symbols:
222
+ * // const unsigned char <prefix>FooData[];
223
+ * // const unsigned char *const <prefix>FooEnd;
224
+ * // const unsigned int <prefix>FooSize;
225
+ * @endcode
226
+ *
227
+ * If however you specify a style before including: e.g:
228
+ * @code
229
+ * #define INCBIN_STYLE INCBIN_STYLE_SNAKE
230
+ * #include "incbin.h"
231
+ * INCBIN(foo, "foo.txt");
232
+ *
233
+ * // Now you have the following symbols:
234
+ * // const unsigned char <prefix>foo_data[];
235
+ * // const unsigned char *const <prefix>foo_end;
236
+ * // const unsigned int <prefix>foo_size;
237
+ * @endcode
238
+ */
239
+ #if !defined(INCBIN_STYLE)
240
+ # define INCBIN_STYLE INCBIN_STYLE_CAMEL
241
+ #endif
242
+
243
+ /* Style lookup tables */
244
+ #define INCBIN_STYLE_0_DATA Data
245
+ #define INCBIN_STYLE_0_END End
246
+ #define INCBIN_STYLE_0_SIZE Size
247
+ #define INCBIN_STYLE_1_DATA _data
248
+ #define INCBIN_STYLE_1_END _end
249
+ #define INCBIN_STYLE_1_SIZE _size
250
+
251
+ /* Style lookup: returning identifier */
252
+ #define INCBIN_STYLE_IDENT(TYPE) \
253
+ INCBIN_CONCATENATE( \
254
+ INCBIN_STYLE_, \
255
+ INCBIN_CONCATENATE( \
256
+ INCBIN_EVAL(INCBIN_STYLE), \
257
+ INCBIN_CONCATENATE(_, TYPE)))
258
+
259
+ /* Style lookup: returning string literal */
260
+ #define INCBIN_STYLE_STRING(TYPE) \
261
+ INCBIN_STRINGIZE( \
262
+ INCBIN_STYLE_IDENT(TYPE)) \
263
+
264
+ /* Generate the global labels by indirectly invoking the macro with our style
265
+ * type and concatenating the name against them. */
266
+ #define INCBIN_GLOBAL_LABELS(NAME, TYPE) \
267
+ INCBIN_INVOKE( \
268
+ INCBIN_GLOBAL, \
269
+ INCBIN_CONCATENATE( \
270
+ NAME, \
271
+ INCBIN_INVOKE( \
272
+ INCBIN_STYLE_IDENT, \
273
+ TYPE))) \
274
+ INCBIN_INVOKE( \
275
+ INCBIN_TYPE, \
276
+ INCBIN_CONCATENATE( \
277
+ NAME, \
278
+ INCBIN_INVOKE( \
279
+ INCBIN_STYLE_IDENT, \
280
+ TYPE)))
281
+
282
+ /**
283
+ * @brief Externally reference binary data included in another translation unit.
284
+ *
285
+ * Produces three external symbols that reference the binary data included in
286
+ * another translation unit.
287
+ *
288
+ * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
289
+ * "Data", as well as "End" and "Size" after. An example is provided below.
290
+ *
291
+ * @param NAME The name given for the binary data
292
+ *
293
+ * @code
294
+ * INCBIN_EXTERN(Foo);
295
+ *
296
+ * // Now you have the following symbols:
297
+ * // extern const unsigned char <prefix>FooData[];
298
+ * // extern const unsigned char *const <prefix>FooEnd;
299
+ * // extern const unsigned int <prefix>FooSize;
300
+ * @endcode
301
+ */
302
+ #define INCBIN_EXTERN(NAME) \
303
+ INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char \
304
+ INCBIN_CONCATENATE( \
305
+ INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
306
+ INCBIN_STYLE_IDENT(DATA))[]; \
307
+ INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const \
308
+ INCBIN_CONCATENATE( \
309
+ INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
310
+ INCBIN_STYLE_IDENT(END)); \
311
+ INCBIN_EXTERNAL const unsigned int \
312
+ INCBIN_CONCATENATE( \
313
+ INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
314
+ INCBIN_STYLE_IDENT(SIZE))
315
+
316
+ /**
317
+ * @brief Include a binary file into the current translation unit.
318
+ *
319
+ * Includes a binary file into the current translation unit, producing three symbols
320
+ * for objects that encode the data and size respectively.
321
+ *
322
+ * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
323
+ * "Data", as well as "End" and "Size" after. An example is provided below.
324
+ *
325
+ * @param NAME The name to associate with this binary data (as an identifier.)
326
+ * @param FILENAME The file to include (as a string literal.)
327
+ *
328
+ * @code
329
+ * INCBIN(Icon, "icon.png");
330
+ *
331
+ * // Now you have the following symbols:
332
+ * // const unsigned char <prefix>IconData[];
333
+ * // const unsigned char *const <prefix>IconEnd;
334
+ * // const unsigned int <prefix>IconSize;
335
+ * @endcode
336
+ *
337
+ * @warning This must be used in global scope
338
+ * @warning The identifiers may be different if INCBIN_STYLE is not default
339
+ *
340
+ * To externally reference the data included by this in another translation unit
341
+ * please @see INCBIN_EXTERN.
342
+ */
343
+ #ifdef _MSC_VER
344
+ #define INCBIN(NAME, FILENAME) \
345
+ INCBIN_EXTERN(NAME)
346
+ #else
347
+ #define INCBIN(NAME, FILENAME) \
348
+ __asm__(INCBIN_SECTION \
349
+ INCBIN_GLOBAL_LABELS(NAME, DATA) \
350
+ INCBIN_ALIGN_HOST \
351
+ INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \
352
+ INCBIN_MACRO " \"" FILENAME "\"\n" \
353
+ INCBIN_GLOBAL_LABELS(NAME, END) \
354
+ INCBIN_ALIGN_BYTE \
355
+ INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \
356
+ INCBIN_BYTE "1\n" \
357
+ INCBIN_GLOBAL_LABELS(NAME, SIZE) \
358
+ INCBIN_ALIGN_HOST \
359
+ INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE) ":\n" \
360
+ INCBIN_INT INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) " - " \
361
+ INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) "\n" \
362
+ INCBIN_ALIGN_HOST \
363
+ ".text\n" \
364
+ ); \
365
+ INCBIN_EXTERN(NAME)
366
+
367
+ #endif
368
+ #endif
src/main.cpp ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <iostream>
20
+
21
+ #include "bitboard.h"
22
+ #include "endgame.h"
23
+ #include "position.h"
24
+ #include "psqt.h"
25
+ #include "search.h"
26
+ #include "syzygy/tbprobe.h"
27
+ #include "thread.h"
28
+ #include "tt.h"
29
+ #include "uci.h"
30
+
31
+ using namespace Stockfish;
32
+
33
+ int main(int argc, char* argv[]) {
34
+
35
+ std::cout << engine_info() << std::endl;
36
+
37
+ CommandLine::init(argc, argv);
38
+ UCI::init(Options);
39
+ Tune::init();
40
+ PSQT::init();
41
+ Bitboards::init();
42
+ Position::init();
43
+ Bitbases::init();
44
+ Endgames::init();
45
+ Threads.set(size_t(Options["Threads"]));
46
+ Search::clear(); // After threads are up
47
+ Eval::NNUE::init();
48
+
49
+ UCI::loop(argc, argv);
50
+
51
+ Threads.set(0);
52
+ return 0;
53
+ }
src/material.cpp ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <cassert>
20
+ #include <cstring> // For std::memset
21
+
22
+ #include "material.h"
23
+ #include "thread.h"
24
+
25
+ using namespace std;
26
+
27
+ namespace Stockfish {
28
+
29
+ namespace {
30
+ #define S(mg, eg) make_score(mg, eg)
31
+
32
+ // Polynomial material imbalance parameters
33
+
34
+ // One Score parameter for each pair (our piece, another of our pieces)
35
+ constexpr Score QuadraticOurs[][PIECE_TYPE_NB] = {
36
+ // OUR PIECE 2
37
+ // bishop pair pawn knight bishop rook queen
38
+ {S(1419, 1455) }, // Bishop pair
39
+ {S( 101, 28), S( 37, 39) }, // Pawn
40
+ {S( 57, 64), S(249, 187), S(-49, -62) }, // Knight OUR PIECE 1
41
+ {S( 0, 0), S(118, 137), S( 10, 27), S( 0, 0) }, // Bishop
42
+ {S( -63, -68), S( -5, 3), S(100, 81), S(132, 118), S(-246, -244) }, // Rook
43
+ {S(-210, -211), S( 37, 14), S(147, 141), S(161, 105), S(-158, -174), S(-9,-31) } // Queen
44
+ };
45
+
46
+ // One Score parameter for each pair (our piece, their piece)
47
+ constexpr Score QuadraticTheirs[][PIECE_TYPE_NB] = {
48
+ // THEIR PIECE
49
+ // bishop pair pawn knight bishop rook queen
50
+ { }, // Bishop pair
51
+ {S( 33, 30) }, // Pawn
52
+ {S( 46, 18), S(106, 84) }, // Knight OUR PIECE
53
+ {S( 75, 35), S( 59, 44), S( 60, 15) }, // Bishop
54
+ {S( 26, 35), S( 6, 22), S( 38, 39), S(-12, -2) }, // Rook
55
+ {S( 97, 93), S(100, 163), S(-58, -91), S(112, 192), S(276, 225) } // Queen
56
+ };
57
+
58
+ #undef S
59
+
60
+ // Endgame evaluation and scaling functions are accessed directly and not through
61
+ // the function maps because they correspond to more than one material hash key.
62
+ Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
63
+
64
+ Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) };
65
+ Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) };
66
+ Endgame<KPsK> ScaleKPsK[] = { Endgame<KPsK>(WHITE), Endgame<KPsK>(BLACK) };
67
+ Endgame<KPKP> ScaleKPKP[] = { Endgame<KPKP>(WHITE), Endgame<KPKP>(BLACK) };
68
+
69
+ // Helper used to detect a given material distribution
70
+ bool is_KXK(const Position& pos, Color us) {
71
+ return !more_than_one(pos.pieces(~us))
72
+ && pos.non_pawn_material(us) >= RookValueMg;
73
+ }
74
+
75
+ bool is_KBPsK(const Position& pos, Color us) {
76
+ return pos.non_pawn_material(us) == BishopValueMg
77
+ && pos.count<PAWN>(us) >= 1;
78
+ }
79
+
80
+ bool is_KQKRPs(const Position& pos, Color us) {
81
+ return !pos.count<PAWN>(us)
82
+ && pos.non_pawn_material(us) == QueenValueMg
83
+ && pos.count<ROOK>(~us) == 1
84
+ && pos.count<PAWN>(~us) >= 1;
85
+ }
86
+
87
+
88
+ /// imbalance() calculates the imbalance by comparing the piece count of each
89
+ /// piece type for both colors.
90
+
91
+ template<Color Us>
92
+ Score imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
93
+
94
+ constexpr Color Them = ~Us;
95
+
96
+ Score bonus = SCORE_ZERO;
97
+
98
+ // Second-degree polynomial material imbalance, by Tord Romstad
99
+ for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
100
+ {
101
+ if (!pieceCount[Us][pt1])
102
+ continue;
103
+
104
+ int v = QuadraticOurs[pt1][pt1] * pieceCount[Us][pt1];
105
+
106
+ for (int pt2 = NO_PIECE_TYPE; pt2 < pt1; ++pt2)
107
+ v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2]
108
+ + QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2];
109
+
110
+ bonus += pieceCount[Us][pt1] * v;
111
+ }
112
+
113
+ return bonus;
114
+ }
115
+
116
+ } // namespace
117
+
118
+ namespace Material {
119
+
120
+
121
+ /// Material::probe() looks up the current position's material configuration in
122
+ /// the material hash table. It returns a pointer to the Entry if the position
123
+ /// is found. Otherwise a new Entry is computed and stored there, so we don't
124
+ /// have to recompute all when the same material configuration occurs again.
125
+
126
+ Entry* probe(const Position& pos) {
127
+
128
+ Key key = pos.material_key();
129
+ Entry* e = pos.this_thread()->materialTable[key];
130
+
131
+ if (e->key == key)
132
+ return e;
133
+
134
+ std::memset(e, 0, sizeof(Entry));
135
+ e->key = key;
136
+ e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
137
+
138
+ Value npm_w = pos.non_pawn_material(WHITE);
139
+ Value npm_b = pos.non_pawn_material(BLACK);
140
+ Value npm = std::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
141
+
142
+ // Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
143
+ e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
144
+
145
+ // Let's look if we have a specialized evaluation function for this particular
146
+ // material configuration. Firstly we look for a fixed configuration one, then
147
+ // for a generic one if the previous search failed.
148
+ if ((e->evaluationFunction = Endgames::probe<Value>(key)) != nullptr)
149
+ return e;
150
+
151
+ for (Color c : { WHITE, BLACK })
152
+ if (is_KXK(pos, c))
153
+ {
154
+ e->evaluationFunction = &EvaluateKXK[c];
155
+ return e;
156
+ }
157
+
158
+ // OK, we didn't find any special evaluation function for the current material
159
+ // configuration. Is there a suitable specialized scaling function?
160
+ const auto* sf = Endgames::probe<ScaleFactor>(key);
161
+
162
+ if (sf)
163
+ {
164
+ e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned
165
+ return e;
166
+ }
167
+
168
+ // We didn't find any specialized scaling function, so fall back on generic
169
+ // ones that refer to more than one material distribution. Note that in this
170
+ // case we don't return after setting the function.
171
+ for (Color c : { WHITE, BLACK })
172
+ {
173
+ if (is_KBPsK(pos, c))
174
+ e->scalingFunction[c] = &ScaleKBPsK[c];
175
+
176
+ else if (is_KQKRPs(pos, c))
177
+ e->scalingFunction[c] = &ScaleKQKRPs[c];
178
+ }
179
+
180
+ if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board
181
+ {
182
+ if (!pos.count<PAWN>(BLACK))
183
+ {
184
+ assert(pos.count<PAWN>(WHITE) >= 2);
185
+
186
+ e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
187
+ }
188
+ else if (!pos.count<PAWN>(WHITE))
189
+ {
190
+ assert(pos.count<PAWN>(BLACK) >= 2);
191
+
192
+ e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
193
+ }
194
+ else if (pos.count<PAWN>(WHITE) == 1 && pos.count<PAWN>(BLACK) == 1)
195
+ {
196
+ // This is a special case because we set scaling functions
197
+ // for both colors instead of only one.
198
+ e->scalingFunction[WHITE] = &ScaleKPKP[WHITE];
199
+ e->scalingFunction[BLACK] = &ScaleKPKP[BLACK];
200
+ }
201
+ }
202
+
203
+ // Zero or just one pawn makes it difficult to win, even with a small material
204
+ // advantage. This catches some trivial draws like KK, KBK and KNK and gives a
205
+ // drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN).
206
+ if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
207
+ e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW :
208
+ npm_b <= BishopValueMg ? 4 : 14);
209
+
210
+ if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg)
211
+ e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW :
212
+ npm_w <= BishopValueMg ? 4 : 14);
213
+
214
+ // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
215
+ // for the bishop pair "extended piece", which allows us to be more flexible
216
+ // in defining bishop pair bonuses.
217
+ const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
218
+ { pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),
219
+ pos.count<BISHOP>(WHITE) , pos.count<ROOK>(WHITE), pos.count<QUEEN >(WHITE) },
220
+ { pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
221
+ pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
222
+
223
+ e->score = (imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16;
224
+ return e;
225
+ }
226
+
227
+ } // namespace Material
228
+
229
+ } // namespace Stockfish
src/material.h ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef MATERIAL_H_INCLUDED
20
+ #define MATERIAL_H_INCLUDED
21
+
22
+ #include "endgame.h"
23
+ #include "misc.h"
24
+ #include "position.h"
25
+ #include "types.h"
26
+
27
+ namespace Stockfish::Material {
28
+
29
+ /// Material::Entry contains various information about a material configuration.
30
+ /// It contains a material imbalance evaluation, a function pointer to a special
31
+ /// endgame evaluation function (which in most cases is NULL, meaning that the
32
+ /// standard evaluation function will be used), and scale factors.
33
+ ///
34
+ /// The scale factors are used to scale the evaluation score up or down. For
35
+ /// instance, in KRB vs KR endgames, the score is scaled down by a factor of 4,
36
+ /// which will result in scores of absolute value less than one pawn.
37
+
38
+ struct Entry {
39
+
40
+ Score imbalance() const { return score; }
41
+ Phase game_phase() const { return (Phase)gamePhase; }
42
+ bool specialized_eval_exists() const { return evaluationFunction != nullptr; }
43
+ Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); }
44
+
45
+ // scale_factor() takes a position and a color as input and returns a scale factor
46
+ // for the given color. We have to provide the position in addition to the color
47
+ // because the scale factor may also be a function which should be applied to
48
+ // the position. For instance, in KBP vs K endgames, the scaling function looks
49
+ // for rook pawns and wrong-colored bishops.
50
+ ScaleFactor scale_factor(const Position& pos, Color c) const {
51
+ ScaleFactor sf = scalingFunction[c] ? (*scalingFunction[c])(pos)
52
+ : SCALE_FACTOR_NONE;
53
+ return sf != SCALE_FACTOR_NONE ? sf : ScaleFactor(factor[c]);
54
+ }
55
+
56
+ Key key;
57
+ const EndgameBase<Value>* evaluationFunction;
58
+ const EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
59
+ // side (e.g. KPKP, KBPsK)
60
+ Score score;
61
+ int16_t gamePhase;
62
+ uint8_t factor[COLOR_NB];
63
+ };
64
+
65
+ typedef HashTable<Entry, 8192> Table;
66
+
67
+ Entry* probe(const Position& pos);
68
+
69
+ } // namespace Stockfish::Material
70
+
71
+ #endif // #ifndef MATERIAL_H_INCLUDED
src/misc.cpp ADDED
@@ -0,0 +1,687 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifdef _WIN32
20
+ #if _WIN32_WINNT < 0x0601
21
+ #undef _WIN32_WINNT
22
+ #define _WIN32_WINNT 0x0601 // Force to include needed API prototypes
23
+ #endif
24
+
25
+ #ifndef NOMINMAX
26
+ #define NOMINMAX
27
+ #endif
28
+
29
+ #include <windows.h>
30
+ // The needed Windows API for processor groups could be missed from old Windows
31
+ // versions, so instead of calling them directly (forcing the linker to resolve
32
+ // the calls at compile time), try to load them at runtime. To do this we need
33
+ // first to define the corresponding function pointers.
34
+ extern "C" {
35
+ typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP,
36
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD);
37
+ typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY);
38
+ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
39
+ typedef bool(*fun4_t)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT);
40
+ typedef WORD(*fun5_t)();
41
+ }
42
+ #endif
43
+
44
+ #include <fstream>
45
+ #include <iomanip>
46
+ #include <iostream>
47
+ #include <sstream>
48
+ #include <vector>
49
+ #include <cstdlib>
50
+
51
+ #if defined(__linux__) && !defined(__ANDROID__)
52
+ #include <stdlib.h>
53
+ #include <sys/mman.h>
54
+ #endif
55
+
56
+ #if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) || defined(__e2k__)
57
+ #define POSIXALIGNEDALLOC
58
+ #include <stdlib.h>
59
+ #endif
60
+
61
+ #include "misc.h"
62
+ #include "thread.h"
63
+
64
+ using namespace std;
65
+
66
+ namespace Stockfish {
67
+
68
+ namespace {
69
+
70
+ /// Version number or dev.
71
+ const string version = "15.1";
72
+
73
+ /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
74
+ /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
75
+ /// can toggle the logging of std::cout and std:cin at runtime whilst preserving
76
+ /// usual I/O functionality, all without changing a single line of code!
77
+ /// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
78
+
79
+ struct Tie: public streambuf { // MSVC requires split streambuf for cin and cout
80
+
81
+ Tie(streambuf* b, streambuf* l) : buf(b), logBuf(l) {}
82
+
83
+ int sync() override { return logBuf->pubsync(), buf->pubsync(); }
84
+ int overflow(int c) override { return log(buf->sputc((char)c), "<< "); }
85
+ int underflow() override { return buf->sgetc(); }
86
+ int uflow() override { return log(buf->sbumpc(), ">> "); }
87
+
88
+ streambuf *buf, *logBuf;
89
+
90
+ int log(int c, const char* prefix) {
91
+
92
+ static int last = '\n'; // Single log file
93
+
94
+ if (last == '\n')
95
+ logBuf->sputn(prefix, 3);
96
+
97
+ return last = logBuf->sputc((char)c);
98
+ }
99
+ };
100
+
101
+ class Logger {
102
+
103
+ Logger() : in(cin.rdbuf(), file.rdbuf()), out(cout.rdbuf(), file.rdbuf()) {}
104
+ ~Logger() { start(""); }
105
+
106
+ ofstream file;
107
+ Tie in, out;
108
+
109
+ public:
110
+ static void start(const std::string& fname) {
111
+
112
+ static Logger l;
113
+
114
+ if (l.file.is_open())
115
+ {
116
+ cout.rdbuf(l.out.buf);
117
+ cin.rdbuf(l.in.buf);
118
+ l.file.close();
119
+ }
120
+
121
+ if (!fname.empty())
122
+ {
123
+ l.file.open(fname, ifstream::out);
124
+
125
+ if (!l.file.is_open())
126
+ {
127
+ cerr << "Unable to open debug log file " << fname << endl;
128
+ exit(EXIT_FAILURE);
129
+ }
130
+
131
+ cin.rdbuf(&l.in);
132
+ cout.rdbuf(&l.out);
133
+ }
134
+ }
135
+ };
136
+
137
+ } // namespace
138
+
139
+
140
+ /// engine_info() returns the full name of the current Stockfish version.
141
+ /// For local dev compiles we try to append the commit sha and commit date
142
+ /// from git if that fails only the local compilation date is set and "nogit" is specified:
143
+ /// Stockfish dev-YYYYMMDD-SHA
144
+ /// or
145
+ /// Stockfish dev-YYYYMMDD-nogit
146
+ ///
147
+ /// For releases (non dev builds) we only include the version number:
148
+ /// Stockfish version
149
+
150
+ string engine_info(bool to_uci) {
151
+ stringstream ss;
152
+ ss << "Stockfish " << version << setfill('0');
153
+
154
+ if (version == "dev")
155
+ {
156
+ ss << "-";
157
+ #ifdef GIT_DATE
158
+ ss << GIT_DATE;
159
+ #else
160
+ const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
161
+ string month, day, year;
162
+ stringstream date(__DATE__); // From compiler, format is "Sep 21 2008"
163
+
164
+ date >> month >> day >> year;
165
+ ss << year << setw(2) << setfill('0') << (1 + months.find(month) / 4) << setw(2) << setfill('0') << day;
166
+ #endif
167
+
168
+ ss << "-";
169
+
170
+ #ifdef GIT_SHA
171
+ ss << GIT_SHA;
172
+ #else
173
+ ss << "nogit";
174
+ #endif
175
+ }
176
+
177
+ ss << (to_uci ? "\nid author ": " by ")
178
+ << "the Stockfish developers (see AUTHORS file)";
179
+
180
+ return ss.str();
181
+ }
182
+
183
+
184
+ /// compiler_info() returns a string trying to describe the compiler we use
185
+
186
+ std::string compiler_info() {
187
+
188
+ #define stringify2(x) #x
189
+ #define stringify(x) stringify2(x)
190
+ #define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch)
191
+
192
+ /// Predefined macros hell:
193
+ ///
194
+ /// __GNUC__ Compiler is gcc, Clang or Intel on Linux
195
+ /// __INTEL_COMPILER Compiler is Intel
196
+ /// _MSC_VER Compiler is MSVC or Intel on Windows
197
+ /// _WIN32 Building on Windows (any)
198
+ /// _WIN64 Building on Windows 64 bit
199
+
200
+ std::string compiler = "\nCompiled by ";
201
+
202
+ #ifdef __clang__
203
+ compiler += "clang++ ";
204
+ compiler += make_version_string(__clang_major__, __clang_minor__, __clang_patchlevel__);
205
+ #elif __INTEL_COMPILER
206
+ compiler += "Intel compiler ";
207
+ compiler += "(version ";
208
+ compiler += stringify(__INTEL_COMPILER) " update " stringify(__INTEL_COMPILER_UPDATE);
209
+ compiler += ")";
210
+ #elif _MSC_VER
211
+ compiler += "MSVC ";
212
+ compiler += "(version ";
213
+ compiler += stringify(_MSC_FULL_VER) "." stringify(_MSC_BUILD);
214
+ compiler += ")";
215
+ #elif defined(__e2k__) && defined(__LCC__)
216
+ #define dot_ver2(n) \
217
+ compiler += (char)'.'; \
218
+ compiler += (char)('0' + (n) / 10); \
219
+ compiler += (char)('0' + (n) % 10);
220
+
221
+ compiler += "MCST LCC ";
222
+ compiler += "(version ";
223
+ compiler += std::to_string(__LCC__ / 100);
224
+ dot_ver2(__LCC__ % 100)
225
+ dot_ver2(__LCC_MINOR__)
226
+ compiler += ")";
227
+ #elif __GNUC__
228
+ compiler += "g++ (GNUC) ";
229
+ compiler += make_version_string(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
230
+ #else
231
+ compiler += "Unknown compiler ";
232
+ compiler += "(unknown version)";
233
+ #endif
234
+
235
+ #if defined(__APPLE__)
236
+ compiler += " on Apple";
237
+ #elif defined(__CYGWIN__)
238
+ compiler += " on Cygwin";
239
+ #elif defined(__MINGW64__)
240
+ compiler += " on MinGW64";
241
+ #elif defined(__MINGW32__)
242
+ compiler += " on MinGW32";
243
+ #elif defined(__ANDROID__)
244
+ compiler += " on Android";
245
+ #elif defined(__linux__)
246
+ compiler += " on Linux";
247
+ #elif defined(_WIN64)
248
+ compiler += " on Microsoft Windows 64-bit";
249
+ #elif defined(_WIN32)
250
+ compiler += " on Microsoft Windows 32-bit";
251
+ #else
252
+ compiler += " on unknown system";
253
+ #endif
254
+
255
+ compiler += "\nCompilation settings include: ";
256
+ compiler += (Is64Bit ? " 64bit" : " 32bit");
257
+ #if defined(USE_VNNI)
258
+ compiler += " VNNI";
259
+ #endif
260
+ #if defined(USE_AVX512)
261
+ compiler += " AVX512";
262
+ #endif
263
+ compiler += (HasPext ? " BMI2" : "");
264
+ #if defined(USE_AVX2)
265
+ compiler += " AVX2";
266
+ #endif
267
+ #if defined(USE_SSE41)
268
+ compiler += " SSE41";
269
+ #endif
270
+ #if defined(USE_SSSE3)
271
+ compiler += " SSSE3";
272
+ #endif
273
+ #if defined(USE_SSE2)
274
+ compiler += " SSE2";
275
+ #endif
276
+ compiler += (HasPopCnt ? " POPCNT" : "");
277
+ #if defined(USE_MMX)
278
+ compiler += " MMX";
279
+ #endif
280
+ #if defined(USE_NEON)
281
+ compiler += " NEON";
282
+ #endif
283
+
284
+ #if !defined(NDEBUG)
285
+ compiler += " DEBUG";
286
+ #endif
287
+
288
+ compiler += "\n__VERSION__ macro expands to: ";
289
+ #ifdef __VERSION__
290
+ compiler += __VERSION__;
291
+ #else
292
+ compiler += "(undefined macro)";
293
+ #endif
294
+ compiler += "\n";
295
+
296
+ return compiler;
297
+ }
298
+
299
+
300
+ /// Debug functions used mainly to collect run-time statistics
301
+ static std::atomic<int64_t> hits[2], means[2];
302
+
303
+ void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
304
+ void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); }
305
+ void dbg_mean_of(int v) { ++means[0]; means[1] += v; }
306
+
307
+ void dbg_print() {
308
+
309
+ if (hits[0])
310
+ cerr << "Total " << hits[0] << " Hits " << hits[1]
311
+ << " hit rate (%) " << 100 * hits[1] / hits[0] << endl;
312
+
313
+ if (means[0])
314
+ cerr << "Total " << means[0] << " Mean "
315
+ << (double)means[1] / means[0] << endl;
316
+ }
317
+
318
+
319
+ /// Used to serialize access to std::cout to avoid multiple threads writing at
320
+ /// the same time.
321
+
322
+ std::ostream& operator<<(std::ostream& os, SyncCout sc) {
323
+
324
+ static std::mutex m;
325
+
326
+ if (sc == IO_LOCK)
327
+ m.lock();
328
+
329
+ if (sc == IO_UNLOCK)
330
+ m.unlock();
331
+
332
+ return os;
333
+ }
334
+
335
+
336
+ /// Trampoline helper to avoid moving Logger to misc.h
337
+ void start_logger(const std::string& fname) { Logger::start(fname); }
338
+
339
+
340
+ /// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking
341
+ /// function that doesn't stall the CPU waiting for data to be loaded from memory,
342
+ /// which can be quite slow.
343
+ #ifdef NO_PREFETCH
344
+
345
+ void prefetch(void*) {}
346
+
347
+ #else
348
+
349
+ void prefetch(void* addr) {
350
+
351
+ # if defined(__INTEL_COMPILER)
352
+ // This hack prevents prefetches from being optimized away by
353
+ // Intel compiler. Both MSVC and gcc seem not be affected by this.
354
+ __asm__ ("");
355
+ # endif
356
+
357
+ # if defined(__INTEL_COMPILER) || defined(_MSC_VER)
358
+ _mm_prefetch((char*)addr, _MM_HINT_T0);
359
+ # else
360
+ __builtin_prefetch(addr);
361
+ # endif
362
+ }
363
+
364
+ #endif
365
+
366
+
367
+ /// std_aligned_alloc() is our wrapper for systems where the c++17 implementation
368
+ /// does not guarantee the availability of aligned_alloc(). Memory allocated with
369
+ /// std_aligned_alloc() must be freed with std_aligned_free().
370
+
371
+ void* std_aligned_alloc(size_t alignment, size_t size) {
372
+
373
+ #if defined(POSIXALIGNEDALLOC)
374
+ void *mem;
375
+ return posix_memalign(&mem, alignment, size) ? nullptr : mem;
376
+ #elif defined(_WIN32)
377
+ return _mm_malloc(size, alignment);
378
+ #else
379
+ return std::aligned_alloc(alignment, size);
380
+ #endif
381
+ }
382
+
383
+ void std_aligned_free(void* ptr) {
384
+
385
+ #if defined(POSIXALIGNEDALLOC)
386
+ free(ptr);
387
+ #elif defined(_WIN32)
388
+ _mm_free(ptr);
389
+ #else
390
+ free(ptr);
391
+ #endif
392
+ }
393
+
394
+ /// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages.
395
+
396
+ #if defined(_WIN32)
397
+
398
+ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize) {
399
+
400
+ #if !defined(_WIN64)
401
+ return nullptr;
402
+ #else
403
+
404
+ HANDLE hProcessToken { };
405
+ LUID luid { };
406
+ void* mem = nullptr;
407
+
408
+ const size_t largePageSize = GetLargePageMinimum();
409
+ if (!largePageSize)
410
+ return nullptr;
411
+
412
+ // We need SeLockMemoryPrivilege, so try to enable it for the process
413
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken))
414
+ return nullptr;
415
+
416
+ if (LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luid))
417
+ {
418
+ TOKEN_PRIVILEGES tp { };
419
+ TOKEN_PRIVILEGES prevTp { };
420
+ DWORD prevTpLen = 0;
421
+
422
+ tp.PrivilegeCount = 1;
423
+ tp.Privileges[0].Luid = luid;
424
+ tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
425
+
426
+ // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds,
427
+ // we still need to query GetLastError() to ensure that the privileges were actually obtained.
428
+ if (AdjustTokenPrivileges(
429
+ hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) &&
430
+ GetLastError() == ERROR_SUCCESS)
431
+ {
432
+ // Round up size to full pages and allocate
433
+ allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1);
434
+ mem = VirtualAlloc(
435
+ NULL, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
436
+
437
+ // Privilege no longer needed, restore previous state
438
+ AdjustTokenPrivileges(hProcessToken, FALSE, &prevTp, 0, NULL, NULL);
439
+ }
440
+ }
441
+
442
+ CloseHandle(hProcessToken);
443
+
444
+ return mem;
445
+
446
+ #endif
447
+ }
448
+
449
+ void* aligned_large_pages_alloc(size_t allocSize) {
450
+
451
+ // Try to allocate large pages
452
+ void* mem = aligned_large_pages_alloc_windows(allocSize);
453
+
454
+ // Fall back to regular, page aligned, allocation if necessary
455
+ if (!mem)
456
+ mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
457
+
458
+ return mem;
459
+ }
460
+
461
+ #else
462
+
463
+ void* aligned_large_pages_alloc(size_t allocSize) {
464
+
465
+ #if defined(__linux__)
466
+ constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page size
467
+ #else
468
+ constexpr size_t alignment = 4096; // assumed small page size
469
+ #endif
470
+
471
+ // round up to multiples of alignment
472
+ size_t size = ((allocSize + alignment - 1) / alignment) * alignment;
473
+ void *mem = std_aligned_alloc(alignment, size);
474
+ #if defined(MADV_HUGEPAGE)
475
+ madvise(mem, size, MADV_HUGEPAGE);
476
+ #endif
477
+ return mem;
478
+ }
479
+
480
+ #endif
481
+
482
+
483
+ /// aligned_large_pages_free() will free the previously allocated ttmem
484
+
485
+ #if defined(_WIN32)
486
+
487
+ void aligned_large_pages_free(void* mem) {
488
+
489
+ if (mem && !VirtualFree(mem, 0, MEM_RELEASE))
490
+ {
491
+ DWORD err = GetLastError();
492
+ std::cerr << "Failed to free large page memory. Error code: 0x"
493
+ << std::hex << err
494
+ << std::dec << std::endl;
495
+ exit(EXIT_FAILURE);
496
+ }
497
+ }
498
+
499
+ #else
500
+
501
+ void aligned_large_pages_free(void *mem) {
502
+ std_aligned_free(mem);
503
+ }
504
+
505
+ #endif
506
+
507
+
508
+ namespace WinProcGroup {
509
+
510
+ #ifndef _WIN32
511
+
512
+ void bindThisThread(size_t) {}
513
+
514
+ #else
515
+
516
+ /// best_node() retrieves logical processor information using Windows specific
517
+ /// API and returns the best node id for the thread with index idx. Original
518
+ /// code from Texel by Peter Österlund.
519
+
520
+ int best_node(size_t idx) {
521
+
522
+ int threads = 0;
523
+ int nodes = 0;
524
+ int cores = 0;
525
+ DWORD returnLength = 0;
526
+ DWORD byteOffset = 0;
527
+
528
+ // Early exit if the needed API is not available at runtime
529
+ HMODULE k32 = GetModuleHandle("Kernel32.dll");
530
+ auto fun1 = (fun1_t)(void(*)())GetProcAddress(k32, "GetLogicalProcessorInformationEx");
531
+ if (!fun1)
532
+ return -1;
533
+
534
+ // First call to GetLogicalProcessorInformationEx() to get returnLength.
535
+ // We expect the call to fail due to null buffer.
536
+ if (fun1(RelationAll, nullptr, &returnLength))
537
+ return -1;
538
+
539
+ // Once we know returnLength, allocate the buffer
540
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr;
541
+ ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength);
542
+
543
+ // Second call to GetLogicalProcessorInformationEx(), now we expect to succeed
544
+ if (!fun1(RelationAll, buffer, &returnLength))
545
+ {
546
+ free(buffer);
547
+ return -1;
548
+ }
549
+
550
+ while (byteOffset < returnLength)
551
+ {
552
+ if (ptr->Relationship == RelationNumaNode)
553
+ nodes++;
554
+
555
+ else if (ptr->Relationship == RelationProcessorCore)
556
+ {
557
+ cores++;
558
+ threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1;
559
+ }
560
+
561
+ assert(ptr->Size);
562
+ byteOffset += ptr->Size;
563
+ ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size);
564
+ }
565
+
566
+ free(buffer);
567
+
568
+ std::vector<int> groups;
569
+
570
+ // Run as many threads as possible on the same node until core limit is
571
+ // reached, then move on filling the next node.
572
+ for (int n = 0; n < nodes; n++)
573
+ for (int i = 0; i < cores / nodes; i++)
574
+ groups.push_back(n);
575
+
576
+ // In case a core has more than one logical processor (we assume 2) and we
577
+ // have still threads to allocate, then spread them evenly across available
578
+ // nodes.
579
+ for (int t = 0; t < threads - cores; t++)
580
+ groups.push_back(t % nodes);
581
+
582
+ // If we still have more threads than the total number of logical processors
583
+ // then return -1 and let the OS to decide what to do.
584
+ return idx < groups.size() ? groups[idx] : -1;
585
+ }
586
+
587
+
588
+ /// bindThisThread() set the group affinity of the current thread
589
+
590
+ void bindThisThread(size_t idx) {
591
+
592
+ // Use only local variables to be thread-safe
593
+ int node = best_node(idx);
594
+
595
+ if (node == -1)
596
+ return;
597
+
598
+ // Early exit if the needed API are not available at runtime
599
+ HMODULE k32 = GetModuleHandle("Kernel32.dll");
600
+ auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx");
601
+ auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity");
602
+ auto fun4 = (fun4_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2");
603
+ auto fun5 = (fun5_t)(void(*)())GetProcAddress(k32, "GetMaximumProcessorGroupCount");
604
+
605
+ if (!fun2 || !fun3)
606
+ return;
607
+
608
+ if (!fun4 || !fun5)
609
+ {
610
+ GROUP_AFFINITY affinity;
611
+ if (fun2(node, &affinity)) // GetNumaNodeProcessorMaskEx
612
+ fun3(GetCurrentThread(), &affinity, nullptr); // SetThreadGroupAffinity
613
+ }
614
+ else
615
+ {
616
+ // If a numa node has more than one processor group, we assume they are
617
+ // sized equal and we spread threads evenly across the groups.
618
+ USHORT elements, returnedElements;
619
+ elements = fun5(); // GetMaximumProcessorGroupCount
620
+ GROUP_AFFINITY *affinity = (GROUP_AFFINITY*)malloc(elements * sizeof(GROUP_AFFINITY));
621
+ if (fun4(node, affinity, elements, &returnedElements)) // GetNumaNodeProcessorMask2
622
+ fun3(GetCurrentThread(), &affinity[idx % returnedElements], nullptr); // SetThreadGroupAffinity
623
+ free(affinity);
624
+ }
625
+ }
626
+
627
+ #endif
628
+
629
+ } // namespace WinProcGroup
630
+
631
+ #ifdef _WIN32
632
+ #include <direct.h>
633
+ #define GETCWD _getcwd
634
+ #else
635
+ #include <unistd.h>
636
+ #define GETCWD getcwd
637
+ #endif
638
+
639
+ namespace CommandLine {
640
+
641
+ string argv0; // path+name of the executable binary, as given by argv[0]
642
+ string binaryDirectory; // path of the executable directory
643
+ string workingDirectory; // path of the working directory
644
+
645
+ void init([[maybe_unused]] int argc, char* argv[]) {
646
+ string pathSeparator;
647
+
648
+ // extract the path+name of the executable binary
649
+ argv0 = argv[0];
650
+
651
+ #ifdef _WIN32
652
+ pathSeparator = "\\";
653
+ #ifdef _MSC_VER
654
+ // Under windows argv[0] may not have the extension. Also _get_pgmptr() had
655
+ // issues in some windows 10 versions, so check returned values carefully.
656
+ char* pgmptr = nullptr;
657
+ if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr)
658
+ argv0 = pgmptr;
659
+ #endif
660
+ #else
661
+ pathSeparator = "/";
662
+ #endif
663
+
664
+ // extract the working directory
665
+ workingDirectory = "";
666
+ char buff[40000];
667
+ char* cwd = GETCWD(buff, 40000);
668
+ if (cwd)
669
+ workingDirectory = cwd;
670
+
671
+ // extract the binary directory path from argv0
672
+ binaryDirectory = argv0;
673
+ size_t pos = binaryDirectory.find_last_of("\\/");
674
+ if (pos == std::string::npos)
675
+ binaryDirectory = "." + pathSeparator;
676
+ else
677
+ binaryDirectory.resize(pos + 1);
678
+
679
+ // pattern replacement: "./" at the start of path is replaced by the working directory
680
+ if (binaryDirectory.find("." + pathSeparator) == 0)
681
+ binaryDirectory.replace(0, 1, workingDirectory);
682
+ }
683
+
684
+
685
+ } // namespace CommandLine
686
+
687
+ } // namespace Stockfish
src/misc.h ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef MISC_H_INCLUDED
20
+ #define MISC_H_INCLUDED
21
+
22
+ #include <cassert>
23
+ #include <chrono>
24
+ #include <ostream>
25
+ #include <string>
26
+ #include <vector>
27
+ #include <cstdint>
28
+
29
+ #include "types.h"
30
+
31
+ namespace Stockfish {
32
+
33
+ std::string engine_info(bool to_uci = false);
34
+ std::string compiler_info();
35
+ void prefetch(void* addr);
36
+ void start_logger(const std::string& fname);
37
+ void* std_aligned_alloc(size_t alignment, size_t size);
38
+ void std_aligned_free(void* ptr);
39
+ void* aligned_large_pages_alloc(size_t size); // memory aligned by page size, min alignment: 4096 bytes
40
+ void aligned_large_pages_free(void* mem); // nop if mem == nullptr
41
+
42
+ void dbg_hit_on(bool b);
43
+ void dbg_hit_on(bool c, bool b);
44
+ void dbg_mean_of(int v);
45
+ void dbg_print();
46
+
47
+ typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds
48
+ static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits");
49
+ inline TimePoint now() {
50
+ return std::chrono::duration_cast<std::chrono::milliseconds>
51
+ (std::chrono::steady_clock::now().time_since_epoch()).count();
52
+ }
53
+
54
+ template<class Entry, int Size>
55
+ struct HashTable {
56
+ Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; }
57
+
58
+ private:
59
+ std::vector<Entry> table = std::vector<Entry>(Size); // Allocate on the heap
60
+ };
61
+
62
+
63
+ enum SyncCout { IO_LOCK, IO_UNLOCK };
64
+ std::ostream& operator<<(std::ostream&, SyncCout);
65
+
66
+ #define sync_cout std::cout << IO_LOCK
67
+ #define sync_endl std::endl << IO_UNLOCK
68
+
69
+
70
+ // align_ptr_up() : get the first aligned element of an array.
71
+ // ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes,
72
+ // where N is the number of elements in the array.
73
+ template <uintptr_t Alignment, typename T>
74
+ T* align_ptr_up(T* ptr)
75
+ {
76
+ static_assert(alignof(T) < Alignment);
77
+
78
+ const uintptr_t ptrint = reinterpret_cast<uintptr_t>(reinterpret_cast<char*>(ptr));
79
+ return reinterpret_cast<T*>(reinterpret_cast<char*>((ptrint + (Alignment - 1)) / Alignment * Alignment));
80
+ }
81
+
82
+
83
+ // IsLittleEndian : true if and only if the binary is compiled on a little endian machine
84
+ static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 };
85
+ static inline const bool IsLittleEndian = (Le.c[0] == 4);
86
+
87
+
88
+ // RunningAverage : a class to calculate a running average of a series of values.
89
+ // For efficiency, all computations are done with integers.
90
+ class RunningAverage {
91
+ public:
92
+
93
+ // Reset the running average to rational value p / q
94
+ void set(int64_t p, int64_t q)
95
+ { average = p * PERIOD * RESOLUTION / q; }
96
+
97
+ // Update average with value v
98
+ void update(int64_t v)
99
+ { average = RESOLUTION * v + (PERIOD - 1) * average / PERIOD; }
100
+
101
+ // Test if average is strictly greater than rational a / b
102
+ bool is_greater(int64_t a, int64_t b) const
103
+ { return b * average > a * (PERIOD * RESOLUTION); }
104
+
105
+ int64_t value() const
106
+ { return average / (PERIOD * RESOLUTION); }
107
+
108
+ private :
109
+ static constexpr int64_t PERIOD = 4096;
110
+ static constexpr int64_t RESOLUTION = 1024;
111
+ int64_t average;
112
+ };
113
+
114
+ template <typename T, std::size_t MaxSize>
115
+ class ValueList {
116
+
117
+ public:
118
+ std::size_t size() const { return size_; }
119
+ void push_back(const T& value) { values_[size_++] = value; }
120
+ const T* begin() const { return values_; }
121
+ const T* end() const { return values_ + size_; }
122
+
123
+ private:
124
+ T values_[MaxSize];
125
+ std::size_t size_ = 0;
126
+ };
127
+
128
+
129
+ /// xorshift64star Pseudo-Random Number Generator
130
+ /// This class is based on original code written and dedicated
131
+ /// to the public domain by Sebastiano Vigna (2014).
132
+ /// It has the following characteristics:
133
+ ///
134
+ /// - Outputs 64-bit numbers
135
+ /// - Passes Dieharder and SmallCrush test batteries
136
+ /// - Does not require warm-up, no zeroland to escape
137
+ /// - Internal state is a single 64-bit integer
138
+ /// - Period is 2^64 - 1
139
+ /// - Speed: 1.60 ns/call (Core i7 @3.40GHz)
140
+ ///
141
+ /// For further analysis see
142
+ /// <http://vigna.di.unimi.it/ftp/papers/xorshift.pdf>
143
+
144
+ class PRNG {
145
+
146
+ uint64_t s;
147
+
148
+ uint64_t rand64() {
149
+
150
+ s ^= s >> 12, s ^= s << 25, s ^= s >> 27;
151
+ return s * 2685821657736338717LL;
152
+ }
153
+
154
+ public:
155
+ PRNG(uint64_t seed) : s(seed) { assert(seed); }
156
+
157
+ template<typename T> T rand() { return T(rand64()); }
158
+
159
+ /// Special generator used to fast init magic numbers.
160
+ /// Output values only have 1/8th of their bits set on average.
161
+ template<typename T> T sparse_rand()
162
+ { return T(rand64() & rand64() & rand64()); }
163
+ };
164
+
165
+ inline uint64_t mul_hi64(uint64_t a, uint64_t b) {
166
+ #if defined(__GNUC__) && defined(IS_64BIT)
167
+ __extension__ typedef unsigned __int128 uint128;
168
+ return ((uint128)a * (uint128)b) >> 64;
169
+ #else
170
+ uint64_t aL = (uint32_t)a, aH = a >> 32;
171
+ uint64_t bL = (uint32_t)b, bH = b >> 32;
172
+ uint64_t c1 = (aL * bL) >> 32;
173
+ uint64_t c2 = aH * bL + c1;
174
+ uint64_t c3 = aL * bH + (uint32_t)c2;
175
+ return aH * bH + (c2 >> 32) + (c3 >> 32);
176
+ #endif
177
+ }
178
+
179
+ /// Under Windows it is not possible for a process to run on more than one
180
+ /// logical processor group. This usually means to be limited to use max 64
181
+ /// cores. To overcome this, some special platform specific API should be
182
+ /// called to set group affinity for each thread. Original code from Texel by
183
+ /// Peter Österlund.
184
+
185
+ namespace WinProcGroup {
186
+ void bindThisThread(size_t idx);
187
+ }
188
+
189
+ namespace CommandLine {
190
+ void init(int argc, char* argv[]);
191
+
192
+ extern std::string binaryDirectory; // path of the executable directory
193
+ extern std::string workingDirectory; // path of the working directory
194
+ }
195
+
196
+ } // namespace Stockfish
197
+
198
+ #endif // #ifndef MISC_H_INCLUDED
src/movegen.cpp ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <cassert>
20
+
21
+ #include "movegen.h"
22
+ #include "position.h"
23
+
24
+ namespace Stockfish {
25
+
26
+ namespace {
27
+
28
+ template<GenType Type, Direction D>
29
+ ExtMove* make_promotions(ExtMove* moveList, Square to) {
30
+
31
+ if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
32
+ *moveList++ = make<PROMOTION>(to - D, to, QUEEN);
33
+
34
+ if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
35
+ {
36
+ *moveList++ = make<PROMOTION>(to - D, to, ROOK);
37
+ *moveList++ = make<PROMOTION>(to - D, to, BISHOP);
38
+ *moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
39
+ }
40
+
41
+ return moveList;
42
+ }
43
+
44
+
45
+ template<Color Us, GenType Type>
46
+ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
47
+
48
+ constexpr Color Them = ~Us;
49
+ constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
50
+ constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
51
+ constexpr Direction Up = pawn_push(Us);
52
+ constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
53
+ constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
54
+
55
+ const Bitboard emptySquares = ~pos.pieces();
56
+ const Bitboard enemies = Type == EVASIONS ? pos.checkers()
57
+ : pos.pieces(Them);
58
+
59
+ Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
60
+ Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
61
+
62
+ // Single and double pawn pushes, no promotions
63
+ if (Type != CAPTURES)
64
+ {
65
+ Bitboard b1 = shift<Up>(pawnsNotOn7) & emptySquares;
66
+ Bitboard b2 = shift<Up>(b1 & TRank3BB) & emptySquares;
67
+
68
+ if (Type == EVASIONS) // Consider only blocking squares
69
+ {
70
+ b1 &= target;
71
+ b2 &= target;
72
+ }
73
+
74
+ if (Type == QUIET_CHECKS)
75
+ {
76
+ // To make a quiet check, you either make a direct check by pushing a pawn
77
+ // or push a blocker pawn that is not on the same file as the enemy king.
78
+ // Discovered check promotion has been already generated amongst the captures.
79
+ Square ksq = pos.square<KING>(Them);
80
+ Bitboard dcCandidatePawns = pos.blockers_for_king(Them) & ~file_bb(ksq);
81
+ b1 &= pawn_attacks_bb(Them, ksq) | shift< Up>(dcCandidatePawns);
82
+ b2 &= pawn_attacks_bb(Them, ksq) | shift<Up+Up>(dcCandidatePawns);
83
+ }
84
+
85
+ while (b1)
86
+ {
87
+ Square to = pop_lsb(b1);
88
+ *moveList++ = make_move(to - Up, to);
89
+ }
90
+
91
+ while (b2)
92
+ {
93
+ Square to = pop_lsb(b2);
94
+ *moveList++ = make_move(to - Up - Up, to);
95
+ }
96
+ }
97
+
98
+ // Promotions and underpromotions
99
+ if (pawnsOn7)
100
+ {
101
+ Bitboard b1 = shift<UpRight>(pawnsOn7) & enemies;
102
+ Bitboard b2 = shift<UpLeft >(pawnsOn7) & enemies;
103
+ Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
104
+
105
+ if (Type == EVASIONS)
106
+ b3 &= target;
107
+
108
+ while (b1)
109
+ moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(b1));
110
+
111
+ while (b2)
112
+ moveList = make_promotions<Type, UpLeft >(moveList, pop_lsb(b2));
113
+
114
+ while (b3)
115
+ moveList = make_promotions<Type, Up >(moveList, pop_lsb(b3));
116
+ }
117
+
118
+ // Standard and en passant captures
119
+ if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
120
+ {
121
+ Bitboard b1 = shift<UpRight>(pawnsNotOn7) & enemies;
122
+ Bitboard b2 = shift<UpLeft >(pawnsNotOn7) & enemies;
123
+
124
+ while (b1)
125
+ {
126
+ Square to = pop_lsb(b1);
127
+ *moveList++ = make_move(to - UpRight, to);
128
+ }
129
+
130
+ while (b2)
131
+ {
132
+ Square to = pop_lsb(b2);
133
+ *moveList++ = make_move(to - UpLeft, to);
134
+ }
135
+
136
+ if (pos.ep_square() != SQ_NONE)
137
+ {
138
+ assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6));
139
+
140
+ // An en passant capture cannot resolve a discovered check
141
+ if (Type == EVASIONS && (target & (pos.ep_square() + Up)))
142
+ return moveList;
143
+
144
+ b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square());
145
+
146
+ assert(b1);
147
+
148
+ while (b1)
149
+ *moveList++ = make<EN_PASSANT>(pop_lsb(b1), pos.ep_square());
150
+ }
151
+ }
152
+
153
+ return moveList;
154
+ }
155
+
156
+
157
+ template<Color Us, PieceType Pt, bool Checks>
158
+ ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
159
+
160
+ static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()");
161
+
162
+ Bitboard bb = pos.pieces(Us, Pt);
163
+
164
+ while (bb)
165
+ {
166
+ Square from = pop_lsb(bb);
167
+ Bitboard b = attacks_bb<Pt>(from, pos.pieces()) & target;
168
+
169
+ // To check, you either move freely a blocker or make a direct check.
170
+ if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from)))
171
+ b &= pos.check_squares(Pt);
172
+
173
+ while (b)
174
+ *moveList++ = make_move(from, pop_lsb(b));
175
+ }
176
+
177
+ return moveList;
178
+ }
179
+
180
+
181
+ template<Color Us, GenType Type>
182
+ ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
183
+
184
+ static_assert(Type != LEGAL, "Unsupported type in generate_all()");
185
+
186
+ constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations
187
+ const Square ksq = pos.square<KING>(Us);
188
+ Bitboard target;
189
+
190
+ // Skip generating non-king moves when in double check
191
+ if (Type != EVASIONS || !more_than_one(pos.checkers()))
192
+ {
193
+ target = Type == EVASIONS ? between_bb(ksq, lsb(pos.checkers()))
194
+ : Type == NON_EVASIONS ? ~pos.pieces( Us)
195
+ : Type == CAPTURES ? pos.pieces(~Us)
196
+ : ~pos.pieces( ); // QUIETS || QUIET_CHECKS
197
+
198
+ moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
199
+ moveList = generate_moves<Us, KNIGHT, Checks>(pos, moveList, target);
200
+ moveList = generate_moves<Us, BISHOP, Checks>(pos, moveList, target);
201
+ moveList = generate_moves<Us, ROOK, Checks>(pos, moveList, target);
202
+ moveList = generate_moves<Us, QUEEN, Checks>(pos, moveList, target);
203
+ }
204
+
205
+ if (!Checks || pos.blockers_for_king(~Us) & ksq)
206
+ {
207
+ Bitboard b = attacks_bb<KING>(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target);
208
+ if (Checks)
209
+ b &= ~attacks_bb<QUEEN>(pos.square<KING>(~Us));
210
+
211
+ while (b)
212
+ *moveList++ = make_move(ksq, pop_lsb(b));
213
+
214
+ if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING))
215
+ for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } )
216
+ if (!pos.castling_impeded(cr) && pos.can_castle(cr))
217
+ *moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(cr));
218
+ }
219
+
220
+ return moveList;
221
+ }
222
+
223
+ } // namespace
224
+
225
+
226
+ /// <CAPTURES> Generates all pseudo-legal captures plus queen promotions
227
+ /// <QUIETS> Generates all pseudo-legal non-captures and underpromotions
228
+ /// <EVASIONS> Generates all pseudo-legal check evasions when the side to move is in check
229
+ /// <QUIET_CHECKS> Generates all pseudo-legal non-captures giving check, except castling and promotions
230
+ /// <NON_EVASIONS> Generates all pseudo-legal captures and non-captures
231
+ ///
232
+ /// Returns a pointer to the end of the move list.
233
+
234
+ template<GenType Type>
235
+ ExtMove* generate(const Position& pos, ExtMove* moveList) {
236
+
237
+ static_assert(Type != LEGAL, "Unsupported type in generate()");
238
+ assert((Type == EVASIONS) == (bool)pos.checkers());
239
+
240
+ Color us = pos.side_to_move();
241
+
242
+ return us == WHITE ? generate_all<WHITE, Type>(pos, moveList)
243
+ : generate_all<BLACK, Type>(pos, moveList);
244
+ }
245
+
246
+ // Explicit template instantiations
247
+ template ExtMove* generate<CAPTURES>(const Position&, ExtMove*);
248
+ template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
249
+ template ExtMove* generate<EVASIONS>(const Position&, ExtMove*);
250
+ template ExtMove* generate<QUIET_CHECKS>(const Position&, ExtMove*);
251
+ template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
252
+
253
+
254
+ /// generate<LEGAL> generates all the legal moves in the given position
255
+
256
+ template<>
257
+ ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
258
+
259
+ Color us = pos.side_to_move();
260
+ Bitboard pinned = pos.blockers_for_king(us) & pos.pieces(us);
261
+ Square ksq = pos.square<KING>(us);
262
+ ExtMove* cur = moveList;
263
+
264
+ moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList)
265
+ : generate<NON_EVASIONS>(pos, moveList);
266
+ while (cur != moveList)
267
+ if ( ((pinned && pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT)
268
+ && !pos.legal(*cur))
269
+ *cur = (--moveList)->move;
270
+ else
271
+ ++cur;
272
+
273
+ return moveList;
274
+ }
275
+
276
+ } // namespace Stockfish
src/movegen.h ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef MOVEGEN_H_INCLUDED
20
+ #define MOVEGEN_H_INCLUDED
21
+
22
+ #include <algorithm>
23
+
24
+ #include "types.h"
25
+
26
+ namespace Stockfish {
27
+
28
+ class Position;
29
+
30
+ enum GenType {
31
+ CAPTURES,
32
+ QUIETS,
33
+ QUIET_CHECKS,
34
+ EVASIONS,
35
+ NON_EVASIONS,
36
+ LEGAL
37
+ };
38
+
39
+ struct ExtMove {
40
+ Move move;
41
+ int value;
42
+
43
+ operator Move() const { return move; }
44
+ void operator=(Move m) { move = m; }
45
+
46
+ // Inhibit unwanted implicit conversions to Move
47
+ // with an ambiguity that yields to a compile error.
48
+ operator float() const = delete;
49
+ };
50
+
51
+ inline bool operator<(const ExtMove& f, const ExtMove& s) {
52
+ return f.value < s.value;
53
+ }
54
+
55
+ template<GenType>
56
+ ExtMove* generate(const Position& pos, ExtMove* moveList);
57
+
58
+ /// The MoveList struct is a simple wrapper around generate(). It sometimes comes
59
+ /// in handy to use this class instead of the low level generate() function.
60
+ template<GenType T>
61
+ struct MoveList {
62
+
63
+ explicit MoveList(const Position& pos) : last(generate<T>(pos, moveList)) {}
64
+ const ExtMove* begin() const { return moveList; }
65
+ const ExtMove* end() const { return last; }
66
+ size_t size() const { return last - moveList; }
67
+ bool contains(Move move) const {
68
+ return std::find(begin(), end(), move) != end();
69
+ }
70
+
71
+ private:
72
+ ExtMove moveList[MAX_MOVES], *last;
73
+ };
74
+
75
+ } // namespace Stockfish
76
+
77
+ #endif // #ifndef MOVEGEN_H_INCLUDED
src/movepick.cpp ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <cassert>
20
+
21
+ #include "bitboard.h"
22
+ #include "movepick.h"
23
+
24
+ namespace Stockfish {
25
+
26
+ namespace {
27
+
28
+ enum Stages {
29
+ MAIN_TT, CAPTURE_INIT, GOOD_CAPTURE, REFUTATION, QUIET_INIT, QUIET, BAD_CAPTURE,
30
+ EVASION_TT, EVASION_INIT, EVASION,
31
+ PROBCUT_TT, PROBCUT_INIT, PROBCUT,
32
+ QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK
33
+ };
34
+
35
+ // partial_insertion_sort() sorts moves in descending order up to and including
36
+ // a given limit. The order of moves smaller than the limit is left unspecified.
37
+ void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
38
+
39
+ for (ExtMove *sortedEnd = begin, *p = begin + 1; p < end; ++p)
40
+ if (p->value >= limit)
41
+ {
42
+ ExtMove tmp = *p, *q;
43
+ *p = *++sortedEnd;
44
+ for (q = sortedEnd; q != begin && *(q - 1) < tmp; --q)
45
+ *q = *(q - 1);
46
+ *q = tmp;
47
+ }
48
+ }
49
+
50
+ } // namespace
51
+
52
+
53
+ /// Constructors of the MovePicker class. As arguments we pass information
54
+ /// to help it to return the (presumably) good moves first, to decide which
55
+ /// moves to return (in the quiescence search, for instance, we only want to
56
+ /// search captures, promotions, and some checks) and how important good move
57
+ /// ordering is at the current node.
58
+
59
+ /// MovePicker constructor for the main search
60
+ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
61
+ const CapturePieceToHistory* cph,
62
+ const PieceToHistory** ch,
63
+ Move cm,
64
+ const Move* killers)
65
+ : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch),
66
+ ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d)
67
+ {
68
+ assert(d > 0);
69
+
70
+ stage = (pos.checkers() ? EVASION_TT : MAIN_TT) +
71
+ !(ttm && pos.pseudo_legal(ttm));
72
+ threatenedPieces = 0;
73
+ }
74
+
75
+ /// MovePicker constructor for quiescence search
76
+ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
77
+ const CapturePieceToHistory* cph,
78
+ const PieceToHistory** ch,
79
+ Square rs)
80
+ : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d)
81
+ {
82
+ assert(d <= 0);
83
+
84
+ stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) +
85
+ !( ttm
86
+ && pos.pseudo_legal(ttm));
87
+ }
88
+
89
+ /// MovePicker constructor for ProbCut: we generate captures with SEE greater
90
+ /// than or equal to the given threshold.
91
+ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph)
92
+ : pos(p), captureHistory(cph), ttMove(ttm), threshold(th)
93
+ {
94
+ assert(!pos.checkers());
95
+
96
+ stage = PROBCUT_TT + !(ttm && pos.capture(ttm)
97
+ && pos.pseudo_legal(ttm)
98
+ && pos.see_ge(ttm, threshold));
99
+ }
100
+
101
+ /// MovePicker::score() assigns a numerical value to each move in a list, used
102
+ /// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring
103
+ /// captures with a good history. Quiets moves are ordered using the histories.
104
+ template<GenType Type>
105
+ void MovePicker::score() {
106
+
107
+ static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
108
+
109
+ [[maybe_unused]] Bitboard threatenedByPawn, threatenedByMinor, threatenedByRook;
110
+ if constexpr (Type == QUIETS)
111
+ {
112
+ Color us = pos.side_to_move();
113
+
114
+ threatenedByPawn = pos.attacks_by<PAWN>(~us);
115
+ threatenedByMinor = pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatenedByPawn;
116
+ threatenedByRook = pos.attacks_by<ROOK>(~us) | threatenedByMinor;
117
+
118
+ // Pieces threatened by pieces of lesser material value
119
+ threatenedPieces = (pos.pieces(us, QUEEN) & threatenedByRook)
120
+ | (pos.pieces(us, ROOK) & threatenedByMinor)
121
+ | (pos.pieces(us, KNIGHT, BISHOP) & threatenedByPawn);
122
+ }
123
+
124
+ for (auto& m : *this)
125
+ if constexpr (Type == CAPTURES)
126
+ m.value = 6 * int(PieceValue[MG][pos.piece_on(to_sq(m))])
127
+ + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))];
128
+
129
+ else if constexpr (Type == QUIETS)
130
+ m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)]
131
+ + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
132
+ + (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
133
+ + (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
134
+ + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)]
135
+ + (threatenedPieces & from_sq(m) ?
136
+ (type_of(pos.moved_piece(m)) == QUEEN && !(to_sq(m) & threatenedByRook) ? 50000
137
+ : type_of(pos.moved_piece(m)) == ROOK && !(to_sq(m) & threatenedByMinor) ? 25000
138
+ : !(to_sq(m) & threatenedByPawn) ? 15000
139
+ : 0)
140
+ : 0)
141
+ + bool(pos.check_squares(type_of(pos.moved_piece(m))) & to_sq(m)) * 16384;
142
+ else // Type == EVASIONS
143
+ {
144
+ if (pos.capture(m))
145
+ m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
146
+ - Value(type_of(pos.moved_piece(m)))
147
+ + (1 << 28);
148
+ else
149
+ m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
150
+ + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)];
151
+ }
152
+ }
153
+
154
+ /// MovePicker::select() returns the next move satisfying a predicate function.
155
+ /// It never returns the TT move.
156
+ template<MovePicker::PickType T, typename Pred>
157
+ Move MovePicker::select(Pred filter) {
158
+
159
+ while (cur < endMoves)
160
+ {
161
+ if (T == Best)
162
+ std::swap(*cur, *std::max_element(cur, endMoves));
163
+
164
+ if (*cur != ttMove && filter())
165
+ return *cur++;
166
+
167
+ cur++;
168
+ }
169
+ return MOVE_NONE;
170
+ }
171
+
172
+ /// MovePicker::next_move() is the most important method of the MovePicker class. It
173
+ /// returns a new pseudo-legal move every time it is called until there are no more
174
+ /// moves left, picking the move with the highest score from a list of generated moves.
175
+ Move MovePicker::next_move(bool skipQuiets) {
176
+
177
+ top:
178
+ switch (stage) {
179
+
180
+ case MAIN_TT:
181
+ case EVASION_TT:
182
+ case QSEARCH_TT:
183
+ case PROBCUT_TT:
184
+ ++stage;
185
+ return ttMove;
186
+
187
+ case CAPTURE_INIT:
188
+ case PROBCUT_INIT:
189
+ case QCAPTURE_INIT:
190
+ cur = endBadCaptures = moves;
191
+ endMoves = generate<CAPTURES>(pos, cur);
192
+
193
+ score<CAPTURES>();
194
+ partial_insertion_sort(cur, endMoves, std::numeric_limits<int>::min());
195
+ ++stage;
196
+ goto top;
197
+
198
+ case GOOD_CAPTURE:
199
+ if (select<Next>([&](){
200
+ return pos.see_ge(*cur, Value(-69 * cur->value / 1024)) ?
201
+ // Move losing capture to endBadCaptures to be tried later
202
+ true : (*endBadCaptures++ = *cur, false); }))
203
+ return *(cur - 1);
204
+
205
+ // Prepare the pointers to loop over the refutations array
206
+ cur = std::begin(refutations);
207
+ endMoves = std::end(refutations);
208
+
209
+ // If the countermove is the same as a killer, skip it
210
+ if ( refutations[0].move == refutations[2].move
211
+ || refutations[1].move == refutations[2].move)
212
+ --endMoves;
213
+
214
+ ++stage;
215
+ [[fallthrough]];
216
+
217
+ case REFUTATION:
218
+ if (select<Next>([&](){ return *cur != MOVE_NONE
219
+ && !pos.capture(*cur)
220
+ && pos.pseudo_legal(*cur); }))
221
+ return *(cur - 1);
222
+ ++stage;
223
+ [[fallthrough]];
224
+
225
+ case QUIET_INIT:
226
+ if (!skipQuiets)
227
+ {
228
+ cur = endBadCaptures;
229
+ endMoves = generate<QUIETS>(pos, cur);
230
+
231
+ score<QUIETS>();
232
+ partial_insertion_sort(cur, endMoves, -3000 * depth);
233
+ }
234
+
235
+ ++stage;
236
+ [[fallthrough]];
237
+
238
+ case QUIET:
239
+ if ( !skipQuiets
240
+ && select<Next>([&](){return *cur != refutations[0].move
241
+ && *cur != refutations[1].move
242
+ && *cur != refutations[2].move;}))
243
+ return *(cur - 1);
244
+
245
+ // Prepare the pointers to loop over the bad captures
246
+ cur = moves;
247
+ endMoves = endBadCaptures;
248
+
249
+ ++stage;
250
+ [[fallthrough]];
251
+
252
+ case BAD_CAPTURE:
253
+ return select<Next>([](){ return true; });
254
+
255
+ case EVASION_INIT:
256
+ cur = moves;
257
+ endMoves = generate<EVASIONS>(pos, cur);
258
+
259
+ score<EVASIONS>();
260
+ ++stage;
261
+ [[fallthrough]];
262
+
263
+ case EVASION:
264
+ return select<Best>([](){ return true; });
265
+
266
+ case PROBCUT:
267
+ return select<Next>([&](){ return pos.see_ge(*cur, threshold); });
268
+
269
+ case QCAPTURE:
270
+ if (select<Next>([&](){ return depth > DEPTH_QS_RECAPTURES
271
+ || to_sq(*cur) == recaptureSquare; }))
272
+ return *(cur - 1);
273
+
274
+ // If we did not find any move and we do not try checks, we have finished
275
+ if (depth != DEPTH_QS_CHECKS)
276
+ return MOVE_NONE;
277
+
278
+ ++stage;
279
+ [[fallthrough]];
280
+
281
+ case QCHECK_INIT:
282
+ cur = moves;
283
+ endMoves = generate<QUIET_CHECKS>(pos, cur);
284
+
285
+ ++stage;
286
+ [[fallthrough]];
287
+
288
+ case QCHECK:
289
+ return select<Next>([](){ return true; });
290
+ }
291
+
292
+ assert(false);
293
+ return MOVE_NONE; // Silence warning
294
+ }
295
+
296
+ } // namespace Stockfish
src/movepick.h ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef MOVEPICK_H_INCLUDED
20
+ #define MOVEPICK_H_INCLUDED
21
+
22
+ #include <array>
23
+ #include <limits>
24
+ #include <type_traits>
25
+
26
+ #include "movegen.h"
27
+ #include "position.h"
28
+ #include "types.h"
29
+
30
+ namespace Stockfish {
31
+
32
+ /// StatsEntry stores the stat table value. It is usually a number but could
33
+ /// be a move or even a nested history. We use a class instead of naked value
34
+ /// to directly call history update operator<<() on the entry so to use stats
35
+ /// tables at caller sites as simple multi-dim arrays.
36
+ template<typename T, int D>
37
+ class StatsEntry {
38
+
39
+ T entry;
40
+
41
+ public:
42
+ void operator=(const T& v) { entry = v; }
43
+ T* operator&() { return &entry; }
44
+ T* operator->() { return &entry; }
45
+ operator const T&() const { return entry; }
46
+
47
+ void operator<<(int bonus) {
48
+ assert(abs(bonus) <= D); // Ensure range is [-D, D]
49
+ static_assert(D <= std::numeric_limits<T>::max(), "D overflows T");
50
+
51
+ entry += bonus - entry * abs(bonus) / D;
52
+
53
+ assert(abs(entry) <= D);
54
+ }
55
+ };
56
+
57
+ /// Stats is a generic N-dimensional array used to store various statistics.
58
+ /// The first template parameter T is the base type of the array, the second
59
+ /// template parameter D limits the range of updates in [-D, D] when we update
60
+ /// values with the << operator, while the last parameters (Size and Sizes)
61
+ /// encode the dimensions of the array.
62
+ template <typename T, int D, int Size, int... Sizes>
63
+ struct Stats : public std::array<Stats<T, D, Sizes...>, Size>
64
+ {
65
+ typedef Stats<T, D, Size, Sizes...> stats;
66
+
67
+ void fill(const T& v) {
68
+
69
+ // For standard-layout 'this' points to first struct member
70
+ assert(std::is_standard_layout<stats>::value);
71
+
72
+ typedef StatsEntry<T, D> entry;
73
+ entry* p = reinterpret_cast<entry*>(this);
74
+ std::fill(p, p + sizeof(*this) / sizeof(entry), v);
75
+ }
76
+ };
77
+
78
+ template <typename T, int D, int Size>
79
+ struct Stats<T, D, Size> : public std::array<StatsEntry<T, D>, Size> {};
80
+
81
+ /// In stats table, D=0 means that the template parameter is not used
82
+ enum StatsParams { NOT_USED = 0 };
83
+ enum StatsType { NoCaptures, Captures };
84
+
85
+ /// ButterflyHistory records how often quiet moves have been successful or
86
+ /// unsuccessful during the current search, and is used for reduction and move
87
+ /// ordering decisions. It uses 2 tables (one for each color) indexed by
88
+ /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
89
+ /// (~11 elo)
90
+ typedef Stats<int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
91
+
92
+ /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
93
+ /// move, see www.chessprogramming.org/Countermove_Heuristic
94
+ typedef Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB> CounterMoveHistory;
95
+
96
+ /// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
97
+ typedef Stats<int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB> CapturePieceToHistory;
98
+
99
+ /// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to]
100
+ typedef Stats<int16_t, 29952, PIECE_NB, SQUARE_NB> PieceToHistory;
101
+
102
+ /// ContinuationHistory is the combined history of a given pair of moves, usually
103
+ /// the current one given a previous one. The nested history table is based on
104
+ /// PieceToHistory instead of ButterflyBoards.
105
+ /// (~63 elo)
106
+ typedef Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB> ContinuationHistory;
107
+
108
+
109
+ /// MovePicker class is used to pick one pseudo-legal move at a time from the
110
+ /// current position. The most important method is next_move(), which returns a
111
+ /// new pseudo-legal move each time it is called, until there are no moves left,
112
+ /// when MOVE_NONE is returned. In order to improve the efficiency of the
113
+ /// alpha-beta algorithm, MovePicker attempts to return the moves which are most
114
+ /// likely to get a cut-off first.
115
+ class MovePicker {
116
+
117
+ enum PickType { Next, Best };
118
+
119
+ public:
120
+ MovePicker(const MovePicker&) = delete;
121
+ MovePicker& operator=(const MovePicker&) = delete;
122
+ MovePicker(const Position&, Move, Depth, const ButterflyHistory*,
123
+ const CapturePieceToHistory*,
124
+ const PieceToHistory**,
125
+ Move,
126
+ const Move*);
127
+ MovePicker(const Position&, Move, Depth, const ButterflyHistory*,
128
+ const CapturePieceToHistory*,
129
+ const PieceToHistory**,
130
+ Square);
131
+ MovePicker(const Position&, Move, Value, const CapturePieceToHistory*);
132
+ Move next_move(bool skipQuiets = false);
133
+
134
+ Bitboard threatenedPieces;
135
+
136
+ private:
137
+ template<PickType T, typename Pred> Move select(Pred);
138
+ template<GenType> void score();
139
+ ExtMove* begin() { return cur; }
140
+ ExtMove* end() { return endMoves; }
141
+
142
+ const Position& pos;
143
+ const ButterflyHistory* mainHistory;
144
+ const CapturePieceToHistory* captureHistory;
145
+ const PieceToHistory** continuationHistory;
146
+ Move ttMove;
147
+ ExtMove refutations[3], *cur, *endMoves, *endBadCaptures;
148
+ int stage;
149
+ Square recaptureSquare;
150
+ Value threshold;
151
+ Depth depth;
152
+ ExtMove moves[MAX_MOVES];
153
+ };
154
+
155
+ } // namespace Stockfish
156
+
157
+ #endif // #ifndef MOVEPICK_H_INCLUDED
src/nnue/.DS_Store ADDED
Binary file (6.15 kB). View file
 
src/nnue/evaluate_nnue.cpp ADDED
@@ -0,0 +1,406 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ // Code for calculating NNUE evaluation function
20
+
21
+ #include <iostream>
22
+ #include <set>
23
+ #include <sstream>
24
+ #include <iomanip>
25
+ #include <fstream>
26
+
27
+ #include "../evaluate.h"
28
+ #include "../position.h"
29
+ #include "../misc.h"
30
+ #include "../uci.h"
31
+ #include "../types.h"
32
+
33
+ #include "evaluate_nnue.h"
34
+
35
+ namespace Stockfish::Eval::NNUE {
36
+
37
+ // Input feature converter
38
+ LargePagePtr<FeatureTransformer> featureTransformer;
39
+
40
+ // Evaluation function
41
+ AlignedPtr<Network> network[LayerStacks];
42
+
43
+ // Evaluation function file name
44
+ std::string fileName;
45
+ std::string netDescription;
46
+
47
+ namespace Detail {
48
+
49
+ // Initialize the evaluation function parameters
50
+ template <typename T>
51
+ void initialize(AlignedPtr<T>& pointer) {
52
+
53
+ pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
54
+ std::memset(pointer.get(), 0, sizeof(T));
55
+ }
56
+
57
+ template <typename T>
58
+ void initialize(LargePagePtr<T>& pointer) {
59
+
60
+ static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
61
+ pointer.reset(reinterpret_cast<T*>(aligned_large_pages_alloc(sizeof(T))));
62
+ std::memset(pointer.get(), 0, sizeof(T));
63
+ }
64
+
65
+ // Read evaluation function parameters
66
+ template <typename T>
67
+ bool read_parameters(std::istream& stream, T& reference) {
68
+
69
+ std::uint32_t header;
70
+ header = read_little_endian<std::uint32_t>(stream);
71
+ if (!stream || header != T::get_hash_value()) return false;
72
+ return reference.read_parameters(stream);
73
+ }
74
+
75
+ // Write evaluation function parameters
76
+ template <typename T>
77
+ bool write_parameters(std::ostream& stream, const T& reference) {
78
+
79
+ write_little_endian<std::uint32_t>(stream, T::get_hash_value());
80
+ return reference.write_parameters(stream);
81
+ }
82
+
83
+ } // namespace Detail
84
+
85
+ // Initialize the evaluation function parameters
86
+ void initialize() {
87
+
88
+ Detail::initialize(featureTransformer);
89
+ for (std::size_t i = 0; i < LayerStacks; ++i)
90
+ Detail::initialize(network[i]);
91
+ }
92
+
93
+ // Read network header
94
+ bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc)
95
+ {
96
+ std::uint32_t version, size;
97
+
98
+ version = read_little_endian<std::uint32_t>(stream);
99
+ *hashValue = read_little_endian<std::uint32_t>(stream);
100
+ size = read_little_endian<std::uint32_t>(stream);
101
+ if (!stream || version != Version) return false;
102
+ desc->resize(size);
103
+ stream.read(&(*desc)[0], size);
104
+ return !stream.fail();
105
+ }
106
+
107
+ // Write network header
108
+ bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc)
109
+ {
110
+ write_little_endian<std::uint32_t>(stream, Version);
111
+ write_little_endian<std::uint32_t>(stream, hashValue);
112
+ write_little_endian<std::uint32_t>(stream, (std::uint32_t)desc.size());
113
+ stream.write(&desc[0], desc.size());
114
+ return !stream.fail();
115
+ }
116
+
117
+ // Read network parameters
118
+ bool read_parameters(std::istream& stream) {
119
+
120
+ std::uint32_t hashValue;
121
+ if (!read_header(stream, &hashValue, &netDescription)) return false;
122
+ if (hashValue != HashValue) return false;
123
+ if (!Detail::read_parameters(stream, *featureTransformer)) return false;
124
+ for (std::size_t i = 0; i < LayerStacks; ++i)
125
+ if (!Detail::read_parameters(stream, *(network[i]))) return false;
126
+ return stream && stream.peek() == std::ios::traits_type::eof();
127
+ }
128
+
129
+ // Write network parameters
130
+ bool write_parameters(std::ostream& stream) {
131
+
132
+ if (!write_header(stream, HashValue, netDescription)) return false;
133
+ if (!Detail::write_parameters(stream, *featureTransformer)) return false;
134
+ for (std::size_t i = 0; i < LayerStacks; ++i)
135
+ if (!Detail::write_parameters(stream, *(network[i]))) return false;
136
+ return (bool)stream;
137
+ }
138
+
139
+ // Evaluation function. Perform differential calculation.
140
+ Value evaluate(const Position& pos, bool adjusted, int* complexity) {
141
+
142
+ // We manually align the arrays on the stack because with gcc < 9.3
143
+ // overaligning stack variables with alignas() doesn't work correctly.
144
+
145
+ constexpr uint64_t alignment = CacheLineSize;
146
+ int delta = 24 - pos.non_pawn_material() / 9560;
147
+
148
+ #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
149
+ TransformedFeatureType transformedFeaturesUnaligned[
150
+ FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)];
151
+
152
+ auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
153
+ #else
154
+ alignas(alignment)
155
+ TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
156
+ #endif
157
+
158
+ ASSERT_ALIGNED(transformedFeatures, alignment);
159
+
160
+ const int bucket = (pos.count<ALL_PIECES>() - 1) / 4;
161
+ const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket);
162
+ const auto positional = network[bucket]->propagate(transformedFeatures);
163
+
164
+ if (complexity)
165
+ *complexity = abs(psqt - positional) / OutputScale;
166
+
167
+ // Give more value to positional evaluation when adjusted flag is set
168
+ if (adjusted)
169
+ return static_cast<Value>(((1024 - delta) * psqt + (1024 + delta) * positional) / (1024 * OutputScale));
170
+ else
171
+ return static_cast<Value>((psqt + positional) / OutputScale);
172
+ }
173
+
174
+ struct NnueEvalTrace {
175
+ static_assert(LayerStacks == PSQTBuckets);
176
+
177
+ Value psqt[LayerStacks];
178
+ Value positional[LayerStacks];
179
+ std::size_t correctBucket;
180
+ };
181
+
182
+ static NnueEvalTrace trace_evaluate(const Position& pos) {
183
+
184
+ // We manually align the arrays on the stack because with gcc < 9.3
185
+ // overaligning stack variables with alignas() doesn't work correctly.
186
+
187
+ constexpr uint64_t alignment = CacheLineSize;
188
+
189
+ #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
190
+ TransformedFeatureType transformedFeaturesUnaligned[
191
+ FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)];
192
+
193
+ auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
194
+ #else
195
+ alignas(alignment)
196
+ TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
197
+ #endif
198
+
199
+ ASSERT_ALIGNED(transformedFeatures, alignment);
200
+
201
+ NnueEvalTrace t{};
202
+ t.correctBucket = (pos.count<ALL_PIECES>() - 1) / 4;
203
+ for (IndexType bucket = 0; bucket < LayerStacks; ++bucket) {
204
+ const auto materialist = featureTransformer->transform(pos, transformedFeatures, bucket);
205
+ const auto positional = network[bucket]->propagate(transformedFeatures);
206
+
207
+ t.psqt[bucket] = static_cast<Value>( materialist / OutputScale );
208
+ t.positional[bucket] = static_cast<Value>( positional / OutputScale );
209
+ }
210
+
211
+ return t;
212
+ }
213
+
214
+ static const std::string PieceToChar(" PNBRQK pnbrqk");
215
+
216
+
217
+ // format_cp_compact() converts a Value into (centi)pawns and writes it in a buffer.
218
+ // The buffer must have capacity for at least 5 chars.
219
+ static void format_cp_compact(Value v, char* buffer) {
220
+
221
+ buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' ');
222
+
223
+ int cp = std::abs(100 * v / UCI::NormalizeToPawnValue);
224
+ if (cp >= 10000)
225
+ {
226
+ buffer[1] = '0' + cp / 10000; cp %= 10000;
227
+ buffer[2] = '0' + cp / 1000; cp %= 1000;
228
+ buffer[3] = '0' + cp / 100;
229
+ buffer[4] = ' ';
230
+ }
231
+ else if (cp >= 1000)
232
+ {
233
+ buffer[1] = '0' + cp / 1000; cp %= 1000;
234
+ buffer[2] = '0' + cp / 100; cp %= 100;
235
+ buffer[3] = '.';
236
+ buffer[4] = '0' + cp / 10;
237
+ }
238
+ else
239
+ {
240
+ buffer[1] = '0' + cp / 100; cp %= 100;
241
+ buffer[2] = '.';
242
+ buffer[3] = '0' + cp / 10; cp %= 10;
243
+ buffer[4] = '0' + cp / 1;
244
+ }
245
+ }
246
+
247
+
248
+ // format_cp_aligned_dot() converts a Value into (centi)pawns and writes it in a buffer,
249
+ // always keeping two decimals. The buffer must have capacity for at least 7 chars.
250
+ static void format_cp_aligned_dot(Value v, char* buffer) {
251
+
252
+ buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' ');
253
+
254
+ double cp = 1.0 * std::abs(int(v)) / UCI::NormalizeToPawnValue;
255
+ sprintf(&buffer[1], "%6.2f", cp);
256
+ }
257
+
258
+
259
+ // trace() returns a string with the value of each piece on a board,
260
+ // and a table for (PSQT, Layers) values bucket by bucket.
261
+
262
+ std::string trace(Position& pos) {
263
+
264
+ std::stringstream ss;
265
+
266
+ char board[3*8+1][8*8+2];
267
+ std::memset(board, ' ', sizeof(board));
268
+ for (int row = 0; row < 3*8+1; ++row)
269
+ board[row][8*8+1] = '\0';
270
+
271
+ // A lambda to output one box of the board
272
+ auto writeSquare = [&board](File file, Rank rank, Piece pc, Value value) {
273
+
274
+ const int x = ((int)file) * 8;
275
+ const int y = (7 - (int)rank) * 3;
276
+ for (int i = 1; i < 8; ++i)
277
+ board[y][x+i] = board[y+3][x+i] = '-';
278
+ for (int i = 1; i < 3; ++i)
279
+ board[y+i][x] = board[y+i][x+8] = '|';
280
+ board[y][x] = board[y][x+8] = board[y+3][x+8] = board[y+3][x] = '+';
281
+ if (pc != NO_PIECE)
282
+ board[y+1][x+4] = PieceToChar[pc];
283
+ if (value != VALUE_NONE)
284
+ format_cp_compact(value, &board[y+2][x+2]);
285
+ };
286
+
287
+ // We estimate the value of each piece by doing a differential evaluation from
288
+ // the current base eval, simulating the removal of the piece from its square.
289
+ Value base = evaluate(pos);
290
+ base = pos.side_to_move() == WHITE ? base : -base;
291
+
292
+ for (File f = FILE_A; f <= FILE_H; ++f)
293
+ for (Rank r = RANK_1; r <= RANK_8; ++r)
294
+ {
295
+ Square sq = make_square(f, r);
296
+ Piece pc = pos.piece_on(sq);
297
+ Value v = VALUE_NONE;
298
+
299
+ if (pc != NO_PIECE && type_of(pc) != KING)
300
+ {
301
+ auto st = pos.state();
302
+
303
+ pos.remove_piece(sq);
304
+ st->accumulator.computed[WHITE] = false;
305
+ st->accumulator.computed[BLACK] = false;
306
+
307
+ Value eval = evaluate(pos);
308
+ eval = pos.side_to_move() == WHITE ? eval : -eval;
309
+ v = base - eval;
310
+
311
+ pos.put_piece(pc, sq);
312
+ st->accumulator.computed[WHITE] = false;
313
+ st->accumulator.computed[BLACK] = false;
314
+ }
315
+
316
+ writeSquare(f, r, pc, v);
317
+ }
318
+
319
+ ss << " NNUE derived piece values:\n";
320
+ for (int row = 0; row < 3*8+1; ++row)
321
+ ss << board[row] << '\n';
322
+ ss << '\n';
323
+
324
+ auto t = trace_evaluate(pos);
325
+
326
+ ss << " NNUE network contributions "
327
+ << (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl
328
+ << "+------------+------------+------------+------------+\n"
329
+ << "| Bucket | Material | Positional | Total |\n"
330
+ << "| | (PSQT) | (Layers) | |\n"
331
+ << "+------------+------------+------------+------------+\n";
332
+
333
+ for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket)
334
+ {
335
+ char buffer[3][8];
336
+ std::memset(buffer, '\0', sizeof(buffer));
337
+
338
+ format_cp_aligned_dot(t.psqt[bucket], buffer[0]);
339
+ format_cp_aligned_dot(t.positional[bucket], buffer[1]);
340
+ format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], buffer[2]);
341
+
342
+ ss << "| " << bucket << " "
343
+ << " | " << buffer[0] << " "
344
+ << " | " << buffer[1] << " "
345
+ << " | " << buffer[2] << " "
346
+ << " |";
347
+ if (bucket == t.correctBucket)
348
+ ss << " <-- this bucket is used";
349
+ ss << '\n';
350
+ }
351
+
352
+ ss << "+------------+------------+------------+------------+\n";
353
+
354
+ return ss.str();
355
+ }
356
+
357
+
358
+ // Load eval, from a file stream or a memory stream
359
+ bool load_eval(std::string name, std::istream& stream) {
360
+
361
+ initialize();
362
+ fileName = name;
363
+ return read_parameters(stream);
364
+ }
365
+
366
+ // Save eval, to a file stream or a memory stream
367
+ bool save_eval(std::ostream& stream) {
368
+
369
+ if (fileName.empty())
370
+ return false;
371
+
372
+ return write_parameters(stream);
373
+ }
374
+
375
+ /// Save eval, to a file given by its name
376
+ bool save_eval(const std::optional<std::string>& filename) {
377
+
378
+ std::string actualFilename;
379
+ std::string msg;
380
+
381
+ if (filename.has_value())
382
+ actualFilename = filename.value();
383
+ else
384
+ {
385
+ if (currentEvalFileName != EvalFileDefaultName)
386
+ {
387
+ msg = "Failed to export a net. A non-embedded net can only be saved if the filename is specified";
388
+
389
+ sync_cout << msg << sync_endl;
390
+ return false;
391
+ }
392
+ actualFilename = EvalFileDefaultName;
393
+ }
394
+
395
+ std::ofstream stream(actualFilename, std::ios_base::binary);
396
+ bool saved = save_eval(stream);
397
+
398
+ msg = saved ? "Network saved successfully to " + actualFilename
399
+ : "Failed to export a net";
400
+
401
+ sync_cout << msg << sync_endl;
402
+ return saved;
403
+ }
404
+
405
+
406
+ } // namespace Stockfish::Eval::NNUE
src/nnue/evaluate_nnue.h ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ // header used in NNUE evaluation function
20
+
21
+ #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
22
+ #define NNUE_EVALUATE_NNUE_H_INCLUDED
23
+
24
+ #include "nnue_feature_transformer.h"
25
+
26
+ #include <memory>
27
+
28
+ namespace Stockfish::Eval::NNUE {
29
+
30
+ // Hash value of evaluation function structure
31
+ constexpr std::uint32_t HashValue =
32
+ FeatureTransformer::get_hash_value() ^ Network::get_hash_value();
33
+
34
+ // Deleter for automating release of memory area
35
+ template <typename T>
36
+ struct AlignedDeleter {
37
+ void operator()(T* ptr) const {
38
+ ptr->~T();
39
+ std_aligned_free(ptr);
40
+ }
41
+ };
42
+
43
+ template <typename T>
44
+ struct LargePageDeleter {
45
+ void operator()(T* ptr) const {
46
+ ptr->~T();
47
+ aligned_large_pages_free(ptr);
48
+ }
49
+ };
50
+
51
+ template <typename T>
52
+ using AlignedPtr = std::unique_ptr<T, AlignedDeleter<T>>;
53
+
54
+ template <typename T>
55
+ using LargePagePtr = std::unique_ptr<T, LargePageDeleter<T>>;
56
+
57
+ } // namespace Stockfish::Eval::NNUE
58
+
59
+ #endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
src/nnue/features/half_ka_v2_hm.cpp ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ //Definition of input features HalfKAv2_hm of NNUE evaluation function
20
+
21
+ #include "half_ka_v2_hm.h"
22
+
23
+ #include "../../position.h"
24
+
25
+ namespace Stockfish::Eval::NNUE::Features {
26
+
27
+ // Index of a feature for a given king position and another piece on some square
28
+ template<Color Perspective>
29
+ inline IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq) {
30
+ return IndexType((int(s) ^ OrientTBL[Perspective][ksq]) + PieceSquareIndex[Perspective][pc] + KingBuckets[Perspective][ksq]);
31
+ }
32
+
33
+ // Get a list of indices for active features
34
+ template<Color Perspective>
35
+ void HalfKAv2_hm::append_active_indices(
36
+ const Position& pos,
37
+ IndexList& active
38
+ ) {
39
+ Square ksq = pos.square<KING>(Perspective);
40
+ Bitboard bb = pos.pieces();
41
+ while (bb)
42
+ {
43
+ Square s = pop_lsb(bb);
44
+ active.push_back(make_index<Perspective>(s, pos.piece_on(s), ksq));
45
+ }
46
+ }
47
+
48
+ // Explicit template instantiations
49
+ template void HalfKAv2_hm::append_active_indices<WHITE>(const Position& pos, IndexList& active);
50
+ template void HalfKAv2_hm::append_active_indices<BLACK>(const Position& pos, IndexList& active);
51
+
52
+ // append_changed_indices() : get a list of indices for recently changed features
53
+ template<Color Perspective>
54
+ void HalfKAv2_hm::append_changed_indices(
55
+ Square ksq,
56
+ const DirtyPiece& dp,
57
+ IndexList& removed,
58
+ IndexList& added
59
+ ) {
60
+ for (int i = 0; i < dp.dirty_num; ++i) {
61
+ if (dp.from[i] != SQ_NONE)
62
+ removed.push_back(make_index<Perspective>(dp.from[i], dp.piece[i], ksq));
63
+ if (dp.to[i] != SQ_NONE)
64
+ added.push_back(make_index<Perspective>(dp.to[i], dp.piece[i], ksq));
65
+ }
66
+ }
67
+
68
+ // Explicit template instantiations
69
+ template void HalfKAv2_hm::append_changed_indices<WHITE>(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added);
70
+ template void HalfKAv2_hm::append_changed_indices<BLACK>(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added);
71
+
72
+ int HalfKAv2_hm::update_cost(const StateInfo* st) {
73
+ return st->dirtyPiece.dirty_num;
74
+ }
75
+
76
+ int HalfKAv2_hm::refresh_cost(const Position& pos) {
77
+ return pos.count<ALL_PIECES>();
78
+ }
79
+
80
+ bool HalfKAv2_hm::requires_refresh(const StateInfo* st, Color perspective) {
81
+ return st->dirtyPiece.piece[0] == make_piece(perspective, KING);
82
+ }
83
+
84
+ } // namespace Stockfish::Eval::NNUE::Features
src/nnue/features/half_ka_v2_hm.h ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ //Definition of input features HalfKP of NNUE evaluation function
20
+
21
+ #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED
22
+ #define NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED
23
+
24
+ #include "../nnue_common.h"
25
+
26
+ #include "../../evaluate.h"
27
+ #include "../../misc.h"
28
+
29
+ namespace Stockfish {
30
+ struct StateInfo;
31
+ }
32
+
33
+ namespace Stockfish::Eval::NNUE::Features {
34
+
35
+ // Feature HalfKAv2_hm: Combination of the position of own king
36
+ // and the position of pieces. Position mirrored such that king always on e..h files.
37
+ class HalfKAv2_hm {
38
+
39
+ // unique number for each piece type on each square
40
+ enum {
41
+ PS_NONE = 0,
42
+ PS_W_PAWN = 0,
43
+ PS_B_PAWN = 1 * SQUARE_NB,
44
+ PS_W_KNIGHT = 2 * SQUARE_NB,
45
+ PS_B_KNIGHT = 3 * SQUARE_NB,
46
+ PS_W_BISHOP = 4 * SQUARE_NB,
47
+ PS_B_BISHOP = 5 * SQUARE_NB,
48
+ PS_W_ROOK = 6 * SQUARE_NB,
49
+ PS_B_ROOK = 7 * SQUARE_NB,
50
+ PS_W_QUEEN = 8 * SQUARE_NB,
51
+ PS_B_QUEEN = 9 * SQUARE_NB,
52
+ PS_KING = 10 * SQUARE_NB,
53
+ PS_NB = 11 * SQUARE_NB
54
+ };
55
+
56
+ static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = {
57
+ // convention: W - us, B - them
58
+ // viewed from other side, W and B are reversed
59
+ { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE,
60
+ PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE },
61
+ { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE,
62
+ PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE }
63
+ };
64
+
65
+ // Index of a feature for a given king position and another piece on some square
66
+ template<Color Perspective>
67
+ static IndexType make_index(Square s, Piece pc, Square ksq);
68
+
69
+ public:
70
+ // Feature name
71
+ static constexpr const char* Name = "HalfKAv2_hm(Friend)";
72
+
73
+ // Hash value embedded in the evaluation file
74
+ static constexpr std::uint32_t HashValue = 0x7f234cb8u;
75
+
76
+ // Number of feature dimensions
77
+ static constexpr IndexType Dimensions =
78
+ static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_NB) / 2;
79
+
80
+ #define B(v) (v * PS_NB)
81
+ static constexpr int KingBuckets[COLOR_NB][SQUARE_NB] = {
82
+ { B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28),
83
+ B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24),
84
+ B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20),
85
+ B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16),
86
+ B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12),
87
+ B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8),
88
+ B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4),
89
+ B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0) },
90
+ { B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0),
91
+ B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4),
92
+ B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8),
93
+ B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12),
94
+ B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16),
95
+ B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20),
96
+ B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24),
97
+ B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28) }
98
+ };
99
+ #undef B
100
+
101
+ // Orient a square according to perspective (rotates by 180 for black)
102
+ static constexpr int OrientTBL[COLOR_NB][SQUARE_NB] = {
103
+ { SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
104
+ SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
105
+ SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
106
+ SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
107
+ SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
108
+ SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
109
+ SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
110
+ SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1 },
111
+ { SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
112
+ SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
113
+ SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
114
+ SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
115
+ SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
116
+ SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
117
+ SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
118
+ SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8 }
119
+ };
120
+
121
+ // Maximum number of simultaneously active features.
122
+ static constexpr IndexType MaxActiveDimensions = 32;
123
+ using IndexList = ValueList<IndexType, MaxActiveDimensions>;
124
+
125
+ // Get a list of indices for active features
126
+ template<Color Perspective>
127
+ static void append_active_indices(
128
+ const Position& pos,
129
+ IndexList& active);
130
+
131
+ // Get a list of indices for recently changed features
132
+ template<Color Perspective>
133
+ static void append_changed_indices(
134
+ Square ksq,
135
+ const DirtyPiece& dp,
136
+ IndexList& removed,
137
+ IndexList& added
138
+ );
139
+
140
+ // Returns the cost of updating one perspective, the most costly one.
141
+ // Assumes no refresh needed.
142
+ static int update_cost(const StateInfo* st);
143
+ static int refresh_cost(const Position& pos);
144
+
145
+ // Returns whether the change stored in this StateInfo means that
146
+ // a full accumulator refresh is required.
147
+ static bool requires_refresh(const StateInfo* st, Color perspective);
148
+ };
149
+
150
+ } // namespace Stockfish::Eval::NNUE::Features
151
+
152
+ #endif // #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED
src/nnue/layers/affine_transform.h ADDED
@@ -0,0 +1,545 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ // Definition of layer AffineTransform of NNUE evaluation function
20
+
21
+ #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
22
+ #define NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
23
+
24
+ #include <iostream>
25
+ #include <algorithm>
26
+ #include <type_traits>
27
+ #include "../nnue_common.h"
28
+ #include "simd.h"
29
+
30
+ /*
31
+ This file contains the definition for a fully connected layer (aka affine transform).
32
+ Two approaches are employed, depending on the sizes of the transform.
33
+
34
+ Approach 1:
35
+ - used when the PaddedInputDimensions >= 128
36
+ - uses AVX512 if possible
37
+ - processes inputs in batches of 2*InputSimdWidth
38
+ - so in batches of 128 for AVX512
39
+ - the weight blocks of size InputSimdWidth are transposed such that
40
+ access is sequential
41
+ - N columns of the weight matrix are processed a time, where N
42
+ depends on the architecture (the amount of registers)
43
+ - accumulate + hadd is used
44
+
45
+ Approach 2:
46
+ - used when the PaddedInputDimensions < 128
47
+ - does not use AVX512
48
+ - expected use-case is for when PaddedInputDimensions == 32 and InputDimensions <= 32.
49
+ - that's why AVX512 is hard to implement
50
+ - expected use-case is small layers
51
+ - not optimized as well as the approach 1
52
+ - inputs are processed in chunks of 4, weights are respectively transposed
53
+ - accumulation happens directly to int32s
54
+ */
55
+
56
+ namespace Stockfish::Eval::NNUE::Layers {
57
+
58
+ // Fallback implementation for older/other architectures.
59
+ // Identical for both approaches. Requires the input to be padded to at least 16 values.
60
+ #if !defined(USE_SSSE3)
61
+ template <IndexType InputDimensions, IndexType PaddedInputDimensions, IndexType OutputDimensions>
62
+ static void affine_transform_non_ssse3(std::int32_t* output, const std::int8_t* weights, const std::int32_t* biases, const std::uint8_t* input)
63
+ {
64
+ # if defined(USE_SSE2)
65
+ // At least a multiple of 16, with SSE2.
66
+ constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 16) / 16;
67
+ const __m128i Zeros = _mm_setzero_si128();
68
+ const auto inputVector = reinterpret_cast<const __m128i*>(input);
69
+
70
+ # elif defined(USE_MMX)
71
+ constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / 8;
72
+ const __m64 Zeros = _mm_setzero_si64();
73
+ const auto inputVector = reinterpret_cast<const __m64*>(input);
74
+
75
+ # elif defined(USE_NEON)
76
+ constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 16) / 16;
77
+ const auto inputVector = reinterpret_cast<const int8x8_t*>(input);
78
+ # endif
79
+
80
+ for (IndexType i = 0; i < OutputDimensions; ++i) {
81
+ const IndexType offset = i * PaddedInputDimensions;
82
+
83
+ # if defined(USE_SSE2)
84
+ __m128i sumLo = _mm_cvtsi32_si128(biases[i]);
85
+ __m128i sumHi = Zeros;
86
+ const auto row = reinterpret_cast<const __m128i*>(&weights[offset]);
87
+ for (IndexType j = 0; j < NumChunks; ++j) {
88
+ __m128i row_j = _mm_load_si128(&row[j]);
89
+ __m128i input_j = _mm_load_si128(&inputVector[j]);
90
+ __m128i extendedRowLo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8);
91
+ __m128i extendedRowHi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8);
92
+ __m128i extendedInputLo = _mm_unpacklo_epi8(input_j, Zeros);
93
+ __m128i extendedInputHi = _mm_unpackhi_epi8(input_j, Zeros);
94
+ __m128i productLo = _mm_madd_epi16(extendedRowLo, extendedInputLo);
95
+ __m128i productHi = _mm_madd_epi16(extendedRowHi, extendedInputHi);
96
+ sumLo = _mm_add_epi32(sumLo, productLo);
97
+ sumHi = _mm_add_epi32(sumHi, productHi);
98
+ }
99
+ __m128i sum = _mm_add_epi32(sumLo, sumHi);
100
+ __m128i sumHigh_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2));
101
+ sum = _mm_add_epi32(sum, sumHigh_64);
102
+ __m128i sum_second_32 = _mm_shufflelo_epi16(sum, _MM_SHUFFLE(1, 0, 3, 2));
103
+ sum = _mm_add_epi32(sum, sum_second_32);
104
+ output[i] = _mm_cvtsi128_si32(sum);
105
+
106
+ # elif defined(USE_MMX)
107
+ __m64 sumLo = _mm_cvtsi32_si64(biases[i]);
108
+ __m64 sumHi = Zeros;
109
+ const auto row = reinterpret_cast<const __m64*>(&weights[offset]);
110
+ for (IndexType j = 0; j < NumChunks; ++j) {
111
+ __m64 row_j = row[j];
112
+ __m64 input_j = inputVector[j];
113
+ __m64 extendedRowLo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8);
114
+ __m64 extendedRowHi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8);
115
+ __m64 extendedInputLo = _mm_unpacklo_pi8(input_j, Zeros);
116
+ __m64 extendedInputHi = _mm_unpackhi_pi8(input_j, Zeros);
117
+ __m64 productLo = _mm_madd_pi16(extendedRowLo, extendedInputLo);
118
+ __m64 productHi = _mm_madd_pi16(extendedRowHi, extendedInputHi);
119
+ sumLo = _mm_add_pi32(sumLo, productLo);
120
+ sumHi = _mm_add_pi32(sumHi, productHi);
121
+ }
122
+ __m64 sum = _mm_add_pi32(sumLo, sumHi);
123
+ sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum));
124
+ output[i] = _mm_cvtsi64_si32(sum);
125
+
126
+ # elif defined(USE_NEON)
127
+ int32x4_t sum = {biases[i]};
128
+ const auto row = reinterpret_cast<const int8x8_t*>(&weights[offset]);
129
+ for (IndexType j = 0; j < NumChunks; ++j) {
130
+ int16x8_t product = vmull_s8(inputVector[j * 2], row[j * 2]);
131
+ product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]);
132
+ sum = vpadalq_s16(sum, product);
133
+ }
134
+ output[i] = sum[0] + sum[1] + sum[2] + sum[3];
135
+
136
+ # else
137
+ std::int32_t sum = biases[i];
138
+ for (IndexType j = 0; j < InputDimensions; ++j) {
139
+ sum += weights[offset + j] * input[j];
140
+ }
141
+ output[i] = sum;
142
+ # endif
143
+ }
144
+
145
+ # if defined(USE_MMX)
146
+ _mm_empty();
147
+ # endif
148
+ }
149
+ #endif
150
+
151
+ template <IndexType InDims, IndexType OutDims, typename Enabled = void>
152
+ class AffineTransform;
153
+
154
+ #if defined (USE_AVX512)
155
+ constexpr IndexType LargeInputSize = 2 * 64;
156
+ #else
157
+ constexpr IndexType LargeInputSize = std::numeric_limits<IndexType>::max();
158
+ #endif
159
+
160
+ // A specialization for large inputs.
161
+ template <IndexType InDims, IndexType OutDims>
162
+ class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) >= LargeInputSize)>> {
163
+ public:
164
+ // Input/output type
165
+ using InputType = std::uint8_t;
166
+ using OutputType = std::int32_t;
167
+
168
+ // Number of input/output dimensions
169
+ static constexpr IndexType InputDimensions = InDims;
170
+ static constexpr IndexType OutputDimensions = OutDims;
171
+
172
+ static constexpr IndexType PaddedInputDimensions =
173
+ ceil_to_multiple<IndexType>(InputDimensions, MaxSimdWidth);
174
+ static constexpr IndexType PaddedOutputDimensions =
175
+ ceil_to_multiple<IndexType>(OutputDimensions, MaxSimdWidth);
176
+
177
+ using OutputBuffer = OutputType[PaddedOutputDimensions];
178
+
179
+ static_assert(PaddedInputDimensions >= LargeInputSize, "Something went wrong. This specialization should not have been chosen.");
180
+
181
+ #if defined (USE_AVX512)
182
+ static constexpr const IndexType InputSimdWidth = 64;
183
+ static constexpr const IndexType MaxNumOutputRegs = 16;
184
+ #elif defined (USE_AVX2)
185
+ static constexpr const IndexType InputSimdWidth = 32;
186
+ static constexpr const IndexType MaxNumOutputRegs = 8;
187
+ #elif defined (USE_SSSE3)
188
+ static constexpr const IndexType InputSimdWidth = 16;
189
+ static constexpr const IndexType MaxNumOutputRegs = 8;
190
+ #elif defined (USE_NEON)
191
+ static constexpr const IndexType InputSimdWidth = 8;
192
+ static constexpr const IndexType MaxNumOutputRegs = 8;
193
+ #else
194
+ // The fallback implementation will not have permuted weights.
195
+ // We define these to avoid a lot of ifdefs later.
196
+ static constexpr const IndexType InputSimdWidth = 1;
197
+ static constexpr const IndexType MaxNumOutputRegs = 1;
198
+ #endif
199
+
200
+ // A big block is a region in the weight matrix of the size [PaddedInputDimensions, NumOutputRegs].
201
+ // A small block is a region of size [InputSimdWidth, 1]
202
+
203
+ static constexpr const IndexType NumOutputRegs = std::min(MaxNumOutputRegs, OutputDimensions);
204
+ static constexpr const IndexType SmallBlockSize = InputSimdWidth;
205
+ static constexpr const IndexType BigBlockSize = NumOutputRegs * PaddedInputDimensions;
206
+ static constexpr const IndexType NumSmallBlocksInBigBlock = BigBlockSize / SmallBlockSize;
207
+ static constexpr const IndexType NumSmallBlocksPerOutput = PaddedInputDimensions / SmallBlockSize;
208
+ static constexpr const IndexType NumBigBlocks = OutputDimensions / NumOutputRegs;
209
+
210
+ static_assert(OutputDimensions % NumOutputRegs == 0);
211
+
212
+ // Hash value embedded in the evaluation file
213
+ static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
214
+ std::uint32_t hashValue = 0xCC03DAE4u;
215
+ hashValue += OutputDimensions;
216
+ hashValue ^= prevHash >> 1;
217
+ hashValue ^= prevHash << 31;
218
+ return hashValue;
219
+ }
220
+
221
+ /*
222
+ Transposes the small blocks within a block.
223
+ Effectively means that weights can be traversed sequentially during inference.
224
+ */
225
+ static IndexType get_weight_index(IndexType i)
226
+ {
227
+ const IndexType smallBlock = (i / SmallBlockSize) % NumSmallBlocksInBigBlock;
228
+ const IndexType smallBlockCol = smallBlock / NumSmallBlocksPerOutput;
229
+ const IndexType smallBlockRow = smallBlock % NumSmallBlocksPerOutput;
230
+ const IndexType bigBlock = i / BigBlockSize;
231
+ const IndexType rest = i % SmallBlockSize;
232
+
233
+ const IndexType idx =
234
+ bigBlock * BigBlockSize
235
+ + smallBlockRow * SmallBlockSize * NumOutputRegs
236
+ + smallBlockCol * SmallBlockSize
237
+ + rest;
238
+
239
+ return idx;
240
+ }
241
+
242
+ // Read network parameters
243
+ bool read_parameters(std::istream& stream) {
244
+ for (IndexType i = 0; i < OutputDimensions; ++i)
245
+ biases[i] = read_little_endian<BiasType>(stream);
246
+
247
+ for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
248
+ weights[get_weight_index(i)] = read_little_endian<WeightType>(stream);
249
+
250
+ return !stream.fail();
251
+ }
252
+
253
+ // Write network parameters
254
+ bool write_parameters(std::ostream& stream) const {
255
+ for (IndexType i = 0; i < OutputDimensions; ++i)
256
+ write_little_endian<BiasType>(stream, biases[i]);
257
+
258
+ for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
259
+ write_little_endian<WeightType>(stream, weights[get_weight_index(i)]);
260
+
261
+ return !stream.fail();
262
+ }
263
+
264
+ // Forward propagation
265
+ const OutputType* propagate(
266
+ const InputType* input, OutputType* output) const {
267
+
268
+ #if defined (USE_AVX512)
269
+ using acc_vec_t = __m512i;
270
+ using bias_vec_t = __m128i;
271
+ using weight_vec_t = __m512i;
272
+ using in_vec_t = __m512i;
273
+ #define vec_zero _mm512_setzero_si512()
274
+ #define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2
275
+ #define vec_hadd Simd::m512_hadd
276
+ #define vec_haddx4 Simd::m512_haddx4
277
+ #elif defined (USE_AVX2)
278
+ using acc_vec_t = __m256i;
279
+ using bias_vec_t = __m128i;
280
+ using weight_vec_t = __m256i;
281
+ using in_vec_t = __m256i;
282
+ #define vec_zero _mm256_setzero_si256()
283
+ #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2
284
+ #define vec_hadd Simd::m256_hadd
285
+ #define vec_haddx4 Simd::m256_haddx4
286
+ #elif defined (USE_SSSE3)
287
+ using acc_vec_t = __m128i;
288
+ using bias_vec_t = __m128i;
289
+ using weight_vec_t = __m128i;
290
+ using in_vec_t = __m128i;
291
+ #define vec_zero _mm_setzero_si128()
292
+ #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2
293
+ #define vec_hadd Simd::m128_hadd
294
+ #define vec_haddx4 Simd::m128_haddx4
295
+ #elif defined (USE_NEON)
296
+ using acc_vec_t = int32x4_t;
297
+ using bias_vec_t = int32x4_t;
298
+ using weight_vec_t = int8x8_t;
299
+ using in_vec_t = int8x8_t;
300
+ #define vec_zero {0}
301
+ #define vec_add_dpbusd_32x2 Simd::neon_m128_add_dpbusd_epi32x2
302
+ #define vec_hadd Simd::neon_m128_hadd
303
+ #define vec_haddx4 Simd::neon_m128_haddx4
304
+ #endif
305
+
306
+ #if defined (USE_SSSE3) || defined (USE_NEON)
307
+ const in_vec_t* invec = reinterpret_cast<const in_vec_t*>(input);
308
+
309
+ // Perform accumulation to registers for each big block
310
+ for (IndexType bigBlock = 0; bigBlock < NumBigBlocks; ++bigBlock)
311
+ {
312
+ acc_vec_t acc[NumOutputRegs] = { vec_zero };
313
+
314
+ // Each big block has NumOutputRegs small blocks in each "row", one per register.
315
+ // We process two small blocks at a time to save on one addition without VNNI.
316
+ for (IndexType smallBlock = 0; smallBlock < NumSmallBlocksPerOutput; smallBlock += 2)
317
+ {
318
+ const weight_vec_t* weightvec =
319
+ reinterpret_cast<const weight_vec_t*>(
320
+ weights
321
+ + bigBlock * BigBlockSize
322
+ + smallBlock * SmallBlockSize * NumOutputRegs);
323
+
324
+ const in_vec_t in0 = invec[smallBlock + 0];
325
+ const in_vec_t in1 = invec[smallBlock + 1];
326
+
327
+ for (IndexType k = 0; k < NumOutputRegs; ++k)
328
+ vec_add_dpbusd_32x2(acc[k], in0, weightvec[k], in1, weightvec[k + NumOutputRegs]);
329
+ }
330
+
331
+ // Horizontally add all accumulators.
332
+ if constexpr (NumOutputRegs % 4 == 0)
333
+ {
334
+ bias_vec_t* outputvec = reinterpret_cast<bias_vec_t*>(output);
335
+ const bias_vec_t* biasvec = reinterpret_cast<const bias_vec_t*>(biases);
336
+
337
+ for (IndexType k = 0; k < NumOutputRegs; k += 4)
338
+ {
339
+ const IndexType idx = (bigBlock * NumOutputRegs + k) / 4;
340
+ outputvec[idx] = vec_haddx4(acc[k+0], acc[k+1], acc[k+2], acc[k+3], biasvec[idx]);
341
+ }
342
+ }
343
+ else
344
+ {
345
+ for (IndexType k = 0; k < NumOutputRegs; ++k)
346
+ {
347
+ const IndexType idx = (bigBlock * NumOutputRegs + k);
348
+ output[idx] = vec_hadd(acc[k], biases[idx]);
349
+ }
350
+ }
351
+ }
352
+
353
+ # undef vec_zero
354
+ # undef vec_add_dpbusd_32x2
355
+ # undef vec_hadd
356
+ # undef vec_haddx4
357
+ #else
358
+ // Use old implementation for the other architectures.
359
+ affine_transform_non_ssse3<
360
+ InputDimensions,
361
+ PaddedInputDimensions,
362
+ OutputDimensions>(output, weights, biases, input);
363
+
364
+ #endif
365
+
366
+ return output;
367
+ }
368
+
369
+ private:
370
+ using BiasType = OutputType;
371
+ using WeightType = std::int8_t;
372
+
373
+ alignas(CacheLineSize) BiasType biases[OutputDimensions];
374
+ alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions];
375
+ };
376
+
377
+ template <IndexType InDims, IndexType OutDims>
378
+ class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) < LargeInputSize)>> {
379
+ public:
380
+ // Input/output type
381
+ // Input/output type
382
+ using InputType = std::uint8_t;
383
+ using OutputType = std::int32_t;
384
+
385
+ // Number of input/output dimensions
386
+ static constexpr IndexType InputDimensions = InDims;
387
+ static constexpr IndexType OutputDimensions = OutDims;
388
+
389
+ static constexpr IndexType PaddedInputDimensions =
390
+ ceil_to_multiple<IndexType>(InputDimensions, MaxSimdWidth);
391
+ static constexpr IndexType PaddedOutputDimensions =
392
+ ceil_to_multiple<IndexType>(OutputDimensions, MaxSimdWidth);
393
+
394
+ using OutputBuffer = OutputType[PaddedOutputDimensions];
395
+
396
+ static_assert(PaddedInputDimensions < LargeInputSize, "Something went wrong. This specialization should not have been chosen.");
397
+
398
+ #if defined (USE_SSSE3)
399
+ static constexpr const IndexType OutputSimdWidth = SimdWidth / 4;
400
+ static constexpr const IndexType InputSimdWidth = SimdWidth;
401
+ #endif
402
+
403
+ // Hash value embedded in the evaluation file
404
+ static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
405
+ std::uint32_t hashValue = 0xCC03DAE4u;
406
+ hashValue += OutputDimensions;
407
+ hashValue ^= prevHash >> 1;
408
+ hashValue ^= prevHash << 31;
409
+ return hashValue;
410
+ }
411
+
412
+ static IndexType get_weight_index_scrambled(IndexType i)
413
+ {
414
+ return
415
+ (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 +
416
+ i / PaddedInputDimensions * 4 +
417
+ i % 4;
418
+ }
419
+
420
+ static IndexType get_weight_index(IndexType i)
421
+ {
422
+ #if defined (USE_SSSE3)
423
+ return get_weight_index_scrambled(i);
424
+ #else
425
+ return i;
426
+ #endif
427
+ }
428
+
429
+ // Read network parameters
430
+ bool read_parameters(std::istream& stream) {
431
+ for (IndexType i = 0; i < OutputDimensions; ++i)
432
+ biases[i] = read_little_endian<BiasType>(stream);
433
+ for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
434
+ weights[get_weight_index(i)] = read_little_endian<WeightType>(stream);
435
+
436
+ return !stream.fail();
437
+ }
438
+
439
+ // Write network parameters
440
+ bool write_parameters(std::ostream& stream) const {
441
+ for (IndexType i = 0; i < OutputDimensions; ++i)
442
+ write_little_endian<BiasType>(stream, biases[i]);
443
+
444
+ for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
445
+ write_little_endian<WeightType>(stream, weights[get_weight_index(i)]);
446
+
447
+ return !stream.fail();
448
+ }
449
+ // Forward propagation
450
+ const OutputType* propagate(
451
+ const InputType* input, OutputType* output) const {
452
+
453
+ #if defined (USE_AVX2)
454
+ using vec_t = __m256i;
455
+ #define vec_setzero _mm256_setzero_si256
456
+ #define vec_set_32 _mm256_set1_epi32
457
+ #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32
458
+ #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2
459
+ #define vec_add_dpbusd_32x4 Simd::m256_add_dpbusd_epi32x4
460
+ #define vec_hadd Simd::m256_hadd
461
+ #define vec_haddx4 Simd::m256_haddx4
462
+ #elif defined (USE_SSSE3)
463
+ using vec_t = __m128i;
464
+ #define vec_setzero _mm_setzero_si128
465
+ #define vec_set_32 _mm_set1_epi32
466
+ #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32
467
+ #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2
468
+ #define vec_add_dpbusd_32x4 Simd::m128_add_dpbusd_epi32x4
469
+ #define vec_hadd Simd::m128_hadd
470
+ #define vec_haddx4 Simd::m128_haddx4
471
+ #endif
472
+
473
+ #if defined (USE_SSSE3)
474
+ const auto inputVector = reinterpret_cast<const vec_t*>(input);
475
+
476
+ static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1);
477
+
478
+ if constexpr (OutputDimensions % OutputSimdWidth == 0)
479
+ {
480
+ constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / 4;
481
+ constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth;
482
+
483
+ const auto input32 = reinterpret_cast<const std::int32_t*>(input);
484
+ const vec_t* biasvec = reinterpret_cast<const vec_t*>(biases);
485
+ vec_t acc[NumRegs];
486
+ for (IndexType k = 0; k < NumRegs; ++k)
487
+ acc[k] = biasvec[k];
488
+
489
+ for (IndexType i = 0; i < NumChunks; i += 2)
490
+ {
491
+ const vec_t in0 = vec_set_32(input32[i + 0]);
492
+ const vec_t in1 = vec_set_32(input32[i + 1]);
493
+ const auto col0 = reinterpret_cast<const vec_t*>(&weights[(i + 0) * OutputDimensions * 4]);
494
+ const auto col1 = reinterpret_cast<const vec_t*>(&weights[(i + 1) * OutputDimensions * 4]);
495
+ for (IndexType k = 0; k < NumRegs; ++k)
496
+ vec_add_dpbusd_32x2(acc[k], in0, col0[k], in1, col1[k]);
497
+ }
498
+
499
+ vec_t* outptr = reinterpret_cast<vec_t*>(output);
500
+ for (IndexType k = 0; k < NumRegs; ++k)
501
+ outptr[k] = acc[k];
502
+ }
503
+ else if constexpr (OutputDimensions == 1)
504
+ {
505
+ constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth;
506
+ vec_t sum0 = vec_setzero();
507
+ const auto row0 = reinterpret_cast<const vec_t*>(&weights[0]);
508
+
509
+ for (int j = 0; j < (int)NumChunks; ++j)
510
+ {
511
+ const vec_t in = inputVector[j];
512
+ vec_add_dpbusd_32(sum0, in, row0[j]);
513
+ }
514
+ output[0] = vec_hadd(sum0, biases[0]);
515
+ }
516
+
517
+ # undef vec_setzero
518
+ # undef vec_set_32
519
+ # undef vec_add_dpbusd_32
520
+ # undef vec_add_dpbusd_32x2
521
+ # undef vec_add_dpbusd_32x4
522
+ # undef vec_hadd
523
+ # undef vec_haddx4
524
+ #else
525
+ // Use old implementation for the other architectures.
526
+ affine_transform_non_ssse3<
527
+ InputDimensions,
528
+ PaddedInputDimensions,
529
+ OutputDimensions>(output, weights, biases, input);
530
+ #endif
531
+
532
+ return output;
533
+ }
534
+
535
+ private:
536
+ using BiasType = OutputType;
537
+ using WeightType = std::int8_t;
538
+
539
+ alignas(CacheLineSize) BiasType biases[OutputDimensions];
540
+ alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions];
541
+ };
542
+
543
+ } // namespace Stockfish::Eval::NNUE::Layers
544
+
545
+ #endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
src/nnue/layers/clipped_relu.h ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ // Definition of layer ClippedReLU of NNUE evaluation function
20
+
21
+ #ifndef NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED
22
+ #define NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED
23
+
24
+ #include "../nnue_common.h"
25
+
26
+ namespace Stockfish::Eval::NNUE::Layers {
27
+
28
+ // Clipped ReLU
29
+ template <IndexType InDims>
30
+ class ClippedReLU {
31
+ public:
32
+ // Input/output type
33
+ using InputType = std::int32_t;
34
+ using OutputType = std::uint8_t;
35
+
36
+ // Number of input/output dimensions
37
+ static constexpr IndexType InputDimensions = InDims;
38
+ static constexpr IndexType OutputDimensions = InputDimensions;
39
+ static constexpr IndexType PaddedOutputDimensions =
40
+ ceil_to_multiple<IndexType>(OutputDimensions, 32);
41
+
42
+ using OutputBuffer = OutputType[PaddedOutputDimensions];
43
+
44
+ // Hash value embedded in the evaluation file
45
+ static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
46
+ std::uint32_t hashValue = 0x538D24C7u;
47
+ hashValue += prevHash;
48
+ return hashValue;
49
+ }
50
+
51
+ // Read network parameters
52
+ bool read_parameters(std::istream&) {
53
+ return true;
54
+ }
55
+
56
+ // Write network parameters
57
+ bool write_parameters(std::ostream&) const {
58
+ return true;
59
+ }
60
+
61
+ // Forward propagation
62
+ const OutputType* propagate(
63
+ const InputType* input, OutputType* output) const {
64
+
65
+ #if defined(USE_AVX2)
66
+ if constexpr (InputDimensions % SimdWidth == 0) {
67
+ constexpr IndexType NumChunks = InputDimensions / SimdWidth;
68
+ const __m256i Zero = _mm256_setzero_si256();
69
+ const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0);
70
+ const auto in = reinterpret_cast<const __m256i*>(input);
71
+ const auto out = reinterpret_cast<__m256i*>(output);
72
+ for (IndexType i = 0; i < NumChunks; ++i) {
73
+ const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32(
74
+ _mm256_load_si256(&in[i * 4 + 0]),
75
+ _mm256_load_si256(&in[i * 4 + 1])), WeightScaleBits);
76
+ const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32(
77
+ _mm256_load_si256(&in[i * 4 + 2]),
78
+ _mm256_load_si256(&in[i * 4 + 3])), WeightScaleBits);
79
+ _mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8(
80
+ _mm256_packs_epi16(words0, words1), Zero), Offsets));
81
+ }
82
+ } else {
83
+ constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2);
84
+ const __m128i Zero = _mm_setzero_si128();
85
+ const auto in = reinterpret_cast<const __m128i*>(input);
86
+ const auto out = reinterpret_cast<__m128i*>(output);
87
+ for (IndexType i = 0; i < NumChunks; ++i) {
88
+ const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32(
89
+ _mm_load_si128(&in[i * 4 + 0]),
90
+ _mm_load_si128(&in[i * 4 + 1])), WeightScaleBits);
91
+ const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32(
92
+ _mm_load_si128(&in[i * 4 + 2]),
93
+ _mm_load_si128(&in[i * 4 + 3])), WeightScaleBits);
94
+ const __m128i packedbytes = _mm_packs_epi16(words0, words1);
95
+ _mm_store_si128(&out[i], _mm_max_epi8(packedbytes, Zero));
96
+ }
97
+ }
98
+ constexpr IndexType Start =
99
+ InputDimensions % SimdWidth == 0
100
+ ? InputDimensions / SimdWidth * SimdWidth
101
+ : InputDimensions / (SimdWidth / 2) * (SimdWidth / 2);
102
+
103
+ #elif defined(USE_SSE2)
104
+ constexpr IndexType NumChunks = InputDimensions / SimdWidth;
105
+
106
+ #ifdef USE_SSE41
107
+ const __m128i Zero = _mm_setzero_si128();
108
+ #else
109
+ const __m128i k0x80s = _mm_set1_epi8(-128);
110
+ #endif
111
+
112
+ const auto in = reinterpret_cast<const __m128i*>(input);
113
+ const auto out = reinterpret_cast<__m128i*>(output);
114
+ for (IndexType i = 0; i < NumChunks; ++i) {
115
+ const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32(
116
+ _mm_load_si128(&in[i * 4 + 0]),
117
+ _mm_load_si128(&in[i * 4 + 1])), WeightScaleBits);
118
+ const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32(
119
+ _mm_load_si128(&in[i * 4 + 2]),
120
+ _mm_load_si128(&in[i * 4 + 3])), WeightScaleBits);
121
+ const __m128i packedbytes = _mm_packs_epi16(words0, words1);
122
+ _mm_store_si128(&out[i],
123
+
124
+ #ifdef USE_SSE41
125
+ _mm_max_epi8(packedbytes, Zero)
126
+ #else
127
+ _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)
128
+ #endif
129
+
130
+ );
131
+ }
132
+ constexpr IndexType Start = NumChunks * SimdWidth;
133
+
134
+ #elif defined(USE_MMX)
135
+ constexpr IndexType NumChunks = InputDimensions / SimdWidth;
136
+ const __m64 k0x80s = _mm_set1_pi8(-128);
137
+ const auto in = reinterpret_cast<const __m64*>(input);
138
+ const auto out = reinterpret_cast<__m64*>(output);
139
+ for (IndexType i = 0; i < NumChunks; ++i) {
140
+ const __m64 words0 = _mm_srai_pi16(
141
+ _mm_packs_pi32(in[i * 4 + 0], in[i * 4 + 1]),
142
+ WeightScaleBits);
143
+ const __m64 words1 = _mm_srai_pi16(
144
+ _mm_packs_pi32(in[i * 4 + 2], in[i * 4 + 3]),
145
+ WeightScaleBits);
146
+ const __m64 packedbytes = _mm_packs_pi16(words0, words1);
147
+ out[i] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s);
148
+ }
149
+ _mm_empty();
150
+ constexpr IndexType Start = NumChunks * SimdWidth;
151
+
152
+ #elif defined(USE_NEON)
153
+ constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2);
154
+ const int8x8_t Zero = {0};
155
+ const auto in = reinterpret_cast<const int32x4_t*>(input);
156
+ const auto out = reinterpret_cast<int8x8_t*>(output);
157
+ for (IndexType i = 0; i < NumChunks; ++i) {
158
+ int16x8_t shifted;
159
+ const auto pack = reinterpret_cast<int16x4_t*>(&shifted);
160
+ pack[0] = vqshrn_n_s32(in[i * 2 + 0], WeightScaleBits);
161
+ pack[1] = vqshrn_n_s32(in[i * 2 + 1], WeightScaleBits);
162
+ out[i] = vmax_s8(vqmovn_s16(shifted), Zero);
163
+ }
164
+ constexpr IndexType Start = NumChunks * (SimdWidth / 2);
165
+ #else
166
+ constexpr IndexType Start = 0;
167
+ #endif
168
+
169
+ for (IndexType i = Start; i < InputDimensions; ++i) {
170
+ output[i] = static_cast<OutputType>(
171
+ std::max(0, std::min(127, input[i] >> WeightScaleBits)));
172
+ }
173
+
174
+ return output;
175
+ }
176
+ };
177
+
178
+ } // namespace Stockfish::Eval::NNUE::Layers
179
+
180
+ #endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED
src/nnue/layers/simd.h ADDED
@@ -0,0 +1,387 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef STOCKFISH_SIMD_H_INCLUDED
20
+ #define STOCKFISH_SIMD_H_INCLUDED
21
+
22
+ #if defined(USE_AVX2)
23
+ # include <immintrin.h>
24
+
25
+ #elif defined(USE_SSE41)
26
+ # include <smmintrin.h>
27
+
28
+ #elif defined(USE_SSSE3)
29
+ # include <tmmintrin.h>
30
+
31
+ #elif defined(USE_SSE2)
32
+ # include <emmintrin.h>
33
+
34
+ #elif defined(USE_MMX)
35
+ # include <mmintrin.h>
36
+
37
+ #elif defined(USE_NEON)
38
+ # include <arm_neon.h>
39
+ #endif
40
+
41
+ // The inline asm is only safe for GCC, where it is necessary to get good codegen.
42
+ // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101693
43
+ // Clang does fine without it.
44
+ // Play around here: https://godbolt.org/z/7EWqrYq51
45
+ #if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER))
46
+ #define USE_INLINE_ASM
47
+ #endif
48
+
49
+ // Use either the AVX512 or AVX-VNNI version of the VNNI instructions.
50
+ #if defined(USE_AVXVNNI)
51
+ #define VNNI_PREFIX "%{vex%} "
52
+ #else
53
+ #define VNNI_PREFIX ""
54
+ #endif
55
+
56
+ namespace Stockfish::Simd {
57
+
58
+ #if defined (USE_AVX512)
59
+
60
+ [[maybe_unused]] static int m512_hadd(__m512i sum, int bias) {
61
+ return _mm512_reduce_add_epi32(sum) + bias;
62
+ }
63
+
64
+ /*
65
+ Parameters:
66
+ sum0 = [zmm0.i128[0], zmm0.i128[1], zmm0.i128[2], zmm0.i128[3]]
67
+ sum1 = [zmm1.i128[0], zmm1.i128[1], zmm1.i128[2], zmm1.i128[3]]
68
+ sum2 = [zmm2.i128[0], zmm2.i128[1], zmm2.i128[2], zmm2.i128[3]]
69
+ sum3 = [zmm3.i128[0], zmm3.i128[1], zmm3.i128[2], zmm3.i128[3]]
70
+
71
+ Returns:
72
+ ret = [
73
+ reduce_add_epi32(zmm0.i128[0]), reduce_add_epi32(zmm1.i128[0]), reduce_add_epi32(zmm2.i128[0]), reduce_add_epi32(zmm3.i128[0]),
74
+ reduce_add_epi32(zmm0.i128[1]), reduce_add_epi32(zmm1.i128[1]), reduce_add_epi32(zmm2.i128[1]), reduce_add_epi32(zmm3.i128[1]),
75
+ reduce_add_epi32(zmm0.i128[2]), reduce_add_epi32(zmm1.i128[2]), reduce_add_epi32(zmm2.i128[2]), reduce_add_epi32(zmm3.i128[2]),
76
+ reduce_add_epi32(zmm0.i128[3]), reduce_add_epi32(zmm1.i128[3]), reduce_add_epi32(zmm2.i128[3]), reduce_add_epi32(zmm3.i128[3])
77
+ ]
78
+ */
79
+ [[maybe_unused]] static __m512i m512_hadd128x16_interleave(
80
+ __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) {
81
+
82
+ __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1);
83
+ __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1);
84
+
85
+ __m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3);
86
+ __m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3);
87
+
88
+ __m512i sum01 = _mm512_add_epi32(sum01a, sum01b);
89
+ __m512i sum23 = _mm512_add_epi32(sum23a, sum23b);
90
+
91
+ __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23);
92
+ __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23);
93
+
94
+ return _mm512_add_epi32(sum0123a, sum0123b);
95
+ }
96
+
97
+ [[maybe_unused]] static __m128i m512_haddx4(
98
+ __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3,
99
+ __m128i bias) {
100
+
101
+ __m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3);
102
+
103
+ __m256i sum256lo = _mm512_castsi512_si256(sum);
104
+ __m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1);
105
+
106
+ sum256lo = _mm256_add_epi32(sum256lo, sum256hi);
107
+
108
+ __m128i sum128lo = _mm256_castsi256_si128(sum256lo);
109
+ __m128i sum128hi = _mm256_extracti128_si256(sum256lo, 1);
110
+
111
+ return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias);
112
+ }
113
+
114
+ [[maybe_unused]] static void m512_add_dpbusd_epi32(
115
+ __m512i& acc,
116
+ __m512i a,
117
+ __m512i b) {
118
+
119
+ # if defined (USE_VNNI)
120
+ # if defined (USE_INLINE_ASM)
121
+ asm(
122
+ "vpdpbusd %[b], %[a], %[acc]\n\t"
123
+ : [acc]"+v"(acc)
124
+ : [a]"v"(a), [b]"vm"(b)
125
+ );
126
+ # else
127
+ acc = _mm512_dpbusd_epi32(acc, a, b);
128
+ # endif
129
+ # else
130
+ # if defined (USE_INLINE_ASM)
131
+ __m512i tmp = _mm512_maddubs_epi16(a, b);
132
+ asm(
133
+ "vpmaddwd %[tmp], %[ones], %[tmp]\n\t"
134
+ "vpaddd %[acc], %[tmp], %[acc]\n\t"
135
+ : [acc]"+v"(acc), [tmp]"+&v"(tmp)
136
+ : [ones]"v"(_mm512_set1_epi16(1))
137
+ );
138
+ # else
139
+ __m512i product0 = _mm512_maddubs_epi16(a, b);
140
+ product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1));
141
+ acc = _mm512_add_epi32(acc, product0);
142
+ # endif
143
+ # endif
144
+ }
145
+
146
+ [[maybe_unused]] static void m512_add_dpbusd_epi32x2(
147
+ __m512i& acc,
148
+ __m512i a0, __m512i b0,
149
+ __m512i a1, __m512i b1) {
150
+
151
+ # if defined (USE_VNNI)
152
+ # if defined (USE_INLINE_ASM)
153
+ asm(
154
+ "vpdpbusd %[b0], %[a0], %[acc]\n\t"
155
+ "vpdpbusd %[b1], %[a1], %[acc]\n\t"
156
+ : [acc]"+v"(acc)
157
+ : [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1)
158
+ );
159
+ # else
160
+ acc = _mm512_dpbusd_epi32(acc, a0, b0);
161
+ acc = _mm512_dpbusd_epi32(acc, a1, b1);
162
+ # endif
163
+ # else
164
+ # if defined (USE_INLINE_ASM)
165
+ __m512i tmp0 = _mm512_maddubs_epi16(a0, b0);
166
+ __m512i tmp1 = _mm512_maddubs_epi16(a1, b1);
167
+ asm(
168
+ "vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t"
169
+ "vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t"
170
+ "vpaddd %[acc], %[tmp0], %[acc]\n\t"
171
+ : [acc]"+v"(acc), [tmp0]"+&v"(tmp0)
172
+ : [tmp1]"v"(tmp1), [ones]"v"(_mm512_set1_epi16(1))
173
+ );
174
+ # else
175
+ __m512i product0 = _mm512_maddubs_epi16(a0, b0);
176
+ __m512i product1 = _mm512_maddubs_epi16(a1, b1);
177
+ product0 = _mm512_adds_epi16(product0, product1);
178
+ product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1));
179
+ acc = _mm512_add_epi32(acc, product0);
180
+ # endif
181
+ # endif
182
+ }
183
+
184
+ #endif
185
+
186
+ #if defined (USE_AVX2)
187
+
188
+ [[maybe_unused]] static int m256_hadd(__m256i sum, int bias) {
189
+ __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1));
190
+ sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC));
191
+ sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB));
192
+ return _mm_cvtsi128_si32(sum128) + bias;
193
+ }
194
+
195
+ [[maybe_unused]] static __m128i m256_haddx4(
196
+ __m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3,
197
+ __m128i bias) {
198
+
199
+ sum0 = _mm256_hadd_epi32(sum0, sum1);
200
+ sum2 = _mm256_hadd_epi32(sum2, sum3);
201
+
202
+ sum0 = _mm256_hadd_epi32(sum0, sum2);
203
+
204
+ __m128i sum128lo = _mm256_castsi256_si128(sum0);
205
+ __m128i sum128hi = _mm256_extracti128_si256(sum0, 1);
206
+
207
+ return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias);
208
+ }
209
+
210
+ [[maybe_unused]] static void m256_add_dpbusd_epi32(
211
+ __m256i& acc,
212
+ __m256i a,
213
+ __m256i b) {
214
+
215
+ # if defined (USE_VNNI)
216
+ # if defined (USE_INLINE_ASM)
217
+ asm(
218
+ VNNI_PREFIX "vpdpbusd %[b], %[a], %[acc]\n\t"
219
+ : [acc]"+v"(acc)
220
+ : [a]"v"(a), [b]"vm"(b)
221
+ );
222
+ # else
223
+ acc = _mm256_dpbusd_epi32(acc, a, b);
224
+ # endif
225
+ # else
226
+ # if defined (USE_INLINE_ASM)
227
+ __m256i tmp = _mm256_maddubs_epi16(a, b);
228
+ asm(
229
+ "vpmaddwd %[tmp], %[ones], %[tmp]\n\t"
230
+ "vpaddd %[acc], %[tmp], %[acc]\n\t"
231
+ : [acc]"+v"(acc), [tmp]"+&v"(tmp)
232
+ : [ones]"v"(_mm256_set1_epi16(1))
233
+ );
234
+ # else
235
+ __m256i product0 = _mm256_maddubs_epi16(a, b);
236
+ product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1));
237
+ acc = _mm256_add_epi32(acc, product0);
238
+ # endif
239
+ # endif
240
+ }
241
+
242
+ [[maybe_unused]] static void m256_add_dpbusd_epi32x2(
243
+ __m256i& acc,
244
+ __m256i a0, __m256i b0,
245
+ __m256i a1, __m256i b1) {
246
+
247
+ # if defined (USE_VNNI)
248
+ # if defined (USE_INLINE_ASM)
249
+ asm(
250
+ VNNI_PREFIX "vpdpbusd %[b0], %[a0], %[acc]\n\t"
251
+ VNNI_PREFIX "vpdpbusd %[b1], %[a1], %[acc]\n\t"
252
+ : [acc]"+v"(acc)
253
+ : [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1)
254
+ );
255
+ # else
256
+ acc = _mm256_dpbusd_epi32(acc, a0, b0);
257
+ acc = _mm256_dpbusd_epi32(acc, a1, b1);
258
+ # endif
259
+ # else
260
+ # if defined (USE_INLINE_ASM)
261
+ __m256i tmp0 = _mm256_maddubs_epi16(a0, b0);
262
+ __m256i tmp1 = _mm256_maddubs_epi16(a1, b1);
263
+ asm(
264
+ "vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t"
265
+ "vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t"
266
+ "vpaddd %[acc], %[tmp0], %[acc]\n\t"
267
+ : [acc]"+v"(acc), [tmp0]"+&v"(tmp0)
268
+ : [tmp1]"v"(tmp1), [ones]"v"(_mm256_set1_epi16(1))
269
+ );
270
+ # else
271
+ __m256i product0 = _mm256_maddubs_epi16(a0, b0);
272
+ __m256i product1 = _mm256_maddubs_epi16(a1, b1);
273
+ product0 = _mm256_adds_epi16(product0, product1);
274
+ product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1));
275
+ acc = _mm256_add_epi32(acc, product0);
276
+ # endif
277
+ # endif
278
+ }
279
+
280
+ #endif
281
+
282
+ #if defined (USE_SSSE3)
283
+
284
+ [[maybe_unused]] static int m128_hadd(__m128i sum, int bias) {
285
+ sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC
286
+ sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB
287
+ return _mm_cvtsi128_si32(sum) + bias;
288
+ }
289
+
290
+ [[maybe_unused]] static __m128i m128_haddx4(
291
+ __m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3,
292
+ __m128i bias) {
293
+
294
+ sum0 = _mm_hadd_epi32(sum0, sum1);
295
+ sum2 = _mm_hadd_epi32(sum2, sum3);
296
+ sum0 = _mm_hadd_epi32(sum0, sum2);
297
+ return _mm_add_epi32(sum0, bias);
298
+ }
299
+
300
+ [[maybe_unused]] static void m128_add_dpbusd_epi32(
301
+ __m128i& acc,
302
+ __m128i a,
303
+ __m128i b) {
304
+
305
+ # if defined (USE_INLINE_ASM)
306
+ __m128i tmp = _mm_maddubs_epi16(a, b);
307
+ asm(
308
+ "pmaddwd %[ones], %[tmp]\n\t"
309
+ "paddd %[tmp], %[acc]\n\t"
310
+ : [acc]"+v"(acc), [tmp]"+&v"(tmp)
311
+ : [ones]"v"(_mm_set1_epi16(1))
312
+ );
313
+ # else
314
+ __m128i product0 = _mm_maddubs_epi16(a, b);
315
+ product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1));
316
+ acc = _mm_add_epi32(acc, product0);
317
+ # endif
318
+ }
319
+
320
+ [[maybe_unused]] static void m128_add_dpbusd_epi32x2(
321
+ __m128i& acc,
322
+ __m128i a0, __m128i b0,
323
+ __m128i a1, __m128i b1) {
324
+
325
+ # if defined (USE_INLINE_ASM)
326
+ __m128i tmp0 = _mm_maddubs_epi16(a0, b0);
327
+ __m128i tmp1 = _mm_maddubs_epi16(a1, b1);
328
+ asm(
329
+ "paddsw %[tmp1], %[tmp0]\n\t"
330
+ "pmaddwd %[ones], %[tmp0]\n\t"
331
+ "paddd %[tmp0], %[acc]\n\t"
332
+ : [acc]"+v"(acc), [tmp0]"+&v"(tmp0)
333
+ : [tmp1]"v"(tmp1), [ones]"v"(_mm_set1_epi16(1))
334
+ );
335
+ # else
336
+ __m128i product0 = _mm_maddubs_epi16(a0, b0);
337
+ __m128i product1 = _mm_maddubs_epi16(a1, b1);
338
+ product0 = _mm_adds_epi16(product0, product1);
339
+ product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1));
340
+ acc = _mm_add_epi32(acc, product0);
341
+ # endif
342
+ }
343
+
344
+ #endif
345
+
346
+ #if defined (USE_NEON)
347
+
348
+ [[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) {
349
+ # if USE_NEON >= 8
350
+ return vaddvq_s32(s);
351
+ # else
352
+ return s[0] + s[1] + s[2] + s[3];
353
+ # endif
354
+ }
355
+
356
+ [[maybe_unused]] static int neon_m128_hadd(int32x4_t sum, int bias) {
357
+ return neon_m128_reduce_add_epi32(sum) + bias;
358
+ }
359
+
360
+ [[maybe_unused]] static int32x4_t neon_m128_haddx4(
361
+ int32x4_t sum0, int32x4_t sum1, int32x4_t sum2, int32x4_t sum3,
362
+ int32x4_t bias) {
363
+
364
+ int32x4_t hsums {
365
+ neon_m128_reduce_add_epi32(sum0),
366
+ neon_m128_reduce_add_epi32(sum1),
367
+ neon_m128_reduce_add_epi32(sum2),
368
+ neon_m128_reduce_add_epi32(sum3)
369
+ };
370
+ return vaddq_s32(hsums, bias);
371
+ }
372
+
373
+ [[maybe_unused]] static void neon_m128_add_dpbusd_epi32x2(
374
+ int32x4_t& acc,
375
+ int8x8_t a0, int8x8_t b0,
376
+ int8x8_t a1, int8x8_t b1) {
377
+
378
+ int16x8_t product = vmull_s8(a0, b0);
379
+ product = vmlal_s8(product, a1, b1);
380
+ acc = vpadalq_s16(acc, product);
381
+ }
382
+
383
+ #endif
384
+
385
+ }
386
+
387
+ #endif // STOCKFISH_SIMD_H_INCLUDED
src/nnue/layers/sqr_clipped_relu.h ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ // Definition of layer ClippedReLU of NNUE evaluation function
20
+
21
+ #ifndef NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED
22
+ #define NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED
23
+
24
+ #include "../nnue_common.h"
25
+
26
+ namespace Stockfish::Eval::NNUE::Layers {
27
+
28
+ // Clipped ReLU
29
+ template <IndexType InDims>
30
+ class SqrClippedReLU {
31
+ public:
32
+ // Input/output type
33
+ using InputType = std::int32_t;
34
+ using OutputType = std::uint8_t;
35
+
36
+ // Number of input/output dimensions
37
+ static constexpr IndexType InputDimensions = InDims;
38
+ static constexpr IndexType OutputDimensions = InputDimensions;
39
+ static constexpr IndexType PaddedOutputDimensions =
40
+ ceil_to_multiple<IndexType>(OutputDimensions, 32);
41
+
42
+ using OutputBuffer = OutputType[PaddedOutputDimensions];
43
+
44
+ // Hash value embedded in the evaluation file
45
+ static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
46
+ std::uint32_t hashValue = 0x538D24C7u;
47
+ hashValue += prevHash;
48
+ return hashValue;
49
+ }
50
+
51
+ // Read network parameters
52
+ bool read_parameters(std::istream&) {
53
+ return true;
54
+ }
55
+
56
+ // Write network parameters
57
+ bool write_parameters(std::ostream&) const {
58
+ return true;
59
+ }
60
+
61
+ // Forward propagation
62
+ const OutputType* propagate(
63
+ const InputType* input, OutputType* output) const {
64
+
65
+ #if defined(USE_SSE2)
66
+ constexpr IndexType NumChunks = InputDimensions / 16;
67
+
68
+ #ifdef USE_SSE41
69
+ const __m128i Zero = _mm_setzero_si128();
70
+ #else
71
+ const __m128i k0x80s = _mm_set1_epi8(-128);
72
+ #endif
73
+
74
+ static_assert(WeightScaleBits == 6);
75
+ const auto in = reinterpret_cast<const __m128i*>(input);
76
+ const auto out = reinterpret_cast<__m128i*>(output);
77
+ for (IndexType i = 0; i < NumChunks; ++i) {
78
+ __m128i words0 = _mm_packs_epi32(
79
+ _mm_load_si128(&in[i * 4 + 0]),
80
+ _mm_load_si128(&in[i * 4 + 1]));
81
+ __m128i words1 = _mm_packs_epi32(
82
+ _mm_load_si128(&in[i * 4 + 2]),
83
+ _mm_load_si128(&in[i * 4 + 3]));
84
+
85
+ // Not sure if
86
+ words0 = _mm_srli_epi16(_mm_mulhi_epi16(words0, words0), 3);
87
+ words1 = _mm_srli_epi16(_mm_mulhi_epi16(words1, words1), 3);
88
+
89
+ const __m128i packedbytes = _mm_packs_epi16(words0, words1);
90
+
91
+ _mm_store_si128(&out[i],
92
+
93
+ #ifdef USE_SSE41
94
+ _mm_max_epi8(packedbytes, Zero)
95
+ #else
96
+ _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)
97
+ #endif
98
+
99
+ );
100
+ }
101
+ constexpr IndexType Start = NumChunks * 16;
102
+
103
+ #else
104
+ constexpr IndexType Start = 0;
105
+ #endif
106
+
107
+ for (IndexType i = Start; i < InputDimensions; ++i) {
108
+ output[i] = static_cast<OutputType>(
109
+ // realy should be /127 but we need to make it fast
110
+ // needs to be accounted for in the trainer
111
+ std::max(0ll, std::min(127ll, (((long long)input[i] * input[i]) >> (2 * WeightScaleBits)) / 128)));
112
+ }
113
+
114
+ return output;
115
+ }
116
+ };
117
+
118
+ } // namespace Stockfish::Eval::NNUE::Layers
119
+
120
+ #endif // NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED
src/nnue/nnue_accumulator.h ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ // Class for difference calculation of NNUE evaluation function
20
+
21
+ #ifndef NNUE_ACCUMULATOR_H_INCLUDED
22
+ #define NNUE_ACCUMULATOR_H_INCLUDED
23
+
24
+ #include "nnue_architecture.h"
25
+
26
+ namespace Stockfish::Eval::NNUE {
27
+
28
+ // Class that holds the result of affine transformation of input features
29
+ struct alignas(CacheLineSize) Accumulator {
30
+ std::int16_t accumulation[2][TransformedFeatureDimensions];
31
+ std::int32_t psqtAccumulation[2][PSQTBuckets];
32
+ bool computed[2];
33
+ };
34
+
35
+ } // namespace Stockfish::Eval::NNUE
36
+
37
+ #endif // NNUE_ACCUMULATOR_H_INCLUDED
src/nnue/nnue_architecture.h ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ // Input features and network structure used in NNUE evaluation function
20
+
21
+ #ifndef NNUE_ARCHITECTURE_H_INCLUDED
22
+ #define NNUE_ARCHITECTURE_H_INCLUDED
23
+
24
+ #include <memory>
25
+
26
+ #include "nnue_common.h"
27
+
28
+ #include "features/half_ka_v2_hm.h"
29
+
30
+ #include "layers/affine_transform.h"
31
+ #include "layers/clipped_relu.h"
32
+ #include "layers/sqr_clipped_relu.h"
33
+
34
+ #include "../misc.h"
35
+
36
+ namespace Stockfish::Eval::NNUE {
37
+
38
+ // Input features used in evaluation function
39
+ using FeatureSet = Features::HalfKAv2_hm;
40
+
41
+ // Number of input feature dimensions after conversion
42
+ constexpr IndexType TransformedFeatureDimensions = 1024;
43
+ constexpr IndexType PSQTBuckets = 8;
44
+ constexpr IndexType LayerStacks = 8;
45
+
46
+ struct Network
47
+ {
48
+ static constexpr int FC_0_OUTPUTS = 15;
49
+ static constexpr int FC_1_OUTPUTS = 32;
50
+
51
+ Layers::AffineTransform<TransformedFeatureDimensions, FC_0_OUTPUTS + 1> fc_0;
52
+ Layers::SqrClippedReLU<FC_0_OUTPUTS + 1> ac_sqr_0;
53
+ Layers::ClippedReLU<FC_0_OUTPUTS + 1> ac_0;
54
+ Layers::AffineTransform<FC_0_OUTPUTS * 2, FC_1_OUTPUTS> fc_1;
55
+ Layers::ClippedReLU<FC_1_OUTPUTS> ac_1;
56
+ Layers::AffineTransform<FC_1_OUTPUTS, 1> fc_2;
57
+
58
+ // Hash value embedded in the evaluation file
59
+ static constexpr std::uint32_t get_hash_value() {
60
+ // input slice hash
61
+ std::uint32_t hashValue = 0xEC42E90Du;
62
+ hashValue ^= TransformedFeatureDimensions * 2;
63
+
64
+ hashValue = decltype(fc_0)::get_hash_value(hashValue);
65
+ hashValue = decltype(ac_0)::get_hash_value(hashValue);
66
+ hashValue = decltype(fc_1)::get_hash_value(hashValue);
67
+ hashValue = decltype(ac_1)::get_hash_value(hashValue);
68
+ hashValue = decltype(fc_2)::get_hash_value(hashValue);
69
+
70
+ return hashValue;
71
+ }
72
+
73
+ // Read network parameters
74
+ bool read_parameters(std::istream& stream) {
75
+ if (!fc_0.read_parameters(stream)) return false;
76
+ if (!ac_0.read_parameters(stream)) return false;
77
+ if (!fc_1.read_parameters(stream)) return false;
78
+ if (!ac_1.read_parameters(stream)) return false;
79
+ if (!fc_2.read_parameters(stream)) return false;
80
+ return true;
81
+ }
82
+
83
+ // Read network parameters
84
+ bool write_parameters(std::ostream& stream) const {
85
+ if (!fc_0.write_parameters(stream)) return false;
86
+ if (!ac_0.write_parameters(stream)) return false;
87
+ if (!fc_1.write_parameters(stream)) return false;
88
+ if (!ac_1.write_parameters(stream)) return false;
89
+ if (!fc_2.write_parameters(stream)) return false;
90
+ return true;
91
+ }
92
+
93
+ std::int32_t propagate(const TransformedFeatureType* transformedFeatures)
94
+ {
95
+ struct alignas(CacheLineSize) Buffer
96
+ {
97
+ alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out;
98
+ alignas(CacheLineSize) decltype(ac_sqr_0)::OutputType ac_sqr_0_out[ceil_to_multiple<IndexType>(FC_0_OUTPUTS * 2, 32)];
99
+ alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out;
100
+ alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out;
101
+ alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out;
102
+ alignas(CacheLineSize) decltype(fc_2)::OutputBuffer fc_2_out;
103
+
104
+ Buffer()
105
+ {
106
+ std::memset(this, 0, sizeof(*this));
107
+ }
108
+ };
109
+
110
+ #if defined(__clang__) && (__APPLE__)
111
+ // workaround for a bug reported with xcode 12
112
+ static thread_local auto tlsBuffer = std::make_unique<Buffer>();
113
+ // Access TLS only once, cache result.
114
+ Buffer& buffer = *tlsBuffer;
115
+ #else
116
+ alignas(CacheLineSize) static thread_local Buffer buffer;
117
+ #endif
118
+
119
+ fc_0.propagate(transformedFeatures, buffer.fc_0_out);
120
+ ac_sqr_0.propagate(buffer.fc_0_out, buffer.ac_sqr_0_out);
121
+ ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out);
122
+ std::memcpy(buffer.ac_sqr_0_out + FC_0_OUTPUTS, buffer.ac_0_out, FC_0_OUTPUTS * sizeof(decltype(ac_0)::OutputType));
123
+ fc_1.propagate(buffer.ac_sqr_0_out, buffer.fc_1_out);
124
+ ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out);
125
+ fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out);
126
+
127
+ // buffer.fc_0_out[FC_0_OUTPUTS] is such that 1.0 is equal to 127*(1<<WeightScaleBits) in quantized form
128
+ // but we want 1.0 to be equal to 600*OutputScale
129
+ std::int32_t fwdOut = int(buffer.fc_0_out[FC_0_OUTPUTS]) * (600*OutputScale) / (127*(1<<WeightScaleBits));
130
+ std::int32_t outputValue = buffer.fc_2_out[0] + fwdOut;
131
+
132
+ return outputValue;
133
+ }
134
+ };
135
+
136
+ } // namespace Stockfish::Eval::NNUE
137
+
138
+ #endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED
src/nnue/nnue_common.h ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ // Constants used in NNUE evaluation function
20
+
21
+ #ifndef NNUE_COMMON_H_INCLUDED
22
+ #define NNUE_COMMON_H_INCLUDED
23
+
24
+ #include <cstring>
25
+ #include <iostream>
26
+
27
+ #include "../misc.h" // for IsLittleEndian
28
+
29
+ #if defined(USE_AVX2)
30
+ #include <immintrin.h>
31
+
32
+ #elif defined(USE_SSE41)
33
+ #include <smmintrin.h>
34
+
35
+ #elif defined(USE_SSSE3)
36
+ #include <tmmintrin.h>
37
+
38
+ #elif defined(USE_SSE2)
39
+ #include <emmintrin.h>
40
+
41
+ #elif defined(USE_MMX)
42
+ #include <mmintrin.h>
43
+
44
+ #elif defined(USE_NEON)
45
+ #include <arm_neon.h>
46
+ #endif
47
+
48
+ namespace Stockfish::Eval::NNUE {
49
+
50
+ // Version of the evaluation file
51
+ constexpr std::uint32_t Version = 0x7AF32F20u;
52
+
53
+ // Constant used in evaluation value calculation
54
+ constexpr int OutputScale = 16;
55
+ constexpr int WeightScaleBits = 6;
56
+
57
+ // Size of cache line (in bytes)
58
+ constexpr std::size_t CacheLineSize = 64;
59
+
60
+ // SIMD width (in bytes)
61
+ #if defined(USE_AVX2)
62
+ constexpr std::size_t SimdWidth = 32;
63
+
64
+ #elif defined(USE_SSE2)
65
+ constexpr std::size_t SimdWidth = 16;
66
+
67
+ #elif defined(USE_MMX)
68
+ constexpr std::size_t SimdWidth = 8;
69
+
70
+ #elif defined(USE_NEON)
71
+ constexpr std::size_t SimdWidth = 16;
72
+ #endif
73
+
74
+ constexpr std::size_t MaxSimdWidth = 32;
75
+
76
+ // Type of input feature after conversion
77
+ using TransformedFeatureType = std::uint8_t;
78
+ using IndexType = std::uint32_t;
79
+
80
+ // Round n up to be a multiple of base
81
+ template <typename IntType>
82
+ constexpr IntType ceil_to_multiple(IntType n, IntType base) {
83
+ return (n + base - 1) / base * base;
84
+ }
85
+
86
+ // read_little_endian() is our utility to read an integer (signed or unsigned, any size)
87
+ // from a stream in little-endian order. We swap the byte order after the read if
88
+ // necessary to return a result with the byte ordering of the compiling machine.
89
+ template <typename IntType>
90
+ inline IntType read_little_endian(std::istream& stream) {
91
+ IntType result;
92
+
93
+ if (IsLittleEndian)
94
+ stream.read(reinterpret_cast<char*>(&result), sizeof(IntType));
95
+ else
96
+ {
97
+ std::uint8_t u[sizeof(IntType)];
98
+ typename std::make_unsigned<IntType>::type v = 0;
99
+
100
+ stream.read(reinterpret_cast<char*>(u), sizeof(IntType));
101
+ for (std::size_t i = 0; i < sizeof(IntType); ++i)
102
+ v = (v << 8) | u[sizeof(IntType) - i - 1];
103
+
104
+ std::memcpy(&result, &v, sizeof(IntType));
105
+ }
106
+
107
+ return result;
108
+ }
109
+
110
+ // write_little_endian() is our utility to write an integer (signed or unsigned, any size)
111
+ // to a stream in little-endian order. We swap the byte order before the write if
112
+ // necessary to always write in little endian order, independently of the byte
113
+ // ordering of the compiling machine.
114
+ template <typename IntType>
115
+ inline void write_little_endian(std::ostream& stream, IntType value) {
116
+
117
+ if (IsLittleEndian)
118
+ stream.write(reinterpret_cast<const char*>(&value), sizeof(IntType));
119
+ else
120
+ {
121
+ std::uint8_t u[sizeof(IntType)];
122
+ typename std::make_unsigned<IntType>::type v = value;
123
+
124
+ std::size_t i = 0;
125
+ // if constexpr to silence the warning about shift by 8
126
+ if constexpr (sizeof(IntType) > 1)
127
+ {
128
+ for (; i + 1 < sizeof(IntType); ++i)
129
+ {
130
+ u[i] = (std::uint8_t)v;
131
+ v >>= 8;
132
+ }
133
+ }
134
+ u[i] = (std::uint8_t)v;
135
+
136
+ stream.write(reinterpret_cast<char*>(u), sizeof(IntType));
137
+ }
138
+ }
139
+
140
+ // read_little_endian(s, out, N) : read integers in bulk from a little indian stream.
141
+ // This reads N integers from stream s and put them in array out.
142
+ template <typename IntType>
143
+ inline void read_little_endian(std::istream& stream, IntType* out, std::size_t count) {
144
+ if (IsLittleEndian)
145
+ stream.read(reinterpret_cast<char*>(out), sizeof(IntType) * count);
146
+ else
147
+ for (std::size_t i = 0; i < count; ++i)
148
+ out[i] = read_little_endian<IntType>(stream);
149
+ }
150
+
151
+ // write_little_endian(s, values, N) : write integers in bulk to a little indian stream.
152
+ // This takes N integers from array values and writes them on stream s.
153
+ template <typename IntType>
154
+ inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) {
155
+ if (IsLittleEndian)
156
+ stream.write(reinterpret_cast<const char*>(values), sizeof(IntType) * count);
157
+ else
158
+ for (std::size_t i = 0; i < count; ++i)
159
+ write_little_endian<IntType>(stream, values[i]);
160
+ }
161
+
162
+ } // namespace Stockfish::Eval::NNUE
163
+
164
+ #endif // #ifndef NNUE_COMMON_H_INCLUDED
src/nnue/nnue_feature_transformer.h ADDED
@@ -0,0 +1,589 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ // A class that converts the input features of the NNUE evaluation function
20
+
21
+ #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED
22
+ #define NNUE_FEATURE_TRANSFORMER_H_INCLUDED
23
+
24
+ #include "nnue_common.h"
25
+ #include "nnue_architecture.h"
26
+
27
+ #include <cstring> // std::memset()
28
+
29
+ namespace Stockfish::Eval::NNUE {
30
+
31
+ using BiasType = std::int16_t;
32
+ using WeightType = std::int16_t;
33
+ using PSQTWeightType = std::int32_t;
34
+
35
+ // If vector instructions are enabled, we update and refresh the
36
+ // accumulator tile by tile such that each tile fits in the CPU's
37
+ // vector registers.
38
+ #define VECTOR
39
+
40
+ static_assert(PSQTBuckets % 8 == 0,
41
+ "Per feature PSQT values cannot be processed at granularity lower than 8 at a time.");
42
+
43
+ #ifdef USE_AVX512
44
+ typedef __m512i vec_t;
45
+ typedef __m256i psqt_vec_t;
46
+ #define vec_load(a) _mm512_load_si512(a)
47
+ #define vec_store(a,b) _mm512_store_si512(a,b)
48
+ #define vec_add_16(a,b) _mm512_add_epi16(a,b)
49
+ #define vec_sub_16(a,b) _mm512_sub_epi16(a,b)
50
+ #define vec_mul_16(a,b) _mm512_mullo_epi16(a,b)
51
+ #define vec_zero() _mm512_setzero_epi32()
52
+ #define vec_set_16(a) _mm512_set1_epi16(a)
53
+ #define vec_max_16(a,b) _mm512_max_epi16(a,b)
54
+ #define vec_min_16(a,b) _mm512_min_epi16(a,b)
55
+ inline vec_t vec_msb_pack_16(vec_t a, vec_t b){
56
+ vec_t compacted = _mm512_packs_epi16(_mm512_srli_epi16(a,7),_mm512_srli_epi16(b,7));
57
+ return _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7), compacted);
58
+ }
59
+ #define vec_load_psqt(a) _mm256_load_si256(a)
60
+ #define vec_store_psqt(a,b) _mm256_store_si256(a,b)
61
+ #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b)
62
+ #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b)
63
+ #define vec_zero_psqt() _mm256_setzero_si256()
64
+ #define NumRegistersSIMD 32
65
+ #define MaxChunkSize 64
66
+
67
+ #elif USE_AVX2
68
+ typedef __m256i vec_t;
69
+ typedef __m256i psqt_vec_t;
70
+ #define vec_load(a) _mm256_load_si256(a)
71
+ #define vec_store(a,b) _mm256_store_si256(a,b)
72
+ #define vec_add_16(a,b) _mm256_add_epi16(a,b)
73
+ #define vec_sub_16(a,b) _mm256_sub_epi16(a,b)
74
+ #define vec_mul_16(a,b) _mm256_mullo_epi16(a,b)
75
+ #define vec_zero() _mm256_setzero_si256()
76
+ #define vec_set_16(a) _mm256_set1_epi16(a)
77
+ #define vec_max_16(a,b) _mm256_max_epi16(a,b)
78
+ #define vec_min_16(a,b) _mm256_min_epi16(a,b)
79
+ inline vec_t vec_msb_pack_16(vec_t a, vec_t b){
80
+ vec_t compacted = _mm256_packs_epi16(_mm256_srli_epi16(a,7), _mm256_srli_epi16(b,7));
81
+ return _mm256_permute4x64_epi64(compacted, 0b11011000);
82
+ }
83
+ #define vec_load_psqt(a) _mm256_load_si256(a)
84
+ #define vec_store_psqt(a,b) _mm256_store_si256(a,b)
85
+ #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b)
86
+ #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b)
87
+ #define vec_zero_psqt() _mm256_setzero_si256()
88
+ #define NumRegistersSIMD 16
89
+ #define MaxChunkSize 32
90
+
91
+ #elif USE_SSE2
92
+ typedef __m128i vec_t;
93
+ typedef __m128i psqt_vec_t;
94
+ #define vec_load(a) (*(a))
95
+ #define vec_store(a,b) *(a)=(b)
96
+ #define vec_add_16(a,b) _mm_add_epi16(a,b)
97
+ #define vec_sub_16(a,b) _mm_sub_epi16(a,b)
98
+ #define vec_mul_16(a,b) _mm_mullo_epi16(a,b)
99
+ #define vec_zero() _mm_setzero_si128()
100
+ #define vec_set_16(a) _mm_set1_epi16(a)
101
+ #define vec_max_16(a,b) _mm_max_epi16(a,b)
102
+ #define vec_min_16(a,b) _mm_min_epi16(a,b)
103
+ #define vec_msb_pack_16(a,b) _mm_packs_epi16(_mm_srli_epi16(a,7),_mm_srli_epi16(b,7))
104
+ #define vec_load_psqt(a) (*(a))
105
+ #define vec_store_psqt(a,b) *(a)=(b)
106
+ #define vec_add_psqt_32(a,b) _mm_add_epi32(a,b)
107
+ #define vec_sub_psqt_32(a,b) _mm_sub_epi32(a,b)
108
+ #define vec_zero_psqt() _mm_setzero_si128()
109
+ #define NumRegistersSIMD (Is64Bit ? 16 : 8)
110
+ #define MaxChunkSize 16
111
+
112
+ #elif USE_MMX
113
+ typedef __m64 vec_t;
114
+ typedef __m64 psqt_vec_t;
115
+ #define vec_load(a) (*(a))
116
+ #define vec_store(a,b) *(a)=(b)
117
+ #define vec_add_16(a,b) _mm_add_pi16(a,b)
118
+ #define vec_sub_16(a,b) _mm_sub_pi16(a,b)
119
+ #define vec_mul_16(a,b) _mm_mullo_pi16(a,b)
120
+ #define vec_zero() _mm_setzero_si64()
121
+ #define vec_set_16(a) _mm_set1_pi16(a)
122
+ inline vec_t vec_max_16(vec_t a,vec_t b){
123
+ vec_t comparison = _mm_cmpgt_pi16(a,b);
124
+ return _mm_or_si64(_mm_and_si64(comparison, a), _mm_andnot_si64(comparison, b));
125
+ }
126
+ inline vec_t vec_min_16(vec_t a,vec_t b){
127
+ vec_t comparison = _mm_cmpgt_pi16(a,b);
128
+ return _mm_or_si64(_mm_and_si64(comparison, b), _mm_andnot_si64(comparison, a));
129
+ }
130
+ #define vec_msb_pack_16(a,b) _mm_packs_pi16(_mm_srli_pi16(a,7),_mm_srli_pi16(b,7))
131
+ #define vec_load_psqt(a) (*(a))
132
+ #define vec_store_psqt(a,b) *(a)=(b)
133
+ #define vec_add_psqt_32(a,b) _mm_add_pi32(a,b)
134
+ #define vec_sub_psqt_32(a,b) _mm_sub_pi32(a,b)
135
+ #define vec_zero_psqt() _mm_setzero_si64()
136
+ #define vec_cleanup() _mm_empty()
137
+ #define NumRegistersSIMD 8
138
+ #define MaxChunkSize 8
139
+
140
+ #elif USE_NEON
141
+ typedef int16x8_t vec_t;
142
+ typedef int32x4_t psqt_vec_t;
143
+ #define vec_load(a) (*(a))
144
+ #define vec_store(a,b) *(a)=(b)
145
+ #define vec_add_16(a,b) vaddq_s16(a,b)
146
+ #define vec_sub_16(a,b) vsubq_s16(a,b)
147
+ #define vec_mul_16(a,b) vmulq_s16(a,b)
148
+ #define vec_zero() vec_t{0}
149
+ #define vec_set_16(a) vdupq_n_s16(a)
150
+ #define vec_max_16(a,b) vmaxq_s16(a,b)
151
+ #define vec_min_16(a,b) vminq_s16(a,b)
152
+ inline vec_t vec_msb_pack_16(vec_t a, vec_t b){
153
+ const int8x8_t shifta = vshrn_n_s16(a, 7);
154
+ const int8x8_t shiftb = vshrn_n_s16(b, 7);
155
+ const int8x16_t compacted = vcombine_s8(shifta,shiftb);
156
+ return *reinterpret_cast<const vec_t*> (&compacted);
157
+ }
158
+ #define vec_load_psqt(a) (*(a))
159
+ #define vec_store_psqt(a,b) *(a)=(b)
160
+ #define vec_add_psqt_32(a,b) vaddq_s32(a,b)
161
+ #define vec_sub_psqt_32(a,b) vsubq_s32(a,b)
162
+ #define vec_zero_psqt() psqt_vec_t{0}
163
+ #define NumRegistersSIMD 16
164
+ #define MaxChunkSize 16
165
+
166
+ #else
167
+ #undef VECTOR
168
+
169
+ #endif
170
+
171
+
172
+ #ifdef VECTOR
173
+
174
+ // Compute optimal SIMD register count for feature transformer accumulation.
175
+
176
+ // We use __m* types as template arguments, which causes GCC to emit warnings
177
+ // about losing some attribute information. This is irrelevant to us as we
178
+ // only take their size, so the following pragma are harmless.
179
+ #if defined(__GNUC__)
180
+ #pragma GCC diagnostic push
181
+ #pragma GCC diagnostic ignored "-Wignored-attributes"
182
+ #endif
183
+
184
+ template <typename SIMDRegisterType,
185
+ typename LaneType,
186
+ int NumLanes,
187
+ int MaxRegisters>
188
+ static constexpr int BestRegisterCount()
189
+ {
190
+ #define RegisterSize sizeof(SIMDRegisterType)
191
+ #define LaneSize sizeof(LaneType)
192
+
193
+ static_assert(RegisterSize >= LaneSize);
194
+ static_assert(MaxRegisters <= NumRegistersSIMD);
195
+ static_assert(MaxRegisters > 0);
196
+ static_assert(NumRegistersSIMD > 0);
197
+ static_assert(RegisterSize % LaneSize == 0);
198
+ static_assert((NumLanes * LaneSize) % RegisterSize == 0);
199
+
200
+ const int ideal = (NumLanes * LaneSize) / RegisterSize;
201
+ if (ideal <= MaxRegisters)
202
+ return ideal;
203
+
204
+ // Look for the largest divisor of the ideal register count that is smaller than MaxRegisters
205
+ for (int divisor = MaxRegisters; divisor > 1; --divisor)
206
+ if (ideal % divisor == 0)
207
+ return divisor;
208
+
209
+ return 1;
210
+ }
211
+
212
+ static constexpr int NumRegs = BestRegisterCount<vec_t, WeightType, TransformedFeatureDimensions, NumRegistersSIMD>();
213
+ static constexpr int NumPsqtRegs = BestRegisterCount<psqt_vec_t, PSQTWeightType, PSQTBuckets, NumRegistersSIMD>();
214
+ #if defined(__GNUC__)
215
+ #pragma GCC diagnostic pop
216
+ #endif
217
+ #endif
218
+
219
+
220
+
221
+ // Input feature converter
222
+ class FeatureTransformer {
223
+
224
+ private:
225
+ // Number of output dimensions for one side
226
+ static constexpr IndexType HalfDimensions = TransformedFeatureDimensions;
227
+
228
+ #ifdef VECTOR
229
+ static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2;
230
+ static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4;
231
+ static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions");
232
+ static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets");
233
+ #endif
234
+
235
+ public:
236
+ // Output type
237
+ using OutputType = TransformedFeatureType;
238
+
239
+ // Number of input/output dimensions
240
+ static constexpr IndexType InputDimensions = FeatureSet::Dimensions;
241
+ static constexpr IndexType OutputDimensions = HalfDimensions;
242
+
243
+ // Size of forward propagation buffer
244
+ static constexpr std::size_t BufferSize =
245
+ OutputDimensions * sizeof(OutputType);
246
+
247
+ // Hash value embedded in the evaluation file
248
+ static constexpr std::uint32_t get_hash_value() {
249
+ return FeatureSet::HashValue ^ (OutputDimensions * 2);
250
+ }
251
+
252
+ // Read network parameters
253
+ bool read_parameters(std::istream& stream) {
254
+
255
+ read_little_endian<BiasType >(stream, biases , HalfDimensions );
256
+ read_little_endian<WeightType >(stream, weights , HalfDimensions * InputDimensions);
257
+ read_little_endian<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
258
+
259
+ return !stream.fail();
260
+ }
261
+
262
+ // Write network parameters
263
+ bool write_parameters(std::ostream& stream) const {
264
+
265
+ write_little_endian<BiasType >(stream, biases , HalfDimensions );
266
+ write_little_endian<WeightType >(stream, weights , HalfDimensions * InputDimensions);
267
+ write_little_endian<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
268
+
269
+ return !stream.fail();
270
+ }
271
+
272
+ // Convert input features
273
+ std::int32_t transform(const Position& pos, OutputType* output, int bucket) const {
274
+ update_accumulator<WHITE>(pos);
275
+ update_accumulator<BLACK>(pos);
276
+
277
+ const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()};
278
+ const auto& accumulation = pos.state()->accumulator.accumulation;
279
+ const auto& psqtAccumulation = pos.state()->accumulator.psqtAccumulation;
280
+
281
+ const auto psqt = (
282
+ psqtAccumulation[perspectives[0]][bucket]
283
+ - psqtAccumulation[perspectives[1]][bucket]
284
+ ) / 2;
285
+
286
+
287
+ for (IndexType p = 0; p < 2; ++p)
288
+ {
289
+ const IndexType offset = (HalfDimensions / 2) * p;
290
+
291
+ #if defined(VECTOR)
292
+
293
+ constexpr IndexType OutputChunkSize = MaxChunkSize;
294
+ static_assert((HalfDimensions / 2) % OutputChunkSize == 0);
295
+ constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize;
296
+
297
+ vec_t Zero = vec_zero();
298
+ vec_t One = vec_set_16(127);
299
+
300
+ const vec_t* in0 = reinterpret_cast<const vec_t*>(&(accumulation[perspectives[p]][0]));
301
+ const vec_t* in1 = reinterpret_cast<const vec_t*>(&(accumulation[perspectives[p]][HalfDimensions / 2]));
302
+ vec_t* out = reinterpret_cast< vec_t*>(output + offset);
303
+
304
+ for (IndexType j = 0; j < NumOutputChunks; j += 1)
305
+ {
306
+ const vec_t sum0a = vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero);
307
+ const vec_t sum0b = vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero);
308
+ const vec_t sum1a = vec_max_16(vec_min_16(in1[j * 2 + 0], One), Zero);
309
+ const vec_t sum1b = vec_max_16(vec_min_16(in1[j * 2 + 1], One), Zero);
310
+
311
+ const vec_t pa = vec_mul_16(sum0a, sum1a);
312
+ const vec_t pb = vec_mul_16(sum0b, sum1b);
313
+
314
+ out[j] = vec_msb_pack_16(pa, pb);
315
+ }
316
+
317
+ #else
318
+
319
+ for (IndexType j = 0; j < HalfDimensions / 2; ++j) {
320
+ BiasType sum0 = accumulation[static_cast<int>(perspectives[p])][j + 0];
321
+ BiasType sum1 = accumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
322
+ sum0 = std::max<int>(0, std::min<int>(127, sum0));
323
+ sum1 = std::max<int>(0, std::min<int>(127, sum1));
324
+ output[offset + j] = static_cast<OutputType>(sum0 * sum1 / 128);
325
+ }
326
+
327
+ #endif
328
+ }
329
+
330
+ #if defined(vec_cleanup)
331
+ vec_cleanup();
332
+ #endif
333
+
334
+ return psqt;
335
+
336
+ } // end of function transform()
337
+
338
+
339
+
340
+ private:
341
+ template<Color Perspective>
342
+ void update_accumulator(const Position& pos) const {
343
+
344
+ // The size must be enough to contain the largest possible update.
345
+ // That might depend on the feature set and generally relies on the
346
+ // feature set's update cost calculation to be correct and never
347
+ // allow updates with more added/removed features than MaxActiveDimensions.
348
+
349
+ #ifdef VECTOR
350
+ // Gcc-10.2 unnecessarily spills AVX2 registers if this array
351
+ // is defined in the VECTOR code below, once in each branch
352
+ vec_t acc[NumRegs];
353
+ psqt_vec_t psqt[NumPsqtRegs];
354
+ #endif
355
+
356
+ // Look for a usable accumulator of an earlier position. We keep track
357
+ // of the estimated gain in terms of features to be added/subtracted.
358
+ StateInfo *st = pos.state(), *next = nullptr;
359
+ int gain = FeatureSet::refresh_cost(pos);
360
+ while (st->previous && !st->accumulator.computed[Perspective])
361
+ {
362
+ // This governs when a full feature refresh is needed and how many
363
+ // updates are better than just one full refresh.
364
+ if ( FeatureSet::requires_refresh(st, Perspective)
365
+ || (gain -= FeatureSet::update_cost(st) + 1) < 0)
366
+ break;
367
+ next = st;
368
+ st = st->previous;
369
+ }
370
+
371
+ if (st->accumulator.computed[Perspective])
372
+ {
373
+ if (next == nullptr)
374
+ return;
375
+
376
+ // Update incrementally in two steps. First, we update the "next"
377
+ // accumulator. Then, we update the current accumulator (pos.state()).
378
+
379
+ // Gather all features to be updated.
380
+ const Square ksq = pos.square<KING>(Perspective);
381
+ FeatureSet::IndexList removed[2], added[2];
382
+ FeatureSet::append_changed_indices<Perspective>(
383
+ ksq, next->dirtyPiece, removed[0], added[0]);
384
+ for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous)
385
+ FeatureSet::append_changed_indices<Perspective>(
386
+ ksq, st2->dirtyPiece, removed[1], added[1]);
387
+
388
+ // Mark the accumulators as computed.
389
+ next->accumulator.computed[Perspective] = true;
390
+ pos.state()->accumulator.computed[Perspective] = true;
391
+
392
+ // Now update the accumulators listed in states_to_update[], where the last element is a sentinel.
393
+ StateInfo *states_to_update[3] =
394
+ { next, next == pos.state() ? nullptr : pos.state(), nullptr };
395
+ #ifdef VECTOR
396
+ for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
397
+ {
398
+ // Load accumulator
399
+ auto accTile = reinterpret_cast<vec_t*>(
400
+ &st->accumulator.accumulation[Perspective][j * TileHeight]);
401
+ for (IndexType k = 0; k < NumRegs; ++k)
402
+ acc[k] = vec_load(&accTile[k]);
403
+
404
+ for (IndexType i = 0; states_to_update[i]; ++i)
405
+ {
406
+ // Difference calculation for the deactivated features
407
+ for (const auto index : removed[i])
408
+ {
409
+ const IndexType offset = HalfDimensions * index + j * TileHeight;
410
+ auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
411
+ for (IndexType k = 0; k < NumRegs; ++k)
412
+ acc[k] = vec_sub_16(acc[k], column[k]);
413
+ }
414
+
415
+ // Difference calculation for the activated features
416
+ for (const auto index : added[i])
417
+ {
418
+ const IndexType offset = HalfDimensions * index + j * TileHeight;
419
+ auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
420
+ for (IndexType k = 0; k < NumRegs; ++k)
421
+ acc[k] = vec_add_16(acc[k], column[k]);
422
+ }
423
+
424
+ // Store accumulator
425
+ accTile = reinterpret_cast<vec_t*>(
426
+ &states_to_update[i]->accumulator.accumulation[Perspective][j * TileHeight]);
427
+ for (IndexType k = 0; k < NumRegs; ++k)
428
+ vec_store(&accTile[k], acc[k]);
429
+ }
430
+ }
431
+
432
+ for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
433
+ {
434
+ // Load accumulator
435
+ auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
436
+ &st->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
437
+ for (std::size_t k = 0; k < NumPsqtRegs; ++k)
438
+ psqt[k] = vec_load_psqt(&accTilePsqt[k]);
439
+
440
+ for (IndexType i = 0; states_to_update[i]; ++i)
441
+ {
442
+ // Difference calculation for the deactivated features
443
+ for (const auto index : removed[i])
444
+ {
445
+ const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
446
+ auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
447
+ for (std::size_t k = 0; k < NumPsqtRegs; ++k)
448
+ psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]);
449
+ }
450
+
451
+ // Difference calculation for the activated features
452
+ for (const auto index : added[i])
453
+ {
454
+ const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
455
+ auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
456
+ for (std::size_t k = 0; k < NumPsqtRegs; ++k)
457
+ psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
458
+ }
459
+
460
+ // Store accumulator
461
+ accTilePsqt = reinterpret_cast<psqt_vec_t*>(
462
+ &states_to_update[i]->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
463
+ for (std::size_t k = 0; k < NumPsqtRegs; ++k)
464
+ vec_store_psqt(&accTilePsqt[k], psqt[k]);
465
+ }
466
+ }
467
+
468
+ #else
469
+ for (IndexType i = 0; states_to_update[i]; ++i)
470
+ {
471
+ std::memcpy(states_to_update[i]->accumulator.accumulation[Perspective],
472
+ st->accumulator.accumulation[Perspective],
473
+ HalfDimensions * sizeof(BiasType));
474
+
475
+ for (std::size_t k = 0; k < PSQTBuckets; ++k)
476
+ states_to_update[i]->accumulator.psqtAccumulation[Perspective][k] = st->accumulator.psqtAccumulation[Perspective][k];
477
+
478
+ st = states_to_update[i];
479
+
480
+ // Difference calculation for the deactivated features
481
+ for (const auto index : removed[i])
482
+ {
483
+ const IndexType offset = HalfDimensions * index;
484
+
485
+ for (IndexType j = 0; j < HalfDimensions; ++j)
486
+ st->accumulator.accumulation[Perspective][j] -= weights[offset + j];
487
+
488
+ for (std::size_t k = 0; k < PSQTBuckets; ++k)
489
+ st->accumulator.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k];
490
+ }
491
+
492
+ // Difference calculation for the activated features
493
+ for (const auto index : added[i])
494
+ {
495
+ const IndexType offset = HalfDimensions * index;
496
+
497
+ for (IndexType j = 0; j < HalfDimensions; ++j)
498
+ st->accumulator.accumulation[Perspective][j] += weights[offset + j];
499
+
500
+ for (std::size_t k = 0; k < PSQTBuckets; ++k)
501
+ st->accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k];
502
+ }
503
+ }
504
+ #endif
505
+ }
506
+ else
507
+ {
508
+ // Refresh the accumulator
509
+ auto& accumulator = pos.state()->accumulator;
510
+ accumulator.computed[Perspective] = true;
511
+ FeatureSet::IndexList active;
512
+ FeatureSet::append_active_indices<Perspective>(pos, active);
513
+
514
+ #ifdef VECTOR
515
+ for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
516
+ {
517
+ auto biasesTile = reinterpret_cast<const vec_t*>(
518
+ &biases[j * TileHeight]);
519
+ for (IndexType k = 0; k < NumRegs; ++k)
520
+ acc[k] = biasesTile[k];
521
+
522
+ for (const auto index : active)
523
+ {
524
+ const IndexType offset = HalfDimensions * index + j * TileHeight;
525
+ auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
526
+
527
+ for (unsigned k = 0; k < NumRegs; ++k)
528
+ acc[k] = vec_add_16(acc[k], column[k]);
529
+ }
530
+
531
+ auto accTile = reinterpret_cast<vec_t*>(
532
+ &accumulator.accumulation[Perspective][j * TileHeight]);
533
+ for (unsigned k = 0; k < NumRegs; k++)
534
+ vec_store(&accTile[k], acc[k]);
535
+ }
536
+
537
+ for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
538
+ {
539
+ for (std::size_t k = 0; k < NumPsqtRegs; ++k)
540
+ psqt[k] = vec_zero_psqt();
541
+
542
+ for (const auto index : active)
543
+ {
544
+ const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
545
+ auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
546
+
547
+ for (std::size_t k = 0; k < NumPsqtRegs; ++k)
548
+ psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
549
+ }
550
+
551
+ auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
552
+ &accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
553
+ for (std::size_t k = 0; k < NumPsqtRegs; ++k)
554
+ vec_store_psqt(&accTilePsqt[k], psqt[k]);
555
+ }
556
+
557
+ #else
558
+ std::memcpy(accumulator.accumulation[Perspective], biases,
559
+ HalfDimensions * sizeof(BiasType));
560
+
561
+ for (std::size_t k = 0; k < PSQTBuckets; ++k)
562
+ accumulator.psqtAccumulation[Perspective][k] = 0;
563
+
564
+ for (const auto index : active)
565
+ {
566
+ const IndexType offset = HalfDimensions * index;
567
+
568
+ for (IndexType j = 0; j < HalfDimensions; ++j)
569
+ accumulator.accumulation[Perspective][j] += weights[offset + j];
570
+
571
+ for (std::size_t k = 0; k < PSQTBuckets; ++k)
572
+ accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k];
573
+ }
574
+ #endif
575
+ }
576
+
577
+ #if defined(USE_MMX)
578
+ _mm_empty();
579
+ #endif
580
+ }
581
+
582
+ alignas(CacheLineSize) BiasType biases[HalfDimensions];
583
+ alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions];
584
+ alignas(CacheLineSize) PSQTWeightType psqtWeights[InputDimensions * PSQTBuckets];
585
+ };
586
+
587
+ } // namespace Stockfish::Eval::NNUE
588
+
589
+ #endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED
src/pawns.cpp ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <algorithm>
20
+ #include <cassert>
21
+
22
+ #include "bitboard.h"
23
+ #include "pawns.h"
24
+ #include "position.h"
25
+ #include "thread.h"
26
+
27
+ namespace Stockfish {
28
+
29
+ namespace {
30
+
31
+ #define V Value
32
+ #define S(mg, eg) make_score(mg, eg)
33
+
34
+ // Pawn penalties
35
+ constexpr Score Backward = S( 6, 19);
36
+ constexpr Score Doubled = S(11, 51);
37
+ constexpr Score DoubledEarly = S(17, 7);
38
+ constexpr Score Isolated = S( 1, 20);
39
+ constexpr Score WeakLever = S( 2, 57);
40
+ constexpr Score WeakUnopposed = S(15, 18);
41
+
42
+ // Bonus for blocked pawns at 5th or 6th rank
43
+ constexpr Score BlockedPawn[2] = { S(-19, -8), S(-7, 3) };
44
+
45
+ constexpr Score BlockedStorm[RANK_NB] = {
46
+ S(0, 0), S(0, 0), S(64, 75), S(-3, 14), S(-12, 19), S(-7, 4), S(-10, 5)
47
+ };
48
+
49
+ // Connected pawn bonus
50
+ constexpr int Connected[RANK_NB] = { 0, 3, 7, 7, 15, 54, 86 };
51
+
52
+ // Strength of pawn shelter for our king by [distance from edge][rank].
53
+ // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king.
54
+ constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = {
55
+ { V(-2), V(85), V(95), V(53), V(39), V(23), V(25) },
56
+ { V(-55), V(64), V(32), V(-55), V(-30), V(-11), V(-61) },
57
+ { V(-11), V(75), V(19), V(-6), V(26), V(9), V(-47) },
58
+ { V(-41), V(-11), V(-27), V(-58), V(-42), V(-66), V(-163) }
59
+ };
60
+
61
+ // Danger of enemy pawns moving toward our king by [distance from edge][rank].
62
+ // RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn
63
+ // is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn
64
+ // on edge, likely blocked by our king.
65
+ constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = {
66
+ { V(94), V(-280), V(-170), V(90), V(59), V(47), V(53) },
67
+ { V(43), V(-17), V(128), V(39), V(26), V(-17), V(15) },
68
+ { V(-9), V(62), V(170), V(34), V(-5), V(-20), V(-11) },
69
+ { V(-27), V(-19), V(106), V(10), V(2), V(-13), V(-24) }
70
+ };
71
+
72
+
73
+ // KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties
74
+ // for king when the king is on a semi-open or open file.
75
+ constexpr Score KingOnFile[2][2] = {{ S(-18,11), S(-6,-3) },
76
+ { S( 0, 0), S( 5,-4) }};
77
+
78
+ #undef S
79
+ #undef V
80
+
81
+
82
+ /// evaluate() calculates a score for the static pawn structure of the given position.
83
+ /// We cannot use the location of pieces or king in this function, as the evaluation
84
+ /// of the pawn structure will be stored in a small cache for speed reasons, and will
85
+ /// be re-used even when the pieces have moved.
86
+
87
+ template<Color Us>
88
+ Score evaluate(const Position& pos, Pawns::Entry* e) {
89
+
90
+ constexpr Color Them = ~Us;
91
+ constexpr Direction Up = pawn_push(Us);
92
+ constexpr Direction Down = -Up;
93
+
94
+ Bitboard neighbours, stoppers, support, phalanx, opposed;
95
+ Bitboard lever, leverPush, blocked;
96
+ Square s;
97
+ bool backward, passed, doubled;
98
+ Score score = SCORE_ZERO;
99
+ Bitboard b = pos.pieces(Us, PAWN);
100
+
101
+ Bitboard ourPawns = pos.pieces( Us, PAWN);
102
+ Bitboard theirPawns = pos.pieces(Them, PAWN);
103
+
104
+ Bitboard doubleAttackThem = pawn_double_attacks_bb<Them>(theirPawns);
105
+
106
+ e->passedPawns[Us] = 0;
107
+ e->kingSquares[Us] = SQ_NONE;
108
+ e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb<Us>(ourPawns);
109
+ e->blockedCount += popcount(shift<Up>(ourPawns) & (theirPawns | doubleAttackThem));
110
+
111
+ // Loop through all pawns of the current color and score each pawn
112
+ while (b)
113
+ {
114
+ s = pop_lsb(b);
115
+
116
+ assert(pos.piece_on(s) == make_piece(Us, PAWN));
117
+
118
+ Rank r = relative_rank(Us, s);
119
+
120
+ // Flag the pawn
121
+ opposed = theirPawns & forward_file_bb(Us, s);
122
+ blocked = theirPawns & (s + Up);
123
+ stoppers = theirPawns & passed_pawn_span(Us, s);
124
+ lever = theirPawns & pawn_attacks_bb(Us, s);
125
+ leverPush = theirPawns & pawn_attacks_bb(Us, s + Up);
126
+ doubled = ourPawns & (s - Up);
127
+ neighbours = ourPawns & adjacent_files_bb(s);
128
+ phalanx = neighbours & rank_bb(s);
129
+ support = neighbours & rank_bb(s - Up);
130
+
131
+ if (doubled)
132
+ {
133
+ // Additional doubled penalty if none of their pawns is fixed
134
+ if (!(ourPawns & shift<Down>(theirPawns | pawn_attacks_bb<Them>(theirPawns))))
135
+ score -= DoubledEarly;
136
+ }
137
+
138
+ // A pawn is backward when it is behind all pawns of the same color on
139
+ // the adjacent files and cannot safely advance.
140
+ backward = !(neighbours & forward_ranks_bb(Them, s + Up))
141
+ && (leverPush | blocked);
142
+
143
+ // Compute additional span if pawn is not backward nor blocked
144
+ if (!backward && !blocked)
145
+ e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
146
+
147
+ // A pawn is passed if one of the three following conditions is true:
148
+ // (a) there is no stoppers except some levers
149
+ // (b) the only stoppers are the leverPush, but we outnumber them
150
+ // (c) there is only one front stopper which can be levered.
151
+ // (Refined in Evaluation::passed)
152
+ passed = !(stoppers ^ lever)
153
+ || ( !(stoppers ^ leverPush)
154
+ && popcount(phalanx) >= popcount(leverPush))
155
+ || ( stoppers == blocked && r >= RANK_5
156
+ && (shift<Up>(support) & ~(theirPawns | doubleAttackThem)));
157
+
158
+ passed &= !(forward_file_bb(Us, s) & ourPawns);
159
+
160
+ // Passed pawns will be properly scored later in evaluation when we have
161
+ // full attack info.
162
+ if (passed)
163
+ e->passedPawns[Us] |= s;
164
+
165
+ // Score this pawn
166
+ if (support | phalanx)
167
+ {
168
+ int v = Connected[r] * (2 + bool(phalanx) - bool(opposed))
169
+ + 22 * popcount(support);
170
+
171
+ score += make_score(v, v * (r - 2) / 4);
172
+ }
173
+
174
+ else if (!neighbours)
175
+ {
176
+ if ( opposed
177
+ && (ourPawns & forward_file_bb(Them, s))
178
+ && !(theirPawns & adjacent_files_bb(s)))
179
+ score -= Doubled;
180
+ else
181
+ score -= Isolated
182
+ + WeakUnopposed * !opposed;
183
+ }
184
+
185
+ else if (backward)
186
+ score -= Backward
187
+ + WeakUnopposed * !opposed * bool(~(FileABB | FileHBB) & s);
188
+
189
+ if (!support)
190
+ score -= Doubled * doubled
191
+ + WeakLever * more_than_one(lever);
192
+
193
+ if (blocked && r >= RANK_5)
194
+ score += BlockedPawn[r - RANK_5];
195
+ }
196
+
197
+ return score;
198
+ }
199
+
200
+ } // namespace
201
+
202
+ namespace Pawns {
203
+
204
+
205
+ /// Pawns::probe() looks up the current position's pawns configuration in
206
+ /// the pawns hash table. It returns a pointer to the Entry if the position
207
+ /// is found. Otherwise a new Entry is computed and stored there, so we don't
208
+ /// have to recompute all when the same pawns configuration occurs again.
209
+
210
+ Entry* probe(const Position& pos) {
211
+
212
+ Key key = pos.pawn_key();
213
+ Entry* e = pos.this_thread()->pawnsTable[key];
214
+
215
+ if (e->key == key)
216
+ return e;
217
+
218
+ e->key = key;
219
+ e->blockedCount = 0;
220
+ e->scores[WHITE] = evaluate<WHITE>(pos, e);
221
+ e->scores[BLACK] = evaluate<BLACK>(pos, e);
222
+
223
+ return e;
224
+ }
225
+
226
+
227
+ /// Entry::evaluate_shelter() calculates the shelter bonus and the storm
228
+ /// penalty for a king, looking at the king file and the two closest files.
229
+
230
+ template<Color Us>
231
+ Score Entry::evaluate_shelter(const Position& pos, Square ksq) const {
232
+
233
+ constexpr Color Them = ~Us;
234
+
235
+ Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
236
+ Bitboard ourPawns = b & pos.pieces(Us) & ~pawnAttacks[Them];
237
+ Bitboard theirPawns = b & pos.pieces(Them);
238
+
239
+ Score bonus = make_score(5, 5);
240
+
241
+ File center = std::clamp(file_of(ksq), FILE_B, FILE_G);
242
+ for (File f = File(center - 1); f <= File(center + 1); ++f)
243
+ {
244
+ b = ourPawns & file_bb(f);
245
+ int ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;
246
+
247
+ b = theirPawns & file_bb(f);
248
+ int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;
249
+
250
+ int d = edge_distance(f);
251
+ bonus += make_score(ShelterStrength[d][ourRank], 0);
252
+
253
+ if (ourRank && (ourRank == theirRank - 1))
254
+ bonus -= BlockedStorm[theirRank];
255
+ else
256
+ bonus -= make_score(UnblockedStorm[d][theirRank], 0);
257
+ }
258
+
259
+ // King On File
260
+ bonus -= KingOnFile[pos.is_on_semiopen_file(Us, ksq)][pos.is_on_semiopen_file(Them, ksq)];
261
+
262
+ return bonus;
263
+ }
264
+
265
+
266
+ /// Entry::do_king_safety() calculates a bonus for king safety. It is called only
267
+ /// when king square changes, which is about 20% of total king_safety() calls.
268
+
269
+ template<Color Us>
270
+ Score Entry::do_king_safety(const Position& pos) {
271
+
272
+ Square ksq = pos.square<KING>(Us);
273
+ kingSquares[Us] = ksq;
274
+ castlingRights[Us] = pos.castling_rights(Us);
275
+ auto compare = [](Score a, Score b) { return mg_value(a) < mg_value(b); };
276
+
277
+ Score shelter = evaluate_shelter<Us>(pos, ksq);
278
+
279
+ // If we can castle use the bonus after castling if it is bigger
280
+
281
+ if (pos.can_castle(Us & KING_SIDE))
282
+ shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)), compare);
283
+
284
+ if (pos.can_castle(Us & QUEEN_SIDE))
285
+ shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)), compare);
286
+
287
+ // In endgame we like to bring our king near our closest pawn
288
+ Bitboard pawns = pos.pieces(Us, PAWN);
289
+ int minPawnDist = 6;
290
+
291
+ if (pawns & attacks_bb<KING>(ksq))
292
+ minPawnDist = 1;
293
+ else while (pawns)
294
+ minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(pawns)));
295
+
296
+ return shelter - make_score(0, 16 * minPawnDist);
297
+ }
298
+
299
+ // Explicit template instantiation
300
+ template Score Entry::do_king_safety<WHITE>(const Position& pos);
301
+ template Score Entry::do_king_safety<BLACK>(const Position& pos);
302
+
303
+ } // namespace Pawns
304
+
305
+ } // namespace Stockfish
src/pawns.h ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef PAWNS_H_INCLUDED
20
+ #define PAWNS_H_INCLUDED
21
+
22
+ #include "misc.h"
23
+ #include "position.h"
24
+ #include "types.h"
25
+
26
+ namespace Stockfish::Pawns {
27
+
28
+ /// Pawns::Entry contains various information about a pawn structure. A lookup
29
+ /// to the pawn hash table (performed by calling the probe function) returns a
30
+ /// pointer to an Entry object.
31
+
32
+ struct Entry {
33
+
34
+ Score pawn_score(Color c) const { return scores[c]; }
35
+ Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
36
+ Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
37
+ Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
38
+ int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); }
39
+ int blocked_count() const { return blockedCount; }
40
+
41
+ template<Color Us>
42
+ Score king_safety(const Position& pos) {
43
+ return kingSquares[Us] == pos.square<KING>(Us) && castlingRights[Us] == pos.castling_rights(Us)
44
+ ? kingSafety[Us] : (kingSafety[Us] = do_king_safety<Us>(pos));
45
+ }
46
+
47
+ template<Color Us>
48
+ Score do_king_safety(const Position& pos);
49
+
50
+ template<Color Us>
51
+ Score evaluate_shelter(const Position& pos, Square ksq) const;
52
+
53
+ Key key;
54
+ Score scores[COLOR_NB];
55
+ Bitboard passedPawns[COLOR_NB];
56
+ Bitboard pawnAttacks[COLOR_NB];
57
+ Bitboard pawnAttacksSpan[COLOR_NB];
58
+ Square kingSquares[COLOR_NB];
59
+ Score kingSafety[COLOR_NB];
60
+ int castlingRights[COLOR_NB];
61
+ int blockedCount;
62
+ };
63
+
64
+ typedef HashTable<Entry, 131072> Table;
65
+
66
+ Entry* probe(const Position& pos);
67
+
68
+ } // namespace Stockfish::Pawns
69
+
70
+ #endif // #ifndef PAWNS_H_INCLUDED
src/position.cpp ADDED
@@ -0,0 +1,1353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <algorithm>
20
+ #include <cassert>
21
+ #include <cstddef> // For offsetof()
22
+ #include <cstring> // For std::memset, std::memcmp
23
+ #include <iomanip>
24
+ #include <sstream>
25
+
26
+ #include "bitboard.h"
27
+ #include "misc.h"
28
+ #include "movegen.h"
29
+ #include "position.h"
30
+ #include "thread.h"
31
+ #include "tt.h"
32
+ #include "uci.h"
33
+ #include "syzygy/tbprobe.h"
34
+
35
+ using std::string;
36
+
37
+ namespace Stockfish {
38
+
39
+ namespace Zobrist {
40
+
41
+ Key psq[PIECE_NB][SQUARE_NB];
42
+ Key enpassant[FILE_NB];
43
+ Key castling[CASTLING_RIGHT_NB];
44
+ Key side, noPawns;
45
+ }
46
+
47
+ namespace {
48
+
49
+ const string PieceToChar(" PNBRQK pnbrqk");
50
+
51
+ constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
52
+ B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
53
+ } // namespace
54
+
55
+
56
+ /// operator<<(Position) returns an ASCII representation of the position
57
+
58
+ std::ostream& operator<<(std::ostream& os, const Position& pos) {
59
+
60
+ os << "\n +---+---+---+---+---+---+---+---+\n";
61
+
62
+ for (Rank r = RANK_8; r >= RANK_1; --r)
63
+ {
64
+ for (File f = FILE_A; f <= FILE_H; ++f)
65
+ os << " | " << PieceToChar[pos.piece_on(make_square(f, r))];
66
+
67
+ os << " | " << (1 + r) << "\n +---+---+---+---+---+---+---+---+\n";
68
+ }
69
+
70
+ os << " a b c d e f g h\n"
71
+ << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase
72
+ << std::setfill('0') << std::setw(16) << pos.key()
73
+ << std::setfill(' ') << std::dec << "\nCheckers: ";
74
+
75
+ for (Bitboard b = pos.checkers(); b; )
76
+ os << UCI::square(pop_lsb(b)) << " ";
77
+
78
+ if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces())
79
+ && !pos.can_castle(ANY_CASTLING))
80
+ {
81
+ StateInfo st;
82
+ ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize);
83
+
84
+ Position p;
85
+ p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread());
86
+ Tablebases::ProbeState s1, s2;
87
+ Tablebases::WDLScore wdl = Tablebases::probe_wdl(p, &s1);
88
+ int dtz = Tablebases::probe_dtz(p, &s2);
89
+ os << "\nTablebases WDL: " << std::setw(4) << wdl << " (" << s1 << ")"
90
+ << "\nTablebases DTZ: " << std::setw(4) << dtz << " (" << s2 << ")";
91
+ }
92
+
93
+ return os;
94
+ }
95
+
96
+
97
+ // Marcel van Kervinck's cuckoo algorithm for fast detection of "upcoming repetition"
98
+ // situations. Description of the algorithm in the following paper:
99
+ // https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
100
+
101
+ // First and second hash functions for indexing the cuckoo tables
102
+ inline int H1(Key h) { return h & 0x1fff; }
103
+ inline int H2(Key h) { return (h >> 16) & 0x1fff; }
104
+
105
+ // Cuckoo tables with Zobrist hashes of valid reversible moves, and the moves themselves
106
+ Key cuckoo[8192];
107
+ Move cuckooMove[8192];
108
+
109
+
110
+ /// Position::init() initializes at startup the various arrays used to compute hash keys
111
+
112
+ void Position::init() {
113
+
114
+ PRNG rng(1070372);
115
+
116
+ for (Piece pc : Pieces)
117
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
118
+ Zobrist::psq[pc][s] = rng.rand<Key>();
119
+
120
+ for (File f = FILE_A; f <= FILE_H; ++f)
121
+ Zobrist::enpassant[f] = rng.rand<Key>();
122
+
123
+ for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr)
124
+ Zobrist::castling[cr] = rng.rand<Key>();
125
+
126
+ Zobrist::side = rng.rand<Key>();
127
+ Zobrist::noPawns = rng.rand<Key>();
128
+
129
+ // Prepare the cuckoo tables
130
+ std::memset(cuckoo, 0, sizeof(cuckoo));
131
+ std::memset(cuckooMove, 0, sizeof(cuckooMove));
132
+ [[maybe_unused]] int count = 0;
133
+ for (Piece pc : Pieces)
134
+ for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
135
+ for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2)
136
+ if ((type_of(pc) != PAWN) && (attacks_bb(type_of(pc), s1, 0) & s2))
137
+ {
138
+ Move move = make_move(s1, s2);
139
+ Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side;
140
+ int i = H1(key);
141
+ while (true)
142
+ {
143
+ std::swap(cuckoo[i], key);
144
+ std::swap(cuckooMove[i], move);
145
+ if (move == MOVE_NONE) // Arrived at empty slot?
146
+ break;
147
+ i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot
148
+ }
149
+ count++;
150
+ }
151
+ assert(count == 3668);
152
+ }
153
+
154
+
155
+ /// Position::set() initializes the position object with the given FEN string.
156
+ /// This function is not very robust - make sure that input FENs are correct,
157
+ /// this is assumed to be the responsibility of the GUI.
158
+
159
+ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Thread* th) {
160
+ /*
161
+ A FEN string defines a particular position using only the ASCII character set.
162
+
163
+ A FEN string contains six fields separated by a space. The fields are:
164
+
165
+ 1) Piece placement (from white's perspective). Each rank is described, starting
166
+ with rank 8 and ending with rank 1. Within each rank, the contents of each
167
+ square are described from file A through file H. Following the Standard
168
+ Algebraic Notation (SAN), each piece is identified by a single letter taken
169
+ from the standard English names. White pieces are designated using upper-case
170
+ letters ("PNBRQK") whilst Black uses lowercase ("pnbrqk"). Blank squares are
171
+ noted using digits 1 through 8 (the number of blank squares), and "/"
172
+ separates ranks.
173
+
174
+ 2) Active color. "w" means white moves next, "b" means black.
175
+
176
+ 3) Castling availability. If neither side can castle, this is "-". Otherwise,
177
+ this has one or more letters: "K" (White can castle kingside), "Q" (White
178
+ can castle queenside), "k" (Black can castle kingside), and/or "q" (Black
179
+ can castle queenside).
180
+
181
+ 4) En passant target square (in algebraic notation). If there's no en passant
182
+ target square, this is "-". If a pawn has just made a 2-square move, this
183
+ is the position "behind" the pawn. Following X-FEN standard, this is recorded only
184
+ if there is a pawn in position to make an en passant capture, and if there really
185
+ is a pawn that might have advanced two squares.
186
+
187
+ 5) Halfmove clock. This is the number of halfmoves since the last pawn advance
188
+ or capture. This is used to determine if a draw can be claimed under the
189
+ fifty-move rule.
190
+
191
+ 6) Fullmove number. The number of the full move. It starts at 1, and is
192
+ incremented after Black's move.
193
+ */
194
+
195
+ unsigned char col, row, token;
196
+ size_t idx;
197
+ Square sq = SQ_A8;
198
+ std::istringstream ss(fenStr);
199
+
200
+ std::memset(this, 0, sizeof(Position));
201
+ std::memset(si, 0, sizeof(StateInfo));
202
+ st = si;
203
+
204
+ ss >> std::noskipws;
205
+
206
+ // 1. Piece placement
207
+ while ((ss >> token) && !isspace(token))
208
+ {
209
+ if (isdigit(token))
210
+ sq += (token - '0') * EAST; // Advance the given number of files
211
+
212
+ else if (token == '/')
213
+ sq += 2 * SOUTH;
214
+
215
+ else if ((idx = PieceToChar.find(token)) != string::npos) {
216
+ put_piece(Piece(idx), sq);
217
+ ++sq;
218
+ }
219
+ }
220
+
221
+ // 2. Active color
222
+ ss >> token;
223
+ sideToMove = (token == 'w' ? WHITE : BLACK);
224
+ ss >> token;
225
+
226
+ // 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
227
+ // Shredder-FEN that uses the letters of the columns on which the rooks began
228
+ // the game instead of KQkq and also X-FEN standard that, in case of Chess960,
229
+ // if an inner rook is associated with the castling right, the castling tag is
230
+ // replaced by the file letter of the involved rook, as for the Shredder-FEN.
231
+ while ((ss >> token) && !isspace(token))
232
+ {
233
+ Square rsq;
234
+ Color c = islower(token) ? BLACK : WHITE;
235
+ Piece rook = make_piece(c, ROOK);
236
+
237
+ token = char(toupper(token));
238
+
239
+ if (token == 'K')
240
+ for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) {}
241
+
242
+ else if (token == 'Q')
243
+ for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) {}
244
+
245
+ else if (token >= 'A' && token <= 'H')
246
+ rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
247
+
248
+ else
249
+ continue;
250
+
251
+ set_castling_right(c, rsq);
252
+ }
253
+
254
+ // 4. En passant square.
255
+ // Ignore if square is invalid or not on side to move relative rank 6.
256
+ bool enpassant = false;
257
+
258
+ if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
259
+ && ((ss >> row) && (row == (sideToMove == WHITE ? '6' : '3'))))
260
+ {
261
+ st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
262
+
263
+ // En passant square will be considered only if
264
+ // a) side to move have a pawn threatening epSquare
265
+ // b) there is an enemy pawn in front of epSquare
266
+ // c) there is no piece on epSquare or behind epSquare
267
+ enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
268
+ && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
269
+ && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
270
+ }
271
+
272
+ if (!enpassant)
273
+ st->epSquare = SQ_NONE;
274
+
275
+ // 5-6. Halfmove clock and fullmove number
276
+ ss >> std::skipws >> st->rule50 >> gamePly;
277
+
278
+ // Convert from fullmove starting from 1 to gamePly starting from 0,
279
+ // handle also common incorrect FEN with fullmove = 0.
280
+ gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
281
+
282
+ chess960 = isChess960;
283
+ thisThread = th;
284
+ set_state(st);
285
+
286
+ assert(pos_is_ok());
287
+
288
+ return *this;
289
+ }
290
+
291
+
292
+ /// Position::set_castling_right() is a helper function used to set castling
293
+ /// rights given the corresponding color and the rook starting square.
294
+
295
+ void Position::set_castling_right(Color c, Square rfrom) {
296
+
297
+ Square kfrom = square<KING>(c);
298
+ CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE);
299
+
300
+ st->castlingRights |= cr;
301
+ castlingRightsMask[kfrom] |= cr;
302
+ castlingRightsMask[rfrom] |= cr;
303
+ castlingRookSquare[cr] = rfrom;
304
+
305
+ Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1);
306
+ Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1);
307
+
308
+ castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto))
309
+ & ~(kfrom | rfrom);
310
+ }
311
+
312
+
313
+ /// Position::set_check_info() sets king attacks to detect if a move gives check
314
+
315
+ void Position::set_check_info(StateInfo* si) const {
316
+
317
+ si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square<KING>(WHITE), si->pinners[BLACK]);
318
+ si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square<KING>(BLACK), si->pinners[WHITE]);
319
+
320
+ Square ksq = square<KING>(~sideToMove);
321
+
322
+ si->checkSquares[PAWN] = pawn_attacks_bb(~sideToMove, ksq);
323
+ si->checkSquares[KNIGHT] = attacks_bb<KNIGHT>(ksq);
324
+ si->checkSquares[BISHOP] = attacks_bb<BISHOP>(ksq, pieces());
325
+ si->checkSquares[ROOK] = attacks_bb<ROOK>(ksq, pieces());
326
+ si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK];
327
+ si->checkSquares[KING] = 0;
328
+ }
329
+
330
+
331
+ /// Position::set_state() computes the hash keys of the position, and other
332
+ /// data that once computed is updated incrementally as moves are made.
333
+ /// The function is only used when a new position is set up, and to verify
334
+ /// the correctness of the StateInfo data when running in debug mode.
335
+
336
+ void Position::set_state(StateInfo* si) const {
337
+
338
+ si->key = si->materialKey = 0;
339
+ si->pawnKey = Zobrist::noPawns;
340
+ si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO;
341
+ si->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
342
+
343
+ set_check_info(si);
344
+
345
+ for (Bitboard b = pieces(); b; )
346
+ {
347
+ Square s = pop_lsb(b);
348
+ Piece pc = piece_on(s);
349
+ si->key ^= Zobrist::psq[pc][s];
350
+
351
+ if (type_of(pc) == PAWN)
352
+ si->pawnKey ^= Zobrist::psq[pc][s];
353
+
354
+ else if (type_of(pc) != KING)
355
+ si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc];
356
+ }
357
+
358
+ if (si->epSquare != SQ_NONE)
359
+ si->key ^= Zobrist::enpassant[file_of(si->epSquare)];
360
+
361
+ if (sideToMove == BLACK)
362
+ si->key ^= Zobrist::side;
363
+
364
+ si->key ^= Zobrist::castling[si->castlingRights];
365
+
366
+ for (Piece pc : Pieces)
367
+ for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
368
+ si->materialKey ^= Zobrist::psq[pc][cnt];
369
+ }
370
+
371
+
372
+ /// Position::set() is an overload to initialize the position object with
373
+ /// the given endgame code string like "KBPKN". It is mainly a helper to
374
+ /// get the material key out of an endgame code.
375
+
376
+ Position& Position::set(const string& code, Color c, StateInfo* si) {
377
+
378
+ assert(code[0] == 'K');
379
+
380
+ string sides[] = { code.substr(code.find('K', 1)), // Weak
381
+ code.substr(0, std::min(code.find('v'), code.find('K', 1))) }; // Strong
382
+
383
+ assert(sides[0].length() > 0 && sides[0].length() < 8);
384
+ assert(sides[1].length() > 0 && sides[1].length() < 8);
385
+
386
+ std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
387
+
388
+ string fenStr = "8/" + sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/"
389
+ + sides[1] + char(8 - sides[1].length() + '0') + "/8 w - - 0 10";
390
+
391
+ return set(fenStr, false, si, nullptr);
392
+ }
393
+
394
+
395
+ /// Position::fen() returns a FEN representation of the position. In case of
396
+ /// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
397
+
398
+ string Position::fen() const {
399
+
400
+ int emptyCnt;
401
+ std::ostringstream ss;
402
+
403
+ for (Rank r = RANK_8; r >= RANK_1; --r)
404
+ {
405
+ for (File f = FILE_A; f <= FILE_H; ++f)
406
+ {
407
+ for (emptyCnt = 0; f <= FILE_H && empty(make_square(f, r)); ++f)
408
+ ++emptyCnt;
409
+
410
+ if (emptyCnt)
411
+ ss << emptyCnt;
412
+
413
+ if (f <= FILE_H)
414
+ ss << PieceToChar[piece_on(make_square(f, r))];
415
+ }
416
+
417
+ if (r > RANK_1)
418
+ ss << '/';
419
+ }
420
+
421
+ ss << (sideToMove == WHITE ? " w " : " b ");
422
+
423
+ if (can_castle(WHITE_OO))
424
+ ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OO ))) : 'K');
425
+
426
+ if (can_castle(WHITE_OOO))
427
+ ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OOO))) : 'Q');
428
+
429
+ if (can_castle(BLACK_OO))
430
+ ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OO ))) : 'k');
431
+
432
+ if (can_castle(BLACK_OOO))
433
+ ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OOO))) : 'q');
434
+
435
+ if (!can_castle(ANY_CASTLING))
436
+ ss << '-';
437
+
438
+ ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ")
439
+ << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2;
440
+
441
+ return ss.str();
442
+ }
443
+
444
+
445
+ /// Position::slider_blockers() returns a bitboard of all the pieces (both colors)
446
+ /// that are blocking attacks on the square 's' from 'sliders'. A piece blocks a
447
+ /// slider if removing that piece from the board would result in a position where
448
+ /// square 's' is attacked. For example, a king-attack blocking piece can be either
449
+ /// a pinned or a discovered check piece, according if its color is the opposite
450
+ /// or the same of the color of the slider.
451
+
452
+ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const {
453
+
454
+ Bitboard blockers = 0;
455
+ pinners = 0;
456
+
457
+ // Snipers are sliders that attack 's' when a piece and other snipers are removed
458
+ Bitboard snipers = ( (attacks_bb< ROOK>(s) & pieces(QUEEN, ROOK))
459
+ | (attacks_bb<BISHOP>(s) & pieces(QUEEN, BISHOP))) & sliders;
460
+ Bitboard occupancy = pieces() ^ snipers;
461
+
462
+ while (snipers)
463
+ {
464
+ Square sniperSq = pop_lsb(snipers);
465
+ Bitboard b = between_bb(s, sniperSq) & occupancy;
466
+
467
+ if (b && !more_than_one(b))
468
+ {
469
+ blockers |= b;
470
+ if (b & pieces(color_of(piece_on(s))))
471
+ pinners |= sniperSq;
472
+ }
473
+ }
474
+ return blockers;
475
+ }
476
+
477
+
478
+ /// Position::attackers_to() computes a bitboard of all pieces which attack a
479
+ /// given square. Slider attacks use the occupied bitboard to indicate occupancy.
480
+
481
+ Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
482
+
483
+ return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN))
484
+ | (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN))
485
+ | (attacks_bb<KNIGHT>(s) & pieces(KNIGHT))
486
+ | (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN))
487
+ | (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
488
+ | (attacks_bb<KING>(s) & pieces(KING));
489
+ }
490
+
491
+
492
+ /// Position::legal() tests whether a pseudo-legal move is legal
493
+
494
+ bool Position::legal(Move m) const {
495
+
496
+ assert(is_ok(m));
497
+
498
+ Color us = sideToMove;
499
+ Square from = from_sq(m);
500
+ Square to = to_sq(m);
501
+
502
+ assert(color_of(moved_piece(m)) == us);
503
+ assert(piece_on(square<KING>(us)) == make_piece(us, KING));
504
+
505
+ // En passant captures are a tricky special case. Because they are rather
506
+ // uncommon, we do it simply by testing whether the king is attacked after
507
+ // the move is made.
508
+ if (type_of(m) == EN_PASSANT)
509
+ {
510
+ Square ksq = square<KING>(us);
511
+ Square capsq = to - pawn_push(us);
512
+ Bitboard occupied = (pieces() ^ from ^ capsq) | to;
513
+
514
+ assert(to == ep_square());
515
+ assert(moved_piece(m) == make_piece(us, PAWN));
516
+ assert(piece_on(capsq) == make_piece(~us, PAWN));
517
+ assert(piece_on(to) == NO_PIECE);
518
+
519
+ return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK))
520
+ && !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
521
+ }
522
+
523
+ // Castling moves generation does not check if the castling path is clear of
524
+ // enemy attacks, it is delayed at a later time: now!
525
+ if (type_of(m) == CASTLING)
526
+ {
527
+ // After castling, the rook and king final positions are the same in
528
+ // Chess960 as they would be in standard chess.
529
+ to = relative_square(us, to > from ? SQ_G1 : SQ_C1);
530
+ Direction step = to > from ? WEST : EAST;
531
+
532
+ for (Square s = to; s != from; s += step)
533
+ if (attackers_to(s) & pieces(~us))
534
+ return false;
535
+
536
+ // In case of Chess960, verify if the Rook blocks some checks
537
+ // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
538
+ return !chess960 || !(blockers_for_king(us) & to_sq(m));
539
+ }
540
+
541
+ // If the moving piece is a king, check whether the destination square is
542
+ // attacked by the opponent.
543
+ if (type_of(piece_on(from)) == KING)
544
+ return !(attackers_to(to, pieces() ^ from) & pieces(~us));
545
+
546
+ // A non-king move is legal if and only if it is not pinned or it
547
+ // is moving along the ray towards or away from the king.
548
+ return !(blockers_for_king(us) & from)
549
+ || aligned(from, to, square<KING>(us));
550
+ }
551
+
552
+
553
+ /// Position::pseudo_legal() takes a random move and tests whether the move is
554
+ /// pseudo legal. It is used to validate moves from TT that can be corrupted
555
+ /// due to SMP concurrent access or hash position key aliasing.
556
+
557
+ bool Position::pseudo_legal(const Move m) const {
558
+
559
+ Color us = sideToMove;
560
+ Square from = from_sq(m);
561
+ Square to = to_sq(m);
562
+ Piece pc = moved_piece(m);
563
+
564
+ // Use a slower but simpler function for uncommon cases
565
+ // yet we skip the legality check of MoveList<LEGAL>().
566
+ if (type_of(m) != NORMAL)
567
+ return checkers() ? MoveList< EVASIONS>(*this).contains(m)
568
+ : MoveList<NON_EVASIONS>(*this).contains(m);
569
+
570
+ // Is not a promotion, so promotion piece must be empty
571
+ if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE)
572
+ return false;
573
+
574
+ // If the 'from' square is not occupied by a piece belonging to the side to
575
+ // move, the move is obviously not legal.
576
+ if (pc == NO_PIECE || color_of(pc) != us)
577
+ return false;
578
+
579
+ // The destination square cannot be occupied by a friendly piece
580
+ if (pieces(us) & to)
581
+ return false;
582
+
583
+ // Handle the special case of a pawn move
584
+ if (type_of(pc) == PAWN)
585
+ {
586
+ // We have already handled promotion moves, so destination
587
+ // cannot be on the 8th/1st rank.
588
+ if ((Rank8BB | Rank1BB) & to)
589
+ return false;
590
+
591
+ if ( !(pawn_attacks_bb(us, from) & pieces(~us) & to) // Not a capture
592
+ && !((from + pawn_push(us) == to) && empty(to)) // Not a single push
593
+ && !( (from + 2 * pawn_push(us) == to) // Not a double push
594
+ && (relative_rank(us, from) == RANK_2)
595
+ && empty(to)
596
+ && empty(to - pawn_push(us))))
597
+ return false;
598
+ }
599
+ else if (!(attacks_bb(type_of(pc), from, pieces()) & to))
600
+ return false;
601
+
602
+ // Evasions generator already takes care to avoid some kind of illegal moves
603
+ // and legal() relies on this. We therefore have to take care that the same
604
+ // kind of moves are filtered out here.
605
+ if (checkers())
606
+ {
607
+ if (type_of(pc) != KING)
608
+ {
609
+ // Double check? In this case a king move is required
610
+ if (more_than_one(checkers()))
611
+ return false;
612
+
613
+ // Our move must be a blocking interposition or a capture of the checking piece
614
+ if (!(between_bb(square<KING>(us), lsb(checkers())) & to))
615
+ return false;
616
+ }
617
+ // In case of king moves under check we have to remove king so as to catch
618
+ // invalid moves like b1a1 when opposite queen is on c1.
619
+ else if (attackers_to(to, pieces() ^ from) & pieces(~us))
620
+ return false;
621
+ }
622
+
623
+ return true;
624
+ }
625
+
626
+
627
+ /// Position::gives_check() tests whether a pseudo-legal move gives a check
628
+
629
+ bool Position::gives_check(Move m) const {
630
+
631
+ assert(is_ok(m));
632
+ assert(color_of(moved_piece(m)) == sideToMove);
633
+
634
+ Square from = from_sq(m);
635
+ Square to = to_sq(m);
636
+
637
+ // Is there a direct check?
638
+ if (check_squares(type_of(piece_on(from))) & to)
639
+ return true;
640
+
641
+ // Is there a discovered check?
642
+ if ( (blockers_for_king(~sideToMove) & from)
643
+ && !aligned(from, to, square<KING>(~sideToMove)))
644
+ return true;
645
+
646
+ switch (type_of(m))
647
+ {
648
+ case NORMAL:
649
+ return false;
650
+
651
+ case PROMOTION:
652
+ return attacks_bb(promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove);
653
+
654
+ // En passant capture with check? We have already handled the case
655
+ // of direct checks and ordinary discovered check, so the only case we
656
+ // need to handle is the unusual case of a discovered check through
657
+ // the captured pawn.
658
+ case EN_PASSANT:
659
+ {
660
+ Square capsq = make_square(file_of(to), rank_of(from));
661
+ Bitboard b = (pieces() ^ from ^ capsq) | to;
662
+
663
+ return (attacks_bb< ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK))
664
+ | (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP));
665
+ }
666
+ default: //CASTLING
667
+ {
668
+ // Castling is encoded as 'king captures the rook'
669
+ Square ksq = square<KING>(~sideToMove);
670
+ Square rto = relative_square(sideToMove, to > from ? SQ_F1 : SQ_D1);
671
+
672
+ return (attacks_bb<ROOK>(rto) & ksq)
673
+ && (attacks_bb<ROOK>(rto, pieces() ^ from ^ to) & ksq);
674
+ }
675
+ }
676
+ }
677
+
678
+
679
+ /// Position::do_move() makes a move, and saves all information necessary
680
+ /// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
681
+ /// moves should be filtered out before this function is called.
682
+
683
+ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
684
+
685
+ assert(is_ok(m));
686
+ assert(&newSt != st);
687
+
688
+ thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
689
+ Key k = st->key ^ Zobrist::side;
690
+
691
+ // Copy some fields of the old state to our new StateInfo object except the
692
+ // ones which are going to be recalculated from scratch anyway and then switch
693
+ // our state pointer to point to the new (ready to be updated) state.
694
+ std::memcpy(&newSt, st, offsetof(StateInfo, key));
695
+ newSt.previous = st;
696
+ st = &newSt;
697
+
698
+ // Increment ply counters. In particular, rule50 will be reset to zero later on
699
+ // in case of a capture or a pawn move.
700
+ ++gamePly;
701
+ ++st->rule50;
702
+ ++st->pliesFromNull;
703
+
704
+ // Used by NNUE
705
+ st->accumulator.computed[WHITE] = false;
706
+ st->accumulator.computed[BLACK] = false;
707
+ auto& dp = st->dirtyPiece;
708
+ dp.dirty_num = 1;
709
+
710
+ Color us = sideToMove;
711
+ Color them = ~us;
712
+ Square from = from_sq(m);
713
+ Square to = to_sq(m);
714
+ Piece pc = piece_on(from);
715
+ Piece captured = type_of(m) == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to);
716
+
717
+ assert(color_of(pc) == us);
718
+ assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us));
719
+ assert(type_of(captured) != KING);
720
+
721
+ if (type_of(m) == CASTLING)
722
+ {
723
+ assert(pc == make_piece(us, KING));
724
+ assert(captured == make_piece(us, ROOK));
725
+
726
+ Square rfrom, rto;
727
+ do_castling<true>(us, from, to, rfrom, rto);
728
+
729
+ k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
730
+ captured = NO_PIECE;
731
+ }
732
+
733
+ if (captured)
734
+ {
735
+ Square capsq = to;
736
+
737
+ // If the captured piece is a pawn, update pawn hash key, otherwise
738
+ // update non-pawn material.
739
+ if (type_of(captured) == PAWN)
740
+ {
741
+ if (type_of(m) == EN_PASSANT)
742
+ {
743
+ capsq -= pawn_push(us);
744
+
745
+ assert(pc == make_piece(us, PAWN));
746
+ assert(to == st->epSquare);
747
+ assert(relative_rank(us, to) == RANK_6);
748
+ assert(piece_on(to) == NO_PIECE);
749
+ assert(piece_on(capsq) == make_piece(them, PAWN));
750
+ }
751
+
752
+ st->pawnKey ^= Zobrist::psq[captured][capsq];
753
+ }
754
+ else
755
+ st->nonPawnMaterial[them] -= PieceValue[MG][captured];
756
+
757
+ if (Eval::useNNUE)
758
+ {
759
+ dp.dirty_num = 2; // 1 piece moved, 1 piece captured
760
+ dp.piece[1] = captured;
761
+ dp.from[1] = capsq;
762
+ dp.to[1] = SQ_NONE;
763
+ }
764
+
765
+ // Update board and piece lists
766
+ remove_piece(capsq);
767
+
768
+ if (type_of(m) == EN_PASSANT)
769
+ board[capsq] = NO_PIECE;
770
+
771
+ // Update material hash key and prefetch access to materialTable
772
+ k ^= Zobrist::psq[captured][capsq];
773
+ st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]];
774
+ prefetch(thisThread->materialTable[st->materialKey]);
775
+
776
+ // Reset rule 50 counter
777
+ st->rule50 = 0;
778
+ }
779
+
780
+ // Update hash key
781
+ k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
782
+
783
+ // Reset en passant square
784
+ if (st->epSquare != SQ_NONE)
785
+ {
786
+ k ^= Zobrist::enpassant[file_of(st->epSquare)];
787
+ st->epSquare = SQ_NONE;
788
+ }
789
+
790
+ // Update castling rights if needed
791
+ if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to]))
792
+ {
793
+ k ^= Zobrist::castling[st->castlingRights];
794
+ st->castlingRights &= ~(castlingRightsMask[from] | castlingRightsMask[to]);
795
+ k ^= Zobrist::castling[st->castlingRights];
796
+ }
797
+
798
+ // Move the piece. The tricky Chess960 castling is handled earlier
799
+ if (type_of(m) != CASTLING)
800
+ {
801
+ if (Eval::useNNUE)
802
+ {
803
+ dp.piece[0] = pc;
804
+ dp.from[0] = from;
805
+ dp.to[0] = to;
806
+ }
807
+
808
+ move_piece(from, to);
809
+ }
810
+
811
+ // If the moving piece is a pawn do some special extra work
812
+ if (type_of(pc) == PAWN)
813
+ {
814
+ // Set en passant square if the moved pawn can be captured
815
+ if ( (int(to) ^ int(from)) == 16
816
+ && (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN)))
817
+ {
818
+ st->epSquare = to - pawn_push(us);
819
+ k ^= Zobrist::enpassant[file_of(st->epSquare)];
820
+ }
821
+
822
+ else if (type_of(m) == PROMOTION)
823
+ {
824
+ Piece promotion = make_piece(us, promotion_type(m));
825
+
826
+ assert(relative_rank(us, to) == RANK_8);
827
+ assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
828
+
829
+ remove_piece(to);
830
+ put_piece(promotion, to);
831
+
832
+ if (Eval::useNNUE)
833
+ {
834
+ // Promoting pawn to SQ_NONE, promoted piece from SQ_NONE
835
+ dp.to[0] = SQ_NONE;
836
+ dp.piece[dp.dirty_num] = promotion;
837
+ dp.from[dp.dirty_num] = SQ_NONE;
838
+ dp.to[dp.dirty_num] = to;
839
+ dp.dirty_num++;
840
+ }
841
+
842
+ // Update hash keys
843
+ k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to];
844
+ st->pawnKey ^= Zobrist::psq[pc][to];
845
+ st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1]
846
+ ^ Zobrist::psq[pc][pieceCount[pc]];
847
+
848
+ // Update material
849
+ st->nonPawnMaterial[us] += PieceValue[MG][promotion];
850
+ }
851
+
852
+ // Update pawn hash key
853
+ st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
854
+
855
+ // Reset rule 50 draw counter
856
+ st->rule50 = 0;
857
+ }
858
+
859
+ // Set capture piece
860
+ st->capturedPiece = captured;
861
+
862
+ // Update the key with the final value
863
+ st->key = k;
864
+
865
+ // Calculate checkers bitboard (if move gives check)
866
+ st->checkersBB = givesCheck ? attackers_to(square<KING>(them)) & pieces(us) : 0;
867
+
868
+ sideToMove = ~sideToMove;
869
+
870
+ // Update king attacks used for fast check detection
871
+ set_check_info(st);
872
+
873
+ // Calculate the repetition info. It is the ply distance from the previous
874
+ // occurrence of the same position, negative in the 3-fold case, or zero
875
+ // if the position was not repeated.
876
+ st->repetition = 0;
877
+ int end = std::min(st->rule50, st->pliesFromNull);
878
+ if (end >= 4)
879
+ {
880
+ StateInfo* stp = st->previous->previous;
881
+ for (int i = 4; i <= end; i += 2)
882
+ {
883
+ stp = stp->previous->previous;
884
+ if (stp->key == st->key)
885
+ {
886
+ st->repetition = stp->repetition ? -i : i;
887
+ break;
888
+ }
889
+ }
890
+ }
891
+
892
+ assert(pos_is_ok());
893
+ }
894
+
895
+
896
+ /// Position::undo_move() unmakes a move. When it returns, the position should
897
+ /// be restored to exactly the same state as before the move was made.
898
+
899
+ void Position::undo_move(Move m) {
900
+
901
+ assert(is_ok(m));
902
+
903
+ sideToMove = ~sideToMove;
904
+
905
+ Color us = sideToMove;
906
+ Square from = from_sq(m);
907
+ Square to = to_sq(m);
908
+ Piece pc = piece_on(to);
909
+
910
+ assert(empty(from) || type_of(m) == CASTLING);
911
+ assert(type_of(st->capturedPiece) != KING);
912
+
913
+ if (type_of(m) == PROMOTION)
914
+ {
915
+ assert(relative_rank(us, to) == RANK_8);
916
+ assert(type_of(pc) == promotion_type(m));
917
+ assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN);
918
+
919
+ remove_piece(to);
920
+ pc = make_piece(us, PAWN);
921
+ put_piece(pc, to);
922
+ }
923
+
924
+ if (type_of(m) == CASTLING)
925
+ {
926
+ Square rfrom, rto;
927
+ do_castling<false>(us, from, to, rfrom, rto);
928
+ }
929
+ else
930
+ {
931
+ move_piece(to, from); // Put the piece back at the source square
932
+
933
+ if (st->capturedPiece)
934
+ {
935
+ Square capsq = to;
936
+
937
+ if (type_of(m) == EN_PASSANT)
938
+ {
939
+ capsq -= pawn_push(us);
940
+
941
+ assert(type_of(pc) == PAWN);
942
+ assert(to == st->previous->epSquare);
943
+ assert(relative_rank(us, to) == RANK_6);
944
+ assert(piece_on(capsq) == NO_PIECE);
945
+ assert(st->capturedPiece == make_piece(~us, PAWN));
946
+ }
947
+
948
+ put_piece(st->capturedPiece, capsq); // Restore the captured piece
949
+ }
950
+ }
951
+
952
+ // Finally point our state pointer back to the previous state
953
+ st = st->previous;
954
+ --gamePly;
955
+
956
+ assert(pos_is_ok());
957
+ }
958
+
959
+
960
+ /// Position::do_castling() is a helper used to do/undo a castling move. This
961
+ /// is a bit tricky in Chess960 where from/to squares can overlap.
962
+ template<bool Do>
963
+ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) {
964
+
965
+ bool kingSide = to > from;
966
+ rfrom = to; // Castling is encoded as "king captures friendly rook"
967
+ rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
968
+ to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
969
+
970
+ if (Do && Eval::useNNUE)
971
+ {
972
+ auto& dp = st->dirtyPiece;
973
+ dp.piece[0] = make_piece(us, KING);
974
+ dp.from[0] = from;
975
+ dp.to[0] = to;
976
+ dp.piece[1] = make_piece(us, ROOK);
977
+ dp.from[1] = rfrom;
978
+ dp.to[1] = rto;
979
+ dp.dirty_num = 2;
980
+ }
981
+
982
+ // Remove both pieces first since squares could overlap in Chess960
983
+ remove_piece(Do ? from : to);
984
+ remove_piece(Do ? rfrom : rto);
985
+ board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do this for us
986
+ put_piece(make_piece(us, KING), Do ? to : from);
987
+ put_piece(make_piece(us, ROOK), Do ? rto : rfrom);
988
+ }
989
+
990
+
991
+ /// Position::do_null_move() is used to do a "null move": it flips
992
+ /// the side to move without executing any move on the board.
993
+
994
+ void Position::do_null_move(StateInfo& newSt) {
995
+
996
+ assert(!checkers());
997
+ assert(&newSt != st);
998
+
999
+ std::memcpy(&newSt, st, offsetof(StateInfo, accumulator));
1000
+
1001
+ newSt.previous = st;
1002
+ st = &newSt;
1003
+
1004
+ st->dirtyPiece.dirty_num = 0;
1005
+ st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator()
1006
+ st->accumulator.computed[WHITE] = false;
1007
+ st->accumulator.computed[BLACK] = false;
1008
+
1009
+ if (st->epSquare != SQ_NONE)
1010
+ {
1011
+ st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
1012
+ st->epSquare = SQ_NONE;
1013
+ }
1014
+
1015
+ st->key ^= Zobrist::side;
1016
+ ++st->rule50;
1017
+ prefetch(TT.first_entry(key()));
1018
+
1019
+ st->pliesFromNull = 0;
1020
+
1021
+ sideToMove = ~sideToMove;
1022
+
1023
+ set_check_info(st);
1024
+
1025
+ st->repetition = 0;
1026
+
1027
+ assert(pos_is_ok());
1028
+ }
1029
+
1030
+
1031
+ /// Position::undo_null_move() must be used to undo a "null move"
1032
+
1033
+ void Position::undo_null_move() {
1034
+
1035
+ assert(!checkers());
1036
+
1037
+ st = st->previous;
1038
+ sideToMove = ~sideToMove;
1039
+ }
1040
+
1041
+
1042
+ /// Position::key_after() computes the new hash key after the given move. Needed
1043
+ /// for speculative prefetch. It doesn't recognize special moves like castling,
1044
+ /// en passant and promotions.
1045
+
1046
+ Key Position::key_after(Move m) const {
1047
+
1048
+ Square from = from_sq(m);
1049
+ Square to = to_sq(m);
1050
+ Piece pc = piece_on(from);
1051
+ Piece captured = piece_on(to);
1052
+ Key k = st->key ^ Zobrist::side;
1053
+
1054
+ if (captured)
1055
+ k ^= Zobrist::psq[captured][to];
1056
+
1057
+ k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
1058
+
1059
+ return (captured || type_of(pc) == PAWN)
1060
+ ? k : adjust_key50<true>(k);
1061
+ }
1062
+
1063
+
1064
+ /// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the
1065
+ /// SEE value of move is greater or equal to the given threshold. We'll use an
1066
+ /// algorithm similar to alpha-beta pruning with a null window.
1067
+
1068
+ bool Position::see_ge(Move m, Value threshold) const {
1069
+
1070
+ assert(is_ok(m));
1071
+
1072
+ // Only deal with normal moves, assume others pass a simple SEE
1073
+ if (type_of(m) != NORMAL)
1074
+ return VALUE_ZERO >= threshold;
1075
+
1076
+ Square from = from_sq(m), to = to_sq(m);
1077
+
1078
+ int swap = PieceValue[MG][piece_on(to)] - threshold;
1079
+ if (swap < 0)
1080
+ return false;
1081
+
1082
+ swap = PieceValue[MG][piece_on(from)] - swap;
1083
+ if (swap <= 0)
1084
+ return true;
1085
+
1086
+ assert(color_of(piece_on(from)) == sideToMove);
1087
+ Bitboard occupied = pieces() ^ from ^ to;
1088
+ Color stm = sideToMove;
1089
+ Bitboard attackers = attackers_to(to, occupied);
1090
+ Bitboard stmAttackers, bb;
1091
+ int res = 1;
1092
+
1093
+ while (true)
1094
+ {
1095
+ stm = ~stm;
1096
+ attackers &= occupied;
1097
+
1098
+ // If stm has no more attackers then give up: stm loses
1099
+ if (!(stmAttackers = attackers & pieces(stm)))
1100
+ break;
1101
+
1102
+ // Don't allow pinned pieces to attack as long as there are
1103
+ // pinners on their original square.
1104
+ if (pinners(~stm) & occupied)
1105
+ {
1106
+ stmAttackers &= ~blockers_for_king(stm);
1107
+
1108
+ if (!stmAttackers)
1109
+ break;
1110
+ }
1111
+
1112
+ res ^= 1;
1113
+
1114
+ // Locate and remove the next least valuable attacker, and add to
1115
+ // the bitboard 'attackers' any X-ray attackers behind it.
1116
+ if ((bb = stmAttackers & pieces(PAWN)))
1117
+ {
1118
+ if ((swap = PawnValueMg - swap) < res)
1119
+ break;
1120
+
1121
+ occupied ^= least_significant_square_bb(bb);
1122
+ attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
1123
+ }
1124
+
1125
+ else if ((bb = stmAttackers & pieces(KNIGHT)))
1126
+ {
1127
+ if ((swap = KnightValueMg - swap) < res)
1128
+ break;
1129
+
1130
+ occupied ^= least_significant_square_bb(bb);
1131
+ }
1132
+
1133
+ else if ((bb = stmAttackers & pieces(BISHOP)))
1134
+ {
1135
+ if ((swap = BishopValueMg - swap) < res)
1136
+ break;
1137
+
1138
+ occupied ^= least_significant_square_bb(bb);
1139
+ attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
1140
+ }
1141
+
1142
+ else if ((bb = stmAttackers & pieces(ROOK)))
1143
+ {
1144
+ if ((swap = RookValueMg - swap) < res)
1145
+ break;
1146
+
1147
+ occupied ^= least_significant_square_bb(bb);
1148
+ attackers |= attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN);
1149
+ }
1150
+
1151
+ else if ((bb = stmAttackers & pieces(QUEEN)))
1152
+ {
1153
+ if ((swap = QueenValueMg - swap) < res)
1154
+ break;
1155
+
1156
+ occupied ^= least_significant_square_bb(bb);
1157
+ attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
1158
+ | (attacks_bb<ROOK >(to, occupied) & pieces(ROOK , QUEEN));
1159
+ }
1160
+
1161
+ else // KING
1162
+ // If we "capture" with the king but opponent still has attackers,
1163
+ // reverse the result.
1164
+ return (attackers & ~pieces(stm)) ? res ^ 1 : res;
1165
+ }
1166
+
1167
+ return bool(res);
1168
+ }
1169
+
1170
+
1171
+ /// Position::is_draw() tests whether the position is drawn by 50-move rule
1172
+ /// or by repetition. It does not detect stalemates.
1173
+
1174
+ bool Position::is_draw(int ply) const {
1175
+
1176
+ if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
1177
+ return true;
1178
+
1179
+ // Return a draw score if a position repeats once earlier but strictly
1180
+ // after the root, or repeats twice before or at the root.
1181
+ return st->repetition && st->repetition < ply;
1182
+ }
1183
+
1184
+
1185
+ // Position::has_repeated() tests whether there has been at least one repetition
1186
+ // of positions since the last capture or pawn move.
1187
+
1188
+ bool Position::has_repeated() const {
1189
+
1190
+ StateInfo* stc = st;
1191
+ int end = std::min(st->rule50, st->pliesFromNull);
1192
+ while (end-- >= 4)
1193
+ {
1194
+ if (stc->repetition)
1195
+ return true;
1196
+
1197
+ stc = stc->previous;
1198
+ }
1199
+ return false;
1200
+ }
1201
+
1202
+
1203
+ /// Position::has_game_cycle() tests if the position has a move which draws by repetition,
1204
+ /// or an earlier position has a move that directly reaches the current position.
1205
+
1206
+ bool Position::has_game_cycle(int ply) const {
1207
+
1208
+ int j;
1209
+
1210
+ int end = std::min(st->rule50, st->pliesFromNull);
1211
+
1212
+ if (end < 3)
1213
+ return false;
1214
+
1215
+ Key originalKey = st->key;
1216
+ StateInfo* stp = st->previous;
1217
+
1218
+ for (int i = 3; i <= end; i += 2)
1219
+ {
1220
+ stp = stp->previous->previous;
1221
+
1222
+ Key moveKey = originalKey ^ stp->key;
1223
+ if ( (j = H1(moveKey), cuckoo[j] == moveKey)
1224
+ || (j = H2(moveKey), cuckoo[j] == moveKey))
1225
+ {
1226
+ Move move = cuckooMove[j];
1227
+ Square s1 = from_sq(move);
1228
+ Square s2 = to_sq(move);
1229
+
1230
+ if (!((between_bb(s1, s2) ^ s2) & pieces()))
1231
+ {
1232
+ if (ply > i)
1233
+ return true;
1234
+
1235
+ // For nodes before or at the root, check that the move is a
1236
+ // repetition rather than a move to the current position.
1237
+ // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in
1238
+ // the same location, so we have to select which square to check.
1239
+ if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move())
1240
+ continue;
1241
+
1242
+ // For repetitions before or at the root, require one more
1243
+ if (stp->repetition)
1244
+ return true;
1245
+ }
1246
+ }
1247
+ }
1248
+ return false;
1249
+ }
1250
+
1251
+
1252
+ /// Position::flip() flips position with the white and black sides reversed. This
1253
+ /// is only useful for debugging e.g. for finding evaluation symmetry bugs.
1254
+
1255
+ void Position::flip() {
1256
+
1257
+ string f, token;
1258
+ std::stringstream ss(fen());
1259
+
1260
+ for (Rank r = RANK_8; r >= RANK_1; --r) // Piece placement
1261
+ {
1262
+ std::getline(ss, token, r > RANK_1 ? '/' : ' ');
1263
+ f.insert(0, token + (f.empty() ? " " : "/"));
1264
+ }
1265
+
1266
+ ss >> token; // Active color
1267
+ f += (token == "w" ? "B " : "W "); // Will be lowercased later
1268
+
1269
+ ss >> token; // Castling availability
1270
+ f += token + " ";
1271
+
1272
+ std::transform(f.begin(), f.end(), f.begin(),
1273
+ [](char c) { return char(islower(c) ? toupper(c) : tolower(c)); });
1274
+
1275
+ ss >> token; // En passant square
1276
+ f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3"));
1277
+
1278
+ std::getline(ss, token); // Half and full moves
1279
+ f += token;
1280
+
1281
+ set(f, is_chess960(), st, this_thread());
1282
+
1283
+ assert(pos_is_ok());
1284
+ }
1285
+
1286
+
1287
+ /// Position::pos_is_ok() performs some consistency checks for the
1288
+ /// position object and raises an asserts if something wrong is detected.
1289
+ /// This is meant to be helpful when debugging.
1290
+
1291
+ bool Position::pos_is_ok() const {
1292
+
1293
+ constexpr bool Fast = true; // Quick (default) or full check?
1294
+
1295
+ if ( (sideToMove != WHITE && sideToMove != BLACK)
1296
+ || piece_on(square<KING>(WHITE)) != W_KING
1297
+ || piece_on(square<KING>(BLACK)) != B_KING
1298
+ || ( ep_square() != SQ_NONE
1299
+ && relative_rank(sideToMove, ep_square()) != RANK_6))
1300
+ assert(0 && "pos_is_ok: Default");
1301
+
1302
+ if (Fast)
1303
+ return true;
1304
+
1305
+ if ( pieceCount[W_KING] != 1
1306
+ || pieceCount[B_KING] != 1
1307
+ || attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove))
1308
+ assert(0 && "pos_is_ok: Kings");
1309
+
1310
+ if ( (pieces(PAWN) & (Rank1BB | Rank8BB))
1311
+ || pieceCount[W_PAWN] > 8
1312
+ || pieceCount[B_PAWN] > 8)
1313
+ assert(0 && "pos_is_ok: Pawns");
1314
+
1315
+ if ( (pieces(WHITE) & pieces(BLACK))
1316
+ || (pieces(WHITE) | pieces(BLACK)) != pieces()
1317
+ || popcount(pieces(WHITE)) > 16
1318
+ || popcount(pieces(BLACK)) > 16)
1319
+ assert(0 && "pos_is_ok: Bitboards");
1320
+
1321
+ for (PieceType p1 = PAWN; p1 <= KING; ++p1)
1322
+ for (PieceType p2 = PAWN; p2 <= KING; ++p2)
1323
+ if (p1 != p2 && (pieces(p1) & pieces(p2)))
1324
+ assert(0 && "pos_is_ok: Bitboards");
1325
+
1326
+ StateInfo si = *st;
1327
+ ASSERT_ALIGNED(&si, Eval::NNUE::CacheLineSize);
1328
+
1329
+ set_state(&si);
1330
+ if (std::memcmp(&si, st, sizeof(StateInfo)))
1331
+ assert(0 && "pos_is_ok: State");
1332
+
1333
+ for (Piece pc : Pieces)
1334
+ if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
1335
+ || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
1336
+ assert(0 && "pos_is_ok: Pieces");
1337
+
1338
+ for (Color c : { WHITE, BLACK })
1339
+ for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE})
1340
+ {
1341
+ if (!can_castle(cr))
1342
+ continue;
1343
+
1344
+ if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK)
1345
+ || castlingRightsMask[castlingRookSquare[cr]] != cr
1346
+ || (castlingRightsMask[square<KING>(c)] & cr) != cr)
1347
+ assert(0 && "pos_is_ok: Castling");
1348
+ }
1349
+
1350
+ return true;
1351
+ }
1352
+
1353
+ } // namespace Stockfish
src/position.h ADDED
@@ -0,0 +1,443 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef POSITION_H_INCLUDED
20
+ #define POSITION_H_INCLUDED
21
+
22
+ #include <cassert>
23
+ #include <deque>
24
+ #include <memory> // For std::unique_ptr
25
+ #include <string>
26
+
27
+ #include "bitboard.h"
28
+ #include "evaluate.h"
29
+ #include "psqt.h"
30
+ #include "types.h"
31
+
32
+ #include "nnue/nnue_accumulator.h"
33
+
34
+ namespace Stockfish {
35
+
36
+ /// StateInfo struct stores information needed to restore a Position object to
37
+ /// its previous state when we retract a move. Whenever a move is made on the
38
+ /// board (by calling Position::do_move), a StateInfo object must be passed.
39
+
40
+ struct StateInfo {
41
+
42
+ // Copied when making a move
43
+ Key pawnKey;
44
+ Key materialKey;
45
+ Value nonPawnMaterial[COLOR_NB];
46
+ int castlingRights;
47
+ int rule50;
48
+ int pliesFromNull;
49
+ Square epSquare;
50
+
51
+ // Not copied when making a move (will be recomputed anyhow)
52
+ Key key;
53
+ Bitboard checkersBB;
54
+ StateInfo* previous;
55
+ Bitboard blockersForKing[COLOR_NB];
56
+ Bitboard pinners[COLOR_NB];
57
+ Bitboard checkSquares[PIECE_TYPE_NB];
58
+ Piece capturedPiece;
59
+ int repetition;
60
+
61
+ // Used by NNUE
62
+ Eval::NNUE::Accumulator accumulator;
63
+ DirtyPiece dirtyPiece;
64
+ };
65
+
66
+
67
+ /// A list to keep track of the position states along the setup moves (from the
68
+ /// start position to the position just before the search starts). Needed by
69
+ /// 'draw by repetition' detection. Use a std::deque because pointers to
70
+ /// elements are not invalidated upon list resizing.
71
+ typedef std::unique_ptr<std::deque<StateInfo>> StateListPtr;
72
+
73
+
74
+ /// Position class stores information regarding the board representation as
75
+ /// pieces, side to move, hash keys, castling info, etc. Important methods are
76
+ /// do_move() and undo_move(), used by the search to update node info when
77
+ /// traversing the search tree.
78
+ class Thread;
79
+
80
+ class Position {
81
+ public:
82
+ static void init();
83
+
84
+ Position() = default;
85
+ Position(const Position&) = delete;
86
+ Position& operator=(const Position&) = delete;
87
+
88
+ // FEN string input/output
89
+ Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th);
90
+ Position& set(const std::string& code, Color c, StateInfo* si);
91
+ std::string fen() const;
92
+
93
+ // Position representation
94
+ Bitboard pieces(PieceType pt) const;
95
+ Bitboard pieces(PieceType pt1, PieceType pt2) const;
96
+ Bitboard pieces(Color c) const;
97
+ Bitboard pieces(Color c, PieceType pt) const;
98
+ Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const;
99
+ Piece piece_on(Square s) const;
100
+ Square ep_square() const;
101
+ bool empty(Square s) const;
102
+ template<PieceType Pt> int count(Color c) const;
103
+ template<PieceType Pt> int count() const;
104
+ template<PieceType Pt> Square square(Color c) const;
105
+ bool is_on_semiopen_file(Color c, Square s) const;
106
+
107
+ // Castling
108
+ CastlingRights castling_rights(Color c) const;
109
+ bool can_castle(CastlingRights cr) const;
110
+ bool castling_impeded(CastlingRights cr) const;
111
+ Square castling_rook_square(CastlingRights cr) const;
112
+
113
+ // Checking
114
+ Bitboard checkers() const;
115
+ Bitboard blockers_for_king(Color c) const;
116
+ Bitboard check_squares(PieceType pt) const;
117
+ Bitboard pinners(Color c) const;
118
+
119
+ // Attacks to/from a given square
120
+ Bitboard attackers_to(Square s) const;
121
+ Bitboard attackers_to(Square s, Bitboard occupied) const;
122
+ Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const;
123
+ template<PieceType Pt> Bitboard attacks_by(Color c) const;
124
+
125
+ // Properties of moves
126
+ bool legal(Move m) const;
127
+ bool pseudo_legal(const Move m) const;
128
+ bool capture(Move m) const;
129
+ bool gives_check(Move m) const;
130
+ Piece moved_piece(Move m) const;
131
+ Piece captured_piece() const;
132
+
133
+ // Piece specific
134
+ bool pawn_passed(Color c, Square s) const;
135
+ bool opposite_bishops() const;
136
+ int pawns_on_same_color_squares(Color c, Square s) const;
137
+
138
+ // Doing and undoing moves
139
+ void do_move(Move m, StateInfo& newSt);
140
+ void do_move(Move m, StateInfo& newSt, bool givesCheck);
141
+ void undo_move(Move m);
142
+ void do_null_move(StateInfo& newSt);
143
+ void undo_null_move();
144
+
145
+ // Static Exchange Evaluation
146
+ bool see_ge(Move m, Value threshold = VALUE_ZERO) const;
147
+
148
+ // Accessing hash keys
149
+ Key key() const;
150
+ Key key_after(Move m) const;
151
+ Key material_key() const;
152
+ Key pawn_key() const;
153
+
154
+ // Other properties of the position
155
+ Color side_to_move() const;
156
+ int game_ply() const;
157
+ bool is_chess960() const;
158
+ Thread* this_thread() const;
159
+ bool is_draw(int ply) const;
160
+ bool has_game_cycle(int ply) const;
161
+ bool has_repeated() const;
162
+ int rule50_count() const;
163
+ Score psq_score() const;
164
+ Value psq_eg_stm() const;
165
+ Value non_pawn_material(Color c) const;
166
+ Value non_pawn_material() const;
167
+
168
+ // Position consistency check, for debugging
169
+ bool pos_is_ok() const;
170
+ void flip();
171
+
172
+ // Used by NNUE
173
+ StateInfo* state() const;
174
+
175
+ void put_piece(Piece pc, Square s);
176
+ void remove_piece(Square s);
177
+
178
+ private:
179
+ // Initialization helpers (used while setting up a position)
180
+ void set_castling_right(Color c, Square rfrom);
181
+ void set_state(StateInfo* si) const;
182
+ void set_check_info(StateInfo* si) const;
183
+
184
+ // Other helpers
185
+ void move_piece(Square from, Square to);
186
+ template<bool Do>
187
+ void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto);
188
+ template<bool AfterMove>
189
+ Key adjust_key50(Key k) const;
190
+
191
+ // Data members
192
+ Piece board[SQUARE_NB];
193
+ Bitboard byTypeBB[PIECE_TYPE_NB];
194
+ Bitboard byColorBB[COLOR_NB];
195
+ int pieceCount[PIECE_NB];
196
+ int castlingRightsMask[SQUARE_NB];
197
+ Square castlingRookSquare[CASTLING_RIGHT_NB];
198
+ Bitboard castlingPath[CASTLING_RIGHT_NB];
199
+ Thread* thisThread;
200
+ StateInfo* st;
201
+ int gamePly;
202
+ Color sideToMove;
203
+ Score psq;
204
+ bool chess960;
205
+ };
206
+
207
+ extern std::ostream& operator<<(std::ostream& os, const Position& pos);
208
+
209
+ inline Color Position::side_to_move() const {
210
+ return sideToMove;
211
+ }
212
+
213
+ inline Piece Position::piece_on(Square s) const {
214
+ assert(is_ok(s));
215
+ return board[s];
216
+ }
217
+
218
+ inline bool Position::empty(Square s) const {
219
+ return piece_on(s) == NO_PIECE;
220
+ }
221
+
222
+ inline Piece Position::moved_piece(Move m) const {
223
+ return piece_on(from_sq(m));
224
+ }
225
+
226
+ inline Bitboard Position::pieces(PieceType pt = ALL_PIECES) const {
227
+ return byTypeBB[pt];
228
+ }
229
+
230
+ inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const {
231
+ return pieces(pt1) | pieces(pt2);
232
+ }
233
+
234
+ inline Bitboard Position::pieces(Color c) const {
235
+ return byColorBB[c];
236
+ }
237
+
238
+ inline Bitboard Position::pieces(Color c, PieceType pt) const {
239
+ return pieces(c) & pieces(pt);
240
+ }
241
+
242
+ inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const {
243
+ return pieces(c) & (pieces(pt1) | pieces(pt2));
244
+ }
245
+
246
+ template<PieceType Pt> inline int Position::count(Color c) const {
247
+ return pieceCount[make_piece(c, Pt)];
248
+ }
249
+
250
+ template<PieceType Pt> inline int Position::count() const {
251
+ return count<Pt>(WHITE) + count<Pt>(BLACK);
252
+ }
253
+
254
+ template<PieceType Pt> inline Square Position::square(Color c) const {
255
+ assert(count<Pt>(c) == 1);
256
+ return lsb(pieces(c, Pt));
257
+ }
258
+
259
+ inline Square Position::ep_square() const {
260
+ return st->epSquare;
261
+ }
262
+
263
+ inline bool Position::is_on_semiopen_file(Color c, Square s) const {
264
+ return !(pieces(c, PAWN) & file_bb(s));
265
+ }
266
+
267
+ inline bool Position::can_castle(CastlingRights cr) const {
268
+ return st->castlingRights & cr;
269
+ }
270
+
271
+ inline CastlingRights Position::castling_rights(Color c) const {
272
+ return c & CastlingRights(st->castlingRights);
273
+ }
274
+
275
+ inline bool Position::castling_impeded(CastlingRights cr) const {
276
+ assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO);
277
+
278
+ return pieces() & castlingPath[cr];
279
+ }
280
+
281
+ inline Square Position::castling_rook_square(CastlingRights cr) const {
282
+ assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO);
283
+
284
+ return castlingRookSquare[cr];
285
+ }
286
+
287
+ inline Bitboard Position::attackers_to(Square s) const {
288
+ return attackers_to(s, pieces());
289
+ }
290
+
291
+ template<PieceType Pt>
292
+ inline Bitboard Position::attacks_by(Color c) const {
293
+
294
+ if constexpr (Pt == PAWN)
295
+ return c == WHITE ? pawn_attacks_bb<WHITE>(pieces(WHITE, PAWN))
296
+ : pawn_attacks_bb<BLACK>(pieces(BLACK, PAWN));
297
+ else
298
+ {
299
+ Bitboard threats = 0;
300
+ Bitboard attackers = pieces(c, Pt);
301
+ while (attackers)
302
+ threats |= attacks_bb<Pt>(pop_lsb(attackers), pieces());
303
+ return threats;
304
+ }
305
+ }
306
+
307
+ inline Bitboard Position::checkers() const {
308
+ return st->checkersBB;
309
+ }
310
+
311
+ inline Bitboard Position::blockers_for_king(Color c) const {
312
+ return st->blockersForKing[c];
313
+ }
314
+
315
+ inline Bitboard Position::pinners(Color c) const {
316
+ return st->pinners[c];
317
+ }
318
+
319
+ inline Bitboard Position::check_squares(PieceType pt) const {
320
+ return st->checkSquares[pt];
321
+ }
322
+
323
+ inline bool Position::pawn_passed(Color c, Square s) const {
324
+ return !(pieces(~c, PAWN) & passed_pawn_span(c, s));
325
+ }
326
+
327
+ inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
328
+ return popcount(pieces(c, PAWN) & ((DarkSquares & s) ? DarkSquares : ~DarkSquares));
329
+ }
330
+
331
+ inline Key Position::key() const {
332
+ return adjust_key50<false>(st->key);
333
+ }
334
+
335
+ template<bool AfterMove>
336
+ inline Key Position::adjust_key50(Key k) const
337
+ {
338
+ return st->rule50 < 14 - AfterMove
339
+ ? k : k ^ make_key((st->rule50 - (14 - AfterMove)) / 8);
340
+ }
341
+
342
+ inline Key Position::pawn_key() const {
343
+ return st->pawnKey;
344
+ }
345
+
346
+ inline Key Position::material_key() const {
347
+ return st->materialKey;
348
+ }
349
+
350
+ inline Score Position::psq_score() const {
351
+ return psq;
352
+ }
353
+
354
+ inline Value Position::psq_eg_stm() const {
355
+ return (sideToMove == WHITE ? 1 : -1) * eg_value(psq);
356
+ }
357
+
358
+ inline Value Position::non_pawn_material(Color c) const {
359
+ return st->nonPawnMaterial[c];
360
+ }
361
+
362
+ inline Value Position::non_pawn_material() const {
363
+ return non_pawn_material(WHITE) + non_pawn_material(BLACK);
364
+ }
365
+
366
+ inline int Position::game_ply() const {
367
+ return gamePly;
368
+ }
369
+
370
+ inline int Position::rule50_count() const {
371
+ return st->rule50;
372
+ }
373
+
374
+ inline bool Position::opposite_bishops() const {
375
+ return count<BISHOP>(WHITE) == 1
376
+ && count<BISHOP>(BLACK) == 1
377
+ && opposite_colors(square<BISHOP>(WHITE), square<BISHOP>(BLACK));
378
+ }
379
+
380
+ inline bool Position::is_chess960() const {
381
+ return chess960;
382
+ }
383
+
384
+ inline bool Position::capture(Move m) const {
385
+ assert(is_ok(m));
386
+ // Castling is encoded as "king captures rook"
387
+ return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == EN_PASSANT;
388
+ }
389
+
390
+ inline Piece Position::captured_piece() const {
391
+ return st->capturedPiece;
392
+ }
393
+
394
+ inline Thread* Position::this_thread() const {
395
+ return thisThread;
396
+ }
397
+
398
+ inline void Position::put_piece(Piece pc, Square s) {
399
+
400
+ board[s] = pc;
401
+ byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s;
402
+ byColorBB[color_of(pc)] |= s;
403
+ pieceCount[pc]++;
404
+ pieceCount[make_piece(color_of(pc), ALL_PIECES)]++;
405
+ psq += PSQT::psq[pc][s];
406
+ }
407
+
408
+ inline void Position::remove_piece(Square s) {
409
+
410
+ Piece pc = board[s];
411
+ byTypeBB[ALL_PIECES] ^= s;
412
+ byTypeBB[type_of(pc)] ^= s;
413
+ byColorBB[color_of(pc)] ^= s;
414
+ board[s] = NO_PIECE;
415
+ pieceCount[pc]--;
416
+ pieceCount[make_piece(color_of(pc), ALL_PIECES)]--;
417
+ psq -= PSQT::psq[pc][s];
418
+ }
419
+
420
+ inline void Position::move_piece(Square from, Square to) {
421
+
422
+ Piece pc = board[from];
423
+ Bitboard fromTo = from | to;
424
+ byTypeBB[ALL_PIECES] ^= fromTo;
425
+ byTypeBB[type_of(pc)] ^= fromTo;
426
+ byColorBB[color_of(pc)] ^= fromTo;
427
+ board[from] = NO_PIECE;
428
+ board[to] = pc;
429
+ psq += PSQT::psq[pc][to] - PSQT::psq[pc][from];
430
+ }
431
+
432
+ inline void Position::do_move(Move m, StateInfo& newSt) {
433
+ do_move(m, newSt, gives_check(m));
434
+ }
435
+
436
+ inline StateInfo* Position::state() const {
437
+
438
+ return st;
439
+ }
440
+
441
+ } // namespace Stockfish
442
+
443
+ #endif // #ifndef POSITION_H_INCLUDED
src/psqt.cpp ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+
20
+ #include "psqt.h"
21
+
22
+ #include <algorithm>
23
+
24
+ #include "bitboard.h"
25
+ #include "types.h"
26
+
27
+ namespace Stockfish {
28
+
29
+ namespace
30
+ {
31
+
32
+ auto constexpr S = make_score;
33
+
34
+ // 'Bonus' contains Piece-Square parameters.
35
+ // Scores are explicit for files A to D, implicitly mirrored for E to H.
36
+ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
37
+ { },
38
+ { },
39
+ { // Knight
40
+ { S(-175, -96), S(-92,-65), S(-74,-49), S(-73,-21) },
41
+ { S( -77, -67), S(-41,-54), S(-27,-18), S(-15, 8) },
42
+ { S( -61, -40), S(-17,-27), S( 6, -8), S( 12, 29) },
43
+ { S( -35, -35), S( 8, -2), S( 40, 13), S( 49, 28) },
44
+ { S( -34, -45), S( 13,-16), S( 44, 9), S( 51, 39) },
45
+ { S( -9, -51), S( 22,-44), S( 58,-16), S( 53, 17) },
46
+ { S( -67, -69), S(-27,-50), S( 4,-51), S( 37, 12) },
47
+ { S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) }
48
+ },
49
+ { // Bishop
50
+ { S(-37,-40), S(-4 ,-21), S( -6,-26), S(-16, -8) },
51
+ { S(-11,-26), S( 6, -9), S( 13,-12), S( 3, 1) },
52
+ { S(-5 ,-11), S( 15, -1), S( -4, -1), S( 12, 7) },
53
+ { S(-4 ,-14), S( 8, -4), S( 18, 0), S( 27, 12) },
54
+ { S(-8 ,-12), S( 20, -1), S( 15,-10), S( 22, 11) },
55
+ { S(-11,-21), S( 4, 4), S( 1, 3), S( 8, 4) },
56
+ { S(-12,-22), S(-10,-14), S( 4, -1), S( 0, 1) },
57
+ { S(-34,-32), S( 1,-29), S(-10,-26), S(-16,-17) }
58
+ },
59
+ { // Rook
60
+ { S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) },
61
+ { S(-21,-12), S(-13, -9), S( -8, -1), S( 6, -2) },
62
+ { S(-25, 6), S(-11, -8), S( -1, -2), S( 3, -6) },
63
+ { S(-13, -6), S( -5, 1), S( -4, -9), S(-6, 7) },
64
+ { S(-27, -5), S(-15, 8), S( -4, 7), S( 3, -6) },
65
+ { S(-22, 6), S( -2, 1), S( 6, -7), S(12, 10) },
66
+ { S( -2, 4), S( 12, 5), S( 16, 20), S(18, -5) },
67
+ { S(-17, 18), S(-19, 0), S( -1, 19), S( 9, 13) }
68
+ },
69
+ { // Queen
70
+ { S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) },
71
+ { S(-3,-54), S( 5,-31), S( 8,-22), S(12, -4) },
72
+ { S(-3,-39), S( 6,-18), S(13, -9), S( 7, 3) },
73
+ { S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) },
74
+ { S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) },
75
+ { S(-4,-38), S(10,-18), S( 6,-11), S( 8, 1) },
76
+ { S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) },
77
+ { S(-2,-74), S(-2,-52), S( 1,-43), S(-2,-34) }
78
+ },
79
+ { // King
80
+ { S(271, 1), S(327, 45), S(271, 85), S(198, 76) },
81
+ { S(278, 53), S(303,100), S(234,133), S(179,135) },
82
+ { S(195, 88), S(258,130), S(169,169), S(120,175) },
83
+ { S(164,103), S(190,156), S(138,172), S( 98,172) },
84
+ { S(154, 96), S(179,166), S(105,199), S( 70,199) },
85
+ { S(123, 92), S(145,172), S( 81,184), S( 31,191) },
86
+ { S( 88, 47), S(120,121), S( 65,116), S( 33,131) },
87
+ { S( 59, 11), S( 89, 59), S( 45, 73), S( -1, 78) }
88
+ }
89
+ };
90
+
91
+ constexpr Score PBonus[RANK_NB][FILE_NB] =
92
+ { // Pawn (asymmetric distribution)
93
+ { },
94
+ { S( 2, -8), S( 4, -6), S( 11, 9), S( 18, 5), S( 16, 16), S( 21, 6), S( 9, -6), S( -3,-18) },
95
+ { S( -9, -9), S(-15, -7), S( 11,-10), S( 15, 5), S( 31, 2), S( 23, 3), S( 6, -8), S(-20, -5) },
96
+ { S( -3, 7), S(-20, 1), S( 8, -8), S( 19, -2), S( 39,-14), S( 17,-13), S( 2,-11), S( -5, -6) },
97
+ { S( 11, 12), S( -4, 6), S(-11, 2), S( 2, -6), S( 11, -5), S( 0, -4), S(-12, 14), S( 5, 9) },
98
+ { S( 3, 27), S(-11, 18), S( -6, 19), S( 22, 29), S( -8, 30), S( -5, 9), S(-14, 8), S(-11, 14) },
99
+ { S( -7, -1), S( 6,-14), S( -2, 13), S(-11, 22), S( 4, 24), S(-14, 17), S( 10, 7), S( -9, 7) }
100
+ };
101
+
102
+ } // namespace
103
+
104
+
105
+ namespace PSQT
106
+ {
107
+
108
+ Score psq[PIECE_NB][SQUARE_NB];
109
+
110
+ // PSQT::init() initializes piece-square tables: the white halves of the tables are
111
+ // copied from Bonus[] and PBonus[], adding the piece value, then the black halves of
112
+ // the tables are initialized by flipping and changing the sign of the white scores.
113
+ void init() {
114
+
115
+ for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING})
116
+ {
117
+ Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
118
+
119
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
120
+ {
121
+ File f = File(edge_distance(file_of(s)));
122
+ psq[ pc][s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)]
123
+ : Bonus[pc][rank_of(s)][f]);
124
+ psq[~pc][flip_rank(s)] = -psq[pc][s];
125
+ }
126
+ }
127
+ }
128
+
129
+ } // namespace PSQT
130
+
131
+ } // namespace Stockfish
src/psqt.h ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+
20
+ #ifndef PSQT_H_INCLUDED
21
+ #define PSQT_H_INCLUDED
22
+
23
+
24
+ #include "types.h"
25
+
26
+
27
+ namespace Stockfish::PSQT
28
+ {
29
+
30
+ extern Score psq[PIECE_NB][SQUARE_NB];
31
+
32
+ // Fill psqt array from a set of internally linked parameters
33
+ extern void init();
34
+
35
+ } // namespace Stockfish::PSQT
36
+
37
+
38
+ #endif // PSQT_H_INCLUDED
src/search.cpp ADDED
@@ -0,0 +1,1959 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <algorithm>
20
+ #include <cassert>
21
+ #include <cmath>
22
+ #include <cstring> // For std::memset
23
+ #include <iostream>
24
+ #include <sstream>
25
+
26
+ #include "evaluate.h"
27
+ #include "misc.h"
28
+ #include "movegen.h"
29
+ #include "movepick.h"
30
+ #include "position.h"
31
+ #include "search.h"
32
+ #include "thread.h"
33
+ #include "timeman.h"
34
+ #include "tt.h"
35
+ #include "uci.h"
36
+ #include "syzygy/tbprobe.h"
37
+
38
+ namespace Stockfish {
39
+
40
+ namespace Search {
41
+
42
+ LimitsType Limits;
43
+ }
44
+
45
+ namespace Tablebases {
46
+
47
+ int Cardinality;
48
+ bool RootInTB;
49
+ bool UseRule50;
50
+ Depth ProbeDepth;
51
+ }
52
+
53
+ namespace TB = Tablebases;
54
+
55
+ using std::string;
56
+ using Eval::evaluate;
57
+ using namespace Search;
58
+
59
+ namespace {
60
+
61
+ // Different node types, used as a template parameter
62
+ enum NodeType { NonPV, PV, Root };
63
+
64
+ // Futility margin
65
+ Value futility_margin(Depth d, bool improving) {
66
+ return Value(165 * (d - improving));
67
+ }
68
+
69
+ // Reductions lookup table, initialized at startup
70
+ int Reductions[MAX_MOVES]; // [depth or moveNumber]
71
+
72
+ Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) {
73
+ int r = Reductions[d] * Reductions[mn];
74
+ return (r + 1642 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 916);
75
+ }
76
+
77
+ constexpr int futility_move_count(bool improving, Depth depth) {
78
+ return improving ? (3 + depth * depth)
79
+ : (3 + depth * depth) / 2;
80
+ }
81
+
82
+ // History and stats update bonus, based on depth
83
+ int stat_bonus(Depth d) {
84
+ return std::min((12 * d + 282) * d - 349 , 1594);
85
+ }
86
+
87
+ // Add a small random component to draw evaluations to avoid 3-fold blindness
88
+ Value value_draw(const Thread* thisThread) {
89
+ return VALUE_DRAW - 1 + Value(thisThread->nodes & 0x2);
90
+ }
91
+
92
+ // Skill structure is used to implement strength limit. If we have an uci_elo then
93
+ // we convert it to a suitable fractional skill level using anchoring to CCRL Elo
94
+ // (goldfish 1.13 = 2000) and a fit through Ordo derived Elo for match (TC 60+0.6)
95
+ // results spanning a wide range of k values.
96
+ struct Skill {
97
+ Skill(int skill_level, int uci_elo) {
98
+ if (uci_elo)
99
+ level = std::clamp(std::pow((uci_elo - 1346.6) / 143.4, 1 / 0.806), 0.0, 20.0);
100
+ else
101
+ level = double(skill_level);
102
+ }
103
+ bool enabled() const { return level < 20.0; }
104
+ bool time_to_pick(Depth depth) const { return depth == 1 + int(level); }
105
+ Move pick_best(size_t multiPV);
106
+
107
+ double level;
108
+ Move best = MOVE_NONE;
109
+ };
110
+
111
+ template <NodeType nodeType>
112
+ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
113
+
114
+ template <NodeType nodeType>
115
+ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = 0);
116
+
117
+ Value value_to_tt(Value v, int ply);
118
+ Value value_from_tt(Value v, int ply, int r50c);
119
+ void update_pv(Move* pv, Move move, const Move* childPv);
120
+ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus);
121
+ void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus);
122
+ void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq,
123
+ Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth);
124
+
125
+ // perft() is our utility to verify move generation. All the leaf nodes up
126
+ // to the given depth are generated and counted, and the sum is returned.
127
+ template<bool Root>
128
+ uint64_t perft(Position& pos, Depth depth) {
129
+
130
+ StateInfo st;
131
+ ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize);
132
+
133
+ uint64_t cnt, nodes = 0;
134
+ const bool leaf = (depth == 2);
135
+
136
+ for (const auto& m : MoveList<LEGAL>(pos))
137
+ {
138
+ if (Root && depth <= 1)
139
+ cnt = 1, nodes++;
140
+ else
141
+ {
142
+ pos.do_move(m, st);
143
+ cnt = leaf ? MoveList<LEGAL>(pos).size() : perft<false>(pos, depth - 1);
144
+ nodes += cnt;
145
+ pos.undo_move(m);
146
+ }
147
+ if (Root)
148
+ sync_cout << UCI::move(m, pos.is_chess960()) << ": " << cnt << sync_endl;
149
+ }
150
+ return nodes;
151
+ }
152
+
153
+ } // namespace
154
+
155
+
156
+ /// Search::init() is called at startup to initialize various lookup tables
157
+
158
+ void Search::init() {
159
+
160
+ for (int i = 1; i < MAX_MOVES; ++i)
161
+ Reductions[i] = int((20.26 + std::log(Threads.size()) / 2) * std::log(i));
162
+ }
163
+
164
+
165
+ /// Search::clear() resets search state to its initial value
166
+
167
+ void Search::clear() {
168
+
169
+ Threads.main()->wait_for_search_finished();
170
+
171
+ Time.availableNodes = 0;
172
+ TT.clear();
173
+ Threads.clear();
174
+ Tablebases::init(Options["SyzygyPath"]); // Free mapped files
175
+ }
176
+
177
+
178
+ /// MainThread::search() is started when the program receives the UCI 'go'
179
+ /// command. It searches from the root position and outputs the "bestmove".
180
+
181
+ void MainThread::search() {
182
+
183
+ if (Limits.perft)
184
+ {
185
+ nodes = perft<true>(rootPos, Limits.perft);
186
+ sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl;
187
+ return;
188
+ }
189
+
190
+ Color us = rootPos.side_to_move();
191
+ Time.init(Limits, us, rootPos.game_ply());
192
+ TT.new_search();
193
+
194
+ Eval::NNUE::verify();
195
+
196
+ if (rootMoves.empty())
197
+ {
198
+ rootMoves.emplace_back(MOVE_NONE);
199
+ sync_cout << "info depth 0 score "
200
+ << UCI::value(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW)
201
+ << sync_endl;
202
+ }
203
+ else
204
+ {
205
+ Threads.start_searching(); // start non-main threads
206
+ Thread::search(); // main thread start searching
207
+ }
208
+
209
+ // When we reach the maximum depth, we can arrive here without a raise of
210
+ // Threads.stop. However, if we are pondering or in an infinite search,
211
+ // the UCI protocol states that we shouldn't print the best move before the
212
+ // GUI sends a "stop" or "ponderhit" command. We therefore simply wait here
213
+ // until the GUI sends one of those commands.
214
+
215
+ while (!Threads.stop && (ponder || Limits.infinite))
216
+ {} // Busy wait for a stop or a ponder reset
217
+
218
+ // Stop the threads if not already stopped (also raise the stop if
219
+ // "ponderhit" just reset Threads.ponder).
220
+ Threads.stop = true;
221
+
222
+ // Wait until all threads have finished
223
+ Threads.wait_for_search_finished();
224
+
225
+ // When playing in 'nodes as time' mode, subtract the searched nodes from
226
+ // the available ones before exiting.
227
+ if (Limits.npmsec)
228
+ Time.availableNodes += Limits.inc[us] - Threads.nodes_searched();
229
+
230
+ Thread* bestThread = this;
231
+ Skill skill = Skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0);
232
+
233
+ if ( int(Options["MultiPV"]) == 1
234
+ && !Limits.depth
235
+ && !skill.enabled()
236
+ && rootMoves[0].pv[0] != MOVE_NONE)
237
+ bestThread = Threads.get_best_thread();
238
+
239
+ bestPreviousScore = bestThread->rootMoves[0].score;
240
+ bestPreviousAverageScore = bestThread->rootMoves[0].averageScore;
241
+
242
+ for (Thread* th : Threads)
243
+ th->previousDepth = bestThread->completedDepth;
244
+
245
+ // Send again PV info if we have a new best thread
246
+ if (bestThread != this)
247
+ sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth) << sync_endl;
248
+
249
+ sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960());
250
+
251
+ if (bestThread->rootMoves[0].pv.size() > 1 || bestThread->rootMoves[0].extract_ponder_from_tt(rootPos))
252
+ std::cout << " ponder " << UCI::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960());
253
+
254
+ std::cout << sync_endl;
255
+ }
256
+
257
+
258
+ /// Thread::search() is the main iterative deepening loop. It calls search()
259
+ /// repeatedly with increasing depth until the allocated thinking time has been
260
+ /// consumed, the user stops the search, or the maximum search depth is reached.
261
+
262
+ void Thread::search() {
263
+
264
+ // To allow access to (ss-7) up to (ss+2), the stack must be oversized.
265
+ // The former is needed to allow update_continuation_histories(ss-1, ...),
266
+ // which accesses its argument at ss-6, also near the root.
267
+ // The latter is needed for statScore and killer initialization.
268
+ Stack stack[MAX_PLY+10], *ss = stack+7;
269
+ Move pv[MAX_PLY+1];
270
+ Value alpha, beta, delta;
271
+ Move lastBestMove = MOVE_NONE;
272
+ Depth lastBestMoveDepth = 0;
273
+ MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr);
274
+ double timeReduction = 1, totBestMoveChanges = 0;
275
+ Color us = rootPos.side_to_move();
276
+ int iterIdx = 0;
277
+
278
+ std::memset(ss-7, 0, 10 * sizeof(Stack));
279
+ for (int i = 7; i > 0; i--)
280
+ (ss-i)->continuationHistory = &this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel
281
+
282
+ for (int i = 0; i <= MAX_PLY + 2; ++i)
283
+ (ss+i)->ply = i;
284
+
285
+ ss->pv = pv;
286
+
287
+ bestValue = delta = alpha = -VALUE_INFINITE;
288
+ beta = VALUE_INFINITE;
289
+
290
+ if (mainThread)
291
+ {
292
+ if (mainThread->bestPreviousScore == VALUE_INFINITE)
293
+ for (int i = 0; i < 4; ++i)
294
+ mainThread->iterValue[i] = VALUE_ZERO;
295
+ else
296
+ for (int i = 0; i < 4; ++i)
297
+ mainThread->iterValue[i] = mainThread->bestPreviousScore;
298
+ }
299
+
300
+ size_t multiPV = size_t(Options["MultiPV"]);
301
+ Skill skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0);
302
+
303
+ // When playing with strength handicap enable MultiPV search that we will
304
+ // use behind the scenes to retrieve a set of possible moves.
305
+ if (skill.enabled())
306
+ multiPV = std::max(multiPV, (size_t)4);
307
+
308
+ multiPV = std::min(multiPV, rootMoves.size());
309
+
310
+ complexityAverage.set(155, 1);
311
+
312
+ optimism[us] = optimism[~us] = VALUE_ZERO;
313
+
314
+ int searchAgainCounter = 0;
315
+
316
+ // Iterative deepening loop until requested to stop or the target depth is reached
317
+ while ( ++rootDepth < MAX_PLY
318
+ && !Threads.stop
319
+ && !(Limits.depth && mainThread && rootDepth > Limits.depth))
320
+ {
321
+ // Age out PV variability metric
322
+ if (mainThread)
323
+ totBestMoveChanges /= 2;
324
+
325
+ // Save the last iteration's scores before first PV line is searched and
326
+ // all the move scores except the (new) PV are set to -VALUE_INFINITE.
327
+ for (RootMove& rm : rootMoves)
328
+ rm.previousScore = rm.score;
329
+
330
+ size_t pvFirst = 0;
331
+ pvLast = 0;
332
+
333
+ if (!Threads.increaseDepth)
334
+ searchAgainCounter++;
335
+
336
+ // MultiPV loop. We perform a full root search for each PV line
337
+ for (pvIdx = 0; pvIdx < multiPV && !Threads.stop; ++pvIdx)
338
+ {
339
+ if (pvIdx == pvLast)
340
+ {
341
+ pvFirst = pvLast;
342
+ for (pvLast++; pvLast < rootMoves.size(); pvLast++)
343
+ if (rootMoves[pvLast].tbRank != rootMoves[pvFirst].tbRank)
344
+ break;
345
+ }
346
+
347
+ // Reset UCI info selDepth for each depth and each PV line
348
+ selDepth = 0;
349
+
350
+ // Reset aspiration window starting size
351
+ if (rootDepth >= 4)
352
+ {
353
+ Value prev = rootMoves[pvIdx].averageScore;
354
+ delta = Value(10) + int(prev) * prev / 15620;
355
+ alpha = std::max(prev - delta,-VALUE_INFINITE);
356
+ beta = std::min(prev + delta, VALUE_INFINITE);
357
+
358
+ // Adjust optimism based on root move's previousScore
359
+ int opt = 118 * prev / (std::abs(prev) + 169);
360
+ optimism[ us] = Value(opt);
361
+ optimism[~us] = -optimism[us];
362
+ }
363
+
364
+ // Start with a small aspiration window and, in the case of a fail
365
+ // high/low, re-search with a bigger window until we don't fail
366
+ // high/low anymore.
367
+ int failedHighCnt = 0;
368
+ while (true)
369
+ {
370
+ // Adjust the effective depth searched, but ensuring at least one effective increment for every
371
+ // four searchAgain steps (see issue #2717).
372
+ Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - 3 * (searchAgainCounter + 1) / 4);
373
+ bestValue = Stockfish::search<Root>(rootPos, ss, alpha, beta, adjustedDepth, false);
374
+
375
+ // Bring the best move to the front. It is critical that sorting
376
+ // is done with a stable algorithm because all the values but the
377
+ // first and eventually the new best one are set to -VALUE_INFINITE
378
+ // and we want to keep the same order for all the moves except the
379
+ // new PV that goes to the front. Note that in case of MultiPV
380
+ // search the already searched PV lines are preserved.
381
+ std::stable_sort(rootMoves.begin() + pvIdx, rootMoves.begin() + pvLast);
382
+
383
+ // If search has been stopped, we break immediately. Sorting is
384
+ // safe because RootMoves is still valid, although it refers to
385
+ // the previous iteration.
386
+ if (Threads.stop)
387
+ break;
388
+
389
+ // When failing high/low give some update (without cluttering
390
+ // the UI) before a re-search.
391
+ if ( mainThread
392
+ && multiPV == 1
393
+ && (bestValue <= alpha || bestValue >= beta)
394
+ && Time.elapsed() > 3000)
395
+ sync_cout << UCI::pv(rootPos, rootDepth) << sync_endl;
396
+
397
+ // In case of failing low/high increase aspiration window and
398
+ // re-search, otherwise exit the loop.
399
+ if (bestValue <= alpha)
400
+ {
401
+ beta = (alpha + beta) / 2;
402
+ alpha = std::max(bestValue - delta, -VALUE_INFINITE);
403
+
404
+ failedHighCnt = 0;
405
+ if (mainThread)
406
+ mainThread->stopOnPonderhit = false;
407
+ }
408
+ else if (bestValue >= beta)
409
+ {
410
+ beta = std::min(bestValue + delta, VALUE_INFINITE);
411
+ ++failedHighCnt;
412
+ }
413
+ else
414
+ break;
415
+
416
+ delta += delta / 4 + 2;
417
+
418
+ assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
419
+ }
420
+
421
+ // Sort the PV lines searched so far and update the GUI
422
+ std::stable_sort(rootMoves.begin() + pvFirst, rootMoves.begin() + pvIdx + 1);
423
+
424
+ if ( mainThread
425
+ && (Threads.stop || pvIdx + 1 == multiPV || Time.elapsed() > 3000))
426
+ sync_cout << UCI::pv(rootPos, rootDepth) << sync_endl;
427
+ }
428
+
429
+ if (!Threads.stop)
430
+ completedDepth = rootDepth;
431
+
432
+ if (rootMoves[0].pv[0] != lastBestMove) {
433
+ lastBestMove = rootMoves[0].pv[0];
434
+ lastBestMoveDepth = rootDepth;
435
+ }
436
+
437
+ // Have we found a "mate in x"?
438
+ if ( Limits.mate
439
+ && bestValue >= VALUE_MATE_IN_MAX_PLY
440
+ && VALUE_MATE - bestValue <= 2 * Limits.mate)
441
+ Threads.stop = true;
442
+
443
+ if (!mainThread)
444
+ continue;
445
+
446
+ // If skill level is enabled and time is up, pick a sub-optimal best move
447
+ if (skill.enabled() && skill.time_to_pick(rootDepth))
448
+ skill.pick_best(multiPV);
449
+
450
+ // Use part of the gained time from a previous stable move for the current move
451
+ for (Thread* th : Threads)
452
+ {
453
+ totBestMoveChanges += th->bestMoveChanges;
454
+ th->bestMoveChanges = 0;
455
+ }
456
+
457
+ // Do we have time for the next iteration? Can we stop searching now?
458
+ if ( Limits.use_time_management()
459
+ && !Threads.stop
460
+ && !mainThread->stopOnPonderhit)
461
+ {
462
+ double fallingEval = (71 + 12 * (mainThread->bestPreviousAverageScore - bestValue)
463
+ + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 656.7;
464
+ fallingEval = std::clamp(fallingEval, 0.5, 1.5);
465
+
466
+ // If the bestMove is stable over several iterations, reduce time accordingly
467
+ timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.37 : 0.65;
468
+ double reduction = (1.4 + mainThread->previousTimeReduction) / (2.15 * timeReduction);
469
+ double bestMoveInstability = 1 + 1.7 * totBestMoveChanges / Threads.size();
470
+ int complexity = mainThread->complexityAverage.value();
471
+ double complexPosition = std::min(1.0 + (complexity - 261) / 1738.7, 1.5);
472
+
473
+ double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition;
474
+
475
+ // Cap used time in case of a single legal move for a better viewer experience in tournaments
476
+ // yielding correct scores and sufficiently fast moves.
477
+ if (rootMoves.size() == 1)
478
+ totalTime = std::min(500.0, totalTime);
479
+
480
+ // Stop the search if we have exceeded the totalTime
481
+ if (Time.elapsed() > totalTime)
482
+ {
483
+ // If we are allowed to ponder do not stop the search now but
484
+ // keep pondering until the GUI sends "ponderhit" or "stop".
485
+ if (mainThread->ponder)
486
+ mainThread->stopOnPonderhit = true;
487
+ else
488
+ Threads.stop = true;
489
+ }
490
+ else if ( Threads.increaseDepth
491
+ && !mainThread->ponder
492
+ && Time.elapsed() > totalTime * 0.53)
493
+ Threads.increaseDepth = false;
494
+ else
495
+ Threads.increaseDepth = true;
496
+ }
497
+
498
+ mainThread->iterValue[iterIdx] = bestValue;
499
+ iterIdx = (iterIdx + 1) & 3;
500
+ }
501
+
502
+ if (!mainThread)
503
+ return;
504
+
505
+ mainThread->previousTimeReduction = timeReduction;
506
+
507
+ // If skill level is enabled, swap best PV line with the sub-optimal one
508
+ if (skill.enabled())
509
+ std::swap(rootMoves[0], *std::find(rootMoves.begin(), rootMoves.end(),
510
+ skill.best ? skill.best : skill.pick_best(multiPV)));
511
+ }
512
+
513
+
514
+ namespace {
515
+
516
+ // search<>() is the main search function for both PV and non-PV nodes
517
+
518
+ template <NodeType nodeType>
519
+ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) {
520
+
521
+ constexpr bool PvNode = nodeType != NonPV;
522
+ constexpr bool rootNode = nodeType == Root;
523
+ const Depth maxNextDepth = rootNode ? depth : depth + 1;
524
+
525
+ // Check if we have an upcoming move which draws by repetition, or
526
+ // if the opponent had an alternative move earlier to this position.
527
+ if ( !rootNode
528
+ && pos.rule50_count() >= 3
529
+ && alpha < VALUE_DRAW
530
+ && pos.has_game_cycle(ss->ply))
531
+ {
532
+ alpha = value_draw(pos.this_thread());
533
+ if (alpha >= beta)
534
+ return alpha;
535
+ }
536
+
537
+ // Dive into quiescence search when the depth reaches zero
538
+ if (depth <= 0)
539
+ return qsearch<PvNode ? PV : NonPV>(pos, ss, alpha, beta);
540
+
541
+ assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE);
542
+ assert(PvNode || (alpha == beta - 1));
543
+ assert(0 < depth && depth < MAX_PLY);
544
+ assert(!(PvNode && cutNode));
545
+
546
+ Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64];
547
+ StateInfo st;
548
+ ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize);
549
+
550
+ TTEntry* tte;
551
+ Key posKey;
552
+ Move ttMove, move, excludedMove, bestMove;
553
+ Depth extension, newDepth;
554
+ Value bestValue, value, ttValue, eval, maxValue, probCutBeta;
555
+ bool givesCheck, improving, priorCapture, singularQuietLMR;
556
+ bool capture, moveCountPruning, ttCapture;
557
+ Piece movedPiece;
558
+ int moveCount, captureCount, quietCount, improvement, complexity;
559
+
560
+ // Step 1. Initialize node
561
+ Thread* thisThread = pos.this_thread();
562
+ ss->inCheck = pos.checkers();
563
+ priorCapture = pos.captured_piece();
564
+ Color us = pos.side_to_move();
565
+ moveCount = captureCount = quietCount = ss->moveCount = 0;
566
+ bestValue = -VALUE_INFINITE;
567
+ maxValue = VALUE_INFINITE;
568
+
569
+ // Check for the available remaining time
570
+ if (thisThread == Threads.main())
571
+ static_cast<MainThread*>(thisThread)->check_time();
572
+
573
+ // Used to send selDepth info to GUI (selDepth counts from 1, ply from 0)
574
+ if (PvNode && thisThread->selDepth < ss->ply + 1)
575
+ thisThread->selDepth = ss->ply + 1;
576
+
577
+ if (!rootNode)
578
+ {
579
+ // Step 2. Check for aborted search and immediate draw
580
+ if ( Threads.stop.load(std::memory_order_relaxed)
581
+ || pos.is_draw(ss->ply)
582
+ || ss->ply >= MAX_PLY)
583
+ return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos)
584
+ : value_draw(pos.this_thread());
585
+
586
+ // Step 3. Mate distance pruning. Even if we mate at the next move our score
587
+ // would be at best mate_in(ss->ply+1), but if alpha is already bigger because
588
+ // a shorter mate was found upward in the tree then there is no need to search
589
+ // because we will never beat the current alpha. Same logic but with reversed
590
+ // signs applies also in the opposite condition of being mated instead of giving
591
+ // mate. In this case return a fail-high score.
592
+ alpha = std::max(mated_in(ss->ply), alpha);
593
+ beta = std::min(mate_in(ss->ply+1), beta);
594
+ if (alpha >= beta)
595
+ return alpha;
596
+ }
597
+ else
598
+ thisThread->rootDelta = beta - alpha;
599
+
600
+ assert(0 <= ss->ply && ss->ply < MAX_PLY);
601
+
602
+ (ss+1)->ttPv = false;
603
+ (ss+1)->excludedMove = bestMove = MOVE_NONE;
604
+ (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
605
+ (ss+2)->cutoffCnt = 0;
606
+ ss->doubleExtensions = (ss-1)->doubleExtensions;
607
+ Square prevSq = to_sq((ss-1)->currentMove);
608
+
609
+ // Initialize statScore to zero for the grandchildren of the current position.
610
+ // So statScore is shared between all grandchildren and only the first grandchild
611
+ // starts with statScore = 0. Later grandchildren start with the last calculated
612
+ // statScore of the previous grandchild. This influences the reduction rules in
613
+ // LMR which are based on the statScore of parent position.
614
+ if (!rootNode)
615
+ (ss+2)->statScore = 0;
616
+
617
+ // Step 4. Transposition table lookup. We don't want the score of a partial
618
+ // search to overwrite a previous full search TT value, so we use a different
619
+ // position key in case of an excluded move.
620
+ excludedMove = ss->excludedMove;
621
+ posKey = excludedMove == MOVE_NONE ? pos.key() : pos.key() ^ make_key(excludedMove);
622
+ tte = TT.probe(posKey, ss->ttHit);
623
+ ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
624
+ ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
625
+ : ss->ttHit ? tte->move() : MOVE_NONE;
626
+ ttCapture = ttMove && pos.capture(ttMove);
627
+ if (!excludedMove)
628
+ ss->ttPv = PvNode || (ss->ttHit && tte->is_pv());
629
+
630
+ // At non-PV nodes we check for an early TT cutoff
631
+ if ( !PvNode
632
+ && ss->ttHit
633
+ && tte->depth() > depth - (tte->bound() == BOUND_EXACT)
634
+ && ttValue != VALUE_NONE // Possible in case of TT access race
635
+ && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER)))
636
+ {
637
+ // If ttMove is quiet, update move sorting heuristics on TT hit (~1 Elo)
638
+ if (ttMove)
639
+ {
640
+ if (ttValue >= beta)
641
+ {
642
+ // Bonus for a quiet ttMove that fails high (~3 Elo)
643
+ if (!ttCapture)
644
+ update_quiet_stats(pos, ss, ttMove, stat_bonus(depth));
645
+
646
+ // Extra penalty for early quiet moves of the previous ply (~0 Elo)
647
+ if ((ss-1)->moveCount <= 2 && !priorCapture)
648
+ update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1));
649
+ }
650
+ // Penalty for a quiet ttMove that fails low (~1 Elo)
651
+ else if (!ttCapture)
652
+ {
653
+ int penalty = -stat_bonus(depth);
654
+ thisThread->mainHistory[us][from_to(ttMove)] << penalty;
655
+ update_continuation_histories(ss, pos.moved_piece(ttMove), to_sq(ttMove), penalty);
656
+ }
657
+ }
658
+
659
+ // Partial workaround for the graph history interaction problem
660
+ // For high rule50 counts don't produce transposition table cutoffs.
661
+ if (pos.rule50_count() < 90)
662
+ return ttValue;
663
+ }
664
+
665
+ // Step 5. Tablebases probe
666
+ if (!rootNode && TB::Cardinality)
667
+ {
668
+ int piecesCount = pos.count<ALL_PIECES>();
669
+
670
+ if ( piecesCount <= TB::Cardinality
671
+ && (piecesCount < TB::Cardinality || depth >= TB::ProbeDepth)
672
+ && pos.rule50_count() == 0
673
+ && !pos.can_castle(ANY_CASTLING))
674
+ {
675
+ TB::ProbeState err;
676
+ TB::WDLScore wdl = Tablebases::probe_wdl(pos, &err);
677
+
678
+ // Force check of time on the next occasion
679
+ if (thisThread == Threads.main())
680
+ static_cast<MainThread*>(thisThread)->callsCnt = 0;
681
+
682
+ if (err != TB::ProbeState::FAIL)
683
+ {
684
+ thisThread->tbHits.fetch_add(1, std::memory_order_relaxed);
685
+
686
+ int drawScore = TB::UseRule50 ? 1 : 0;
687
+
688
+ // use the range VALUE_MATE_IN_MAX_PLY to VALUE_TB_WIN_IN_MAX_PLY to score
689
+ value = wdl < -drawScore ? VALUE_MATED_IN_MAX_PLY + ss->ply + 1
690
+ : wdl > drawScore ? VALUE_MATE_IN_MAX_PLY - ss->ply - 1
691
+ : VALUE_DRAW + 2 * wdl * drawScore;
692
+
693
+ Bound b = wdl < -drawScore ? BOUND_UPPER
694
+ : wdl > drawScore ? BOUND_LOWER : BOUND_EXACT;
695
+
696
+ if ( b == BOUND_EXACT
697
+ || (b == BOUND_LOWER ? value >= beta : value <= alpha))
698
+ {
699
+ tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, b,
700
+ std::min(MAX_PLY - 1, depth + 6),
701
+ MOVE_NONE, VALUE_NONE);
702
+
703
+ return value;
704
+ }
705
+
706
+ if (PvNode)
707
+ {
708
+ if (b == BOUND_LOWER)
709
+ bestValue = value, alpha = std::max(alpha, bestValue);
710
+ else
711
+ maxValue = value;
712
+ }
713
+ }
714
+ }
715
+ }
716
+
717
+ CapturePieceToHistory& captureHistory = thisThread->captureHistory;
718
+
719
+ // Step 6. Static evaluation of the position
720
+ if (ss->inCheck)
721
+ {
722
+ // Skip early pruning when in check
723
+ ss->staticEval = eval = VALUE_NONE;
724
+ improving = false;
725
+ improvement = 0;
726
+ complexity = 0;
727
+ goto moves_loop;
728
+ }
729
+ else if (ss->ttHit)
730
+ {
731
+ // Never assume anything about values stored in TT
732
+ ss->staticEval = eval = tte->eval();
733
+ if (eval == VALUE_NONE)
734
+ ss->staticEval = eval = evaluate(pos, &complexity);
735
+ else // Fall back to (semi)classical complexity for TT hits, the NNUE complexity is lost
736
+ complexity = abs(ss->staticEval - pos.psq_eg_stm());
737
+
738
+ // ttValue can be used as a better position evaluation (~4 Elo)
739
+ if ( ttValue != VALUE_NONE
740
+ && (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER)))
741
+ eval = ttValue;
742
+ }
743
+ else
744
+ {
745
+ ss->staticEval = eval = evaluate(pos, &complexity);
746
+
747
+ // Save static evaluation into transposition table
748
+ if (!excludedMove)
749
+ tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval);
750
+ }
751
+
752
+ thisThread->complexityAverage.update(complexity);
753
+
754
+ // Use static evaluation difference to improve quiet move ordering (~3 Elo)
755
+ if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture)
756
+ {
757
+ int bonus = std::clamp(-19 * int((ss-1)->staticEval + ss->staticEval), -1914, 1914);
758
+ thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus;
759
+ }
760
+
761
+ // Set up the improvement variable, which is the difference between the current
762
+ // static evaluation and the previous static evaluation at our turn (if we were
763
+ // in check at our previous move we look at the move prior to it). The improvement
764
+ // margin and the improving flag are used in various pruning heuristics.
765
+ improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval
766
+ : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval
767
+ : 168;
768
+ improving = improvement > 0;
769
+
770
+ // Step 7. Razoring.
771
+ // If eval is really low check with qsearch if it can exceed alpha, if it can't,
772
+ // return a fail low.
773
+ if (eval < alpha - 369 - 254 * depth * depth)
774
+ {
775
+ value = qsearch<NonPV>(pos, ss, alpha - 1, alpha);
776
+ if (value < alpha)
777
+ return value;
778
+ }
779
+
780
+ // Step 8. Futility pruning: child node (~25 Elo).
781
+ // The depth condition is important for mate finding.
782
+ if ( !ss->ttPv
783
+ && depth < 8
784
+ && eval - futility_margin(depth, improving) - (ss-1)->statScore / 303 >= beta
785
+ && eval >= beta
786
+ && eval < 28031) // larger than VALUE_KNOWN_WIN, but smaller than TB wins
787
+ return eval;
788
+
789
+ // Step 9. Null move search with verification search (~22 Elo)
790
+ if ( !PvNode
791
+ && (ss-1)->currentMove != MOVE_NULL
792
+ && (ss-1)->statScore < 17139
793
+ && eval >= beta
794
+ && eval >= ss->staticEval
795
+ && ss->staticEval >= beta - 20 * depth - improvement / 13 + 233 + complexity / 25
796
+ && !excludedMove
797
+ && pos.non_pawn_material(us)
798
+ && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
799
+ {
800
+ assert(eval - beta >= 0);
801
+
802
+ // Null move dynamic reduction based on depth, eval and complexity of position
803
+ Depth R = std::min(int(eval - beta) / 168, 7) + depth / 3 + 4 - (complexity > 861);
804
+
805
+ ss->currentMove = MOVE_NULL;
806
+ ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0];
807
+
808
+ pos.do_null_move(st);
809
+
810
+ Value nullValue = -search<NonPV>(pos, ss+1, -beta, -beta+1, depth-R, !cutNode);
811
+
812
+ pos.undo_null_move();
813
+
814
+ if (nullValue >= beta)
815
+ {
816
+ // Do not return unproven mate or TB scores
817
+ if (nullValue >= VALUE_TB_WIN_IN_MAX_PLY)
818
+ nullValue = beta;
819
+
820
+ if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 14))
821
+ return nullValue;
822
+
823
+ assert(!thisThread->nmpMinPly); // Recursive verification is not allowed
824
+
825
+ // Do verification search at high depths, with null move pruning disabled
826
+ // for us, until ply exceeds nmpMinPly.
827
+ thisThread->nmpMinPly = ss->ply + 3 * (depth-R) / 4;
828
+ thisThread->nmpColor = us;
829
+
830
+ Value v = search<NonPV>(pos, ss, beta-1, beta, depth-R, false);
831
+
832
+ thisThread->nmpMinPly = 0;
833
+
834
+ if (v >= beta)
835
+ return nullValue;
836
+ }
837
+ }
838
+
839
+ probCutBeta = beta + 191 - 54 * improving;
840
+
841
+ // Step 10. ProbCut (~4 Elo)
842
+ // If we have a good enough capture and a reduced search returns a value
843
+ // much above beta, we can (almost) safely prune the previous move.
844
+ if ( !PvNode
845
+ && depth > 4
846
+ && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY
847
+ // if value from transposition table is lower than probCutBeta, don't attempt probCut
848
+ // there and in further interactions with transposition table cutoff depth is set to depth - 3
849
+ // because probCut search has depth set to depth - 4 but we also do a move before it
850
+ // so effective depth is equal to depth - 3
851
+ && !( ss->ttHit
852
+ && tte->depth() >= depth - 3
853
+ && ttValue != VALUE_NONE
854
+ && ttValue < probCutBeta))
855
+ {
856
+ assert(probCutBeta < VALUE_INFINITE);
857
+
858
+ MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory);
859
+
860
+ while ((move = mp.next_move()) != MOVE_NONE)
861
+ if (move != excludedMove && pos.legal(move))
862
+ {
863
+ assert(pos.capture(move) || promotion_type(move) == QUEEN);
864
+
865
+ ss->currentMove = move;
866
+ ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
867
+ [true]
868
+ [pos.moved_piece(move)]
869
+ [to_sq(move)];
870
+
871
+ pos.do_move(move, st);
872
+
873
+ // Perform a preliminary qsearch to verify that the move holds
874
+ value = -qsearch<NonPV>(pos, ss+1, -probCutBeta, -probCutBeta+1);
875
+
876
+ // If the qsearch held, perform the regular search
877
+ if (value >= probCutBeta)
878
+ value = -search<NonPV>(pos, ss+1, -probCutBeta, -probCutBeta+1, depth - 4, !cutNode);
879
+
880
+ pos.undo_move(move);
881
+
882
+ if (value >= probCutBeta)
883
+ {
884
+ // Save ProbCut data into transposition table
885
+ tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3, move, ss->staticEval);
886
+ return value;
887
+ }
888
+ }
889
+ }
890
+
891
+ // Step 11. If the position is not in TT, decrease depth by 3.
892
+ // Use qsearch if depth is equal or below zero (~4 Elo)
893
+ if ( PvNode
894
+ && !ttMove)
895
+ depth -= 3;
896
+
897
+ if (depth <= 0)
898
+ return qsearch<PV>(pos, ss, alpha, beta);
899
+
900
+ if ( cutNode
901
+ && depth >= 9
902
+ && !ttMove)
903
+ depth -= 2;
904
+
905
+ moves_loop: // When in check, search starts here
906
+
907
+ // Step 12. A small Probcut idea, when we are in check (~0 Elo)
908
+ probCutBeta = beta + 417;
909
+ if ( ss->inCheck
910
+ && !PvNode
911
+ && depth >= 2
912
+ && ttCapture
913
+ && (tte->bound() & BOUND_LOWER)
914
+ && tte->depth() >= depth - 3
915
+ && ttValue >= probCutBeta
916
+ && abs(ttValue) <= VALUE_KNOWN_WIN
917
+ && abs(beta) <= VALUE_KNOWN_WIN
918
+ )
919
+ return probCutBeta;
920
+
921
+
922
+ const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory,
923
+ nullptr , (ss-4)->continuationHistory,
924
+ nullptr , (ss-6)->continuationHistory };
925
+
926
+ Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq];
927
+
928
+ MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory,
929
+ &captureHistory,
930
+ contHist,
931
+ countermove,
932
+ ss->killers);
933
+
934
+ value = bestValue;
935
+ moveCountPruning = singularQuietLMR = false;
936
+
937
+ // Indicate PvNodes that will probably fail low if the node was searched
938
+ // at a depth equal or greater than the current depth, and the result of this search was a fail low.
939
+ bool likelyFailLow = PvNode
940
+ && ttMove
941
+ && (tte->bound() & BOUND_UPPER)
942
+ && tte->depth() >= depth;
943
+
944
+ // Step 13. Loop through all pseudo-legal moves until no moves remain
945
+ // or a beta cutoff occurs.
946
+ while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE)
947
+ {
948
+ assert(is_ok(move));
949
+
950
+ if (move == excludedMove)
951
+ continue;
952
+
953
+ // At root obey the "searchmoves" option and skip moves not listed in Root
954
+ // Move List. As a consequence any illegal move is also skipped. In MultiPV
955
+ // mode we also skip PV moves which have been already searched and those
956
+ // of lower "TB rank" if we are in a TB root position.
957
+ if (rootNode && !std::count(thisThread->rootMoves.begin() + thisThread->pvIdx,
958
+ thisThread->rootMoves.begin() + thisThread->pvLast, move))
959
+ continue;
960
+
961
+ // Check for legality
962
+ if (!rootNode && !pos.legal(move))
963
+ continue;
964
+
965
+ ss->moveCount = ++moveCount;
966
+
967
+ if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000)
968
+ sync_cout << "info depth " << depth
969
+ << " currmove " << UCI::move(move, pos.is_chess960())
970
+ << " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl;
971
+ if (PvNode)
972
+ (ss+1)->pv = nullptr;
973
+
974
+ extension = 0;
975
+ capture = pos.capture(move);
976
+ movedPiece = pos.moved_piece(move);
977
+ givesCheck = pos.gives_check(move);
978
+
979
+ // Calculate new depth for this move
980
+ newDepth = depth - 1;
981
+
982
+ Value delta = beta - alpha;
983
+
984
+ // Step 14. Pruning at shallow depth (~98 Elo). Depth conditions are important for mate finding.
985
+ if ( !rootNode
986
+ && pos.non_pawn_material(us)
987
+ && bestValue > VALUE_TB_LOSS_IN_MAX_PLY)
988
+ {
989
+ // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~7 Elo)
990
+ moveCountPruning = moveCount >= futility_move_count(improving, depth);
991
+
992
+ // Reduced depth of the next LMR search
993
+ int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, delta, thisThread->rootDelta), 0);
994
+
995
+ if ( capture
996
+ || givesCheck)
997
+ {
998
+ // Futility pruning for captures (~0 Elo)
999
+ if ( !givesCheck
1000
+ && !PvNode
1001
+ && lmrDepth < 7
1002
+ && !ss->inCheck
1003
+ && ss->staticEval + 180 + 201 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))]
1004
+ + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 6 < alpha)
1005
+ continue;
1006
+
1007
+ // SEE based pruning (~9 Elo)
1008
+ if (!pos.see_ge(move, Value(-222) * depth))
1009
+ continue;
1010
+ }
1011
+ else
1012
+ {
1013
+ int history = (*contHist[0])[movedPiece][to_sq(move)]
1014
+ + (*contHist[1])[movedPiece][to_sq(move)]
1015
+ + (*contHist[3])[movedPiece][to_sq(move)];
1016
+
1017
+ // Continuation history based pruning (~2 Elo)
1018
+ if ( lmrDepth < 5
1019
+ && history < -3875 * (depth - 1))
1020
+ continue;
1021
+
1022
+ history += 2 * thisThread->mainHistory[us][from_to(move)];
1023
+
1024
+ // Futility pruning: parent node (~9 Elo)
1025
+ if ( !ss->inCheck
1026
+ && lmrDepth < 13
1027
+ && ss->staticEval + 106 + 145 * lmrDepth + history / 52 <= alpha)
1028
+ continue;
1029
+
1030
+ // Prune moves with negative SEE (~3 Elo)
1031
+ if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth)))
1032
+ continue;
1033
+ }
1034
+ }
1035
+
1036
+ // Step 15. Extensions (~66 Elo)
1037
+ // We take care to not overdo to avoid search getting stuck.
1038
+ if (ss->ply < thisThread->rootDepth * 2)
1039
+ {
1040
+ // Singular extension search (~58 Elo). If all moves but one fail low on a
1041
+ // search of (alpha-s, beta-s), and just one fails high on (alpha, beta),
1042
+ // then that move is singular and should be extended. To verify this we do
1043
+ // a reduced search on all the other moves but the ttMove and if the
1044
+ // result is lower than ttValue minus a margin, then we will extend the ttMove.
1045
+ if ( !rootNode
1046
+ && depth >= 4 - (thisThread->previousDepth > 24) + 2 * (PvNode && tte->is_pv())
1047
+ && move == ttMove
1048
+ && !excludedMove // Avoid recursive singular search
1049
+ /* && ttValue != VALUE_NONE Already implicit in the next condition */
1050
+ && abs(ttValue) < VALUE_KNOWN_WIN
1051
+ && (tte->bound() & BOUND_LOWER)
1052
+ && tte->depth() >= depth - 3)
1053
+ {
1054
+ Value singularBeta = ttValue - (3 + (ss->ttPv && !PvNode)) * depth;
1055
+ Depth singularDepth = (depth - 1) / 2;
1056
+
1057
+ ss->excludedMove = move;
1058
+ value = search<NonPV>(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode);
1059
+ ss->excludedMove = MOVE_NONE;
1060
+
1061
+ if (value < singularBeta)
1062
+ {
1063
+ extension = 1;
1064
+ singularQuietLMR = !ttCapture;
1065
+
1066
+ // Avoid search explosion by limiting the number of double extensions
1067
+ if ( !PvNode
1068
+ && value < singularBeta - 25
1069
+ && ss->doubleExtensions <= 9)
1070
+ extension = 2;
1071
+ }
1072
+
1073
+ // Multi-cut pruning
1074
+ // Our ttMove is assumed to fail high, and now we failed high also on a reduced
1075
+ // search without the ttMove. So we assume this expected Cut-node is not singular,
1076
+ // that multiple moves fail high, and we can prune the whole subtree by returning
1077
+ // a soft bound.
1078
+ else if (singularBeta >= beta)
1079
+ return singularBeta;
1080
+
1081
+ // If the eval of ttMove is greater than beta, we reduce it (negative extension)
1082
+ else if (ttValue >= beta)
1083
+ extension = -2;
1084
+
1085
+ // If the eval of ttMove is less than alpha and value, we reduce it (negative extension)
1086
+ else if (ttValue <= alpha && ttValue <= value)
1087
+ extension = -1;
1088
+ }
1089
+
1090
+ // Check extensions (~1 Elo)
1091
+ else if ( givesCheck
1092
+ && depth > 9
1093
+ && abs(ss->staticEval) > 82)
1094
+ extension = 1;
1095
+
1096
+ // Quiet ttMove extensions (~0 Elo)
1097
+ else if ( PvNode
1098
+ && move == ttMove
1099
+ && move == ss->killers[0]
1100
+ && (*contHist[0])[movedPiece][to_sq(move)] >= 5177)
1101
+ extension = 1;
1102
+ }
1103
+
1104
+ // Add extension to new depth
1105
+ newDepth += extension;
1106
+ ss->doubleExtensions = (ss-1)->doubleExtensions + (extension == 2);
1107
+
1108
+ // Speculative prefetch as early as possible
1109
+ prefetch(TT.first_entry(pos.key_after(move)));
1110
+
1111
+ // Update the current move (this must be done after singular extension search)
1112
+ ss->currentMove = move;
1113
+ ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
1114
+ [capture]
1115
+ [movedPiece]
1116
+ [to_sq(move)];
1117
+
1118
+ // Step 16. Make the move
1119
+ pos.do_move(move, st, givesCheck);
1120
+
1121
+ // Step 17. Late moves reduction / extension (LMR, ~98 Elo)
1122
+ // We use various heuristics for the sons of a node after the first son has
1123
+ // been searched. In general we would like to reduce them, but there are many
1124
+ // cases where we extend a son if it has good chances to be "interesting".
1125
+ if ( depth >= 2
1126
+ && moveCount > 1 + (PvNode && ss->ply <= 1)
1127
+ && ( !ss->ttPv
1128
+ || !capture
1129
+ || (cutNode && (ss-1)->moveCount > 1)))
1130
+ {
1131
+ Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta);
1132
+
1133
+ // Decrease reduction if position is or has been on the PV
1134
+ // and node is not likely to fail low. (~3 Elo)
1135
+ if ( ss->ttPv
1136
+ && !likelyFailLow)
1137
+ r -= 2;
1138
+
1139
+ // Decrease reduction if opponent's move count is high (~1 Elo)
1140
+ if ((ss-1)->moveCount > 7)
1141
+ r--;
1142
+
1143
+ // Increase reduction for cut nodes (~3 Elo)
1144
+ if (cutNode)
1145
+ r += 2;
1146
+
1147
+ // Increase reduction if ttMove is a capture (~3 Elo)
1148
+ if (ttCapture)
1149
+ r++;
1150
+
1151
+ // Decrease reduction for PvNodes based on depth
1152
+ if (PvNode)
1153
+ r -= 1 + 11 / (3 + depth);
1154
+
1155
+ // Decrease reduction if ttMove has been singularly extended (~1 Elo)
1156
+ if (singularQuietLMR)
1157
+ r--;
1158
+
1159
+ // Decrease reduction if we move a threatened piece (~1 Elo)
1160
+ if ( depth > 9
1161
+ && (mp.threatenedPieces & from_sq(move)))
1162
+ r--;
1163
+
1164
+ // Increase reduction if next ply has a lot of fail high
1165
+ if ((ss+1)->cutoffCnt > 3)
1166
+ r++;
1167
+
1168
+ ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)]
1169
+ + (*contHist[0])[movedPiece][to_sq(move)]
1170
+ + (*contHist[1])[movedPiece][to_sq(move)]
1171
+ + (*contHist[3])[movedPiece][to_sq(move)]
1172
+ - 4433;
1173
+
1174
+ // Decrease/increase reduction for moves with a good/bad history (~30 Elo)
1175
+ r -= ss->statScore / (13628 + 4000 * (depth > 7 && depth < 19));
1176
+
1177
+ // In general we want to cap the LMR depth search at newDepth, but when
1178
+ // reduction is negative, we allow this move a limited search extension
1179
+ // beyond the first move depth. This may lead to hidden double extensions.
1180
+ Depth d = std::clamp(newDepth - r, 1, newDepth + 1);
1181
+
1182
+ value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
1183
+
1184
+ // Do full depth search when reduced LMR search fails high
1185
+ if (value > alpha && d < newDepth)
1186
+ {
1187
+ // Adjust full depth search based on LMR results - if result
1188
+ // was good enough search deeper, if it was bad enough search shallower
1189
+ const bool doDeeperSearch = value > (alpha + 64 + 11 * (newDepth - d));
1190
+ const bool doShallowerSearch = value < bestValue + newDepth;
1191
+
1192
+ newDepth += doDeeperSearch - doShallowerSearch;
1193
+
1194
+ if (newDepth > d)
1195
+ value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
1196
+
1197
+ int bonus = value > alpha ? stat_bonus(newDepth)
1198
+ : -stat_bonus(newDepth);
1199
+
1200
+ if (capture)
1201
+ bonus /= 6;
1202
+
1203
+ update_continuation_histories(ss, movedPiece, to_sq(move), bonus);
1204
+ }
1205
+ }
1206
+
1207
+ // Step 18. Full depth search when LMR is skipped
1208
+ else if (!PvNode || moveCount > 1)
1209
+ {
1210
+ value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
1211
+ }
1212
+
1213
+ // For PV nodes only, do a full PV search on the first move or after a fail
1214
+ // high (in the latter case search only if value < beta), otherwise let the
1215
+ // parent node fail low with value <= alpha and try another move.
1216
+ if (PvNode && (moveCount == 1 || (value > alpha && (rootNode || value < beta))))
1217
+ {
1218
+ (ss+1)->pv = pv;
1219
+ (ss+1)->pv[0] = MOVE_NONE;
1220
+
1221
+ value = -search<PV>(pos, ss+1, -beta, -alpha,
1222
+ std::min(maxNextDepth, newDepth), false);
1223
+ }
1224
+
1225
+ // Step 19. Undo move
1226
+ pos.undo_move(move);
1227
+
1228
+ assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
1229
+
1230
+ // Step 20. Check for a new best move
1231
+ // Finished searching the move. If a stop occurred, the return value of
1232
+ // the search cannot be trusted, and we return immediately without
1233
+ // updating best move, PV and TT.
1234
+ if (Threads.stop.load(std::memory_order_relaxed))
1235
+ return VALUE_ZERO;
1236
+
1237
+ if (rootNode)
1238
+ {
1239
+ RootMove& rm = *std::find(thisThread->rootMoves.begin(),
1240
+ thisThread->rootMoves.end(), move);
1241
+
1242
+ rm.averageScore = rm.averageScore != -VALUE_INFINITE ? (2 * value + rm.averageScore) / 3 : value;
1243
+
1244
+ // PV move or new best move?
1245
+ if (moveCount == 1 || value > alpha)
1246
+ {
1247
+ rm.score = value;
1248
+ rm.selDepth = thisThread->selDepth;
1249
+ rm.scoreLowerbound = value >= beta;
1250
+ rm.scoreUpperbound = value <= alpha;
1251
+ rm.pv.resize(1);
1252
+
1253
+ assert((ss+1)->pv);
1254
+
1255
+ for (Move* m = (ss+1)->pv; *m != MOVE_NONE; ++m)
1256
+ rm.pv.push_back(*m);
1257
+
1258
+ // We record how often the best move has been changed in each iteration.
1259
+ // This information is used for time management. In MultiPV mode,
1260
+ // we must take care to only do this for the first PV line.
1261
+ if ( moveCount > 1
1262
+ && !thisThread->pvIdx)
1263
+ ++thisThread->bestMoveChanges;
1264
+ }
1265
+ else
1266
+ // All other moves but the PV are set to the lowest value: this
1267
+ // is not a problem when sorting because the sort is stable and the
1268
+ // move position in the list is preserved - just the PV is pushed up.
1269
+ rm.score = -VALUE_INFINITE;
1270
+ }
1271
+
1272
+ if (value > bestValue)
1273
+ {
1274
+ bestValue = value;
1275
+
1276
+ if (value > alpha)
1277
+ {
1278
+ bestMove = move;
1279
+
1280
+ if (PvNode && !rootNode) // Update pv even in fail-high case
1281
+ update_pv(ss->pv, move, (ss+1)->pv);
1282
+
1283
+ if (PvNode && value < beta) // Update alpha! Always alpha < beta
1284
+ {
1285
+ alpha = value;
1286
+
1287
+ // Reduce other moves if we have found at least one score improvement
1288
+ if ( depth > 1
1289
+ && depth < 6
1290
+ && beta < VALUE_KNOWN_WIN
1291
+ && alpha > -VALUE_KNOWN_WIN)
1292
+ depth -= 1;
1293
+
1294
+ assert(depth > 0);
1295
+ }
1296
+ else
1297
+ {
1298
+ ss->cutoffCnt++;
1299
+ assert(value >= beta); // Fail high
1300
+ break;
1301
+ }
1302
+ }
1303
+ }
1304
+
1305
+
1306
+ // If the move is worse than some previously searched move, remember it to update its stats later
1307
+ if (move != bestMove)
1308
+ {
1309
+ if (capture && captureCount < 32)
1310
+ capturesSearched[captureCount++] = move;
1311
+
1312
+ else if (!capture && quietCount < 64)
1313
+ quietsSearched[quietCount++] = move;
1314
+ }
1315
+ }
1316
+
1317
+ // The following condition would detect a stop only after move loop has been
1318
+ // completed. But in this case bestValue is valid because we have fully
1319
+ // searched our subtree, and we can anyhow save the result in TT.
1320
+ /*
1321
+ if (Threads.stop)
1322
+ return VALUE_DRAW;
1323
+ */
1324
+
1325
+ // Step 21. Check for mate and stalemate
1326
+ // All legal moves have been searched and if there are no legal moves, it
1327
+ // must be a mate or a stalemate. If we are in a singular extension search then
1328
+ // return a fail low score.
1329
+
1330
+ assert(moveCount || !ss->inCheck || excludedMove || !MoveList<LEGAL>(pos).size());
1331
+
1332
+ if (!moveCount)
1333
+ bestValue = excludedMove ? alpha :
1334
+ ss->inCheck ? mated_in(ss->ply)
1335
+ : VALUE_DRAW;
1336
+
1337
+ // If there is a move which produces search value greater than alpha we update stats of searched moves
1338
+ else if (bestMove)
1339
+ update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq,
1340
+ quietsSearched, quietCount, capturesSearched, captureCount, depth);
1341
+
1342
+ // Bonus for prior countermove that caused the fail low
1343
+ else if ( (depth >= 5 || PvNode)
1344
+ && !priorCapture)
1345
+ {
1346
+ //Assign extra bonus if current node is PvNode or cutNode
1347
+ //or fail low was really bad
1348
+ bool extraBonus = PvNode
1349
+ || cutNode
1350
+ || bestValue < alpha - 62 * depth;
1351
+
1352
+ update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus));
1353
+ }
1354
+
1355
+ if (PvNode)
1356
+ bestValue = std::min(bestValue, maxValue);
1357
+
1358
+ // If no good move is found and the previous position was ttPv, then the previous
1359
+ // opponent move is probably good and the new position is added to the search tree.
1360
+ if (bestValue <= alpha)
1361
+ ss->ttPv = ss->ttPv || ((ss-1)->ttPv && depth > 3);
1362
+
1363
+ // Write gathered information in transposition table
1364
+ if (!excludedMove && !(rootNode && thisThread->pvIdx))
1365
+ tte->save(posKey, value_to_tt(bestValue, ss->ply), ss->ttPv,
1366
+ bestValue >= beta ? BOUND_LOWER :
1367
+ PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
1368
+ depth, bestMove, ss->staticEval);
1369
+
1370
+ assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
1371
+
1372
+ return bestValue;
1373
+ }
1374
+
1375
+
1376
+ // qsearch() is the quiescence search function, which is called by the main search
1377
+ // function with zero depth, or recursively with further decreasing depth per call.
1378
+ // (~155 elo)
1379
+ template <NodeType nodeType>
1380
+ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
1381
+
1382
+ static_assert(nodeType != Root);
1383
+ constexpr bool PvNode = nodeType == PV;
1384
+
1385
+ assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE);
1386
+ assert(PvNode || (alpha == beta - 1));
1387
+ assert(depth <= 0);
1388
+
1389
+ Move pv[MAX_PLY+1];
1390
+ StateInfo st;
1391
+ ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize);
1392
+
1393
+ TTEntry* tte;
1394
+ Key posKey;
1395
+ Move ttMove, move, bestMove;
1396
+ Depth ttDepth;
1397
+ Value bestValue, value, ttValue, futilityValue, futilityBase;
1398
+ bool pvHit, givesCheck, capture;
1399
+ int moveCount;
1400
+
1401
+ if (PvNode)
1402
+ {
1403
+ (ss+1)->pv = pv;
1404
+ ss->pv[0] = MOVE_NONE;
1405
+ }
1406
+
1407
+ Thread* thisThread = pos.this_thread();
1408
+ bestMove = MOVE_NONE;
1409
+ ss->inCheck = pos.checkers();
1410
+ moveCount = 0;
1411
+
1412
+ // Check for an immediate draw or maximum ply reached
1413
+ if ( pos.is_draw(ss->ply)
1414
+ || ss->ply >= MAX_PLY)
1415
+ return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) : VALUE_DRAW;
1416
+
1417
+ assert(0 <= ss->ply && ss->ply < MAX_PLY);
1418
+
1419
+ // Decide whether or not to include checks: this fixes also the type of
1420
+ // TT entry depth that we are going to use. Note that in qsearch we use
1421
+ // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS.
1422
+ ttDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS
1423
+ : DEPTH_QS_NO_CHECKS;
1424
+ // Transposition table lookup
1425
+ posKey = pos.key();
1426
+ tte = TT.probe(posKey, ss->ttHit);
1427
+ ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
1428
+ ttMove = ss->ttHit ? tte->move() : MOVE_NONE;
1429
+ pvHit = ss->ttHit && tte->is_pv();
1430
+
1431
+ if ( !PvNode
1432
+ && ss->ttHit
1433
+ && tte->depth() >= ttDepth
1434
+ && ttValue != VALUE_NONE // Only in case of TT access race
1435
+ && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER)))
1436
+ return ttValue;
1437
+
1438
+ // Evaluate the position statically
1439
+ if (ss->inCheck)
1440
+ {
1441
+ ss->staticEval = VALUE_NONE;
1442
+ bestValue = futilityBase = -VALUE_INFINITE;
1443
+ }
1444
+ else
1445
+ {
1446
+ if (ss->ttHit)
1447
+ {
1448
+ // Never assume anything about values stored in TT
1449
+ if ((ss->staticEval = bestValue = tte->eval()) == VALUE_NONE)
1450
+ ss->staticEval = bestValue = evaluate(pos);
1451
+
1452
+ // ttValue can be used as a better position evaluation (~7 Elo)
1453
+ if ( ttValue != VALUE_NONE
1454
+ && (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER)))
1455
+ bestValue = ttValue;
1456
+ }
1457
+ else
1458
+ // In case of null move search use previous static eval with a different sign
1459
+ ss->staticEval = bestValue =
1460
+ (ss-1)->currentMove != MOVE_NULL ? evaluate(pos)
1461
+ : -(ss-1)->staticEval;
1462
+
1463
+ // Stand pat. Return immediately if static value is at least beta
1464
+ if (bestValue >= beta)
1465
+ {
1466
+ // Save gathered info in transposition table
1467
+ if (!ss->ttHit)
1468
+ tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER,
1469
+ DEPTH_NONE, MOVE_NONE, ss->staticEval);
1470
+
1471
+ return bestValue;
1472
+ }
1473
+
1474
+ if (PvNode && bestValue > alpha)
1475
+ alpha = bestValue;
1476
+
1477
+ futilityBase = bestValue + 153;
1478
+ }
1479
+
1480
+ const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory,
1481
+ nullptr , (ss-4)->continuationHistory,
1482
+ nullptr , (ss-6)->continuationHistory };
1483
+
1484
+ // Initialize a MovePicker object for the current position, and prepare
1485
+ // to search the moves. Because the depth is <= 0 here, only captures,
1486
+ // queen promotions, and other checks (only if depth >= DEPTH_QS_CHECKS)
1487
+ // will be generated.
1488
+ Square prevSq = to_sq((ss-1)->currentMove);
1489
+ MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory,
1490
+ &thisThread->captureHistory,
1491
+ contHist,
1492
+ prevSq);
1493
+
1494
+ int quietCheckEvasions = 0;
1495
+
1496
+ // Loop through the moves until no moves remain or a beta cutoff occurs
1497
+ while ((move = mp.next_move()) != MOVE_NONE)
1498
+ {
1499
+ assert(is_ok(move));
1500
+
1501
+ // Check for legality
1502
+ if (!pos.legal(move))
1503
+ continue;
1504
+
1505
+ givesCheck = pos.gives_check(move);
1506
+ capture = pos.capture(move);
1507
+
1508
+ moveCount++;
1509
+
1510
+ // Futility pruning and moveCount pruning (~5 Elo)
1511
+ if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY
1512
+ && !givesCheck
1513
+ && to_sq(move) != prevSq
1514
+ && futilityBase > -VALUE_KNOWN_WIN
1515
+ && type_of(move) != PROMOTION)
1516
+ {
1517
+
1518
+ if (moveCount > 2)
1519
+ continue;
1520
+
1521
+ futilityValue = futilityBase + PieceValue[EG][pos.piece_on(to_sq(move))];
1522
+
1523
+ if (futilityValue <= alpha)
1524
+ {
1525
+ bestValue = std::max(bestValue, futilityValue);
1526
+ continue;
1527
+ }
1528
+
1529
+ if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1))
1530
+ {
1531
+ bestValue = std::max(bestValue, futilityBase);
1532
+ continue;
1533
+ }
1534
+ }
1535
+
1536
+ // Do not search moves with negative SEE values (~5 Elo)
1537
+ if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY
1538
+ && !pos.see_ge(move))
1539
+ continue;
1540
+
1541
+ // Speculative prefetch as early as possible
1542
+ prefetch(TT.first_entry(pos.key_after(move)));
1543
+
1544
+ ss->currentMove = move;
1545
+ ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
1546
+ [capture]
1547
+ [pos.moved_piece(move)]
1548
+ [to_sq(move)];
1549
+
1550
+ // Continuation history based pruning (~2 Elo)
1551
+ if ( !capture
1552
+ && bestValue > VALUE_TB_LOSS_IN_MAX_PLY
1553
+ && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0
1554
+ && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0)
1555
+ continue;
1556
+
1557
+ // We prune after 2nd quiet check evasion where being 'in check' is implicitly checked through the counter
1558
+ // and being a 'quiet' apart from being a tt move is assumed after an increment because captures are pushed ahead.
1559
+ if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY
1560
+ && quietCheckEvasions > 1)
1561
+ break;
1562
+
1563
+ quietCheckEvasions += !capture && ss->inCheck;
1564
+
1565
+ // Make and search the move
1566
+ pos.do_move(move, st, givesCheck);
1567
+ value = -qsearch<nodeType>(pos, ss+1, -beta, -alpha, depth - 1);
1568
+ pos.undo_move(move);
1569
+
1570
+ assert(value > -VALUE_INFINITE && value < VALUE_INFINITE);
1571
+
1572
+ // Check for a new best move
1573
+ if (value > bestValue)
1574
+ {
1575
+ bestValue = value;
1576
+
1577
+ if (value > alpha)
1578
+ {
1579
+ bestMove = move;
1580
+
1581
+ if (PvNode) // Update pv even in fail-high case
1582
+ update_pv(ss->pv, move, (ss+1)->pv);
1583
+
1584
+ if (PvNode && value < beta) // Update alpha here!
1585
+ alpha = value;
1586
+ else
1587
+ break; // Fail high
1588
+ }
1589
+ }
1590
+ }
1591
+
1592
+ // All legal moves have been searched. A special case: if we're in check
1593
+ // and no legal moves were found, it is checkmate.
1594
+ if (ss->inCheck && bestValue == -VALUE_INFINITE)
1595
+ {
1596
+ assert(!MoveList<LEGAL>(pos).size());
1597
+
1598
+ return mated_in(ss->ply); // Plies to mate from the root
1599
+ }
1600
+
1601
+ // Save gathered info in transposition table
1602
+ tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit,
1603
+ bestValue >= beta ? BOUND_LOWER : BOUND_UPPER,
1604
+ ttDepth, bestMove, ss->staticEval);
1605
+
1606
+ assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
1607
+
1608
+ return bestValue;
1609
+ }
1610
+
1611
+
1612
+ // value_to_tt() adjusts a mate or TB score from "plies to mate from the root" to
1613
+ // "plies to mate from the current position". Standard scores are unchanged.
1614
+ // The function is called before storing a value in the transposition table.
1615
+
1616
+ Value value_to_tt(Value v, int ply) {
1617
+
1618
+ assert(v != VALUE_NONE);
1619
+
1620
+ return v >= VALUE_TB_WIN_IN_MAX_PLY ? v + ply
1621
+ : v <= VALUE_TB_LOSS_IN_MAX_PLY ? v - ply : v;
1622
+ }
1623
+
1624
+
1625
+ // value_from_tt() is the inverse of value_to_tt(): it adjusts a mate or TB score
1626
+ // from the transposition table (which refers to the plies to mate/be mated from
1627
+ // current position) to "plies to mate/be mated (TB win/loss) from the root". However,
1628
+ // for mate scores, to avoid potentially false mate scores related to the 50 moves rule
1629
+ // and the graph history interaction, we return an optimal TB score instead.
1630
+
1631
+ Value value_from_tt(Value v, int ply, int r50c) {
1632
+
1633
+ if (v == VALUE_NONE)
1634
+ return VALUE_NONE;
1635
+
1636
+ if (v >= VALUE_TB_WIN_IN_MAX_PLY) // TB win or better
1637
+ {
1638
+ if (v >= VALUE_MATE_IN_MAX_PLY && VALUE_MATE - v > 99 - r50c)
1639
+ return VALUE_MATE_IN_MAX_PLY - 1; // do not return a potentially false mate score
1640
+
1641
+ return v - ply;
1642
+ }
1643
+
1644
+ if (v <= VALUE_TB_LOSS_IN_MAX_PLY) // TB loss or worse
1645
+ {
1646
+ if (v <= VALUE_MATED_IN_MAX_PLY && VALUE_MATE + v > 99 - r50c)
1647
+ return VALUE_MATED_IN_MAX_PLY + 1; // do not return a potentially false mate score
1648
+
1649
+ return v + ply;
1650
+ }
1651
+
1652
+ return v;
1653
+ }
1654
+
1655
+
1656
+ // update_pv() adds current move and appends child pv[]
1657
+
1658
+ void update_pv(Move* pv, Move move, const Move* childPv) {
1659
+
1660
+ for (*pv++ = move; childPv && *childPv != MOVE_NONE; )
1661
+ *pv++ = *childPv++;
1662
+ *pv = MOVE_NONE;
1663
+ }
1664
+
1665
+
1666
+ // update_all_stats() updates stats at the end of search() when a bestMove is found
1667
+
1668
+ void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq,
1669
+ Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth) {
1670
+
1671
+ Color us = pos.side_to_move();
1672
+ Thread* thisThread = pos.this_thread();
1673
+ CapturePieceToHistory& captureHistory = thisThread->captureHistory;
1674
+ Piece moved_piece = pos.moved_piece(bestMove);
1675
+ PieceType captured = type_of(pos.piece_on(to_sq(bestMove)));
1676
+ int bonus1 = stat_bonus(depth + 1);
1677
+
1678
+ if (!pos.capture(bestMove))
1679
+ {
1680
+ int bonus2 = bestValue > beta + 137 ? bonus1 // larger bonus
1681
+ : stat_bonus(depth); // smaller bonus
1682
+
1683
+ // Increase stats for the best move in case it was a quiet move
1684
+ update_quiet_stats(pos, ss, bestMove, bonus2);
1685
+
1686
+ // Decrease stats for all non-best quiet moves
1687
+ for (int i = 0; i < quietCount; ++i)
1688
+ {
1689
+ thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bonus2;
1690
+ update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), to_sq(quietsSearched[i]), -bonus2);
1691
+ }
1692
+ }
1693
+ else
1694
+ // Increase stats for the best move in case it was a capture move
1695
+ captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1;
1696
+
1697
+ // Extra penalty for a quiet early move that was not a TT move or
1698
+ // main killer move in previous ply when it gets refuted.
1699
+ if ( ((ss-1)->moveCount == 1 + (ss-1)->ttHit || ((ss-1)->currentMove == (ss-1)->killers[0]))
1700
+ && !pos.captured_piece())
1701
+ update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -bonus1);
1702
+
1703
+ // Decrease stats for all non-best capture moves
1704
+ for (int i = 0; i < captureCount; ++i)
1705
+ {
1706
+ moved_piece = pos.moved_piece(capturesSearched[i]);
1707
+ captured = type_of(pos.piece_on(to_sq(capturesSearched[i])));
1708
+ captureHistory[moved_piece][to_sq(capturesSearched[i])][captured] << -bonus1;
1709
+ }
1710
+ }
1711
+
1712
+
1713
+ // update_continuation_histories() updates histories of the move pairs formed
1714
+ // by moves at ply -1, -2, -4, and -6 with current move.
1715
+
1716
+ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) {
1717
+
1718
+ for (int i : {1, 2, 4, 6})
1719
+ {
1720
+ // Only update first 2 continuation histories if we are in check
1721
+ if (ss->inCheck && i > 2)
1722
+ break;
1723
+ if (is_ok((ss-i)->currentMove))
1724
+ (*(ss-i)->continuationHistory)[pc][to] << bonus;
1725
+ }
1726
+ }
1727
+
1728
+
1729
+ // update_quiet_stats() updates move sorting heuristics
1730
+
1731
+ void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) {
1732
+
1733
+ // Update killers
1734
+ if (ss->killers[0] != move)
1735
+ {
1736
+ ss->killers[1] = ss->killers[0];
1737
+ ss->killers[0] = move;
1738
+ }
1739
+
1740
+ Color us = pos.side_to_move();
1741
+ Thread* thisThread = pos.this_thread();
1742
+ thisThread->mainHistory[us][from_to(move)] << bonus;
1743
+ update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus);
1744
+
1745
+ // Update countermove history
1746
+ if (is_ok((ss-1)->currentMove))
1747
+ {
1748
+ Square prevSq = to_sq((ss-1)->currentMove);
1749
+ thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move;
1750
+ }
1751
+ }
1752
+
1753
+ // When playing with strength handicap, choose best move among a set of RootMoves
1754
+ // using a statistical rule dependent on 'level'. Idea by Heinz van Saanen.
1755
+
1756
+ Move Skill::pick_best(size_t multiPV) {
1757
+
1758
+ const RootMoves& rootMoves = Threads.main()->rootMoves;
1759
+ static PRNG rng(now()); // PRNG sequence should be non-deterministic
1760
+
1761
+ // RootMoves are already sorted by score in descending order
1762
+ Value topScore = rootMoves[0].score;
1763
+ int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValueMg);
1764
+ int maxScore = -VALUE_INFINITE;
1765
+ double weakness = 120 - 2 * level;
1766
+
1767
+ // Choose best move. For each move score we add two terms, both dependent on
1768
+ // weakness. One is deterministic and bigger for weaker levels, and one is
1769
+ // random. Then we choose the move with the resulting highest score.
1770
+ for (size_t i = 0; i < multiPV; ++i)
1771
+ {
1772
+ // This is our magic formula
1773
+ int push = int(( weakness * int(topScore - rootMoves[i].score)
1774
+ + delta * (rng.rand<unsigned>() % int(weakness))) / 128);
1775
+
1776
+ if (rootMoves[i].score + push >= maxScore)
1777
+ {
1778
+ maxScore = rootMoves[i].score + push;
1779
+ best = rootMoves[i].pv[0];
1780
+ }
1781
+ }
1782
+
1783
+ return best;
1784
+ }
1785
+
1786
+ } // namespace
1787
+
1788
+
1789
+ /// MainThread::check_time() is used to print debug info and, more importantly,
1790
+ /// to detect when we are out of available time and thus stop the search.
1791
+
1792
+ void MainThread::check_time() {
1793
+
1794
+ if (--callsCnt > 0)
1795
+ return;
1796
+
1797
+ // When using nodes, ensure checking rate is not lower than 0.1% of nodes
1798
+ callsCnt = Limits.nodes ? std::min(1024, int(Limits.nodes / 1024)) : 1024;
1799
+
1800
+ static TimePoint lastInfoTime = now();
1801
+
1802
+ TimePoint elapsed = Time.elapsed();
1803
+ TimePoint tick = Limits.startTime + elapsed;
1804
+
1805
+ if (tick - lastInfoTime >= 1000)
1806
+ {
1807
+ lastInfoTime = tick;
1808
+ dbg_print();
1809
+ }
1810
+
1811
+ // We should not stop pondering until told so by the GUI
1812
+ if (ponder)
1813
+ return;
1814
+
1815
+ if ( (Limits.use_time_management() && (elapsed > Time.maximum() - 10 || stopOnPonderhit))
1816
+ || (Limits.movetime && elapsed >= Limits.movetime)
1817
+ || (Limits.nodes && Threads.nodes_searched() >= (uint64_t)Limits.nodes))
1818
+ Threads.stop = true;
1819
+ }
1820
+
1821
+
1822
+ /// UCI::pv() formats PV information according to the UCI protocol. UCI requires
1823
+ /// that all (if any) unsearched PV lines are sent using a previous search score.
1824
+
1825
+ string UCI::pv(const Position& pos, Depth depth) {
1826
+
1827
+ std::stringstream ss;
1828
+ TimePoint elapsed = Time.elapsed() + 1;
1829
+ const RootMoves& rootMoves = pos.this_thread()->rootMoves;
1830
+ size_t pvIdx = pos.this_thread()->pvIdx;
1831
+ size_t multiPV = std::min((size_t)Options["MultiPV"], rootMoves.size());
1832
+ uint64_t nodesSearched = Threads.nodes_searched();
1833
+ uint64_t tbHits = Threads.tb_hits() + (TB::RootInTB ? rootMoves.size() : 0);
1834
+
1835
+ for (size_t i = 0; i < multiPV; ++i)
1836
+ {
1837
+ bool updated = rootMoves[i].score != -VALUE_INFINITE;
1838
+
1839
+ if (depth == 1 && !updated && i > 0)
1840
+ continue;
1841
+
1842
+ Depth d = updated ? depth : std::max(1, depth - 1);
1843
+ Value v = updated ? rootMoves[i].score : rootMoves[i].previousScore;
1844
+
1845
+ if (v == -VALUE_INFINITE)
1846
+ v = VALUE_ZERO;
1847
+
1848
+ bool tb = TB::RootInTB && abs(v) < VALUE_MATE_IN_MAX_PLY;
1849
+ v = tb ? rootMoves[i].tbScore : v;
1850
+
1851
+ if (ss.rdbuf()->in_avail()) // Not at first line
1852
+ ss << "\n";
1853
+
1854
+ ss << "info"
1855
+ << " depth " << d
1856
+ << " seldepth " << rootMoves[i].selDepth
1857
+ << " multipv " << i + 1
1858
+ << " score " << UCI::value(v);
1859
+
1860
+ if (Options["UCI_ShowWDL"])
1861
+ ss << UCI::wdl(v, pos.game_ply());
1862
+
1863
+ if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact
1864
+ ss << (rootMoves[i].scoreLowerbound ? " lowerbound" : (rootMoves[i].scoreUpperbound ? " upperbound" : ""));
1865
+
1866
+ ss << " nodes " << nodesSearched
1867
+ << " nps " << nodesSearched * 1000 / elapsed
1868
+ << " hashfull " << TT.hashfull()
1869
+ << " tbhits " << tbHits
1870
+ << " time " << elapsed
1871
+ << " pv";
1872
+
1873
+ for (Move m : rootMoves[i].pv)
1874
+ ss << " " << UCI::move(m, pos.is_chess960());
1875
+ }
1876
+
1877
+ return ss.str();
1878
+ }
1879
+
1880
+
1881
+ /// RootMove::extract_ponder_from_tt() is called in case we have no ponder move
1882
+ /// before exiting the search, for instance, in case we stop the search during a
1883
+ /// fail high at root. We try hard to have a ponder move to return to the GUI,
1884
+ /// otherwise in case of 'ponder on' we have nothing to think on.
1885
+
1886
+ bool RootMove::extract_ponder_from_tt(Position& pos) {
1887
+
1888
+ StateInfo st;
1889
+ ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize);
1890
+
1891
+ bool ttHit;
1892
+
1893
+ assert(pv.size() == 1);
1894
+
1895
+ if (pv[0] == MOVE_NONE)
1896
+ return false;
1897
+
1898
+ pos.do_move(pv[0], st);
1899
+ TTEntry* tte = TT.probe(pos.key(), ttHit);
1900
+
1901
+ if (ttHit)
1902
+ {
1903
+ Move m = tte->move(); // Local copy to be SMP safe
1904
+ if (MoveList<LEGAL>(pos).contains(m))
1905
+ pv.push_back(m);
1906
+ }
1907
+
1908
+ pos.undo_move(pv[0]);
1909
+ return pv.size() > 1;
1910
+ }
1911
+
1912
+ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) {
1913
+
1914
+ RootInTB = false;
1915
+ UseRule50 = bool(Options["Syzygy50MoveRule"]);
1916
+ ProbeDepth = int(Options["SyzygyProbeDepth"]);
1917
+ Cardinality = int(Options["SyzygyProbeLimit"]);
1918
+ bool dtz_available = true;
1919
+
1920
+ // Tables with fewer pieces than SyzygyProbeLimit are searched with
1921
+ // ProbeDepth == DEPTH_ZERO
1922
+ if (Cardinality > MaxCardinality)
1923
+ {
1924
+ Cardinality = MaxCardinality;
1925
+ ProbeDepth = 0;
1926
+ }
1927
+
1928
+ if (Cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING))
1929
+ {
1930
+ // Rank moves using DTZ tables
1931
+ RootInTB = root_probe(pos, rootMoves);
1932
+
1933
+ if (!RootInTB)
1934
+ {
1935
+ // DTZ tables are missing; try to rank moves using WDL tables
1936
+ dtz_available = false;
1937
+ RootInTB = root_probe_wdl(pos, rootMoves);
1938
+ }
1939
+ }
1940
+
1941
+ if (RootInTB)
1942
+ {
1943
+ // Sort moves according to TB rank
1944
+ std::stable_sort(rootMoves.begin(), rootMoves.end(),
1945
+ [](const RootMove &a, const RootMove &b) { return a.tbRank > b.tbRank; } );
1946
+
1947
+ // Probe during search only if DTZ is not available and we are winning
1948
+ if (dtz_available || rootMoves[0].tbScore <= VALUE_DRAW)
1949
+ Cardinality = 0;
1950
+ }
1951
+ else
1952
+ {
1953
+ // Clean up if root_probe() and root_probe_wdl() have failed
1954
+ for (auto& m : rootMoves)
1955
+ m.tbRank = 0;
1956
+ }
1957
+ }
1958
+
1959
+ } // namespace Stockfish
src/search.h ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef SEARCH_H_INCLUDED
20
+ #define SEARCH_H_INCLUDED
21
+
22
+ #include <vector>
23
+
24
+ #include "misc.h"
25
+ #include "movepick.h"
26
+ #include "types.h"
27
+
28
+ namespace Stockfish {
29
+
30
+ class Position;
31
+
32
+ namespace Search {
33
+
34
+
35
+ /// Stack struct keeps track of the information we need to remember from nodes
36
+ /// shallower and deeper in the tree during the search. Each search thread has
37
+ /// its own array of Stack objects, indexed by the current ply.
38
+
39
+ struct Stack {
40
+ Move* pv;
41
+ PieceToHistory* continuationHistory;
42
+ int ply;
43
+ Move currentMove;
44
+ Move excludedMove;
45
+ Move killers[2];
46
+ Value staticEval;
47
+ int statScore;
48
+ int moveCount;
49
+ bool inCheck;
50
+ bool ttPv;
51
+ bool ttHit;
52
+ int doubleExtensions;
53
+ int cutoffCnt;
54
+ };
55
+
56
+
57
+ /// RootMove struct is used for moves at the root of the tree. For each root move
58
+ /// we store a score and a PV (really a refutation in the case of moves which
59
+ /// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves.
60
+
61
+ struct RootMove {
62
+
63
+ explicit RootMove(Move m) : pv(1, m) {}
64
+ bool extract_ponder_from_tt(Position& pos);
65
+ bool operator==(const Move& m) const { return pv[0] == m; }
66
+ bool operator<(const RootMove& m) const { // Sort in descending order
67
+ return m.score != score ? m.score < score
68
+ : m.previousScore < previousScore;
69
+ }
70
+
71
+ Value score = -VALUE_INFINITE;
72
+ Value previousScore = -VALUE_INFINITE;
73
+ Value averageScore = -VALUE_INFINITE;
74
+ bool scoreLowerbound = false;
75
+ bool scoreUpperbound = false;
76
+ int selDepth = 0;
77
+ int tbRank = 0;
78
+ Value tbScore;
79
+ std::vector<Move> pv;
80
+ };
81
+
82
+ typedef std::vector<RootMove> RootMoves;
83
+
84
+
85
+ /// LimitsType struct stores information sent by GUI about available time to
86
+ /// search the current move, maximum depth/time, or if we are in analysis mode.
87
+
88
+ struct LimitsType {
89
+
90
+ LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC
91
+ time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0);
92
+ movestogo = depth = mate = perft = infinite = 0;
93
+ nodes = 0;
94
+ }
95
+
96
+ bool use_time_management() const {
97
+ return time[WHITE] || time[BLACK];
98
+ }
99
+
100
+ std::vector<Move> searchmoves;
101
+ TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime;
102
+ int movestogo, depth, mate, perft, infinite;
103
+ int64_t nodes;
104
+ };
105
+
106
+ extern LimitsType Limits;
107
+
108
+ void init();
109
+ void clear();
110
+
111
+ } // namespace Search
112
+
113
+ } // namespace Stockfish
114
+
115
+ #endif // #ifndef SEARCH_H_INCLUDED
src/syzygy/tbprobe.cpp ADDED
@@ -0,0 +1,1628 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <algorithm>
20
+ #include <atomic>
21
+ #include <cstdint>
22
+ #include <cstring> // For std::memset and std::memcpy
23
+ #include <deque>
24
+ #include <fstream>
25
+ #include <iostream>
26
+ #include <list>
27
+ #include <sstream>
28
+ #include <type_traits>
29
+ #include <mutex>
30
+
31
+ #include "../bitboard.h"
32
+ #include "../movegen.h"
33
+ #include "../position.h"
34
+ #include "../search.h"
35
+ #include "../types.h"
36
+ #include "../uci.h"
37
+
38
+ #include "tbprobe.h"
39
+
40
+ #ifndef _WIN32
41
+ #include <fcntl.h>
42
+ #include <unistd.h>
43
+ #include <sys/mman.h>
44
+ #include <sys/stat.h>
45
+ #else
46
+ #define WIN32_LEAN_AND_MEAN
47
+ #ifndef NOMINMAX
48
+ # define NOMINMAX // Disable macros min() and max()
49
+ #endif
50
+ #include <windows.h>
51
+ #endif
52
+
53
+ using namespace Stockfish::Tablebases;
54
+
55
+ int Stockfish::Tablebases::MaxCardinality;
56
+
57
+ namespace Stockfish {
58
+
59
+ namespace {
60
+
61
+ constexpr int TBPIECES = 7; // Max number of supported pieces
62
+ constexpr int MAX_DTZ = 1 << 18; // Max DTZ supported, large enough to deal with the syzygy TB limit.
63
+
64
+ enum { BigEndian, LittleEndian };
65
+ enum TBType { WDL, DTZ }; // Used as template parameter
66
+
67
+ // Each table has a set of flags: all of them refer to DTZ tables, the last one to WDL tables
68
+ enum TBFlag { STM = 1, Mapped = 2, WinPlies = 4, LossPlies = 8, Wide = 16, SingleValue = 128 };
69
+
70
+ inline WDLScore operator-(WDLScore d) { return WDLScore(-int(d)); }
71
+ inline Square operator^(Square s, int i) { return Square(int(s) ^ i); }
72
+
73
+ const std::string PieceToChar = " PNBRQK pnbrqk";
74
+
75
+ int MapPawns[SQUARE_NB];
76
+ int MapB1H1H7[SQUARE_NB];
77
+ int MapA1D1D4[SQUARE_NB];
78
+ int MapKK[10][SQUARE_NB]; // [MapA1D1D4][SQUARE_NB]
79
+
80
+ int Binomial[6][SQUARE_NB]; // [k][n] k elements from a set of n elements
81
+ int LeadPawnIdx[6][SQUARE_NB]; // [leadPawnsCnt][SQUARE_NB]
82
+ int LeadPawnsSize[6][4]; // [leadPawnsCnt][FILE_A..FILE_D]
83
+
84
+ // Comparison function to sort leading pawns in ascending MapPawns[] order
85
+ bool pawns_comp(Square i, Square j) { return MapPawns[i] < MapPawns[j]; }
86
+ int off_A1H8(Square sq) { return int(rank_of(sq)) - file_of(sq); }
87
+
88
+ constexpr Value WDL_to_value[] = {
89
+ -VALUE_MATE + MAX_PLY + 1,
90
+ VALUE_DRAW - 2,
91
+ VALUE_DRAW,
92
+ VALUE_DRAW + 2,
93
+ VALUE_MATE - MAX_PLY - 1
94
+ };
95
+
96
+ template<typename T, int Half = sizeof(T) / 2, int End = sizeof(T) - 1>
97
+ inline void swap_endian(T& x)
98
+ {
99
+ static_assert(std::is_unsigned<T>::value, "Argument of swap_endian not unsigned");
100
+
101
+ uint8_t tmp, *c = (uint8_t*)&x;
102
+ for (int i = 0; i < Half; ++i)
103
+ tmp = c[i], c[i] = c[End - i], c[End - i] = tmp;
104
+ }
105
+ template<> inline void swap_endian<uint8_t>(uint8_t&) {}
106
+
107
+ template<typename T, int LE> T number(void* addr)
108
+ {
109
+ T v;
110
+
111
+ if ((uintptr_t)addr & (alignof(T) - 1)) // Unaligned pointer (very rare)
112
+ std::memcpy(&v, addr, sizeof(T));
113
+ else
114
+ v = *((T*)addr);
115
+
116
+ if (LE != IsLittleEndian)
117
+ swap_endian(v);
118
+ return v;
119
+ }
120
+
121
+ // DTZ tables don't store valid scores for moves that reset the rule50 counter
122
+ // like captures and pawn moves but we can easily recover the correct dtz of the
123
+ // previous move if we know the position's WDL score.
124
+ int dtz_before_zeroing(WDLScore wdl) {
125
+ return wdl == WDLWin ? 1 :
126
+ wdl == WDLCursedWin ? 101 :
127
+ wdl == WDLBlessedLoss ? -101 :
128
+ wdl == WDLLoss ? -1 : 0;
129
+ }
130
+
131
+ // Return the sign of a number (-1, 0, 1)
132
+ template <typename T> int sign_of(T val) {
133
+ return (T(0) < val) - (val < T(0));
134
+ }
135
+
136
+ // Numbers in little endian used by sparseIndex[] to point into blockLength[]
137
+ struct SparseEntry {
138
+ char block[4]; // Number of block
139
+ char offset[2]; // Offset within the block
140
+ };
141
+
142
+ static_assert(sizeof(SparseEntry) == 6, "SparseEntry must be 6 bytes");
143
+
144
+ typedef uint16_t Sym; // Huffman symbol
145
+
146
+ struct LR {
147
+ enum Side { Left, Right };
148
+
149
+ uint8_t lr[3]; // The first 12 bits is the left-hand symbol, the second 12
150
+ // bits is the right-hand symbol. If symbol has length 1,
151
+ // then the left-hand symbol is the stored value.
152
+ template<Side S>
153
+ Sym get() {
154
+ return S == Left ? ((lr[1] & 0xF) << 8) | lr[0] :
155
+ S == Right ? (lr[2] << 4) | (lr[1] >> 4) : (assert(false), Sym(-1));
156
+ }
157
+ };
158
+
159
+ static_assert(sizeof(LR) == 3, "LR tree entry must be 3 bytes");
160
+
161
+ // Tablebases data layout is structured as following:
162
+ //
163
+ // TBFile: memory maps/unmaps the physical .rtbw and .rtbz files
164
+ // TBTable: one object for each file with corresponding indexing information
165
+ // TBTables: has ownership of TBTable objects, keeping a list and a hash
166
+
167
+ // class TBFile memory maps/unmaps the single .rtbw and .rtbz files. Files are
168
+ // memory mapped for best performance. Files are mapped at first access: at init
169
+ // time only existence of the file is checked.
170
+ class TBFile : public std::ifstream {
171
+
172
+ std::string fname;
173
+
174
+ public:
175
+ // Look for and open the file among the Paths directories where the .rtbw
176
+ // and .rtbz files can be found. Multiple directories are separated by ";"
177
+ // on Windows and by ":" on Unix-based operating systems.
178
+ //
179
+ // Example:
180
+ // C:\tb\wdl345;C:\tb\wdl6;D:\tb\dtz345;D:\tb\dtz6
181
+ static std::string Paths;
182
+
183
+ TBFile(const std::string& f) {
184
+
185
+ #ifndef _WIN32
186
+ constexpr char SepChar = ':';
187
+ #else
188
+ constexpr char SepChar = ';';
189
+ #endif
190
+ std::stringstream ss(Paths);
191
+ std::string path;
192
+
193
+ while (std::getline(ss, path, SepChar))
194
+ {
195
+ fname = path + "/" + f;
196
+ std::ifstream::open(fname);
197
+ if (is_open())
198
+ return;
199
+ }
200
+ }
201
+
202
+ // Memory map the file and check it. File should be already open and will be
203
+ // closed after mapping.
204
+ uint8_t* map(void** baseAddress, uint64_t* mapping, TBType type) {
205
+
206
+ assert(is_open());
207
+
208
+ close(); // Need to re-open to get native file descriptor
209
+
210
+ #ifndef _WIN32
211
+ struct stat statbuf;
212
+ int fd = ::open(fname.c_str(), O_RDONLY);
213
+
214
+ if (fd == -1)
215
+ return *baseAddress = nullptr, nullptr;
216
+
217
+ fstat(fd, &statbuf);
218
+
219
+ if (statbuf.st_size % 64 != 16)
220
+ {
221
+ std::cerr << "Corrupt tablebase file " << fname << std::endl;
222
+ exit(EXIT_FAILURE);
223
+ }
224
+
225
+ *mapping = statbuf.st_size;
226
+ *baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
227
+ #if defined(MADV_RANDOM)
228
+ madvise(*baseAddress, statbuf.st_size, MADV_RANDOM);
229
+ #endif
230
+ ::close(fd);
231
+
232
+ if (*baseAddress == MAP_FAILED)
233
+ {
234
+ std::cerr << "Could not mmap() " << fname << std::endl;
235
+ exit(EXIT_FAILURE);
236
+ }
237
+ #else
238
+ // Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored.
239
+ HANDLE fd = CreateFile(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
240
+ OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr);
241
+
242
+ if (fd == INVALID_HANDLE_VALUE)
243
+ return *baseAddress = nullptr, nullptr;
244
+
245
+ DWORD size_high;
246
+ DWORD size_low = GetFileSize(fd, &size_high);
247
+
248
+ if (size_low % 64 != 16)
249
+ {
250
+ std::cerr << "Corrupt tablebase file " << fname << std::endl;
251
+ exit(EXIT_FAILURE);
252
+ }
253
+
254
+ HANDLE mmap = CreateFileMapping(fd, nullptr, PAGE_READONLY, size_high, size_low, nullptr);
255
+ CloseHandle(fd);
256
+
257
+ if (!mmap)
258
+ {
259
+ std::cerr << "CreateFileMapping() failed" << std::endl;
260
+ exit(EXIT_FAILURE);
261
+ }
262
+
263
+ *mapping = (uint64_t)mmap;
264
+ *baseAddress = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0);
265
+
266
+ if (!*baseAddress)
267
+ {
268
+ std::cerr << "MapViewOfFile() failed, name = " << fname
269
+ << ", error = " << GetLastError() << std::endl;
270
+ exit(EXIT_FAILURE);
271
+ }
272
+ #endif
273
+ uint8_t* data = (uint8_t*)*baseAddress;
274
+
275
+ constexpr uint8_t Magics[][4] = { { 0xD7, 0x66, 0x0C, 0xA5 },
276
+ { 0x71, 0xE8, 0x23, 0x5D } };
277
+
278
+ if (memcmp(data, Magics[type == WDL], 4))
279
+ {
280
+ std::cerr << "Corrupted table in file " << fname << std::endl;
281
+ unmap(*baseAddress, *mapping);
282
+ return *baseAddress = nullptr, nullptr;
283
+ }
284
+
285
+ return data + 4; // Skip Magics's header
286
+ }
287
+
288
+ static void unmap(void* baseAddress, uint64_t mapping) {
289
+
290
+ #ifndef _WIN32
291
+ munmap(baseAddress, mapping);
292
+ #else
293
+ UnmapViewOfFile(baseAddress);
294
+ CloseHandle((HANDLE)mapping);
295
+ #endif
296
+ }
297
+ };
298
+
299
+ std::string TBFile::Paths;
300
+
301
+ // struct PairsData contains low level indexing information to access TB data.
302
+ // There are 8, 4 or 2 PairsData records for each TBTable, according to type of
303
+ // table and if positions have pawns or not. It is populated at first access.
304
+ struct PairsData {
305
+ uint8_t flags; // Table flags, see enum TBFlag
306
+ uint8_t maxSymLen; // Maximum length in bits of the Huffman symbols
307
+ uint8_t minSymLen; // Minimum length in bits of the Huffman symbols
308
+ uint32_t blocksNum; // Number of blocks in the TB file
309
+ size_t sizeofBlock; // Block size in bytes
310
+ size_t span; // About every span values there is a SparseIndex[] entry
311
+ Sym* lowestSym; // lowestSym[l] is the symbol of length l with the lowest value
312
+ LR* btree; // btree[sym] stores the left and right symbols that expand sym
313
+ uint16_t* blockLength; // Number of stored positions (minus one) for each block: 1..65536
314
+ uint32_t blockLengthSize; // Size of blockLength[] table: padded so it's bigger than blocksNum
315
+ SparseEntry* sparseIndex; // Partial indices into blockLength[]
316
+ size_t sparseIndexSize; // Size of SparseIndex[] table
317
+ uint8_t* data; // Start of Huffman compressed data
318
+ std::vector<uint64_t> base64; // base64[l - min_sym_len] is the 64bit-padded lowest symbol of length l
319
+ std::vector<uint8_t> symlen; // Number of values (-1) represented by a given Huffman symbol: 1..256
320
+ Piece pieces[TBPIECES]; // Position pieces: the order of pieces defines the groups
321
+ uint64_t groupIdx[TBPIECES+1]; // Start index used for the encoding of the group's pieces
322
+ int groupLen[TBPIECES+1]; // Number of pieces in a given group: KRKN -> (3, 1)
323
+ uint16_t map_idx[4]; // WDLWin, WDLLoss, WDLCursedWin, WDLBlessedLoss (used in DTZ)
324
+ };
325
+
326
+ // struct TBTable contains indexing information to access the corresponding TBFile.
327
+ // There are 2 types of TBTable, corresponding to a WDL or a DTZ file. TBTable
328
+ // is populated at init time but the nested PairsData records are populated at
329
+ // first access, when the corresponding file is memory mapped.
330
+ template<TBType Type>
331
+ struct TBTable {
332
+ typedef typename std::conditional<Type == WDL, WDLScore, int>::type Ret;
333
+
334
+ static constexpr int Sides = Type == WDL ? 2 : 1;
335
+
336
+ std::atomic_bool ready;
337
+ void* baseAddress;
338
+ uint8_t* map;
339
+ uint64_t mapping;
340
+ Key key;
341
+ Key key2;
342
+ int pieceCount;
343
+ bool hasPawns;
344
+ bool hasUniquePieces;
345
+ uint8_t pawnCount[2]; // [Lead color / other color]
346
+ PairsData items[Sides][4]; // [wtm / btm][FILE_A..FILE_D or 0]
347
+
348
+ PairsData* get(int stm, int f) {
349
+ return &items[stm % Sides][hasPawns ? f : 0];
350
+ }
351
+
352
+ TBTable() : ready(false), baseAddress(nullptr) {}
353
+ explicit TBTable(const std::string& code);
354
+ explicit TBTable(const TBTable<WDL>& wdl);
355
+
356
+ ~TBTable() {
357
+ if (baseAddress)
358
+ TBFile::unmap(baseAddress, mapping);
359
+ }
360
+ };
361
+
362
+ template<>
363
+ TBTable<WDL>::TBTable(const std::string& code) : TBTable() {
364
+
365
+ StateInfo st;
366
+ Position pos;
367
+
368
+ key = pos.set(code, WHITE, &st).material_key();
369
+ pieceCount = pos.count<ALL_PIECES>();
370
+ hasPawns = pos.pieces(PAWN);
371
+
372
+ hasUniquePieces = false;
373
+ for (Color c : { WHITE, BLACK })
374
+ for (PieceType pt = PAWN; pt < KING; ++pt)
375
+ if (popcount(pos.pieces(c, pt)) == 1)
376
+ hasUniquePieces = true;
377
+
378
+ // Set the leading color. In case both sides have pawns the leading color
379
+ // is the side with less pawns because this leads to better compression.
380
+ bool c = !pos.count<PAWN>(BLACK)
381
+ || ( pos.count<PAWN>(WHITE)
382
+ && pos.count<PAWN>(BLACK) >= pos.count<PAWN>(WHITE));
383
+
384
+ pawnCount[0] = pos.count<PAWN>(c ? WHITE : BLACK);
385
+ pawnCount[1] = pos.count<PAWN>(c ? BLACK : WHITE);
386
+
387
+ key2 = pos.set(code, BLACK, &st).material_key();
388
+ }
389
+
390
+ template<>
391
+ TBTable<DTZ>::TBTable(const TBTable<WDL>& wdl) : TBTable() {
392
+
393
+ // Use the corresponding WDL table to avoid recalculating all from scratch
394
+ key = wdl.key;
395
+ key2 = wdl.key2;
396
+ pieceCount = wdl.pieceCount;
397
+ hasPawns = wdl.hasPawns;
398
+ hasUniquePieces = wdl.hasUniquePieces;
399
+ pawnCount[0] = wdl.pawnCount[0];
400
+ pawnCount[1] = wdl.pawnCount[1];
401
+ }
402
+
403
+ // class TBTables creates and keeps ownership of the TBTable objects, one for
404
+ // each TB file found. It supports a fast, hash based, table lookup. Populated
405
+ // at init time, accessed at probe time.
406
+ class TBTables {
407
+
408
+ struct Entry
409
+ {
410
+ Key key;
411
+ TBTable<WDL>* wdl;
412
+ TBTable<DTZ>* dtz;
413
+
414
+ template <TBType Type>
415
+ TBTable<Type>* get() const {
416
+ return (TBTable<Type>*)(Type == WDL ? (void*)wdl : (void*)dtz);
417
+ }
418
+ };
419
+
420
+ static constexpr int Size = 1 << 12; // 4K table, indexed by key's 12 lsb
421
+ static constexpr int Overflow = 1; // Number of elements allowed to map to the last bucket
422
+
423
+ Entry hashTable[Size + Overflow];
424
+
425
+ std::deque<TBTable<WDL>> wdlTable;
426
+ std::deque<TBTable<DTZ>> dtzTable;
427
+
428
+ void insert(Key key, TBTable<WDL>* wdl, TBTable<DTZ>* dtz) {
429
+ uint32_t homeBucket = (uint32_t)key & (Size - 1);
430
+ Entry entry{ key, wdl, dtz };
431
+
432
+ // Ensure last element is empty to avoid overflow when looking up
433
+ for (uint32_t bucket = homeBucket; bucket < Size + Overflow - 1; ++bucket) {
434
+ Key otherKey = hashTable[bucket].key;
435
+ if (otherKey == key || !hashTable[bucket].get<WDL>()) {
436
+ hashTable[bucket] = entry;
437
+ return;
438
+ }
439
+
440
+ // Robin Hood hashing: If we've probed for longer than this element,
441
+ // insert here and search for a new spot for the other element instead.
442
+ uint32_t otherHomeBucket = (uint32_t)otherKey & (Size - 1);
443
+ if (otherHomeBucket > homeBucket) {
444
+ std::swap(entry, hashTable[bucket]);
445
+ key = otherKey;
446
+ homeBucket = otherHomeBucket;
447
+ }
448
+ }
449
+ std::cerr << "TB hash table size too low!" << std::endl;
450
+ exit(EXIT_FAILURE);
451
+ }
452
+
453
+ public:
454
+ template<TBType Type>
455
+ TBTable<Type>* get(Key key) {
456
+ for (const Entry* entry = &hashTable[(uint32_t)key & (Size - 1)]; ; ++entry) {
457
+ if (entry->key == key || !entry->get<Type>())
458
+ return entry->get<Type>();
459
+ }
460
+ }
461
+
462
+ void clear() {
463
+ memset(hashTable, 0, sizeof(hashTable));
464
+ wdlTable.clear();
465
+ dtzTable.clear();
466
+ }
467
+ size_t size() const { return wdlTable.size(); }
468
+ void add(const std::vector<PieceType>& pieces);
469
+ };
470
+
471
+ TBTables TBTables;
472
+
473
+ // If the corresponding file exists two new objects TBTable<WDL> and TBTable<DTZ>
474
+ // are created and added to the lists and hash table. Called at init time.
475
+ void TBTables::add(const std::vector<PieceType>& pieces) {
476
+
477
+ std::string code;
478
+
479
+ for (PieceType pt : pieces)
480
+ code += PieceToChar[pt];
481
+
482
+ TBFile file(code.insert(code.find('K', 1), "v") + ".rtbw"); // KRK -> KRvK
483
+
484
+ if (!file.is_open()) // Only WDL file is checked
485
+ return;
486
+
487
+ file.close();
488
+
489
+ MaxCardinality = std::max((int)pieces.size(), MaxCardinality);
490
+
491
+ wdlTable.emplace_back(code);
492
+ dtzTable.emplace_back(wdlTable.back());
493
+
494
+ // Insert into the hash keys for both colors: KRvK with KR white and black
495
+ insert(wdlTable.back().key , &wdlTable.back(), &dtzTable.back());
496
+ insert(wdlTable.back().key2, &wdlTable.back(), &dtzTable.back());
497
+ }
498
+
499
+ // TB tables are compressed with canonical Huffman code. The compressed data is divided into
500
+ // blocks of size d->sizeofBlock, and each block stores a variable number of symbols.
501
+ // Each symbol represents either a WDL or a (remapped) DTZ value, or a pair of other symbols
502
+ // (recursively). If you keep expanding the symbols in a block, you end up with up to 65536
503
+ // WDL or DTZ values. Each symbol represents up to 256 values and will correspond after
504
+ // Huffman coding to at least 1 bit. So a block of 32 bytes corresponds to at most
505
+ // 32 x 8 x 256 = 65536 values. This maximum is only reached for tables that consist mostly
506
+ // of draws or mostly of wins, but such tables are actually quite common. In principle, the
507
+ // blocks in WDL tables are 64 bytes long (and will be aligned on cache lines). But for
508
+ // mostly-draw or mostly-win tables this can leave many 64-byte blocks only half-filled, so
509
+ // in such cases blocks are 32 bytes long. The blocks of DTZ tables are up to 1024 bytes long.
510
+ // The generator picks the size that leads to the smallest table. The "book" of symbols and
511
+ // Huffman codes is the same for all blocks in the table. A non-symmetric pawnless TB file
512
+ // will have one table for wtm and one for btm, a TB file with pawns will have tables per
513
+ // file a,b,c,d also in this case one set for wtm and one for btm.
514
+ int decompress_pairs(PairsData* d, uint64_t idx) {
515
+
516
+ // Special case where all table positions store the same value
517
+ if (d->flags & TBFlag::SingleValue)
518
+ return d->minSymLen;
519
+
520
+ // First we need to locate the right block that stores the value at index "idx".
521
+ // Because each block n stores blockLength[n] + 1 values, the index i of the block
522
+ // that contains the value at position idx is:
523
+ //
524
+ // for (i = -1, sum = 0; sum <= idx; i++)
525
+ // sum += blockLength[i + 1] + 1;
526
+ //
527
+ // This can be slow, so we use SparseIndex[] populated with a set of SparseEntry that
528
+ // point to known indices into blockLength[]. Namely SparseIndex[k] is a SparseEntry
529
+ // that stores the blockLength[] index and the offset within that block of the value
530
+ // with index I(k), where:
531
+ //
532
+ // I(k) = k * d->span + d->span / 2 (1)
533
+
534
+ // First step is to get the 'k' of the I(k) nearest to our idx, using definition (1)
535
+ uint32_t k = uint32_t(idx / d->span);
536
+
537
+ // Then we read the corresponding SparseIndex[] entry
538
+ uint32_t block = number<uint32_t, LittleEndian>(&d->sparseIndex[k].block);
539
+ int offset = number<uint16_t, LittleEndian>(&d->sparseIndex[k].offset);
540
+
541
+ // Now compute the difference idx - I(k). From definition of k we know that
542
+ //
543
+ // idx = k * d->span + idx % d->span (2)
544
+ //
545
+ // So from (1) and (2) we can compute idx - I(K):
546
+ int diff = idx % d->span - d->span / 2;
547
+
548
+ // Sum the above to offset to find the offset corresponding to our idx
549
+ offset += diff;
550
+
551
+ // Move to previous/next block, until we reach the correct block that contains idx,
552
+ // that is when 0 <= offset <= d->blockLength[block]
553
+ while (offset < 0)
554
+ offset += d->blockLength[--block] + 1;
555
+
556
+ while (offset > d->blockLength[block])
557
+ offset -= d->blockLength[block++] + 1;
558
+
559
+ // Finally, we find the start address of our block of canonical Huffman symbols
560
+ uint32_t* ptr = (uint32_t*)(d->data + ((uint64_t)block * d->sizeofBlock));
561
+
562
+ // Read the first 64 bits in our block, this is a (truncated) sequence of
563
+ // unknown number of symbols of unknown length but we know the first one
564
+ // is at the beginning of this 64 bits sequence.
565
+ uint64_t buf64 = number<uint64_t, BigEndian>(ptr); ptr += 2;
566
+ int buf64Size = 64;
567
+ Sym sym;
568
+
569
+ while (true)
570
+ {
571
+ int len = 0; // This is the symbol length - d->min_sym_len
572
+
573
+ // Now get the symbol length. For any symbol s64 of length l right-padded
574
+ // to 64 bits we know that d->base64[l-1] >= s64 >= d->base64[l] so we
575
+ // can find the symbol length iterating through base64[].
576
+ while (buf64 < d->base64[len])
577
+ ++len;
578
+
579
+ // All the symbols of a given length are consecutive integers (numerical
580
+ // sequence property), so we can compute the offset of our symbol of
581
+ // length len, stored at the beginning of buf64.
582
+ sym = Sym((buf64 - d->base64[len]) >> (64 - len - d->minSymLen));
583
+
584
+ // Now add the value of the lowest symbol of length len to get our symbol
585
+ sym += number<Sym, LittleEndian>(&d->lowestSym[len]);
586
+
587
+ // If our offset is within the number of values represented by symbol sym
588
+ // we are done...
589
+ if (offset < d->symlen[sym] + 1)
590
+ break;
591
+
592
+ // ...otherwise update the offset and continue to iterate
593
+ offset -= d->symlen[sym] + 1;
594
+ len += d->minSymLen; // Get the real length
595
+ buf64 <<= len; // Consume the just processed symbol
596
+ buf64Size -= len;
597
+
598
+ if (buf64Size <= 32) { // Refill the buffer
599
+ buf64Size += 32;
600
+ buf64 |= (uint64_t)number<uint32_t, BigEndian>(ptr++) << (64 - buf64Size);
601
+ }
602
+ }
603
+
604
+ // Ok, now we have our symbol that expands into d->symlen[sym] + 1 symbols.
605
+ // We binary-search for our value recursively expanding into the left and
606
+ // right child symbols until we reach a leaf node where symlen[sym] + 1 == 1
607
+ // that will store the value we need.
608
+ while (d->symlen[sym])
609
+ {
610
+ Sym left = d->btree[sym].get<LR::Left>();
611
+
612
+ // If a symbol contains 36 sub-symbols (d->symlen[sym] + 1 = 36) and
613
+ // expands in a pair (d->symlen[left] = 23, d->symlen[right] = 11), then
614
+ // we know that, for instance the ten-th value (offset = 10) will be on
615
+ // the left side because in Recursive Pairing child symbols are adjacent.
616
+ if (offset < d->symlen[left] + 1)
617
+ sym = left;
618
+ else {
619
+ offset -= d->symlen[left] + 1;
620
+ sym = d->btree[sym].get<LR::Right>();
621
+ }
622
+ }
623
+
624
+ return d->btree[sym].get<LR::Left>();
625
+ }
626
+
627
+ bool check_dtz_stm(TBTable<WDL>*, int, File) { return true; }
628
+
629
+ bool check_dtz_stm(TBTable<DTZ>* entry, int stm, File f) {
630
+
631
+ auto flags = entry->get(stm, f)->flags;
632
+ return (flags & TBFlag::STM) == stm
633
+ || ((entry->key == entry->key2) && !entry->hasPawns);
634
+ }
635
+
636
+ // DTZ scores are sorted by frequency of occurrence and then assigned the
637
+ // values 0, 1, 2, ... in order of decreasing frequency. This is done for each
638
+ // of the four WDLScore values. The mapping information necessary to reconstruct
639
+ // the original values is stored in the TB file and read during map[] init.
640
+ WDLScore map_score(TBTable<WDL>*, File, int value, WDLScore) { return WDLScore(value - 2); }
641
+
642
+ int map_score(TBTable<DTZ>* entry, File f, int value, WDLScore wdl) {
643
+
644
+ constexpr int WDLMap[] = { 1, 3, 0, 2, 0 };
645
+
646
+ auto flags = entry->get(0, f)->flags;
647
+
648
+ uint8_t* map = entry->map;
649
+ uint16_t* idx = entry->get(0, f)->map_idx;
650
+ if (flags & TBFlag::Mapped) {
651
+ if (flags & TBFlag::Wide)
652
+ value = ((uint16_t *)map)[idx[WDLMap[wdl + 2]] + value];
653
+ else
654
+ value = map[idx[WDLMap[wdl + 2]] + value];
655
+ }
656
+
657
+ // DTZ tables store distance to zero in number of moves or plies. We
658
+ // want to return plies, so we have convert to plies when needed.
659
+ if ( (wdl == WDLWin && !(flags & TBFlag::WinPlies))
660
+ || (wdl == WDLLoss && !(flags & TBFlag::LossPlies))
661
+ || wdl == WDLCursedWin
662
+ || wdl == WDLBlessedLoss)
663
+ value *= 2;
664
+
665
+ return value + 1;
666
+ }
667
+
668
+ // Compute a unique index out of a position and use it to probe the TB file. To
669
+ // encode k pieces of same type and color, first sort the pieces by square in
670
+ // ascending order s1 <= s2 <= ... <= sk then compute the unique index as:
671
+ //
672
+ // idx = Binomial[1][s1] + Binomial[2][s2] + ... + Binomial[k][sk]
673
+ //
674
+ template<typename T, typename Ret = typename T::Ret>
675
+ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* result) {
676
+
677
+ Square squares[TBPIECES];
678
+ Piece pieces[TBPIECES];
679
+ uint64_t idx;
680
+ int next = 0, size = 0, leadPawnsCnt = 0;
681
+ PairsData* d;
682
+ Bitboard b, leadPawns = 0;
683
+ File tbFile = FILE_A;
684
+
685
+ // A given TB entry like KRK has associated two material keys: KRvk and Kvkr.
686
+ // If both sides have the same pieces keys are equal. In this case TB tables
687
+ // only store the 'white to move' case, so if the position to lookup has black
688
+ // to move, we need to switch the color and flip the squares before to lookup.
689
+ bool symmetricBlackToMove = (entry->key == entry->key2 && pos.side_to_move());
690
+
691
+ // TB files are calculated for white as stronger side. For instance we have
692
+ // KRvK, not KvKR. A position where stronger side is white will have its
693
+ // material key == entry->key, otherwise we have to switch the color and
694
+ // flip the squares before to lookup.
695
+ bool blackStronger = (pos.material_key() != entry->key);
696
+
697
+ int flipColor = (symmetricBlackToMove || blackStronger) * 8;
698
+ int flipSquares = (symmetricBlackToMove || blackStronger) * 56;
699
+ int stm = (symmetricBlackToMove || blackStronger) ^ pos.side_to_move();
700
+
701
+ // For pawns, TB files store 4 separate tables according if leading pawn is on
702
+ // file a, b, c or d after reordering. The leading pawn is the one with maximum
703
+ // MapPawns[] value, that is the one most toward the edges and with lowest rank.
704
+ if (entry->hasPawns) {
705
+
706
+ // In all the 4 tables, pawns are at the beginning of the piece sequence and
707
+ // their color is the reference one. So we just pick the first one.
708
+ Piece pc = Piece(entry->get(0, 0)->pieces[0] ^ flipColor);
709
+
710
+ assert(type_of(pc) == PAWN);
711
+
712
+ leadPawns = b = pos.pieces(color_of(pc), PAWN);
713
+ do
714
+ squares[size++] = pop_lsb(b) ^ flipSquares;
715
+ while (b);
716
+
717
+ leadPawnsCnt = size;
718
+
719
+ std::swap(squares[0], *std::max_element(squares, squares + leadPawnsCnt, pawns_comp));
720
+
721
+ tbFile = File(edge_distance(file_of(squares[0])));
722
+ }
723
+
724
+ // DTZ tables are one-sided, i.e. they store positions only for white to
725
+ // move or only for black to move, so check for side to move to be stm,
726
+ // early exit otherwise.
727
+ if (!check_dtz_stm(entry, stm, tbFile))
728
+ return *result = CHANGE_STM, Ret();
729
+
730
+ // Now we are ready to get all the position pieces (but the lead pawns) and
731
+ // directly map them to the correct color and square.
732
+ b = pos.pieces() ^ leadPawns;
733
+ do {
734
+ Square s = pop_lsb(b);
735
+ squares[size] = s ^ flipSquares;
736
+ pieces[size++] = Piece(pos.piece_on(s) ^ flipColor);
737
+ } while (b);
738
+
739
+ assert(size >= 2);
740
+
741
+ d = entry->get(stm, tbFile);
742
+
743
+ // Then we reorder the pieces to have the same sequence as the one stored
744
+ // in pieces[i]: the sequence that ensures the best compression.
745
+ for (int i = leadPawnsCnt; i < size - 1; ++i)
746
+ for (int j = i + 1; j < size; ++j)
747
+ if (d->pieces[i] == pieces[j])
748
+ {
749
+ std::swap(pieces[i], pieces[j]);
750
+ std::swap(squares[i], squares[j]);
751
+ break;
752
+ }
753
+
754
+ // Now we map again the squares so that the square of the lead piece is in
755
+ // the triangle A1-D1-D4.
756
+ if (file_of(squares[0]) > FILE_D)
757
+ for (int i = 0; i < size; ++i)
758
+ squares[i] = flip_file(squares[i]);
759
+
760
+ // Encode leading pawns starting with the one with minimum MapPawns[] and
761
+ // proceeding in ascending order.
762
+ if (entry->hasPawns) {
763
+ idx = LeadPawnIdx[leadPawnsCnt][squares[0]];
764
+
765
+ std::stable_sort(squares + 1, squares + leadPawnsCnt, pawns_comp);
766
+
767
+ for (int i = 1; i < leadPawnsCnt; ++i)
768
+ idx += Binomial[i][MapPawns[squares[i]]];
769
+
770
+ goto encode_remaining; // With pawns we have finished special treatments
771
+ }
772
+
773
+ // In positions without pawns, we further flip the squares to ensure leading
774
+ // piece is below RANK_5.
775
+ if (rank_of(squares[0]) > RANK_4)
776
+ for (int i = 0; i < size; ++i)
777
+ squares[i] = flip_rank(squares[i]);
778
+
779
+ // Look for the first piece of the leading group not on the A1-D4 diagonal
780
+ // and ensure it is mapped below the diagonal.
781
+ for (int i = 0; i < d->groupLen[0]; ++i) {
782
+ if (!off_A1H8(squares[i]))
783
+ continue;
784
+
785
+ if (off_A1H8(squares[i]) > 0) // A1-H8 diagonal flip: SQ_A3 -> SQ_C1
786
+ for (int j = i; j < size; ++j)
787
+ squares[j] = Square(((squares[j] >> 3) | (squares[j] << 3)) & 63);
788
+ break;
789
+ }
790
+
791
+ // Encode the leading group.
792
+ //
793
+ // Suppose we have KRvK. Let's say the pieces are on square numbers wK, wR
794
+ // and bK (each 0...63). The simplest way to map this position to an index
795
+ // is like this:
796
+ //
797
+ // index = wK * 64 * 64 + wR * 64 + bK;
798
+ //
799
+ // But this way the TB is going to have 64*64*64 = 262144 positions, with
800
+ // lots of positions being equivalent (because they are mirrors of each
801
+ // other) and lots of positions being invalid (two pieces on one square,
802
+ // adjacent kings, etc.).
803
+ // Usually the first step is to take the wK and bK together. There are just
804
+ // 462 ways legal and not-mirrored ways to place the wK and bK on the board.
805
+ // Once we have placed the wK and bK, there are 62 squares left for the wR
806
+ // Mapping its square from 0..63 to available squares 0..61 can be done like:
807
+ //
808
+ // wR -= (wR > wK) + (wR > bK);
809
+ //
810
+ // In words: if wR "comes later" than wK, we deduct 1, and the same if wR
811
+ // "comes later" than bK. In case of two same pieces like KRRvK we want to
812
+ // place the two Rs "together". If we have 62 squares left, we can place two
813
+ // Rs "together" in 62 * 61 / 2 ways (we divide by 2 because rooks can be
814
+ // swapped and still get the same position.)
815
+ //
816
+ // In case we have at least 3 unique pieces (included kings) we encode them
817
+ // together.
818
+ if (entry->hasUniquePieces) {
819
+
820
+ int adjust1 = squares[1] > squares[0];
821
+ int adjust2 = (squares[2] > squares[0]) + (squares[2] > squares[1]);
822
+
823
+ // First piece is below a1-h8 diagonal. MapA1D1D4[] maps the b1-d1-d3
824
+ // triangle to 0...5. There are 63 squares for second piece and and 62
825
+ // (mapped to 0...61) for the third.
826
+ if (off_A1H8(squares[0]))
827
+ idx = ( MapA1D1D4[squares[0]] * 63
828
+ + (squares[1] - adjust1)) * 62
829
+ + squares[2] - adjust2;
830
+
831
+ // First piece is on a1-h8 diagonal, second below: map this occurrence to
832
+ // 6 to differentiate from the above case, rank_of() maps a1-d4 diagonal
833
+ // to 0...3 and finally MapB1H1H7[] maps the b1-h1-h7 triangle to 0..27.
834
+ else if (off_A1H8(squares[1]))
835
+ idx = ( 6 * 63 + rank_of(squares[0]) * 28
836
+ + MapB1H1H7[squares[1]]) * 62
837
+ + squares[2] - adjust2;
838
+
839
+ // First two pieces are on a1-h8 diagonal, third below
840
+ else if (off_A1H8(squares[2]))
841
+ idx = 6 * 63 * 62 + 4 * 28 * 62
842
+ + rank_of(squares[0]) * 7 * 28
843
+ + (rank_of(squares[1]) - adjust1) * 28
844
+ + MapB1H1H7[squares[2]];
845
+
846
+ // All 3 pieces on the diagonal a1-h8
847
+ else
848
+ idx = 6 * 63 * 62 + 4 * 28 * 62 + 4 * 7 * 28
849
+ + rank_of(squares[0]) * 7 * 6
850
+ + (rank_of(squares[1]) - adjust1) * 6
851
+ + (rank_of(squares[2]) - adjust2);
852
+ } else
853
+ // We don't have at least 3 unique pieces, like in KRRvKBB, just map
854
+ // the kings.
855
+ idx = MapKK[MapA1D1D4[squares[0]]][squares[1]];
856
+
857
+ encode_remaining:
858
+ idx *= d->groupIdx[0];
859
+ Square* groupSq = squares + d->groupLen[0];
860
+
861
+ // Encode remaining pawns then pieces according to square, in ascending order
862
+ bool remainingPawns = entry->hasPawns && entry->pawnCount[1];
863
+
864
+ while (d->groupLen[++next])
865
+ {
866
+ std::stable_sort(groupSq, groupSq + d->groupLen[next]);
867
+ uint64_t n = 0;
868
+
869
+ // Map down a square if "comes later" than a square in the previous
870
+ // groups (similar to what done earlier for leading group pieces).
871
+ for (int i = 0; i < d->groupLen[next]; ++i)
872
+ {
873
+ auto f = [&](Square s) { return groupSq[i] > s; };
874
+ auto adjust = std::count_if(squares, groupSq, f);
875
+ n += Binomial[i + 1][groupSq[i] - adjust - 8 * remainingPawns];
876
+ }
877
+
878
+ remainingPawns = false;
879
+ idx += n * d->groupIdx[next];
880
+ groupSq += d->groupLen[next];
881
+ }
882
+
883
+ // Now that we have the index, decompress the pair and get the score
884
+ return map_score(entry, tbFile, decompress_pairs(d, idx), wdl);
885
+ }
886
+
887
+ // Group together pieces that will be encoded together. The general rule is that
888
+ // a group contains pieces of same type and color. The exception is the leading
889
+ // group that, in case of positions without pawns, can be formed by 3 different
890
+ // pieces (default) or by the king pair when there is not a unique piece apart
891
+ // from the kings. When there are pawns, pawns are always first in pieces[].
892
+ //
893
+ // As example KRKN -> KRK + N, KNNK -> KK + NN, KPPKP -> P + PP + K + K
894
+ //
895
+ // The actual grouping depends on the TB generator and can be inferred from the
896
+ // sequence of pieces in piece[] array.
897
+ template<typename T>
898
+ void set_groups(T& e, PairsData* d, int order[], File f) {
899
+
900
+ int n = 0, firstLen = e.hasPawns ? 0 : e.hasUniquePieces ? 3 : 2;
901
+ d->groupLen[n] = 1;
902
+
903
+ // Number of pieces per group is stored in groupLen[], for instance in KRKN
904
+ // the encoder will default on '111', so groupLen[] will be (3, 1).
905
+ for (int i = 1; i < e.pieceCount; ++i)
906
+ if (--firstLen > 0 || d->pieces[i] == d->pieces[i - 1])
907
+ d->groupLen[n]++;
908
+ else
909
+ d->groupLen[++n] = 1;
910
+
911
+ d->groupLen[++n] = 0; // Zero-terminated
912
+
913
+ // The sequence in pieces[] defines the groups, but not the order in which
914
+ // they are encoded. If the pieces in a group g can be combined on the board
915
+ // in N(g) different ways, then the position encoding will be of the form:
916
+ //
917
+ // g1 * N(g2) * N(g3) + g2 * N(g3) + g3
918
+ //
919
+ // This ensures unique encoding for the whole position. The order of the
920
+ // groups is a per-table parameter and could not follow the canonical leading
921
+ // pawns/pieces -> remaining pawns -> remaining pieces. In particular the
922
+ // first group is at order[0] position and the remaining pawns, when present,
923
+ // are at order[1] position.
924
+ bool pp = e.hasPawns && e.pawnCount[1]; // Pawns on both sides
925
+ int next = pp ? 2 : 1;
926
+ int freeSquares = 64 - d->groupLen[0] - (pp ? d->groupLen[1] : 0);
927
+ uint64_t idx = 1;
928
+
929
+ for (int k = 0; next < n || k == order[0] || k == order[1]; ++k)
930
+ if (k == order[0]) // Leading pawns or pieces
931
+ {
932
+ d->groupIdx[0] = idx;
933
+ idx *= e.hasPawns ? LeadPawnsSize[d->groupLen[0]][f]
934
+ : e.hasUniquePieces ? 31332 : 462;
935
+ }
936
+ else if (k == order[1]) // Remaining pawns
937
+ {
938
+ d->groupIdx[1] = idx;
939
+ idx *= Binomial[d->groupLen[1]][48 - d->groupLen[0]];
940
+ }
941
+ else // Remaining pieces
942
+ {
943
+ d->groupIdx[next] = idx;
944
+ idx *= Binomial[d->groupLen[next]][freeSquares];
945
+ freeSquares -= d->groupLen[next++];
946
+ }
947
+
948
+ d->groupIdx[n] = idx;
949
+ }
950
+
951
+ // In Recursive Pairing each symbol represents a pair of children symbols. So
952
+ // read d->btree[] symbols data and expand each one in his left and right child
953
+ // symbol until reaching the leafs that represent the symbol value.
954
+ uint8_t set_symlen(PairsData* d, Sym s, std::vector<bool>& visited) {
955
+
956
+ visited[s] = true; // We can set it now because tree is acyclic
957
+ Sym sr = d->btree[s].get<LR::Right>();
958
+
959
+ if (sr == 0xFFF)
960
+ return 0;
961
+
962
+ Sym sl = d->btree[s].get<LR::Left>();
963
+
964
+ if (!visited[sl])
965
+ d->symlen[sl] = set_symlen(d, sl, visited);
966
+
967
+ if (!visited[sr])
968
+ d->symlen[sr] = set_symlen(d, sr, visited);
969
+
970
+ return d->symlen[sl] + d->symlen[sr] + 1;
971
+ }
972
+
973
+ uint8_t* set_sizes(PairsData* d, uint8_t* data) {
974
+
975
+ d->flags = *data++;
976
+
977
+ if (d->flags & TBFlag::SingleValue) {
978
+ d->blocksNum = d->blockLengthSize = 0;
979
+ d->span = d->sparseIndexSize = 0; // Broken MSVC zero-init
980
+ d->minSymLen = *data++; // Here we store the single value
981
+ return data;
982
+ }
983
+
984
+ // groupLen[] is a zero-terminated list of group lengths, the last groupIdx[]
985
+ // element stores the biggest index that is the tb size.
986
+ uint64_t tbSize = d->groupIdx[std::find(d->groupLen, d->groupLen + 7, 0) - d->groupLen];
987
+
988
+ d->sizeofBlock = 1ULL << *data++;
989
+ d->span = 1ULL << *data++;
990
+ d->sparseIndexSize = size_t((tbSize + d->span - 1) / d->span); // Round up
991
+ auto padding = number<uint8_t, LittleEndian>(data++);
992
+ d->blocksNum = number<uint32_t, LittleEndian>(data); data += sizeof(uint32_t);
993
+ d->blockLengthSize = d->blocksNum + padding; // Padded to ensure SparseIndex[]
994
+ // does not point out of range.
995
+ d->maxSymLen = *data++;
996
+ d->minSymLen = *data++;
997
+ d->lowestSym = (Sym*)data;
998
+ d->base64.resize(d->maxSymLen - d->minSymLen + 1);
999
+
1000
+ // The canonical code is ordered such that longer symbols (in terms of
1001
+ // the number of bits of their Huffman code) have lower numeric value,
1002
+ // so that d->lowestSym[i] >= d->lowestSym[i+1] (when read as LittleEndian).
1003
+ // Starting from this we compute a base64[] table indexed by symbol length
1004
+ // and containing 64 bit values so that d->base64[i] >= d->base64[i+1].
1005
+ // See https://en.wikipedia.org/wiki/Huffman_coding
1006
+ for (int i = d->base64.size() - 2; i >= 0; --i) {
1007
+ d->base64[i] = (d->base64[i + 1] + number<Sym, LittleEndian>(&d->lowestSym[i])
1008
+ - number<Sym, LittleEndian>(&d->lowestSym[i + 1])) / 2;
1009
+
1010
+ assert(d->base64[i] * 2 >= d->base64[i+1]);
1011
+ }
1012
+
1013
+ // Now left-shift by an amount so that d->base64[i] gets shifted 1 bit more
1014
+ // than d->base64[i+1] and given the above assert condition, we ensure that
1015
+ // d->base64[i] >= d->base64[i+1]. Moreover for any symbol s64 of length i
1016
+ // and right-padded to 64 bits holds d->base64[i-1] >= s64 >= d->base64[i].
1017
+ for (size_t i = 0; i < d->base64.size(); ++i)
1018
+ d->base64[i] <<= 64 - i - d->minSymLen; // Right-padding to 64 bits
1019
+
1020
+ data += d->base64.size() * sizeof(Sym);
1021
+ d->symlen.resize(number<uint16_t, LittleEndian>(data)); data += sizeof(uint16_t);
1022
+ d->btree = (LR*)data;
1023
+
1024
+ // The compression scheme used is "Recursive Pairing", that replaces the most
1025
+ // frequent adjacent pair of symbols in the source message by a new symbol,
1026
+ // reevaluating the frequencies of all of the symbol pairs with respect to
1027
+ // the extended alphabet, and then repeating the process.
1028
+ // See http://www.larsson.dogma.net/dcc99.pdf
1029
+ std::vector<bool> visited(d->symlen.size());
1030
+
1031
+ for (Sym sym = 0; sym < d->symlen.size(); ++sym)
1032
+ if (!visited[sym])
1033
+ d->symlen[sym] = set_symlen(d, sym, visited);
1034
+
1035
+ return data + d->symlen.size() * sizeof(LR) + (d->symlen.size() & 1);
1036
+ }
1037
+
1038
+ uint8_t* set_dtz_map(TBTable<WDL>&, uint8_t* data, File) { return data; }
1039
+
1040
+ uint8_t* set_dtz_map(TBTable<DTZ>& e, uint8_t* data, File maxFile) {
1041
+
1042
+ e.map = data;
1043
+
1044
+ for (File f = FILE_A; f <= maxFile; ++f) {
1045
+ auto flags = e.get(0, f)->flags;
1046
+ if (flags & TBFlag::Mapped) {
1047
+ if (flags & TBFlag::Wide) {
1048
+ data += (uintptr_t)data & 1; // Word alignment, we may have a mixed table
1049
+ for (int i = 0; i < 4; ++i) { // Sequence like 3,x,x,x,1,x,0,2,x,x
1050
+ e.get(0, f)->map_idx[i] = (uint16_t)((uint16_t *)data - (uint16_t *)e.map + 1);
1051
+ data += 2 * number<uint16_t, LittleEndian>(data) + 2;
1052
+ }
1053
+ }
1054
+ else {
1055
+ for (int i = 0; i < 4; ++i) {
1056
+ e.get(0, f)->map_idx[i] = (uint16_t)(data - e.map + 1);
1057
+ data += *data + 1;
1058
+ }
1059
+ }
1060
+ }
1061
+ }
1062
+
1063
+ return data += (uintptr_t)data & 1; // Word alignment
1064
+ }
1065
+
1066
+ // Populate entry's PairsData records with data from the just memory mapped file.
1067
+ // Called at first access.
1068
+ template<typename T>
1069
+ void set(T& e, uint8_t* data) {
1070
+
1071
+ PairsData* d;
1072
+
1073
+ enum { Split = 1, HasPawns = 2 };
1074
+
1075
+ assert(e.hasPawns == bool(*data & HasPawns));
1076
+ assert((e.key != e.key2) == bool(*data & Split));
1077
+
1078
+ data++; // First byte stores flags
1079
+
1080
+ const int sides = T::Sides == 2 && (e.key != e.key2) ? 2 : 1;
1081
+ const File maxFile = e.hasPawns ? FILE_D : FILE_A;
1082
+
1083
+ bool pp = e.hasPawns && e.pawnCount[1]; // Pawns on both sides
1084
+
1085
+ assert(!pp || e.pawnCount[0]);
1086
+
1087
+ for (File f = FILE_A; f <= maxFile; ++f) {
1088
+
1089
+ for (int i = 0; i < sides; i++)
1090
+ *e.get(i, f) = PairsData();
1091
+
1092
+ int order[][2] = { { *data & 0xF, pp ? *(data + 1) & 0xF : 0xF },
1093
+ { *data >> 4, pp ? *(data + 1) >> 4 : 0xF } };
1094
+ data += 1 + pp;
1095
+
1096
+ for (int k = 0; k < e.pieceCount; ++k, ++data)
1097
+ for (int i = 0; i < sides; i++)
1098
+ e.get(i, f)->pieces[k] = Piece(i ? *data >> 4 : *data & 0xF);
1099
+
1100
+ for (int i = 0; i < sides; ++i)
1101
+ set_groups(e, e.get(i, f), order[i], f);
1102
+ }
1103
+
1104
+ data += (uintptr_t)data & 1; // Word alignment
1105
+
1106
+ for (File f = FILE_A; f <= maxFile; ++f)
1107
+ for (int i = 0; i < sides; i++)
1108
+ data = set_sizes(e.get(i, f), data);
1109
+
1110
+ data = set_dtz_map(e, data, maxFile);
1111
+
1112
+ for (File f = FILE_A; f <= maxFile; ++f)
1113
+ for (int i = 0; i < sides; i++) {
1114
+ (d = e.get(i, f))->sparseIndex = (SparseEntry*)data;
1115
+ data += d->sparseIndexSize * sizeof(SparseEntry);
1116
+ }
1117
+
1118
+ for (File f = FILE_A; f <= maxFile; ++f)
1119
+ for (int i = 0; i < sides; i++) {
1120
+ (d = e.get(i, f))->blockLength = (uint16_t*)data;
1121
+ data += d->blockLengthSize * sizeof(uint16_t);
1122
+ }
1123
+
1124
+ for (File f = FILE_A; f <= maxFile; ++f)
1125
+ for (int i = 0; i < sides; i++) {
1126
+ data = (uint8_t*)(((uintptr_t)data + 0x3F) & ~0x3F); // 64 byte alignment
1127
+ (d = e.get(i, f))->data = data;
1128
+ data += d->blocksNum * d->sizeofBlock;
1129
+ }
1130
+ }
1131
+
1132
+ // If the TB file corresponding to the given position is already memory mapped
1133
+ // then return its base address, otherwise try to memory map and init it. Called
1134
+ // at every probe, memory map and init only at first access. Function is thread
1135
+ // safe and can be called concurrently.
1136
+ template<TBType Type>
1137
+ void* mapped(TBTable<Type>& e, const Position& pos) {
1138
+
1139
+ static std::mutex mutex;
1140
+
1141
+ // Use 'acquire' to avoid a thread reading 'ready' == true while
1142
+ // another is still working. (compiler reordering may cause this).
1143
+ if (e.ready.load(std::memory_order_acquire))
1144
+ return e.baseAddress; // Could be nullptr if file does not exist
1145
+
1146
+ std::scoped_lock<std::mutex> lk(mutex);
1147
+
1148
+ if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock
1149
+ return e.baseAddress;
1150
+
1151
+ // Pieces strings in decreasing order for each color, like ("KPP","KR")
1152
+ std::string fname, w, b;
1153
+ for (PieceType pt = KING; pt >= PAWN; --pt) {
1154
+ w += std::string(popcount(pos.pieces(WHITE, pt)), PieceToChar[pt]);
1155
+ b += std::string(popcount(pos.pieces(BLACK, pt)), PieceToChar[pt]);
1156
+ }
1157
+
1158
+ fname = (e.key == pos.material_key() ? w + 'v' + b : b + 'v' + w)
1159
+ + (Type == WDL ? ".rtbw" : ".rtbz");
1160
+
1161
+ uint8_t* data = TBFile(fname).map(&e.baseAddress, &e.mapping, Type);
1162
+
1163
+ if (data)
1164
+ set(e, data);
1165
+
1166
+ e.ready.store(true, std::memory_order_release);
1167
+ return e.baseAddress;
1168
+ }
1169
+
1170
+ template<TBType Type, typename Ret = typename TBTable<Type>::Ret>
1171
+ Ret probe_table(const Position& pos, ProbeState* result, WDLScore wdl = WDLDraw) {
1172
+
1173
+ if (pos.count<ALL_PIECES>() == 2) // KvK
1174
+ return Ret(WDLDraw);
1175
+
1176
+ TBTable<Type>* entry = TBTables.get<Type>(pos.material_key());
1177
+
1178
+ if (!entry || !mapped(*entry, pos))
1179
+ return *result = FAIL, Ret();
1180
+
1181
+ return do_probe_table(pos, entry, wdl, result);
1182
+ }
1183
+
1184
+ // For a position where the side to move has a winning capture it is not necessary
1185
+ // to store a winning value so the generator treats such positions as "don't cares"
1186
+ // and tries to assign to it a value that improves the compression ratio. Similarly,
1187
+ // if the side to move has a drawing capture, then the position is at least drawn.
1188
+ // If the position is won, then the TB needs to store a win value. But if the
1189
+ // position is drawn, the TB may store a loss value if that is better for compression.
1190
+ // All of this means that during probing, the engine must look at captures and probe
1191
+ // their results and must probe the position itself. The "best" result of these
1192
+ // probes is the correct result for the position.
1193
+ // DTZ tables do not store values when a following move is a zeroing winning move
1194
+ // (winning capture or winning pawn move). Also DTZ store wrong values for positions
1195
+ // where the best move is an ep-move (even if losing). So in all these cases set
1196
+ // the state to ZEROING_BEST_MOVE.
1197
+ template<bool CheckZeroingMoves>
1198
+ WDLScore search(Position& pos, ProbeState* result) {
1199
+
1200
+ WDLScore value, bestValue = WDLLoss;
1201
+ StateInfo st;
1202
+
1203
+ auto moveList = MoveList<LEGAL>(pos);
1204
+ size_t totalCount = moveList.size(), moveCount = 0;
1205
+
1206
+ for (const Move move : moveList)
1207
+ {
1208
+ if ( !pos.capture(move)
1209
+ && (!CheckZeroingMoves || type_of(pos.moved_piece(move)) != PAWN))
1210
+ continue;
1211
+
1212
+ moveCount++;
1213
+
1214
+ pos.do_move(move, st);
1215
+ value = -search<false>(pos, result);
1216
+ pos.undo_move(move);
1217
+
1218
+ if (*result == FAIL)
1219
+ return WDLDraw;
1220
+
1221
+ if (value > bestValue)
1222
+ {
1223
+ bestValue = value;
1224
+
1225
+ if (value >= WDLWin)
1226
+ {
1227
+ *result = ZEROING_BEST_MOVE; // Winning DTZ-zeroing move
1228
+ return value;
1229
+ }
1230
+ }
1231
+ }
1232
+
1233
+ // In case we have already searched all the legal moves we don't have to probe
1234
+ // the TB because the stored score could be wrong. For instance TB tables
1235
+ // do not contain information on position with ep rights, so in this case
1236
+ // the result of probe_wdl_table is wrong. Also in case of only capture
1237
+ // moves, for instance here 4K3/4q3/6p1/2k5/6p1/8/8/8 w - - 0 7, we have to
1238
+ // return with ZEROING_BEST_MOVE set.
1239
+ bool noMoreMoves = (moveCount && moveCount == totalCount);
1240
+
1241
+ if (noMoreMoves)
1242
+ value = bestValue;
1243
+ else
1244
+ {
1245
+ value = probe_table<WDL>(pos, result);
1246
+
1247
+ if (*result == FAIL)
1248
+ return WDLDraw;
1249
+ }
1250
+
1251
+ // DTZ stores a "don't care" value if bestValue is a win
1252
+ if (bestValue >= value)
1253
+ return *result = ( bestValue > WDLDraw
1254
+ || noMoreMoves ? ZEROING_BEST_MOVE : OK), bestValue;
1255
+
1256
+ return *result = OK, value;
1257
+ }
1258
+
1259
+ } // namespace
1260
+
1261
+
1262
+ /// Tablebases::init() is called at startup and after every change to
1263
+ /// "SyzygyPath" UCI option to (re)create the various tables. It is not thread
1264
+ /// safe, nor it needs to be.
1265
+ void Tablebases::init(const std::string& paths) {
1266
+
1267
+ TBTables.clear();
1268
+ MaxCardinality = 0;
1269
+ TBFile::Paths = paths;
1270
+
1271
+ if (paths.empty() || paths == "<empty>")
1272
+ return;
1273
+
1274
+ // MapB1H1H7[] encodes a square below a1-h8 diagonal to 0..27
1275
+ int code = 0;
1276
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
1277
+ if (off_A1H8(s) < 0)
1278
+ MapB1H1H7[s] = code++;
1279
+
1280
+ // MapA1D1D4[] encodes a square in the a1-d1-d4 triangle to 0..9
1281
+ std::vector<Square> diagonal;
1282
+ code = 0;
1283
+ for (Square s = SQ_A1; s <= SQ_D4; ++s)
1284
+ if (off_A1H8(s) < 0 && file_of(s) <= FILE_D)
1285
+ MapA1D1D4[s] = code++;
1286
+
1287
+ else if (!off_A1H8(s) && file_of(s) <= FILE_D)
1288
+ diagonal.push_back(s);
1289
+
1290
+ // Diagonal squares are encoded as last ones
1291
+ for (auto s : diagonal)
1292
+ MapA1D1D4[s] = code++;
1293
+
1294
+ // MapKK[] encodes all the 462 possible legal positions of two kings where
1295
+ // the first is in the a1-d1-d4 triangle. If the first king is on the a1-d4
1296
+ // diagonal, the other one shall not to be above the a1-h8 diagonal.
1297
+ std::vector<std::pair<int, Square>> bothOnDiagonal;
1298
+ code = 0;
1299
+ for (int idx = 0; idx < 10; idx++)
1300
+ for (Square s1 = SQ_A1; s1 <= SQ_D4; ++s1)
1301
+ if (MapA1D1D4[s1] == idx && (idx || s1 == SQ_B1)) // SQ_B1 is mapped to 0
1302
+ {
1303
+ for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
1304
+ if ((PseudoAttacks[KING][s1] | s1) & s2)
1305
+ continue; // Illegal position
1306
+
1307
+ else if (!off_A1H8(s1) && off_A1H8(s2) > 0)
1308
+ continue; // First on diagonal, second above
1309
+
1310
+ else if (!off_A1H8(s1) && !off_A1H8(s2))
1311
+ bothOnDiagonal.emplace_back(idx, s2);
1312
+
1313
+ else
1314
+ MapKK[idx][s2] = code++;
1315
+ }
1316
+
1317
+ // Legal positions with both kings on diagonal are encoded as last ones
1318
+ for (auto p : bothOnDiagonal)
1319
+ MapKK[p.first][p.second] = code++;
1320
+
1321
+ // Binomial[] stores the Binomial Coefficients using Pascal rule. There
1322
+ // are Binomial[k][n] ways to choose k elements from a set of n elements.
1323
+ Binomial[0][0] = 1;
1324
+
1325
+ for (int n = 1; n < 64; n++) // Squares
1326
+ for (int k = 0; k < 6 && k <= n; ++k) // Pieces
1327
+ Binomial[k][n] = (k > 0 ? Binomial[k - 1][n - 1] : 0)
1328
+ + (k < n ? Binomial[k ][n - 1] : 0);
1329
+
1330
+ // MapPawns[s] encodes squares a2-h7 to 0..47. This is the number of possible
1331
+ // available squares when the leading one is in 's'. Moreover the pawn with
1332
+ // highest MapPawns[] is the leading pawn, the one nearest the edge and,
1333
+ // among pawns with same file, the one with lowest rank.
1334
+ int availableSquares = 47; // Available squares when lead pawn is in a2
1335
+
1336
+ // Init the tables for the encoding of leading pawns group: with 7-men TB we
1337
+ // can have up to 5 leading pawns (KPPPPPK).
1338
+ for (int leadPawnsCnt = 1; leadPawnsCnt <= 5; ++leadPawnsCnt)
1339
+ for (File f = FILE_A; f <= FILE_D; ++f)
1340
+ {
1341
+ // Restart the index at every file because TB table is split
1342
+ // by file, so we can reuse the same index for different files.
1343
+ int idx = 0;
1344
+
1345
+ // Sum all possible combinations for a given file, starting with
1346
+ // the leading pawn on rank 2 and increasing the rank.
1347
+ for (Rank r = RANK_2; r <= RANK_7; ++r)
1348
+ {
1349
+ Square sq = make_square(f, r);
1350
+
1351
+ // Compute MapPawns[] at first pass.
1352
+ // If sq is the leading pawn square, any other pawn cannot be
1353
+ // below or more toward the edge of sq. There are 47 available
1354
+ // squares when sq = a2 and reduced by 2 for any rank increase
1355
+ // due to mirroring: sq == a3 -> no a2, h2, so MapPawns[a3] = 45
1356
+ if (leadPawnsCnt == 1)
1357
+ {
1358
+ MapPawns[sq] = availableSquares--;
1359
+ MapPawns[flip_file(sq)] = availableSquares--;
1360
+ }
1361
+ LeadPawnIdx[leadPawnsCnt][sq] = idx;
1362
+ idx += Binomial[leadPawnsCnt - 1][MapPawns[sq]];
1363
+ }
1364
+ // After a file is traversed, store the cumulated per-file index
1365
+ LeadPawnsSize[leadPawnsCnt][f] = idx;
1366
+ }
1367
+
1368
+ // Add entries in TB tables if the corresponding ".rtbw" file exists
1369
+ for (PieceType p1 = PAWN; p1 < KING; ++p1) {
1370
+ TBTables.add({KING, p1, KING});
1371
+
1372
+ for (PieceType p2 = PAWN; p2 <= p1; ++p2) {
1373
+ TBTables.add({KING, p1, p2, KING});
1374
+ TBTables.add({KING, p1, KING, p2});
1375
+
1376
+ for (PieceType p3 = PAWN; p3 < KING; ++p3)
1377
+ TBTables.add({KING, p1, p2, KING, p3});
1378
+
1379
+ for (PieceType p3 = PAWN; p3 <= p2; ++p3) {
1380
+ TBTables.add({KING, p1, p2, p3, KING});
1381
+
1382
+ for (PieceType p4 = PAWN; p4 <= p3; ++p4) {
1383
+ TBTables.add({KING, p1, p2, p3, p4, KING});
1384
+
1385
+ for (PieceType p5 = PAWN; p5 <= p4; ++p5)
1386
+ TBTables.add({KING, p1, p2, p3, p4, p5, KING});
1387
+
1388
+ for (PieceType p5 = PAWN; p5 < KING; ++p5)
1389
+ TBTables.add({KING, p1, p2, p3, p4, KING, p5});
1390
+ }
1391
+
1392
+ for (PieceType p4 = PAWN; p4 < KING; ++p4) {
1393
+ TBTables.add({KING, p1, p2, p3, KING, p4});
1394
+
1395
+ for (PieceType p5 = PAWN; p5 <= p4; ++p5)
1396
+ TBTables.add({KING, p1, p2, p3, KING, p4, p5});
1397
+ }
1398
+ }
1399
+
1400
+ for (PieceType p3 = PAWN; p3 <= p1; ++p3)
1401
+ for (PieceType p4 = PAWN; p4 <= (p1 == p3 ? p2 : p3); ++p4)
1402
+ TBTables.add({KING, p1, p2, KING, p3, p4});
1403
+ }
1404
+ }
1405
+
1406
+ sync_cout << "info string Found " << TBTables.size() << " tablebases" << sync_endl;
1407
+ }
1408
+
1409
+ // Probe the WDL table for a particular position.
1410
+ // If *result != FAIL, the probe was successful.
1411
+ // The return value is from the point of view of the side to move:
1412
+ // -2 : loss
1413
+ // -1 : loss, but draw under 50-move rule
1414
+ // 0 : draw
1415
+ // 1 : win, but draw under 50-move rule
1416
+ // 2 : win
1417
+ WDLScore Tablebases::probe_wdl(Position& pos, ProbeState* result) {
1418
+
1419
+ *result = OK;
1420
+ return search<false>(pos, result);
1421
+ }
1422
+
1423
+ // Probe the DTZ table for a particular position.
1424
+ // If *result != FAIL, the probe was successful.
1425
+ // The return value is from the point of view of the side to move:
1426
+ // n < -100 : loss, but draw under 50-move rule
1427
+ // -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0)
1428
+ // -1 : loss, the side to move is mated
1429
+ // 0 : draw
1430
+ // 1 < n <= 100 : win in n ply (assuming 50-move counter == 0)
1431
+ // 100 < n : win, but draw under 50-move rule
1432
+ //
1433
+ // The return value n can be off by 1: a return value -n can mean a loss
1434
+ // in n+1 ply and a return value +n can mean a win in n+1 ply. This
1435
+ // cannot happen for tables with positions exactly on the "edge" of
1436
+ // the 50-move rule.
1437
+ //
1438
+ // This implies that if dtz > 0 is returned, the position is certainly
1439
+ // a win if dtz + 50-move-counter <= 99. Care must be taken that the engine
1440
+ // picks moves that preserve dtz + 50-move-counter <= 99.
1441
+ //
1442
+ // If n = 100 immediately after a capture or pawn move, then the position
1443
+ // is also certainly a win, and during the whole phase until the next
1444
+ // capture or pawn move, the inequality to be preserved is
1445
+ // dtz + 50-move-counter <= 100.
1446
+ //
1447
+ // In short, if a move is available resulting in dtz + 50-move-counter <= 99,
1448
+ // then do not accept moves leading to dtz + 50-move-counter == 100.
1449
+ int Tablebases::probe_dtz(Position& pos, ProbeState* result) {
1450
+
1451
+ *result = OK;
1452
+ WDLScore wdl = search<true>(pos, result);
1453
+
1454
+ if (*result == FAIL || wdl == WDLDraw) // DTZ tables don't store draws
1455
+ return 0;
1456
+
1457
+ // DTZ stores a 'don't care' value in this case, or even a plain wrong
1458
+ // one as in case the best move is a losing ep, so it cannot be probed.
1459
+ if (*result == ZEROING_BEST_MOVE)
1460
+ return dtz_before_zeroing(wdl);
1461
+
1462
+ int dtz = probe_table<DTZ>(pos, result, wdl);
1463
+
1464
+ if (*result == FAIL)
1465
+ return 0;
1466
+
1467
+ if (*result != CHANGE_STM)
1468
+ return (dtz + 100 * (wdl == WDLBlessedLoss || wdl == WDLCursedWin)) * sign_of(wdl);
1469
+
1470
+ // DTZ stores results for the other side, so we need to do a 1-ply search and
1471
+ // find the winning move that minimizes DTZ.
1472
+ StateInfo st;
1473
+ int minDTZ = 0xFFFF;
1474
+
1475
+ for (const Move move : MoveList<LEGAL>(pos))
1476
+ {
1477
+ bool zeroing = pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN;
1478
+
1479
+ pos.do_move(move, st);
1480
+
1481
+ // For zeroing moves we want the dtz of the move _before_ doing it,
1482
+ // otherwise we will get the dtz of the next move sequence. Search the
1483
+ // position after the move to get the score sign (because even in a
1484
+ // winning position we could make a losing capture or going for a draw).
1485
+ dtz = zeroing ? -dtz_before_zeroing(search<false>(pos, result))
1486
+ : -probe_dtz(pos, result);
1487
+
1488
+ // If the move mates, force minDTZ to 1
1489
+ if (dtz == 1 && pos.checkers() && MoveList<LEGAL>(pos).size() == 0)
1490
+ minDTZ = 1;
1491
+
1492
+ // Convert result from 1-ply search. Zeroing moves are already accounted
1493
+ // by dtz_before_zeroing() that returns the DTZ of the previous move.
1494
+ if (!zeroing)
1495
+ dtz += sign_of(dtz);
1496
+
1497
+ // Skip the draws and if we are winning only pick positive dtz
1498
+ if (dtz < minDTZ && sign_of(dtz) == sign_of(wdl))
1499
+ minDTZ = dtz;
1500
+
1501
+ pos.undo_move(move);
1502
+
1503
+ if (*result == FAIL)
1504
+ return 0;
1505
+ }
1506
+
1507
+ // When there are no legal moves, the position is mate: we return -1
1508
+ return minDTZ == 0xFFFF ? -1 : minDTZ;
1509
+ }
1510
+
1511
+
1512
+ // Use the DTZ tables to rank root moves.
1513
+ //
1514
+ // A return value false indicates that not all probes were successful.
1515
+ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) {
1516
+
1517
+ ProbeState result;
1518
+ StateInfo st;
1519
+
1520
+ // Obtain 50-move counter for the root position
1521
+ int cnt50 = pos.rule50_count();
1522
+
1523
+ // Check whether a position was repeated since the last zeroing move.
1524
+ bool rep = pos.has_repeated();
1525
+
1526
+ int dtz, bound = Options["Syzygy50MoveRule"] ? (MAX_DTZ - 100) : 1;
1527
+
1528
+ // Probe and rank each move
1529
+ for (auto& m : rootMoves)
1530
+ {
1531
+ pos.do_move(m.pv[0], st);
1532
+
1533
+ // Calculate dtz for the current move counting from the root position
1534
+ if (pos.rule50_count() == 0)
1535
+ {
1536
+ // In case of a zeroing move, dtz is one of -101/-1/0/1/101
1537
+ WDLScore wdl = -probe_wdl(pos, &result);
1538
+ dtz = dtz_before_zeroing(wdl);
1539
+ }
1540
+ else if (pos.is_draw(1))
1541
+ {
1542
+ // In case a root move leads to a draw by repetition or
1543
+ // 50-move rule, we set dtz to zero. Note: since we are
1544
+ // only 1 ply from the root, this must be a true 3-fold
1545
+ // repetition inside the game history.
1546
+ dtz = 0;
1547
+ }
1548
+ else
1549
+ {
1550
+ // Otherwise, take dtz for the new position and correct by 1 ply
1551
+ dtz = -probe_dtz(pos, &result);
1552
+ dtz = dtz > 0 ? dtz + 1
1553
+ : dtz < 0 ? dtz - 1 : dtz;
1554
+ }
1555
+
1556
+ // Make sure that a mating move is assigned a dtz value of 1
1557
+ if ( pos.checkers()
1558
+ && dtz == 2
1559
+ && MoveList<LEGAL>(pos).size() == 0)
1560
+ dtz = 1;
1561
+
1562
+ pos.undo_move(m.pv[0]);
1563
+
1564
+ if (result == FAIL)
1565
+ return false;
1566
+
1567
+ // Better moves are ranked higher. Certain wins are ranked equally.
1568
+ // Losing moves are ranked equally unless a 50-move draw is in sight.
1569
+ int r = dtz > 0 ? (dtz + cnt50 <= 99 && !rep ? MAX_DTZ : MAX_DTZ - (dtz + cnt50))
1570
+ : dtz < 0 ? (-dtz * 2 + cnt50 < 100 ? -MAX_DTZ : -MAX_DTZ + (-dtz + cnt50))
1571
+ : 0;
1572
+ m.tbRank = r;
1573
+
1574
+ // Determine the score to be displayed for this move. Assign at least
1575
+ // 1 cp to cursed wins and let it grow to 49 cp as the positions gets
1576
+ // closer to a real win.
1577
+ m.tbScore = r >= bound ? VALUE_MATE - MAX_PLY - 1
1578
+ : r > 0 ? Value((std::max( 3, r - (MAX_DTZ - 200)) * int(PawnValueEg)) / 200)
1579
+ : r == 0 ? VALUE_DRAW
1580
+ : r > -bound ? Value((std::min(-3, r + (MAX_DTZ - 200)) * int(PawnValueEg)) / 200)
1581
+ : -VALUE_MATE + MAX_PLY + 1;
1582
+ }
1583
+
1584
+ return true;
1585
+ }
1586
+
1587
+
1588
+ // Use the WDL tables to rank root moves.
1589
+ // This is a fallback for the case that some or all DTZ tables are missing.
1590
+ //
1591
+ // A return value false indicates that not all probes were successful.
1592
+ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) {
1593
+
1594
+ static const int WDL_to_rank[] = { -MAX_DTZ, -MAX_DTZ + 101, 0, MAX_DTZ - 101, MAX_DTZ };
1595
+
1596
+ ProbeState result;
1597
+ StateInfo st;
1598
+ WDLScore wdl;
1599
+
1600
+ bool rule50 = Options["Syzygy50MoveRule"];
1601
+
1602
+ // Probe and rank each move
1603
+ for (auto& m : rootMoves)
1604
+ {
1605
+ pos.do_move(m.pv[0], st);
1606
+
1607
+ if (pos.is_draw(1))
1608
+ wdl = WDLDraw;
1609
+ else
1610
+ wdl = -probe_wdl(pos, &result);
1611
+
1612
+ pos.undo_move(m.pv[0]);
1613
+
1614
+ if (result == FAIL)
1615
+ return false;
1616
+
1617
+ m.tbRank = WDL_to_rank[wdl + 2];
1618
+
1619
+ if (!rule50)
1620
+ wdl = wdl > WDLDraw ? WDLWin
1621
+ : wdl < WDLDraw ? WDLLoss : WDLDraw;
1622
+ m.tbScore = WDL_to_value[wdl + 2];
1623
+ }
1624
+
1625
+ return true;
1626
+ }
1627
+
1628
+ } // namespace Stockfish
src/syzygy/tbprobe.h ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef TBPROBE_H
20
+ #define TBPROBE_H
21
+
22
+ #include <ostream>
23
+
24
+ #include "../search.h"
25
+
26
+ namespace Stockfish::Tablebases {
27
+
28
+ enum WDLScore {
29
+ WDLLoss = -2, // Loss
30
+ WDLBlessedLoss = -1, // Loss, but draw under 50-move rule
31
+ WDLDraw = 0, // Draw
32
+ WDLCursedWin = 1, // Win, but draw under 50-move rule
33
+ WDLWin = 2, // Win
34
+ };
35
+
36
+ // Possible states after a probing operation
37
+ enum ProbeState {
38
+ FAIL = 0, // Probe failed (missing file table)
39
+ OK = 1, // Probe successful
40
+ CHANGE_STM = -1, // DTZ should check the other side
41
+ ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move)
42
+ };
43
+
44
+ extern int MaxCardinality;
45
+
46
+ void init(const std::string& paths);
47
+ WDLScore probe_wdl(Position& pos, ProbeState* result);
48
+ int probe_dtz(Position& pos, ProbeState* result);
49
+ bool root_probe(Position& pos, Search::RootMoves& rootMoves);
50
+ bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves);
51
+ void rank_root_moves(Position& pos, Search::RootMoves& rootMoves);
52
+
53
+ inline std::ostream& operator<<(std::ostream& os, const WDLScore v) {
54
+
55
+ os << (v == WDLLoss ? "Loss" :
56
+ v == WDLBlessedLoss ? "Blessed loss" :
57
+ v == WDLDraw ? "Draw" :
58
+ v == WDLCursedWin ? "Cursed win" :
59
+ v == WDLWin ? "Win" : "None");
60
+
61
+ return os;
62
+ }
63
+
64
+ inline std::ostream& operator<<(std::ostream& os, const ProbeState v) {
65
+
66
+ os << (v == FAIL ? "Failed" :
67
+ v == OK ? "Success" :
68
+ v == CHANGE_STM ? "Probed opponent side" :
69
+ v == ZEROING_BEST_MOVE ? "Best move zeroes DTZ" : "None");
70
+
71
+ return os;
72
+ }
73
+
74
+ } // namespace Stockfish::Tablebases
75
+
76
+ #endif
src/thread.cpp ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <cassert>
20
+
21
+ #include <algorithm> // For std::count
22
+ #include "movegen.h"
23
+ #include "search.h"
24
+ #include "thread.h"
25
+ #include "uci.h"
26
+ #include "syzygy/tbprobe.h"
27
+ #include "tt.h"
28
+
29
+ namespace Stockfish {
30
+
31
+ ThreadPool Threads; // Global object
32
+
33
+
34
+ /// Thread constructor launches the thread and waits until it goes to sleep
35
+ /// in idle_loop(). Note that 'searching' and 'exit' should be already set.
36
+
37
+ Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) {
38
+
39
+ wait_for_search_finished();
40
+ }
41
+
42
+
43
+ /// Thread destructor wakes up the thread in idle_loop() and waits
44
+ /// for its termination. Thread should be already waiting.
45
+
46
+ Thread::~Thread() {
47
+
48
+ assert(!searching);
49
+
50
+ exit = true;
51
+ start_searching();
52
+ stdThread.join();
53
+ }
54
+
55
+
56
+ /// Thread::clear() reset histories, usually before a new game
57
+
58
+ void Thread::clear() {
59
+
60
+ counterMoves.fill(MOVE_NONE);
61
+ mainHistory.fill(0);
62
+ captureHistory.fill(0);
63
+ previousDepth = 0;
64
+
65
+ for (bool inCheck : { false, true })
66
+ for (StatsType c : { NoCaptures, Captures })
67
+ for (auto& to : continuationHistory[inCheck][c])
68
+ for (auto& h : to)
69
+ h->fill(-71);
70
+ }
71
+
72
+
73
+ /// Thread::start_searching() wakes up the thread that will start the search
74
+
75
+ void Thread::start_searching() {
76
+
77
+ std::lock_guard<std::mutex> lk(mutex);
78
+ searching = true;
79
+ cv.notify_one(); // Wake up the thread in idle_loop()
80
+ }
81
+
82
+
83
+ /// Thread::wait_for_search_finished() blocks on the condition variable
84
+ /// until the thread has finished searching.
85
+
86
+ void Thread::wait_for_search_finished() {
87
+
88
+ std::unique_lock<std::mutex> lk(mutex);
89
+ cv.wait(lk, [&]{ return !searching; });
90
+ }
91
+
92
+
93
+ /// Thread::idle_loop() is where the thread is parked, blocked on the
94
+ /// condition variable, when it has no work to do.
95
+
96
+ void Thread::idle_loop() {
97
+
98
+ // If OS already scheduled us on a different group than 0 then don't overwrite
99
+ // the choice, eventually we are one of many one-threaded processes running on
100
+ // some Windows NUMA hardware, for instance in fishtest. To make it simple,
101
+ // just check if running threads are below a threshold, in this case all this
102
+ // NUMA machinery is not needed.
103
+ if (Options["Threads"] > 8)
104
+ WinProcGroup::bindThisThread(idx);
105
+
106
+ while (true)
107
+ {
108
+ std::unique_lock<std::mutex> lk(mutex);
109
+ searching = false;
110
+ cv.notify_one(); // Wake up anyone waiting for search finished
111
+ cv.wait(lk, [&]{ return searching; });
112
+
113
+ if (exit)
114
+ return;
115
+
116
+ lk.unlock();
117
+
118
+ search();
119
+ }
120
+ }
121
+
122
+ /// ThreadPool::set() creates/destroys threads to match the requested number.
123
+ /// Created and launched threads will immediately go to sleep in idle_loop.
124
+ /// Upon resizing, threads are recreated to allow for binding if necessary.
125
+
126
+ void ThreadPool::set(size_t requested) {
127
+
128
+ if (size() > 0) // destroy any existing thread(s)
129
+ {
130
+ main()->wait_for_search_finished();
131
+
132
+ while (size() > 0)
133
+ delete back(), pop_back();
134
+ }
135
+
136
+ if (requested > 0) // create new thread(s)
137
+ {
138
+ push_back(new MainThread(0));
139
+
140
+ while (size() < requested)
141
+ push_back(new Thread(size()));
142
+ clear();
143
+
144
+ // Reallocate the hash with the new threadpool size
145
+ TT.resize(size_t(Options["Hash"]));
146
+
147
+ // Init thread number dependent search params.
148
+ Search::init();
149
+ }
150
+ }
151
+
152
+
153
+ /// ThreadPool::clear() sets threadPool data to initial values
154
+
155
+ void ThreadPool::clear() {
156
+
157
+ for (Thread* th : *this)
158
+ th->clear();
159
+
160
+ main()->callsCnt = 0;
161
+ main()->bestPreviousScore = VALUE_INFINITE;
162
+ main()->bestPreviousAverageScore = VALUE_INFINITE;
163
+ main()->previousTimeReduction = 1.0;
164
+ }
165
+
166
+
167
+ /// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and
168
+ /// returns immediately. Main thread will wake up other threads and start the search.
169
+
170
+ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
171
+ const Search::LimitsType& limits, bool ponderMode) {
172
+
173
+ main()->wait_for_search_finished();
174
+
175
+ main()->stopOnPonderhit = stop = false;
176
+ increaseDepth = true;
177
+ main()->ponder = ponderMode;
178
+ Search::Limits = limits;
179
+ Search::RootMoves rootMoves;
180
+
181
+ for (const auto& m : MoveList<LEGAL>(pos))
182
+ if ( limits.searchmoves.empty()
183
+ || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
184
+ rootMoves.emplace_back(m);
185
+
186
+ if (!rootMoves.empty())
187
+ Tablebases::rank_root_moves(pos, rootMoves);
188
+
189
+ // After ownership transfer 'states' becomes empty, so if we stop the search
190
+ // and call 'go' again without setting a new position states.get() == NULL.
191
+ assert(states.get() || setupStates.get());
192
+
193
+ if (states.get())
194
+ setupStates = std::move(states); // Ownership transfer, states is now empty
195
+
196
+ // We use Position::set() to set root position across threads. But there are
197
+ // some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot
198
+ // be deduced from a fen string, so set() clears them and they are set from
199
+ // setupStates->back() later. The rootState is per thread, earlier states are shared
200
+ // since they are read-only.
201
+ for (Thread* th : *this)
202
+ {
203
+ th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0;
204
+ th->rootDepth = th->completedDepth = 0;
205
+ th->rootMoves = rootMoves;
206
+ th->rootPos.set(pos.fen(), pos.is_chess960(), &th->rootState, th);
207
+ th->rootState = setupStates->back();
208
+ }
209
+
210
+ main()->start_searching();
211
+ }
212
+
213
+ Thread* ThreadPool::get_best_thread() const {
214
+
215
+ Thread* bestThread = front();
216
+ std::map<Move, int64_t> votes;
217
+ Value minScore = VALUE_NONE;
218
+
219
+ // Find minimum score of all threads
220
+ for (Thread* th: *this)
221
+ minScore = std::min(minScore, th->rootMoves[0].score);
222
+
223
+ // Vote according to score and depth, and select the best thread
224
+ auto thread_value = [minScore](Thread* th) {
225
+ return (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
226
+ };
227
+
228
+ for (Thread* th : *this)
229
+ votes[th->rootMoves[0].pv[0]] += thread_value(th);
230
+
231
+ for (Thread* th : *this)
232
+ if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY)
233
+ {
234
+ // Make sure we pick the shortest mate / TB conversion or stave off mate the longest
235
+ if (th->rootMoves[0].score > bestThread->rootMoves[0].score)
236
+ bestThread = th;
237
+ }
238
+ else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY
239
+ || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY
240
+ && ( votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]
241
+ || ( votes[th->rootMoves[0].pv[0]] == votes[bestThread->rootMoves[0].pv[0]]
242
+ && thread_value(th) > thread_value(bestThread)))))
243
+ bestThread = th;
244
+
245
+ return bestThread;
246
+ }
247
+
248
+
249
+ /// Start non-main threads
250
+
251
+ void ThreadPool::start_searching() {
252
+
253
+ for (Thread* th : *this)
254
+ if (th != front())
255
+ th->start_searching();
256
+ }
257
+
258
+
259
+ /// Wait for non-main threads
260
+
261
+ void ThreadPool::wait_for_search_finished() const {
262
+
263
+ for (Thread* th : *this)
264
+ if (th != front())
265
+ th->wait_for_search_finished();
266
+ }
267
+
268
+ } // namespace Stockfish
src/thread.h ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef THREAD_H_INCLUDED
20
+ #define THREAD_H_INCLUDED
21
+
22
+ #include <atomic>
23
+ #include <condition_variable>
24
+ #include <mutex>
25
+ #include <thread>
26
+ #include <vector>
27
+
28
+ #include "material.h"
29
+ #include "movepick.h"
30
+ #include "pawns.h"
31
+ #include "position.h"
32
+ #include "search.h"
33
+ #include "thread_win32_osx.h"
34
+
35
+ namespace Stockfish {
36
+
37
+ /// Thread class keeps together all the thread-related stuff. We use
38
+ /// per-thread pawn and material hash tables so that once we get a
39
+ /// pointer to an entry its life time is unlimited and we don't have
40
+ /// to care about someone changing the entry under our feet.
41
+
42
+ class Thread {
43
+
44
+ std::mutex mutex;
45
+ std::condition_variable cv;
46
+ size_t idx;
47
+ bool exit = false, searching = true; // Set before starting std::thread
48
+ NativeThread stdThread;
49
+
50
+ public:
51
+ explicit Thread(size_t);
52
+ virtual ~Thread();
53
+ virtual void search();
54
+ void clear();
55
+ void idle_loop();
56
+ void start_searching();
57
+ void wait_for_search_finished();
58
+ size_t id() const { return idx; }
59
+
60
+ Pawns::Table pawnsTable;
61
+ Material::Table materialTable;
62
+ size_t pvIdx, pvLast;
63
+ RunningAverage complexityAverage;
64
+ std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
65
+ int selDepth, nmpMinPly;
66
+ Color nmpColor;
67
+ Value bestValue, optimism[COLOR_NB];
68
+
69
+ Position rootPos;
70
+ StateInfo rootState;
71
+ Search::RootMoves rootMoves;
72
+ Depth rootDepth, completedDepth, previousDepth;
73
+ Value rootDelta;
74
+ CounterMoveHistory counterMoves;
75
+ ButterflyHistory mainHistory;
76
+ CapturePieceToHistory captureHistory;
77
+ ContinuationHistory continuationHistory[2][2];
78
+ };
79
+
80
+
81
+ /// MainThread is a derived class specific for main thread
82
+
83
+ struct MainThread : public Thread {
84
+
85
+ using Thread::Thread;
86
+
87
+ void search() override;
88
+ void check_time();
89
+
90
+ double previousTimeReduction;
91
+ Value bestPreviousScore;
92
+ Value bestPreviousAverageScore;
93
+ Value iterValue[4];
94
+ int callsCnt;
95
+ bool stopOnPonderhit;
96
+ std::atomic_bool ponder;
97
+ };
98
+
99
+
100
+ /// ThreadPool struct handles all the threads-related stuff like init, starting,
101
+ /// parking and, most importantly, launching a thread. All the access to threads
102
+ /// is done through this class.
103
+
104
+ struct ThreadPool : public std::vector<Thread*> {
105
+
106
+ void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false);
107
+ void clear();
108
+ void set(size_t);
109
+
110
+ MainThread* main() const { return static_cast<MainThread*>(front()); }
111
+ uint64_t nodes_searched() const { return accumulate(&Thread::nodes); }
112
+ uint64_t tb_hits() const { return accumulate(&Thread::tbHits); }
113
+ Thread* get_best_thread() const;
114
+ void start_searching();
115
+ void wait_for_search_finished() const;
116
+
117
+ std::atomic_bool stop, increaseDepth;
118
+
119
+ private:
120
+ StateListPtr setupStates;
121
+
122
+ uint64_t accumulate(std::atomic<uint64_t> Thread::* member) const {
123
+
124
+ uint64_t sum = 0;
125
+ for (Thread* th : *this)
126
+ sum += (th->*member).load(std::memory_order_relaxed);
127
+ return sum;
128
+ }
129
+ };
130
+
131
+ extern ThreadPool Threads;
132
+
133
+ } // namespace Stockfish
134
+
135
+ #endif // #ifndef THREAD_H_INCLUDED
src/thread_win32_osx.h ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef THREAD_WIN32_OSX_H_INCLUDED
20
+ #define THREAD_WIN32_OSX_H_INCLUDED
21
+
22
+ #include <thread>
23
+
24
+ /// On OSX threads other than the main thread are created with a reduced stack
25
+ /// size of 512KB by default, this is too low for deep searches, which require
26
+ /// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE.
27
+ /// The implementation calls pthread_create() with the stack size parameter
28
+ /// equal to the linux 8MB default, on platforms that support it.
29
+
30
+ #if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS)
31
+
32
+ #include <pthread.h>
33
+
34
+ namespace Stockfish {
35
+
36
+ static const size_t TH_STACK_SIZE = 8 * 1024 * 1024;
37
+
38
+ template <class T, class P = std::pair<T*, void(T::*)()>>
39
+ void* start_routine(void* ptr)
40
+ {
41
+ P* p = reinterpret_cast<P*>(ptr);
42
+ (p->first->*(p->second))(); // Call member function pointer
43
+ delete p;
44
+ return NULL;
45
+ }
46
+
47
+ class NativeThread {
48
+
49
+ pthread_t thread;
50
+
51
+ public:
52
+ template<class T, class P = std::pair<T*, void(T::*)()>>
53
+ explicit NativeThread(void(T::*fun)(), T* obj) {
54
+ pthread_attr_t attr_storage, *attr = &attr_storage;
55
+ pthread_attr_init(attr);
56
+ pthread_attr_setstacksize(attr, TH_STACK_SIZE);
57
+ pthread_create(&thread, attr, start_routine<T>, new P(obj, fun));
58
+ }
59
+ void join() { pthread_join(thread, NULL); }
60
+ };
61
+
62
+ } // namespace Stockfish
63
+
64
+ #else // Default case: use STL classes
65
+
66
+ namespace Stockfish {
67
+
68
+ typedef std::thread NativeThread;
69
+
70
+ } // namespace Stockfish
71
+
72
+ #endif
73
+
74
+ #endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED
src/timeman.cpp ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <algorithm>
20
+ #include <cfloat>
21
+ #include <cmath>
22
+
23
+ #include "search.h"
24
+ #include "timeman.h"
25
+ #include "uci.h"
26
+
27
+ namespace Stockfish {
28
+
29
+ TimeManagement Time; // Our global time management object
30
+
31
+
32
+ /// TimeManagement::init() is called at the beginning of the search and calculates
33
+ /// the bounds of time allowed for the current game ply. We currently support:
34
+ // 1) x basetime (+ z increment)
35
+ // 2) x moves in y seconds (+ z increment)
36
+
37
+ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
38
+
39
+ TimePoint moveOverhead = TimePoint(Options["Move Overhead"]);
40
+ TimePoint slowMover = TimePoint(Options["Slow Mover"]);
41
+ TimePoint npmsec = TimePoint(Options["nodestime"]);
42
+
43
+ // optScale is a percentage of available time to use for the current move.
44
+ // maxScale is a multiplier applied to optimumTime.
45
+ double optScale, maxScale;
46
+
47
+ // If we have to play in 'nodes as time' mode, then convert from time
48
+ // to nodes, and use resulting values in time management formulas.
49
+ // WARNING: to avoid time losses, the given npmsec (nodes per millisecond)
50
+ // must be much lower than the real engine speed.
51
+ if (npmsec)
52
+ {
53
+ if (!availableNodes) // Only once at game start
54
+ availableNodes = npmsec * limits.time[us]; // Time is in msec
55
+
56
+ // Convert from milliseconds to nodes
57
+ limits.time[us] = TimePoint(availableNodes);
58
+ limits.inc[us] *= npmsec;
59
+ limits.npmsec = npmsec;
60
+ }
61
+
62
+ startTime = limits.startTime;
63
+
64
+ // Maximum move horizon of 50 moves
65
+ int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50;
66
+
67
+ // Make sure timeLeft is > 0 since we may use it as a divisor
68
+ TimePoint timeLeft = std::max(TimePoint(1),
69
+ limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg));
70
+
71
+ // Use extra time with larger increments
72
+ double optExtra = std::clamp(1.0 + 12.0 * limits.inc[us] / limits.time[us], 1.0, 1.12);
73
+
74
+ // A user may scale time usage by setting UCI option "Slow Mover"
75
+ // Default is 100 and changing this value will probably lose elo.
76
+ timeLeft = slowMover * timeLeft / 100;
77
+
78
+ // x basetime (+ z increment)
79
+ // If there is a healthy increment, timeLeft can exceed actual available
80
+ // game time for the current move, so also cap to 20% of available game time.
81
+ if (limits.movestogo == 0)
82
+ {
83
+ optScale = std::min(0.0120 + std::pow(ply + 3.0, 0.45) * 0.0039,
84
+ 0.2 * limits.time[us] / double(timeLeft))
85
+ * optExtra;
86
+ maxScale = std::min(7.0, 4.0 + ply / 12.0);
87
+ }
88
+
89
+ // x moves in y seconds (+ z increment)
90
+ else
91
+ {
92
+ optScale = std::min((0.88 + ply / 116.4) / mtg,
93
+ 0.88 * limits.time[us] / double(timeLeft));
94
+ maxScale = std::min(6.3, 1.5 + 0.11 * mtg);
95
+ }
96
+
97
+ // Never use more than 80% of the available time for this move
98
+ optimumTime = TimePoint(optScale * timeLeft);
99
+ maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime));
100
+
101
+ if (Options["Ponder"])
102
+ optimumTime += optimumTime / 4;
103
+ }
104
+
105
+ } // namespace Stockfish
src/timeman.h ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef TIMEMAN_H_INCLUDED
20
+ #define TIMEMAN_H_INCLUDED
21
+
22
+ #include "misc.h"
23
+ #include "search.h"
24
+ #include "thread.h"
25
+
26
+ namespace Stockfish {
27
+
28
+ /// The TimeManagement class computes the optimal time to think depending on
29
+ /// the maximum available time, the game move number and other parameters.
30
+
31
+ class TimeManagement {
32
+ public:
33
+ void init(Search::LimitsType& limits, Color us, int ply);
34
+ TimePoint optimum() const { return optimumTime; }
35
+ TimePoint maximum() const { return maximumTime; }
36
+ TimePoint elapsed() const { return Search::Limits.npmsec ?
37
+ TimePoint(Threads.nodes_searched()) : now() - startTime; }
38
+
39
+ int64_t availableNodes; // When in 'nodes as time' mode
40
+
41
+ private:
42
+ TimePoint startTime;
43
+ TimePoint optimumTime;
44
+ TimePoint maximumTime;
45
+ };
46
+
47
+ extern TimeManagement Time;
48
+
49
+ } // namespace Stockfish
50
+
51
+ #endif // #ifndef TIMEMAN_H_INCLUDED
src/tt.cpp ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
+ Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
4
+
5
+ Stockfish is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Stockfish is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <cstring> // For std::memset
20
+ #include <iostream>
21
+ #include <thread>
22
+
23
+ #include "bitboard.h"
24
+ #include "misc.h"
25
+ #include "thread.h"
26
+ #include "tt.h"
27
+ #include "uci.h"
28
+
29
+ namespace Stockfish {
30
+
31
+ TranspositionTable TT; // Our global transposition table
32
+
33
+ /// TTEntry::save() populates the TTEntry with a new node's data, possibly
34
+ /// overwriting an old position. Update is not atomic and can be racy.
35
+
36
+ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) {
37
+
38
+ // Preserve any existing move for the same position
39
+ if (m || (uint16_t)k != key16)
40
+ move16 = (uint16_t)m;
41
+
42
+ // Overwrite less valuable entries (cheapest checks first)
43
+ if ( b == BOUND_EXACT
44
+ || (uint16_t)k != key16
45
+ || d - DEPTH_OFFSET + 2 * pv > depth8 - 4)
46
+ {
47
+ assert(d > DEPTH_OFFSET);
48
+ assert(d < 256 + DEPTH_OFFSET);
49
+
50
+ key16 = (uint16_t)k;
51
+ depth8 = (uint8_t)(d - DEPTH_OFFSET);
52
+ genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
53
+ value16 = (int16_t)v;
54
+ eval16 = (int16_t)ev;
55
+ }
56
+ }
57
+
58
+
59
+ /// TranspositionTable::resize() sets the size of the transposition table,
60
+ /// measured in megabytes. Transposition table consists of a power of 2 number
61
+ /// of clusters and each cluster consists of ClusterSize number of TTEntry.
62
+
63
+ void TranspositionTable::resize(size_t mbSize) {
64
+
65
+ Threads.main()->wait_for_search_finished();
66
+
67
+ aligned_large_pages_free(table);
68
+
69
+ clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
70
+
71
+ table = static_cast<Cluster*>(aligned_large_pages_alloc(clusterCount * sizeof(Cluster)));
72
+ if (!table)
73
+ {
74
+ std::cerr << "Failed to allocate " << mbSize
75
+ << "MB for transposition table." << std::endl;
76
+ exit(EXIT_FAILURE);
77
+ }
78
+
79
+ clear();
80
+ }
81
+
82
+
83
+ /// TranspositionTable::clear() initializes the entire transposition table to zero,
84
+ // in a multi-threaded way.
85
+
86
+ void TranspositionTable::clear() {
87
+
88
+ std::vector<std::thread> threads;
89
+
90
+ for (size_t idx = 0; idx < Options["Threads"]; ++idx)
91
+ {
92
+ threads.emplace_back([this, idx]() {
93
+
94
+ // Thread binding gives faster search on systems with a first-touch policy
95
+ if (Options["Threads"] > 8)
96
+ WinProcGroup::bindThisThread(idx);
97
+
98
+ // Each thread will zero its part of the hash table
99
+ const size_t stride = size_t(clusterCount / Options["Threads"]),
100
+ start = size_t(stride * idx),
101
+ len = idx != Options["Threads"] - 1 ?
102
+ stride : clusterCount - start;
103
+
104
+ std::memset(&table[start], 0, len * sizeof(Cluster));
105
+ });
106
+ }
107
+
108
+ for (std::thread& th : threads)
109
+ th.join();
110
+ }
111
+
112
+
113
+ /// TranspositionTable::probe() looks up the current position in the transposition
114
+ /// table. It returns true and a pointer to the TTEntry if the position is found.
115
+ /// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry
116
+ /// to be replaced later. The replace value of an entry is calculated as its depth
117
+ /// minus 8 times its relative age. TTEntry t1 is considered more valuable than
118
+ /// TTEntry t2 if its replace value is greater than that of t2.
119
+
120
+ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
121
+
122
+ TTEntry* const tte = first_entry(key);
123
+ const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster
124
+
125
+ for (int i = 0; i < ClusterSize; ++i)
126
+ if (tte[i].key16 == key16 || !tte[i].depth8)
127
+ {
128
+ tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & (GENERATION_DELTA - 1))); // Refresh
129
+
130
+ return found = (bool)tte[i].depth8, &tte[i];
131
+ }
132
+
133
+ // Find an entry to be replaced according to the replacement strategy
134
+ TTEntry* replace = tte;
135
+ for (int i = 1; i < ClusterSize; ++i)
136
+ // Due to our packed storage format for generation and its cyclic
137
+ // nature we add GENERATION_CYCLE (256 is the modulus, plus what
138
+ // is needed to keep the unrelated lowest n bits from affecting
139
+ // the result) to calculate the entry age correctly even after
140
+ // generation8 overflows into the next cycle.
141
+ if ( replace->depth8 - ((GENERATION_CYCLE + generation8 - replace->genBound8) & GENERATION_MASK)
142
+ > tte[i].depth8 - ((GENERATION_CYCLE + generation8 - tte[i].genBound8) & GENERATION_MASK))
143
+ replace = &tte[i];
144
+
145
+ return found = false, replace;
146
+ }
147
+
148
+
149
+ /// TranspositionTable::hashfull() returns an approximation of the hashtable
150
+ /// occupation during a search. The hash is x permill full, as per UCI protocol.
151
+
152
+ int TranspositionTable::hashfull() const {
153
+
154
+ int cnt = 0;
155
+ for (int i = 0; i < 1000; ++i)
156
+ for (int j = 0; j < ClusterSize; ++j)
157
+ cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8;
158
+
159
+ return cnt / ClusterSize;
160
+ }
161
+
162
+ } // namespace Stockfish