Thanks for using Compiler Explorer
Sponsors
Jakt
C++
Ada
Analysis
Android Java
Android Kotlin
Assembly
C
C3
Carbon
C++ (Circle)
CIRCT
Clean
CMake
CMakeScript
COBOL
C++ for OpenCL
MLIR
Cppx
Cppx-Blue
Cppx-Gold
Cpp2-cppfront
Crystal
C#
CUDA C++
D
Dart
Elixir
Erlang
Fortran
F#
Go
Haskell
HLSL
Hook
Hylo
ispc
Java
Julia
Kotlin
LLVM IR
LLVM MIR
Modula-2
Nim
Objective-C
Objective-C++
OCaml
OpenCL C
Pascal
Pony
Python
Racket
Ruby
Rust
Snowball
Scala
Solidity
Spice
Swift
LLVM TableGen
Toit
TypeScript Native
V
Vala
Visual Basic
Zig
Javascript
GIMPLE
c++ source #1
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
6502-c++ 11.1.0
ARM GCC 10.2.0
ARM GCC 10.3.0
ARM GCC 10.4.0
ARM GCC 10.5.0
ARM GCC 11.1.0
ARM GCC 11.2.0
ARM GCC 11.3.0
ARM GCC 11.4.0
ARM GCC 12.1.0
ARM GCC 12.2.0
ARM GCC 12.3.0
ARM GCC 13.1.0
ARM GCC 13.2.0
ARM GCC 13.2.0 (unknown-eabi)
ARM GCC 4.5.4
ARM GCC 4.6.4
ARM GCC 5.4
ARM GCC 6.3.0
ARM GCC 6.4.0
ARM GCC 7.3.0
ARM GCC 7.5.0
ARM GCC 8.2.0
ARM GCC 8.5.0
ARM GCC 9.3.0
ARM GCC 9.4.0
ARM GCC 9.5.0
ARM GCC trunk
ARM gcc 10.2.1 (none)
ARM gcc 10.3.1 (2021.07 none)
ARM gcc 10.3.1 (2021.10 none)
ARM gcc 11.2.1 (none)
ARM gcc 5.4.1 (none)
ARM gcc 7.2.1 (none)
ARM gcc 8.2 (WinCE)
ARM gcc 8.3.1 (none)
ARM gcc 9.2.1 (none)
ARM msvc v19.0 (WINE)
ARM msvc v19.10 (WINE)
ARM msvc v19.14 (WINE)
ARM64 Morello gcc 10.1 Alpha 2
ARM64 gcc 10.2
ARM64 gcc 10.3
ARM64 gcc 10.4
ARM64 gcc 10.5.0
ARM64 gcc 11.1
ARM64 gcc 11.2
ARM64 gcc 11.3
ARM64 gcc 11.4.0
ARM64 gcc 12.1
ARM64 gcc 12.2.0
ARM64 gcc 12.3.0
ARM64 gcc 13.1.0
ARM64 gcc 13.2.0
ARM64 gcc 5.4
ARM64 gcc 6.3
ARM64 gcc 6.4
ARM64 gcc 7.3
ARM64 gcc 7.5
ARM64 gcc 8.2
ARM64 gcc 8.5
ARM64 gcc 9.3
ARM64 gcc 9.4
ARM64 gcc 9.5
ARM64 gcc trunk
ARM64 msvc v19.14 (WINE)
AVR gcc 10.3.0
AVR gcc 11.1.0
AVR gcc 12.1.0
AVR gcc 12.2.0
AVR gcc 12.3.0
AVR gcc 13.1.0
AVR gcc 13.2.0
AVR gcc 4.5.4
AVR gcc 4.6.4
AVR gcc 5.4.0
AVR gcc 9.2.0
AVR gcc 9.3.0
Arduino Mega (1.8.9)
Arduino Uno (1.8.9)
BPF clang (trunk)
BPF clang 13.0.0
BPF clang 14.0.0
BPF clang 15.0.0
BPF clang 16.0.0
BPF clang 17.0.1
BPF clang 18.1.0
BPF gcc 13.1.0
BPF gcc 13.2.0
BPF gcc trunk
EDG (experimental reflection)
EDG 6.5
EDG 6.5 (GNU mode gcc 13)
EDG 6.6
EDG 6.6 (GNU mode gcc 13)
FRC 2019
FRC 2020
FRC 2023
KVX ACB 4.1.0 (GCC 7.5.0)
KVX ACB 4.1.0-cd1 (GCC 7.5.0)
KVX ACB 4.10.0 (GCC 10.3.1)
KVX ACB 4.11.1 (GCC 10.3.1)
KVX ACB 4.12.0 (GCC 11.3.0)
KVX ACB 4.2.0 (GCC 7.5.0)
KVX ACB 4.3.0 (GCC 7.5.0)
KVX ACB 4.4.0 (GCC 7.5.0)
KVX ACB 4.6.0 (GCC 9.4.1)
KVX ACB 4.8.0 (GCC 9.4.1)
KVX ACB 4.9.0 (GCC 9.4.1)
M68K gcc 13.1.0
M68K gcc 13.2.0
M68k clang (trunk)
MRISC32 gcc (trunk)
MSP430 gcc 4.5.3
MSP430 gcc 5.3.0
MSP430 gcc 6.2.1
MinGW clang 14.0.3
MinGW clang 14.0.6
MinGW clang 15.0.7
MinGW clang 16.0.0
MinGW clang 16.0.2
MinGW gcc 11.3.0
MinGW gcc 12.1.0
MinGW gcc 12.2.0
MinGW gcc 13.1.0
RISC-V (32-bits) gcc (trunk)
RISC-V (32-bits) gcc 10.2.0
RISC-V (32-bits) gcc 10.3.0
RISC-V (32-bits) gcc 11.2.0
RISC-V (32-bits) gcc 11.3.0
RISC-V (32-bits) gcc 11.4.0
RISC-V (32-bits) gcc 12.1.0
RISC-V (32-bits) gcc 12.2.0
RISC-V (32-bits) gcc 12.3.0
RISC-V (32-bits) gcc 13.1.0
RISC-V (32-bits) gcc 13.2.0
RISC-V (32-bits) gcc 8.2.0
RISC-V (32-bits) gcc 8.5.0
RISC-V (32-bits) gcc 9.4.0
RISC-V (64-bits) gcc (trunk)
RISC-V (64-bits) gcc 10.2.0
RISC-V (64-bits) gcc 10.3.0
RISC-V (64-bits) gcc 11.2.0
RISC-V (64-bits) gcc 11.3.0
RISC-V (64-bits) gcc 11.4.0
RISC-V (64-bits) gcc 12.1.0
RISC-V (64-bits) gcc 12.2.0
RISC-V (64-bits) gcc 12.3.0
RISC-V (64-bits) gcc 13.1.0
RISC-V (64-bits) gcc 13.2.0
RISC-V (64-bits) gcc 8.2.0
RISC-V (64-bits) gcc 8.5.0
RISC-V (64-bits) gcc 9.4.0
RISC-V rv32gc clang (trunk)
RISC-V rv32gc clang 10.0.0
RISC-V rv32gc clang 10.0.1
RISC-V rv32gc clang 11.0.0
RISC-V rv32gc clang 11.0.1
RISC-V rv32gc clang 12.0.0
RISC-V rv32gc clang 12.0.1
RISC-V rv32gc clang 13.0.0
RISC-V rv32gc clang 13.0.1
RISC-V rv32gc clang 14.0.0
RISC-V rv32gc clang 15.0.0
RISC-V rv32gc clang 16.0.0
RISC-V rv32gc clang 17.0.1
RISC-V rv32gc clang 18.1.0
RISC-V rv32gc clang 9.0.0
RISC-V rv32gc clang 9.0.1
RISC-V rv64gc clang (trunk)
RISC-V rv64gc clang 10.0.0
RISC-V rv64gc clang 10.0.1
RISC-V rv64gc clang 11.0.0
RISC-V rv64gc clang 11.0.1
RISC-V rv64gc clang 12.0.0
RISC-V rv64gc clang 12.0.1
RISC-V rv64gc clang 13.0.0
RISC-V rv64gc clang 13.0.1
RISC-V rv64gc clang 14.0.0
RISC-V rv64gc clang 15.0.0
RISC-V rv64gc clang 16.0.0
RISC-V rv64gc clang 17.0.1
RISC-V rv64gc clang 18.1.0
RISC-V rv64gc clang 9.0.0
RISC-V rv64gc clang 9.0.1
Raspbian Buster
Raspbian Stretch
SPARC LEON gcc 12.2.0
SPARC LEON gcc 12.3.0
SPARC LEON gcc 13.1.0
SPARC LEON gcc 13.2.0
SPARC gcc 12.2.0
SPARC gcc 12.3.0
SPARC gcc 13.1.0
SPARC gcc 13.2.0
SPARC64 gcc 12.2.0
SPARC64 gcc 12.3.0
SPARC64 gcc 13.1.0
SPARC64 gcc 13.2.0
TI C6x gcc 12.2.0
TI C6x gcc 12.3.0
TI C6x gcc 13.1.0
TI C6x gcc 13.2.0
TI CL430 21.6.1
VAX gcc NetBSDELF 10.4.0
VAX gcc NetBSDELF 10.5.0 (Nov 15 03:50:22 2023)
WebAssembly clang (trunk)
Xtensa ESP32 gcc 11.2.0 (2022r1)
Xtensa ESP32 gcc 12.2.0 (20230208)
Xtensa ESP32 gcc 8.2.0 (2019r2)
Xtensa ESP32 gcc 8.2.0 (2020r1)
Xtensa ESP32 gcc 8.2.0 (2020r2)
Xtensa ESP32 gcc 8.4.0 (2020r3)
Xtensa ESP32 gcc 8.4.0 (2021r1)
Xtensa ESP32 gcc 8.4.0 (2021r2)
Xtensa ESP32-S2 gcc 11.2.0 (2022r1)
Xtensa ESP32-S2 gcc 12.2.0 (20230208)
Xtensa ESP32-S2 gcc 8.2.0 (2019r2)
Xtensa ESP32-S2 gcc 8.2.0 (2020r1)
Xtensa ESP32-S2 gcc 8.2.0 (2020r2)
Xtensa ESP32-S2 gcc 8.4.0 (2020r3)
Xtensa ESP32-S2 gcc 8.4.0 (2021r1)
Xtensa ESP32-S2 gcc 8.4.0 (2021r2)
Xtensa ESP32-S3 gcc 11.2.0 (2022r1)
Xtensa ESP32-S3 gcc 12.2.0 (20230208)
Xtensa ESP32-S3 gcc 8.4.0 (2020r3)
Xtensa ESP32-S3 gcc 8.4.0 (2021r1)
Xtensa ESP32-S3 gcc 8.4.0 (2021r2)
arm64 msvc v19.28 VS16.9
arm64 msvc v19.29 VS16.10
arm64 msvc v19.29 VS16.11
arm64 msvc v19.30
arm64 msvc v19.31
arm64 msvc v19.32
arm64 msvc v19.33
arm64 msvc v19.34
arm64 msvc v19.35
arm64 msvc v19.36
arm64 msvc v19.37
arm64 msvc v19.38
arm64 msvc v19.latest
armv7-a clang (trunk)
armv7-a clang 10.0.0
armv7-a clang 10.0.1
armv7-a clang 11.0.0
armv7-a clang 11.0.1
armv7-a clang 9.0.0
armv7-a clang 9.0.1
armv8-a clang (all architectural features, trunk)
armv8-a clang (trunk)
armv8-a clang 10.0.0
armv8-a clang 10.0.1
armv8-a clang 11.0.0
armv8-a clang 11.0.1
armv8-a clang 12.0.0
armv8-a clang 13.0.0
armv8-a clang 14.0.0
armv8-a clang 15.0.0
armv8-a clang 16.0.0
armv8-a clang 17.0.1
armv8-a clang 18.1.0
armv8-a clang 9.0.0
armv8-a clang 9.0.1
ellcc 0.1.33
ellcc 0.1.34
ellcc 2017-07-16
hexagon-clang 16.0.5
llvm-mos atari2600-3e
llvm-mos atari2600-4k
llvm-mos atari2600-common
llvm-mos atari5200-supercart
llvm-mos atari8-cart-megacart
llvm-mos atari8-cart-std
llvm-mos atari8-cart-xegs
llvm-mos atari8-common
llvm-mos atari8-dos
llvm-mos c128
llvm-mos c64
llvm-mos commodore
llvm-mos cpm65
llvm-mos cx16
llvm-mos dodo
llvm-mos eater
llvm-mos mega65
llvm-mos nes
llvm-mos nes-action53
llvm-mos nes-cnrom
llvm-mos nes-gtrom
llvm-mos nes-mmc1
llvm-mos nes-mmc3
llvm-mos nes-nrom
llvm-mos nes-unrom
llvm-mos nes-unrom-512
llvm-mos osi-c1p
llvm-mos pce
llvm-mos pce-cd
llvm-mos pce-common
llvm-mos pet
llvm-mos rp6502
llvm-mos rpc8e
llvm-mos supervision
llvm-mos vic20
loongarch64 gcc 12.2.0
loongarch64 gcc 12.3.0
loongarch64 gcc 13.1.0
loongarch64 gcc 13.2.0
mips clang 13.0.0
mips clang 14.0.0
mips clang 15.0.0
mips clang 16.0.0
mips clang 17.0.1
mips clang 18.1.0
mips gcc 11.2.0
mips gcc 12.1.0
mips gcc 12.2.0
mips gcc 12.3.0
mips gcc 13.1.0
mips gcc 13.2.0
mips gcc 4.9.4
mips gcc 5.4
mips gcc 5.5.0
mips gcc 9.3.0 (codescape)
mips gcc 9.5.0
mips64 (el) gcc 12.1.0
mips64 (el) gcc 12.2.0
mips64 (el) gcc 12.3.0
mips64 (el) gcc 13.1.0
mips64 (el) gcc 13.2.0
mips64 (el) gcc 4.9.4
mips64 (el) gcc 5.4.0
mips64 (el) gcc 5.5.0
mips64 (el) gcc 9.5.0
mips64 clang 13.0.0
mips64 clang 14.0.0
mips64 clang 15.0.0
mips64 clang 16.0.0
mips64 clang 17.0.1
mips64 clang 18.1.0
mips64 gcc 11.2.0
mips64 gcc 12.1.0
mips64 gcc 12.2.0
mips64 gcc 12.3.0
mips64 gcc 13.1.0
mips64 gcc 13.2.0
mips64 gcc 4.9.4
mips64 gcc 5.4.0
mips64 gcc 5.5.0
mips64 gcc 9.5.0
mips64el clang 13.0.0
mips64el clang 14.0.0
mips64el clang 15.0.0
mips64el clang 16.0.0
mips64el clang 17.0.1
mips64el clang 18.1.0
mipsel clang 13.0.0
mipsel clang 14.0.0
mipsel clang 15.0.0
mipsel clang 16.0.0
mipsel clang 17.0.1
mipsel clang 18.1.0
mipsel gcc 12.1.0
mipsel gcc 12.2.0
mipsel gcc 12.3.0
mipsel gcc 13.1.0
mipsel gcc 13.2.0
mipsel gcc 4.9.4
mipsel gcc 5.4.0
mipsel gcc 5.5.0
mipsel gcc 9.5.0
nanoMIPS gcc 6.3.0 (mtk)
power gcc 11.2.0
power gcc 12.1.0
power gcc 12.2.0
power gcc 12.3.0
power gcc 13.1.0
power gcc 13.2.0
power gcc 4.8.5
power64 AT12.0 (gcc8)
power64 AT13.0 (gcc9)
power64 gcc 11.2.0
power64 gcc 12.1.0
power64 gcc 12.2.0
power64 gcc 12.3.0
power64 gcc 13.1.0
power64 gcc 13.2.0
power64 gcc trunk
power64le AT12.0 (gcc8)
power64le AT13.0 (gcc9)
power64le clang (trunk)
power64le gcc 11.2.0
power64le gcc 12.1.0
power64le gcc 12.2.0
power64le gcc 12.3.0
power64le gcc 13.1.0
power64le gcc 13.2.0
power64le gcc 6.3.0
power64le gcc trunk
powerpc64 clang (trunk)
s390x gcc 11.2.0
s390x gcc 12.1.0
s390x gcc 12.2.0
s390x gcc 12.3.0
s390x gcc 13.1.0
s390x gcc 13.2.0
sh gcc 12.2.0
sh gcc 12.3.0
sh gcc 13.1.0
sh gcc 13.2.0
sh gcc 4.9.4
sh gcc 9.5.0
vast (trunk)
x64 msvc v19.0 (WINE)
x64 msvc v19.10 (WINE)
x64 msvc v19.14
x64 msvc v19.14 (WINE)
x64 msvc v19.15
x64 msvc v19.16
x64 msvc v19.20
x64 msvc v19.21
x64 msvc v19.22
x64 msvc v19.23
x64 msvc v19.24
x64 msvc v19.25
x64 msvc v19.26
x64 msvc v19.27
x64 msvc v19.28
x64 msvc v19.28 VS16.9
x64 msvc v19.29 VS16.10
x64 msvc v19.29 VS16.11
x64 msvc v19.30
x64 msvc v19.31
x64 msvc v19.32
x64 msvc v19.33
x64 msvc v19.34
x64 msvc v19.35
x64 msvc v19.36
x64 msvc v19.37
x64 msvc v19.38
x64 msvc v19.latest
x86 djgpp 4.9.4
x86 djgpp 5.5.0
x86 djgpp 6.4.0
x86 djgpp 7.2.0
x86 msvc v19.0 (WINE)
x86 msvc v19.10 (WINE)
x86 msvc v19.14
x86 msvc v19.14 (WINE)
x86 msvc v19.15
x86 msvc v19.16
x86 msvc v19.20
x86 msvc v19.21
x86 msvc v19.22
x86 msvc v19.23
x86 msvc v19.24
x86 msvc v19.25
x86 msvc v19.26
x86 msvc v19.27
x86 msvc v19.28
x86 msvc v19.28 VS16.9
x86 msvc v19.29 VS16.10
x86 msvc v19.29 VS16.11
x86 msvc v19.30
x86 msvc v19.31
x86 msvc v19.32
x86 msvc v19.33
x86 msvc v19.34
x86 msvc v19.35
x86 msvc v19.36
x86 msvc v19.37
x86 msvc v19.38
x86 msvc v19.latest
x86 nvc++ 22.11
x86 nvc++ 22.7
x86 nvc++ 22.9
x86 nvc++ 23.1
x86 nvc++ 23.11
x86 nvc++ 23.3
x86 nvc++ 23.5
x86 nvc++ 23.7
x86 nvc++ 23.9
x86 nvc++ 24.1
x86 nvc++ 24.3
x86-64 Zapcc 190308
x86-64 clang (amd-stg-open)
x86-64 clang (assertions trunk)
x86-64 clang (clangir)
x86-64 clang (experimental -Wlifetime)
x86-64 clang (experimental P1061)
x86-64 clang (experimental P1144)
x86-64 clang (experimental P1221)
x86-64 clang (experimental P2996)
x86-64 clang (experimental metaprogramming - P2632)
x86-64 clang (experimental pattern matching)
x86-64 clang (old concepts branch)
x86-64 clang (reflection)
x86-64 clang (resugar)
x86-64 clang (thephd.dev)
x86-64 clang (trunk)
x86-64 clang (variadic friends - P2893)
x86-64 clang (widberg)
x86-64 clang 10.0.0
x86-64 clang 10.0.0 (assertions)
x86-64 clang 10.0.1
x86-64 clang 11.0.0
x86-64 clang 11.0.0 (assertions)
x86-64 clang 11.0.1
x86-64 clang 12.0.0
x86-64 clang 12.0.0 (assertions)
x86-64 clang 12.0.1
x86-64 clang 13.0.0
x86-64 clang 13.0.0 (assertions)
x86-64 clang 13.0.1
x86-64 clang 14.0.0
x86-64 clang 14.0.0 (assertions)
x86-64 clang 15.0.0
x86-64 clang 15.0.0 (assertions)
x86-64 clang 16.0.0
x86-64 clang 16.0.0 (assertions)
x86-64 clang 17.0.1
x86-64 clang 17.0.1 (assertions)
x86-64 clang 18.1.0
x86-64 clang 18.1.0 (assertions)
x86-64 clang 2.6.0 (assertions)
x86-64 clang 2.7.0 (assertions)
x86-64 clang 2.8.0 (assertions)
x86-64 clang 2.9.0 (assertions)
x86-64 clang 3.0.0
x86-64 clang 3.0.0 (assertions)
x86-64 clang 3.1
x86-64 clang 3.1 (assertions)
x86-64 clang 3.2
x86-64 clang 3.2 (assertions)
x86-64 clang 3.3
x86-64 clang 3.3 (assertions)
x86-64 clang 3.4 (assertions)
x86-64 clang 3.4.1
x86-64 clang 3.5
x86-64 clang 3.5 (assertions)
x86-64 clang 3.5.1
x86-64 clang 3.5.2
x86-64 clang 3.6
x86-64 clang 3.6 (assertions)
x86-64 clang 3.7
x86-64 clang 3.7 (assertions)
x86-64 clang 3.7.1
x86-64 clang 3.8
x86-64 clang 3.8 (assertions)
x86-64 clang 3.8.1
x86-64 clang 3.9.0
x86-64 clang 3.9.0 (assertions)
x86-64 clang 3.9.1
x86-64 clang 4.0.0
x86-64 clang 4.0.0 (assertions)
x86-64 clang 4.0.1
x86-64 clang 5.0.0
x86-64 clang 5.0.0 (assertions)
x86-64 clang 5.0.1
x86-64 clang 5.0.2
x86-64 clang 6.0.0
x86-64 clang 6.0.0 (assertions)
x86-64 clang 6.0.1
x86-64 clang 7.0.0
x86-64 clang 7.0.0 (assertions)
x86-64 clang 7.0.1
x86-64 clang 7.1.0
x86-64 clang 8.0.0
x86-64 clang 8.0.0 (assertions)
x86-64 clang 8.0.1
x86-64 clang 9.0.0
x86-64 clang 9.0.0 (assertions)
x86-64 clang 9.0.1
x86-64 clang rocm-4.5.2
x86-64 clang rocm-5.0.2
x86-64 clang rocm-5.1.3
x86-64 clang rocm-5.2.3
x86-64 clang rocm-5.3.3
x86-64 clang rocm-5.7.0
x86-64 gcc (contract labels)
x86-64 gcc (contracts natural syntax)
x86-64 gcc (contracts)
x86-64 gcc (coroutines)
x86-64 gcc (modules)
x86-64 gcc (trunk)
x86-64 gcc 10.1
x86-64 gcc 10.2
x86-64 gcc 10.3
x86-64 gcc 10.4
x86-64 gcc 10.5
x86-64 gcc 11.1
x86-64 gcc 11.2
x86-64 gcc 11.3
x86-64 gcc 11.4
x86-64 gcc 12.1
x86-64 gcc 12.2
x86-64 gcc 12.3
x86-64 gcc 13.1
x86-64 gcc 13.2
x86-64 gcc 4.1.2
x86-64 gcc 4.4.7
x86-64 gcc 4.5.3
x86-64 gcc 4.6.4
x86-64 gcc 4.7.1
x86-64 gcc 4.7.2
x86-64 gcc 4.7.3
x86-64 gcc 4.7.4
x86-64 gcc 4.8.1
x86-64 gcc 4.8.2
x86-64 gcc 4.8.3
x86-64 gcc 4.8.4
x86-64 gcc 4.8.5
x86-64 gcc 4.9.0
x86-64 gcc 4.9.1
x86-64 gcc 4.9.2
x86-64 gcc 4.9.3
x86-64 gcc 4.9.4
x86-64 gcc 5.1
x86-64 gcc 5.2
x86-64 gcc 5.3
x86-64 gcc 5.4
x86-64 gcc 5.5
x86-64 gcc 6.1
x86-64 gcc 6.2
x86-64 gcc 6.3
x86-64 gcc 6.4
x86-64 gcc 7.1
x86-64 gcc 7.2
x86-64 gcc 7.3
x86-64 gcc 7.4
x86-64 gcc 7.5
x86-64 gcc 8.1
x86-64 gcc 8.2
x86-64 gcc 8.3
x86-64 gcc 8.4
x86-64 gcc 8.5
x86-64 gcc 9.1
x86-64 gcc 9.2
x86-64 gcc 9.3
x86-64 gcc 9.4
x86-64 gcc 9.5
x86-64 icc 13.0.1
x86-64 icc 16.0.3
x86-64 icc 17.0.0
x86-64 icc 18.0.0
x86-64 icc 19.0.0
x86-64 icc 19.0.1
x86-64 icc 2021.1.2
x86-64 icc 2021.10.0
x86-64 icc 2021.2.0
x86-64 icc 2021.3.0
x86-64 icc 2021.4.0
x86-64 icc 2021.5.0
x86-64 icc 2021.6.0
x86-64 icc 2021.7.0
x86-64 icc 2021.7.1
x86-64 icc 2021.8.0
x86-64 icc 2021.9.0
x86-64 icx (latest)
x86-64 icx 2021.1.2
x86-64 icx 2021.2.0
x86-64 icx 2021.3.0
x86-64 icx 2021.4.0
x86-64 icx 2022.0.0
x86-64 icx 2022.1.0
x86-64 icx 2022.2.0
x86-64 icx 2022.2.1
x86-64 icx 2023.0.0
x86-64 icx 2023.1.0
x86-64 icx 2023.2.1
x86-64 icx 2024.0.0
zig c++ 0.10.0
zig c++ 0.11.0
zig c++ 0.12.0
zig c++ 0.6.0
zig c++ 0.7.0
zig c++ 0.7.1
zig c++ 0.8.0
zig c++ 0.9.0
zig c++ trunk
Options
Source code
/* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <algorithm> #include <cassert> /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef BITBOARD_H_INCLUDED #define BITBOARD_H_INCLUDED #include <string> /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef TYPES_H_INCLUDED #define TYPES_H_INCLUDED /// When compiling with provided Makefile (e.g. for Linux and OSX), configuration /// is done automatically. To get started type 'make help'. /// /// When Makefile is not used (e.g. with Microsoft Visual Studio) some switches /// need to be set manually: /// /// -DNDEBUG | Disable debugging mode. Always use this for release. /// /// -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to /// | run on some very old machines. /// /// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works /// | only in 64-bit mode and requires hardware with popcnt support. /// /// -DUSE_PEXT | Add runtime support for use of pext asm-instruction. Works /// | only in 64-bit mode and requires hardware with pext support. #include <cassert> #include <cctype> #include <climits> #include <cstdint> #include <cstdlib> #if defined(_MSC_VER) // Disable some silly and noisy warning from MSVC compiler #pragma warning(disable: 4127) // Conditional expression is constant #pragma warning(disable: 4146) // Unary minus operator applied to unsigned type #pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false' #endif /// Predefined macros hell: /// /// __GNUC__ Compiler is gcc, Clang or Intel on Linux /// __INTEL_COMPILER Compiler is Intel /// _MSC_VER Compiler is MSVC or Intel on Windows /// _WIN32 Building on Windows (any) /// _WIN64 Building on Windows 64 bit #if defined(_WIN64) && defined(_MSC_VER) // No Makefile used # include <intrin.h> // Microsoft header for _BitScanForward64() # define IS_64BIT #endif #if defined(USE_POPCNT) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) # include <nmmintrin.h> // Intel and Microsoft header for _mm_popcnt_u64() #endif #if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) # include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch() #endif #if defined(USE_PEXT) # include <immintrin.h> // Header for _pext_u64() intrinsic # define pext(b, m) _pext_u64(b, m) #else # define pext(b, m) 0 #endif #ifdef USE_POPCNT constexpr bool HasPopCnt = true; #else constexpr bool HasPopCnt = false; #endif #ifdef USE_PEXT constexpr bool HasPext = true; #else constexpr bool HasPext = false; #endif #ifdef IS_64BIT constexpr bool Is64Bit = true; #else constexpr bool Is64Bit = false; #endif typedef uint64_t Key; typedef uint64_t Bitboard; constexpr int MAX_MOVES = 256; constexpr int MAX_PLY = 246; /// A move needs 16 bits to be stored /// /// bit 0- 5: destination square (from 0 to 63) /// bit 6-11: origin square (from 0 to 63) /// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2) /// bit 14-15: special move flag: promotion (1), en passant (2), castling (3) /// NOTE: EN-PASSANT bit is set only when a pawn can be captured /// /// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in /// any normal move destination square is always different from origin square /// while MOVE_NONE and MOVE_NULL have the same origin and destination square. enum Move : int { MOVE_NONE, MOVE_NULL = 65 }; enum MoveType { NORMAL, PROMOTION = 1 << 14, ENPASSANT = 2 << 14, CASTLING = 3 << 14 }; enum Color { WHITE, BLACK, COLOR_NB = 2 }; enum CastlingRights { NO_CASTLING, WHITE_OO, WHITE_OOO = WHITE_OO << 1, BLACK_OO = WHITE_OO << 2, BLACK_OOO = WHITE_OO << 3, KING_SIDE = WHITE_OO | BLACK_OO, QUEEN_SIDE = WHITE_OOO | BLACK_OOO, WHITE_CASTLING = WHITE_OO | WHITE_OOO, BLACK_CASTLING = BLACK_OO | BLACK_OOO, ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING, CASTLING_RIGHT_NB = 16 }; enum Phase { PHASE_ENDGAME, PHASE_MIDGAME = 128, MG = 0, EG = 1, PHASE_NB = 2 }; enum ScaleFactor { SCALE_FACTOR_DRAW = 0, SCALE_FACTOR_NORMAL = 64, SCALE_FACTOR_MAX = 128, SCALE_FACTOR_NONE = 255 }; enum Bound { BOUND_NONE, BOUND_UPPER, BOUND_LOWER, BOUND_EXACT = BOUND_UPPER | BOUND_LOWER }; enum Value : int { VALUE_ZERO = 0, VALUE_DRAW = 0, VALUE_KNOWN_WIN = 10000, VALUE_MATE = 32000, VALUE_INFINITE = 32001, VALUE_NONE = 32002, VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, PawnValueMg = 128, PawnValueEg = 213, KnightValueMg = 782, KnightValueEg = 865, BishopValueMg = 830, BishopValueEg = 918, RookValueMg = 1289, RookValueEg = 1378, QueenValueMg = 2529, QueenValueEg = 2687, MidgameLimit = 15258, EndgameLimit = 3915 }; enum PieceType { NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, ALL_PIECES = 0, PIECE_TYPE_NB = 8 }; enum Piece { NO_PIECE, W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING, PIECE_NB = 16 }; extern Value PieceValue[PHASE_NB][PIECE_NB]; enum Depth : int { ONE_PLY = 1, DEPTH_ZERO = 0 * ONE_PLY, DEPTH_QS_CHECKS = 0 * ONE_PLY, DEPTH_QS_NO_CHECKS = -1 * ONE_PLY, DEPTH_QS_RECAPTURES = -5 * ONE_PLY, DEPTH_NONE = -6 * ONE_PLY, DEPTH_OFFSET = DEPTH_NONE, DEPTH_MAX = MAX_PLY * ONE_PLY }; static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2"); enum Square : int { SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1, SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2, SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3, SQ_A4, SQ_B4, SQ_C4, SQ_D4, SQ_E4, SQ_F4, SQ_G4, SQ_H4, SQ_A5, SQ_B5, SQ_C5, SQ_D5, SQ_E5, SQ_F5, SQ_G5, SQ_H5, SQ_A6, SQ_B6, SQ_C6, SQ_D6, SQ_E6, SQ_F6, SQ_G6, SQ_H6, SQ_A7, SQ_B7, SQ_C7, SQ_D7, SQ_E7, SQ_F7, SQ_G7, SQ_H7, SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8, SQ_NONE, SQUARE_NB = 64 }; enum Direction : int { NORTH = 8, EAST = 1, SOUTH = -NORTH, WEST = -EAST, NORTH_EAST = NORTH + EAST, SOUTH_EAST = SOUTH + EAST, SOUTH_WEST = SOUTH + WEST, NORTH_WEST = NORTH + WEST }; enum File : int { FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB }; enum Rank : int { RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB }; /// Score enum stores a middlegame and an endgame value in a single integer (enum). /// The least significant 16 bits are used to store the middlegame value and the /// upper 16 bits are used to store the endgame value. We have to take care to /// avoid left-shifting a signed int to avoid undefined behavior. enum Score : int { SCORE_ZERO }; constexpr Score make_score(int mg, int eg) { return Score((int)((unsigned int)eg << 16) + mg); } /// Extracting the signed lower and upper 16 bits is not so trivial because /// according to the standard a simple cast to short is implementation defined /// and so is a right shift of a signed integer. inline Value eg_value(Score s) { union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s + 0x8000) >> 16) }; return Value(eg.s); } inline Value mg_value(Score s) { union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s)) }; return Value(mg.s); } #define ENABLE_BASE_OPERATORS_ON(T) \ constexpr T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \ constexpr T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \ constexpr T operator-(T d) { return T(-int(d)); } \ inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \ inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; } #define ENABLE_INCR_OPERATORS_ON(T) \ inline T& operator++(T& d) { return d = T(int(d) + 1); } \ inline T& operator--(T& d) { return d = T(int(d) - 1); } #define ENABLE_FULL_OPERATORS_ON(T) \ ENABLE_BASE_OPERATORS_ON(T) \ constexpr T operator*(int i, T d) { return T(i * int(d)); } \ constexpr T operator*(T d, int i) { return T(int(d) * i); } \ constexpr T operator/(T d, int i) { return T(int(d) / i); } \ constexpr int operator/(T d1, T d2) { return int(d1) / int(d2); } \ inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } \ inline T& operator/=(T& d, int i) { return d = T(int(d) / i); } ENABLE_FULL_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(Depth) ENABLE_FULL_OPERATORS_ON(Direction) ENABLE_INCR_OPERATORS_ON(PieceType) ENABLE_INCR_OPERATORS_ON(Piece) ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(File) ENABLE_INCR_OPERATORS_ON(Rank) ENABLE_BASE_OPERATORS_ON(Score) #undef ENABLE_FULL_OPERATORS_ON #undef ENABLE_INCR_OPERATORS_ON #undef ENABLE_BASE_OPERATORS_ON /// Additional operators to add integers to a Value constexpr Value operator+(Value v, int i) { return Value(int(v) + i); } constexpr Value operator-(Value v, int i) { return Value(int(v) - i); } inline Value& operator+=(Value& v, int i) { return v = v + i; } inline Value& operator-=(Value& v, int i) { return v = v - i; } /// Additional operators to add a Direction to a Square constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); } constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); } inline Square& operator+=(Square& s, Direction d) { return s = s + d; } inline Square& operator-=(Square& s, Direction d) { return s = s - d; } /// Only declared but not defined. We don't want to multiply two scores due to /// a very high risk of overflow. So user should explicitly convert to integer. Score operator*(Score, Score) = delete; /// Division of a Score must be handled separately for each term inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); } /// Multiplication of a Score by an integer. We check for overflow in debug mode. inline Score operator*(Score s, int i) { Score result = Score(int(s) * i); assert(eg_value(result) == (i * eg_value(s))); assert(mg_value(result) == (i * mg_value(s))); assert((i == 0) || (result / i) == s); return result; } /// Multiplication of a Score by an boolean inline Score operator*(Score s, bool b) { return Score(int(s) * int(b)); } constexpr Color operator~(Color c) { return Color(c ^ BLACK); // Toggle color } constexpr Square operator~(Square s) { return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8 } constexpr File operator~(File f) { return File(f ^ FILE_H); // Horizontal flip FILE_A -> FILE_H } constexpr Piece operator~(Piece pc) { return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT } constexpr CastlingRights operator&(Color c, CastlingRights cr) { return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr); } constexpr Value mate_in(int ply) { return VALUE_MATE - ply; } constexpr Value mated_in(int ply) { return -VALUE_MATE + ply; } constexpr Square make_square(File f, Rank r) { return Square((r << 3) + f); } constexpr Piece make_piece(Color c, PieceType pt) { return Piece((c << 3) + pt); } constexpr PieceType type_of(Piece pc) { return PieceType(pc & 7); } inline Color color_of(Piece pc) { assert(pc != NO_PIECE); return Color(pc >> 3); } constexpr bool is_ok(Square s) { return s >= SQ_A1 && s <= SQ_H8; } constexpr File file_of(Square s) { return File(s & 7); } constexpr Rank rank_of(Square s) { return Rank(s >> 3); } constexpr Square relative_square(Color c, Square s) { return Square(s ^ (c * 56)); } constexpr Rank relative_rank(Color c, Rank r) { return Rank(r ^ (c * 7)); } constexpr Rank relative_rank(Color c, Square s) { return relative_rank(c, rank_of(s)); } constexpr Direction pawn_push(Color c) { return c == WHITE ? NORTH : SOUTH; } constexpr Square from_sq(Move m) { return Square((m >> 6) & 0x3F); } constexpr Square to_sq(Move m) { return Square(m & 0x3F); } constexpr int from_to(Move m) { return m & 0xFFF; } constexpr MoveType type_of(Move m) { return MoveType(m & (3 << 14)); } constexpr PieceType promotion_type(Move m) { return PieceType(((m >> 12) & 3) + KNIGHT); } constexpr Move make_move(Square from, Square to) { return Move((from << 6) + to); } constexpr Move reverse_move(Move m) { return make_move(to_sq(m), from_sq(m)); } template<MoveType T> constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) { return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); } constexpr bool is_ok(Move m) { return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE } #endif // #ifndef TYPES_H_INCLUDED namespace Bitbases { void init(); bool probe(Square wksq, Square wpsq, Square bksq, Color us); } namespace Bitboards { void init(); const std::string pretty(Bitboard b); } constexpr Bitboard NoSquares = Bitboard(0); constexpr Bitboard AllSquares = ~Bitboard(0); constexpr Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL; constexpr Bitboard FileABB = 0x0101010101010101ULL; constexpr Bitboard FileBBB = FileABB << 1; constexpr Bitboard FileCBB = FileABB << 2; constexpr Bitboard FileDBB = FileABB << 3; constexpr Bitboard FileEBB = FileABB << 4; constexpr Bitboard FileFBB = FileABB << 5; constexpr Bitboard FileGBB = FileABB << 6; constexpr Bitboard FileHBB = FileABB << 7; constexpr Bitboard Rank1BB = 0xFF; constexpr Bitboard Rank2BB = Rank1BB << (8 * 1); constexpr Bitboard Rank3BB = Rank1BB << (8 * 2); constexpr Bitboard Rank4BB = Rank1BB << (8 * 3); constexpr Bitboard Rank5BB = Rank1BB << (8 * 4); constexpr Bitboard Rank6BB = Rank1BB << (8 * 5); constexpr Bitboard Rank7BB = Rank1BB << (8 * 6); constexpr Bitboard Rank8BB = Rank1BB << (8 * 7); constexpr Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB; constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB; constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB; constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB); constexpr Bitboard KingFlank[FILE_NB] = { QueenSide ^ FileDBB, QueenSide, QueenSide, CenterFiles, CenterFiles, KingSide, KingSide, KingSide ^ FileEBB }; extern uint8_t PopCnt16[1 << 16]; extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; extern Bitboard SquareBB[SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; /// Magic holds all magic bitboards relevant data for a single square struct Magic { Bitboard mask; Bitboard magic; Bitboard* attacks; unsigned shift; // Compute the attack's index using the 'magic bitboards' approach unsigned index(Bitboard occupied) const { if (HasPext) return unsigned(pext(occupied, mask)); if (Is64Bit) return unsigned(((occupied & mask) * magic) >> shift); unsigned lo = unsigned(occupied) & unsigned(mask); unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32); return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift; } }; extern Magic RookMagics[SQUARE_NB]; extern Magic BishopMagics[SQUARE_NB]; inline Bitboard square_bb(Square s) { assert(s >= SQ_A1 && s <= SQ_H8); return SquareBB[s]; } /// Overloads of bitwise operators between a Bitboard and a Square for testing /// whether a given bit is set in a bitboard, and for setting and clearing bits. inline Bitboard operator&( Bitboard b, Square s) { return b & square_bb(s); } inline Bitboard operator|( Bitboard b, Square s) { return b | square_bb(s); } inline Bitboard operator^( Bitboard b, Square s) { return b ^ square_bb(s); } inline Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); } inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); } constexpr bool more_than_one(Bitboard b) { return b & (b - 1); } inline bool opposite_colors(Square s1, Square s2) { return bool(DarkSquares & s1) != bool(DarkSquares & s2); } /// rank_bb() and file_bb() return a bitboard representing all the squares on /// the given file or rank. inline Bitboard rank_bb(Rank r) { return Rank1BB << (8 * r); } inline Bitboard rank_bb(Square s) { return rank_bb(rank_of(s)); } inline Bitboard file_bb(File f) { return FileABB << f; } inline Bitboard file_bb(Square s) { return file_bb(file_of(s)); } /// shift() moves a bitboard one step along direction D template<Direction D> constexpr Bitboard shift(Bitboard b) { return D == NORTH ? b << 8 : D == SOUTH ? b >> 8 : D == NORTH+NORTH? b <<16 : D == SOUTH+SOUTH? b >>16 : D == EAST ? (b & ~FileHBB) << 1 : D == WEST ? (b & ~FileABB) >> 1 : D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7 : D == SOUTH_EAST ? (b & ~FileHBB) >> 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9 : 0; } /// pawn_attacks_bb() returns the squares attacked by pawns of the given color /// from the squares in the given bitboard. template<Color C> constexpr Bitboard pawn_attacks_bb(Bitboard b) { return C == WHITE ? shift<NORTH_WEST>(b) | shift<NORTH_EAST>(b) : shift<SOUTH_WEST>(b) | shift<SOUTH_EAST>(b); } /// pawn_double_attacks_bb() returns the squares doubly attacked by pawns of the /// given color from the squares in the given bitboard. template<Color C> constexpr Bitboard pawn_double_attacks_bb(Bitboard b) { return C == WHITE ? shift<NORTH_WEST>(b) & shift<NORTH_EAST>(b) : shift<SOUTH_WEST>(b) & shift<SOUTH_EAST>(b); } /// adjacent_files_bb() returns a bitboard representing all the squares on the /// two adjacent files of the given file bitboard. inline Bitboard adjacent_files_bb(Bitboard file_bb) { return shift<EAST>(file_bb) | shift<WEST>(file_bb); } /// adjacent_files_bb() returns a bitboard representing all the squares on the /// adjacent files of the given one. inline Bitboard adjacent_files_bb(Square s) { return adjacent_files_bb( file_bb(s) ); } /// between_bb() returns squares that are linearly between the given squares /// If the given squares are not on a same file/rank/diagonal, return 0. inline Bitboard between_bb(Square s1, Square s2) { return LineBB[s1][s2] & ( (AllSquares << (s1 + (s1 < s2))) ^(AllSquares << (s2 + !(s1 < s2)))); } /// forward_ranks_bb() returns a bitboard representing the squares on the ranks /// in front of the given rank, from the point of view of the given color. For instance, /// forward_ranks_bb(BLACK, RANK_3) will return RANK_2 | RANK_1; inline Bitboard forward_ranks_bb(Color c, Bitboard b) { return (c == WHITE) ? ~( b | ((b & FileABB) - 1U)) : (b & FileABB) - 1U; } /// forward_ranks_bb() returns a bitboard representing the squares on the ranks /// in front of the given rank, from the point of view of the given color. For instance, /// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2. inline Bitboard forward_ranks_bb(Color c, Square s) { return c == WHITE ? ~Rank1BB << 8 * (rank_of(s) - RANK_1) : ~Rank8BB >> 8 * (RANK_8 - rank_of(s)); } /// forward_file_bb() returns a bitboard representing all the squares along the /// line in front of the square intersecting the file bitboard and the rank bitboard, /// from the point of view of the given color. inline Bitboard forward_file_bb(Color c, Bitboard file_bb, Bitboard rank_bb) { return forward_ranks_bb(c, rank_bb) & file_bb; } /// forward_file_bb() returns a bitboard representing all the squares along the /// line in front of the given one, from the point of view of the given color. inline Bitboard forward_file_bb(Color c, Square s) { return forward_ranks_bb(c, s) & file_bb(s); } /// pawn_attack_span() returns a bitboard representing all the squares that can /// be attacked by a pawn of the given color when it moves along its file, /// starting from the given square. inline Bitboard pawn_attack_span(Color c, Square s) { return forward_ranks_bb(c, s) & adjacent_files_bb(s); } /// passed_pawn_span() returns a bitboard which can be used to test if a pawn of /// the given color and on the given square (intersecting file & rank bitboards) is a passed pawn. inline Bitboard passed_pawn_span(Color c, Bitboard square_file_bb, Bitboard square_rank_bb) { return forward_ranks_bb(c, square_rank_bb) & (adjacent_files_bb(square_file_bb) | square_file_bb); } /// passed_pawn_span() returns a bitboard which can be used to test if a pawn of /// the given color and on the given square is a passed pawn. inline Bitboard passed_pawn_span(Color c, Square s) { return passed_pawn_span(c, file_bb(s), rank_bb(s)); } /// aligned() returns true if the squares s1, s2 and s3 are aligned either on a /// straight or on a diagonal line. inline bool aligned(Square s1, Square s2, Square s3) { return LineBB[s1][s2] & s3; } /// distance() functions return the distance between x and y, defined as the /// number of steps for a king in x to reach y. template<typename T1 = Square> inline int distance(Square x, Square y); template<> inline int distance<File>(Square x, Square y) { return std::abs(file_of(x) - file_of(y)); } template<> inline int distance<Rank>(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); } template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; } template<class T> constexpr const T& clamp(const T& v, const T& lo, const T& hi) { return v < lo ? lo : v > hi ? hi : v; } /// attacks_bb() returns a bitboard representing all the squares attacked by a /// piece of type Pt (bishop or rook) placed on 's'. template<PieceType Pt> inline Bitboard attacks_bb(Square s, Bitboard occupied) { const Magic& m = Pt == ROOK ? RookMagics[s] : BishopMagics[s]; return m.attacks[m.index(occupied)]; } inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) { assert(pt != PAWN); switch (pt) { case BISHOP: return attacks_bb<BISHOP>(s, occupied); case ROOK : return attacks_bb< ROOK>(s, occupied); case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied); default : return PseudoAttacks[pt][s]; } } /// popcount() counts the number of non-zero bits in a bitboard inline int popcount(Bitboard b) { #ifndef USE_POPCNT union { Bitboard bb; uint16_t u[4]; } v = { b }; return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]]; #elif defined(_MSC_VER) || defined(__INTEL_COMPILER) return (int)_mm_popcnt_u64(b); #else // Assumed gcc or compatible compiler return __builtin_popcountll(b); #endif } /// lsb() and msb() return the least/most significant bit in a non-zero bitboard #if defined(__GNUC__) // GCC, Clang, ICC inline Square lsb(Bitboard b) { assert(b); return Square(__builtin_ctzll(b)); } inline Square msb(Bitboard b) { assert(b); return Square(63 ^ __builtin_clzll(b)); } #elif defined(_MSC_VER) // MSVC #ifdef _WIN64 // MSVC, WIN64 inline Square lsb(Bitboard b) { assert(b); unsigned long idx; _BitScanForward64(&idx, b); return (Square) idx; } inline Square msb(Bitboard b) { assert(b); unsigned long idx; _BitScanReverse64(&idx, b); return (Square) idx; } #else // MSVC, WIN32 inline Square lsb(Bitboard b) { assert(b); unsigned long idx; if (b & 0xffffffff) { _BitScanForward(&idx, int32_t(b)); return Square(idx); } else { _BitScanForward(&idx, int32_t(b >> 32)); return Square(idx + 32); } } inline Square msb(Bitboard b) { assert(b); unsigned long idx; if (b >> 32) { _BitScanReverse(&idx, int32_t(b >> 32)); return Square(idx + 32); } else { _BitScanReverse(&idx, int32_t(b)); return Square(idx); } } #endif #else // Compiler is neither GCC nor MSVC compatible #error "Compiler not supported." #endif /// pop_lsb() finds and clears the least significant bit in a non-zero bitboard inline Square pop_lsb(Bitboard* b) { const Square s = lsb(*b); *b &= *b - 1; return s; } /// frontmost_sq() returns the most advanced square for the given color inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); } #endif // #ifndef BITBOARD_H_INCLUDED /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef PAWNS_H_INCLUDED #define PAWNS_H_INCLUDED /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef POSITION_H_INCLUDED #define POSITION_H_INCLUDED #include <cassert> #include <deque> #include <memory> // For std::unique_ptr #include <string> /// StateInfo struct stores information needed to restore a Position object to /// its previous state when we retract a move. Whenever a move is made on the /// board (by calling Position::do_move), a StateInfo object must be passed. struct StateInfo { // Copied when making a move Key pawnKey; Key materialKey; Value nonPawnMaterial[COLOR_NB]; int castlingRights; int rule50; int pliesFromNull; Square epSquare; // Not copied when making a move (will be recomputed anyhow) int repetition; Key key; Bitboard checkersBB; Piece capturedPiece; StateInfo* previous; Bitboard blockersForKing[COLOR_NB]; Bitboard pinners[COLOR_NB]; Bitboard checkSquares[PIECE_TYPE_NB]; }; /// A list to keep track of the position states along the setup moves (from the /// start position to the position just before the search starts). Needed by /// 'draw by repetition' detection. Use a std::deque because pointers to /// elements are not invalidated upon list resizing. typedef std::unique_ptr<std::deque<StateInfo>> StateListPtr; /// Position class stores information regarding the board representation as /// pieces, side to move, hash keys, castling info, etc. Important methods are /// do_move() and undo_move(), used by the search to update node info when /// traversing the search tree. class Thread; class Position { public: static void init(); Position() = default; Position(const Position&) = delete; Position& operator=(const Position&) = delete; // FEN string input/output Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th); Position& set(const std::string& code, Color c, StateInfo* si); const std::string fen() const; // Position representation Bitboard pieces() const; Bitboard pieces(PieceType pt) const; Bitboard pieces(PieceType pt1, PieceType pt2) const; Bitboard pieces(Color c) const; Bitboard pieces(Color c, PieceType pt) const; Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const; Piece piece_on(Square s) const; Square ep_square() const; bool empty(Square s) const; template<PieceType Pt> int count(Color c) const; template<PieceType Pt> int count() const; template<PieceType Pt> const Square* squares(Color c) const; template<PieceType Pt> Square square(Color c) const; bool is_on_semiopen_file(Color c, Square s) const; // Castling int castling_rights(Color c) const; bool can_castle(CastlingRights cr) const; bool castling_impeded(CastlingRights cr) const; Square castling_rook_square(CastlingRights cr) const; // Checking Bitboard checkers() const; Bitboard blockers_for_king(Color c) const; Bitboard check_squares(PieceType pt) const; bool is_discovery_check_on_king(Color c, Move m) const; // Attacks to/from a given square Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s, Bitboard occupied) const; Bitboard attacks_from(PieceType pt, Square s) const; template<PieceType> Bitboard attacks_from(Square s) const; template<PieceType> Bitboard attacks_from(Square s, Color c) const; Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const; // Properties of moves bool legal(Move m) const; bool pseudo_legal(const Move m) const; bool capture(Move m) const; bool capture_or_promotion(Move m) const; bool gives_check(Move m) const; bool advanced_pawn_push(Move m) const; Piece moved_piece(Move m) const; Piece captured_piece() const; // Piece specific bool pawn_passed(Color c, Square s) const; bool opposite_bishops() const; int pawns_on_same_color_squares(Color c, Square s) const; // Doing and undoing moves void do_move(Move m, StateInfo& newSt); void do_move(Move m, StateInfo& newSt, bool givesCheck); void undo_move(Move m); void do_null_move(StateInfo& newSt); void undo_null_move(); // Static Exchange Evaluation bool see_ge(Move m, Value threshold = VALUE_ZERO) const; // Accessing hash keys Key key() const; Key key_after(Move m) const; Key material_key() const; Key pawn_key() const; // Other properties of the position Color side_to_move() const; int game_ply() const; bool is_chess960() const; Thread* this_thread() const; bool is_draw(int ply) const; bool has_game_cycle(int ply) const; bool has_repeated() const; int rule50_count() const; Score psq_score() const; Value non_pawn_material(Color c) const; Value non_pawn_material() const; // Position consistency check, for debugging bool pos_is_ok() const; void flip(); private: // Initialization helpers (used while setting up a position) void set_castling_right(Color c, Square rfrom); void set_state(StateInfo* si) const; void set_check_info(StateInfo* si) const; // Other helpers void put_piece(Piece pc, Square s); void remove_piece(Piece pc, Square s); void move_piece(Piece pc, Square from, Square to); template<bool Do> void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); // Data members Piece board[SQUARE_NB]; Bitboard byTypeBB[PIECE_TYPE_NB]; Bitboard byColorBB[COLOR_NB]; int pieceCount[PIECE_NB]; Square pieceList[PIECE_NB][16]; int index[SQUARE_NB]; int castlingRightsMask[SQUARE_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB]; Bitboard castlingPath[CASTLING_RIGHT_NB]; int gamePly; Color sideToMove; Score psq; Thread* thisThread; StateInfo* st; bool chess960; }; namespace PSQT { extern Score psq[PIECE_NB][SQUARE_NB]; } extern std::ostream& operator<<(std::ostream& os, const Position& pos); inline Color Position::side_to_move() const { return sideToMove; } inline bool Position::empty(Square s) const { return board[s] == NO_PIECE; } inline Piece Position::piece_on(Square s) const { return board[s]; } inline Piece Position::moved_piece(Move m) const { return board[from_sq(m)]; } inline Bitboard Position::pieces() const { return byTypeBB[ALL_PIECES]; } inline Bitboard Position::pieces(PieceType pt) const { return byTypeBB[pt]; } inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const { return byTypeBB[pt1] | byTypeBB[pt2]; } inline Bitboard Position::pieces(Color c) const { return byColorBB[c]; } inline Bitboard Position::pieces(Color c, PieceType pt) const { return byColorBB[c] & byTypeBB[pt]; } inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const { return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]); } template<PieceType Pt> inline int Position::count(Color c) const { return pieceCount[make_piece(c, Pt)]; } template<PieceType Pt> inline int Position::count() const { return pieceCount[make_piece(WHITE, Pt)] + pieceCount[make_piece(BLACK, Pt)]; } template<PieceType Pt> inline const Square* Position::squares(Color c) const { return pieceList[make_piece(c, Pt)]; } template<PieceType Pt> inline Square Position::square(Color c) const { assert(pieceCount[make_piece(c, Pt)] == 1); return pieceList[make_piece(c, Pt)][0]; } inline Square Position::ep_square() const { return st->epSquare; } inline bool Position::is_on_semiopen_file(Color c, Square s) const { return !(pieces(c, PAWN) & file_bb(s)); } inline bool Position::can_castle(CastlingRights cr) const { return st->castlingRights & cr; } inline int Position::castling_rights(Color c) const { return st->castlingRights & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING); } inline bool Position::castling_impeded(CastlingRights cr) const { return byTypeBB[ALL_PIECES] & castlingPath[cr]; } inline Square Position::castling_rook_square(CastlingRights cr) const { return castlingRookSquare[cr]; } template<PieceType Pt> inline Bitboard Position::attacks_from(Square s) const { assert(Pt != PAWN); return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES]) : Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s) : PseudoAttacks[Pt][s]; } template<> inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const { return PawnAttacks[c][s]; } inline Bitboard Position::attacks_from(PieceType pt, Square s) const { return attacks_bb(pt, s, byTypeBB[ALL_PIECES]); } inline Bitboard Position::attackers_to(Square s) const { return attackers_to(s, byTypeBB[ALL_PIECES]); } inline Bitboard Position::checkers() const { return st->checkersBB; } inline Bitboard Position::blockers_for_king(Color c) const { return st->blockersForKing[c]; } inline Bitboard Position::check_squares(PieceType pt) const { return st->checkSquares[pt]; } inline bool Position::is_discovery_check_on_king(Color c, Move m) const { return st->blockersForKing[c] & from_sq(m); } inline bool Position::pawn_passed(Color c, Square s) const { return !(pieces(~c, PAWN) & passed_pawn_span(c, s)); } inline bool Position::advanced_pawn_push(Move m) const { return type_of(moved_piece(m)) == PAWN && relative_rank(sideToMove, to_sq(m)) > RANK_5; } inline int Position::pawns_on_same_color_squares(Color c, Square s) const { return popcount(pieces(c, PAWN) & ((DarkSquares & s) ? DarkSquares : ~DarkSquares)); } inline Key Position::key() const { return st->key; } inline Key Position::pawn_key() const { return st->pawnKey; } inline Key Position::material_key() const { return st->materialKey; } inline Score Position::psq_score() const { return psq; } inline Value Position::non_pawn_material(Color c) const { return st->nonPawnMaterial[c]; } inline Value Position::non_pawn_material() const { return st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK]; } inline int Position::game_ply() const { return gamePly; } inline int Position::rule50_count() const { return st->rule50; } inline bool Position::opposite_bishops() const { return pieceCount[W_BISHOP] == 1 && pieceCount[B_BISHOP] == 1 && opposite_colors(square<BISHOP>(WHITE), square<BISHOP>(BLACK)); } inline bool Position::is_chess960() const { return chess960; } inline bool Position::capture_or_promotion(Move m) const { assert(is_ok(m)); return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m)); } inline bool Position::capture(Move m) const { assert(is_ok(m)); // Castling is encoded as "king captures rook" return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT; } inline Piece Position::captured_piece() const { return st->capturedPiece; } inline Thread* Position::this_thread() const { return thisThread; } inline void Position::put_piece(Piece pc, Square s) { board[s] = pc; byTypeBB[ALL_PIECES] |= s; byTypeBB[type_of(pc)] |= s; byColorBB[color_of(pc)] |= s; index[s] = pieceCount[pc]++; pieceList[pc][index[s]] = s; pieceCount[make_piece(color_of(pc), ALL_PIECES)]++; psq += PSQT::psq[pc][s]; } inline void Position::remove_piece(Piece pc, Square s) { // WARNING: This is not a reversible operation. If we remove a piece in // do_move() and then replace it in undo_move() we will put it at the end of // the list and not in its original place, it means index[] and pieceList[] // are not invariant to a do_move() + undo_move() sequence. byTypeBB[ALL_PIECES] ^= s; byTypeBB[type_of(pc)] ^= s; byColorBB[color_of(pc)] ^= s; /* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */ Square lastSquare = pieceList[pc][--pieceCount[pc]]; index[lastSquare] = index[s]; pieceList[pc][index[lastSquare]] = lastSquare; pieceList[pc][pieceCount[pc]] = SQ_NONE; pieceCount[make_piece(color_of(pc), ALL_PIECES)]--; psq -= PSQT::psq[pc][s]; } inline void Position::move_piece(Piece pc, Square from, Square to) { // index[from] is not updated and becomes stale. This works as long as index[] // is accessed just by known occupied squares. Bitboard fromTo = square_bb(from) | square_bb(to); byTypeBB[ALL_PIECES] ^= fromTo; byTypeBB[type_of(pc)] ^= fromTo; byColorBB[color_of(pc)] ^= fromTo; board[from] = NO_PIECE; board[to] = pc; index[to] = index[from]; pieceList[pc][index[to]] = to; psq += PSQT::psq[pc][to] - PSQT::psq[pc][from]; } inline void Position::do_move(Move m, StateInfo& newSt) { do_move(m, newSt, gives_check(m)); } #endif // #ifndef POSITION_H_INCLUDED /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Stockfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef MISC_H_INCLUDED #define MISC_H_INCLUDED #include <cassert> #include <chrono> #include <ostream> #include <string> #include <vector> const std::string engine_info(bool to_uci = false); void prefetch(void* addr); void start_logger(const std::string& fname); void dbg_hit_on(bool b); void dbg_hit_on(bool c, bool b); void dbg_mean_of(int v); void dbg_print(); typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits"); inline TimePoint now() { return std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::steady_clock::now().time_since_epoch()).count(); } template<class Entry, int Size> struct HashTable { Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; } private: std::vector<Entry> table = std::vector<Entry>(Size); // Allocate on the heap }; enum SyncCout { IO_LOCK, IO_UNLOCK }; std::ostream& operator<<(std::ostream&, SyncCout); #define sync_cout std::cout << IO_LOCK #define sync_endl std::endl << IO_UNLOCK /// xorshift64star Pseudo-Random Number Generator /// This class is based on original code written and dedicated /// to the public domain by Sebastiano Vigna (2014). /// It has the following characteristics: /// /// - Outputs 64-bit numbers /// - Passes Dieharder and SmallCrush test batteries /// - Does not require warm-up, no zeroland to escape /// - Internal state is a single 64-bit integer /// - Period is 2^64 - 1 /// - Speed: 1.60 ns/call (Core i7 @3.40GHz) /// /// For further analysis see /// <http://vigna.di.unimi.it/ftp/papers/xorshift.pdf> class PRNG { uint64_t s; uint64_t rand64() { s ^= s >> 12, s ^= s << 25, s ^= s >> 27; return s * 2685821657736338717LL; } public: PRNG(uint64_t seed) : s(seed) { assert(seed); } template<typename T> T rand() { return T(rand64()); } /// Special generator used to fast init magic numbers. /// Output values only have 1/8th of their bits set on average. template<typename T> T sparse_rand() { return T(rand64() & rand64() & rand64()); } }; /// Under Windows it is not possible for a process to run on more than one /// logical processor group. This usually means to be limited to use max 64 /// cores. To overcome this, some special platform specific API should be /// called to set group affinity for each thread. Original code from Texel by /// Peter Ă–sterlund. namespace WinProcGroup { void bindThisThread(size_t idx); } #endif // #ifndef MISC_H_INCLUDED namespace Pawns { /// Pawns::Entry contains various information about a pawn structure. A lookup /// to the pawn hash table (performed by calling the probe function) returns a /// pointer to an Entry object. struct Entry { Score pawn_score(Color c) const { return scores[c]; } Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); } template<Color Us> Score king_safety(const Position& pos) { return kingSquares[Us] == pos.square<KING>(Us) && castlingRights[Us] == pos.castling_rights(Us) ? kingSafety[Us] : (kingSafety[Us] = do_king_safety<Us>(pos)); } template<Color Us> Score do_king_safety(const Position& pos); template<Color Us> Score evaluate_shelter(const Position& pos, Square ksq); Key key; Score scores[COLOR_NB]; Bitboard passedPawns[COLOR_NB]; Bitboard pawnAttacks[COLOR_NB]; Bitboard pawnAttacksSpan[COLOR_NB]; Square kingSquares[COLOR_NB]; Score kingSafety[COLOR_NB]; int castlingRights[COLOR_NB]; }; typedef HashTable<Entry, 131072> Table; Entry* probe(const Position& pos); } // namespace Pawns #endif // #ifndef PAWNS_H_INCLUDED namespace { #define V Value #define S(mg, eg) make_score(mg, eg) // Pawn penalties constexpr Score Backward = S( 9, 24); constexpr Score BlockedStorm = S(82, 82); constexpr Score Doubled = S(11, 56); constexpr Score Isolated = S( 5, 15); constexpr Score WeakLever = S( 0, 56); constexpr Score WeakUnopposed = S(13, 27); // Connected pawn bonus constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; // Strength of pawn shelter for our king by [distance from edge][rank]. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = { { V( -6), V( 81), V( 93), V( 58), V( 39), V( 18), V( 25) }, { V(-43), V( 61), V( 35), V(-49), V(-29), V(-11), V( -63) }, { V(-10), V( 75), V( 23), V( -2), V( 32), V( 3), V( -45) }, { V(-39), V(-13), V(-29), V(-52), V(-48), V(-67), V(-166) } }; // Danger of enemy pawns moving toward our king by [distance from edge][rank]. // RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn // is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn // on edge, likely blocked by our king. constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = { { V( 89), V(-285), V(-185), V(93), V(57), V( 45), V( 51) }, { V( 44), V( -18), V( 123), V(46), V(39), V( -7), V( 23) }, { V( 4), V( 52), V( 162), V(37), V( 7), V(-14), V( -2) }, { V(-10), V( -14), V( 90), V(15), V( 2), V( -7), V(-16) } }; #undef S #undef V template<Color C> constexpr Bitboard pawn_double_attacks_bb(Bitboard b) { return C == WHITE ? shift<NORTH_WEST>(b) & shift<NORTH_EAST>(b) : shift<SOUTH_WEST>(b) & shift<SOUTH_EAST>(b); } template<Color Us> inline Score evaluate_pawn( Bitboard square_file_bb, Bitboard square_rank_bb, int connected_scale_factor, int rank_scale_factor, Bitboard ourPawns, Bitboard theirPawns, Bitboard doubleAttackThem, Bitboard &phalanxResult, Bitboard &leverPushResult, Bitboard &passedResult ) { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); constexpr Bitboard FarSideBB = (Us == WHITE) ? (RANK_5 | RANK_6 | RANK_7 | RANK_8) : (RANK_1 | RANK_2 | RANK_3 | RANK_4); Bitboard square_bb = square_file_bb & square_rank_bb; Bitboard above2_bb = shift<Direction(NORTH+NORTH)>(square_bb); Bitboard neighbour_mask_bb = adjacent_files_bb(square_file_bb); Bitboard above1_mask_bb = shift<Up>(neighbour_mask_bb & square_rank_bb); Bitboard above2_mask_bb = shift<Up>(above1_mask_bb); Bitboard opposed = theirPawns & forward_file_bb( Us, square_file_bb, square_rank_bb); Bitboard stoppers = theirPawns & passed_pawn_span(Us, square_file_bb, square_rank_bb); Bitboard lever = theirPawns & above1_mask_bb; Bitboard leverPush = theirPawns & above2_mask_bb; Bitboard doubled = ourPawns & shift<Down>(square_bb); // should this be anywhere behind, not just immediately behind? Bitboard neighbours = ourPawns & neighbour_mask_bb; Bitboard phalanx = neighbours & square_rank_bb; Bitboard support = neighbours & shift<Down>(square_rank_bb); Bitboard west_bb = support & shift<WEST>(square_file_bb); Bitboard east_bb = support & shift<EAST>(square_file_bb); #if defined(USE_SSE4) #define IFTHEN2(A,B) ((A) * (B)) #define IFTHEN3(A,B,C) ((A) * (B) * (C)) #define IFTHEN4(A,B,C,D) ((A) * (B) * (C) * (D)) #else #define IFTHEN2(A,B) ((A) ? (B) : 0) #define IFTHEN3(A,B,C) ((A) ? ((B) ? (C) : 0) : 0) #define IFTHEN4(A,B,C,D) ((A) ? ((B) ? ((C) ? (D) : 0) : 0) : 0) #endif // A pawn is backward when it is behind all pawns of the same color on // the adjacent files and cannot safely advance. // Phalanx and isolated pawns will be excluded when the pawn is scored. Bitboard forward_neighbours = neighbours & forward_ranks_bb(Them, square_rank_bb); Bitboard stoppers_leverPush_above = stoppers & (leverPush | above2_bb); int backward_is_true = (forward_neighbours == 0) & (stoppers_leverPush_above != 0); // A pawn is passed if one of the three following conditions is true: // (a) there is no stoppers except some levers int passed_is_true = ((stoppers ^ lever) == 0); // (b) the only stoppers are the leverPush, but we outnumber them // However, because we can't vectorize popcount(), we have to defer this // calculation to the caller's exit phase int stoppers_xor_leverpush_iszero = ((stoppers ^ leverPush) == 0); // (c) there is only one front stopper which can be levered. Bitboard theirPawns_or_doubleAttackThem = (theirPawns | doubleAttackThem); passed_is_true |= ( (stoppers == above2_bb) & ((square_rank_bb & FarSideBB) != 0) & (shift<Up>(support) & ~theirPawns_or_doubleAttackThem) ); int phalanx_iszero = (phalanx == 0); int opposed_iszero = (opposed == 0); int neighbours_iszero = (neighbours == 0); int support_iszero = (support == 0); int support_or_phalanx_iszero = ((support | phalanx) == 0); int support_or_phalanx_isnonzero = 1 - support_or_phalanx_iszero; int phalanx_scale = 3 - phalanx_iszero; int opposed_scale = 2 - opposed_iszero; // reversed sense, to avoid having to divide int v = connected_scale_factor * phalanx_scale * opposed_scale; v += 17 * (west_bb != 0); v += 17 * (east_bb != 0); int isolated_score = Isolated + IFTHEN2(opposed_iszero, WeakUnopposed); int backward_score = Backward + IFTHEN2(opposed_iszero, WeakUnopposed); int lever_more_than_one = lever & (lever - 1); int doubled_score = int(Doubled) * (doubled != 0) + int(WeakLever) * (lever_more_than_one != 0); // Score this pawn int score = IFTHEN2(support_or_phalanx_isnonzero, v * rank_scale_factor); score -= IFTHEN3(neighbours_iszero, support_or_phalanx_iszero, isolated_score); score += IFTHEN4(neighbours_iszero, support_or_phalanx_iszero, backward_is_true, backward_score); score -= IFTHEN2(support_iszero, doubled_score); passedResult = IFTHEN2(passed_is_true, square_bb); phalanxResult = IFTHEN2(stoppers_xor_leverpush_iszero, phalanx); leverPushResult = IFTHEN2(stoppers_xor_leverpush_iszero, leverPush); return Score(score); } template<Color Us> Score evaluate_pawn_set(const Square* SquareList, int NUM_PAWNS, Bitboard ourPawns, Bitboard theirPawns, Bitboard &passedResult) { Bitboard Square_file_bb [8]; Bitboard Square_rank_bb [8]; Score score_array [8]; Bitboard passed_array [8]; Bitboard phalanx_array [8]; Bitboard leverPush_array[8]; Square squares[8]; for(int i = 0; i < NUM_PAWNS; ++i) squares[i] = SquareList[i]; for(int i = NUM_PAWNS; i < 8; ++i) squares[i] = SquareList[NUM_PAWNS-1]; int connected_scale_array[8]; int rank_scale_array [8]; Score final_score; Bitboard final_passed; int i; Square s; constexpr Color Them = (Us == WHITE) ? BLACK : WHITE; Bitboard doubleAttackThem = pawn_double_attacks_bb<Them>(theirPawns); // Non-vectorizable entry code for (i=0; i<8; i++) { s = squares[i]; Square_file_bb[i] = file_bb(s); Square_rank_bb[i] = rank_bb(s); Rank r = relative_rank(Us, s); connected_scale_array[i] = Connected[r] >> 1; rank_scale_array[i] = 65536 + (r - 2) / 4; // scales v to yield a Score } // Vectorizable main code for (i=0; i<8; i++) { score_array[i] = evaluate_pawn<Us>( Square_file_bb[i], Square_rank_bb[i], connected_scale_array[i], rank_scale_array[i], ourPawns, theirPawns, doubleAttackThem, phalanx_array[i], leverPush_array[i], passed_array[i] ); } // Non-vectorizable exit code final_passed = Bitboard(0); final_score = SCORE_ZERO; for (i=0; i<NUM_PAWNS; i++) { final_passed |= passed_array[i]; if ( phalanx_array[i] && (popcount(phalanx_array[i]) >= popcount(leverPush_array[i])) ) { final_passed |= Square_file_bb[i] & Square_rank_bb[i]; } final_score += score_array[i]; } passedResult = final_passed; return final_score; } // To evaluate all the pawns, select the appropriate unrolled template // based on both the current side's colour and on the number of pawns. // This allows the compiler to implement this differently as needed. template<Color Us> Score evaluate(const Position& pos, Pawns::Entry* e) { const Square* pl = pos.squares<PAWN>(Us); Score final_score; Bitboard final_passed; constexpr Color Them = (Us == WHITE ? BLACK : WHITE); Bitboard ourPawns = pos.pieces(Us, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN); e->kingSquares[Us] = SQ_NONE; e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns); switch (popcount(ourPawns)) { case 0: final_score = SCORE_ZERO; final_passed = NoSquares; break; case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: final_score = evaluate_pawn_set<Us>(pl, popcount(ourPawns), ourPawns, theirPawns, final_passed); break; default: final_score = SCORE_ZERO; final_passed = NoSquares; break; } e->passedPawns[Us] = final_passed; return final_score; } } // namespace namespace Pawns { /// Entry::evaluate_shelter() calculates the shelter bonus and the storm /// penalty for a king, looking at the king file and the two closest files. template<Color Us> Score Entry::evaluate_shelter(const Position& pos, Square ksq) { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq); Bitboard ourPawns = b & pos.pieces(Us); Bitboard theirPawns = b & pos.pieces(Them); Score bonus = make_score(5, 5); File center = clamp(file_of(ksq), FILE_B, FILE_G); for (File f = File(center - 1); f <= File(center + 1); ++f) { b = ourPawns & file_bb(f); int ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; b = theirPawns & file_bb(f); int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; int d = std::min(f, ~f); bonus += make_score(ShelterStrength[d][ourRank], 0); if (ourRank && (ourRank == theirRank - 1)) bonus -= BlockedStorm * int(theirRank == RANK_3); else bonus -= make_score(UnblockedStorm[d][theirRank], 0); } return bonus; } /// Entry::do_king_safety() calculates a bonus for king safety. It is called only /// when king square changes, which is about 20% of total king_safety() calls. template<Color Us> Score Entry::do_king_safety(const Position& pos) { Square ksq = pos.square<KING>(Us); kingSquares[Us] = ksq; castlingRights[Us] = pos.castling_rights(Us); Score shelters[3] = { evaluate_shelter<Us>(pos, ksq), make_score(-VALUE_INFINITE, 0), make_score(-VALUE_INFINITE, 0) }; // If we can castle use the bonus after castling if it is bigger if (pos.can_castle(Us & KING_SIDE)) shelters[1] = evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)); if (pos.can_castle(Us & QUEEN_SIDE)) shelters[2] = evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)); for (int i : {1, 2}) if (mg_value(shelters[i]) > mg_value(shelters[0])) shelters[0] = shelters[i]; // In endgame we like to bring our king near our closest pawn Bitboard pawns = pos.pieces(Us, PAWN); int minPawnDist = pawns ? 8 : 0; if (pawns & PseudoAttacks[KING][ksq]) minPawnDist = 1; else while (pawns) minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns))); return shelters[0] - make_score(0, 16 * minPawnDist); } // Explicit template instantiation template Score Entry::do_king_safety<WHITE>(const Position& pos); template Score Entry::do_king_safety<BLACK>(const Position& pos); void test(const Position& pos, Pawns::Entry* e) { evaluate<WHITE>(pos, e); } } // namespace Pawns
Become a Patron
Sponsor on GitHub
Donate via PayPal
Source on GitHub
Mailing list
Installed libraries
Wiki
Report an issue
How it works
Contact the author
CE on Mastodon
About the author
Statistics
Changelog
Version tree