mirror of
https://github.com/opelly27/Stockfish.git
synced 2026-05-20 14:27:45 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 48cfdfcc46 | |||
| fa7b244dc9 | |||
| 29ad6a73fc | |||
| ac48b16708 | |||
| 38b1c4b6b8 | |||
| 162dbeaee8 | |||
| 85146ca0a9 | |||
| 02e12a69a7 | |||
| 6e8116e38f | |||
| 29f7fab2a9 | |||
| 2af986bf31 | |||
| b67146b100 | |||
| c1b1a94d81 | |||
| 17212e5fcc | |||
| 46921dff27 | |||
| 941016e7a2 | |||
| 290caf9960 | |||
| 43fa3a4d64 | |||
| 64b4836d12 | |||
| 5df7d62eb9 | |||
| 82179c70dc | |||
| de17652e47 | |||
| 647b79b556 |
@@ -1,333 +0,0 @@
|
||||
name: Stockfish
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- tools
|
||||
- github_ci
|
||||
- github_ci_armv7
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- tools
|
||||
jobs:
|
||||
Stockfish:
|
||||
name: ${{ matrix.config.name }}
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
env:
|
||||
COMPILER: ${{ matrix.config.compiler }}
|
||||
COMP: ${{ matrix.config.comp }}
|
||||
CXXFLAGS: "-Werror"
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
# set the variable for the required tests:
|
||||
# run_expensive_tests: true
|
||||
# run_32bit_tests: true
|
||||
# run_64bit_tests: true
|
||||
# run_armv8_tests: true
|
||||
# run_armv7_tests: true
|
||||
- {
|
||||
name: "Ubuntu 20.04 GCC",
|
||||
os: ubuntu-20.04,
|
||||
compiler: g++,
|
||||
comp: gcc,
|
||||
run_expensive_tests: true,
|
||||
run_32bit_tests: true,
|
||||
run_64bit_tests: true,
|
||||
shell: 'bash {0}'
|
||||
}
|
||||
- {
|
||||
name: "Ubuntu 20.04 Clang",
|
||||
os: ubuntu-20.04,
|
||||
compiler: clang++,
|
||||
comp: clang,
|
||||
run_32bit_tests: true,
|
||||
run_64bit_tests: true,
|
||||
shell: 'bash {0}'
|
||||
}
|
||||
- {
|
||||
name: "Ubuntu 20.04 NDK armv8",
|
||||
os: ubuntu-20.04,
|
||||
compiler: aarch64-linux-android21-clang++,
|
||||
comp: ndk,
|
||||
run_armv8_tests: true,
|
||||
shell: 'bash {0}'
|
||||
}
|
||||
- {
|
||||
name: "Ubuntu 20.04 NDK armv7",
|
||||
os: ubuntu-20.04,
|
||||
compiler: armv7a-linux-androideabi21-clang++,
|
||||
comp: ndk,
|
||||
run_armv7_tests: true,
|
||||
shell: 'bash {0}'
|
||||
}
|
||||
- {
|
||||
name: "MacOS 10.15 Apple Clang",
|
||||
os: macos-10.15,
|
||||
compiler: clang++,
|
||||
comp: clang,
|
||||
run_64bit_tests: true,
|
||||
shell: 'bash {0}'
|
||||
}
|
||||
- {
|
||||
name: "MacOS 10.15 GCC 10",
|
||||
os: macos-10.15,
|
||||
compiler: g++-10,
|
||||
comp: gcc,
|
||||
run_64bit_tests: true,
|
||||
shell: 'bash {0}'
|
||||
}
|
||||
- {
|
||||
name: "Windows 2022 Mingw-w64 GCC x86_64",
|
||||
os: windows-2022,
|
||||
compiler: g++,
|
||||
comp: mingw,
|
||||
run_64bit_tests: true,
|
||||
msys_sys: 'mingw64',
|
||||
msys_env: 'x86_64-gcc',
|
||||
shell: 'msys2 {0}'
|
||||
}
|
||||
- {
|
||||
name: "Windows 2022 Mingw-w64 GCC i686",
|
||||
os: windows-2022,
|
||||
compiler: g++,
|
||||
comp: mingw,
|
||||
run_32bit_tests: true,
|
||||
msys_sys: 'mingw32',
|
||||
msys_env: 'i686-gcc',
|
||||
shell: 'msys2 {0}'
|
||||
}
|
||||
- {
|
||||
name: "Windows 2022 Mingw-w64 Clang x86_64",
|
||||
os: windows-2022,
|
||||
compiler: clang++,
|
||||
comp: clang,
|
||||
run_64bit_tests: true,
|
||||
msys_sys: 'clang64',
|
||||
msys_env: 'clang-x86_64-clang',
|
||||
shell: 'msys2 {0}'
|
||||
}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: src
|
||||
shell: ${{ matrix.config.shell }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download required linux packages
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install expect valgrind g++-multilib qemu-user
|
||||
|
||||
- name: Setup msys and install required packages
|
||||
if: runner.os == 'Windows'
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{matrix.config.msys_sys}}
|
||||
install: mingw-w64-${{matrix.config.msys_env}} make git expect
|
||||
|
||||
- name: Download the used network from the fishtest framework
|
||||
run: |
|
||||
make net
|
||||
|
||||
- name: Extract the bench number from the commit history
|
||||
run: |
|
||||
git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig
|
||||
[ -s git_sig ] && echo "benchref=$(cat git_sig)" >> $GITHUB_ENV && echo "Reference bench:" $(cat git_sig) || echo "No bench found"
|
||||
|
||||
- name: Check compiler
|
||||
run: |
|
||||
export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin
|
||||
$COMPILER -v
|
||||
|
||||
- name: Test help target
|
||||
run: |
|
||||
make help
|
||||
|
||||
# x86-32 tests
|
||||
|
||||
- name: Test debug x86-32 build
|
||||
if: ${{ matrix.config.run_32bit_tests }}
|
||||
run: |
|
||||
export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG"
|
||||
make clean
|
||||
make -j2 ARCH=x86-32 optimize=no debug=yes build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-32 build
|
||||
if: ${{ matrix.config.run_32bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-32 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-32-sse41-popcnt build
|
||||
if: ${{ matrix.config.run_32bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-32-sse41-popcnt build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-32-sse2 build
|
||||
if: ${{ matrix.config.run_32bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-32-sse2 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test general-32 build
|
||||
if: ${{ matrix.config.run_32bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=general-32 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
# x86-64 tests
|
||||
|
||||
- name: Test debug x86-64-modern build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG"
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-modern optimize=no debug=yes build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-64-modern build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-modern build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-64-ssse3 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-ssse3 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-64-sse3-popcnt build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-sse3-popcnt build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-64 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test general-64 build
|
||||
if: matrix.config.run_64bit_tests
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=general-64 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
# x86-64 with newer extensions tests
|
||||
|
||||
- name: Compile x86-64-avx2 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-avx2 build
|
||||
|
||||
- name: Compile x86-64-bmi2 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-bmi2 build
|
||||
|
||||
- name: Compile x86-64-avx512 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-avx512 build
|
||||
|
||||
- name: Compile x86-64-vnni512 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-vnni512 build
|
||||
|
||||
- name: Compile x86-64-vnni256 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-vnni256 build
|
||||
|
||||
# armv8 tests
|
||||
|
||||
- name: Test armv8 build
|
||||
if: ${{ matrix.config.run_armv8_tests }}
|
||||
run: |
|
||||
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
|
||||
export LDFLAGS="-static -Wno-unused-command-line-argument"
|
||||
make clean
|
||||
make -j2 ARCH=armv8 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
# armv7 tests
|
||||
|
||||
- name: Test armv7 build
|
||||
if: ${{ matrix.config.run_armv7_tests }}
|
||||
run: |
|
||||
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
|
||||
export LDFLAGS="-static -Wno-unused-command-line-argument"
|
||||
make clean
|
||||
make -j2 ARCH=armv7 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test armv7-neon build
|
||||
if: ${{ matrix.config.run_armv7_tests }}
|
||||
run: |
|
||||
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
|
||||
export LDFLAGS="-static -Wno-unused-command-line-argument"
|
||||
make clean
|
||||
make -j2 ARCH=armv7-neon build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
# Other tests
|
||||
|
||||
- name: Check perft and search reproducibility
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-modern build
|
||||
../tests/perft.sh
|
||||
../tests/reprosearch.sh
|
||||
|
||||
# Sanitizers
|
||||
|
||||
- name: Run under valgrind
|
||||
if: ${{ matrix.config.run_expensive_tests }}
|
||||
run: |
|
||||
export CXXFLAGS="-O1 -fno-inline"
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-modern debug=yes optimize=no build > /dev/null
|
||||
../tests/instrumented.sh --valgrind
|
||||
../tests/instrumented.sh --valgrind-thread
|
||||
|
||||
- name: Run with UB sanitizer
|
||||
if: ${{ matrix.config.run_expensive_tests }}
|
||||
run: |
|
||||
export CXXFLAGS="-O1 -fno-inline"
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-modern sanitize=undefined optimize=no debug=yes build > /dev/null
|
||||
../tests/instrumented.sh --sanitizer-undefined
|
||||
|
||||
- name: Run with thread sanitizer
|
||||
if: ${{ matrix.config.run_expensive_tests }}
|
||||
run: |
|
||||
export CXXFLAGS="-O1 -fno-inline"
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-modern sanitize=thread optimize=no debug=yes build > /dev/null
|
||||
../tests/instrumented.sh --sanitizer-thread
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
# Files from build
|
||||
**/*.o
|
||||
**/*.s
|
||||
src/.depend
|
||||
|
||||
# Built binary
|
||||
src/stockfish*
|
||||
src/-lstdc++.res
|
||||
|
||||
# Neural network for the NNUE evaluation
|
||||
**/*.nnue
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
# List of authors for Stockfish
|
||||
|
||||
# Founders of the Stockfish project and fishtest infrastructure
|
||||
Tord Romstad (romstad)
|
||||
Marco Costalba (mcostalba)
|
||||
Joona Kiiski (zamar)
|
||||
Gary Linscott (glinscott)
|
||||
|
||||
# Authors and inventors of NNUE, training, NNUE port
|
||||
Yu Nasu (ynasu87)
|
||||
Motohiro Isozaki (yaneurao)
|
||||
Hisayori Noda (nodchip)
|
||||
|
||||
# all other authors of the code in alphabetical order
|
||||
Aditya (absimaldata)
|
||||
Adrian Petrescu (apetresc)
|
||||
Ajith Chandy Jose (ajithcj)
|
||||
Alain Savard (Rocky640)
|
||||
Alayan Feh (Alayan-stk-2)
|
||||
Alexander Kure
|
||||
Alexander Pagel (Lolligerhans)
|
||||
Alfredo Menezes (lonfom169)
|
||||
Ali AlZhrani (Cooffe)
|
||||
Andrei Vetrov (proukornew)
|
||||
Andrew Grant (AndyGrant)
|
||||
Andrey Neporada (nepal)
|
||||
Andy Duplain
|
||||
Antoine Champion (antoinechampion)
|
||||
Aram Tumanian (atumanian)
|
||||
Arjun Temurnikar
|
||||
Artem Solopiy (EntityFX)
|
||||
Auguste Pop
|
||||
Balint Pfliegel
|
||||
Ben Chaney (Chaneybenjamini)
|
||||
Ben Koshy (BKSpurgeon)
|
||||
Bill Henry (VoyagerOne)
|
||||
Bojun Guo (noobpwnftw, Nooby)
|
||||
braich
|
||||
Brian Sheppard (SapphireBrand, briansheppard-toast)
|
||||
Bruno de Melo Costa (BM123499)
|
||||
Bryan Cross (crossbr)
|
||||
candirufish
|
||||
Chess13234
|
||||
Chris Cain (ceebo)
|
||||
Dale Weiler (graphitemaster)
|
||||
Dan Schmidt (dfannius)
|
||||
Daniel Axtens (daxtens)
|
||||
Daniel Dugovic (ddugovic)
|
||||
Dariusz Orzechowski (dorzechowski)
|
||||
David Zar
|
||||
Daylen Yang (daylen)
|
||||
Deshawn Mohan-Smith (GoldenRare)
|
||||
Dieter Dobbelaere (ddobbelaere)
|
||||
DiscanX
|
||||
Dominik Schlösser (domschl)
|
||||
double-beep
|
||||
Douglas Matos Gomes (dsmsgms)
|
||||
Eduardo Cáceres (eduherminio)
|
||||
Eelco de Groot (KingDefender)
|
||||
Elvin Liu (solarlight2)
|
||||
erbsenzaehler
|
||||
Ernesto Gatti
|
||||
Linmiao Xu (linrock)
|
||||
Fabian Beuke (madnight)
|
||||
Fabian Fichter (ianfab)
|
||||
Fanael Linithien (Fanael)
|
||||
fanon
|
||||
Fauzi Akram Dabat (FauziAkram)
|
||||
Felix Wittmann
|
||||
gamander
|
||||
Gary Heckman (gheckman)
|
||||
George Sobala (gsobala)
|
||||
gguliash
|
||||
Giacomo Lorenzetti (G-Lorenz)
|
||||
Gian-Carlo Pascutto (gcp)
|
||||
Gontran Lemaire (gonlem)
|
||||
Goodkov Vasiliy Aleksandrovich (goodkov)
|
||||
Gregor Cramer
|
||||
GuardianRM
|
||||
Günther Demetz (pb00067, pb00068)
|
||||
Guy Vreuls (gvreuls)
|
||||
Henri Wiechers
|
||||
Hiraoka Takuya (HiraokaTakuya)
|
||||
homoSapiensSapiens
|
||||
Hongzhi Cheng
|
||||
Ivan Ivec (IIvec)
|
||||
Jacques B. (Timshel)
|
||||
Jan Ondruš (hxim)
|
||||
Jared Kish (Kurtbusch)
|
||||
Jarrod Torriero (DU-jdto)
|
||||
Jean Gauthier (OuaisBla)
|
||||
Jean-Francois Romang (jromang)
|
||||
Jekaa
|
||||
Jerry Donald Watson (jerrydonaldwatson)
|
||||
jjoshua2
|
||||
Jonathan Calovski (Mysseno)
|
||||
Jonathan Buladas Dumale (SFisGOD)
|
||||
Joost VandeVondele (vondele)
|
||||
Jörg Oster (joergoster)
|
||||
Joseph Ellis (jhellis3)
|
||||
Joseph R. Prostko
|
||||
Julian Willemer (NightlyKing)
|
||||
jundery
|
||||
Justin Blanchard (UncombedCoconut)
|
||||
Kelly Wilson
|
||||
Ken Takusagawa
|
||||
Kian E (KJE-98)
|
||||
kinderchocolate
|
||||
Kiran Panditrao (Krgp)
|
||||
Kojirion
|
||||
Krystian Kuzniarek (kuzkry)
|
||||
Leonardo Ljubičić (ICCF World Champion)
|
||||
Leonid Pechenik (lp--)
|
||||
Liam Keegan (lkeegan)
|
||||
Linus Arver (listx)
|
||||
loco-loco
|
||||
Lub van den Berg (ElbertoOne)
|
||||
Luca Brivio (lucabrivio)
|
||||
Lucas Braesch (lucasart)
|
||||
Lyudmil Antonov (lantonov)
|
||||
Maciej Żenczykowski (zenczykowski)
|
||||
Malcolm Campbell (xoto10)
|
||||
Mark Tenzer (31m059)
|
||||
marotear
|
||||
Matt Ginsberg (mattginsberg)
|
||||
Matthew Lai (matthewlai)
|
||||
Matthew Sullivan (Matt14916)
|
||||
Maxim Molchanov (Maxim)
|
||||
Michael An (man)
|
||||
Michael Byrne (MichaelB7)
|
||||
Michael Chaly (Vizvezdenec)
|
||||
Michael Stembera (mstembera)
|
||||
Michael Whiteley (protonspring)
|
||||
Michel Van den Bergh (vdbergh)
|
||||
Miguel Lahoz (miguel-l)
|
||||
Mikael Bäckman (mbootsector)
|
||||
Mike Babigian (Farseer)
|
||||
Mira
|
||||
Miroslav Fontán (Hexik)
|
||||
Moez Jellouli (MJZ1977)
|
||||
Mohammed Li (tthsqe12)
|
||||
Nathan Rugg (nmrugg)
|
||||
Nick Pelling (nickpelling)
|
||||
Nicklas Persson (NicklasPersson)
|
||||
Niklas Fiekas (niklasf)
|
||||
Nikolay Kostov (NikolayIT)
|
||||
Nguyen Pham (nguyenpham)
|
||||
Norman Schmidt (FireFather)
|
||||
notruck
|
||||
Ofek Shochat (OfekShochat, ghostway)
|
||||
Ondrej Mosnáček (WOnder93)
|
||||
Oskar Werkelin Ahlin
|
||||
Pablo Vazquez
|
||||
Panthee
|
||||
Pascal Romaret
|
||||
Pasquale Pigazzini (ppigazzini)
|
||||
Patrick Jansen (mibere)
|
||||
pellanda
|
||||
Peter Schneider (pschneider1968)
|
||||
Peter Zsifkovits (CoffeeOne)
|
||||
Praveen Kumar Tummala (praveentml)
|
||||
Rahul Dsilva (silversolver1)
|
||||
Ralph Stößer (Ralph Stoesser)
|
||||
Raminder Singh
|
||||
renouve
|
||||
Reuven Peleg
|
||||
Richard Lloyd
|
||||
Rodrigo Exterckötter Tjäder
|
||||
Ron Britvich (Britvich)
|
||||
Ronald de Man (syzygy1, syzygy)
|
||||
rqs
|
||||
Rui Coelho (ruicoelhopedro)
|
||||
Ryan Schmitt
|
||||
Ryan Takker
|
||||
Sami Kiminki (skiminki)
|
||||
Sebastian Buchwald (UniQP)
|
||||
Sergei Antonov (saproj)
|
||||
Sergei Ivanov (svivanov72)
|
||||
Sergio Vieri (sergiovieri)
|
||||
sf-x
|
||||
Shane Booth (shane31)
|
||||
Shawn Varghese (xXH4CKST3RXx)
|
||||
Siad Daboul (Topologist)
|
||||
Stefan Geschwentner (locutus2)
|
||||
Stefano Cardanobile (Stefano80)
|
||||
Steinar Gunderson (sesse)
|
||||
Stéphane Nicolet (snicolet)
|
||||
Prokop Randáček (ProkopRandacek)
|
||||
Thanar2
|
||||
thaspel
|
||||
theo77186
|
||||
Tom Truscott
|
||||
Tom Vijlbrief (tomtor)
|
||||
Tomasz Sobczyk (Sopel97)
|
||||
Torsten Franz (torfranz, tfranzer)
|
||||
Torsten Hellwig (Torom)
|
||||
Tracey Emery (basepr1me)
|
||||
tttak
|
||||
Unai Corzo (unaiic)
|
||||
Uri Blass (uriblass)
|
||||
Vince Negri (cuddlestmonkey)
|
||||
xefoci7612
|
||||
zz4032
|
||||
|
||||
|
||||
# Additionally, we acknowledge the authors and maintainers of fishtest,
|
||||
# an amazing and essential framework for the development of Stockfish!
|
||||
#
|
||||
# https://github.com/glinscott/fishtest/blob/master/AUTHORS
|
||||
@@ -1,337 +0,0 @@
|
||||
## Overview
|
||||
|
||||
[](https://github.com/official-stockfish/Stockfish/actions)
|
||||
[](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master)
|
||||
|
||||
[Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine
|
||||
derived from Glaurung 2.1. Stockfish is not a complete chess program and requires a
|
||||
UCI-compatible graphical user interface (GUI) (e.g. XBoard with PolyGlot, Scid,
|
||||
Cute Chess, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order
|
||||
to be used comfortably. Read the documentation for your GUI of choice for information
|
||||
about how to use Stockfish with it.
|
||||
|
||||
The Stockfish engine features two evaluation functions for chess. The efficiently
|
||||
updatable neural network (NNUE) based evaluation is the default and by far the strongest.
|
||||
The classical evaluation based on handcrafted terms remains available. The strongest
|
||||
network is integrated in the binary and downloaded automatically during the build process.
|
||||
The NNUE evaluation benefits from the vector intrinsics available on most CPUs (sse2,
|
||||
avx2, neon, or similar).
|
||||
|
||||
## Files
|
||||
|
||||
This distribution of Stockfish consists of the following files:
|
||||
|
||||
* [Readme.md](https://github.com/official-stockfish/Stockfish/blob/master/README.md),
|
||||
the file you are currently reading.
|
||||
|
||||
* [Copying.txt](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt),
|
||||
a text file containing the GNU General Public License version 3.
|
||||
|
||||
* [AUTHORS](https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS),
|
||||
a text file with the list of authors for the project
|
||||
|
||||
* [src](https://github.com/official-stockfish/Stockfish/tree/master/src),
|
||||
a subdirectory containing the full source code, including a Makefile
|
||||
that can be used to compile Stockfish on Unix-like systems.
|
||||
|
||||
* a file with the .nnue extension, storing the neural network for the NNUE
|
||||
evaluation. Binary distributions will have this file embedded.
|
||||
|
||||
## The UCI protocol and available options
|
||||
|
||||
The Universal Chess Interface (UCI) is a standard protocol used to communicate with
|
||||
a chess engine, and is the recommended way to do so for typical graphical user interfaces
|
||||
(GUI) or chess tools. Stockfish implements the majority of its options as described
|
||||
in [the UCI protocol](https://www.shredderchess.com/download/div/uci.zip).
|
||||
|
||||
Developers can see the default values for UCI options available in Stockfish by typing
|
||||
`./stockfish uci` in a terminal, but the majority of users will typically see them and
|
||||
change them via a chess GUI. This is a list of available UCI options in Stockfish:
|
||||
|
||||
* #### Threads
|
||||
The number of CPU threads used for searching a position. For best performance, set
|
||||
this equal to the number of CPU cores available.
|
||||
|
||||
* #### Hash
|
||||
The size of the hash table in MB. It is recommended to set Hash after setting Threads.
|
||||
|
||||
* #### Clear Hash
|
||||
Clear the hash table.
|
||||
|
||||
* #### Ponder
|
||||
Let Stockfish ponder its next move while the opponent is thinking.
|
||||
|
||||
* #### MultiPV
|
||||
Output the N best lines (principal variations, PVs) when searching.
|
||||
Leave at 1 for best performance.
|
||||
|
||||
* #### Use NNUE
|
||||
Toggle between the NNUE and classical evaluation functions. If set to "true",
|
||||
the network parameters must be available to load from file (see also EvalFile),
|
||||
if they are not embedded in the binary.
|
||||
|
||||
* #### EvalFile
|
||||
The name of the file of the NNUE evaluation parameters. Depending on the GUI the
|
||||
filename might have to include the full path to the folder/directory that contains
|
||||
the file. Other locations, such as the directory that contains the binary and the
|
||||
working directory, are also searched.
|
||||
|
||||
* #### UCI_AnalyseMode
|
||||
An option handled by your GUI.
|
||||
|
||||
* #### UCI_Chess960
|
||||
An option handled by your GUI. If true, Stockfish will play Chess960.
|
||||
|
||||
* #### UCI_ShowWDL
|
||||
If enabled, show approximate WDL statistics as part of the engine output.
|
||||
These WDL numbers model expected game outcomes for a given evaluation and
|
||||
game ply for engine self-play at fishtest LTC conditions (60+0.6s per game).
|
||||
|
||||
* #### UCI_LimitStrength
|
||||
Enable weaker play aiming for an Elo rating as set by UCI_Elo. This option overrides Skill Level.
|
||||
|
||||
* #### UCI_Elo
|
||||
If enabled by UCI_LimitStrength, aim for an engine strength of the given Elo.
|
||||
This Elo rating has been calibrated at a time control of 60s+0.6s and anchored to CCRL 40/4.
|
||||
|
||||
* #### Skill Level
|
||||
Lower the Skill Level in order to make Stockfish play weaker (see also UCI_LimitStrength).
|
||||
Internally, MultiPV is enabled, and with a certain probability depending on the Skill Level a
|
||||
weaker move will be played.
|
||||
|
||||
* #### SyzygyPath
|
||||
Path to the folders/directories storing the Syzygy tablebase files. Multiple
|
||||
directories are to be separated by ";" on Windows and by ":" on Unix-based
|
||||
operating systems. Do not use spaces around the ";" or ":".
|
||||
|
||||
Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6`
|
||||
|
||||
It is recommended to store .rtbw files on an SSD. There is no loss in storing
|
||||
the .rtbz files on a regular HDD. It is recommended to verify all md5 checksums
|
||||
of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will
|
||||
lead to engine crashes.
|
||||
|
||||
* #### SyzygyProbeDepth
|
||||
Minimum remaining search depth for which a position is probed. Set this option
|
||||
to a higher value to probe less aggressively if you experience too much slowdown
|
||||
(in terms of nps) due to tablebase probing.
|
||||
|
||||
* #### Syzygy50MoveRule
|
||||
Disable to let fifty-move rule draws detected by Syzygy tablebase probes count
|
||||
as wins or losses. This is useful for ICCF correspondence games.
|
||||
|
||||
* #### SyzygyProbeLimit
|
||||
Limit Syzygy tablebase probing to positions with at most this many pieces left
|
||||
(including kings and pawns).
|
||||
|
||||
* #### Move Overhead
|
||||
Assume a time delay of x ms due to network and GUI overheads. This is useful to
|
||||
avoid losses on time in those cases.
|
||||
|
||||
* #### Slow Mover
|
||||
Lower values will make Stockfish take less time in games, higher values will
|
||||
make it think longer.
|
||||
|
||||
* #### nodestime
|
||||
Tells the engine to use nodes searched instead of wall time to account for
|
||||
elapsed time. Useful for engine testing.
|
||||
|
||||
* #### Debug Log File
|
||||
Write all communication to and from the engine into a text file.
|
||||
|
||||
For developers the following non-standard commands might be of interest, mainly useful for debugging:
|
||||
|
||||
* #### bench *ttSize threads limit fenFile limitType evalType*
|
||||
Performs a standard benchmark using various options. The signature of a version
|
||||
(standard node count) is obtained using all defaults. `bench` is currently
|
||||
`bench 16 1 13 default depth mixed`.
|
||||
|
||||
* #### compiler
|
||||
Give information about the compiler and environment used for building a binary.
|
||||
|
||||
* #### d
|
||||
Display the current position, with ascii art and fen.
|
||||
|
||||
* #### eval
|
||||
Return the evaluation of the current position.
|
||||
|
||||
* #### export_net [filename]
|
||||
Exports the currently loaded network to a file.
|
||||
If the currently loaded network is the embedded network and the filename
|
||||
is not specified then the network is saved to the file matching the name
|
||||
of the embedded network, as defined in evaluate.h.
|
||||
If the currently loaded network is not the embedded network (some net set
|
||||
through the UCI setoption) then the filename parameter is required and the
|
||||
network is saved into that file.
|
||||
|
||||
* #### flip
|
||||
Flips the side to move.
|
||||
|
||||
|
||||
## A note on classical evaluation versus NNUE evaluation
|
||||
|
||||
Both approaches assign a value to a position that is used in alpha-beta (PVS) search
|
||||
to find the best move. The classical evaluation computes this value as a function
|
||||
of various chess concepts, handcrafted by experts, tested and tuned using fishtest.
|
||||
The NNUE evaluation computes this value with a neural network based on basic
|
||||
inputs (e.g. piece positions only). The network is optimized and trained
|
||||
on the evaluations of millions of positions at moderate search depth.
|
||||
|
||||
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
|
||||
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
|
||||
of the neural network need to be updated after a typical chess move.
|
||||
[The nodchip repository](https://github.com/nodchip/Stockfish) provided the first
|
||||
version of the needed tools to train and develop the NNUE networks. Today, more
|
||||
advanced training tools are available in
|
||||
[the nnue-pytorch repository](https://github.com/glinscott/nnue-pytorch/),
|
||||
while data generation tools are available in
|
||||
[a dedicated branch](https://github.com/official-stockfish/Stockfish/tree/tools).
|
||||
|
||||
On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation
|
||||
results in much stronger playing strength, even if the nodes per second computed by
|
||||
the engine is somewhat lower (roughly 80% of nps is typical).
|
||||
|
||||
Notes:
|
||||
|
||||
1) the NNUE evaluation depends on the Stockfish binary and the network parameter file
|
||||
(see the EvalFile UCI option). Not every parameter file is compatible with a given
|
||||
Stockfish binary, but the default value of the EvalFile UCI option is the name of a
|
||||
network that is guaranteed to be compatible with that binary.
|
||||
|
||||
2) to use the NNUE evaluation, the additional data file with neural network parameters
|
||||
needs to be available. Normally, this file is already embedded in the binary or it can
|
||||
be downloaded. The filename for the default (recommended) net can be found as the default
|
||||
value of the `EvalFile` UCI option, with the format `nn-[SHA256 first 12 digits].nnue`
|
||||
(for instance, `nn-c157e0a5755b.nnue`). This file can be downloaded from
|
||||
```
|
||||
https://tests.stockfishchess.org/api/nn/[filename]
|
||||
```
|
||||
replacing `[filename]` as needed.
|
||||
|
||||
## What to expect from the Syzygy tablebases?
|
||||
|
||||
If the engine is searching a position that is not in the tablebases (e.g.
|
||||
a position with 8 pieces), it will access the tablebases during the search.
|
||||
If the engine reports a very large score (typically 153.xx), this means
|
||||
it has found a winning line into a tablebase position.
|
||||
|
||||
If the engine is given a position to search that is in the tablebases, it
|
||||
will use the tablebases at the beginning of the search to preselect all
|
||||
good moves, i.e. all moves that preserve the win or preserve the draw while
|
||||
taking into account the 50-move rule.
|
||||
It will then perform a search only on those moves. **The engine will not move
|
||||
immediately**, unless there is only a single good move. **The engine likely
|
||||
will not report a mate score, even if the position is known to be won.**
|
||||
|
||||
It is therefore clear that this behaviour is not identical to what one might
|
||||
be used to with Nalimov tablebases. There are technical reasons for this
|
||||
difference, the main technical reason being that Nalimov tablebases use the
|
||||
DTM metric (distance-to-mate), while the Syzygy tablebases use a variation of the
|
||||
DTZ metric (distance-to-zero, zero meaning any move that resets the 50-move
|
||||
counter). This special metric is one of the reasons that the Syzygy tablebases are
|
||||
more compact than Nalimov tablebases, while still storing all information
|
||||
needed for optimal play and in addition being able to take into account
|
||||
the 50-move rule.
|
||||
|
||||
## Large Pages
|
||||
|
||||
Stockfish supports large pages on Linux and Windows. Large pages make
|
||||
the hash access more efficient, improving the engine speed, especially
|
||||
on large hash sizes. Typical increases are 5..10% in terms of nodes per
|
||||
second, but speed increases up to 30% have been measured. The support is
|
||||
automatic. Stockfish attempts to use large pages when available and
|
||||
will fall back to regular memory allocation when this is not the case.
|
||||
|
||||
### Support on Linux
|
||||
|
||||
Large page support on Linux is obtained by the Linux kernel
|
||||
transparent huge pages functionality. Typically, transparent huge pages
|
||||
are already enabled, and no configuration is needed.
|
||||
|
||||
### Support on Windows
|
||||
|
||||
The use of large pages requires "Lock Pages in Memory" privilege. See
|
||||
[Enable the Lock Pages in Memory Option (Windows)](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows)
|
||||
on how to enable this privilege, then run [RAMMap](https://docs.microsoft.com/en-us/sysinternals/downloads/rammap)
|
||||
to double-check that large pages are used. We suggest that you reboot
|
||||
your computer after you have enabled large pages, because long Windows
|
||||
sessions suffer from memory fragmentation, which may prevent Stockfish
|
||||
from getting large pages: a fresh session is better in this regard.
|
||||
|
||||
## Compiling Stockfish yourself from the sources
|
||||
|
||||
Stockfish has support for 32 or 64-bit CPUs, certain hardware
|
||||
instructions, big-endian machines such as Power PC, and other platforms.
|
||||
|
||||
On Unix-like systems, it should be easy to compile Stockfish
|
||||
directly from the source code with the included Makefile in the folder
|
||||
`src`. In general it is recommended to run `make help` to see a list of make
|
||||
targets with corresponding descriptions.
|
||||
|
||||
```
|
||||
cd src
|
||||
make help
|
||||
make net
|
||||
make build ARCH=x86-64-modern
|
||||
```
|
||||
|
||||
When not using the Makefile to compile (for instance, with Microsoft MSVC) you
|
||||
need to manually set/unset some switches in the compiler command line; see
|
||||
file *types.h* for a quick reference.
|
||||
|
||||
When reporting an issue or a bug, please tell us which Stockfish version
|
||||
and which compiler you used to create your executable. This information
|
||||
can be found by typing the following command in a console:
|
||||
|
||||
```
|
||||
./stockfish compiler
|
||||
```
|
||||
|
||||
## Understanding the code base and participating in the project
|
||||
|
||||
Stockfish's improvement over the last decade has been a great community
|
||||
effort. There are a few ways to help contribute to its growth.
|
||||
|
||||
### Donating hardware
|
||||
|
||||
Improving Stockfish requires a massive amount of testing. You can donate
|
||||
your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview)
|
||||
and view the current tests on [Fishtest](https://tests.stockfishchess.org/tests).
|
||||
|
||||
### Improving the code
|
||||
|
||||
If you want to help improve the code, there are several valuable resources:
|
||||
|
||||
* [In this wiki,](https://www.chessprogramming.org) many techniques used in
|
||||
Stockfish are explained with a lot of background information.
|
||||
|
||||
* [The section on Stockfish](https://www.chessprogramming.org/Stockfish)
|
||||
describes many features and techniques used by Stockfish. However, it is
|
||||
generic rather than being focused on Stockfish's precise implementation.
|
||||
Nevertheless, a helpful resource.
|
||||
|
||||
* The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish).
|
||||
Discussions about Stockfish take place these days mainly in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking)
|
||||
group and on the [Stockfish Discord channel](https://discord.gg/nv8gDtt).
|
||||
The engine testing is done on [Fishtest](https://tests.stockfishchess.org/tests).
|
||||
If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test)
|
||||
first, where the basics of Stockfish development are explained.
|
||||
|
||||
|
||||
## Terms of use
|
||||
|
||||
Stockfish is free, and distributed under the **GNU General Public License version 3**
|
||||
(GPL v3). Essentially, this means you are free to do almost exactly
|
||||
what you want with the program, including distributing it among your
|
||||
friends, making it available for download from your website, selling
|
||||
it (either by itself or as part of some bigger software package), or
|
||||
using it as the starting point for a software project of your own.
|
||||
|
||||
The only real limitation is that whenever you distribute Stockfish in
|
||||
some way, you MUST always include the license and the full source code
|
||||
(or a pointer to where the source code can be found) to generate the
|
||||
exact binary you are distributing. If you make any changes to the
|
||||
source code, these changes must also be made available under the GPL v3.
|
||||
|
||||
For full details, read the copy of the GPL v3 found in the file named
|
||||
[*Copying.txt*](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt).
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
1. Introduction
|
||||
---------------
|
||||
|
||||
Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
|
||||
not a complete chess program, but requires some UCI compatible GUI
|
||||
(like XBoard with PolyGlot, eboard, Jos�, Arena, Sigma Chess, Shredder,
|
||||
Chess Partner, or Fritz) in order to be used comfortably. Read the
|
||||
documentation for your GUI of choice for information about how to use
|
||||
Stockfish with your GUI.
|
||||
|
||||
This version of Stockfish supports up to 8 CPUs, but has not been
|
||||
tested thoroughly with more than 2. The program tries to detect the
|
||||
number of CPUs on your computer and set the number of search threads
|
||||
accordingly, but please be aware that the detection is not always
|
||||
correct. It is therefore recommended to inspect the value of the
|
||||
"Threads" UCI parameter, and to make sure it equals the number of CPU
|
||||
cores on your computer. If you are using more than four threads, it
|
||||
is recommended to raise the value of "Minimum Split Depth" UCI parameter
|
||||
to 6.
|
||||
|
||||
|
||||
2. Files
|
||||
--------
|
||||
|
||||
This distribution of Stockfish consists of the following files:
|
||||
|
||||
* Readme.txt, the file you are currently reading.
|
||||
|
||||
* Copying.txt, a text file containing the GNU General Public
|
||||
License.
|
||||
|
||||
* src/, a subdirectory containing the full source code, including a
|
||||
Makefile that can be used to compile Stockfish on Unix-like
|
||||
systems. For further information about how to compile Stockfish
|
||||
yourself, read section 4 below.
|
||||
|
||||
* polyglot.ini, for using Stockfish with Fabien Letouzey's PolyGlot
|
||||
adapter.
|
||||
|
||||
|
||||
3. Opening books
|
||||
----------------
|
||||
|
||||
This version of Stockfish has experimental support for PolyGlot opening
|
||||
books. For information about how to create such books, consult the
|
||||
PolyGlot documentation. The book file can be selected by setting the
|
||||
UCI parameter "Book File".
|
||||
|
||||
|
||||
4. Compiling it yourself
|
||||
------------------------
|
||||
|
||||
On Unix-like systems, it should usually be possible to compile
|
||||
Stockfish directly from the source code with the included Makefile.
|
||||
|
||||
For big-endian machines like Power PC you need to enable the proper
|
||||
flag changing from -DNBIGENDIAN to -DBIGENDIAN in the Makefile.
|
||||
|
||||
Stockfish has POPCNT instruction runtime detection and support. This can
|
||||
give an extra speed on Core i7 or similar systems. To enable this feature
|
||||
compile with 'make icc-profile-popcnt'
|
||||
|
||||
On 64 bit Unix-like systems the 'bsfq' assembly instruction will be used
|
||||
for bit counting. Detection is automatic at compile time, but in case you
|
||||
experience compile problems you can comment out #define USE_BSFQ line in types.h
|
||||
|
||||
|
||||
5. Terms of use
|
||||
---------------
|
||||
|
||||
Stockfish is free, and distributed under the GNU General Public License
|
||||
(GPL). Essentially, this means that you are free to do almost exactly
|
||||
what you want with the program, including distributing it among your
|
||||
friends, making it available for download from your web site, selling
|
||||
it (either by itself or as part of some bigger software package), or
|
||||
using it as the starting point for a software project of your own.
|
||||
|
||||
The only real limitation is that whenever you distribute Stockfish in
|
||||
some way, you must always include the full source code, or a pointer
|
||||
to where the source code can be found. If you make any changes to the
|
||||
source code, these changes must also be made available under the GPL.
|
||||
|
||||
For full details, read the copy of the GPL found in the file named
|
||||
Copying.txt.
|
||||
|
||||
|
||||
6. Feedback
|
||||
-----------
|
||||
|
||||
The author's e-mail address is mcostalba@gmail.com
|
||||
@@ -1,235 +0,0 @@
|
||||
Contributors to Fishtest with >10,000 CPU hours, as of 2022-04-14.
|
||||
Thank you!
|
||||
|
||||
Username CPU Hours Games played
|
||||
------------------------------------------------------------------
|
||||
noobpwnftw 31714850 2267266129
|
||||
mlang 2954099 198421098
|
||||
technologov 2324150 102449398
|
||||
dew 1670874 99276012
|
||||
grandphish2 1134273 68070459
|
||||
okrout 901194 77738874
|
||||
TueRens 821388 50207666
|
||||
tvijlbrief 795993 51894442
|
||||
pemo 744463 32486677
|
||||
JojoM 724378 43660674
|
||||
mibere 703840 46867607
|
||||
linrock 626939 17408017
|
||||
gvreuls 534079 34352532
|
||||
cw 507221 34006775
|
||||
fastgm 489749 29344518
|
||||
crunchy 427035 27344275
|
||||
CSU_Dynasty 424643 28525220
|
||||
ctoks 415771 27364603
|
||||
oz 369200 27017658
|
||||
bcross 342642 23671289
|
||||
Fisherman 327231 21829379
|
||||
velislav 325670 20911076
|
||||
leszek 321295 19874113
|
||||
Dantist 274747 16910258
|
||||
mgrabiak 237604 15418700
|
||||
robal 217959 13840386
|
||||
glinscott 217799 13780820
|
||||
nordlandia 211692 13484886
|
||||
drabel 201967 13798360
|
||||
bking_US 198894 11876016
|
||||
mhoram 194862 12261809
|
||||
Thanar 179852 12365359
|
||||
vdv 175544 9904472
|
||||
spams 157128 10319326
|
||||
rpngn 154081 9652139
|
||||
marrco 150300 9402229
|
||||
sqrt2 147963 9724586
|
||||
vdbergh 137430 8955097
|
||||
CoffeeOne 137100 5024116
|
||||
malala 136182 8002293
|
||||
xoto 133759 9159372
|
||||
davar 125240 8117121
|
||||
dsmith 122059 7570238
|
||||
amicic 119659 7937885
|
||||
Data 113305 8220352
|
||||
BrunoBanani 112960 7436849
|
||||
CypressChess 108321 7759588
|
||||
DesolatedDodo 106811 6776980
|
||||
MaZePallas 102823 6633619
|
||||
sterni1971 100532 5880772
|
||||
sunu 100167 7040199
|
||||
ElbertoOne 99028 7023771
|
||||
skiminki 98123 6478402
|
||||
brabos 92118 6186135
|
||||
cuistot 90358 5351004
|
||||
psk 89957 5984901
|
||||
racerschmacer 85712 6119648
|
||||
Vizvezdenec 83761 5344740
|
||||
zeryl 83680 5250995
|
||||
sschnee 83003 4840890
|
||||
0x3C33 82614 5271253
|
||||
BRAVONE 81239 5054681
|
||||
nssy 76497 5259388
|
||||
teddybaer 75125 5407666
|
||||
jromang 74796 5175825
|
||||
Pking_cda 73776 5293873
|
||||
Calis007 72477 4088576
|
||||
solarlight 70517 5028306
|
||||
dv8silencer 70287 3883992
|
||||
Bobo1239 68515 4652287
|
||||
manap 66273 4121774
|
||||
yurikvelo 65716 4457300
|
||||
tinker 64333 4268790
|
||||
Wolfgang 62644 3817410
|
||||
qurashee 61208 3429862
|
||||
robnjr 57262 4053117
|
||||
Freja 56938 3733019
|
||||
ttruscott 56010 3680085
|
||||
rkl 55132 4164467
|
||||
renouve 53811 3501516
|
||||
megaman7de 52434 3243016
|
||||
MaxKlaxxMiner 51977 3153032
|
||||
finfish 51360 3370515
|
||||
eva42 51272 3599691
|
||||
eastorwest 51058 3451555
|
||||
rap 49985 3219146
|
||||
pb00067 49727 3298270
|
||||
Spprtr 48920 3161711
|
||||
bigpen0r 47667 3336927
|
||||
ronaldjerum 47654 3240695
|
||||
biffhero 46564 3111352
|
||||
Fifis 45843 3088497
|
||||
VoyagerOne 45476 3452465
|
||||
speedycpu 43842 3003273
|
||||
jbwiebe 43305 2805433
|
||||
Antihistamine 41788 2761312
|
||||
mhunt 41735 2691355
|
||||
homyur 39893 2850481
|
||||
gri 39871 2515779
|
||||
armo9494 39064 2832326
|
||||
oryx 38867 2976992
|
||||
SC 37299 2731694
|
||||
Garf 37213 2986270
|
||||
tolkki963 37059 2154330
|
||||
csnodgrass 36207 2688994
|
||||
jmdana 36157 2210661
|
||||
strelock 34716 2074055
|
||||
DMBK 34010 2482916
|
||||
EthanOConnor 33370 2090311
|
||||
slakovv 32915 2021889
|
||||
gopeto 30993 2028106
|
||||
manapbk 30987 1810399
|
||||
Prcuvu 30377 2170122
|
||||
anst 30301 2190091
|
||||
jkiiski 30136 1904470
|
||||
hyperbolic.tom 29840 2017394
|
||||
chuckstablers 29659 2093438
|
||||
Pyafue 29650 1902349
|
||||
ncfish1 29105 1704011
|
||||
belzedar94 27935 1789106
|
||||
OuaisBla 27636 1578800
|
||||
chriswk 26902 1868317
|
||||
achambord 26582 1767323
|
||||
Patrick_G 26276 1801617
|
||||
yorkman 26193 1992080
|
||||
SFTUser 25182 1675689
|
||||
nabildanial 24942 1519409
|
||||
Sharaf_DG 24765 1786697
|
||||
rodneyc 24275 1410450
|
||||
agg177 23890 1395014
|
||||
JanErik 23408 1703875
|
||||
Isidor 23388 1680691
|
||||
Norabor 23339 1602636
|
||||
Ente 23270 1651432
|
||||
cisco2015 22897 1762669
|
||||
MarcusTullius 22688 1274821
|
||||
Zirie 22542 1472937
|
||||
team-oh 22272 1636708
|
||||
MazeOfGalious 21978 1629593
|
||||
sg4032 21947 1643265
|
||||
ianh2105 21725 1632562
|
||||
xor12 21628 1680365
|
||||
dex 21612 1467203
|
||||
nesoneg 21494 1463031
|
||||
Roady 21323 1433822
|
||||
sphinx 21211 1384728
|
||||
user213718 21196 1397710
|
||||
spcc 21065 1311338
|
||||
jjoshua2 21001 1423089
|
||||
horst.prack 20878 1465656
|
||||
0xB00B1ES 20590 1208666
|
||||
j3corre 20405 941444
|
||||
kdave 20364 1389254
|
||||
Adrian.Schmidt123 20316 1281436
|
||||
Ulysses 20217 1351500
|
||||
markkulix 19976 1115258
|
||||
wei 19973 1745989
|
||||
rstoesser 19569 1293588
|
||||
eudhan 19274 1283717
|
||||
fishtester 18995 1238686
|
||||
vulcan 18871 1729392
|
||||
jundery 18445 1115855
|
||||
iisiraider 18247 1101015
|
||||
ville 17883 1384026
|
||||
chris 17698 1487385
|
||||
purplefishies 17595 1092533
|
||||
dju 17353 978595
|
||||
Wencey 17125 805964
|
||||
DragonLord 17014 1162790
|
||||
thirdlife 16996 447356
|
||||
IgorLeMasson 16064 1147232
|
||||
ako027ako 15671 1173203
|
||||
AndreasKrug 15550 1194497
|
||||
Nikolay.IT 15154 1068349
|
||||
Andrew Grant 15114 895539
|
||||
scuzzi 14928 953313
|
||||
OssumOpossum 14857 1007129
|
||||
Karby 14808 867120
|
||||
jsys14 14652 855642
|
||||
enedene 14476 905279
|
||||
bpfliegel 14298 884523
|
||||
mpx86 14019 759568
|
||||
jpulman 13982 870599
|
||||
crocogoat 13803 1117422
|
||||
joster 13794 950160
|
||||
Nesa92 13786 1114691
|
||||
mbeier 13650 1044928
|
||||
Hjax 13535 915487
|
||||
Dark_wizzie 13422 1007152
|
||||
Jopo12321 13367 678852
|
||||
Rudolphous 13244 883140
|
||||
Machariel 13010 863104
|
||||
mabichito 12903 749391
|
||||
thijsk 12886 722107
|
||||
AdrianSA 12860 804972
|
||||
infinigon 12807 937332
|
||||
Flopzee 12698 894821
|
||||
fatmurphy 12547 853210
|
||||
SapphireBrand 12416 969604
|
||||
modolief 12386 896470
|
||||
Farseer 12249 694108
|
||||
pgontarz 12151 848794
|
||||
pirt 12008 923149
|
||||
stocky 11954 699440
|
||||
mschmidt 11941 803401
|
||||
dbernier 11609 818636
|
||||
Maxim 11543 836024
|
||||
infinity 11470 727027
|
||||
aga 11409 695071
|
||||
torbjo 11395 729145
|
||||
Thomas A. Anderson 11372 732094
|
||||
savage84 11358 670860
|
||||
FormazChar 11349 850327
|
||||
d64 11263 789184
|
||||
MooTheCow 11237 720174
|
||||
snicolet 11106 869170
|
||||
ali-al-zhrani 11098 768494
|
||||
whelanh 11067 235676
|
||||
Jackfish 10978 720078
|
||||
deflectooor 10886 520116
|
||||
basepi 10637 744851
|
||||
Cubox 10621 826448
|
||||
michaelrpg 10509 739239
|
||||
OIVAS7572 10420 995586
|
||||
dzjp 10343 732529
|
||||
Garruk 10334 704065
|
||||
ols 10259 570669
|
||||
lbraesch 10252 647825
|
||||
qoo_charly_cai 10212 620407
|
||||
Naven94 10069 503192
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
[PolyGlot]
|
||||
|
||||
EngineDir = .
|
||||
EngineCommand = ./stockfish
|
||||
|
||||
Book = false
|
||||
BookFile = book.bin
|
||||
|
||||
Log = true
|
||||
LogFile = stockfish.log
|
||||
|
||||
Resign = true
|
||||
ResignScore = 600
|
||||
|
||||
[Engine]
|
||||
|
||||
Hash = 128
|
||||
Threads = 1
|
||||
OwnBook = false
|
||||
Book File = book.bin
|
||||
Use Search Log = false
|
||||
Mobility (Middle Game) = 100
|
||||
Mobility (Endgame) = 100
|
||||
Pawn Structure (Middle Game) = 100
|
||||
Pawn Structure (Endgame) = 100
|
||||
Passed Pawns (Middle Game) = 100
|
||||
Passed Pawns (Endgame) = 100
|
||||
Aggressiveness = 100
|
||||
Cowardice = 100
|
||||
King Safety Curve = Quadratic
|
||||
Quadratic = Linear
|
||||
King Safety Coefficient = 40
|
||||
King Safety X Intercept = 0
|
||||
King Safety Max Slope = 30
|
||||
King Safety Max Value = 500
|
||||
Queen Contact Check Bonus = 3
|
||||
Queen Check Bonus = 2
|
||||
Rook Check Bonus = 1
|
||||
Bishop Check Bonus = 1
|
||||
Knight Check Bonus = 1
|
||||
Discovered Check Bonus = 3
|
||||
Mate Threat Bonus = 3
|
||||
Check Extension (PV nodes) = 2
|
||||
Check Extension (non-PV nodes) = 1
|
||||
Single Reply Extension (PV nodes) = 2
|
||||
Single Reply Extension (non-PV nodes) = 2
|
||||
Mate Threat Extension (PV nodes) = 0
|
||||
Mate Threat Extension (non-PV nodes) = 0
|
||||
Pawn Push to 7th Extension (PV nodes) = 1
|
||||
Pawn Push to 7th Extension (non-PV nodes) = 1
|
||||
Passed Pawn Extension (PV nodes) = 1
|
||||
Passed Pawn Extension (non-PV nodes) = 0
|
||||
Pawn Endgame Extension (PV nodes) = 2
|
||||
Pawn Endgame Extension (non-PV nodes) = 2
|
||||
Full Depth Moves (PV nodes) = 14
|
||||
Full Depth Moves (non-PV nodes) = 3
|
||||
Threat Depth = 5
|
||||
Futility Pruning (Main Search) = true
|
||||
Futility Pruning (Quiescence Search) = true
|
||||
Randomness = 0
|
||||
Minimum Split Depth = 4
|
||||
Maximum Number of Threads per Split Point = 5
|
||||
+674
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
+261
-915
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "direction.h"
|
||||
#include "endgame.h"
|
||||
#include "evaluate.h"
|
||||
#include "material.h"
|
||||
#include "mersenne.h"
|
||||
#include "misc.h"
|
||||
#include "movepick.h"
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "ucioption.h"
|
||||
|
||||
|
||||
/// Application class is in charge of initializing global resources
|
||||
/// at startup and cleanly releases them when program terminates.
|
||||
|
||||
Application::Application() {
|
||||
|
||||
init_mersenne();
|
||||
init_direction_table();
|
||||
init_bitboards();
|
||||
init_uci_options();
|
||||
Position::init_zobrist();
|
||||
Position::init_piece_square_tables();
|
||||
init_eval(1);
|
||||
init_bitbases();
|
||||
init_threads();
|
||||
|
||||
// Make random number generation less deterministic, for book moves
|
||||
for (int i = abs(get_system_time() % 10000); i > 0; i--)
|
||||
genrand_int32();
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
|
||||
stop_threads();
|
||||
quit_eval();
|
||||
}
|
||||
|
||||
void Application::initialize() {
|
||||
|
||||
// A static Application object is allocated
|
||||
// once only when this function is called.
|
||||
static Application singleton;
|
||||
}
|
||||
|
||||
void Application::exit_with_failure() {
|
||||
|
||||
exit(EXIT_FAILURE); // d'tor will be called automatically
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,22 +17,23 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Class for difference calculation of NNUE evaluation function
|
||||
|
||||
#ifndef NNUE_ACCUMULATOR_H_INCLUDED
|
||||
#define NNUE_ACCUMULATOR_H_INCLUDED
|
||||
#if !defined(APPLICATION_H_INCLUDED)
|
||||
#define APPLICATION_H_INCLUDED
|
||||
|
||||
#include "nnue_architecture.h"
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
/// Singleton class used to housekeep memory and global resources
|
||||
/// so to be sure we always leave in a clean state.
|
||||
|
||||
// Class that holds the result of affine transformation of input features
|
||||
struct alignas(CacheLineSize) Accumulator {
|
||||
std::int16_t accumulation[2][TransformedFeatureDimensions];
|
||||
std::int32_t psqtAccumulation[2][PSQTBuckets];
|
||||
bool computed[2];
|
||||
};
|
||||
class Application {
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
Application();
|
||||
Application(const Application&);
|
||||
~Application();
|
||||
|
||||
#endif // NNUE_ACCUMULATOR_H_INCLUDED
|
||||
public:
|
||||
static void initialize();
|
||||
static void exit_with_failure();
|
||||
};
|
||||
|
||||
#endif // !defined(APPLICATION_H_INCLUDED)
|
||||
+128
-122
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,25 +17,32 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "position.h"
|
||||
#include "benchmark.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "ucioption.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
////
|
||||
//// Variables
|
||||
////
|
||||
|
||||
const vector<string> Defaults = {
|
||||
"setoption name UCI_Chess960 value false",
|
||||
const string BenchmarkPositions[] = {
|
||||
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
|
||||
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11",
|
||||
"r4rk1/1b2qppp/p1n1p3/1p6/1b1PN3/3BRN2/PP3PPP/R2Q2K1 b - - 7 16",
|
||||
"4r1k1/ppq3pp/3b4/2pP4/2Q1p3/4B1P1/PP5P/R5K1 b - - 0 20",
|
||||
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
|
||||
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14 moves d4e6",
|
||||
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14 moves g2g4",
|
||||
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14",
|
||||
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14",
|
||||
"r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15",
|
||||
"r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13",
|
||||
"r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16",
|
||||
@@ -43,133 +51,131 @@ const vector<string> Defaults = {
|
||||
"r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16",
|
||||
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
|
||||
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
|
||||
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22",
|
||||
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26",
|
||||
"6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1",
|
||||
"3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1",
|
||||
"2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1 moves g5g6 f3e3 g6g5 e3f3",
|
||||
"8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1",
|
||||
"7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1",
|
||||
"8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1",
|
||||
"8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1",
|
||||
"8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1",
|
||||
"8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1",
|
||||
"5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1",
|
||||
"6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1",
|
||||
"1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
|
||||
"6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1",
|
||||
"8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1",
|
||||
"5rk1/q6p/2p3bR/1pPp1rP1/1P1Pp3/P3B1Q1/1K3P2/R7 w - - 93 90",
|
||||
"4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21",
|
||||
"r3k2r/3nnpbp/q2pp1p1/p7/Pp1PPPP1/4BNN1/1P5P/R2Q1RK1 w kq - 0 16",
|
||||
"3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40",
|
||||
"4k3/3q1r2/1N2r1b1/3ppN2/2nPP3/1B1R2n1/2R1Q3/3K4 w - - 5 1",
|
||||
|
||||
// 5-man positions
|
||||
"8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
|
||||
"8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate
|
||||
"8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw
|
||||
|
||||
// 6-man positions
|
||||
"8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate
|
||||
"8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate
|
||||
"8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw
|
||||
|
||||
// 7-man positions
|
||||
"8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw
|
||||
|
||||
// Mate and stalemate positions
|
||||
"6k1/3b3r/1p1p4/p1n2p2/1PPNpP1q/P3Q1p1/1R1RB1P1/5K2 b - - 0 1",
|
||||
"r2r1n2/pp2bk2/2p1p2p/3q4/3PN1QP/2P3R1/P4PP1/5RK1 w - - 0 1",
|
||||
"8/8/8/8/8/6k1/6p1/6K1 w - -",
|
||||
"7k/7P/6K1/8/3B4/8/8/8 b - -",
|
||||
|
||||
// Chess 960
|
||||
"setoption name UCI_Chess960 value true",
|
||||
"bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w HFhf - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6",
|
||||
"nqbnrkrb/pppppppp/8/8/8/8/PPPPPPPP/NQBNRKRB w KQkq - 0 1",
|
||||
"setoption name UCI_Chess960 value false"
|
||||
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - 3 22",
|
||||
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26"
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Stockfish {
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
/// setup_bench() builds a list of UCI commands to be run by bench. There
|
||||
/// are five parameters: TT size in MB, number of search threads that
|
||||
/// should be used, the limit value spent for each position, a file name
|
||||
/// where to look for positions in FEN format, the type of the limit:
|
||||
/// depth, perft, nodes and movetime (in millisecs), and evaluation type
|
||||
/// mixed (default), classical, NNUE.
|
||||
///
|
||||
/// bench -> search default positions up to depth 13
|
||||
/// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB)
|
||||
/// bench 64 4 5000 current movetime -> search current position with 4 threads for 5 sec
|
||||
/// bench 64 1 100000 default nodes -> search default positions for 100K nodes each
|
||||
/// bench 16 1 5 default perft -> run a perft 5 on default positions
|
||||
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set
|
||||
/// of positions for a given time each. There are four parameters; the
|
||||
/// transposition table size, the number of search threads that should
|
||||
/// be used, the time in seconds spent for each position (optional, default
|
||||
/// is 60) and an optional file name where to look for positions in fen
|
||||
/// format (default are the BenchmarkPositions defined above).
|
||||
/// The analysis is written to a file named bench.txt.
|
||||
|
||||
vector<string> setup_bench(const Position& current, istream& is) {
|
||||
void benchmark(const string& commandLine) {
|
||||
|
||||
vector<string> fens, list;
|
||||
string go, token;
|
||||
istringstream csVal(commandLine);
|
||||
istringstream csStr(commandLine);
|
||||
string ttSize, threads, fileName, limitType, timFile;
|
||||
int val, secsPerPos, maxDepth, maxNodes;
|
||||
|
||||
// Assign default values to missing arguments
|
||||
string ttSize = (is >> token) ? token : "16";
|
||||
string threads = (is >> token) ? token : "1";
|
||||
string limit = (is >> token) ? token : "13";
|
||||
string fenFile = (is >> token) ? token : "default";
|
||||
string limitType = (is >> token) ? token : "depth";
|
||||
string evalType = (is >> token) ? token : "mixed";
|
||||
|
||||
go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit;
|
||||
|
||||
if (fenFile == "default")
|
||||
fens = Defaults;
|
||||
|
||||
else if (fenFile == "current")
|
||||
fens.push_back(current.fen());
|
||||
|
||||
else
|
||||
csStr >> ttSize;
|
||||
csVal >> val;
|
||||
if (val < 4 || val > 1024)
|
||||
{
|
||||
string fen;
|
||||
ifstream file(fenFile);
|
||||
cerr << "The hash table size must be between 4 and 1024" << endl;
|
||||
Application::exit_with_failure();
|
||||
}
|
||||
csStr >> threads;
|
||||
csVal >> val;
|
||||
if (val < 1 || val > THREAD_MAX)
|
||||
{
|
||||
cerr << "The number of threads must be between 1 and " << THREAD_MAX << endl;
|
||||
Application::exit_with_failure();
|
||||
}
|
||||
set_option_value("Hash", ttSize);
|
||||
set_option_value("Threads", threads);
|
||||
set_option_value("OwnBook", "false");
|
||||
set_option_value("Use Search Log", "true");
|
||||
set_option_value("Search Log Filename", "bench.txt");
|
||||
|
||||
if (!file.is_open())
|
||||
csVal >> val;
|
||||
csVal >> fileName;
|
||||
csVal >> limitType;
|
||||
csVal >> timFile;
|
||||
|
||||
secsPerPos = maxDepth = maxNodes = 0;
|
||||
|
||||
if (limitType == "time")
|
||||
secsPerPos = val * 1000;
|
||||
else if (limitType == "depth" || limitType == "perft")
|
||||
maxDepth = val;
|
||||
else
|
||||
maxNodes = val;
|
||||
|
||||
vector<string> positions;
|
||||
|
||||
if (fileName != "default")
|
||||
{
|
||||
ifstream fenFile(fileName.c_str());
|
||||
if (!fenFile.is_open())
|
||||
{
|
||||
cerr << "Unable to open file " << fenFile << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
cerr << "Unable to open positions file " << fileName << endl;
|
||||
Application::exit_with_failure();
|
||||
}
|
||||
string pos;
|
||||
while (fenFile.good())
|
||||
{
|
||||
getline(fenFile, pos);
|
||||
if (!pos.empty())
|
||||
positions.push_back(pos);
|
||||
}
|
||||
fenFile.close();
|
||||
} else
|
||||
for (int i = 0; i < 16; i++)
|
||||
positions.push_back(string(BenchmarkPositions[i]));
|
||||
|
||||
while (getline(file, fen))
|
||||
if (!fen.empty())
|
||||
fens.push_back(fen);
|
||||
|
||||
file.close();
|
||||
ofstream timingFile;
|
||||
if (!timFile.empty())
|
||||
{
|
||||
timingFile.open(timFile.c_str(), ios::out | ios::app);
|
||||
if (!timingFile.is_open())
|
||||
{
|
||||
cerr << "Unable to open timing file " << timFile << endl;
|
||||
Application::exit_with_failure();
|
||||
}
|
||||
}
|
||||
|
||||
list.emplace_back("setoption name Threads value " + threads);
|
||||
list.emplace_back("setoption name Hash value " + ttSize);
|
||||
list.emplace_back("ucinewgame");
|
||||
vector<string>::iterator it;
|
||||
int cnt = 1;
|
||||
int64_t totalNodes = 0;
|
||||
int startTime = get_system_time();
|
||||
|
||||
size_t posCounter = 0;
|
||||
for (it = positions.begin(); it != positions.end(); ++it, ++cnt)
|
||||
{
|
||||
Move moves[1] = {MOVE_NONE};
|
||||
int dummy[2] = {0, 0};
|
||||
Position pos(*it);
|
||||
cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl;
|
||||
if (limitType == "perft")
|
||||
totalNodes += perft(pos, maxDepth * OnePly);
|
||||
else if (!think(pos, false, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
|
||||
break;
|
||||
totalNodes += nodes_searched();
|
||||
}
|
||||
|
||||
for (const string& fen : fens)
|
||||
if (fen.find("setoption") != string::npos)
|
||||
list.emplace_back(fen);
|
||||
else
|
||||
{
|
||||
if (evalType == "classical" || (evalType == "mixed" && posCounter % 2 == 0))
|
||||
list.emplace_back("setoption name Use NNUE value false");
|
||||
else if (evalType == "NNUE" || (evalType == "mixed" && posCounter % 2 != 0))
|
||||
list.emplace_back("setoption name Use NNUE value true");
|
||||
list.emplace_back("position fen " + fen);
|
||||
list.emplace_back(go);
|
||||
++posCounter;
|
||||
}
|
||||
cnt = get_system_time() - startTime;
|
||||
cerr << "==============================="
|
||||
<< "\nTotal time (ms) : " << cnt
|
||||
<< "\nNodes searched : " << totalNodes
|
||||
<< "\nNodes/second : " << (int)(totalNodes/(cnt/1000.0)) << endl << endl;
|
||||
|
||||
list.emplace_back("setoption name Use NNUE value true");
|
||||
if (!timFile.empty())
|
||||
{
|
||||
timingFile << cnt << endl << endl;
|
||||
timingFile.close();
|
||||
}
|
||||
|
||||
return list;
|
||||
// Under MS Visual C++ debug window always unconditionally closes
|
||||
// when program exits, this is bad because we want to read results before.
|
||||
#if (defined(WINDOWS) || defined(WIN32) || defined(WIN64))
|
||||
cerr << "Press any key to exit" << endl;
|
||||
cin >> fileName;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(BENCHMARK_H_INCLUDED)
|
||||
#define BENCHMARK_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern void benchmark(const std::string& commandLine);
|
||||
|
||||
#endif // !defined(BENCHMARK_H_INCLUDED)
|
||||
+293
-115
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,157 +17,334 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <bitset>
|
||||
|
||||
#include "bitbase.h"
|
||||
#include "bitboard.h"
|
||||
#include "types.h"
|
||||
#include "move.h"
|
||||
#include "square.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
// There are 24 possible pawn squares: files A to D and ranks from 2 to 7.
|
||||
// Positions with the pawn on files E to H will be mirrored before probing.
|
||||
constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608
|
||||
|
||||
std::bitset<MAX_INDEX> KPKBitbase;
|
||||
|
||||
// A KPK bitbase index is an integer in [0, IndexMax] range
|
||||
//
|
||||
// Information is mapped in a way that minimizes the number of iterations:
|
||||
//
|
||||
// bit 0- 5: white king square (from SQ_A1 to SQ_H8)
|
||||
// bit 6-11: black king square (from SQ_A1 to SQ_H8)
|
||||
// bit 12: side to move (WHITE or BLACK)
|
||||
// bit 13-14: white pawn file (from FILE_A to FILE_D)
|
||||
// bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2)
|
||||
unsigned index(Color stm, Square bksq, Square wksq, Square psq) {
|
||||
return int(wksq) | (bksq << 6) | (stm << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15);
|
||||
}
|
||||
|
||||
enum Result {
|
||||
INVALID = 0,
|
||||
UNKNOWN = 1,
|
||||
DRAW = 2,
|
||||
WIN = 4
|
||||
RESULT_UNKNOWN,
|
||||
RESULT_INVALID,
|
||||
RESULT_WIN,
|
||||
RESULT_LOSS,
|
||||
RESULT_DRAW
|
||||
};
|
||||
|
||||
Result& operator|=(Result& r, Result v) { return r = Result(r | v); }
|
||||
|
||||
struct KPKPosition {
|
||||
KPKPosition() = default;
|
||||
explicit KPKPosition(unsigned idx);
|
||||
operator Result() const { return result; }
|
||||
Result classify(const std::vector<KPKPosition>& db);
|
||||
void from_index(int index);
|
||||
int to_index() const;
|
||||
bool is_legal() const;
|
||||
bool is_immediate_draw() const;
|
||||
bool is_immediate_win() const;
|
||||
Bitboard wk_attacks() const;
|
||||
Bitboard bk_attacks() const;
|
||||
Bitboard pawn_attacks() const;
|
||||
|
||||
Color stm;
|
||||
Square ksq[COLOR_NB], psq;
|
||||
Result result;
|
||||
Square whiteKingSquare, blackKingSquare, pawnSquare;
|
||||
Color sideToMove;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) {
|
||||
Result *Bitbase;
|
||||
const int IndexMax = 2*24*64*64;
|
||||
int UnknownCount = 0;
|
||||
|
||||
assert(file_of(wpsq) <= FILE_D);
|
||||
void initialize();
|
||||
bool next_iteration();
|
||||
Result classify_wtm(const KPKPosition &p);
|
||||
Result classify_btm(const KPKPosition &p);
|
||||
int compute_index(Square wksq, Square bksq, Square psq, Color stm);
|
||||
int compress_result(Result r);
|
||||
|
||||
return KPKBitbase[index(stm, bksq, wksq, wpsq)];
|
||||
}
|
||||
|
||||
|
||||
void Bitbases::init() {
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
std::vector<KPKPosition> db(MAX_INDEX);
|
||||
unsigned idx, repeat = 1;
|
||||
void generate_kpk_bitbase(uint8_t bitbase[]) {
|
||||
// Allocate array and initialize:
|
||||
Bitbase = new Result[IndexMax];
|
||||
initialize();
|
||||
|
||||
// Initialize db with known win / draw positions
|
||||
for (idx = 0; idx < MAX_INDEX; ++idx)
|
||||
db[idx] = KPKPosition(idx);
|
||||
// Iterate until all positions are classified:
|
||||
while(next_iteration());
|
||||
|
||||
// Iterate through the positions until none of the unknown positions can be
|
||||
// changed to either wins or draws (15 cycles needed).
|
||||
while (repeat)
|
||||
for (repeat = idx = 0; idx < MAX_INDEX; ++idx)
|
||||
repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN);
|
||||
// Compress bitbase into the supplied parameter:
|
||||
int i, j, b;
|
||||
for(i = 0; i < 24576; i++) {
|
||||
for(b = 0, j = 0; j < 8; b |= (compress_result(Bitbase[8*i+j]) << j), j++);
|
||||
assert(b == int(uint8_t(b)));
|
||||
bitbase[i] = (uint8_t)b;
|
||||
}
|
||||
|
||||
// Fill the bitbase with the decisive results
|
||||
for (idx = 0; idx < MAX_INDEX; ++idx)
|
||||
if (db[idx] == WIN)
|
||||
KPKBitbase.set(idx);
|
||||
// Release allocated memory:
|
||||
delete [] Bitbase;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
KPKPosition::KPKPosition(unsigned idx) {
|
||||
|
||||
ksq[WHITE] = Square((idx >> 0) & 0x3F);
|
||||
ksq[BLACK] = Square((idx >> 6) & 0x3F);
|
||||
stm = Color ((idx >> 12) & 0x01);
|
||||
psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7)));
|
||||
|
||||
// Invalid if two pieces are on the same square or if a king can be captured
|
||||
if ( distance(ksq[WHITE], ksq[BLACK]) <= 1
|
||||
|| ksq[WHITE] == psq
|
||||
|| ksq[BLACK] == psq
|
||||
|| (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK])))
|
||||
result = INVALID;
|
||||
|
||||
// Win if the pawn can be promoted without getting captured
|
||||
else if ( stm == WHITE
|
||||
&& rank_of(psq) == RANK_7
|
||||
&& ksq[WHITE] != psq + NORTH
|
||||
&& ( distance(ksq[BLACK], psq + NORTH) > 1
|
||||
|| (distance(ksq[WHITE], psq + NORTH) == 1)))
|
||||
result = WIN;
|
||||
|
||||
// Draw if it is stalemate or the black king can capture the pawn
|
||||
else if ( stm == BLACK
|
||||
&& ( !(attacks_bb<KING>(ksq[BLACK]) & ~(attacks_bb<KING>(ksq[WHITE]) | pawn_attacks_bb(WHITE, psq)))
|
||||
|| (attacks_bb<KING>(ksq[BLACK]) & ~attacks_bb<KING>(ksq[WHITE]) & psq)))
|
||||
result = DRAW;
|
||||
|
||||
// Position will be classified later
|
||||
else
|
||||
result = UNKNOWN;
|
||||
void KPKPosition::from_index(int index) {
|
||||
int s;
|
||||
sideToMove = Color(index % 2);
|
||||
blackKingSquare = Square((index / 2) % 64);
|
||||
whiteKingSquare = Square((index / 128) % 64);
|
||||
s = (index / 8192) % 24;
|
||||
pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1));
|
||||
}
|
||||
|
||||
Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
|
||||
|
||||
// White to move: If one move leads to a position classified as WIN, the result
|
||||
// of the current position is WIN. If all moves lead to positions classified
|
||||
// as DRAW, the current position is classified as DRAW, otherwise the current
|
||||
// position is classified as UNKNOWN.
|
||||
//
|
||||
// Black to move: If one move leads to a position classified as DRAW, the result
|
||||
// of the current position is DRAW. If all moves lead to positions classified
|
||||
// as WIN, the position is classified as WIN, otherwise the current position is
|
||||
// classified as UNKNOWN.
|
||||
const Result Good = (stm == WHITE ? WIN : DRAW);
|
||||
const Result Bad = (stm == WHITE ? DRAW : WIN);
|
||||
int KPKPosition::to_index() const {
|
||||
return compute_index(whiteKingSquare, blackKingSquare, pawnSquare,
|
||||
sideToMove);
|
||||
}
|
||||
|
||||
Result r = INVALID;
|
||||
Bitboard b = attacks_bb<KING>(ksq[stm]);
|
||||
|
||||
while (b)
|
||||
r |= stm == WHITE ? db[index(BLACK, ksq[BLACK], pop_lsb(b), psq)]
|
||||
: db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)];
|
||||
bool KPKPosition::is_legal() const {
|
||||
if(whiteKingSquare == pawnSquare || whiteKingSquare == blackKingSquare ||
|
||||
pawnSquare == blackKingSquare)
|
||||
return false;
|
||||
if(sideToMove == WHITE) {
|
||||
if(bit_is_set(this->wk_attacks(), blackKingSquare))
|
||||
return false;
|
||||
if(bit_is_set(this->pawn_attacks(), blackKingSquare))
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if(bit_is_set(this->bk_attacks(), whiteKingSquare))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (stm == WHITE)
|
||||
{
|
||||
if (rank_of(psq) < RANK_7) // Single push
|
||||
r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH)];
|
||||
|
||||
if ( rank_of(psq) == RANK_2 // Double push
|
||||
&& psq + NORTH != ksq[WHITE]
|
||||
&& psq + NORTH != ksq[BLACK])
|
||||
r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH + NORTH)];
|
||||
bool KPKPosition::is_immediate_draw() const {
|
||||
if(sideToMove == BLACK) {
|
||||
Bitboard wka = this->wk_attacks();
|
||||
Bitboard bka = this->bk_attacks();
|
||||
|
||||
// Case 1: Stalemate
|
||||
if((bka & ~(wka | this->pawn_attacks())) == EmptyBoardBB)
|
||||
return true;
|
||||
|
||||
// Case 2: King can capture pawn
|
||||
if(bit_is_set(bka, pawnSquare) && !bit_is_set(wka, pawnSquare))
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// Case 1: Stalemate
|
||||
if(whiteKingSquare == SQ_A8 && pawnSquare == SQ_A7 &&
|
||||
(blackKingSquare == SQ_C7 || blackKingSquare == SQ_C8))
|
||||
return true;
|
||||
}
|
||||
|
||||
return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace Stockfish
|
||||
bool KPKPosition::is_immediate_win() const {
|
||||
// The position is an immediate win if it is white to move and the white
|
||||
// pawn can be promoted without getting captured:
|
||||
return
|
||||
sideToMove == WHITE &&
|
||||
square_rank(pawnSquare) == RANK_7 &&
|
||||
(square_distance(blackKingSquare, pawnSquare+DELTA_N) > 1 ||
|
||||
bit_is_set(this->wk_attacks(), pawnSquare+DELTA_N));
|
||||
}
|
||||
|
||||
|
||||
Bitboard KPKPosition::wk_attacks() const {
|
||||
return StepAttackBB[WK][whiteKingSquare];
|
||||
}
|
||||
|
||||
|
||||
Bitboard KPKPosition::bk_attacks() const {
|
||||
return StepAttackBB[BK][blackKingSquare];
|
||||
}
|
||||
|
||||
|
||||
Bitboard KPKPosition::pawn_attacks() const {
|
||||
return StepAttackBB[WP][pawnSquare];
|
||||
}
|
||||
|
||||
|
||||
void initialize() {
|
||||
KPKPosition p;
|
||||
for(int i = 0; i < IndexMax; i++) {
|
||||
p.from_index(i);
|
||||
if(!p.is_legal())
|
||||
Bitbase[i] = RESULT_INVALID;
|
||||
else if(p.is_immediate_draw())
|
||||
Bitbase[i] = RESULT_DRAW;
|
||||
else if(p.is_immediate_win())
|
||||
Bitbase[i] = RESULT_WIN;
|
||||
else {
|
||||
Bitbase[i] = RESULT_UNKNOWN;
|
||||
UnknownCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool next_iteration() {
|
||||
KPKPosition p;
|
||||
int previousUnknownCount = UnknownCount;
|
||||
|
||||
for(int i = 0; i < IndexMax; i++)
|
||||
if(Bitbase[i] == RESULT_UNKNOWN) {
|
||||
p.from_index(i);
|
||||
|
||||
Bitbase[i] = (p.sideToMove == WHITE)? classify_wtm(p) : classify_btm(p);
|
||||
|
||||
if(Bitbase[i] == RESULT_WIN || Bitbase[i] == RESULT_LOSS ||
|
||||
Bitbase[i] == RESULT_DRAW)
|
||||
UnknownCount--;
|
||||
}
|
||||
|
||||
return UnknownCount != previousUnknownCount;
|
||||
}
|
||||
|
||||
|
||||
Result classify_wtm(const KPKPosition &p) {
|
||||
|
||||
// If one move leads to a position classified as RESULT_LOSS, the result
|
||||
// of the current position is RESULT_WIN. If all moves lead to positions
|
||||
// classified as RESULT_DRAW, the current position is classified as
|
||||
// RESULT_DRAW. Otherwise, the current position is classified as
|
||||
// RESULT_UNKNOWN.
|
||||
|
||||
bool unknownFound = false;
|
||||
Bitboard b;
|
||||
Square s;
|
||||
|
||||
// King moves
|
||||
b = p.wk_attacks();
|
||||
while(b) {
|
||||
s = pop_1st_bit(&b);
|
||||
switch(Bitbase[compute_index(s, p.blackKingSquare, p.pawnSquare,
|
||||
BLACK)]) {
|
||||
case RESULT_LOSS:
|
||||
return RESULT_WIN;
|
||||
|
||||
case RESULT_UNKNOWN:
|
||||
unknownFound = true;
|
||||
break;
|
||||
|
||||
case RESULT_DRAW: case RESULT_INVALID:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Pawn moves
|
||||
if(square_rank(p.pawnSquare) < RANK_7) {
|
||||
s = p.pawnSquare + DELTA_N;
|
||||
switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s,
|
||||
BLACK)]) {
|
||||
case RESULT_LOSS:
|
||||
return RESULT_WIN;
|
||||
|
||||
case RESULT_UNKNOWN:
|
||||
unknownFound = true;
|
||||
break;
|
||||
|
||||
case RESULT_DRAW: case RESULT_INVALID:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if(square_rank(s) == RANK_3 &&
|
||||
s != p.whiteKingSquare && s != p.blackKingSquare) {
|
||||
s += DELTA_N;
|
||||
switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s,
|
||||
BLACK)]) {
|
||||
case RESULT_LOSS:
|
||||
return RESULT_WIN;
|
||||
|
||||
case RESULT_UNKNOWN:
|
||||
unknownFound = true;
|
||||
break;
|
||||
|
||||
case RESULT_DRAW: case RESULT_INVALID:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return unknownFound? RESULT_UNKNOWN : RESULT_DRAW;
|
||||
}
|
||||
|
||||
|
||||
Result classify_btm(const KPKPosition &p) {
|
||||
|
||||
// If one move leads to a position classified as RESULT_DRAW, the result
|
||||
// of the current position is RESULT_DRAW. If all moves lead to positions
|
||||
// classified as RESULT_WIN, the current position is classified as
|
||||
// RESULT_LOSS. Otherwise, the current position is classified as
|
||||
// RESULT_UNKNOWN.
|
||||
|
||||
bool unknownFound = false;
|
||||
Bitboard b;
|
||||
Square s;
|
||||
|
||||
// King moves
|
||||
b = p.bk_attacks();
|
||||
while(b) {
|
||||
s = pop_1st_bit(&b);
|
||||
switch(Bitbase[compute_index(p.whiteKingSquare, s, p.pawnSquare,
|
||||
WHITE)]) {
|
||||
case RESULT_DRAW:
|
||||
return RESULT_DRAW;
|
||||
|
||||
case RESULT_UNKNOWN:
|
||||
unknownFound = true;
|
||||
break;
|
||||
|
||||
case RESULT_WIN: case RESULT_INVALID:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
return unknownFound? RESULT_UNKNOWN : RESULT_LOSS;
|
||||
}
|
||||
|
||||
|
||||
int compute_index(Square wksq, Square bksq, Square psq, Color stm) {
|
||||
int p = int(square_file(psq)) + (int(square_rank(psq)) - 1) * 4;
|
||||
int result = int(stm) + 2*int(bksq) + 128*int(wksq) + 8192*p;
|
||||
assert(result >= 0 && result < IndexMax);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int compress_result(Result r) {
|
||||
return (r == RESULT_WIN || r == RESULT_LOSS)? 1 : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-12
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -17,22 +18,21 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PSQT_H_INCLUDED
|
||||
#define PSQT_H_INCLUDED
|
||||
#if !defined(BITBASE_H_INCLUDED)
|
||||
#define BITBASE_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
namespace Stockfish::PSQT
|
||||
{
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern Score psq[PIECE_NB][SQUARE_NB];
|
||||
|
||||
// Fill psqt array from a set of internally linked parameters
|
||||
extern void init();
|
||||
|
||||
} // namespace Stockfish::PSQT
|
||||
extern void generate_kpk_bitbase(uint8_t bitbase[]);
|
||||
|
||||
|
||||
#endif // PSQT_H_INCLUDED
|
||||
#endif // !defined(BITBASE_H_INCLUDED)
|
||||
+482
-160
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,207 +17,528 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "misc.h"
|
||||
#include "bitcount.h"
|
||||
#include "direction.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
uint8_t PopCnt16[1 << 16];
|
||||
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||
#if defined(IS_64BIT)
|
||||
|
||||
Bitboard SquareBB[SQUARE_NB];
|
||||
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
||||
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||
const uint64_t BMult[64] = {
|
||||
0x440049104032280ULL, 0x1021023c82008040ULL, 0x404040082000048ULL,
|
||||
0x48c4440084048090ULL, 0x2801104026490000ULL, 0x4100880442040800ULL,
|
||||
0x181011002e06040ULL, 0x9101004104200e00ULL, 0x1240848848310401ULL,
|
||||
0x2000142828050024ULL, 0x1004024d5000ULL, 0x102044400800200ULL,
|
||||
0x8108108820112000ULL, 0xa880818210c00046ULL, 0x4008008801082000ULL,
|
||||
0x60882404049400ULL, 0x104402004240810ULL, 0xa002084250200ULL,
|
||||
0x100b0880801100ULL, 0x4080201220101ULL, 0x44008080a00000ULL,
|
||||
0x202200842000ULL, 0x5006004882d00808ULL, 0x200045080802ULL,
|
||||
0x86100020200601ULL, 0xa802080a20112c02ULL, 0x80411218080900ULL,
|
||||
0x200a0880080a0ULL, 0x9a01010000104000ULL, 0x28008003100080ULL,
|
||||
0x211021004480417ULL, 0x401004188220806ULL, 0x825051400c2006ULL,
|
||||
0x140c0210943000ULL, 0x242800300080ULL, 0xc2208120080200ULL,
|
||||
0x2430008200002200ULL, 0x1010100112008040ULL, 0x8141050100020842ULL,
|
||||
0x822081014405ULL, 0x800c049e40400804ULL, 0x4a0404028a000820ULL,
|
||||
0x22060201041200ULL, 0x360904200840801ULL, 0x881a08208800400ULL,
|
||||
0x60202c00400420ULL, 0x1204440086061400ULL, 0x8184042804040ULL,
|
||||
0x64040315300400ULL, 0xc01008801090a00ULL, 0x808010401140c00ULL,
|
||||
0x4004830c2020040ULL, 0x80005002020054ULL, 0x40000c14481a0490ULL,
|
||||
0x10500101042048ULL, 0x1010100200424000ULL, 0x640901901040ULL,
|
||||
0xa0201014840ULL, 0x840082aa011002ULL, 0x10010840084240aULL,
|
||||
0x420400810420608ULL, 0x8d40230408102100ULL, 0x4a00200612222409ULL,
|
||||
0xa08520292120600ULL
|
||||
};
|
||||
|
||||
Magic RookMagics[SQUARE_NB];
|
||||
Magic BishopMagics[SQUARE_NB];
|
||||
const uint64_t RMult[64] = {
|
||||
0xa8002c000108020ULL, 0x4440200140003000ULL, 0x8080200010011880ULL,
|
||||
0x380180080141000ULL, 0x1a00060008211044ULL, 0x410001000a0c0008ULL,
|
||||
0x9500060004008100ULL, 0x100024284a20700ULL, 0x802140008000ULL,
|
||||
0x80c01002a00840ULL, 0x402004282011020ULL, 0x9862000820420050ULL,
|
||||
0x1001448011100ULL, 0x6432800200800400ULL, 0x40100010002000cULL,
|
||||
0x2800d0010c080ULL, 0x90c0008000803042ULL, 0x4010004000200041ULL,
|
||||
0x3010010200040ULL, 0xa40828028001000ULL, 0x123010008000430ULL,
|
||||
0x24008004020080ULL, 0x60040001104802ULL, 0x582200028400d1ULL,
|
||||
0x4000802080044000ULL, 0x408208200420308ULL, 0x610038080102000ULL,
|
||||
0x3601000900100020ULL, 0x80080040180ULL, 0xc2020080040080ULL,
|
||||
0x80084400100102ULL, 0x4022408200014401ULL, 0x40052040800082ULL,
|
||||
0xb08200280804000ULL, 0x8a80a008801000ULL, 0x4000480080801000ULL,
|
||||
0x911808800801401ULL, 0x822a003002001894ULL, 0x401068091400108aULL,
|
||||
0x4a10a00004cULL, 0x2000800640008024ULL, 0x1486408102020020ULL,
|
||||
0x100a000d50041ULL, 0x810050020b0020ULL, 0x204000800808004ULL,
|
||||
0x20048100a000cULL, 0x112000831020004ULL, 0x9000040810002ULL,
|
||||
0x440490200208200ULL, 0x8910401000200040ULL, 0x6404200050008480ULL,
|
||||
0x4b824a2010010100ULL, 0x4080801810c0080ULL, 0x400802a0080ULL,
|
||||
0x8224080110026400ULL, 0x40002c4104088200ULL, 0x1002100104a0282ULL,
|
||||
0x1208400811048021ULL, 0x3201014a40d02001ULL, 0x5100019200501ULL,
|
||||
0x101000208001005ULL, 0x2008450080702ULL, 0x1002080301d00cULL,
|
||||
0x410201ce5c030092ULL
|
||||
};
|
||||
|
||||
const int BShift[64] = {
|
||||
58, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 59, 59,
|
||||
59, 59, 57, 57, 57, 57, 59, 59, 59, 59, 57, 55, 55, 57, 59, 59,
|
||||
59, 59, 57, 55, 55, 57, 59, 59, 59, 59, 57, 57, 57, 57, 59, 59,
|
||||
59, 59, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 58
|
||||
};
|
||||
|
||||
const int RShift[64] = {
|
||||
52, 53, 53, 53, 53, 53, 53, 52, 53, 54, 54, 54, 54, 54, 54, 53,
|
||||
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
|
||||
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
|
||||
53, 54, 54, 54, 54, 54, 54, 53, 52, 53, 53, 53, 53, 53, 53, 52
|
||||
};
|
||||
|
||||
#else // if !defined(IS_64BIT)
|
||||
|
||||
const uint64_t BMult[64] = {
|
||||
0x54142844c6a22981ULL, 0x710358a6ea25c19eULL, 0x704f746d63a4a8dcULL,
|
||||
0xbfed1a0b80f838c5ULL, 0x90561d5631e62110ULL, 0x2804260376e60944ULL,
|
||||
0x84a656409aa76871ULL, 0xf0267f64c28b6197ULL, 0x70764ebb762f0585ULL,
|
||||
0x92aa09e0cfe161deULL, 0x41ee1f6bb266f60eULL, 0xddcbf04f6039c444ULL,
|
||||
0x5a3fab7bac0d988aULL, 0xd3727877fa4eaa03ULL, 0xd988402d868ddaaeULL,
|
||||
0x812b291afa075c7cULL, 0x94faf987b685a932ULL, 0x3ed867d8470d08dbULL,
|
||||
0x92517660b8901de8ULL, 0x2d97e43e058814b4ULL, 0x880a10c220b25582ULL,
|
||||
0xc7c6520d1f1a0477ULL, 0xdbfc7fbcd7656aa6ULL, 0x78b1b9bfb1a2b84fULL,
|
||||
0x2f20037f112a0bc1ULL, 0x657171ea2269a916ULL, 0xc08302b07142210eULL,
|
||||
0x880a4403064080bULL, 0x3602420842208c00ULL, 0x852800dc7e0b6602ULL,
|
||||
0x595a3fbbaa0f03b2ULL, 0x9f01411558159d5eULL, 0x2b4a4a5f88b394f2ULL,
|
||||
0x4afcbffc292dd03aULL, 0x4a4094a3b3f10522ULL, 0xb06f00b491f30048ULL,
|
||||
0xd5b3820280d77004ULL, 0x8b2e01e7c8e57a75ULL, 0x2d342794e886c2e6ULL,
|
||||
0xc302c410cde21461ULL, 0x111f426f1379c274ULL, 0xe0569220abb31588ULL,
|
||||
0x5026d3064d453324ULL, 0xe2076040c343cd8aULL, 0x93efd1e1738021eeULL,
|
||||
0xb680804bed143132ULL, 0x44e361b21986944cULL, 0x44c60170ef5c598cULL,
|
||||
0xf4da475c195c9c94ULL, 0xa3afbb5f72060b1dULL, 0xbc75f410e41c4ffcULL,
|
||||
0xb51c099390520922ULL, 0x902c011f8f8ec368ULL, 0x950b56b3d6f5490aULL,
|
||||
0x3909e0635bf202d0ULL, 0x5744f90206ec10ccULL, 0xdc59fd76317abbc1ULL,
|
||||
0x881c7c67fcbfc4f6ULL, 0x47ca41e7e440d423ULL, 0xeb0c88112048d004ULL,
|
||||
0x51c60e04359aef1aULL, 0x1aa1fe0e957a5554ULL, 0xdd9448db4f5e3104ULL,
|
||||
0xdc01f6dca4bebbdcULL,
|
||||
};
|
||||
|
||||
const uint64_t RMult[64] = {
|
||||
0xd7445cdec88002c0ULL, 0xd0a505c1f2001722ULL, 0xe065d1c896002182ULL,
|
||||
0x9a8c41e75a000892ULL, 0x8900b10c89002aa8ULL, 0x9b28d1c1d60005a2ULL,
|
||||
0x15d6c88de002d9aULL, 0xb1dbfc802e8016a9ULL, 0x149a1042d9d60029ULL,
|
||||
0xb9c08050599e002fULL, 0x132208c3af300403ULL, 0xc1000ce2e9c50070ULL,
|
||||
0x9d9aa13c99020012ULL, 0xb6b078daf71e0046ULL, 0x9d880182fb6e002eULL,
|
||||
0x52889f467e850037ULL, 0xda6dc008d19a8480ULL, 0x468286034f902420ULL,
|
||||
0x7140ac09dc54c020ULL, 0xd76ffffa39548808ULL, 0xea901c4141500808ULL,
|
||||
0xc91004093f953a02ULL, 0x2882afa8f6bb402ULL, 0xaebe335692442c01ULL,
|
||||
0xe904a22079fb91eULL, 0x13a514851055f606ULL, 0x76c782018c8fe632ULL,
|
||||
0x1dc012a9d116da06ULL, 0x3c9e0037264fffa6ULL, 0x2036002853c6e4a2ULL,
|
||||
0xe3fe08500afb47d4ULL, 0xf38af25c86b025c2ULL, 0xc0800e2182cf9a40ULL,
|
||||
0x72002480d1f60673ULL, 0x2500200bae6e9b53ULL, 0xc60018c1eefca252ULL,
|
||||
0x600590473e3608aULL, 0x46002c4ab3fe51b2ULL, 0xa200011486bcc8d2ULL,
|
||||
0xb680078095784c63ULL, 0x2742002639bf11aeULL, 0xc7d60021a5bdb142ULL,
|
||||
0xc8c04016bb83d820ULL, 0xbd520028123b4842ULL, 0x9d1600344ac2a832ULL,
|
||||
0x6a808005631c8a05ULL, 0x604600a148d5389aULL, 0xe2e40103d40dea65ULL,
|
||||
0x945b5a0087c62a81ULL, 0x12dc200cd82d28eULL, 0x2431c600b5f9ef76ULL,
|
||||
0xfb142a006a9b314aULL, 0x6870e00a1c97d62ULL, 0x2a9db2004a2689a2ULL,
|
||||
0xd3594600caf5d1a2ULL, 0xee0e4900439344a7ULL, 0x89c4d266ca25007aULL,
|
||||
0x3e0013a2743f97e3ULL, 0x180e31a0431378aULL, 0x3a9e465a4d42a512ULL,
|
||||
0x98d0a11a0c0d9cc2ULL, 0x8e711c1aba19b01eULL, 0x8dcdc836dd201142ULL,
|
||||
0x5ac08a4735370479ULL,
|
||||
};
|
||||
|
||||
const int BShift[64] = {
|
||||
26, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||
27, 27, 25, 25, 25, 25, 27, 27, 27, 27, 25, 23, 23, 25, 27, 27,
|
||||
27, 27, 25, 23, 23, 25, 27, 27, 27, 27, 25, 25, 25, 25, 27, 27,
|
||||
27, 27, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 26
|
||||
};
|
||||
|
||||
const int RShift[64] = {
|
||||
20, 21, 21, 21, 21, 21, 21, 20, 21, 22, 22, 22, 22, 22, 22, 21,
|
||||
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
|
||||
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
|
||||
21, 22, 22, 22, 22, 22, 22, 21, 20, 21, 21, 21, 21, 21, 21, 20
|
||||
};
|
||||
|
||||
#endif // defined(IS_64BIT)
|
||||
|
||||
const Bitboard SquaresByColorBB[2] = { BlackSquaresBB, WhiteSquaresBB };
|
||||
|
||||
const Bitboard FileBB[8] = {
|
||||
FileABB, FileBBB, FileCBB, FileDBB, FileEBB, FileFBB, FileGBB, FileHBB
|
||||
};
|
||||
|
||||
const Bitboard NeighboringFilesBB[8] = {
|
||||
FileBBB, FileABB|FileCBB, FileBBB|FileDBB, FileCBB|FileEBB,
|
||||
FileDBB|FileFBB, FileEBB|FileGBB, FileFBB|FileHBB, FileGBB
|
||||
};
|
||||
|
||||
const Bitboard ThisAndNeighboringFilesBB[8] = {
|
||||
FileABB|FileBBB, FileABB|FileBBB|FileCBB,
|
||||
FileBBB|FileCBB|FileDBB, FileCBB|FileDBB|FileEBB,
|
||||
FileDBB|FileEBB|FileFBB, FileEBB|FileFBB|FileGBB,
|
||||
FileFBB|FileGBB|FileHBB, FileGBB|FileHBB
|
||||
};
|
||||
|
||||
const Bitboard RankBB[8] = {
|
||||
Rank1BB, Rank2BB, Rank3BB, Rank4BB, Rank5BB, Rank6BB, Rank7BB, Rank8BB
|
||||
};
|
||||
|
||||
const Bitboard RelativeRankBB[2][8] = {
|
||||
{ Rank1BB, Rank2BB, Rank3BB, Rank4BB, Rank5BB, Rank6BB, Rank7BB, Rank8BB },
|
||||
{ Rank8BB, Rank7BB, Rank6BB, Rank5BB, Rank4BB, Rank3BB, Rank2BB, Rank1BB }
|
||||
};
|
||||
|
||||
const Bitboard InFrontBB[2][8] = {
|
||||
{ Rank2BB | Rank3BB | Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
|
||||
Rank3BB | Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
|
||||
Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
|
||||
Rank5BB | Rank6BB | Rank7BB | Rank8BB,
|
||||
Rank6BB | Rank7BB | Rank8BB,
|
||||
Rank7BB | Rank8BB,
|
||||
Rank8BB,
|
||||
EmptyBoardBB
|
||||
},
|
||||
{ EmptyBoardBB,
|
||||
Rank1BB,
|
||||
Rank2BB | Rank1BB,
|
||||
Rank3BB | Rank2BB | Rank1BB,
|
||||
Rank4BB | Rank3BB | Rank2BB | Rank1BB,
|
||||
Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB,
|
||||
Rank6BB | Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB,
|
||||
Rank7BB | Rank6BB | Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB
|
||||
}
|
||||
};
|
||||
|
||||
Bitboard RMask[64];
|
||||
int RAttackIndex[64];
|
||||
Bitboard RAttacks[0x19000];
|
||||
|
||||
Bitboard BMask[64];
|
||||
int BAttackIndex[64];
|
||||
Bitboard BAttacks[0x1480];
|
||||
|
||||
Bitboard SetMaskBB[65];
|
||||
Bitboard ClearMaskBB[65];
|
||||
|
||||
Bitboard StepAttackBB[16][64];
|
||||
Bitboard RayBB[64][8];
|
||||
Bitboard BetweenBB[64][64];
|
||||
|
||||
Bitboard PassedPawnMask[2][64];
|
||||
Bitboard OutpostMask[2][64];
|
||||
|
||||
Bitboard BishopPseudoAttacks[64];
|
||||
Bitboard RookPseudoAttacks[64];
|
||||
Bitboard QueenPseudoAttacks[64];
|
||||
|
||||
uint8_t BitCount8Bit[256];
|
||||
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
Bitboard RookTable[0x19000]; // To store rook attacks
|
||||
Bitboard BishopTable[0x1480]; // To store bishop attacks
|
||||
|
||||
void init_magics(PieceType pt, Bitboard table[], Magic magics[]);
|
||||
|
||||
}
|
||||
|
||||
/// safe_destination() returns the bitboard of target square for the given step
|
||||
/// from the given square. If the step is off the board, returns empty bitboard.
|
||||
|
||||
inline Bitboard safe_destination(Square s, int step) {
|
||||
Square to = Square(s + step);
|
||||
return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
|
||||
void init_masks();
|
||||
void init_ray_bitboards();
|
||||
void init_attacks();
|
||||
void init_between_bitboards();
|
||||
Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
|
||||
int fmin, int fmax, int rmin, int rmax);
|
||||
Bitboard index_to_bitboard(int index, Bitboard mask);
|
||||
void init_sliding_attacks(Bitboard attacks[],
|
||||
int attackIndex[], Bitboard mask[],
|
||||
const int shift[2], const Bitboard mult[],
|
||||
int deltas[][2]);
|
||||
void init_pseudo_attacks();
|
||||
}
|
||||
|
||||
|
||||
/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable
|
||||
/// to be printed to standard output. Useful for debugging.
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
std::string Bitboards::pretty(Bitboard b) {
|
||||
/// print_bitboard() prints a bitboard in an easily readable format to the
|
||||
/// standard output. This is sometimes useful for debugging.
|
||||
|
||||
std::string s = "+---+---+---+---+---+---+---+---+\n";
|
||||
|
||||
for (Rank r = RANK_8; r >= RANK_1; --r)
|
||||
{
|
||||
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||
s += b & make_square(f, r) ? "| X " : "| ";
|
||||
|
||||
s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n";
|
||||
void print_bitboard(Bitboard b) {
|
||||
for(Rank r = RANK_8; r >= RANK_1; r--) {
|
||||
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
|
||||
for(File f = FILE_A; f <= FILE_H; f++)
|
||||
std::cout << "| " << (bit_is_set(b, make_square(f, r))? 'X' : ' ') << ' ';
|
||||
std::cout << "|" << std::endl;
|
||||
}
|
||||
s += " a b c d e f g h\n";
|
||||
|
||||
return s;
|
||||
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
/// Bitboards::init() initializes various bitboard tables. It is called at
|
||||
/// startup and relies on global objects to be already zero-initialized.
|
||||
/// init_bitboards() initializes various bitboard arrays. It is called during
|
||||
/// program initialization.
|
||||
|
||||
void Bitboards::init() {
|
||||
void init_bitboards() {
|
||||
int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
|
||||
int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
|
||||
init_masks();
|
||||
init_ray_bitboards();
|
||||
init_attacks();
|
||||
init_between_bitboards();
|
||||
init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas);
|
||||
init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift, BMult, bishopDeltas);
|
||||
init_pseudo_attacks();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < (1 << 16); ++i)
|
||||
PopCnt16[i] = uint8_t(std::bitset<16>(i).count());
|
||||
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
SquareBB[s] = (1ULL << s);
|
||||
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
|
||||
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
|
||||
/// nonzero bitboard.
|
||||
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
|
||||
#if defined(IS_64BIT) && !defined(USE_BSFQ)
|
||||
|
||||
init_magics(ROOK, RookTable, RookMagics);
|
||||
init_magics(BISHOP, BishopTable, BishopMagics);
|
||||
CACHE_LINE_ALIGNMENT
|
||||
static const int BitTable[64] = {
|
||||
0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34, 20, 40, 5, 17, 26, 38, 15,
|
||||
46, 29, 48, 10, 31, 35, 54, 21, 50, 41, 57, 63, 6, 12, 18, 24, 27, 33, 39,
|
||||
16, 37, 45, 47, 30, 53, 49, 56, 62, 11, 23, 32, 36, 44, 52, 55, 61, 22, 43,
|
||||
51, 60, 42, 59, 58
|
||||
};
|
||||
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||
{
|
||||
PawnAttacks[WHITE][s1] = pawn_attacks_bb<WHITE>(square_bb(s1));
|
||||
PawnAttacks[BLACK][s1] = pawn_attacks_bb<BLACK>(square_bb(s1));
|
||||
Square first_1(Bitboard b) {
|
||||
return Square(BitTable[((b & -b) * 0x218a392cd3d5dbfULL) >> 58]);
|
||||
}
|
||||
|
||||
for (int step : {-9, -8, -7, -1, 1, 7, 8, 9} )
|
||||
PseudoAttacks[KING][s1] |= safe_destination(s1, step);
|
||||
Square pop_1st_bit(Bitboard* b) {
|
||||
Bitboard bb = *b;
|
||||
*b &= (*b - 1);
|
||||
return Square(BitTable[((bb & -bb) * 0x218a392cd3d5dbfULL) >> 58]);
|
||||
}
|
||||
|
||||
for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} )
|
||||
PseudoAttacks[KNIGHT][s1] |= safe_destination(s1, step);
|
||||
#elif !defined(USE_BSFQ)
|
||||
|
||||
PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb<BISHOP>(s1, 0);
|
||||
PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0);
|
||||
static CACHE_LINE_ALIGNMENT
|
||||
const int BitTable[64] = {
|
||||
63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2,
|
||||
51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57, 0, 35, 62, 31, 40, 4, 49, 5, 52,
|
||||
26, 60, 6, 23, 44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38, 28,
|
||||
58, 20, 37, 17, 36, 8
|
||||
};
|
||||
|
||||
for (PieceType pt : { BISHOP, ROOK })
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||
{
|
||||
if (PseudoAttacks[pt][s1] & s2)
|
||||
{
|
||||
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
|
||||
BetweenBB[s1][s2] = (attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
|
||||
}
|
||||
BetweenBB[s1][s2] |= s2;
|
||||
}
|
||||
}
|
||||
Square first_1(Bitboard b) {
|
||||
b ^= (b - 1);
|
||||
uint32_t fold = int(b) ^ int(b >> 32);
|
||||
return Square(BitTable[(fold * 0x783a9b23) >> 26]);
|
||||
}
|
||||
|
||||
// Use type-punning
|
||||
union b_union {
|
||||
|
||||
Bitboard b;
|
||||
struct {
|
||||
#if defined (BIGENDIAN)
|
||||
uint32_t h;
|
||||
uint32_t l;
|
||||
#else
|
||||
uint32_t l;
|
||||
uint32_t h;
|
||||
#endif
|
||||
} dw;
|
||||
};
|
||||
|
||||
Square pop_1st_bit(Bitboard* bb) {
|
||||
|
||||
b_union u;
|
||||
Square ret;
|
||||
|
||||
u.b = *bb;
|
||||
|
||||
if (u.dw.l)
|
||||
{
|
||||
ret = Square(BitTable[((u.dw.l ^ (u.dw.l - 1)) * 0x783a9b23) >> 26]);
|
||||
u.dw.l &= (u.dw.l - 1);
|
||||
*bb = u.b;
|
||||
return ret;
|
||||
}
|
||||
ret = Square(BitTable[((~(u.dw.h ^ (u.dw.h - 1))) * 0x783a9b23) >> 26]);
|
||||
u.dw.h &= (u.dw.h - 1);
|
||||
*bb = u.b;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int bitScanReverse32(uint32_t b)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (b > 0xFFFF) {
|
||||
b >>= 16;
|
||||
result += 16;
|
||||
}
|
||||
if (b > 0xFF) {
|
||||
b >>= 8;
|
||||
result += 8;
|
||||
}
|
||||
if (b > 0xF) {
|
||||
b >>= 4;
|
||||
result += 4;
|
||||
}
|
||||
if (b > 0x3) {
|
||||
b >>= 2;
|
||||
result += 2;
|
||||
}
|
||||
return result + (b > 0) + (b > 1);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
|
||||
// All functions below are used to precompute various bitboards during
|
||||
// program initialization. Some of the functions may be difficult to
|
||||
// understand, but they all seem to work correctly, and it should never
|
||||
// be necessary to touch any of them.
|
||||
|
||||
Bitboard attacks = 0;
|
||||
Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
|
||||
Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
|
||||
|
||||
for (Direction d : (pt == ROOK ? RookDirections : BishopDirections))
|
||||
{
|
||||
Square s = sq;
|
||||
while (safe_destination(s, d) && !(occupied & s))
|
||||
attacks |= (s += d);
|
||||
void init_masks() {
|
||||
SetMaskBB[SQ_NONE] = 0ULL;
|
||||
ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE];
|
||||
for(Square s = SQ_A1; s <= SQ_H8; s++) {
|
||||
SetMaskBB[s] = (1ULL << s);
|
||||
ClearMaskBB[s] = ~SetMaskBB[s];
|
||||
}
|
||||
for(Color c = WHITE; c <= BLACK; c++)
|
||||
for(Square s = SQ_A1; s <= SQ_H8; s++) {
|
||||
PassedPawnMask[c][s] =
|
||||
in_front_bb(c, s) & this_and_neighboring_files_bb(s);
|
||||
OutpostMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
|
||||
}
|
||||
|
||||
return attacks;
|
||||
for (Bitboard b = 0ULL; b < 256ULL; b++)
|
||||
BitCount8Bit[b] = (uint8_t)count_1s(b);
|
||||
}
|
||||
|
||||
|
||||
// init_magics() computes all rook and bishop attacks at startup. Magic
|
||||
// bitboards are used to look up attacks of sliding pieces. As a reference see
|
||||
// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so
|
||||
// called "fancy" approach.
|
||||
void init_ray_bitboards() {
|
||||
int d[8] = {1, -1, 16, -16, 17, -17, 15, -15};
|
||||
for(int i = 0; i < 128; i = (i + 9) & ~8) {
|
||||
for(int j = 0; j < 8; j++) {
|
||||
RayBB[(i&7)|((i>>4)<<3)][j] = EmptyBoardBB;
|
||||
for(int k = i + d[j]; (k & 0x88) == 0; k += d[j])
|
||||
set_bit(&(RayBB[(i&7)|((i>>4)<<3)][j]), Square((k&7)|((k>>4)<<3)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_magics(PieceType pt, Bitboard table[], Magic magics[]) {
|
||||
|
||||
// Optimal PRNG seeds to pick the correct magics in the shortest time
|
||||
int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 },
|
||||
{ 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } };
|
||||
void init_attacks() {
|
||||
int i, j, k, l;
|
||||
int step[16][8] = {
|
||||
{0},
|
||||
{7,9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0},
|
||||
{9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}, {0}, {0},
|
||||
{-7,-9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0},
|
||||
{9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}
|
||||
};
|
||||
|
||||
Bitboard occupancy[4096], reference[4096], edges, b;
|
||||
int epoch[4096] = {}, cnt = 0, size = 0;
|
||||
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
{
|
||||
// Board edges are not considered in the relevant occupancies
|
||||
edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
|
||||
|
||||
// Given a square 's', the mask is the bitboard of sliding attacks from
|
||||
// 's' computed on an empty board. The index must be big enough to contain
|
||||
// all the attacks for each possible subset of the mask and so is 2 power
|
||||
// the number of 1s of the mask. Hence we deduce the size of the shift to
|
||||
// apply to the 64 or 32 bits word to get the index.
|
||||
Magic& m = magics[s];
|
||||
m.mask = sliding_attack(pt, s, 0) & ~edges;
|
||||
m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
|
||||
|
||||
// Set the offset for the attacks table of the square. We have individual
|
||||
// table sizes for each square with "Fancy Magic Bitboards".
|
||||
m.attacks = s == SQ_A1 ? table : magics[s - 1].attacks + size;
|
||||
|
||||
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
|
||||
// store the corresponding sliding attack bitboard in reference[].
|
||||
b = size = 0;
|
||||
do {
|
||||
occupancy[size] = b;
|
||||
reference[size] = sliding_attack(pt, s, b);
|
||||
|
||||
if (HasPext)
|
||||
m.attacks[pext(b, m.mask)] = reference[size];
|
||||
|
||||
size++;
|
||||
b = (b - m.mask) & m.mask;
|
||||
} while (b);
|
||||
|
||||
if (HasPext)
|
||||
continue;
|
||||
|
||||
PRNG rng(seeds[Is64Bit][rank_of(s)]);
|
||||
|
||||
// Find a magic for square 's' picking up an (almost) random number
|
||||
// until we find the one that passes the verification test.
|
||||
for (int i = 0; i < size; )
|
||||
{
|
||||
for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6; )
|
||||
m.magic = rng.sparse_rand<Bitboard>();
|
||||
|
||||
// A good magic must map every possible occupancy to an index that
|
||||
// looks up the correct sliding attack in the attacks[s] database.
|
||||
// Note that we build up the database for square 's' as a side
|
||||
// effect of verifying the magic. Keep track of the attempt count
|
||||
// and save it in epoch[], little speed-up trick to avoid resetting
|
||||
// m.attacks[] after every failed attempt.
|
||||
for (++cnt, i = 0; i < size; ++i)
|
||||
{
|
||||
unsigned idx = m.index(occupancy[i]);
|
||||
|
||||
if (epoch[idx] < cnt)
|
||||
{
|
||||
epoch[idx] = cnt;
|
||||
m.attacks[idx] = reference[i];
|
||||
}
|
||||
else if (m.attacks[idx] != reference[i])
|
||||
break;
|
||||
}
|
||||
for(i = 0; i < 64; i++) {
|
||||
for(j = 0; j <= int(BK); j++) {
|
||||
StepAttackBB[j][i] = EmptyBoardBB;
|
||||
for(k = 0; k < 8 && step[j][k] != 0; k++) {
|
||||
l = i + step[j][k];
|
||||
if(l >= 0 && l < 64 && abs((i&7) - (l&7)) < 3)
|
||||
StepAttackBB[j][i] |= (1ULL << l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
|
||||
int fmin=0, int fmax=7, int rmin=0, int rmax=7) {
|
||||
Bitboard result = 0ULL;
|
||||
int rk = sq / 8, fl = sq % 8, r, f, i;
|
||||
for(i = 0; i < dirs; i++) {
|
||||
int dx = deltas[i][0], dy = deltas[i][1];
|
||||
for(f = fl+dx, r = rk+dy;
|
||||
(dx==0 || (f>=fmin && f<=fmax)) && (dy==0 || (r>=rmin && r<=rmax));
|
||||
f += dx, r += dy) {
|
||||
result |= (1ULL << (f + r*8));
|
||||
if(block & (1ULL << (f + r*8))) break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void init_between_bitboards() {
|
||||
SquareDelta step[8] = {
|
||||
DELTA_E, DELTA_W, DELTA_N, DELTA_S, DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE
|
||||
};
|
||||
SignedDirection d;
|
||||
for(Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
|
||||
for(Square s2 = SQ_A1; s2 <= SQ_H8; s2++) {
|
||||
BetweenBB[s1][s2] = EmptyBoardBB;
|
||||
d = signed_direction_between_squares(s1, s2);
|
||||
if(d != SIGNED_DIR_NONE)
|
||||
for(Square s3 = s1 + step[d]; s3 != s2; s3 += step[d])
|
||||
set_bit(&(BetweenBB[s1][s2]), s3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Bitboard index_to_bitboard(int index, Bitboard mask) {
|
||||
int i, j, bits = count_1s(mask);
|
||||
Bitboard result = 0ULL;
|
||||
for(i = 0; i < bits; i++) {
|
||||
j = pop_1st_bit(&mask);
|
||||
if(index & (1 << i)) result |= (1ULL << j);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void init_sliding_attacks(Bitboard attacks[],
|
||||
int attackIndex[], Bitboard mask[],
|
||||
const int shift[2], const Bitboard mult[],
|
||||
int deltas[][2]) {
|
||||
int i, j, k, index = 0;
|
||||
Bitboard b;
|
||||
for(i = 0; i < 64; i++) {
|
||||
attackIndex[i] = index;
|
||||
mask[i] = sliding_attacks(i, 0ULL, 4, deltas, 1, 6, 1, 6);
|
||||
|
||||
#if defined(IS_64BIT)
|
||||
j = (1 << (64 - shift[i]));
|
||||
#else
|
||||
j = (1 << (32 - shift[i]));
|
||||
#endif
|
||||
|
||||
for(k = 0; k < j; k++) {
|
||||
|
||||
#if defined(IS_64BIT)
|
||||
b = index_to_bitboard(k, mask[i]);
|
||||
attacks[index + ((b * mult[i]) >> shift[i])] =
|
||||
sliding_attacks(i, b, 4, deltas);
|
||||
#else
|
||||
b = index_to_bitboard(k, mask[i]);
|
||||
attacks[index +
|
||||
(unsigned(int(b) * int(mult[i]) ^
|
||||
int(b >> 32) * int(mult[i] >> 32))
|
||||
>> shift[i])] =
|
||||
sliding_attacks(i, b, 4, deltas);
|
||||
#endif
|
||||
}
|
||||
index += j;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void init_pseudo_attacks() {
|
||||
Square s;
|
||||
for(s = SQ_A1; s <= SQ_H8; s++) {
|
||||
BishopPseudoAttacks[s] = bishop_attacks_bb(s, EmptyBoardBB);
|
||||
RookPseudoAttacks[s] = rook_attacks_bb(s, EmptyBoardBB);
|
||||
QueenPseudoAttacks[s] = queen_attacks_bb(s, EmptyBoardBB);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+236
-332
@@ -1,12 +1,14 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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
|
||||
@@ -16,436 +18,338 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BITBOARD_H_INCLUDED
|
||||
|
||||
#if !defined(BITBOARD_H_INCLUDED)
|
||||
#define BITBOARD_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "direction.h"
|
||||
#include "piece.h"
|
||||
#include "square.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace Bitbases {
|
||||
////
|
||||
//// Constants and variables
|
||||
////
|
||||
|
||||
void init();
|
||||
bool probe(Square wksq, Square wpsq, Square bksq, Color us);
|
||||
const Bitboard EmptyBoardBB = 0ULL;
|
||||
|
||||
} // namespace Stockfish::Bitbases
|
||||
const Bitboard WhiteSquaresBB = 0x55AA55AA55AA55AAULL;
|
||||
const Bitboard BlackSquaresBB = 0xAA55AA55AA55AA55ULL;
|
||||
|
||||
namespace Bitboards {
|
||||
const Bitboard FileABB = 0x0101010101010101ULL;
|
||||
const Bitboard FileBBB = 0x0202020202020202ULL;
|
||||
const Bitboard FileCBB = 0x0404040404040404ULL;
|
||||
const Bitboard FileDBB = 0x0808080808080808ULL;
|
||||
const Bitboard FileEBB = 0x1010101010101010ULL;
|
||||
const Bitboard FileFBB = 0x2020202020202020ULL;
|
||||
const Bitboard FileGBB = 0x4040404040404040ULL;
|
||||
const Bitboard FileHBB = 0x8080808080808080ULL;
|
||||
|
||||
void init();
|
||||
std::string pretty(Bitboard b);
|
||||
const Bitboard Rank1BB = 0xFFULL;
|
||||
const Bitboard Rank2BB = 0xFF00ULL;
|
||||
const Bitboard Rank3BB = 0xFF0000ULL;
|
||||
const Bitboard Rank4BB = 0xFF000000ULL;
|
||||
const Bitboard Rank5BB = 0xFF00000000ULL;
|
||||
const Bitboard Rank6BB = 0xFF0000000000ULL;
|
||||
const Bitboard Rank7BB = 0xFF000000000000ULL;
|
||||
const Bitboard Rank8BB = 0xFF00000000000000ULL;
|
||||
|
||||
} // namespace Stockfish::Bitboards
|
||||
extern const Bitboard SquaresByColorBB[2];
|
||||
extern const Bitboard FileBB[8];
|
||||
extern const Bitboard NeighboringFilesBB[8];
|
||||
extern const Bitboard ThisAndNeighboringFilesBB[8];
|
||||
extern const Bitboard RankBB[8];
|
||||
extern const Bitboard RelativeRankBB[2][8];
|
||||
extern const Bitboard InFrontBB[2][8];
|
||||
|
||||
constexpr Bitboard AllSquares = ~Bitboard(0);
|
||||
constexpr Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
|
||||
extern Bitboard SetMaskBB[65];
|
||||
extern Bitboard ClearMaskBB[65];
|
||||
|
||||
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;
|
||||
extern Bitboard StepAttackBB[16][64];
|
||||
extern Bitboard RayBB[64][8];
|
||||
extern Bitboard BetweenBB[64][64];
|
||||
|
||||
constexpr Bitboard Rank1BB = 0xFF;
|
||||
constexpr Bitboard Rank2BB = Rank1BB << (8 * 1);
|
||||
constexpr Bitboard Rank3BB = Rank1BB << (8 * 2);
|
||||
constexpr Bitboard Rank4BB = Rank1BB << (8 * 3);
|
||||
constexpr Bitboard Rank5BB = Rank1BB << (8 * 4);
|
||||
constexpr Bitboard Rank6BB = Rank1BB << (8 * 5);
|
||||
constexpr Bitboard Rank7BB = Rank1BB << (8 * 6);
|
||||
constexpr Bitboard Rank8BB = Rank1BB << (8 * 7);
|
||||
extern Bitboard PassedPawnMask[2][64];
|
||||
extern Bitboard OutpostMask[2][64];
|
||||
|
||||
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);
|
||||
extern const uint64_t RMult[64];
|
||||
extern const int RShift[64];
|
||||
extern Bitboard RMask[64];
|
||||
extern int RAttackIndex[64];
|
||||
extern Bitboard RAttacks[0x19000];
|
||||
|
||||
constexpr Bitboard KingFlank[FILE_NB] = {
|
||||
QueenSide ^ FileDBB, QueenSide, QueenSide,
|
||||
CenterFiles, CenterFiles,
|
||||
KingSide, KingSide, KingSide ^ FileEBB
|
||||
};
|
||||
extern const uint64_t BMult[64];
|
||||
extern const int BShift[64];
|
||||
extern Bitboard BMask[64];
|
||||
extern int BAttackIndex[64];
|
||||
extern Bitboard BAttacks[0x1480];
|
||||
|
||||
extern uint8_t PopCnt16[1 << 16];
|
||||
extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||
extern Bitboard BishopPseudoAttacks[64];
|
||||
extern Bitboard RookPseudoAttacks[64];
|
||||
extern Bitboard QueenPseudoAttacks[64];
|
||||
|
||||
extern Bitboard SquareBB[SQUARE_NB];
|
||||
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
||||
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||
extern uint8_t BitCount8Bit[256];
|
||||
|
||||
|
||||
/// Magic holds all magic bitboards relevant data for a single square
|
||||
struct Magic {
|
||||
Bitboard mask;
|
||||
Bitboard magic;
|
||||
Bitboard* attacks;
|
||||
unsigned shift;
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
// Compute the attack's index using the 'magic bitboards' approach
|
||||
unsigned index(Bitboard occupied) const {
|
||||
/// Functions for testing whether a given bit is set in a bitboard, and for
|
||||
/// setting and clearing bits.
|
||||
|
||||
if (HasPext)
|
||||
return unsigned(pext(occupied, mask));
|
||||
inline Bitboard bit_is_set(Bitboard b, Square s) {
|
||||
return b & SetMaskBB[s];
|
||||
}
|
||||
|
||||
if (Is64Bit)
|
||||
return unsigned(((occupied & mask) * magic) >> shift);
|
||||
inline void set_bit(Bitboard *b, Square s) {
|
||||
*b |= SetMaskBB[s];
|
||||
}
|
||||
|
||||
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(is_ok(s));
|
||||
return SquareBB[s];
|
||||
inline void clear_bit(Bitboard *b, Square s) {
|
||||
*b &= ClearMaskBB[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.
|
||||
/// Functions used to update a bitboard after a move. This is faster
|
||||
/// then calling a sequence of clear_bit() + set_bit()
|
||||
|
||||
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); }
|
||||
inline Bitboard make_move_bb(Square from, Square to) {
|
||||
return SetMaskBB[from] | SetMaskBB[to];
|
||||
}
|
||||
|
||||
inline Bitboard operator&(Square s, Bitboard b) { return b & s; }
|
||||
inline Bitboard operator|(Square s, Bitboard b) { return b | s; }
|
||||
inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; }
|
||||
inline void do_move_bb(Bitboard *b, Bitboard move_bb) {
|
||||
*b ^= move_bb;
|
||||
}
|
||||
|
||||
inline Bitboard operator|(Square s1, Square s2) { return square_bb(s1) | s2; }
|
||||
/// rank_bb() and file_bb() gives a bitboard containing all squares on a given
|
||||
/// file or rank. It is also possible to pass a square as input to these
|
||||
/// functions.
|
||||
|
||||
constexpr bool more_than_one(Bitboard b) {
|
||||
return b & (b - 1);
|
||||
inline Bitboard rank_bb(Rank r) {
|
||||
return RankBB[r];
|
||||
}
|
||||
|
||||
inline Bitboard rank_bb(Square s) {
|
||||
return rank_bb(square_rank(s));
|
||||
}
|
||||
|
||||
inline Bitboard file_bb(File f) {
|
||||
return FileBB[f];
|
||||
}
|
||||
|
||||
inline Bitboard file_bb(Square s) {
|
||||
return file_bb(square_file(s));
|
||||
}
|
||||
|
||||
|
||||
constexpr bool opposite_colors(Square s1, Square s2) {
|
||||
return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1;
|
||||
/// neighboring_files_bb takes a file or a square as input, and returns a
|
||||
/// bitboard representing all squares on the neighboring files.
|
||||
|
||||
inline Bitboard neighboring_files_bb(File f) {
|
||||
return NeighboringFilesBB[f];
|
||||
}
|
||||
|
||||
inline Bitboard neighboring_files_bb(Square s) {
|
||||
return neighboring_files_bb(square_file(s));
|
||||
}
|
||||
|
||||
|
||||
/// rank_bb() and file_bb() return a bitboard representing all the squares on
|
||||
/// the given file or rank.
|
||||
/// this_and_neighboring_files_bb takes a file or a square as input, and
|
||||
/// returns a bitboard representing all squares on the given and neighboring
|
||||
/// files.
|
||||
|
||||
constexpr Bitboard rank_bb(Rank r) {
|
||||
return Rank1BB << (8 * r);
|
||||
inline Bitboard this_and_neighboring_files_bb(File f) {
|
||||
return ThisAndNeighboringFilesBB[f];
|
||||
}
|
||||
|
||||
constexpr Bitboard rank_bb(Square s) {
|
||||
return rank_bb(rank_of(s));
|
||||
}
|
||||
|
||||
constexpr Bitboard file_bb(File f) {
|
||||
return FileABB << f;
|
||||
}
|
||||
|
||||
constexpr Bitboard file_bb(Square s) {
|
||||
return file_bb(file_of(s));
|
||||
inline Bitboard this_and_neighboring_files_bb(Square s) {
|
||||
return this_and_neighboring_files_bb(square_file(s));
|
||||
}
|
||||
|
||||
|
||||
/// shift() moves a bitboard one or two steps as specified by the direction D
|
||||
/// relative_rank_bb() takes a color and a rank as input, and returns a bitboard
|
||||
/// representing all squares on the given rank from the given color's point of
|
||||
/// view. For instance, relative_rank_bb(WHITE, 7) gives all squares on the
|
||||
/// 7th rank, while relative_rank_bb(BLACK, 7) gives all squares on the 2nd
|
||||
/// rank.
|
||||
|
||||
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;
|
||||
inline Bitboard relative_rank_bb(Color c, Rank r) {
|
||||
return RelativeRankBB[c][r];
|
||||
}
|
||||
|
||||
|
||||
/// pawn_attacks_bb() returns the squares attacked by pawns of the given color
|
||||
/// from the squares in the given bitboard.
|
||||
/// in_front_bb() takes a color and a rank or square as input, and returns a
|
||||
/// bitboard representing all the squares on all ranks in front of the rank
|
||||
/// (or square), from the given color's point of view. For instance,
|
||||
/// in_front_bb(WHITE, RANK_5) will give all squares on ranks 6, 7 and 8, while
|
||||
/// in_front_bb(BLACK, SQ_D3) will give all squares on ranks 1 and 2.
|
||||
|
||||
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);
|
||||
inline Bitboard in_front_bb(Color c, Rank r) {
|
||||
return InFrontBB[c][r];
|
||||
}
|
||||
|
||||
inline Bitboard pawn_attacks_bb(Color c, Square s) {
|
||||
|
||||
assert(is_ok(s));
|
||||
return PawnAttacks[c][s];
|
||||
inline Bitboard in_front_bb(Color c, Square s) {
|
||||
return in_front_bb(c, square_rank(s));
|
||||
}
|
||||
|
||||
|
||||
/// pawn_double_attacks_bb() returns the squares doubly attacked by pawns of the
|
||||
/// given color from the squares in the given bitboard.
|
||||
/// behind_bb() takes a color and a rank or square as input, and returns a
|
||||
/// bitboard representing all the squares on all ranks behind of the rank
|
||||
/// (or square), from the given color's point of view.
|
||||
|
||||
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);
|
||||
inline Bitboard behind_bb(Color c, Rank r) {
|
||||
return InFrontBB[opposite_color(c)][r];
|
||||
}
|
||||
|
||||
inline Bitboard behind_bb(Color c, Square s) {
|
||||
return in_front_bb(opposite_color(c), square_rank(s));
|
||||
}
|
||||
|
||||
|
||||
/// adjacent_files_bb() returns a bitboard representing all the squares on the
|
||||
/// adjacent files of a given square.
|
||||
/// ray_bb() gives a bitboard representing all squares along the ray in a
|
||||
/// given direction from a given square.
|
||||
|
||||
constexpr Bitboard adjacent_files_bb(Square s) {
|
||||
return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
|
||||
inline Bitboard ray_bb(Square s, SignedDirection d) {
|
||||
return RayBB[s][d];
|
||||
}
|
||||
|
||||
|
||||
/// line_bb() returns a bitboard representing an entire line (from board edge
|
||||
/// to board edge) that intersects the two given squares. If the given squares
|
||||
/// are not on a same file/rank/diagonal, the function returns 0. For instance,
|
||||
/// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal.
|
||||
/// Functions for computing sliding attack bitboards. rook_attacks_bb(),
|
||||
/// bishop_attacks_bb() and queen_attacks_bb() all take a square and a
|
||||
/// bitboard of occupied squares as input, and return a bitboard representing
|
||||
/// all squares attacked by a rook, bishop or queen on the given square.
|
||||
|
||||
inline Bitboard line_bb(Square s1, Square s2) {
|
||||
#if defined(IS_64BIT)
|
||||
|
||||
assert(is_ok(s1) && is_ok(s2));
|
||||
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
|
||||
Bitboard b = blockers & RMask[s];
|
||||
return RAttacks[RAttackIndex[s] + ((b * RMult[s]) >> RShift[s])];
|
||||
}
|
||||
|
||||
return LineBB[s1][s2];
|
||||
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
|
||||
Bitboard b = blockers & BMask[s];
|
||||
return BAttacks[BAttackIndex[s] + ((b * BMult[s]) >> BShift[s])];
|
||||
}
|
||||
|
||||
#else // if !defined(IS_64BIT)
|
||||
|
||||
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
|
||||
Bitboard b = blockers & RMask[s];
|
||||
return RAttacks[RAttackIndex[s] +
|
||||
(unsigned(int(b) * int(RMult[s]) ^
|
||||
int(b >> 32) * int(RMult[s] >> 32))
|
||||
>> RShift[s])];
|
||||
}
|
||||
|
||||
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
|
||||
Bitboard b = blockers & BMask[s];
|
||||
return BAttacks[BAttackIndex[s] +
|
||||
(unsigned(int(b) * int(BMult[s]) ^
|
||||
int(b >> 32) * int(BMult[s] >> 32))
|
||||
>> BShift[s])];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
inline Bitboard queen_attacks_bb(Square s, Bitboard blockers) {
|
||||
return rook_attacks_bb(s, blockers) | bishop_attacks_bb(s, blockers);
|
||||
}
|
||||
|
||||
|
||||
/// between_bb(s1, s2) returns a bitboard representing the squares in the semi-open
|
||||
/// segment between the squares s1 and s2 (excluding s1 but including s2). If the
|
||||
/// given squares are not on a same file/rank/diagonal, it returns s2. For instance,
|
||||
/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5, E6 and F7, but
|
||||
/// between_bb(SQ_E6, SQ_F8) will return a bitboard with the square F8. This trick
|
||||
/// allows to generate non-king evasion moves faster: the defending piece must either
|
||||
/// interpose itself to cover the check or capture the checking piece.
|
||||
|
||||
inline Bitboard between_bb(Square s1, Square s2) {
|
||||
|
||||
assert(is_ok(s1) && is_ok(s2));
|
||||
/// squares_between returns a bitboard representing all squares between
|
||||
/// two squares. For instance, squares_between(SQ_C4, SQ_F7) returns a
|
||||
/// bitboard with the bits for square d5 and e6 set. If s1 and s2 are not
|
||||
/// on the same line, file or diagonal, EmptyBoardBB is returned.
|
||||
|
||||
inline Bitboard squares_between(Square s1, Square s2) {
|
||||
return BetweenBB[s1][s2];
|
||||
}
|
||||
|
||||
|
||||
/// forward_ranks_bb() returns a bitboard representing the squares on the ranks in
|
||||
/// front of the given one, 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.
|
||||
/// squares_in_front_of takes a color and a square as input, and returns a
|
||||
/// bitboard representing all squares along the line in front of the square,
|
||||
/// from the point of view of the given color. For instance,
|
||||
/// squares_in_front_of(BLACK, SQ_E4) returns a bitboard with the squares
|
||||
/// e3, e2 and e1 set.
|
||||
|
||||
constexpr Bitboard forward_ranks_bb(Color c, Square s) {
|
||||
return c == WHITE ? ~Rank1BB << 8 * relative_rank(WHITE, s)
|
||||
: ~Rank8BB >> 8 * relative_rank(BLACK, s);
|
||||
inline Bitboard squares_in_front_of(Color c, Square s) {
|
||||
return in_front_bb(c, s) & file_bb(s);
|
||||
}
|
||||
|
||||
|
||||
/// 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.
|
||||
/// squares_behind is similar to squares_in_front, but returns the squares
|
||||
/// behind the square instead of in front of the square.
|
||||
|
||||
constexpr Bitboard forward_file_bb(Color c, Square s) {
|
||||
return forward_ranks_bb(c, s) & file_bb(s);
|
||||
inline Bitboard squares_behind(Color c, Square s) {
|
||||
return in_front_bb(opposite_color(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.
|
||||
/// passed_pawn_mask takes a color and a square as input, and returns a
|
||||
/// bitboard mask which can be used to test if a pawn of the given color on
|
||||
/// the given square is a passed pawn.
|
||||
|
||||
constexpr Bitboard pawn_attack_span(Color c, Square s) {
|
||||
return forward_ranks_bb(c, s) & adjacent_files_bb(s);
|
||||
inline Bitboard passed_pawn_mask(Color c, Square s) {
|
||||
return PassedPawnMask[c][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 is a passed pawn.
|
||||
/// outpost_mask takes a color and a square as input, and returns a bitboard
|
||||
/// mask which can be used to test whether a piece on the square can possibly
|
||||
/// be driven away by an enemy pawn.
|
||||
|
||||
constexpr Bitboard passed_pawn_span(Color c, Square s) {
|
||||
return pawn_attack_span(c, s) | forward_file_bb(c, s);
|
||||
inline Bitboard outpost_mask(Color c, Square s) {
|
||||
return OutpostMask[c][s];
|
||||
}
|
||||
|
||||
|
||||
/// aligned() returns true if the squares s1, s2 and s3 are aligned either on a
|
||||
/// straight or on a diagonal line.
|
||||
/// isolated_pawn_mask takes a square as input, and returns a bitboard mask
|
||||
/// which can be used to test whether a pawn on the given square is isolated.
|
||||
|
||||
inline bool aligned(Square s1, Square s2, Square s3) {
|
||||
return line_bb(s1, s2) & s3;
|
||||
inline Bitboard isolated_pawn_mask(Square s) {
|
||||
return neighboring_files_bb(s);
|
||||
}
|
||||
|
||||
|
||||
/// distance() functions return the distance between x and y, defined as the
|
||||
/// number of steps for a king in x to reach y.
|
||||
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
|
||||
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
|
||||
/// nonzero bitboard.
|
||||
|
||||
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]; }
|
||||
#if defined(USE_BSFQ) // Assembly code by Heinz van Saanen
|
||||
|
||||
inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
|
||||
inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); }
|
||||
|
||||
|
||||
/// attacks_bb(Square) returns the pseudo attacks of the give piece type
|
||||
/// assuming an empty board.
|
||||
|
||||
template<PieceType Pt>
|
||||
inline Bitboard attacks_bb(Square s) {
|
||||
|
||||
assert((Pt != PAWN) && (is_ok(s)));
|
||||
|
||||
return PseudoAttacks[Pt][s];
|
||||
inline Square first_1(Bitboard b) {
|
||||
Bitboard dummy;
|
||||
__asm__("bsfq %1, %0": "=r"(dummy): "rm"(b) );
|
||||
return (Square)(dummy);
|
||||
}
|
||||
|
||||
|
||||
/// attacks_bb(Square, Bitboard) returns the attacks by the given piece
|
||||
/// assuming the board is occupied according to the passed Bitboard.
|
||||
/// Sliding piece attacks do not continue passed an occupied square.
|
||||
|
||||
template<PieceType Pt>
|
||||
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
|
||||
|
||||
assert((Pt != PAWN) && (is_ok(s)));
|
||||
|
||||
switch (Pt)
|
||||
{
|
||||
case BISHOP: return BishopMagics[s].attacks[BishopMagics[s].index(occupied)];
|
||||
case ROOK : return RookMagics[s].attacks[ RookMagics[s].index(occupied)];
|
||||
case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
|
||||
default : return PseudoAttacks[Pt][s];
|
||||
}
|
||||
}
|
||||
|
||||
inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
|
||||
|
||||
assert((pt != PAWN) && (is_ok(s)));
|
||||
|
||||
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
|
||||
|
||||
/// least_significant_square_bb() returns the bitboard of the least significant
|
||||
/// square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)).
|
||||
|
||||
inline Bitboard least_significant_square_bb(Bitboard b) {
|
||||
assert(b);
|
||||
return b & -b;
|
||||
}
|
||||
|
||||
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard
|
||||
|
||||
inline Square pop_lsb(Bitboard& b) {
|
||||
assert(b);
|
||||
const Square s = lsb(b);
|
||||
b &= b - 1;
|
||||
inline Square pop_1st_bit(Bitboard* b) {
|
||||
const Square s = first_1(*b);
|
||||
*b &= ~(1ULL<<s);
|
||||
return s;
|
||||
}
|
||||
|
||||
#else // if !defined(USE_BSFQ)
|
||||
|
||||
/// frontmost_sq() returns the most advanced square for the given color,
|
||||
/// requires a non-zero bitboard.
|
||||
inline Square frontmost_sq(Color c, Bitboard b) {
|
||||
assert(b);
|
||||
return c == WHITE ? msb(b) : lsb(b);
|
||||
}
|
||||
extern Square first_1(Bitboard b);
|
||||
extern Square pop_1st_bit(Bitboard* b);
|
||||
|
||||
} // namespace Stockfish
|
||||
#endif
|
||||
|
||||
#endif // #ifndef BITBOARD_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern void print_bitboard(Bitboard b);
|
||||
extern void init_bitboards();
|
||||
extern int bitScanReverse32(uint32_t b);
|
||||
|
||||
|
||||
#endif // !defined(BITBOARD_H_INCLUDED)
|
||||
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(BITCOUNT_H_INCLUDED)
|
||||
#define BITCOUNT_H_INCLUDED
|
||||
|
||||
#include "types.h"
|
||||
|
||||
// Select type of intrinsic bit count instruction to use, see
|
||||
// README.txt on how to pgo compile with POPCNT support.
|
||||
|
||||
#if defined(__INTEL_COMPILER) && defined(USE_POPCNT) // Intel compiler
|
||||
|
||||
#include <nmmintrin.h>
|
||||
|
||||
inline bool cpu_has_popcnt() {
|
||||
|
||||
int CPUInfo[4] = {-1};
|
||||
__cpuid(CPUInfo, 0x00000001);
|
||||
return (CPUInfo[2] >> 23) & 1;
|
||||
}
|
||||
|
||||
#define POPCNT_INTRINSIC(x) _mm_popcnt_u64(x)
|
||||
|
||||
#elif defined(_MSC_VER) && defined(USE_POPCNT) // Microsoft compiler
|
||||
|
||||
#include <intrin.h>
|
||||
|
||||
inline bool cpu_has_popcnt() {
|
||||
|
||||
int CPUInfo[4] = {-1};
|
||||
__cpuid(CPUInfo, 0x00000001);
|
||||
return (CPUInfo[2] >> 23) & 1;
|
||||
}
|
||||
|
||||
#define POPCNT_INTRINSIC(x) __popcnt64(x)
|
||||
|
||||
#elif defined(__GNUC__) && defined(USE_POPCNT) // Gcc compiler
|
||||
|
||||
inline void __cpuid(unsigned int op,
|
||||
unsigned int *eax, unsigned int *ebx,
|
||||
unsigned int *ecx, unsigned int *edx)
|
||||
{
|
||||
*eax = op;
|
||||
*ecx = 0;
|
||||
__asm__("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
|
||||
: "0" (*eax), "2" (*ecx));
|
||||
}
|
||||
|
||||
inline bool cpu_has_popcnt() {
|
||||
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
__cpuid(1, &eax, &ebx, &ecx, &edx);
|
||||
return (ecx >> 23) & 1;
|
||||
}
|
||||
|
||||
#define POPCNT_INTRINSIC(x) ({ \
|
||||
unsigned long __ret; \
|
||||
__asm__("popcnt %1, %0" : "=r" (__ret) : "r" (x)); \
|
||||
__ret; })
|
||||
|
||||
#else // Safe fallback for unsupported compilers or when USE_POPCNT is disabled
|
||||
|
||||
inline bool cpu_has_popcnt() { return false; }
|
||||
|
||||
#define POPCNT_INTRINSIC(x) 0
|
||||
|
||||
#endif // cpu_has_popcnt() and POPCNT_INTRINSIC() definitions
|
||||
|
||||
|
||||
/// Software implementation of bit count functions
|
||||
|
||||
#if defined(IS_64BIT)
|
||||
|
||||
inline int count_1s(Bitboard b) {
|
||||
b -= ((b>>1) & 0x5555555555555555ULL);
|
||||
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
|
||||
b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL;
|
||||
b *= 0x0101010101010101ULL;
|
||||
return int(b >> 56);
|
||||
}
|
||||
|
||||
inline int count_1s_max_15(Bitboard b) {
|
||||
b -= (b>>1) & 0x5555555555555555ULL;
|
||||
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
|
||||
b *= 0x1111111111111111ULL;
|
||||
return int(b >> 60);
|
||||
}
|
||||
|
||||
#else // if !defined(IS_64BIT)
|
||||
|
||||
inline int count_1s(Bitboard b) {
|
||||
unsigned w = unsigned(b >> 32), v = unsigned(b);
|
||||
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
|
||||
w -= (w >> 1) & 0x55555555;
|
||||
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
|
||||
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
|
||||
v = ((v >> 4) + v) & 0x0F0F0F0F; // 0-8 in 8 bits
|
||||
v += (((w >> 4) + w) & 0x0F0F0F0F); // 0-16 in 8 bits
|
||||
v *= 0x01010101; // mul is fast on amd procs
|
||||
return int(v >> 24);
|
||||
}
|
||||
|
||||
inline int count_1s_max_15(Bitboard b) {
|
||||
unsigned w = unsigned(b >> 32), v = unsigned(b);
|
||||
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
|
||||
w -= (w >> 1) & 0x55555555;
|
||||
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
|
||||
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
|
||||
v += w; // 0-8 in 4 bits
|
||||
v *= 0x11111111;
|
||||
return int(v >> 28);
|
||||
}
|
||||
|
||||
#endif // BITCOUNT
|
||||
|
||||
|
||||
/// count_1s() counts the number of nonzero bits in a bitboard.
|
||||
/// If template parameter is true an intrinsic is called, otherwise
|
||||
/// we fallback on a software implementation.
|
||||
|
||||
template<bool UseIntrinsic>
|
||||
inline int count_1s(Bitboard b) {
|
||||
|
||||
return UseIntrinsic ? POPCNT_INTRINSIC(b) : count_1s(b);
|
||||
}
|
||||
|
||||
template<bool UseIntrinsic>
|
||||
inline int count_1s_max_15(Bitboard b) {
|
||||
|
||||
return UseIntrinsic ? POPCNT_INTRINSIC(b) : count_1s_max_15(b);
|
||||
}
|
||||
|
||||
|
||||
// Global constant initialized at startup that is set to true if
|
||||
// CPU on which application runs supports POPCNT intrinsic. Unless
|
||||
// USE_POPCNT is not defined.
|
||||
const bool CpuHasPOPCNT = cpu_has_popcnt();
|
||||
|
||||
|
||||
// Global constant used to print info about the use of 64 optimized
|
||||
// functions to verify that a 64 bit compile has been correctly built.
|
||||
#if defined(IS_64BIT)
|
||||
const bool CpuHas64BitPath = true;
|
||||
#else
|
||||
const bool CpuHas64BitPath = false;
|
||||
#endif
|
||||
|
||||
#endif // !defined(BITCOUNT_H_INCLUDED)
|
||||
+583
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
The code in this file is based on the opening book code in PolyGlot
|
||||
by Fabien Letouzey. PolyGlot is available under the GNU General
|
||||
Public License, and can be downloaded from http://wbec-ridderkerk.nl
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "book.h"
|
||||
#include "mersenne.h"
|
||||
#include "movegen.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
////
|
||||
//// Global variables
|
||||
////
|
||||
|
||||
Book OpeningBook;
|
||||
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
/// Book entry size in bytes
|
||||
const int EntrySize = 16;
|
||||
|
||||
|
||||
/// Random numbers from PolyGlot, used to compute book hash keys
|
||||
|
||||
const uint64_t Random64[781] = {
|
||||
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
|
||||
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
|
||||
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
|
||||
0x1A083822CEAFE02DULL, 0x9605D5F0E25EC3B0ULL, 0xD021FF5CD13A2ED5ULL,
|
||||
0x40BDF15D4A672E32ULL, 0x011355146FD56395ULL, 0x5DB4832046F3D9E5ULL,
|
||||
0x239F8B2D7FF719CCULL, 0x05D1A1AE85B49AA1ULL, 0x679F848F6E8FC971ULL,
|
||||
0x7449BBFF801FED0BULL, 0x7D11CDB1C3B7ADF0ULL, 0x82C7709E781EB7CCULL,
|
||||
0xF3218F1C9510786CULL, 0x331478F3AF51BBE6ULL, 0x4BB38DE5E7219443ULL,
|
||||
0xAA649C6EBCFD50FCULL, 0x8DBD98A352AFD40BULL, 0x87D2074B81D79217ULL,
|
||||
0x19F3C751D3E92AE1ULL, 0xB4AB30F062B19ABFULL, 0x7B0500AC42047AC4ULL,
|
||||
0xC9452CA81A09D85DULL, 0x24AA6C514DA27500ULL, 0x4C9F34427501B447ULL,
|
||||
0x14A68FD73C910841ULL, 0xA71B9B83461CBD93ULL, 0x03488B95B0F1850FULL,
|
||||
0x637B2B34FF93C040ULL, 0x09D1BC9A3DD90A94ULL, 0x3575668334A1DD3BULL,
|
||||
0x735E2B97A4C45A23ULL, 0x18727070F1BD400BULL, 0x1FCBACD259BF02E7ULL,
|
||||
0xD310A7C2CE9B6555ULL, 0xBF983FE0FE5D8244ULL, 0x9F74D14F7454A824ULL,
|
||||
0x51EBDC4AB9BA3035ULL, 0x5C82C505DB9AB0FAULL, 0xFCF7FE8A3430B241ULL,
|
||||
0x3253A729B9BA3DDEULL, 0x8C74C368081B3075ULL, 0xB9BC6C87167C33E7ULL,
|
||||
0x7EF48F2B83024E20ULL, 0x11D505D4C351BD7FULL, 0x6568FCA92C76A243ULL,
|
||||
0x4DE0B0F40F32A7B8ULL, 0x96D693460CC37E5DULL, 0x42E240CB63689F2FULL,
|
||||
0x6D2BDCDAE2919661ULL, 0x42880B0236E4D951ULL, 0x5F0F4A5898171BB6ULL,
|
||||
0x39F890F579F92F88ULL, 0x93C5B5F47356388BULL, 0x63DC359D8D231B78ULL,
|
||||
0xEC16CA8AEA98AD76ULL, 0x5355F900C2A82DC7ULL, 0x07FB9F855A997142ULL,
|
||||
0x5093417AA8A7ED5EULL, 0x7BCBC38DA25A7F3CULL, 0x19FC8A768CF4B6D4ULL,
|
||||
0x637A7780DECFC0D9ULL, 0x8249A47AEE0E41F7ULL, 0x79AD695501E7D1E8ULL,
|
||||
0x14ACBAF4777D5776ULL, 0xF145B6BECCDEA195ULL, 0xDABF2AC8201752FCULL,
|
||||
0x24C3C94DF9C8D3F6ULL, 0xBB6E2924F03912EAULL, 0x0CE26C0B95C980D9ULL,
|
||||
0xA49CD132BFBF7CC4ULL, 0xE99D662AF4243939ULL, 0x27E6AD7891165C3FULL,
|
||||
0x8535F040B9744FF1ULL, 0x54B3F4FA5F40D873ULL, 0x72B12C32127FED2BULL,
|
||||
0xEE954D3C7B411F47ULL, 0x9A85AC909A24EAA1ULL, 0x70AC4CD9F04F21F5ULL,
|
||||
0xF9B89D3E99A075C2ULL, 0x87B3E2B2B5C907B1ULL, 0xA366E5B8C54F48B8ULL,
|
||||
0xAE4A9346CC3F7CF2ULL, 0x1920C04D47267BBDULL, 0x87BF02C6B49E2AE9ULL,
|
||||
0x092237AC237F3859ULL, 0xFF07F64EF8ED14D0ULL, 0x8DE8DCA9F03CC54EULL,
|
||||
0x9C1633264DB49C89ULL, 0xB3F22C3D0B0B38EDULL, 0x390E5FB44D01144BULL,
|
||||
0x5BFEA5B4712768E9ULL, 0x1E1032911FA78984ULL, 0x9A74ACB964E78CB3ULL,
|
||||
0x4F80F7A035DAFB04ULL, 0x6304D09A0B3738C4ULL, 0x2171E64683023A08ULL,
|
||||
0x5B9B63EB9CEFF80CULL, 0x506AACF489889342ULL, 0x1881AFC9A3A701D6ULL,
|
||||
0x6503080440750644ULL, 0xDFD395339CDBF4A7ULL, 0xEF927DBCF00C20F2ULL,
|
||||
0x7B32F7D1E03680ECULL, 0xB9FD7620E7316243ULL, 0x05A7E8A57DB91B77ULL,
|
||||
0xB5889C6E15630A75ULL, 0x4A750A09CE9573F7ULL, 0xCF464CEC899A2F8AULL,
|
||||
0xF538639CE705B824ULL, 0x3C79A0FF5580EF7FULL, 0xEDE6C87F8477609DULL,
|
||||
0x799E81F05BC93F31ULL, 0x86536B8CF3428A8CULL, 0x97D7374C60087B73ULL,
|
||||
0xA246637CFF328532ULL, 0x043FCAE60CC0EBA0ULL, 0x920E449535DD359EULL,
|
||||
0x70EB093B15B290CCULL, 0x73A1921916591CBDULL, 0x56436C9FE1A1AA8DULL,
|
||||
0xEFAC4B70633B8F81ULL, 0xBB215798D45DF7AFULL, 0x45F20042F24F1768ULL,
|
||||
0x930F80F4E8EB7462ULL, 0xFF6712FFCFD75EA1ULL, 0xAE623FD67468AA70ULL,
|
||||
0xDD2C5BC84BC8D8FCULL, 0x7EED120D54CF2DD9ULL, 0x22FE545401165F1CULL,
|
||||
0xC91800E98FB99929ULL, 0x808BD68E6AC10365ULL, 0xDEC468145B7605F6ULL,
|
||||
0x1BEDE3A3AEF53302ULL, 0x43539603D6C55602ULL, 0xAA969B5C691CCB7AULL,
|
||||
0xA87832D392EFEE56ULL, 0x65942C7B3C7E11AEULL, 0xDED2D633CAD004F6ULL,
|
||||
0x21F08570F420E565ULL, 0xB415938D7DA94E3CULL, 0x91B859E59ECB6350ULL,
|
||||
0x10CFF333E0ED804AULL, 0x28AED140BE0BB7DDULL, 0xC5CC1D89724FA456ULL,
|
||||
0x5648F680F11A2741ULL, 0x2D255069F0B7DAB3ULL, 0x9BC5A38EF729ABD4ULL,
|
||||
0xEF2F054308F6A2BCULL, 0xAF2042F5CC5C2858ULL, 0x480412BAB7F5BE2AULL,
|
||||
0xAEF3AF4A563DFE43ULL, 0x19AFE59AE451497FULL, 0x52593803DFF1E840ULL,
|
||||
0xF4F076E65F2CE6F0ULL, 0x11379625747D5AF3ULL, 0xBCE5D2248682C115ULL,
|
||||
0x9DA4243DE836994FULL, 0x066F70B33FE09017ULL, 0x4DC4DE189B671A1CULL,
|
||||
0x51039AB7712457C3ULL, 0xC07A3F80C31FB4B4ULL, 0xB46EE9C5E64A6E7CULL,
|
||||
0xB3819A42ABE61C87ULL, 0x21A007933A522A20ULL, 0x2DF16F761598AA4FULL,
|
||||
0x763C4A1371B368FDULL, 0xF793C46702E086A0ULL, 0xD7288E012AEB8D31ULL,
|
||||
0xDE336A2A4BC1C44BULL, 0x0BF692B38D079F23ULL, 0x2C604A7A177326B3ULL,
|
||||
0x4850E73E03EB6064ULL, 0xCFC447F1E53C8E1BULL, 0xB05CA3F564268D99ULL,
|
||||
0x9AE182C8BC9474E8ULL, 0xA4FC4BD4FC5558CAULL, 0xE755178D58FC4E76ULL,
|
||||
0x69B97DB1A4C03DFEULL, 0xF9B5B7C4ACC67C96ULL, 0xFC6A82D64B8655FBULL,
|
||||
0x9C684CB6C4D24417ULL, 0x8EC97D2917456ED0ULL, 0x6703DF9D2924E97EULL,
|
||||
0xC547F57E42A7444EULL, 0x78E37644E7CAD29EULL, 0xFE9A44E9362F05FAULL,
|
||||
0x08BD35CC38336615ULL, 0x9315E5EB3A129ACEULL, 0x94061B871E04DF75ULL,
|
||||
0xDF1D9F9D784BA010ULL, 0x3BBA57B68871B59DULL, 0xD2B7ADEEDED1F73FULL,
|
||||
0xF7A255D83BC373F8ULL, 0xD7F4F2448C0CEB81ULL, 0xD95BE88CD210FFA7ULL,
|
||||
0x336F52F8FF4728E7ULL, 0xA74049DAC312AC71ULL, 0xA2F61BB6E437FDB5ULL,
|
||||
0x4F2A5CB07F6A35B3ULL, 0x87D380BDA5BF7859ULL, 0x16B9F7E06C453A21ULL,
|
||||
0x7BA2484C8A0FD54EULL, 0xF3A678CAD9A2E38CULL, 0x39B0BF7DDE437BA2ULL,
|
||||
0xFCAF55C1BF8A4424ULL, 0x18FCF680573FA594ULL, 0x4C0563B89F495AC3ULL,
|
||||
0x40E087931A00930DULL, 0x8CFFA9412EB642C1ULL, 0x68CA39053261169FULL,
|
||||
0x7A1EE967D27579E2ULL, 0x9D1D60E5076F5B6FULL, 0x3810E399B6F65BA2ULL,
|
||||
0x32095B6D4AB5F9B1ULL, 0x35CAB62109DD038AULL, 0xA90B24499FCFAFB1ULL,
|
||||
0x77A225A07CC2C6BDULL, 0x513E5E634C70E331ULL, 0x4361C0CA3F692F12ULL,
|
||||
0xD941ACA44B20A45BULL, 0x528F7C8602C5807BULL, 0x52AB92BEB9613989ULL,
|
||||
0x9D1DFA2EFC557F73ULL, 0x722FF175F572C348ULL, 0x1D1260A51107FE97ULL,
|
||||
0x7A249A57EC0C9BA2ULL, 0x04208FE9E8F7F2D6ULL, 0x5A110C6058B920A0ULL,
|
||||
0x0CD9A497658A5698ULL, 0x56FD23C8F9715A4CULL, 0x284C847B9D887AAEULL,
|
||||
0x04FEABFBBDB619CBULL, 0x742E1E651C60BA83ULL, 0x9A9632E65904AD3CULL,
|
||||
0x881B82A13B51B9E2ULL, 0x506E6744CD974924ULL, 0xB0183DB56FFC6A79ULL,
|
||||
0x0ED9B915C66ED37EULL, 0x5E11E86D5873D484ULL, 0xF678647E3519AC6EULL,
|
||||
0x1B85D488D0F20CC5ULL, 0xDAB9FE6525D89021ULL, 0x0D151D86ADB73615ULL,
|
||||
0xA865A54EDCC0F019ULL, 0x93C42566AEF98FFBULL, 0x99E7AFEABE000731ULL,
|
||||
0x48CBFF086DDF285AULL, 0x7F9B6AF1EBF78BAFULL, 0x58627E1A149BBA21ULL,
|
||||
0x2CD16E2ABD791E33ULL, 0xD363EFF5F0977996ULL, 0x0CE2A38C344A6EEDULL,
|
||||
0x1A804AADB9CFA741ULL, 0x907F30421D78C5DEULL, 0x501F65EDB3034D07ULL,
|
||||
0x37624AE5A48FA6E9ULL, 0x957BAF61700CFF4EULL, 0x3A6C27934E31188AULL,
|
||||
0xD49503536ABCA345ULL, 0x088E049589C432E0ULL, 0xF943AEE7FEBF21B8ULL,
|
||||
0x6C3B8E3E336139D3ULL, 0x364F6FFA464EE52EULL, 0xD60F6DCEDC314222ULL,
|
||||
0x56963B0DCA418FC0ULL, 0x16F50EDF91E513AFULL, 0xEF1955914B609F93ULL,
|
||||
0x565601C0364E3228ULL, 0xECB53939887E8175ULL, 0xBAC7A9A18531294BULL,
|
||||
0xB344C470397BBA52ULL, 0x65D34954DAF3CEBDULL, 0xB4B81B3FA97511E2ULL,
|
||||
0xB422061193D6F6A7ULL, 0x071582401C38434DULL, 0x7A13F18BBEDC4FF5ULL,
|
||||
0xBC4097B116C524D2ULL, 0x59B97885E2F2EA28ULL, 0x99170A5DC3115544ULL,
|
||||
0x6F423357E7C6A9F9ULL, 0x325928EE6E6F8794ULL, 0xD0E4366228B03343ULL,
|
||||
0x565C31F7DE89EA27ULL, 0x30F5611484119414ULL, 0xD873DB391292ED4FULL,
|
||||
0x7BD94E1D8E17DEBCULL, 0xC7D9F16864A76E94ULL, 0x947AE053EE56E63CULL,
|
||||
0xC8C93882F9475F5FULL, 0x3A9BF55BA91F81CAULL, 0xD9A11FBB3D9808E4ULL,
|
||||
0x0FD22063EDC29FCAULL, 0xB3F256D8ACA0B0B9ULL, 0xB03031A8B4516E84ULL,
|
||||
0x35DD37D5871448AFULL, 0xE9F6082B05542E4EULL, 0xEBFAFA33D7254B59ULL,
|
||||
0x9255ABB50D532280ULL, 0xB9AB4CE57F2D34F3ULL, 0x693501D628297551ULL,
|
||||
0xC62C58F97DD949BFULL, 0xCD454F8F19C5126AULL, 0xBBE83F4ECC2BDECBULL,
|
||||
0xDC842B7E2819E230ULL, 0xBA89142E007503B8ULL, 0xA3BC941D0A5061CBULL,
|
||||
0xE9F6760E32CD8021ULL, 0x09C7E552BC76492FULL, 0x852F54934DA55CC9ULL,
|
||||
0x8107FCCF064FCF56ULL, 0x098954D51FFF6580ULL, 0x23B70EDB1955C4BFULL,
|
||||
0xC330DE426430F69DULL, 0x4715ED43E8A45C0AULL, 0xA8D7E4DAB780A08DULL,
|
||||
0x0572B974F03CE0BBULL, 0xB57D2E985E1419C7ULL, 0xE8D9ECBE2CF3D73FULL,
|
||||
0x2FE4B17170E59750ULL, 0x11317BA87905E790ULL, 0x7FBF21EC8A1F45ECULL,
|
||||
0x1725CABFCB045B00ULL, 0x964E915CD5E2B207ULL, 0x3E2B8BCBF016D66DULL,
|
||||
0xBE7444E39328A0ACULL, 0xF85B2B4FBCDE44B7ULL, 0x49353FEA39BA63B1ULL,
|
||||
0x1DD01AAFCD53486AULL, 0x1FCA8A92FD719F85ULL, 0xFC7C95D827357AFAULL,
|
||||
0x18A6A990C8B35EBDULL, 0xCCCB7005C6B9C28DULL, 0x3BDBB92C43B17F26ULL,
|
||||
0xAA70B5B4F89695A2ULL, 0xE94C39A54A98307FULL, 0xB7A0B174CFF6F36EULL,
|
||||
0xD4DBA84729AF48ADULL, 0x2E18BC1AD9704A68ULL, 0x2DE0966DAF2F8B1CULL,
|
||||
0xB9C11D5B1E43A07EULL, 0x64972D68DEE33360ULL, 0x94628D38D0C20584ULL,
|
||||
0xDBC0D2B6AB90A559ULL, 0xD2733C4335C6A72FULL, 0x7E75D99D94A70F4DULL,
|
||||
0x6CED1983376FA72BULL, 0x97FCAACBF030BC24ULL, 0x7B77497B32503B12ULL,
|
||||
0x8547EDDFB81CCB94ULL, 0x79999CDFF70902CBULL, 0xCFFE1939438E9B24ULL,
|
||||
0x829626E3892D95D7ULL, 0x92FAE24291F2B3F1ULL, 0x63E22C147B9C3403ULL,
|
||||
0xC678B6D860284A1CULL, 0x5873888850659AE7ULL, 0x0981DCD296A8736DULL,
|
||||
0x9F65789A6509A440ULL, 0x9FF38FED72E9052FULL, 0xE479EE5B9930578CULL,
|
||||
0xE7F28ECD2D49EECDULL, 0x56C074A581EA17FEULL, 0x5544F7D774B14AEFULL,
|
||||
0x7B3F0195FC6F290FULL, 0x12153635B2C0CF57ULL, 0x7F5126DBBA5E0CA7ULL,
|
||||
0x7A76956C3EAFB413ULL, 0x3D5774A11D31AB39ULL, 0x8A1B083821F40CB4ULL,
|
||||
0x7B4A38E32537DF62ULL, 0x950113646D1D6E03ULL, 0x4DA8979A0041E8A9ULL,
|
||||
0x3BC36E078F7515D7ULL, 0x5D0A12F27AD310D1ULL, 0x7F9D1A2E1EBE1327ULL,
|
||||
0xDA3A361B1C5157B1ULL, 0xDCDD7D20903D0C25ULL, 0x36833336D068F707ULL,
|
||||
0xCE68341F79893389ULL, 0xAB9090168DD05F34ULL, 0x43954B3252DC25E5ULL,
|
||||
0xB438C2B67F98E5E9ULL, 0x10DCD78E3851A492ULL, 0xDBC27AB5447822BFULL,
|
||||
0x9B3CDB65F82CA382ULL, 0xB67B7896167B4C84ULL, 0xBFCED1B0048EAC50ULL,
|
||||
0xA9119B60369FFEBDULL, 0x1FFF7AC80904BF45ULL, 0xAC12FB171817EEE7ULL,
|
||||
0xAF08DA9177DDA93DULL, 0x1B0CAB936E65C744ULL, 0xB559EB1D04E5E932ULL,
|
||||
0xC37B45B3F8D6F2BAULL, 0xC3A9DC228CAAC9E9ULL, 0xF3B8B6675A6507FFULL,
|
||||
0x9FC477DE4ED681DAULL, 0x67378D8ECCEF96CBULL, 0x6DD856D94D259236ULL,
|
||||
0xA319CE15B0B4DB31ULL, 0x073973751F12DD5EULL, 0x8A8E849EB32781A5ULL,
|
||||
0xE1925C71285279F5ULL, 0x74C04BF1790C0EFEULL, 0x4DDA48153C94938AULL,
|
||||
0x9D266D6A1CC0542CULL, 0x7440FB816508C4FEULL, 0x13328503DF48229FULL,
|
||||
0xD6BF7BAEE43CAC40ULL, 0x4838D65F6EF6748FULL, 0x1E152328F3318DEAULL,
|
||||
0x8F8419A348F296BFULL, 0x72C8834A5957B511ULL, 0xD7A023A73260B45CULL,
|
||||
0x94EBC8ABCFB56DAEULL, 0x9FC10D0F989993E0ULL, 0xDE68A2355B93CAE6ULL,
|
||||
0xA44CFE79AE538BBEULL, 0x9D1D84FCCE371425ULL, 0x51D2B1AB2DDFB636ULL,
|
||||
0x2FD7E4B9E72CD38CULL, 0x65CA5B96B7552210ULL, 0xDD69A0D8AB3B546DULL,
|
||||
0x604D51B25FBF70E2ULL, 0x73AA8A564FB7AC9EULL, 0x1A8C1E992B941148ULL,
|
||||
0xAAC40A2703D9BEA0ULL, 0x764DBEAE7FA4F3A6ULL, 0x1E99B96E70A9BE8BULL,
|
||||
0x2C5E9DEB57EF4743ULL, 0x3A938FEE32D29981ULL, 0x26E6DB8FFDF5ADFEULL,
|
||||
0x469356C504EC9F9DULL, 0xC8763C5B08D1908CULL, 0x3F6C6AF859D80055ULL,
|
||||
0x7F7CC39420A3A545ULL, 0x9BFB227EBDF4C5CEULL, 0x89039D79D6FC5C5CULL,
|
||||
0x8FE88B57305E2AB6ULL, 0xA09E8C8C35AB96DEULL, 0xFA7E393983325753ULL,
|
||||
0xD6B6D0ECC617C699ULL, 0xDFEA21EA9E7557E3ULL, 0xB67C1FA481680AF8ULL,
|
||||
0xCA1E3785A9E724E5ULL, 0x1CFC8BED0D681639ULL, 0xD18D8549D140CAEAULL,
|
||||
0x4ED0FE7E9DC91335ULL, 0xE4DBF0634473F5D2ULL, 0x1761F93A44D5AEFEULL,
|
||||
0x53898E4C3910DA55ULL, 0x734DE8181F6EC39AULL, 0x2680B122BAA28D97ULL,
|
||||
0x298AF231C85BAFABULL, 0x7983EED3740847D5ULL, 0x66C1A2A1A60CD889ULL,
|
||||
0x9E17E49642A3E4C1ULL, 0xEDB454E7BADC0805ULL, 0x50B704CAB602C329ULL,
|
||||
0x4CC317FB9CDDD023ULL, 0x66B4835D9EAFEA22ULL, 0x219B97E26FFC81BDULL,
|
||||
0x261E4E4C0A333A9DULL, 0x1FE2CCA76517DB90ULL, 0xD7504DFA8816EDBBULL,
|
||||
0xB9571FA04DC089C8ULL, 0x1DDC0325259B27DEULL, 0xCF3F4688801EB9AAULL,
|
||||
0xF4F5D05C10CAB243ULL, 0x38B6525C21A42B0EULL, 0x36F60E2BA4FA6800ULL,
|
||||
0xEB3593803173E0CEULL, 0x9C4CD6257C5A3603ULL, 0xAF0C317D32ADAA8AULL,
|
||||
0x258E5A80C7204C4BULL, 0x8B889D624D44885DULL, 0xF4D14597E660F855ULL,
|
||||
0xD4347F66EC8941C3ULL, 0xE699ED85B0DFB40DULL, 0x2472F6207C2D0484ULL,
|
||||
0xC2A1E7B5B459AEB5ULL, 0xAB4F6451CC1D45ECULL, 0x63767572AE3D6174ULL,
|
||||
0xA59E0BD101731A28ULL, 0x116D0016CB948F09ULL, 0x2CF9C8CA052F6E9FULL,
|
||||
0x0B090A7560A968E3ULL, 0xABEEDDB2DDE06FF1ULL, 0x58EFC10B06A2068DULL,
|
||||
0xC6E57A78FBD986E0ULL, 0x2EAB8CA63CE802D7ULL, 0x14A195640116F336ULL,
|
||||
0x7C0828DD624EC390ULL, 0xD74BBE77E6116AC7ULL, 0x804456AF10F5FB53ULL,
|
||||
0xEBE9EA2ADF4321C7ULL, 0x03219A39EE587A30ULL, 0x49787FEF17AF9924ULL,
|
||||
0xA1E9300CD8520548ULL, 0x5B45E522E4B1B4EFULL, 0xB49C3B3995091A36ULL,
|
||||
0xD4490AD526F14431ULL, 0x12A8F216AF9418C2ULL, 0x001F837CC7350524ULL,
|
||||
0x1877B51E57A764D5ULL, 0xA2853B80F17F58EEULL, 0x993E1DE72D36D310ULL,
|
||||
0xB3598080CE64A656ULL, 0x252F59CF0D9F04BBULL, 0xD23C8E176D113600ULL,
|
||||
0x1BDA0492E7E4586EULL, 0x21E0BD5026C619BFULL, 0x3B097ADAF088F94EULL,
|
||||
0x8D14DEDB30BE846EULL, 0xF95CFFA23AF5F6F4ULL, 0x3871700761B3F743ULL,
|
||||
0xCA672B91E9E4FA16ULL, 0x64C8E531BFF53B55ULL, 0x241260ED4AD1E87DULL,
|
||||
0x106C09B972D2E822ULL, 0x7FBA195410E5CA30ULL, 0x7884D9BC6CB569D8ULL,
|
||||
0x0647DFEDCD894A29ULL, 0x63573FF03E224774ULL, 0x4FC8E9560F91B123ULL,
|
||||
0x1DB956E450275779ULL, 0xB8D91274B9E9D4FBULL, 0xA2EBEE47E2FBFCE1ULL,
|
||||
0xD9F1F30CCD97FB09ULL, 0xEFED53D75FD64E6BULL, 0x2E6D02C36017F67FULL,
|
||||
0xA9AA4D20DB084E9BULL, 0xB64BE8D8B25396C1ULL, 0x70CB6AF7C2D5BCF0ULL,
|
||||
0x98F076A4F7A2322EULL, 0xBF84470805E69B5FULL, 0x94C3251F06F90CF3ULL,
|
||||
0x3E003E616A6591E9ULL, 0xB925A6CD0421AFF3ULL, 0x61BDD1307C66E300ULL,
|
||||
0xBF8D5108E27E0D48ULL, 0x240AB57A8B888B20ULL, 0xFC87614BAF287E07ULL,
|
||||
0xEF02CDD06FFDB432ULL, 0xA1082C0466DF6C0AULL, 0x8215E577001332C8ULL,
|
||||
0xD39BB9C3A48DB6CFULL, 0x2738259634305C14ULL, 0x61CF4F94C97DF93DULL,
|
||||
0x1B6BACA2AE4E125BULL, 0x758F450C88572E0BULL, 0x959F587D507A8359ULL,
|
||||
0xB063E962E045F54DULL, 0x60E8ED72C0DFF5D1ULL, 0x7B64978555326F9FULL,
|
||||
0xFD080D236DA814BAULL, 0x8C90FD9B083F4558ULL, 0x106F72FE81E2C590ULL,
|
||||
0x7976033A39F7D952ULL, 0xA4EC0132764CA04BULL, 0x733EA705FAE4FA77ULL,
|
||||
0xB4D8F77BC3E56167ULL, 0x9E21F4F903B33FD9ULL, 0x9D765E419FB69F6DULL,
|
||||
0xD30C088BA61EA5EFULL, 0x5D94337FBFAF7F5BULL, 0x1A4E4822EB4D7A59ULL,
|
||||
0x6FFE73E81B637FB3ULL, 0xDDF957BC36D8B9CAULL, 0x64D0E29EEA8838B3ULL,
|
||||
0x08DD9BDFD96B9F63ULL, 0x087E79E5A57D1D13ULL, 0xE328E230E3E2B3FBULL,
|
||||
0x1C2559E30F0946BEULL, 0x720BF5F26F4D2EAAULL, 0xB0774D261CC609DBULL,
|
||||
0x443F64EC5A371195ULL, 0x4112CF68649A260EULL, 0xD813F2FAB7F5C5CAULL,
|
||||
0x660D3257380841EEULL, 0x59AC2C7873F910A3ULL, 0xE846963877671A17ULL,
|
||||
0x93B633ABFA3469F8ULL, 0xC0C0F5A60EF4CDCFULL, 0xCAF21ECD4377B28CULL,
|
||||
0x57277707199B8175ULL, 0x506C11B9D90E8B1DULL, 0xD83CC2687A19255FULL,
|
||||
0x4A29C6465A314CD1ULL, 0xED2DF21216235097ULL, 0xB5635C95FF7296E2ULL,
|
||||
0x22AF003AB672E811ULL, 0x52E762596BF68235ULL, 0x9AEBA33AC6ECC6B0ULL,
|
||||
0x944F6DE09134DFB6ULL, 0x6C47BEC883A7DE39ULL, 0x6AD047C430A12104ULL,
|
||||
0xA5B1CFDBA0AB4067ULL, 0x7C45D833AFF07862ULL, 0x5092EF950A16DA0BULL,
|
||||
0x9338E69C052B8E7BULL, 0x455A4B4CFE30E3F5ULL, 0x6B02E63195AD0CF8ULL,
|
||||
0x6B17B224BAD6BF27ULL, 0xD1E0CCD25BB9C169ULL, 0xDE0C89A556B9AE70ULL,
|
||||
0x50065E535A213CF6ULL, 0x9C1169FA2777B874ULL, 0x78EDEFD694AF1EEDULL,
|
||||
0x6DC93D9526A50E68ULL, 0xEE97F453F06791EDULL, 0x32AB0EDB696703D3ULL,
|
||||
0x3A6853C7E70757A7ULL, 0x31865CED6120F37DULL, 0x67FEF95D92607890ULL,
|
||||
0x1F2B1D1F15F6DC9CULL, 0xB69E38A8965C6B65ULL, 0xAA9119FF184CCCF4ULL,
|
||||
0xF43C732873F24C13ULL, 0xFB4A3D794A9A80D2ULL, 0x3550C2321FD6109CULL,
|
||||
0x371F77E76BB8417EULL, 0x6BFA9AAE5EC05779ULL, 0xCD04F3FF001A4778ULL,
|
||||
0xE3273522064480CAULL, 0x9F91508BFFCFC14AULL, 0x049A7F41061A9E60ULL,
|
||||
0xFCB6BE43A9F2FE9BULL, 0x08DE8A1C7797DA9BULL, 0x8F9887E6078735A1ULL,
|
||||
0xB5B4071DBFC73A66ULL, 0x230E343DFBA08D33ULL, 0x43ED7F5A0FAE657DULL,
|
||||
0x3A88A0FBBCB05C63ULL, 0x21874B8B4D2DBC4FULL, 0x1BDEA12E35F6A8C9ULL,
|
||||
0x53C065C6C8E63528ULL, 0xE34A1D250E7A8D6BULL, 0xD6B04D3B7651DD7EULL,
|
||||
0x5E90277E7CB39E2DULL, 0x2C046F22062DC67DULL, 0xB10BB459132D0A26ULL,
|
||||
0x3FA9DDFB67E2F199ULL, 0x0E09B88E1914F7AFULL, 0x10E8B35AF3EEAB37ULL,
|
||||
0x9EEDECA8E272B933ULL, 0xD4C718BC4AE8AE5FULL, 0x81536D601170FC20ULL,
|
||||
0x91B534F885818A06ULL, 0xEC8177F83F900978ULL, 0x190E714FADA5156EULL,
|
||||
0xB592BF39B0364963ULL, 0x89C350C893AE7DC1ULL, 0xAC042E70F8B383F2ULL,
|
||||
0xB49B52E587A1EE60ULL, 0xFB152FE3FF26DA89ULL, 0x3E666E6F69AE2C15ULL,
|
||||
0x3B544EBE544C19F9ULL, 0xE805A1E290CF2456ULL, 0x24B33C9D7ED25117ULL,
|
||||
0xE74733427B72F0C1ULL, 0x0A804D18B7097475ULL, 0x57E3306D881EDB4FULL,
|
||||
0x4AE7D6A36EB5DBCBULL, 0x2D8D5432157064C8ULL, 0xD1E649DE1E7F268BULL,
|
||||
0x8A328A1CEDFE552CULL, 0x07A3AEC79624C7DAULL, 0x84547DDC3E203C94ULL,
|
||||
0x990A98FD5071D263ULL, 0x1A4FF12616EEFC89ULL, 0xF6F7FD1431714200ULL,
|
||||
0x30C05B1BA332F41CULL, 0x8D2636B81555A786ULL, 0x46C9FEB55D120902ULL,
|
||||
0xCCEC0A73B49C9921ULL, 0x4E9D2827355FC492ULL, 0x19EBB029435DCB0FULL,
|
||||
0x4659D2B743848A2CULL, 0x963EF2C96B33BE31ULL, 0x74F85198B05A2E7DULL,
|
||||
0x5A0F544DD2B1FB18ULL, 0x03727073C2E134B1ULL, 0xC7F6AA2DE59AEA61ULL,
|
||||
0x352787BAA0D7C22FULL, 0x9853EAB63B5E0B35ULL, 0xABBDCDD7ED5C0860ULL,
|
||||
0xCF05DAF5AC8D77B0ULL, 0x49CAD48CEBF4A71EULL, 0x7A4C10EC2158C4A6ULL,
|
||||
0xD9E92AA246BF719EULL, 0x13AE978D09FE5557ULL, 0x730499AF921549FFULL,
|
||||
0x4E4B705B92903BA4ULL, 0xFF577222C14F0A3AULL, 0x55B6344CF97AAFAEULL,
|
||||
0xB862225B055B6960ULL, 0xCAC09AFBDDD2CDB4ULL, 0xDAF8E9829FE96B5FULL,
|
||||
0xB5FDFC5D3132C498ULL, 0x310CB380DB6F7503ULL, 0xE87FBB46217A360EULL,
|
||||
0x2102AE466EBB1148ULL, 0xF8549E1A3AA5E00DULL, 0x07A69AFDCC42261AULL,
|
||||
0xC4C118BFE78FEAAEULL, 0xF9F4892ED96BD438ULL, 0x1AF3DBE25D8F45DAULL,
|
||||
0xF5B4B0B0D2DEEEB4ULL, 0x962ACEEFA82E1C84ULL, 0x046E3ECAAF453CE9ULL,
|
||||
0xF05D129681949A4CULL, 0x964781CE734B3C84ULL, 0x9C2ED44081CE5FBDULL,
|
||||
0x522E23F3925E319EULL, 0x177E00F9FC32F791ULL, 0x2BC60A63A6F3B3F2ULL,
|
||||
0x222BBFAE61725606ULL, 0x486289DDCC3D6780ULL, 0x7DC7785B8EFDFC80ULL,
|
||||
0x8AF38731C02BA980ULL, 0x1FAB64EA29A2DDF7ULL, 0xE4D9429322CD065AULL,
|
||||
0x9DA058C67844F20CULL, 0x24C0E332B70019B0ULL, 0x233003B5A6CFE6ADULL,
|
||||
0xD586BD01C5C217F6ULL, 0x5E5637885F29BC2BULL, 0x7EBA726D8C94094BULL,
|
||||
0x0A56A5F0BFE39272ULL, 0xD79476A84EE20D06ULL, 0x9E4C1269BAA4BF37ULL,
|
||||
0x17EFEE45B0DEE640ULL, 0x1D95B0A5FCF90BC6ULL, 0x93CBE0B699C2585DULL,
|
||||
0x65FA4F227A2B6D79ULL, 0xD5F9E858292504D5ULL, 0xC2B5A03F71471A6FULL,
|
||||
0x59300222B4561E00ULL, 0xCE2F8642CA0712DCULL, 0x7CA9723FBB2E8988ULL,
|
||||
0x2785338347F2BA08ULL, 0xC61BB3A141E50E8CULL, 0x150F361DAB9DEC26ULL,
|
||||
0x9F6A419D382595F4ULL, 0x64A53DC924FE7AC9ULL, 0x142DE49FFF7A7C3DULL,
|
||||
0x0C335248857FA9E7ULL, 0x0A9C32D5EAE45305ULL, 0xE6C42178C4BBB92EULL,
|
||||
0x71F1CE2490D20B07ULL, 0xF1BCC3D275AFE51AULL, 0xE728E8C83C334074ULL,
|
||||
0x96FBF83A12884624ULL, 0x81A1549FD6573DA5ULL, 0x5FA7867CAF35E149ULL,
|
||||
0x56986E2EF3ED091BULL, 0x917F1DD5F8886C61ULL, 0xD20D8C88C8FFE65FULL,
|
||||
0x31D71DCE64B2C310ULL, 0xF165B587DF898190ULL, 0xA57E6339DD2CF3A0ULL,
|
||||
0x1EF6E6DBB1961EC9ULL, 0x70CC73D90BC26E24ULL, 0xE21A6B35DF0C3AD7ULL,
|
||||
0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL,
|
||||
0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL,
|
||||
0xF8D626AAAF278509ULL
|
||||
};
|
||||
|
||||
|
||||
/// Indices to the Random64[] array
|
||||
|
||||
const int RandomPiece = 0;
|
||||
const int RandomCastle = 768;
|
||||
const int RandomEnPassant = 772;
|
||||
const int RandomTurn = 780;
|
||||
|
||||
|
||||
/// Prototypes
|
||||
|
||||
uint64_t book_key(const Position& pos);
|
||||
uint64_t book_piece_key(Piece p, Square s);
|
||||
uint64_t book_castle_key(const Position& pos);
|
||||
uint64_t book_ep_key(const Position& pos);
|
||||
uint64_t book_color_key(const Position& pos);
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
|
||||
/// Destructor. Be sure file is closed before we leave.
|
||||
|
||||
Book::~Book() {
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
/// Book::open() opens a book file with a given file name
|
||||
|
||||
void Book::open(const string& fName) {
|
||||
|
||||
// Close old file before opening the new
|
||||
close();
|
||||
|
||||
fileName = fName;
|
||||
ifstream::open(fileName.c_str(), ifstream::in | ifstream::binary);
|
||||
if (!is_open())
|
||||
return;
|
||||
|
||||
// Get the book size in number of entries
|
||||
seekg(0, ios::end);
|
||||
bookSize = tellg() / EntrySize;
|
||||
seekg(0, ios::beg);
|
||||
|
||||
if (!good())
|
||||
{
|
||||
cerr << "Failed to open book file " << fileName << endl;
|
||||
Application::exit_with_failure();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Book::close() closes the file only if it is open, otherwise
|
||||
/// we can end up in a little mess due to how std::ifstream works.
|
||||
|
||||
void Book::close() {
|
||||
|
||||
if (is_open())
|
||||
ifstream::close();
|
||||
}
|
||||
|
||||
|
||||
/// Book::file_name() returns the file name of the currently active book,
|
||||
/// or the empty string if no book is open.
|
||||
|
||||
const string Book::file_name() const {
|
||||
|
||||
return is_open() ? fileName : "";
|
||||
}
|
||||
|
||||
|
||||
/// Book::get_move() gets a book move for a given position. Returns
|
||||
/// MOVE_NONE if no book move is found.
|
||||
|
||||
Move Book::get_move(const Position& pos) {
|
||||
|
||||
if (!is_open() || bookSize == 0)
|
||||
return MOVE_NONE;
|
||||
|
||||
int bookMove = 0, scoresSum = 0;
|
||||
uint64_t key = book_key(pos);
|
||||
BookEntry entry;
|
||||
|
||||
// Choose a book move among the possible moves for the given position
|
||||
for (int idx = find_key(key); idx < bookSize; idx++)
|
||||
{
|
||||
read_entry(entry, idx);
|
||||
if (entry.key != key)
|
||||
break;
|
||||
|
||||
int score = entry.count;
|
||||
|
||||
assert(score > 0);
|
||||
|
||||
// Choose book move according to its score. If a move has a very
|
||||
// high score it has more probability to be choosen then a one with
|
||||
// lower score. Note that first entry is always chosen.
|
||||
scoresSum += score;
|
||||
if (int(genrand_int32() % scoresSum) < score)
|
||||
bookMove = entry.move;
|
||||
}
|
||||
if (!bookMove)
|
||||
return MOVE_NONE;
|
||||
|
||||
MoveStack mlist[256];
|
||||
MoveStack* last = generate_moves(pos, mlist);
|
||||
for (MoveStack* cur = mlist; cur != last; cur++)
|
||||
if ((int(cur->move) & 07777) == bookMove)
|
||||
return cur->move;
|
||||
|
||||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
|
||||
/// Book::find_key() takes a book key as input, and does a binary search
|
||||
/// through the book file for the given key. The index to the first book
|
||||
/// entry with the same key as the input is returned. When the key is not
|
||||
/// found in the book file, bookSize is returned.
|
||||
|
||||
int Book::find_key(uint64_t key) {
|
||||
|
||||
int left, right, mid;
|
||||
BookEntry entry;
|
||||
|
||||
// Binary search (finds the leftmost entry)
|
||||
left = 0;
|
||||
right = bookSize - 1;
|
||||
|
||||
assert(left <= right);
|
||||
|
||||
while (left < right)
|
||||
{
|
||||
mid = (left + right) / 2;
|
||||
|
||||
assert(mid >= left && mid < right);
|
||||
|
||||
read_entry(entry, mid);
|
||||
if (key <= entry.key)
|
||||
right = mid;
|
||||
else
|
||||
left = mid + 1;
|
||||
}
|
||||
|
||||
assert(left == right);
|
||||
|
||||
read_entry(entry, left);
|
||||
return (entry.key == key)? left : bookSize;
|
||||
}
|
||||
|
||||
|
||||
/// Book::read_entry() takes a BookEntry reference and an integer index as
|
||||
/// input, and looks up the opening book entry at the given index in the book
|
||||
/// file. The book entry is copied to the first input parameter.
|
||||
|
||||
void Book::read_entry(BookEntry& entry, int idx) {
|
||||
|
||||
assert(idx >= 0 && idx < bookSize);
|
||||
assert(is_open());
|
||||
|
||||
seekg(idx * EntrySize, ios_base::beg);
|
||||
*this >> entry;
|
||||
if (!good())
|
||||
{
|
||||
cerr << "Failed to read book entry at index " << idx << endl;
|
||||
Application::exit_with_failure();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Book::read_integer() reads size chars from the file stream
|
||||
/// and converts them in an integer number.
|
||||
|
||||
uint64_t Book::read_integer(int size) {
|
||||
|
||||
char buf[8];
|
||||
read(buf, size);
|
||||
|
||||
// Numbers are stored on disk as a binary byte stream
|
||||
uint64_t n = 0ULL;
|
||||
for (int i = 0; i < size; i++)
|
||||
n = (n << 8) + (unsigned char)buf[i];
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
uint64_t book_key(const Position& pos) {
|
||||
|
||||
uint64_t result = 0ULL;
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; c++)
|
||||
{
|
||||
Bitboard b = pos.pieces_of_color(c);
|
||||
|
||||
while (b)
|
||||
{
|
||||
Square s = pop_1st_bit(&b);
|
||||
Piece p = pos.piece_on(s);
|
||||
|
||||
assert(piece_is_ok(p));
|
||||
assert(color_of_piece(p) == c);
|
||||
|
||||
result ^= book_piece_key(p, s);
|
||||
}
|
||||
}
|
||||
result ^= book_castle_key(pos);
|
||||
result ^= book_ep_key(pos);
|
||||
result ^= book_color_key(pos);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
uint64_t book_piece_key(Piece p, Square s) {
|
||||
|
||||
/// Convert pieces to the range 0..11
|
||||
static const int PieceTo12[] = { 0, 0, 2, 4, 6, 8, 10, 0, 0, 1, 3, 5, 7, 9, 11 };
|
||||
|
||||
return Random64[RandomPiece + (PieceTo12[int(p)]^1) * 64 + int(s)];
|
||||
}
|
||||
|
||||
|
||||
uint64_t book_castle_key(const Position& pos) {
|
||||
|
||||
uint64_t result = 0ULL;
|
||||
|
||||
if (pos.can_castle_kingside(WHITE))
|
||||
result ^= Random64[RandomCastle+0];
|
||||
|
||||
if (pos.can_castle_queenside(WHITE))
|
||||
result ^= Random64[RandomCastle+1];
|
||||
|
||||
if (pos.can_castle_kingside(BLACK))
|
||||
result ^= Random64[RandomCastle+2];
|
||||
|
||||
if (pos.can_castle_queenside(BLACK))
|
||||
result ^= Random64[RandomCastle+3];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
uint64_t book_ep_key(const Position& pos) {
|
||||
return (pos.ep_square() == SQ_NONE ? 0ULL : Random64[RandomEnPassant + square_file(pos.ep_square())]);
|
||||
}
|
||||
|
||||
|
||||
uint64_t book_color_key(const Position& pos) {
|
||||
return (pos.side_to_move() == WHITE ? Random64[RandomTurn] : 0ULL);
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
The code in this file is based on the opening book code in PolyGlot
|
||||
by Fabien Letouzey. PolyGlot is available under the GNU General
|
||||
Public License, and can be downloaded from http://wbec-ridderkerk.nl
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(BOOK_H_INCLUDED)
|
||||
#define BOOK_H_INCLUDED
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "move.h"
|
||||
#include "position.h"
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
struct BookEntry {
|
||||
uint64_t key;
|
||||
uint16_t move;
|
||||
uint16_t count;
|
||||
uint16_t n;
|
||||
uint16_t sum;
|
||||
};
|
||||
|
||||
class Book : private std::ifstream {
|
||||
public:
|
||||
~Book();
|
||||
void open(const std::string& fName);
|
||||
void close();
|
||||
const std::string file_name() const;
|
||||
Move get_move(const Position& pos);
|
||||
|
||||
private:
|
||||
Book& operator>>(uint64_t& n) { n = read_integer(8); return *this; }
|
||||
Book& operator>>(uint16_t& n) { n = (uint16_t)read_integer(2); return *this; }
|
||||
void operator>>(BookEntry& e) { *this >> e.key >> e.move >> e.count >> e.n >> e.sum; }
|
||||
|
||||
uint64_t read_integer(int size);
|
||||
void read_entry(BookEntry& e, int n);
|
||||
int find_key(uint64_t key);
|
||||
|
||||
std::string fileName;
|
||||
int bookSize;
|
||||
};
|
||||
|
||||
|
||||
////
|
||||
//// Global variables
|
||||
////
|
||||
|
||||
extern Book OpeningBook;
|
||||
|
||||
|
||||
#endif // !defined(BOOK_H_INCLUDED)
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(COLOR_H_INCLUDED)
|
||||
#define COLOR_H_INCLUDED
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
enum Color {
|
||||
WHITE,
|
||||
BLACK,
|
||||
COLOR_NONE
|
||||
};
|
||||
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
inline void operator++ (Color &c, int) { c = Color(int(c) + 1); }
|
||||
|
||||
inline Color opposite_color(Color c) {
|
||||
return Color(int(c) ^ 1);
|
||||
}
|
||||
|
||||
inline bool color_is_ok(Color c) {
|
||||
return c == WHITE || c == BLACK;
|
||||
}
|
||||
|
||||
#endif // !defined(COLOR_H_INCLUDED)
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(DEPTH_H_INCLUDED)
|
||||
#define DEPTH_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
enum Depth {
|
||||
DEPTH_ZERO = 0,
|
||||
DEPTH_MAX = 200 // 100 * OnePly;
|
||||
};
|
||||
|
||||
|
||||
////
|
||||
//// Constants
|
||||
////
|
||||
|
||||
const Depth OnePly = Depth(2);
|
||||
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
inline Depth operator+ (Depth d, int i) { return Depth(int(d) + i); }
|
||||
inline Depth operator+ (Depth d1, Depth d2) { return Depth(int(d1) + int(d2)); }
|
||||
inline void operator+= (Depth &d, int i) { d = Depth(int(d) + i); }
|
||||
inline void operator+= (Depth &d1, Depth d2) { d1 += int(d2); }
|
||||
inline Depth operator- (Depth d, int i) { return Depth(int(d) - i); }
|
||||
inline Depth operator- (Depth d1, Depth d2) { return Depth(int(d1) - int(d2)); }
|
||||
inline void operator-= (Depth & d, int i) { d = Depth(int(d) - i); }
|
||||
inline Depth operator* (Depth d, int i) { return Depth(int(d) * i); }
|
||||
inline Depth operator* (int i, Depth d) { return Depth(int(d) * i); }
|
||||
inline void operator*= (Depth &d, int i) { d = Depth(int(d) * i); }
|
||||
inline Depth operator/ (Depth d, int i) { return Depth(int(d) / i); }
|
||||
inline void operator/= (Depth &d, int i) { d = Depth(int(d) / i); }
|
||||
|
||||
|
||||
#endif // !defined(DEPTH_H_INCLUDED)
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "direction.h"
|
||||
#include "square.h"
|
||||
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
const SquareDelta directionToDelta[] = {
|
||||
DELTA_E, DELTA_W, DELTA_N, DELTA_S, DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE
|
||||
};
|
||||
|
||||
bool reachable(Square orig, Square dest, SignedDirection dir) {
|
||||
|
||||
SquareDelta delta = directionToDelta[dir];
|
||||
Square from = orig;
|
||||
Square to = from + delta;
|
||||
while (to != dest && square_distance(to, from) == 1 && square_is_ok(to))
|
||||
{
|
||||
from = to;
|
||||
to += delta;
|
||||
}
|
||||
return (to == dest && square_distance(from, to) == 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// Variables
|
||||
////
|
||||
|
||||
uint8_t DirectionTable[64][64];
|
||||
uint8_t SignedDirectionTable[64][64];
|
||||
|
||||
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
void init_direction_table() {
|
||||
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
|
||||
{
|
||||
DirectionTable[s1][s2] = uint8_t(DIR_NONE);
|
||||
SignedDirectionTable[s1][s2] = uint8_t(SIGNED_DIR_NONE);
|
||||
if (s1 == s2)
|
||||
continue;
|
||||
|
||||
for (SignedDirection d = SIGNED_DIR_E; d != SIGNED_DIR_NONE; d++)
|
||||
{
|
||||
if (reachable(s1, s2, d))
|
||||
{
|
||||
SignedDirectionTable[s1][s2] = uint8_t(d);
|
||||
DirectionTable[s1][s2] = uint8_t(d / 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(DIRECTION_H_INCLUDED)
|
||||
#define DIRECTION_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "square.h"
|
||||
#include "types.h"
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
enum Direction {
|
||||
DIR_E = 0, DIR_N = 1, DIR_NE = 2, DIR_NW = 3, DIR_NONE = 4
|
||||
};
|
||||
|
||||
enum SignedDirection {
|
||||
SIGNED_DIR_E = 0, SIGNED_DIR_W = 1,
|
||||
SIGNED_DIR_N = 2, SIGNED_DIR_S = 3,
|
||||
SIGNED_DIR_NE = 4, SIGNED_DIR_SW = 5,
|
||||
SIGNED_DIR_NW = 6, SIGNED_DIR_SE = 7,
|
||||
SIGNED_DIR_NONE = 8
|
||||
};
|
||||
|
||||
|
||||
////
|
||||
//// Variables
|
||||
////
|
||||
|
||||
extern uint8_t DirectionTable[64][64];
|
||||
extern uint8_t SignedDirectionTable[64][64];
|
||||
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
inline void operator++ (Direction& d, int) {
|
||||
d = Direction(int(d) + 1);
|
||||
}
|
||||
|
||||
inline void operator++ (SignedDirection& d, int) {
|
||||
d = SignedDirection(int(d) + 1);
|
||||
}
|
||||
|
||||
inline Direction direction_between_squares(Square s1, Square s2) {
|
||||
return Direction(DirectionTable[s1][s2]);
|
||||
}
|
||||
|
||||
inline SignedDirection signed_direction_between_squares(Square s1, Square s2) {
|
||||
return SignedDirection(SignedDirectionTable[s1][s2]);
|
||||
}
|
||||
|
||||
inline int direction_is_diagonal(Square s1, Square s2) {
|
||||
return DirectionTable[s1][s2] & 2;
|
||||
}
|
||||
|
||||
inline bool direction_is_straight(Square s1, Square s2) {
|
||||
return DirectionTable[s1][s2] < 2;
|
||||
}
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern void init_direction_table();
|
||||
|
||||
|
||||
#endif // !defined(DIRECTION_H_INCLUDED)
|
||||
+656
-491
File diff suppressed because it is too large
Load Diff
+68
-88
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,111 +17,90 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ENDGAME_H_INCLUDED
|
||||
|
||||
#if !defined(ENDGAME_H_INCLUDED)
|
||||
#define ENDGAME_H_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "position.h"
|
||||
#include "types.h"
|
||||
#include "scale.h"
|
||||
#include "value.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
/// EndgameCode lists all supported endgame functions by corresponding codes
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
enum EndgameCode {
|
||||
enum EndgameType {
|
||||
|
||||
EVALUATION_FUNCTIONS,
|
||||
KNNK, // KNN vs K
|
||||
KNNKP, // KNN vs KP
|
||||
KXK, // Generic "mate lone king" eval
|
||||
KBNK, // KBN vs K
|
||||
KPK, // KP vs K
|
||||
KRKP, // KR vs KP
|
||||
KRKB, // KR vs KB
|
||||
KRKN, // KR vs KN
|
||||
KQKP, // KQ vs KP
|
||||
KQKR, // KQ vs KR
|
||||
// Evaluation functions
|
||||
KXK, // Generic "mate lone king" eval
|
||||
KBNK, // KBN vs K
|
||||
KPK, // KP vs K
|
||||
KRKP, // KR vs KP
|
||||
KRKB, // KR vs KB
|
||||
KRKN, // KR vs KN
|
||||
KQKR, // KQ vs KR
|
||||
KBBKN, // KBB vs KN
|
||||
KNNK, // KNN vs K
|
||||
KmmKm, // K and two minors vs K and one or two minors
|
||||
|
||||
SCALING_FUNCTIONS,
|
||||
KBPsK, // KB and pawns vs K
|
||||
KQKRPs, // KQ vs KR and pawns
|
||||
KRPKR, // KRP vs KR
|
||||
KRPKB, // KRP vs KB
|
||||
KRPPKRP, // KRPP vs KRP
|
||||
KPsK, // K and pawns vs K
|
||||
KBPKB, // KBP vs KB
|
||||
KBPPKB, // KBPP vs KB
|
||||
KBPKN, // KBP vs KN
|
||||
KPKP // KP vs KP
|
||||
// Scaling functions
|
||||
KBPsK, // KB+pawns vs K
|
||||
KQKRPs, // KQ vs KR+pawns
|
||||
KRPKR, // KRP vs KR
|
||||
KRPPKRP, // KRPP vs KRP
|
||||
KPsK, // King and pawns vs king
|
||||
KBPKB, // KBP vs KB
|
||||
KBPPKB, // KBPP vs KB
|
||||
KBPKN, // KBP vs KN
|
||||
KNPK, // KNP vs K
|
||||
KPKP // KP vs KP
|
||||
};
|
||||
|
||||
|
||||
/// Endgame functions can be of two types depending on whether they return a
|
||||
/// Value or a ScaleFactor.
|
||||
|
||||
template<EndgameCode E> using
|
||||
eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type;
|
||||
|
||||
|
||||
/// Base and derived functors for endgame evaluation and scaling functions
|
||||
/// Template abstract base class for all special endgame functions
|
||||
|
||||
template<typename T>
|
||||
struct EndgameBase {
|
||||
class EndgameFunctionBase {
|
||||
public:
|
||||
EndgameFunctionBase(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {}
|
||||
virtual ~EndgameFunctionBase() {}
|
||||
virtual T apply(const Position&) = 0;
|
||||
Color color() const { return strongerSide; }
|
||||
|
||||
explicit EndgameBase(Color c) : strongSide(c), weakSide(~c) {}
|
||||
virtual ~EndgameBase() = default;
|
||||
virtual T operator()(const Position&) const = 0;
|
||||
protected:
|
||||
Color strongerSide, weakerSide;
|
||||
};
|
||||
|
||||
const Color strongSide, weakSide;
|
||||
typedef EndgameFunctionBase<Value> EndgameEvaluationFunctionBase;
|
||||
typedef EndgameFunctionBase<ScaleFactor> EndgameScalingFunctionBase;
|
||||
|
||||
|
||||
/// Templates subclass for various concrete endgames
|
||||
|
||||
template<EndgameType>
|
||||
struct EvaluationFunction : public EndgameEvaluationFunctionBase {
|
||||
typedef EndgameEvaluationFunctionBase Base;
|
||||
explicit EvaluationFunction(Color c): EndgameEvaluationFunctionBase(c) {}
|
||||
Value apply(const Position&);
|
||||
};
|
||||
|
||||
template<EndgameType>
|
||||
struct ScalingFunction : public EndgameScalingFunctionBase {
|
||||
typedef EndgameScalingFunctionBase Base;
|
||||
explicit ScalingFunction(Color c) : EndgameScalingFunctionBase(c) {}
|
||||
ScaleFactor apply(const Position&);
|
||||
};
|
||||
|
||||
|
||||
template<EndgameCode E, typename T = eg_type<E>>
|
||||
struct Endgame : public EndgameBase<T> {
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
explicit Endgame(Color c) : EndgameBase<T>(c) {}
|
||||
T operator()(const Position&) const override;
|
||||
};
|
||||
extern void init_bitbases();
|
||||
|
||||
|
||||
/// The Endgames namespace handles the pointers to endgame evaluation and scaling
|
||||
/// base objects in two std::map. We use polymorphism to invoke the actual
|
||||
/// endgame function by calling its virtual operator().
|
||||
|
||||
namespace Endgames {
|
||||
|
||||
template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>;
|
||||
template<typename T> using Map = std::unordered_map<Key, Ptr<T>>;
|
||||
|
||||
extern std::pair<Map<Value>, Map<ScaleFactor>> maps;
|
||||
|
||||
void init();
|
||||
|
||||
template<typename T>
|
||||
Map<T>& map() {
|
||||
return std::get<std::is_same<T, ScaleFactor>::value>(maps);
|
||||
}
|
||||
|
||||
template<EndgameCode E, typename T = eg_type<E>>
|
||||
void add(const std::string& code) {
|
||||
|
||||
StateInfo st;
|
||||
map<T>()[Position().set(code, WHITE, &st).material_key()] = Ptr<T>(new Endgame<E>(WHITE));
|
||||
map<T>()[Position().set(code, BLACK, &st).material_key()] = Ptr<T>(new Endgame<E>(BLACK));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const EndgameBase<T>* probe(Key key) {
|
||||
auto it = map<T>().find(key);
|
||||
return it != map<T>().end() ? it->second.get() : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef ENDGAME_H_INCLUDED
|
||||
#endif // !defined(ENDGAME_H_INCLUDED)
|
||||
|
||||
+1115
-1056
File diff suppressed because it is too large
Load Diff
+78
-27
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,47 +17,97 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef EVALUATE_H_INCLUDED
|
||||
|
||||
#if !defined(EVALUATE_H_INCLUDED)
|
||||
#define EVALUATE_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "types.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace Stockfish {
|
||||
#include "material.h"
|
||||
#include "pawns.h"
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
|
||||
/// The EvalInfo struct contains various information computed and collected
|
||||
/// by the evaluation function. An EvalInfo object is passed as one of the
|
||||
/// arguments to the evaluation function, and the search can make use of its
|
||||
/// contents to make intelligent search decisions.
|
||||
///
|
||||
/// At the moment, this is not utilized very much: The only part of the
|
||||
/// EvalInfo object which is used by the search is futilityMargin.
|
||||
class Position;
|
||||
|
||||
namespace Eval {
|
||||
struct EvalInfo {
|
||||
|
||||
std::string trace(Position& pos);
|
||||
Value evaluate(const Position& pos);
|
||||
// Middle game and endgame evaluations
|
||||
Score value;
|
||||
|
||||
extern bool useNNUE;
|
||||
extern std::string currentEvalFileName;
|
||||
// Pointers to material and pawn hash table entries
|
||||
MaterialInfo* mi;
|
||||
PawnInfo* pi;
|
||||
|
||||
// The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue
|
||||
// for the build process (profile-build and fishtest) to work. Do not change the
|
||||
// name of the macro, as it is used in the Makefile.
|
||||
#define EvalFileDefaultName "nn-6877cd24400e.nnue"
|
||||
// attackedBy[color][piece type] is a bitboard representing all squares
|
||||
// attacked by a given color and piece type, attackedBy[color][0] contains
|
||||
// all squares attacked by the given color.
|
||||
Bitboard attackedBy[2][8];
|
||||
Bitboard attacked_by(Color c) const { return attackedBy[c][0]; }
|
||||
Bitboard attacked_by(Color c, PieceType pt) const { return attackedBy[c][pt]; }
|
||||
|
||||
namespace NNUE {
|
||||
// kingZone[color] is the zone around the enemy king which is considered
|
||||
// by the king safety evaluation. This consists of the squares directly
|
||||
// adjacent to the king, and the three (or two, for a king on an edge file)
|
||||
// squares two ranks in front of the king. For instance, if black's king
|
||||
// is on g8, kingZone[WHITE] is a bitboard containing the squares f8, h8,
|
||||
// f7, g7, h7, f6, g6 and h6.
|
||||
Bitboard kingZone[2];
|
||||
|
||||
std::string trace(Position& pos);
|
||||
Value evaluate(const Position& pos, bool adjusted = false);
|
||||
// kingAttackersCount[color] is the number of pieces of the given color
|
||||
// which attack a square in the kingZone of the enemy king.
|
||||
int kingAttackersCount[2];
|
||||
|
||||
void init();
|
||||
void verify();
|
||||
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
|
||||
// given color which attack a square in the kingZone of the enemy king. The
|
||||
// weights of the individual piece types are given by the variables
|
||||
// QueenAttackWeight, RookAttackWeight, BishopAttackWeight and
|
||||
// KnightAttackWeight in evaluate.cpp
|
||||
int kingAttackersWeight[2];
|
||||
|
||||
bool load_eval(std::string name, std::istream& stream);
|
||||
bool save_eval(std::ostream& stream);
|
||||
bool save_eval(const std::optional<std::string>& filename);
|
||||
// kingAdjacentZoneAttacksCount[color] is the number of attacks to squares
|
||||
// directly adjacent to the king of the given color. Pieces which attack
|
||||
// more than one square are counted multiple times. For instance, if black's
|
||||
// king is on g8 and there's a white knight on g5, this knight adds
|
||||
// 2 to kingAdjacentZoneAttacksCount[BLACK].
|
||||
int kingAdjacentZoneAttacksCount[2];
|
||||
|
||||
} // namespace NNUE
|
||||
// mateThreat[color] is a move for the given side which gives a direct mate.
|
||||
Move mateThreat[2];
|
||||
|
||||
} // namespace Eval
|
||||
// Middle game and endgame mobility scores.
|
||||
Score mobility;
|
||||
|
||||
} // namespace Stockfish
|
||||
// Extra futility margin. This is added to the standard futility margin
|
||||
// in the quiescence search.
|
||||
Value futilityMargin;
|
||||
};
|
||||
|
||||
#endif // #ifndef EVALUATE_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern Value evaluate(const Position& pos, EvalInfo& ei, int threadID);
|
||||
extern Value quick_evaluate(const Position& pos);
|
||||
extern void init_eval(int threads);
|
||||
extern void quit_eval();
|
||||
extern void read_weights(Color sideToMove);
|
||||
|
||||
|
||||
#endif // !defined(EVALUATE_H_INCLUDED)
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include "history.h"
|
||||
|
||||
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
|
||||
/// Constructor
|
||||
|
||||
History::History() { clear(); }
|
||||
|
||||
|
||||
/// History::clear() clears the history tables
|
||||
|
||||
void History::clear() {
|
||||
memset(history, 0, 2 * 8 * 64 * sizeof(int));
|
||||
}
|
||||
|
||||
|
||||
/// History::success() registers a move as being successful. This is done
|
||||
/// whenever a non-capturing move causes a beta cutoff in the main search.
|
||||
/// The three parameters are the moving piece, the destination square, and
|
||||
/// the search depth.
|
||||
|
||||
void History::success(Piece p, Square to, Depth d) {
|
||||
|
||||
assert(piece_is_ok(p));
|
||||
assert(square_is_ok(to));
|
||||
|
||||
history[p][to] += int(d) * int(d);
|
||||
|
||||
// Prevent history overflow
|
||||
if (history[p][to] >= HistoryMax)
|
||||
for (int i = 0; i < 16; i++)
|
||||
for (int j = 0; j < 64; j++)
|
||||
history[i][j] /= 4;
|
||||
}
|
||||
|
||||
|
||||
/// History::failure() registers a move as being unsuccessful. The function is
|
||||
/// called for each non-capturing move which failed to produce a beta cutoff
|
||||
/// at a node where a beta cutoff was finally found.
|
||||
|
||||
void History::failure(Piece p, Square to, Depth d) {
|
||||
|
||||
assert(piece_is_ok(p));
|
||||
assert(square_is_ok(to));
|
||||
|
||||
history[p][to] -= int(d) * int(d);
|
||||
if (history[p][to] < 0)
|
||||
history[p][to] = 0;
|
||||
}
|
||||
|
||||
|
||||
/// History::move_ordering_score() returns an integer value used to order the
|
||||
/// non-capturing moves in the MovePicker class.
|
||||
|
||||
int History::move_ordering_score(Piece p, Square to) const {
|
||||
|
||||
assert(piece_is_ok(p));
|
||||
assert(square_is_ok(to));
|
||||
|
||||
return history[p][to];
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(HISTORY_H_INCLUDED)
|
||||
#define HISTORY_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "depth.h"
|
||||
#include "move.h"
|
||||
#include "piece.h"
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
/// The History class stores statistics about how often different moves
|
||||
/// have been successful or unsuccessful during the current search. These
|
||||
/// statistics are used for reduction and move ordering decisions. History
|
||||
/// entries are stored according only to moving piece and destination square,
|
||||
/// in particular two moves with different origin but same destination and
|
||||
/// same piece will be considered identical.
|
||||
|
||||
class History {
|
||||
|
||||
public:
|
||||
History();
|
||||
void clear();
|
||||
void success(Piece p, Square to, Depth d);
|
||||
void failure(Piece p, Square to, Depth d);
|
||||
int move_ordering_score(Piece p, Square to) const;
|
||||
|
||||
private:
|
||||
int history[16][64]; // [piece][square]
|
||||
};
|
||||
|
||||
|
||||
////
|
||||
//// Constants and variables
|
||||
////
|
||||
|
||||
/// HistoryMax controls how often the history counters will be scaled down:
|
||||
/// When the history score for a move gets bigger than HistoryMax, all
|
||||
/// entries in the table are divided by 4. It is difficult to guess what
|
||||
/// the ideal value of this constant is. Scaling down the scores often has
|
||||
/// the effect that parts of the search tree which have been searched
|
||||
/// recently have a bigger importance for move ordering than the moves which
|
||||
/// have been searched a long time ago.
|
||||
|
||||
const int HistoryMax = 25000 * OnePly;
|
||||
|
||||
|
||||
#endif // !defined(HISTORY_H_INCLUDED)
|
||||
@@ -1,26 +0,0 @@
|
||||
The file "incbin.h" is free and unencumbered software released into
|
||||
the public domain by Dale Weiler, see:
|
||||
<https://github.com/graphitemaster/incbin>
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
@@ -1,368 +0,0 @@
|
||||
/**
|
||||
* @file incbin.h
|
||||
* @author Dale Weiler
|
||||
* @brief Utility for including binary files
|
||||
*
|
||||
* Facilities for including binary files into the current translation unit and
|
||||
* making use from them externally in other translation units.
|
||||
*/
|
||||
#ifndef INCBIN_HDR
|
||||
#define INCBIN_HDR
|
||||
#include <limits.h>
|
||||
#if defined(__AVX512BW__) || \
|
||||
defined(__AVX512CD__) || \
|
||||
defined(__AVX512DQ__) || \
|
||||
defined(__AVX512ER__) || \
|
||||
defined(__AVX512PF__) || \
|
||||
defined(__AVX512VL__) || \
|
||||
defined(__AVX512F__)
|
||||
# define INCBIN_ALIGNMENT_INDEX 6
|
||||
#elif defined(__AVX__) || \
|
||||
defined(__AVX2__)
|
||||
# define INCBIN_ALIGNMENT_INDEX 5
|
||||
#elif defined(__SSE__) || \
|
||||
defined(__SSE2__) || \
|
||||
defined(__SSE3__) || \
|
||||
defined(__SSSE3__) || \
|
||||
defined(__SSE4_1__) || \
|
||||
defined(__SSE4_2__) || \
|
||||
defined(__neon__)
|
||||
# define INCBIN_ALIGNMENT_INDEX 4
|
||||
#elif ULONG_MAX != 0xffffffffu
|
||||
# define INCBIN_ALIGNMENT_INDEX 3
|
||||
# else
|
||||
# define INCBIN_ALIGNMENT_INDEX 2
|
||||
#endif
|
||||
|
||||
/* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */
|
||||
#define INCBIN_ALIGN_SHIFT_0 1
|
||||
#define INCBIN_ALIGN_SHIFT_1 2
|
||||
#define INCBIN_ALIGN_SHIFT_2 4
|
||||
#define INCBIN_ALIGN_SHIFT_3 8
|
||||
#define INCBIN_ALIGN_SHIFT_4 16
|
||||
#define INCBIN_ALIGN_SHIFT_5 32
|
||||
#define INCBIN_ALIGN_SHIFT_6 64
|
||||
|
||||
/* Actual alignment value */
|
||||
#define INCBIN_ALIGNMENT \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), \
|
||||
INCBIN_ALIGNMENT_INDEX)
|
||||
|
||||
/* Stringize */
|
||||
#define INCBIN_STR(X) \
|
||||
#X
|
||||
#define INCBIN_STRINGIZE(X) \
|
||||
INCBIN_STR(X)
|
||||
/* Concatenate */
|
||||
#define INCBIN_CAT(X, Y) \
|
||||
X ## Y
|
||||
#define INCBIN_CONCATENATE(X, Y) \
|
||||
INCBIN_CAT(X, Y)
|
||||
/* Deferred macro expansion */
|
||||
#define INCBIN_EVAL(X) \
|
||||
X
|
||||
#define INCBIN_INVOKE(N, ...) \
|
||||
INCBIN_EVAL(N(__VA_ARGS__))
|
||||
|
||||
/* Green Hills uses a different directive for including binary data */
|
||||
#if defined(__ghs__)
|
||||
# if (__ghs_asm == 2)
|
||||
# define INCBIN_MACRO ".file"
|
||||
/* Or consider the ".myrawdata" entry in the ld file */
|
||||
# else
|
||||
# define INCBIN_MACRO "\tINCBIN"
|
||||
# endif
|
||||
#else
|
||||
# define INCBIN_MACRO ".incbin"
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# define INCBIN_ALIGN \
|
||||
__attribute__((aligned(INCBIN_ALIGNMENT)))
|
||||
#else
|
||||
# define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT))
|
||||
#endif
|
||||
|
||||
#if defined(__arm__) || /* GNU C and RealView */ \
|
||||
defined(__arm) || /* Diab */ \
|
||||
defined(_ARM) /* ImageCraft */
|
||||
# define INCBIN_ARM
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* Utilize .balign where supported */
|
||||
# define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
|
||||
# define INCBIN_ALIGN_BYTE ".balign 1\n"
|
||||
#elif defined(INCBIN_ARM)
|
||||
/*
|
||||
* On arm assemblers, the alignment value is calculated as (1 << n) where `n' is
|
||||
* the shift count. This is the value passed to `.align'
|
||||
*/
|
||||
# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n"
|
||||
# define INCBIN_ALIGN_BYTE ".align 0\n"
|
||||
#else
|
||||
/* We assume other inline assembler's treat `.align' as `.balign' */
|
||||
# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
|
||||
# define INCBIN_ALIGN_BYTE ".align 1\n"
|
||||
#endif
|
||||
|
||||
/* INCBIN_CONST is used by incbin.c generated files */
|
||||
#if defined(__cplusplus)
|
||||
# define INCBIN_EXTERNAL extern "C"
|
||||
# define INCBIN_CONST extern const
|
||||
#else
|
||||
# define INCBIN_EXTERNAL extern
|
||||
# define INCBIN_CONST const
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Optionally override the linker section into which data is emitted.
|
||||
*
|
||||
* @warning If you use this facility, you'll have to deal with platform-specific linker output
|
||||
* section naming on your own
|
||||
*
|
||||
* Overriding the default linker output section, e.g for esp8266/Arduino:
|
||||
* @code
|
||||
* #define INCBIN_OUTPUT_SECTION ".irom.text"
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
* // Data is emitted into program memory that never gets copied to RAM
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_OUTPUT_SECTION)
|
||||
# if defined(__APPLE__)
|
||||
# define INCBIN_OUTPUT_SECTION ".const_data"
|
||||
# else
|
||||
# define INCBIN_OUTPUT_SECTION ".rodata"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* The directives are different for Apple branded compilers */
|
||||
# define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n"
|
||||
# define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
|
||||
# define INCBIN_INT ".long "
|
||||
# define INCBIN_MANGLE "_"
|
||||
# define INCBIN_BYTE ".byte "
|
||||
# define INCBIN_TYPE(...)
|
||||
#else
|
||||
# define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n"
|
||||
# define INCBIN_GLOBAL(NAME) ".global " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
|
||||
# if defined(__ghs__)
|
||||
# define INCBIN_INT ".word "
|
||||
# else
|
||||
# define INCBIN_INT ".int "
|
||||
# endif
|
||||
# if defined(__USER_LABEL_PREFIX__)
|
||||
# define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__)
|
||||
# else
|
||||
# define INCBIN_MANGLE ""
|
||||
# endif
|
||||
# if defined(INCBIN_ARM)
|
||||
/* On arm assemblers, `@' is used as a line comment token */
|
||||
# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n"
|
||||
# elif defined(__MINGW32__) || defined(__MINGW64__)
|
||||
/* Mingw doesn't support this directive either */
|
||||
# define INCBIN_TYPE(NAME)
|
||||
# else
|
||||
/* It's safe to use `@' on other architectures */
|
||||
# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n"
|
||||
# endif
|
||||
# define INCBIN_BYTE ".byte "
|
||||
#endif
|
||||
|
||||
/* List of style types used for symbol names */
|
||||
#define INCBIN_STYLE_CAMEL 0
|
||||
#define INCBIN_STYLE_SNAKE 1
|
||||
|
||||
/**
|
||||
* @brief Specify the prefix to use for symbol names.
|
||||
*
|
||||
* By default this is `g', producing symbols of the form:
|
||||
* @code
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char gFooData[];
|
||||
* // const unsigned char *const gFooEnd;
|
||||
* // const unsigned int gFooSize;
|
||||
* @endcode
|
||||
*
|
||||
* If however you specify a prefix before including: e.g:
|
||||
* @code
|
||||
* #define INCBIN_PREFIX incbin
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols instead:
|
||||
* // const unsigned char incbinFooData[];
|
||||
* // const unsigned char *const incbinFooEnd;
|
||||
* // const unsigned int incbinFooSize;
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_PREFIX)
|
||||
# define INCBIN_PREFIX g
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Specify the style used for symbol names.
|
||||
*
|
||||
* Possible options are
|
||||
* - INCBIN_STYLE_CAMEL "CamelCase"
|
||||
* - INCBIN_STYLE_SNAKE "snake_case"
|
||||
*
|
||||
* Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form:
|
||||
* @code
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>FooData[];
|
||||
* // const unsigned char *const <prefix>FooEnd;
|
||||
* // const unsigned int <prefix>FooSize;
|
||||
* @endcode
|
||||
*
|
||||
* If however you specify a style before including: e.g:
|
||||
* @code
|
||||
* #define INCBIN_STYLE INCBIN_STYLE_SNAKE
|
||||
* #include "incbin.h"
|
||||
* INCBIN(foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>foo_data[];
|
||||
* // const unsigned char *const <prefix>foo_end;
|
||||
* // const unsigned int <prefix>foo_size;
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_STYLE)
|
||||
# define INCBIN_STYLE INCBIN_STYLE_CAMEL
|
||||
#endif
|
||||
|
||||
/* Style lookup tables */
|
||||
#define INCBIN_STYLE_0_DATA Data
|
||||
#define INCBIN_STYLE_0_END End
|
||||
#define INCBIN_STYLE_0_SIZE Size
|
||||
#define INCBIN_STYLE_1_DATA _data
|
||||
#define INCBIN_STYLE_1_END _end
|
||||
#define INCBIN_STYLE_1_SIZE _size
|
||||
|
||||
/* Style lookup: returning identifier */
|
||||
#define INCBIN_STYLE_IDENT(TYPE) \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_STYLE_, \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_EVAL(INCBIN_STYLE), \
|
||||
INCBIN_CONCATENATE(_, TYPE)))
|
||||
|
||||
/* Style lookup: returning string literal */
|
||||
#define INCBIN_STYLE_STRING(TYPE) \
|
||||
INCBIN_STRINGIZE( \
|
||||
INCBIN_STYLE_IDENT(TYPE)) \
|
||||
|
||||
/* Generate the global labels by indirectly invoking the macro with our style
|
||||
* type and concatenating the name against them. */
|
||||
#define INCBIN_GLOBAL_LABELS(NAME, TYPE) \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_GLOBAL, \
|
||||
INCBIN_CONCATENATE( \
|
||||
NAME, \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_STYLE_IDENT, \
|
||||
TYPE))) \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_TYPE, \
|
||||
INCBIN_CONCATENATE( \
|
||||
NAME, \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_STYLE_IDENT, \
|
||||
TYPE)))
|
||||
|
||||
/**
|
||||
* @brief Externally reference binary data included in another translation unit.
|
||||
*
|
||||
* Produces three external symbols that reference the binary data included in
|
||||
* another translation unit.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param NAME The name given for the binary data
|
||||
*
|
||||
* @code
|
||||
* INCBIN_EXTERN(Foo);
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // extern const unsigned char <prefix>FooData[];
|
||||
* // extern const unsigned char *const <prefix>FooEnd;
|
||||
* // extern const unsigned int <prefix>FooSize;
|
||||
* @endcode
|
||||
*/
|
||||
#define INCBIN_EXTERN(NAME) \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(DATA))[]; \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(END)); \
|
||||
INCBIN_EXTERNAL const unsigned int \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(SIZE))
|
||||
|
||||
/**
|
||||
* @brief Include a binary file into the current translation unit.
|
||||
*
|
||||
* Includes a binary file into the current translation unit, producing three symbols
|
||||
* for objects that encode the data and size respectively.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param NAME The name to associate with this binary data (as an identifier.)
|
||||
* @param FILENAME The file to include (as a string literal.)
|
||||
*
|
||||
* @code
|
||||
* INCBIN(Icon, "icon.png");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>IconData[];
|
||||
* // const unsigned char *const <prefix>IconEnd;
|
||||
* // const unsigned int <prefix>IconSize;
|
||||
* @endcode
|
||||
*
|
||||
* @warning This must be used in global scope
|
||||
* @warning The identifiers may be different if INCBIN_STYLE is not default
|
||||
*
|
||||
* To externally reference the data included by this in another translation unit
|
||||
* please @see INCBIN_EXTERN.
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
#define INCBIN(NAME, FILENAME) \
|
||||
INCBIN_EXTERN(NAME)
|
||||
#else
|
||||
#define INCBIN(NAME, FILENAME) \
|
||||
__asm__(INCBIN_SECTION \
|
||||
INCBIN_GLOBAL_LABELS(NAME, DATA) \
|
||||
INCBIN_ALIGN_HOST \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \
|
||||
INCBIN_MACRO " \"" FILENAME "\"\n" \
|
||||
INCBIN_GLOBAL_LABELS(NAME, END) \
|
||||
INCBIN_ALIGN_BYTE \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \
|
||||
INCBIN_BYTE "1\n" \
|
||||
INCBIN_GLOBAL_LABELS(NAME, SIZE) \
|
||||
INCBIN_ALIGN_HOST \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE) ":\n" \
|
||||
INCBIN_INT INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) " - " \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) "\n" \
|
||||
INCBIN_ALIGN_HOST \
|
||||
".text\n" \
|
||||
); \
|
||||
INCBIN_EXTERN(NAME)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(LOCK_H_INCLUDED)
|
||||
#define LOCK_H_INCLUDED
|
||||
|
||||
|
||||
// x86 assembly language locks or OS spin locks may perform faster than
|
||||
// mutex locks on some platforms. On my machine, mutexes seem to be the
|
||||
// best.
|
||||
|
||||
//#define ASM_LOCK
|
||||
//#define OS_SPIN_LOCK
|
||||
|
||||
|
||||
#if defined(ASM_LOCK)
|
||||
|
||||
|
||||
typedef volatile int Lock;
|
||||
|
||||
static inline void LockX86(Lock *lock) {
|
||||
int dummy;
|
||||
asm __volatile__("1: movl $1, %0" "\n\t"
|
||||
" xchgl (%1), %0" "\n\t" " testl %0, %0" "\n\t"
|
||||
" jz 3f" "\n\t" "2: pause" "\n\t"
|
||||
" movl (%1), %0" "\n\t" " testl %0, %0" "\n\t"
|
||||
" jnz 2b" "\n\t" " jmp 1b" "\n\t" "3:"
|
||||
"\n\t":"=&q"(dummy)
|
||||
:"q"(lock)
|
||||
:"cc");
|
||||
}
|
||||
|
||||
static inline void UnlockX86(Lock *lock) {
|
||||
int dummy;
|
||||
asm __volatile__("movl $0, (%1)":"=&q"(dummy)
|
||||
:"q"(lock));
|
||||
}
|
||||
|
||||
# define lock_init(x, y) (*(x) = 0)
|
||||
# define lock_grab(x) LockX86(x)
|
||||
# define lock_release(x) UnlockX86(x)
|
||||
# define lock_destroy(x)
|
||||
|
||||
|
||||
#elif defined(OS_SPIN_LOCK)
|
||||
|
||||
|
||||
# include <libkern/OSAtomic.h>
|
||||
|
||||
typedef OSSpinLock Lock;
|
||||
|
||||
# define lock_init(x, y) (*(x) = 0)
|
||||
# define lock_grab(x) OSSpinLockLock(x)
|
||||
# define lock_release(x) OSSpinLockUnlock(x)
|
||||
# define lock_destroy(x)
|
||||
|
||||
|
||||
#elif !defined(_MSC_VER)
|
||||
|
||||
# include <pthread.h>
|
||||
|
||||
typedef pthread_mutex_t Lock;
|
||||
|
||||
# define lock_init(x, y) pthread_mutex_init(x, y)
|
||||
# define lock_grab(x) pthread_mutex_lock(x)
|
||||
# define lock_release(x) pthread_mutex_unlock(x)
|
||||
# define lock_destroy(x) pthread_mutex_destroy(x)
|
||||
|
||||
|
||||
#else
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
|
||||
typedef CRITICAL_SECTION Lock;
|
||||
# define lock_init(x, y) InitializeCriticalSection(x)
|
||||
# define lock_grab(x) EnterCriticalSection(x)
|
||||
# define lock_release(x) LeaveCriticalSection(x)
|
||||
# define lock_destroy(x) DeleteCriticalSection(x)
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // !defined(LOCK_H_INCLUDED)
|
||||
+62
-26
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,38 +17,73 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
// To profile with callgrind uncomment following line
|
||||
//#define USE_CALLGRIND
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "endgame.h"
|
||||
#include "position.h"
|
||||
#include "psqt.h"
|
||||
#include "search.h"
|
||||
#include "syzygy/tbprobe.h"
|
||||
#include "thread.h"
|
||||
#include "tt.h"
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "bitcount.h"
|
||||
#include "misc.h"
|
||||
#include "uci.h"
|
||||
|
||||
using namespace Stockfish;
|
||||
#ifdef USE_CALLGRIND
|
||||
#include <valgrind/callgrind.h>
|
||||
#endif
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
using namespace std;
|
||||
|
||||
std::cout << engine_info() << std::endl;
|
||||
|
||||
CommandLine::init(argc, argv);
|
||||
UCI::init(Options);
|
||||
Tune::init();
|
||||
PSQT::init();
|
||||
Bitboards::init();
|
||||
Position::init();
|
||||
Bitbases::init();
|
||||
Endgames::init();
|
||||
Threads.set(size_t(Options["Threads"]));
|
||||
Search::clear(); // After threads are up
|
||||
Eval::NNUE::init();
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
UCI::loop(argc, argv);
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
Threads.set(0);
|
||||
// Disable IO buffering
|
||||
cout.rdbuf()->pubsetbuf(NULL, 0);
|
||||
cin.rdbuf()->pubsetbuf(NULL, 0);
|
||||
|
||||
// Initialization through global resources manager
|
||||
Application::initialize();
|
||||
|
||||
#ifdef USE_CALLGRIND
|
||||
CALLGRIND_START_INSTRUMENTATION;
|
||||
#endif
|
||||
|
||||
// Process command line arguments if any
|
||||
if (argc > 1)
|
||||
{
|
||||
if (string(argv[1]) != "bench" || argc < 4 || argc > 8)
|
||||
cout << "Usage: stockfish bench <hash size> <threads> "
|
||||
<< "[time = 60s] [fen positions file = default] "
|
||||
<< "[time, depth, perft or node limited = time] "
|
||||
<< "[timing file name = none]" << endl;
|
||||
else
|
||||
{
|
||||
string time = argc > 4 ? argv[4] : "60";
|
||||
string fen = argc > 5 ? argv[5] : "default";
|
||||
string lim = argc > 6 ? argv[6] : "time";
|
||||
string tim = argc > 7 ? argv[7] : "";
|
||||
benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen + " " + lim + " " + tim);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Print copyright notice
|
||||
cout << engine_name()
|
||||
<< ". By Tord Romstad, Marco Costalba, Joona Kiiski." << endl;
|
||||
|
||||
if (CpuHasPOPCNT)
|
||||
cout << "Good! CPU has hardware POPCNT. We will use it." << endl;
|
||||
|
||||
// Enter UCI mode
|
||||
uci_main_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
+359
-158
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,214 +17,414 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring> // For std::memset
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include "material.h"
|
||||
#include "thread.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
|
||||
namespace {
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
// Polynomial material imbalance parameters
|
||||
// Values modified by Joona Kiiski
|
||||
const Value MidgameLimit = Value(15581);
|
||||
const Value EndgameLimit = Value(3998);
|
||||
|
||||
// One Score parameter for each pair (our piece, another of our pieces)
|
||||
constexpr Score QuadraticOurs[][PIECE_TYPE_NB] = {
|
||||
// OUR PIECE 2
|
||||
// bishop pair pawn knight bishop rook queen
|
||||
{S(1419, 1455) }, // Bishop pair
|
||||
{S( 101, 28), S( 37, 39) }, // Pawn
|
||||
{S( 57, 64), S(249, 187), S(-49, -62) }, // Knight OUR PIECE 1
|
||||
{S( 0, 0), S(118, 137), S( 10, 27), S( 0, 0) }, // Bishop
|
||||
{S( -63, -68), S( -5, 3), S(100, 81), S(132, 118), S(-246, -244) }, // Rook
|
||||
{S(-210, -211), S( 37, 14), S(147, 141), S(161, 105), S(-158, -174), S(-9,-31) } // Queen
|
||||
};
|
||||
// Polynomial material balance parameters
|
||||
const Value RedundantQueenPenalty = Value(320);
|
||||
const Value RedundantRookPenalty = Value(554);
|
||||
|
||||
// One Score parameter for each pair (our piece, their piece)
|
||||
constexpr Score QuadraticTheirs[][PIECE_TYPE_NB] = {
|
||||
// THEIR PIECE
|
||||
// bishop pair pawn knight bishop rook queen
|
||||
{ }, // Bishop pair
|
||||
{S( 33, 30) }, // Pawn
|
||||
{S( 46, 18), S(106, 84) }, // Knight OUR PIECE
|
||||
{S( 75, 35), S( 59, 44), S( 60, 15) }, // Bishop
|
||||
{S( 26, 35), S( 6, 22), S( 38, 39), S(-12, -2) }, // Rook
|
||||
{S( 97, 93), S(100, 163), S(-58, -91), S(112, 192), S(276, 225) } // Queen
|
||||
};
|
||||
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
|
||||
|
||||
#undef S
|
||||
const int QuadraticCoefficientsSameColor[][6] = {
|
||||
{ 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 },
|
||||
{ 7, 25, 4, 7, 7, 7 }, { -27, -2, 46, 100, 56, 7 }, { 58, 29, 83, 148, -3, -25 } };
|
||||
|
||||
// Endgame evaluation and scaling functions are accessed directly and not through
|
||||
// the function maps because they correspond to more than one material hash key.
|
||||
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
|
||||
const int QuadraticCoefficientsOppositeColor[][6] = {
|
||||
{ 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 },
|
||||
{ 57, 64, 39, 41, 41, 41 }, { 50, 40, 23, -22, 41, 41 }, { 106, 101, 3, 151, 171, 41 } };
|
||||
|
||||
Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) };
|
||||
Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) };
|
||||
Endgame<KPsK> ScaleKPsK[] = { Endgame<KPsK>(WHITE), Endgame<KPsK>(BLACK) };
|
||||
Endgame<KPKP> ScaleKPKP[] = { Endgame<KPKP>(WHITE), Endgame<KPKP>(BLACK) };
|
||||
// Named endgame evaluation and scaling functions, these
|
||||
// are accessed direcly and not through the function maps.
|
||||
EvaluationFunction<KmmKm> EvaluateKmmKm(WHITE);
|
||||
EvaluationFunction<KXK> EvaluateKXK(WHITE), EvaluateKKX(BLACK);
|
||||
ScalingFunction<KBPsK> ScaleKBPsK(WHITE), ScaleKKBPs(BLACK);
|
||||
ScalingFunction<KQKRPs> ScaleKQKRPs(WHITE), ScaleKRPsKQ(BLACK);
|
||||
ScalingFunction<KPsK> ScaleKPsK(WHITE), ScaleKKPs(BLACK);
|
||||
ScalingFunction<KPKP> ScaleKPKPw(WHITE), ScaleKPKPb(BLACK);
|
||||
|
||||
// Helper used to detect a given material distribution
|
||||
bool is_KXK(const Position& pos, Color us) {
|
||||
return !more_than_one(pos.pieces(~us))
|
||||
&& pos.non_pawn_material(us) >= RookValueMg;
|
||||
}
|
||||
|
||||
bool is_KBPsK(const Position& pos, Color us) {
|
||||
return pos.non_pawn_material(us) == BishopValueMg
|
||||
&& pos.count<PAWN>(us) >= 1;
|
||||
}
|
||||
|
||||
bool is_KQKRPs(const Position& pos, Color us) {
|
||||
return !pos.count<PAWN>(us)
|
||||
&& pos.non_pawn_material(us) == QueenValueMg
|
||||
&& pos.count<ROOK>(~us) == 1
|
||||
&& pos.count<PAWN>(~us) >= 1;
|
||||
}
|
||||
typedef EndgameEvaluationFunctionBase EF;
|
||||
typedef EndgameScalingFunctionBase SF;
|
||||
}
|
||||
|
||||
|
||||
/// imbalance() calculates the imbalance by comparing the piece count of each
|
||||
/// piece type for both colors.
|
||||
////
|
||||
//// Classes
|
||||
////
|
||||
|
||||
template<Color Us>
|
||||
Score imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
|
||||
/// EndgameFunctions class stores endgame evaluation and scaling functions
|
||||
/// in two std::map. Because STL library is not guaranteed to be thread
|
||||
/// safe even for read access, the maps, although with identical content,
|
||||
/// are replicated for each thread. This is faster then using locks.
|
||||
|
||||
constexpr Color Them = ~Us;
|
||||
class EndgameFunctions {
|
||||
public:
|
||||
EndgameFunctions();
|
||||
~EndgameFunctions();
|
||||
template<class T> T* get(Key key) const;
|
||||
|
||||
Score bonus = SCORE_ZERO;
|
||||
private:
|
||||
template<class T> void add(const string& keyCode);
|
||||
|
||||
// Second-degree polynomial material imbalance, by Tord Romstad
|
||||
for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
|
||||
{
|
||||
if (!pieceCount[Us][pt1])
|
||||
continue;
|
||||
static Key buildKey(const string& keyCode);
|
||||
static const string swapColors(const string& keyCode);
|
||||
|
||||
int v = QuadraticOurs[pt1][pt1] * pieceCount[Us][pt1];
|
||||
// Here we store two maps, for evaluate and scaling functions
|
||||
pair<map<Key, EF*>, map<Key, SF*> > maps;
|
||||
|
||||
for (int pt2 = NO_PIECE_TYPE; pt2 < pt1; ++pt2)
|
||||
v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2]
|
||||
+ QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2];
|
||||
// Maps accessing functions returning const and non-const references
|
||||
template<typename T> const map<Key, T*>& get() const { return maps.first; }
|
||||
template<typename T> map<Key, T*>& get() { return maps.first; }
|
||||
};
|
||||
|
||||
bonus += pieceCount[Us][pt1] * v;
|
||||
}
|
||||
// Explicit specializations of a member function shall be declared in
|
||||
// the namespace of which the class template is a member.
|
||||
template<> const map<Key, SF*>&
|
||||
EndgameFunctions::get<SF>() const { return maps.second; }
|
||||
|
||||
return bonus;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Material {
|
||||
template<> map<Key, SF*>&
|
||||
EndgameFunctions::get<SF>() { return maps.second; }
|
||||
|
||||
|
||||
/// Material::probe() looks up the current position's material configuration in
|
||||
/// the material hash table. It returns a pointer to the Entry if the position
|
||||
/// is found. Otherwise a new Entry is computed and stored there, so we don't
|
||||
/// have to recompute all when the same material configuration occurs again.
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
Entry* probe(const Position& pos) {
|
||||
/// MaterialInfoTable c'tor and d'tor, called once by each thread
|
||||
|
||||
Key key = pos.material_key();
|
||||
Entry* e = pos.this_thread()->materialTable[key];
|
||||
MaterialInfoTable::MaterialInfoTable(unsigned int numOfEntries) {
|
||||
|
||||
if (e->key == key)
|
||||
return e;
|
||||
size = numOfEntries;
|
||||
entries = new MaterialInfo[size];
|
||||
funcs = new EndgameFunctions();
|
||||
|
||||
std::memset(e, 0, sizeof(Entry));
|
||||
e->key = key;
|
||||
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
|
||||
|
||||
Value npm_w = pos.non_pawn_material(WHITE);
|
||||
Value npm_b = pos.non_pawn_material(BLACK);
|
||||
Value npm = std::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
|
||||
|
||||
// Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
|
||||
e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
|
||||
|
||||
// Let's look if we have a specialized evaluation function for this particular
|
||||
// material configuration. Firstly we look for a fixed configuration one, then
|
||||
// for a generic one if the previous search failed.
|
||||
if ((e->evaluationFunction = Endgames::probe<Value>(key)) != nullptr)
|
||||
return e;
|
||||
|
||||
for (Color c : { WHITE, BLACK })
|
||||
if (is_KXK(pos, c))
|
||||
{
|
||||
e->evaluationFunction = &EvaluateKXK[c];
|
||||
return e;
|
||||
}
|
||||
|
||||
// OK, we didn't find any special evaluation function for the current material
|
||||
// configuration. Is there a suitable specialized scaling function?
|
||||
const auto* sf = Endgames::probe<ScaleFactor>(key);
|
||||
|
||||
if (sf)
|
||||
if (!entries || !funcs)
|
||||
{
|
||||
e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned
|
||||
return e;
|
||||
cerr << "Failed to allocate " << numOfEntries * sizeof(MaterialInfo)
|
||||
<< " bytes for material hash table." << endl;
|
||||
Application::exit_with_failure();
|
||||
}
|
||||
}
|
||||
|
||||
MaterialInfoTable::~MaterialInfoTable() {
|
||||
|
||||
delete funcs;
|
||||
delete [] entries;
|
||||
}
|
||||
|
||||
|
||||
/// MaterialInfoTable::game_phase() calculates the phase given the current
|
||||
/// position. Because the phase is strictly a function of the material, it
|
||||
/// is stored in MaterialInfo.
|
||||
|
||||
Phase MaterialInfoTable::game_phase(const Position& pos) {
|
||||
|
||||
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
|
||||
|
||||
if (npm >= MidgameLimit)
|
||||
return PHASE_MIDGAME;
|
||||
else if (npm <= EndgameLimit)
|
||||
return PHASE_ENDGAME;
|
||||
|
||||
return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
|
||||
}
|
||||
|
||||
/// MaterialInfoTable::get_material_info() takes a position object as input,
|
||||
/// computes or looks up a MaterialInfo object, and returns a pointer to it.
|
||||
/// If the material configuration is not already present in the table, it
|
||||
/// is stored there, so we don't have to recompute everything when the
|
||||
/// same material configuration occurs again.
|
||||
|
||||
MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
|
||||
|
||||
Key key = pos.get_material_key();
|
||||
int index = key & (size - 1);
|
||||
MaterialInfo* mi = entries + index;
|
||||
|
||||
// If mi->key matches the position's material hash key, it means that we
|
||||
// have analysed this material configuration before, and we can simply
|
||||
// return the information we found the last time instead of recomputing it.
|
||||
if (mi->key == key)
|
||||
return mi;
|
||||
|
||||
// Clear the MaterialInfo object, and set its key
|
||||
mi->clear();
|
||||
mi->key = key;
|
||||
|
||||
// Store game phase
|
||||
mi->gamePhase = MaterialInfoTable::game_phase(pos);
|
||||
|
||||
// Let's look if we have a specialized evaluation function for this
|
||||
// particular material configuration. First we look for a fixed
|
||||
// configuration one, then a generic one if previous search failed.
|
||||
if ((mi->evaluationFunction = funcs->get<EF>(key)) != NULL)
|
||||
return mi;
|
||||
|
||||
else if ( pos.non_pawn_material(BLACK) == Value(0)
|
||||
&& pos.piece_count(BLACK, PAWN) == 0
|
||||
&& pos.non_pawn_material(WHITE) >= RookValueMidgame)
|
||||
{
|
||||
mi->evaluationFunction = &EvaluateKXK;
|
||||
return mi;
|
||||
}
|
||||
else if ( pos.non_pawn_material(WHITE) == Value(0)
|
||||
&& pos.piece_count(WHITE, PAWN) == 0
|
||||
&& pos.non_pawn_material(BLACK) >= RookValueMidgame)
|
||||
{
|
||||
mi->evaluationFunction = &EvaluateKKX;
|
||||
return mi;
|
||||
}
|
||||
else if ( pos.pieces(PAWN) == EmptyBoardBB
|
||||
&& pos.pieces(ROOK) == EmptyBoardBB
|
||||
&& pos.pieces(QUEEN) == EmptyBoardBB)
|
||||
{
|
||||
// Minor piece endgame with at least one minor piece per side and
|
||||
// no pawns. Note that the case KmmK is already handled by KXK.
|
||||
assert((pos.pieces(KNIGHT, WHITE) | pos.pieces(BISHOP, WHITE)));
|
||||
assert((pos.pieces(KNIGHT, BLACK) | pos.pieces(BISHOP, BLACK)));
|
||||
|
||||
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2
|
||||
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
|
||||
{
|
||||
mi->evaluationFunction = &EvaluateKmmKm;
|
||||
return mi;
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't find any specialized scaling function, so fall back on generic
|
||||
// ones that refer to more than one material distribution. Note that in this
|
||||
// case we don't return after setting the function.
|
||||
for (Color c : { WHITE, BLACK })
|
||||
{
|
||||
if (is_KBPsK(pos, c))
|
||||
e->scalingFunction[c] = &ScaleKBPsK[c];
|
||||
// OK, we didn't find any special evaluation function for the current
|
||||
// material configuration. Is there a suitable scaling function?
|
||||
//
|
||||
// The code below is rather messy, and it could easily get worse later,
|
||||
// if we decide to add more special cases. We face problems when there
|
||||
// are several conflicting applicable scaling functions and we need to
|
||||
// decide which one to use.
|
||||
SF* sf;
|
||||
|
||||
else if (is_KQKRPs(pos, c))
|
||||
e->scalingFunction[c] = &ScaleKQKRPs[c];
|
||||
if ((sf = funcs->get<SF>(key)) != NULL)
|
||||
{
|
||||
mi->scalingFunction[sf->color()] = sf;
|
||||
return mi;
|
||||
}
|
||||
|
||||
if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board
|
||||
// Generic scaling functions that refer to more then one material
|
||||
// distribution. Should be probed after the specialized ones.
|
||||
// Note that these ones don't return after setting the function.
|
||||
if ( pos.non_pawn_material(WHITE) == BishopValueMidgame
|
||||
&& pos.piece_count(WHITE, BISHOP) == 1
|
||||
&& pos.piece_count(WHITE, PAWN) >= 1)
|
||||
mi->scalingFunction[WHITE] = &ScaleKBPsK;
|
||||
|
||||
if ( pos.non_pawn_material(BLACK) == BishopValueMidgame
|
||||
&& pos.piece_count(BLACK, BISHOP) == 1
|
||||
&& pos.piece_count(BLACK, PAWN) >= 1)
|
||||
mi->scalingFunction[BLACK] = &ScaleKKBPs;
|
||||
|
||||
if ( pos.piece_count(WHITE, PAWN) == 0
|
||||
&& pos.non_pawn_material(WHITE) == QueenValueMidgame
|
||||
&& pos.piece_count(WHITE, QUEEN) == 1
|
||||
&& pos.piece_count(BLACK, ROOK) == 1
|
||||
&& pos.piece_count(BLACK, PAWN) >= 1)
|
||||
mi->scalingFunction[WHITE] = &ScaleKQKRPs;
|
||||
|
||||
else if ( pos.piece_count(BLACK, PAWN) == 0
|
||||
&& pos.non_pawn_material(BLACK) == QueenValueMidgame
|
||||
&& pos.piece_count(BLACK, QUEEN) == 1
|
||||
&& pos.piece_count(WHITE, ROOK) == 1
|
||||
&& pos.piece_count(WHITE, PAWN) >= 1)
|
||||
mi->scalingFunction[BLACK] = &ScaleKRPsKQ;
|
||||
|
||||
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == Value(0))
|
||||
{
|
||||
if (!pos.count<PAWN>(BLACK))
|
||||
if (pos.piece_count(BLACK, PAWN) == 0)
|
||||
{
|
||||
assert(pos.count<PAWN>(WHITE) >= 2);
|
||||
|
||||
e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
|
||||
assert(pos.piece_count(WHITE, PAWN) >= 2);
|
||||
mi->scalingFunction[WHITE] = &ScaleKPsK;
|
||||
}
|
||||
else if (!pos.count<PAWN>(WHITE))
|
||||
else if (pos.piece_count(WHITE, PAWN) == 0)
|
||||
{
|
||||
assert(pos.count<PAWN>(BLACK) >= 2);
|
||||
|
||||
e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
|
||||
assert(pos.piece_count(BLACK, PAWN) >= 2);
|
||||
mi->scalingFunction[BLACK] = &ScaleKKPs;
|
||||
}
|
||||
else if (pos.count<PAWN>(WHITE) == 1 && pos.count<PAWN>(BLACK) == 1)
|
||||
else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1)
|
||||
{
|
||||
// This is a special case because we set scaling functions
|
||||
// for both colors instead of only one.
|
||||
e->scalingFunction[WHITE] = &ScaleKPKP[WHITE];
|
||||
e->scalingFunction[BLACK] = &ScaleKPKP[BLACK];
|
||||
mi->scalingFunction[WHITE] = &ScaleKPKPw;
|
||||
mi->scalingFunction[BLACK] = &ScaleKPKPb;
|
||||
}
|
||||
}
|
||||
|
||||
// Zero or just one pawn makes it difficult to win, even with a small material
|
||||
// advantage. This catches some trivial draws like KK, KBK and KNK and gives a
|
||||
// drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN).
|
||||
if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
|
||||
e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW :
|
||||
npm_b <= BishopValueMg ? 4 : 14);
|
||||
// Compute the space weight
|
||||
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >=
|
||||
2*QueenValueMidgame + 4*RookValueMidgame + 2*KnightValueMidgame)
|
||||
{
|
||||
int minorPieceCount = pos.piece_count(WHITE, KNIGHT)
|
||||
+ pos.piece_count(BLACK, KNIGHT)
|
||||
+ pos.piece_count(WHITE, BISHOP)
|
||||
+ pos.piece_count(BLACK, BISHOP);
|
||||
|
||||
if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg)
|
||||
e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW :
|
||||
npm_w <= BishopValueMg ? 4 : 14);
|
||||
mi->spaceWeight = minorPieceCount * minorPieceCount;
|
||||
}
|
||||
|
||||
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
|
||||
// for the bishop pair "extended piece", which allows us to be more flexible
|
||||
// in defining bishop pair bonuses.
|
||||
const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
|
||||
{ pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),
|
||||
pos.count<BISHOP>(WHITE) , pos.count<ROOK>(WHITE), pos.count<QUEEN >(WHITE) },
|
||||
{ pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
|
||||
pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
|
||||
// Evaluate the material balance
|
||||
const int pieceCount[2][6] = { { pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
|
||||
pos.piece_count(WHITE, BISHOP), pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) },
|
||||
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
|
||||
pos.piece_count(BLACK, BISHOP), pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
|
||||
Color c, them;
|
||||
int sign, pt1, pt2, pc;
|
||||
int v, vv, matValue = 0;
|
||||
|
||||
e->score = (imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16;
|
||||
return e;
|
||||
for (c = WHITE, sign = 1; c <= BLACK; c++, sign = -sign)
|
||||
{
|
||||
// No pawns makes it difficult to win, even with a material advantage
|
||||
if ( pos.piece_count(c, PAWN) == 0
|
||||
&& pos.non_pawn_material(c) - pos.non_pawn_material(opposite_color(c)) <= BishopValueMidgame)
|
||||
{
|
||||
if ( pos.non_pawn_material(c) == pos.non_pawn_material(opposite_color(c))
|
||||
|| pos.non_pawn_material(c) < RookValueMidgame)
|
||||
mi->factor[c] = 0;
|
||||
else
|
||||
{
|
||||
switch (pos.piece_count(c, BISHOP)) {
|
||||
case 2:
|
||||
mi->factor[c] = 32;
|
||||
break;
|
||||
case 1:
|
||||
mi->factor[c] = 12;
|
||||
break;
|
||||
case 0:
|
||||
mi->factor[c] = 6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Redundancy of major pieces, formula based on Kaufman's paper
|
||||
// "The Evaluation of Material Imbalances in Chess"
|
||||
// http://mywebpages.comcast.net/danheisman/Articles/evaluation_of_material_imbalance.htm
|
||||
if (pieceCount[c][ROOK] >= 1)
|
||||
matValue -= sign * ((pieceCount[c][ROOK] - 1) * RedundantRookPenalty + pieceCount[c][QUEEN] * RedundantQueenPenalty);
|
||||
|
||||
them = opposite_color(c);
|
||||
v = 0;
|
||||
|
||||
// Second-degree polynomial material imbalance by Tord Romstad
|
||||
//
|
||||
// We use NO_PIECE_TYPE as a place holder for the bishop pair "extended piece",
|
||||
// this allow us to be more flexible in defining bishop pair bonuses.
|
||||
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
|
||||
{
|
||||
pc = pieceCount[c][pt1];
|
||||
if (!pc)
|
||||
continue;
|
||||
|
||||
vv = LinearCoefficients[pt1];
|
||||
|
||||
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
|
||||
vv += pieceCount[c][pt2] * QuadraticCoefficientsSameColor[pt1][pt2]
|
||||
+ pieceCount[them][pt2] * QuadraticCoefficientsOppositeColor[pt1][pt2];
|
||||
|
||||
v += pc * vv;
|
||||
}
|
||||
matValue += sign * v;
|
||||
}
|
||||
mi->value = int16_t(matValue / 16);
|
||||
return mi;
|
||||
}
|
||||
|
||||
} // namespace Material
|
||||
|
||||
} // namespace Stockfish
|
||||
/// EndgameFunctions member definitions.
|
||||
|
||||
EndgameFunctions::EndgameFunctions() {
|
||||
|
||||
add<EvaluationFunction<KNNK> >("KNNK");
|
||||
add<EvaluationFunction<KPK> >("KPK");
|
||||
add<EvaluationFunction<KBNK> >("KBNK");
|
||||
add<EvaluationFunction<KRKP> >("KRKP");
|
||||
add<EvaluationFunction<KRKB> >("KRKB");
|
||||
add<EvaluationFunction<KRKN> >("KRKN");
|
||||
add<EvaluationFunction<KQKR> >("KQKR");
|
||||
add<EvaluationFunction<KBBKN> >("KBBKN");
|
||||
|
||||
add<ScalingFunction<KNPK> >("KNPK");
|
||||
add<ScalingFunction<KRPKR> >("KRPKR");
|
||||
add<ScalingFunction<KBPKB> >("KBPKB");
|
||||
add<ScalingFunction<KBPPKB> >("KBPPKB");
|
||||
add<ScalingFunction<KBPKN> >("KBPKN");
|
||||
add<ScalingFunction<KRPPKRP> >("KRPPKRP");
|
||||
}
|
||||
|
||||
EndgameFunctions::~EndgameFunctions() {
|
||||
|
||||
for (map<Key, EF*>::iterator it = maps.first.begin(); it != maps.first.end(); ++it)
|
||||
delete (*it).second;
|
||||
|
||||
for (map<Key, SF*>::iterator it = maps.second.begin(); it != maps.second.end(); ++it)
|
||||
delete (*it).second;
|
||||
}
|
||||
|
||||
Key EndgameFunctions::buildKey(const string& keyCode) {
|
||||
|
||||
assert(keyCode.length() > 0 && keyCode[0] == 'K');
|
||||
assert(keyCode.length() < 8);
|
||||
|
||||
stringstream s;
|
||||
bool upcase = false;
|
||||
|
||||
// Build up a fen string with the given pieces, note that
|
||||
// the fen string could be of an illegal position.
|
||||
for (size_t i = 0; i < keyCode.length(); i++)
|
||||
{
|
||||
if (keyCode[i] == 'K')
|
||||
upcase = !upcase;
|
||||
|
||||
s << char(upcase? toupper(keyCode[i]) : tolower(keyCode[i]));
|
||||
}
|
||||
s << 8 - keyCode.length() << "/8/8/8/8/8/8/8 w -";
|
||||
return Position(s.str()).get_material_key();
|
||||
}
|
||||
|
||||
const string EndgameFunctions::swapColors(const string& keyCode) {
|
||||
|
||||
// Build corresponding key for the opposite color: "KBPKN" -> "KNKBP"
|
||||
size_t idx = keyCode.find("K", 1);
|
||||
return keyCode.substr(idx) + keyCode.substr(0, idx);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void EndgameFunctions::add(const string& keyCode) {
|
||||
|
||||
typedef typename T::Base F;
|
||||
|
||||
get<F>().insert(pair<Key, F*>(buildKey(keyCode), new T(WHITE)));
|
||||
get<F>().insert(pair<Key, F*>(buildKey(swapColors(keyCode)), new T(BLACK)));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T* EndgameFunctions::get(Key key) const {
|
||||
|
||||
typename map<Key, T*>::const_iterator it(get<T>().find(key));
|
||||
return (it != get<T>().end() ? it->second : NULL);
|
||||
}
|
||||
|
||||
+143
-37
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,56 +17,161 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MATERIAL_H_INCLUDED
|
||||
|
||||
#if !defined(MATERIAL_H_INCLUDED)
|
||||
#define MATERIAL_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "endgame.h"
|
||||
#include "misc.h"
|
||||
#include "position.h"
|
||||
#include "types.h"
|
||||
#include "scale.h"
|
||||
|
||||
namespace Stockfish::Material {
|
||||
|
||||
/// Material::Entry contains various information about a material configuration.
|
||||
/// It contains a material imbalance evaluation, a function pointer to a special
|
||||
/// endgame evaluation function (which in most cases is NULL, meaning that the
|
||||
/// standard evaluation function will be used), and scale factors.
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
/// MaterialInfo is a class which contains various information about a
|
||||
/// material configuration. It contains a material balance evaluation,
|
||||
/// a function pointer to a special endgame evaluation function (which in
|
||||
/// most cases is NULL, meaning that the standard evaluation function will
|
||||
/// be used), and "scale factors" for black and white.
|
||||
///
|
||||
/// The scale factors are used to scale the evaluation score up or down. For
|
||||
/// instance, in KRB vs KR endgames, the score is scaled down by a factor of 4,
|
||||
/// which will result in scores of absolute value less than one pawn.
|
||||
/// The scale factors are used to scale the evaluation score up or down.
|
||||
/// For instance, in KRB vs KR endgames, the score is scaled down by a factor
|
||||
/// of 4, which will result in scores of absolute value less than one pawn.
|
||||
|
||||
struct Entry {
|
||||
class MaterialInfo {
|
||||
|
||||
Score imbalance() const { return score; }
|
||||
Phase game_phase() const { return (Phase)gamePhase; }
|
||||
bool specialized_eval_exists() const { return evaluationFunction != nullptr; }
|
||||
Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); }
|
||||
friend class MaterialInfoTable;
|
||||
|
||||
// scale_factor() takes a position and a color as input and returns a scale factor
|
||||
// for the given color. We have to provide the position in addition to the color
|
||||
// because the scale factor may also be a function which should be applied to
|
||||
// the position. For instance, in KBP vs K endgames, the scaling function looks
|
||||
// for rook pawns and wrong-colored bishops.
|
||||
ScaleFactor scale_factor(const Position& pos, Color c) const {
|
||||
ScaleFactor sf = scalingFunction[c] ? (*scalingFunction[c])(pos)
|
||||
: SCALE_FACTOR_NONE;
|
||||
return sf != SCALE_FACTOR_NONE ? sf : ScaleFactor(factor[c]);
|
||||
}
|
||||
public:
|
||||
MaterialInfo() : key(0) { clear(); }
|
||||
|
||||
Score material_value() const;
|
||||
ScaleFactor scale_factor(const Position& pos, Color c) const;
|
||||
int space_weight() const;
|
||||
Phase game_phase() const;
|
||||
bool specialized_eval_exists() const;
|
||||
Value evaluate(const Position& pos) const;
|
||||
|
||||
private:
|
||||
inline void clear();
|
||||
|
||||
Key key;
|
||||
const EndgameBase<Value>* evaluationFunction;
|
||||
const EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
|
||||
// side (e.g. KPKP, KBPsK)
|
||||
Score score;
|
||||
int16_t gamePhase;
|
||||
uint8_t factor[COLOR_NB];
|
||||
int16_t value;
|
||||
uint8_t factor[2];
|
||||
EndgameEvaluationFunctionBase* evaluationFunction;
|
||||
EndgameScalingFunctionBase* scalingFunction[2];
|
||||
int spaceWeight;
|
||||
Phase gamePhase;
|
||||
};
|
||||
|
||||
typedef HashTable<Entry, 8192> Table;
|
||||
/// The MaterialInfoTable class represents a pawn hash table. It is basically
|
||||
/// just an array of MaterialInfo objects and a few methods for accessing these
|
||||
/// objects. The most important method is get_material_info, which looks up a
|
||||
/// position in the table and returns a pointer to a MaterialInfo object.
|
||||
class EndgameFunctions;
|
||||
|
||||
Entry* probe(const Position& pos);
|
||||
class MaterialInfoTable {
|
||||
|
||||
} // namespace Stockfish::Material
|
||||
public:
|
||||
MaterialInfoTable(unsigned numOfEntries);
|
||||
~MaterialInfoTable();
|
||||
MaterialInfo* get_material_info(const Position& pos);
|
||||
|
||||
#endif // #ifndef MATERIAL_H_INCLUDED
|
||||
static Phase game_phase(const Position& pos);
|
||||
|
||||
private:
|
||||
unsigned size;
|
||||
MaterialInfo* entries;
|
||||
EndgameFunctions* funcs;
|
||||
};
|
||||
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
|
||||
/// MaterialInfo::material_value simply returns the material balance
|
||||
/// evaluation that is independent from game phase.
|
||||
|
||||
inline Score MaterialInfo::material_value() const {
|
||||
|
||||
return make_score(value, value);
|
||||
}
|
||||
|
||||
|
||||
/// MaterialInfo::clear() resets a MaterialInfo object to an empty state,
|
||||
/// with all slots at their default values but the key.
|
||||
|
||||
inline void MaterialInfo::clear() {
|
||||
|
||||
value = 0;
|
||||
factor[WHITE] = factor[BLACK] = uint8_t(SCALE_FACTOR_NORMAL);
|
||||
evaluationFunction = NULL;
|
||||
scalingFunction[WHITE] = scalingFunction[BLACK] = NULL;
|
||||
spaceWeight = 0;
|
||||
}
|
||||
|
||||
|
||||
/// MaterialInfo::scale_factor takes a position and a color as input, and
|
||||
/// returns a scale factor for the given color. We have to provide the
|
||||
/// position in addition to the color, because the scale factor need not
|
||||
/// to be a constant: It can also be a function which should be applied to
|
||||
/// the position. For instance, in KBP vs K endgames, a scaling function
|
||||
/// which checks for draws with rook pawns and wrong-colored bishops.
|
||||
|
||||
inline ScaleFactor MaterialInfo::scale_factor(const Position& pos, Color c) const {
|
||||
|
||||
if (scalingFunction[c] != NULL)
|
||||
{
|
||||
ScaleFactor sf = scalingFunction[c]->apply(pos);
|
||||
if (sf != SCALE_FACTOR_NONE)
|
||||
return sf;
|
||||
}
|
||||
return ScaleFactor(factor[c]);
|
||||
}
|
||||
|
||||
|
||||
/// MaterialInfo::space_weight() simply returns the weight for the space
|
||||
/// evaluation for this material configuration.
|
||||
|
||||
inline int MaterialInfo::space_weight() const {
|
||||
|
||||
return spaceWeight;
|
||||
}
|
||||
|
||||
/// MaterialInfo::game_phase() returns the game phase according
|
||||
/// to this material configuration.
|
||||
|
||||
inline Phase MaterialInfo::game_phase() const {
|
||||
|
||||
return gamePhase;
|
||||
}
|
||||
|
||||
|
||||
/// MaterialInfo::specialized_eval_exists decides whether there is a
|
||||
/// specialized evaluation function for the current material configuration,
|
||||
/// or if the normal evaluation function should be used.
|
||||
|
||||
inline bool MaterialInfo::specialized_eval_exists() const {
|
||||
|
||||
return evaluationFunction != NULL;
|
||||
}
|
||||
|
||||
|
||||
/// MaterialInfo::evaluate applies a specialized evaluation function
|
||||
/// to a given position object. It should only be called when
|
||||
/// specialized_eval_exists() returns 'true'.
|
||||
|
||||
inline Value MaterialInfo::evaluate(const Position& pos) const {
|
||||
|
||||
return evaluationFunction->apply(pos);
|
||||
}
|
||||
|
||||
#endif // !defined(MATERIAL_H_INCLUDED)
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
A C-program for MT19937, with initialization improved 2002/1/26.
|
||||
Coded by Takuji Nishimura and Makoto Matsumoto.
|
||||
|
||||
Before using, initialize the state by using init_genrand(seed)
|
||||
or init_by_array(init_key, key_length).
|
||||
|
||||
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of its contributors may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Any feedback is very welcome.
|
||||
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
|
||||
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
#include "mersenne.h"
|
||||
|
||||
/* Period parameters */
|
||||
#define N 624
|
||||
#define M 397
|
||||
#define MATRIX_A 0x9908b0dfUL /* constant vector a */
|
||||
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
|
||||
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
|
||||
|
||||
static unsigned long mt[N]; /* the array for the state vector */
|
||||
static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
|
||||
|
||||
/* initializes mt[N] with a seed */
|
||||
static void init_genrand(unsigned long s)
|
||||
{
|
||||
mt[0]= s & 0xffffffffUL;
|
||||
for (mti=1; mti<N; mti++) {
|
||||
mt[mti] =
|
||||
(1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
|
||||
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
|
||||
/* In the previous versions, MSBs of the seed affect */
|
||||
/* only MSBs of the array mt[]. */
|
||||
/* 2002/01/09 modified by Makoto Matsumoto */
|
||||
mt[mti] &= 0xffffffffUL;
|
||||
/* for >32 bit machines */
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize by an array with array-length */
|
||||
/* init_key is the array for initializing keys */
|
||||
/* key_length is its length */
|
||||
/* slight change for C++, 2004/2/26 */
|
||||
static void init_by_array(unsigned long init_key[], int key_length)
|
||||
{
|
||||
int i, j, k;
|
||||
init_genrand(19650218UL);
|
||||
i=1; j=0;
|
||||
k = (N>key_length ? N : key_length);
|
||||
for (; k; k--) {
|
||||
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
|
||||
+ init_key[j] + j; /* non linear */
|
||||
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
|
||||
i++; j++;
|
||||
if (i>=N) { mt[0] = mt[N-1]; i=1; }
|
||||
if (j>=key_length) j=0;
|
||||
}
|
||||
for (k=N-1; k; k--) {
|
||||
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
|
||||
- i; /* non linear */
|
||||
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
|
||||
i++;
|
||||
if (i>=N) { mt[0] = mt[N-1]; i=1; }
|
||||
}
|
||||
|
||||
mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
|
||||
}
|
||||
|
||||
/* generates a random number on [0,0xffffffff]-interval */
|
||||
uint32_t genrand_int32(void) {
|
||||
unsigned long y;
|
||||
static unsigned long mag01[2]={0x0UL, MATRIX_A};
|
||||
/* mag01[x] = x * MATRIX_A for x=0,1 */
|
||||
|
||||
if (mti >= N) { /* generate N words at one time */
|
||||
int kk;
|
||||
|
||||
if (mti == N+1) /* if init_genrand() has not been called, */
|
||||
init_genrand(5489UL); /* a default initial seed is used */
|
||||
|
||||
for (kk=0;kk<N-M;kk++) {
|
||||
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
|
||||
mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||
}
|
||||
for (;kk<N-1;kk++) {
|
||||
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
|
||||
mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||
}
|
||||
y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
|
||||
mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||
|
||||
mti = 0;
|
||||
}
|
||||
|
||||
y = mt[mti++];
|
||||
|
||||
/* Tempering */
|
||||
y ^= (y >> 11);
|
||||
y ^= (y << 7) & 0x9d2c5680UL;
|
||||
y ^= (y << 15) & 0xefc60000UL;
|
||||
y ^= (y >> 18);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
uint64_t genrand_int64(void) {
|
||||
uint64_t x, y;
|
||||
|
||||
x = genrand_int32(); y = genrand_int32();
|
||||
return (x<<32)|y;
|
||||
}
|
||||
|
||||
void init_mersenne(void) {
|
||||
unsigned long init[4]={0x123, 0x234, 0x345, 0x456}, length=4;
|
||||
init_by_array(init, length);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(MERSENNE_H_INCLUDED)
|
||||
#define MERSENNE_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern uint32_t genrand_int32(void);
|
||||
extern uint64_t genrand_int64(void);
|
||||
extern void init_mersenne(void);
|
||||
|
||||
|
||||
#endif // !defined(MERSENNE_H_INCLUDED)
|
||||
+209
-603
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,657 +17,262 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#if _WIN32_WINNT < 0x0601
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0601 // Force to include needed API prototypes
|
||||
#endif
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
|
||||
# include <sys/time.h>
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
|
||||
#else
|
||||
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
#include <windows.h>
|
||||
// The needed Windows API for processor groups could be missed from old Windows
|
||||
// versions, so instead of calling them directly (forcing the linker to resolve
|
||||
// the calls at compile time), try to load them at runtime. To do this we need
|
||||
// first to define the corresponding function pointers.
|
||||
extern "C" {
|
||||
typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP,
|
||||
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD);
|
||||
typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY);
|
||||
typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
|
||||
typedef bool(*fun4_t)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT);
|
||||
typedef WORD(*fun5_t)();
|
||||
}
|
||||
#include <sys/timeb.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) || defined(__e2k__)
|
||||
#define POSIXALIGNEDALLOC
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include "bitcount.h"
|
||||
#include "misc.h"
|
||||
#include "thread.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Stockfish {
|
||||
/// Version number. If this is left empty, the current date (in the format
|
||||
/// YYMMDD) is used as a version number.
|
||||
|
||||
namespace {
|
||||
|
||||
/// Version number. If Version is left empty, then compile date in the format
|
||||
/// DD-MM-YY and show in engine_info.
|
||||
const string Version = "15";
|
||||
|
||||
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
|
||||
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
|
||||
/// can toggle the logging of std::cout and std:cin at runtime whilst preserving
|
||||
/// usual I/O functionality, all without changing a single line of code!
|
||||
/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
|
||||
|
||||
struct Tie: public streambuf { // MSVC requires split streambuf for cin and cout
|
||||
|
||||
Tie(streambuf* b, streambuf* l) : buf(b), logBuf(l) {}
|
||||
|
||||
int sync() override { return logBuf->pubsync(), buf->pubsync(); }
|
||||
int overflow(int c) override { return log(buf->sputc((char)c), "<< "); }
|
||||
int underflow() override { return buf->sgetc(); }
|
||||
int uflow() override { return log(buf->sbumpc(), ">> "); }
|
||||
|
||||
streambuf *buf, *logBuf;
|
||||
|
||||
int log(int c, const char* prefix) {
|
||||
|
||||
static int last = '\n'; // Single log file
|
||||
|
||||
if (last == '\n')
|
||||
logBuf->sputn(prefix, 3);
|
||||
|
||||
return last = logBuf->sputc((char)c);
|
||||
}
|
||||
};
|
||||
|
||||
class Logger {
|
||||
|
||||
Logger() : in(cin.rdbuf(), file.rdbuf()), out(cout.rdbuf(), file.rdbuf()) {}
|
||||
~Logger() { start(""); }
|
||||
|
||||
ofstream file;
|
||||
Tie in, out;
|
||||
|
||||
public:
|
||||
static void start(const std::string& fname) {
|
||||
|
||||
static Logger l;
|
||||
|
||||
if (l.file.is_open())
|
||||
{
|
||||
cout.rdbuf(l.out.buf);
|
||||
cin.rdbuf(l.in.buf);
|
||||
l.file.close();
|
||||
}
|
||||
|
||||
if (!fname.empty())
|
||||
{
|
||||
l.file.open(fname, ifstream::out);
|
||||
|
||||
if (!l.file.is_open())
|
||||
{
|
||||
cerr << "Unable to open debug log file " << fname << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
cin.rdbuf(&l.in);
|
||||
cout.rdbuf(&l.out);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
static const string EngineVersion = "1.6.3";
|
||||
static const string AppName = "Stockfish";
|
||||
static const string AppTag = "";
|
||||
|
||||
|
||||
/// engine_info() returns the full name of the current Stockfish version. This
|
||||
/// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when
|
||||
/// the program was compiled) or "Stockfish <Version>", depending on whether
|
||||
/// Version is empty.
|
||||
////
|
||||
//// Variables
|
||||
////
|
||||
|
||||
string engine_info(bool to_uci) {
|
||||
bool Chess960;
|
||||
|
||||
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
|
||||
string month, day, year;
|
||||
stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008"
|
||||
uint64_t dbg_cnt0 = 0;
|
||||
uint64_t dbg_cnt1 = 0;
|
||||
|
||||
ss << "Stockfish " << Version << setfill('0');
|
||||
bool dbg_show_mean = false;
|
||||
bool dbg_show_hit_rate = false;
|
||||
|
||||
if (Version.empty())
|
||||
{
|
||||
date >> month >> day >> year;
|
||||
ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
|
||||
}
|
||||
|
||||
ss << (to_uci ? "\nid author ": " by ")
|
||||
<< "the Stockfish developers (see AUTHORS file)";
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
return ss.str();
|
||||
void dbg_hit_on(bool b) {
|
||||
|
||||
assert(!dbg_show_mean);
|
||||
dbg_show_hit_rate = true;
|
||||
dbg_cnt0++;
|
||||
if (b)
|
||||
dbg_cnt1++;
|
||||
}
|
||||
|
||||
void dbg_hit_on_c(bool c, bool b) {
|
||||
|
||||
if (c)
|
||||
dbg_hit_on(b);
|
||||
}
|
||||
|
||||
void dbg_before() {
|
||||
|
||||
assert(!dbg_show_mean);
|
||||
dbg_show_hit_rate = true;
|
||||
dbg_cnt0++;
|
||||
}
|
||||
|
||||
void dbg_after() {
|
||||
|
||||
assert(!dbg_show_mean);
|
||||
dbg_show_hit_rate = true;
|
||||
dbg_cnt1++;
|
||||
}
|
||||
|
||||
void dbg_mean_of(int v) {
|
||||
|
||||
assert(!dbg_show_hit_rate);
|
||||
dbg_show_mean = true;
|
||||
dbg_cnt0++;
|
||||
dbg_cnt1 += v;
|
||||
}
|
||||
|
||||
void dbg_print_hit_rate() {
|
||||
|
||||
cout << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
|
||||
<< " hit rate (%) " << (dbg_cnt1*100)/(dbg_cnt0 ? dbg_cnt0 : 1) << endl;
|
||||
}
|
||||
|
||||
void dbg_print_mean() {
|
||||
|
||||
cout << "Total " << dbg_cnt0 << " Mean "
|
||||
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl;
|
||||
}
|
||||
|
||||
void dbg_print_hit_rate(ofstream& logFile) {
|
||||
|
||||
logFile << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
|
||||
<< " hit rate (%) " << (dbg_cnt1*100)/(dbg_cnt0 ? dbg_cnt0 : 1) << endl;
|
||||
}
|
||||
|
||||
void dbg_print_mean(ofstream& logFile) {
|
||||
|
||||
logFile << "Total " << dbg_cnt0 << " Mean "
|
||||
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl;
|
||||
}
|
||||
|
||||
/// engine_name() returns the full name of the current Stockfish version.
|
||||
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when the
|
||||
/// program was compiled) or "Stockfish <version number>", depending on whether
|
||||
/// the constant EngineVersion (defined in misc.h) is empty.
|
||||
|
||||
const string engine_name() {
|
||||
|
||||
const string cpu64(CpuHas64BitPath ? " 64bit" : "");
|
||||
|
||||
if (!EngineVersion.empty())
|
||||
return AppName+ " " + EngineVersion + cpu64;
|
||||
|
||||
string date(__DATE__); // From compiler, format is "Sep 21 2008"
|
||||
string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
|
||||
|
||||
size_t mon = 1 + months.find(date.substr(0, 3)) / 4;
|
||||
|
||||
stringstream s;
|
||||
string day = (date[4] == ' ' ? date.substr(5, 1) : date.substr(4, 2));
|
||||
|
||||
string name = AppName + " " + AppTag + " ";
|
||||
|
||||
s << name << date.substr(date.length() - 2) << setfill('0')
|
||||
<< setw(2) << mon << setw(2) << day << cpu64;
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
|
||||
/// compiler_info() returns a string trying to describe the compiler we use
|
||||
/// get_system_time() returns the current system time, measured in
|
||||
/// milliseconds.
|
||||
|
||||
std::string compiler_info() {
|
||||
|
||||
#define stringify2(x) #x
|
||||
#define stringify(x) stringify2(x)
|
||||
#define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch)
|
||||
|
||||
/// 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
|
||||
|
||||
std::string compiler = "\nCompiled by ";
|
||||
|
||||
#ifdef __clang__
|
||||
compiler += "clang++ ";
|
||||
compiler += make_version_string(__clang_major__, __clang_minor__, __clang_patchlevel__);
|
||||
#elif __INTEL_COMPILER
|
||||
compiler += "Intel compiler ";
|
||||
compiler += "(version ";
|
||||
compiler += stringify(__INTEL_COMPILER) " update " stringify(__INTEL_COMPILER_UPDATE);
|
||||
compiler += ")";
|
||||
#elif _MSC_VER
|
||||
compiler += "MSVC ";
|
||||
compiler += "(version ";
|
||||
compiler += stringify(_MSC_FULL_VER) "." stringify(_MSC_BUILD);
|
||||
compiler += ")";
|
||||
#elif defined(__e2k__) && defined(__LCC__)
|
||||
#define dot_ver2(n) \
|
||||
compiler += (char)'.'; \
|
||||
compiler += (char)('0' + (n) / 10); \
|
||||
compiler += (char)('0' + (n) % 10);
|
||||
|
||||
compiler += "MCST LCC ";
|
||||
compiler += "(version ";
|
||||
compiler += std::to_string(__LCC__ / 100);
|
||||
dot_ver2(__LCC__ % 100)
|
||||
dot_ver2(__LCC_MINOR__)
|
||||
compiler += ")";
|
||||
#elif __GNUC__
|
||||
compiler += "g++ (GNUC) ";
|
||||
compiler += make_version_string(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
|
||||
#else
|
||||
compiler += "Unknown compiler ";
|
||||
compiler += "(unknown version)";
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
compiler += " on Apple";
|
||||
#elif defined(__CYGWIN__)
|
||||
compiler += " on Cygwin";
|
||||
#elif defined(__MINGW64__)
|
||||
compiler += " on MinGW64";
|
||||
#elif defined(__MINGW32__)
|
||||
compiler += " on MinGW32";
|
||||
#elif defined(__ANDROID__)
|
||||
compiler += " on Android";
|
||||
#elif defined(__linux__)
|
||||
compiler += " on Linux";
|
||||
#elif defined(_WIN64)
|
||||
compiler += " on Microsoft Windows 64-bit";
|
||||
#elif defined(_WIN32)
|
||||
compiler += " on Microsoft Windows 32-bit";
|
||||
#else
|
||||
compiler += " on unknown system";
|
||||
#endif
|
||||
|
||||
compiler += "\nCompilation settings include: ";
|
||||
compiler += (Is64Bit ? " 64bit" : " 32bit");
|
||||
#if defined(USE_VNNI)
|
||||
compiler += " VNNI";
|
||||
#endif
|
||||
#if defined(USE_AVX512)
|
||||
compiler += " AVX512";
|
||||
#endif
|
||||
compiler += (HasPext ? " BMI2" : "");
|
||||
#if defined(USE_AVX2)
|
||||
compiler += " AVX2";
|
||||
#endif
|
||||
#if defined(USE_SSE41)
|
||||
compiler += " SSE41";
|
||||
#endif
|
||||
#if defined(USE_SSSE3)
|
||||
compiler += " SSSE3";
|
||||
#endif
|
||||
#if defined(USE_SSE2)
|
||||
compiler += " SSE2";
|
||||
#endif
|
||||
compiler += (HasPopCnt ? " POPCNT" : "");
|
||||
#if defined(USE_MMX)
|
||||
compiler += " MMX";
|
||||
#endif
|
||||
#if defined(USE_NEON)
|
||||
compiler += " NEON";
|
||||
#endif
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
compiler += " DEBUG";
|
||||
#endif
|
||||
|
||||
compiler += "\n__VERSION__ macro expands to: ";
|
||||
#ifdef __VERSION__
|
||||
compiler += __VERSION__;
|
||||
#else
|
||||
compiler += "(undefined macro)";
|
||||
#endif
|
||||
compiler += "\n";
|
||||
|
||||
return compiler;
|
||||
}
|
||||
|
||||
|
||||
/// Debug functions used mainly to collect run-time statistics
|
||||
static std::atomic<int64_t> hits[2], means[2];
|
||||
|
||||
void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
|
||||
void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); }
|
||||
void dbg_mean_of(int v) { ++means[0]; means[1] += v; }
|
||||
|
||||
void dbg_print() {
|
||||
|
||||
if (hits[0])
|
||||
cerr << "Total " << hits[0] << " Hits " << hits[1]
|
||||
<< " hit rate (%) " << 100 * hits[1] / hits[0] << endl;
|
||||
|
||||
if (means[0])
|
||||
cerr << "Total " << means[0] << " Mean "
|
||||
<< (double)means[1] / means[0] << endl;
|
||||
}
|
||||
|
||||
|
||||
/// Used to serialize access to std::cout to avoid multiple threads writing at
|
||||
/// the same time.
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
|
||||
|
||||
static std::mutex m;
|
||||
|
||||
if (sc == IO_LOCK)
|
||||
m.lock();
|
||||
|
||||
if (sc == IO_UNLOCK)
|
||||
m.unlock();
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
/// Trampoline helper to avoid moving Logger to misc.h
|
||||
void start_logger(const std::string& fname) { Logger::start(fname); }
|
||||
|
||||
|
||||
/// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking
|
||||
/// function that doesn't stall the CPU waiting for data to be loaded from memory,
|
||||
/// which can be quite slow.
|
||||
#ifdef NO_PREFETCH
|
||||
|
||||
void prefetch(void*) {}
|
||||
int get_system_time() {
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
struct _timeb t;
|
||||
_ftime(&t);
|
||||
return int(t.time*1000 + t.millitm);
|
||||
#else
|
||||
struct timeval t;
|
||||
gettimeofday(&t, NULL);
|
||||
return t.tv_sec*1000 + t.tv_usec/1000;
|
||||
#endif
|
||||
}
|
||||
|
||||
void prefetch(void* addr) {
|
||||
|
||||
# if defined(__INTEL_COMPILER)
|
||||
// This hack prevents prefetches from being optimized away by
|
||||
// Intel compiler. Both MSVC and gcc seem not be affected by this.
|
||||
__asm__ ("");
|
||||
# endif
|
||||
/// cpu_count() tries to detect the number of CPU cores.
|
||||
|
||||
# if defined(__INTEL_COMPILER) || defined(_MSC_VER)
|
||||
_mm_prefetch((char*)addr, _MM_HINT_T0);
|
||||
#if !defined(_MSC_VER)
|
||||
|
||||
# if defined(_SC_NPROCESSORS_ONLN)
|
||||
int cpu_count() {
|
||||
return Min(sysconf(_SC_NPROCESSORS_ONLN), 8);
|
||||
}
|
||||
# else
|
||||
__builtin_prefetch(addr);
|
||||
int cpu_count() {
|
||||
return 1;
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// std_aligned_alloc() is our wrapper for systems where the c++17 implementation
|
||||
/// does not guarantee the availability of aligned_alloc(). Memory allocated with
|
||||
/// std_aligned_alloc() must be freed with std_aligned_free().
|
||||
|
||||
void* std_aligned_alloc(size_t alignment, size_t size) {
|
||||
|
||||
#if defined(POSIXALIGNEDALLOC)
|
||||
void *mem;
|
||||
return posix_memalign(&mem, alignment, size) ? nullptr : mem;
|
||||
#elif defined(_WIN32)
|
||||
return _mm_malloc(size, alignment);
|
||||
#else
|
||||
return std::aligned_alloc(alignment, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void std_aligned_free(void* ptr) {
|
||||
|
||||
#if defined(POSIXALIGNEDALLOC)
|
||||
free(ptr);
|
||||
#elif defined(_WIN32)
|
||||
_mm_free(ptr);
|
||||
#else
|
||||
free(ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages.
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
static void* aligned_large_pages_alloc_windows(size_t allocSize) {
|
||||
|
||||
#if !defined(_WIN64)
|
||||
(void)allocSize; // suppress unused-parameter compiler warning
|
||||
return nullptr;
|
||||
#else
|
||||
|
||||
HANDLE hProcessToken { };
|
||||
LUID luid { };
|
||||
void* mem = nullptr;
|
||||
|
||||
const size_t largePageSize = GetLargePageMinimum();
|
||||
if (!largePageSize)
|
||||
return nullptr;
|
||||
|
||||
// We need SeLockMemoryPrivilege, so try to enable it for the process
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken))
|
||||
return nullptr;
|
||||
|
||||
if (LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luid))
|
||||
{
|
||||
TOKEN_PRIVILEGES tp { };
|
||||
TOKEN_PRIVILEGES prevTp { };
|
||||
DWORD prevTpLen = 0;
|
||||
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
// Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds,
|
||||
// we still need to query GetLastError() to ensure that the privileges were actually obtained.
|
||||
if (AdjustTokenPrivileges(
|
||||
hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) &&
|
||||
GetLastError() == ERROR_SUCCESS)
|
||||
{
|
||||
// Round up size to full pages and allocate
|
||||
allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1);
|
||||
mem = VirtualAlloc(
|
||||
NULL, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
|
||||
|
||||
// Privilege no longer needed, restore previous state
|
||||
AdjustTokenPrivileges(hProcessToken, FALSE, &prevTp, 0, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hProcessToken);
|
||||
|
||||
return mem;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void* aligned_large_pages_alloc(size_t allocSize) {
|
||||
|
||||
// Try to allocate large pages
|
||||
void* mem = aligned_large_pages_alloc_windows(allocSize);
|
||||
|
||||
// Fall back to regular, page aligned, allocation if necessary
|
||||
if (!mem)
|
||||
mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void* aligned_large_pages_alloc(size_t allocSize) {
|
||||
|
||||
#if defined(__linux__)
|
||||
constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page size
|
||||
#else
|
||||
constexpr size_t alignment = 4096; // assumed small page size
|
||||
#endif
|
||||
|
||||
// round up to multiples of alignment
|
||||
size_t size = ((allocSize + alignment - 1) / alignment) * alignment;
|
||||
void *mem = std_aligned_alloc(alignment, size);
|
||||
#if defined(MADV_HUGEPAGE)
|
||||
madvise(mem, size, MADV_HUGEPAGE);
|
||||
#endif
|
||||
return mem;
|
||||
int cpu_count() {
|
||||
SYSTEM_INFO s;
|
||||
GetSystemInfo(&s);
|
||||
return Min(s.dwNumberOfProcessors, 8);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// aligned_large_pages_free() will free the previously allocated ttmem
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
void aligned_large_pages_free(void* mem) {
|
||||
|
||||
if (mem && !VirtualFree(mem, 0, MEM_RELEASE))
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
std::cerr << "Failed to free large page memory. Error code: 0x"
|
||||
<< std::hex << err
|
||||
<< std::dec << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void aligned_large_pages_free(void *mem) {
|
||||
std_aligned_free(mem);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace WinProcGroup {
|
||||
|
||||
/*
|
||||
From Beowulf, from Olithink
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
/* Non-windows version */
|
||||
int Bioskey()
|
||||
{
|
||||
fd_set readfds;
|
||||
struct timeval timeout;
|
||||
|
||||
void bindThisThread(size_t) {}
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fileno(stdin), &readfds);
|
||||
/* Set to timeout immediately */
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
select(16, &readfds, 0, 0, &timeout);
|
||||
|
||||
#else
|
||||
|
||||
/// best_node() retrieves logical processor information using Windows specific
|
||||
/// API and returns the best node id for the thread with index idx. Original
|
||||
/// code from Texel by Peter Österlund.
|
||||
|
||||
int best_node(size_t idx) {
|
||||
|
||||
int threads = 0;
|
||||
int nodes = 0;
|
||||
int cores = 0;
|
||||
DWORD returnLength = 0;
|
||||
DWORD byteOffset = 0;
|
||||
|
||||
// Early exit if the needed API is not available at runtime
|
||||
HMODULE k32 = GetModuleHandle("Kernel32.dll");
|
||||
auto fun1 = (fun1_t)(void(*)())GetProcAddress(k32, "GetLogicalProcessorInformationEx");
|
||||
if (!fun1)
|
||||
return -1;
|
||||
|
||||
// First call to GetLogicalProcessorInformationEx() to get returnLength.
|
||||
// We expect the call to fail due to null buffer.
|
||||
if (fun1(RelationAll, nullptr, &returnLength))
|
||||
return -1;
|
||||
|
||||
// Once we know returnLength, allocate the buffer
|
||||
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr;
|
||||
ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength);
|
||||
|
||||
// Second call to GetLogicalProcessorInformationEx(), now we expect to succeed
|
||||
if (!fun1(RelationAll, buffer, &returnLength))
|
||||
{
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (byteOffset < returnLength)
|
||||
{
|
||||
if (ptr->Relationship == RelationNumaNode)
|
||||
nodes++;
|
||||
|
||||
else if (ptr->Relationship == RelationProcessorCore)
|
||||
{
|
||||
cores++;
|
||||
threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1;
|
||||
}
|
||||
|
||||
assert(ptr->Size);
|
||||
byteOffset += ptr->Size;
|
||||
ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size);
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
||||
std::vector<int> groups;
|
||||
|
||||
// Run as many threads as possible on the same node until core limit is
|
||||
// reached, then move on filling the next node.
|
||||
for (int n = 0; n < nodes; n++)
|
||||
for (int i = 0; i < cores / nodes; i++)
|
||||
groups.push_back(n);
|
||||
|
||||
// In case a core has more than one logical processor (we assume 2) and we
|
||||
// have still threads to allocate, then spread them evenly across available
|
||||
// nodes.
|
||||
for (int t = 0; t < threads - cores; t++)
|
||||
groups.push_back(t % nodes);
|
||||
|
||||
// If we still have more threads than the total number of logical processors
|
||||
// then return -1 and let the OS to decide what to do.
|
||||
return idx < groups.size() ? groups[idx] : -1;
|
||||
return (FD_ISSET(fileno(stdin), &readfds));
|
||||
}
|
||||
|
||||
|
||||
/// bindThisThread() set the group affinity of the current thread
|
||||
|
||||
void bindThisThread(size_t idx) {
|
||||
|
||||
// Use only local variables to be thread-safe
|
||||
int node = best_node(idx);
|
||||
|
||||
if (node == -1)
|
||||
return;
|
||||
|
||||
// Early exit if the needed API are not available at runtime
|
||||
HMODULE k32 = GetModuleHandle("Kernel32.dll");
|
||||
auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx");
|
||||
auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity");
|
||||
auto fun4 = (fun4_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2");
|
||||
auto fun5 = (fun5_t)(void(*)())GetProcAddress(k32, "GetMaximumProcessorGroupCount");
|
||||
|
||||
if (!fun2 || !fun3)
|
||||
return;
|
||||
|
||||
if (!fun4 || !fun5)
|
||||
{
|
||||
GROUP_AFFINITY affinity;
|
||||
if (fun2(node, &affinity)) // GetNumaNodeProcessorMaskEx
|
||||
fun3(GetCurrentThread(), &affinity, nullptr); // SetThreadGroupAffinity
|
||||
}
|
||||
else
|
||||
{
|
||||
// If a numa node has more than one processor group, we assume they are
|
||||
// sized equal and we spread threads evenly across the groups.
|
||||
USHORT elements, returnedElements;
|
||||
elements = fun5(); // GetMaximumProcessorGroupCount
|
||||
GROUP_AFFINITY *affinity = (GROUP_AFFINITY*)malloc(elements * sizeof(GROUP_AFFINITY));
|
||||
if (fun4(node, affinity, elements, &returnedElements)) // GetNumaNodeProcessorMask2
|
||||
fun3(GetCurrentThread(), &affinity[idx % returnedElements], nullptr); // SetThreadGroupAffinity
|
||||
free(affinity);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace WinProcGroup
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#define GETCWD _getcwd
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define GETCWD getcwd
|
||||
/* Windows-version */
|
||||
#include <windows.h>
|
||||
#include <conio.h>
|
||||
int Bioskey()
|
||||
{
|
||||
static int init = 0,
|
||||
pipe;
|
||||
static HANDLE inh;
|
||||
DWORD dw;
|
||||
/* If we're running under XBoard then we can't use _kbhit() as the input
|
||||
* commands are sent to us directly over the internal pipe */
|
||||
|
||||
#if defined(FILE_CNT)
|
||||
if (stdin->_cnt > 0)
|
||||
return stdin->_cnt;
|
||||
#endif
|
||||
if (!init) {
|
||||
init = 1;
|
||||
inh = GetStdHandle(STD_INPUT_HANDLE);
|
||||
pipe = !GetConsoleMode(inh, &dw);
|
||||
if (!pipe) {
|
||||
SetConsoleMode(inh, dw & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
|
||||
FlushConsoleInputBuffer(inh);
|
||||
}
|
||||
}
|
||||
if (pipe) {
|
||||
if (!PeekNamedPipe(inh, NULL, 0, NULL, &dw, NULL))
|
||||
return 1;
|
||||
return dw;
|
||||
} else {
|
||||
// Count the number of unread input records, including keyboard,
|
||||
// mouse, and window-resizing input records.
|
||||
GetNumberOfConsoleInputEvents(inh, &dw);
|
||||
if (dw <= 0)
|
||||
return 0;
|
||||
|
||||
namespace CommandLine {
|
||||
// Read data from console without removing it from the buffer
|
||||
INPUT_RECORD rec[256];
|
||||
DWORD recCnt;
|
||||
if (!PeekConsoleInput(inh, rec, Min(dw, 256), &recCnt))
|
||||
return 0;
|
||||
|
||||
string argv0; // path+name of the executable binary, as given by argv[0]
|
||||
string binaryDirectory; // path of the executable directory
|
||||
string workingDirectory; // path of the working directory
|
||||
// Search for at least one keyboard event
|
||||
for (DWORD i = 0; i < recCnt; i++)
|
||||
if (rec[i].EventType == KEY_EVENT)
|
||||
return 1;
|
||||
|
||||
void init(int argc, char* argv[]) {
|
||||
(void)argc;
|
||||
string pathSeparator;
|
||||
|
||||
// extract the path+name of the executable binary
|
||||
argv0 = argv[0];
|
||||
|
||||
#ifdef _WIN32
|
||||
pathSeparator = "\\";
|
||||
#ifdef _MSC_VER
|
||||
// Under windows argv[0] may not have the extension. Also _get_pgmptr() had
|
||||
// issues in some windows 10 versions, so check returned values carefully.
|
||||
char* pgmptr = nullptr;
|
||||
if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr)
|
||||
argv0 = pgmptr;
|
||||
#endif
|
||||
#else
|
||||
pathSeparator = "/";
|
||||
#endif
|
||||
|
||||
// extract the working directory
|
||||
workingDirectory = "";
|
||||
char buff[40000];
|
||||
char* cwd = GETCWD(buff, 40000);
|
||||
if (cwd)
|
||||
workingDirectory = cwd;
|
||||
|
||||
// extract the binary directory path from argv0
|
||||
binaryDirectory = argv0;
|
||||
size_t pos = binaryDirectory.find_last_of("\\/");
|
||||
if (pos == std::string::npos)
|
||||
binaryDirectory = "." + pathSeparator;
|
||||
else
|
||||
binaryDirectory.resize(pos + 1);
|
||||
|
||||
// pattern replacement: "./" at the start of path is replaced by the working directory
|
||||
if (binaryDirectory.find("." + pathSeparator) == 0)
|
||||
binaryDirectory.replace(0, 1, workingDirectory);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace CommandLine
|
||||
|
||||
} // namespace Stockfish
|
||||
#endif
|
||||
|
||||
+45
-203
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,223 +17,64 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MISC_H_INCLUDED
|
||||
|
||||
#if !defined(MISC_H_INCLUDED)
|
||||
#define MISC_H_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "application.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
////
|
||||
//// Macros
|
||||
////
|
||||
|
||||
std::string engine_info(bool to_uci = false);
|
||||
std::string compiler_info();
|
||||
void prefetch(void* addr);
|
||||
void start_logger(const std::string& fname);
|
||||
void* std_aligned_alloc(size_t alignment, size_t size);
|
||||
void std_aligned_free(void* ptr);
|
||||
void* aligned_large_pages_alloc(size_t size); // memory aligned by page size, min alignment: 4096 bytes
|
||||
void aligned_large_pages_free(void* mem); // nop if mem == nullptr
|
||||
|
||||
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
|
||||
};
|
||||
#define Min(x, y) (((x) < (y))? (x) : (y))
|
||||
#define Max(x, y) (((x) < (y))? (y) : (x))
|
||||
|
||||
|
||||
enum SyncCout { IO_LOCK, IO_UNLOCK };
|
||||
std::ostream& operator<<(std::ostream&, SyncCout);
|
||||
////
|
||||
//// Variables
|
||||
////
|
||||
|
||||
#define sync_cout std::cout << IO_LOCK
|
||||
#define sync_endl std::endl << IO_UNLOCK
|
||||
extern bool Chess960;
|
||||
|
||||
|
||||
// align_ptr_up() : get the first aligned element of an array.
|
||||
// ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes,
|
||||
// where N is the number of elements in the array.
|
||||
template <uintptr_t Alignment, typename T>
|
||||
T* align_ptr_up(T* ptr)
|
||||
{
|
||||
static_assert(alignof(T) < Alignment);
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
const uintptr_t ptrint = reinterpret_cast<uintptr_t>(reinterpret_cast<char*>(ptr));
|
||||
return reinterpret_cast<T*>(reinterpret_cast<char*>((ptrint + (Alignment - 1)) / Alignment * Alignment));
|
||||
}
|
||||
extern const std::string engine_name();
|
||||
extern int get_system_time();
|
||||
extern int cpu_count();
|
||||
extern int Bioskey();
|
||||
|
||||
|
||||
// IsLittleEndian : true if and only if the binary is compiled on a little endian machine
|
||||
static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 };
|
||||
static inline const bool IsLittleEndian = (Le.c[0] == 4);
|
||||
////
|
||||
//// Debug
|
||||
////
|
||||
|
||||
extern bool dbg_show_mean;
|
||||
extern bool dbg_show_hit_rate;
|
||||
|
||||
// RunningAverage : a class to calculate a running average of a series of values.
|
||||
// For efficiency, all computations are done with integers.
|
||||
class RunningAverage {
|
||||
public:
|
||||
extern uint64_t dbg_cnt0;
|
||||
extern uint64_t dbg_cnt1;
|
||||
|
||||
// Reset the running average to rational value p / q
|
||||
void set(int64_t p, int64_t q)
|
||||
{ average = p * PERIOD * RESOLUTION / q; }
|
||||
extern void dbg_hit_on(bool b);
|
||||
extern void dbg_hit_on_c(bool c, bool b);
|
||||
extern void dbg_before();
|
||||
extern void dbg_after();
|
||||
extern void dbg_mean_of(int v);
|
||||
extern void dbg_print_hit_rate();
|
||||
extern void dbg_print_mean();
|
||||
extern void dbg_print_hit_rate(std::ofstream& logFile);
|
||||
extern void dbg_print_mean(std::ofstream& logFile);
|
||||
|
||||
// Update average with value v
|
||||
void update(int64_t v)
|
||||
{ average = RESOLUTION * v + (PERIOD - 1) * average / PERIOD; }
|
||||
|
||||
// Test if average is strictly greater than rational a / b
|
||||
bool is_greater(int64_t a, int64_t b) const
|
||||
{ return b * average > a * (PERIOD * RESOLUTION); }
|
||||
|
||||
int64_t value() const
|
||||
{ return average / (PERIOD * RESOLUTION); }
|
||||
|
||||
private :
|
||||
static constexpr int64_t PERIOD = 4096;
|
||||
static constexpr int64_t RESOLUTION = 1024;
|
||||
int64_t average;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t MaxSize>
|
||||
class ValueList {
|
||||
|
||||
public:
|
||||
std::size_t size() const { return size_; }
|
||||
void resize(std::size_t newSize) { size_ = newSize; }
|
||||
void push_back(const T& value) { values_[size_++] = value; }
|
||||
T& operator[](std::size_t index) { return values_[index]; }
|
||||
T* begin() { return values_; }
|
||||
T* end() { return values_ + size_; }
|
||||
const T& operator[](std::size_t index) const { return values_[index]; }
|
||||
const T* begin() const { return values_; }
|
||||
const T* end() const { return values_ + size_; }
|
||||
|
||||
void swap(ValueList& other) {
|
||||
const std::size_t maxSize = std::max(size_, other.size_);
|
||||
for (std::size_t i = 0; i < maxSize; ++i) {
|
||||
std::swap(values_[i], other.values_[i]);
|
||||
}
|
||||
std::swap(size_, other.size_);
|
||||
}
|
||||
|
||||
private:
|
||||
T values_[MaxSize];
|
||||
std::size_t size_ = 0;
|
||||
};
|
||||
|
||||
|
||||
/// sigmoid(t, x0, y0, C, P, Q) implements a sigmoid-like function using only integers,
|
||||
/// with the following properties:
|
||||
///
|
||||
/// - sigmoid is centered in (x0, y0)
|
||||
/// - sigmoid has amplitude [-P/Q , P/Q] instead of [-1 , +1]
|
||||
/// - limit is (y0 - P/Q) when t tends to -infinity
|
||||
/// - limit is (y0 + P/Q) when t tends to +infinity
|
||||
/// - the slope can be adjusted using C > 0, smaller C giving a steeper sigmoid
|
||||
/// - the slope of the sigmoid when t = x0 is P/(Q*C)
|
||||
/// - sigmoid is increasing with t when P > 0 and Q > 0
|
||||
/// - to get a decreasing sigmoid, change sign of P
|
||||
/// - mean value of the sigmoid is y0
|
||||
///
|
||||
/// Use <https://www.desmos.com/calculator/jhh83sqq92> to draw the sigmoid
|
||||
|
||||
inline int64_t sigmoid(int64_t t, int64_t x0,
|
||||
int64_t y0,
|
||||
int64_t C,
|
||||
int64_t P,
|
||||
int64_t Q)
|
||||
{
|
||||
assert(C > 0);
|
||||
assert(Q != 0);
|
||||
return y0 + P * (t-x0) / (Q * (std::abs(t-x0) + C)) ;
|
||||
}
|
||||
|
||||
|
||||
/// 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()); }
|
||||
};
|
||||
|
||||
inline uint64_t mul_hi64(uint64_t a, uint64_t b) {
|
||||
#if defined(__GNUC__) && defined(IS_64BIT)
|
||||
__extension__ typedef unsigned __int128 uint128;
|
||||
return ((uint128)a * (uint128)b) >> 64;
|
||||
#else
|
||||
uint64_t aL = (uint32_t)a, aH = a >> 32;
|
||||
uint64_t bL = (uint32_t)b, bH = b >> 32;
|
||||
uint64_t c1 = (aL * bL) >> 32;
|
||||
uint64_t c2 = aH * bL + c1;
|
||||
uint64_t c3 = aL * bH + (uint32_t)c2;
|
||||
return aH * bH + (c2 >> 32) + (c3 >> 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
namespace CommandLine {
|
||||
void init(int argc, char* argv[]);
|
||||
|
||||
extern std::string binaryDirectory; // path of the executable directory
|
||||
extern std::string workingDirectory; // path of the working directory
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef MISC_H_INCLUDED
|
||||
#endif // !defined(MISC_H_INCLUDED)
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "move.h"
|
||||
#include "piece.h"
|
||||
#include "position.h"
|
||||
|
||||
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
/// move_from_string() takes a position and a string as input, and attempts to
|
||||
/// convert the string to a move, using simple coordinate notation (g1f3,
|
||||
/// a7a8q, etc.). In order to correctly parse en passant captures and castling
|
||||
/// moves, we need the position. This function is not robust, and expects that
|
||||
/// the input move is legal and correctly formatted.
|
||||
|
||||
Move move_from_string(const Position& pos, const std::string& str) {
|
||||
|
||||
Square from, to;
|
||||
Piece piece;
|
||||
Color us = pos.side_to_move();
|
||||
|
||||
if (str.length() < 4)
|
||||
return MOVE_NONE;
|
||||
|
||||
// Read the from and to squares
|
||||
from = square_from_string(str.substr(0, 2));
|
||||
to = square_from_string(str.substr(2, 4));
|
||||
|
||||
// Find the moving piece
|
||||
piece = pos.piece_on(from);
|
||||
|
||||
// If the string has more than 4 characters, try to interpret the 5th
|
||||
// character as a promotion
|
||||
if (type_of_piece(piece) == PAWN && str.length() > 4)
|
||||
{
|
||||
switch (tolower(str[4])) {
|
||||
case 'n':
|
||||
return make_promotion_move(from, to, KNIGHT);
|
||||
case 'b':
|
||||
return make_promotion_move(from, to, BISHOP);
|
||||
case 'r':
|
||||
return make_promotion_move(from, to, ROOK);
|
||||
case 'q':
|
||||
return make_promotion_move(from, to, QUEEN);
|
||||
}
|
||||
}
|
||||
|
||||
if (piece == piece_of_color_and_type(us, KING))
|
||||
{
|
||||
// Is this a castling move? A king move is assumed to be a castling
|
||||
// move if the destination square is occupied by a friendly rook, or
|
||||
// if the distance between the source and destination squares is more
|
||||
// than 1.
|
||||
if (pos.piece_on(to) == piece_of_color_and_type(us, ROOK))
|
||||
return make_castle_move(from, to);
|
||||
|
||||
else if (square_distance(from, to) > 1)
|
||||
{
|
||||
// This is a castling move, but we have to translate it to the
|
||||
// internal "king captures rook" representation.
|
||||
SquareDelta delta = (to > from ? DELTA_E : DELTA_W);
|
||||
Square s = from + delta;
|
||||
while (relative_rank(us, s) == RANK_1 && pos.piece_on(s) != piece_of_color_and_type(us, ROOK))
|
||||
s += delta;
|
||||
|
||||
return (relative_rank(us, s) == RANK_1 ? make_castle_move(from, s) : MOVE_NONE);
|
||||
}
|
||||
}
|
||||
else if (piece == piece_of_color_and_type(us, PAWN))
|
||||
{
|
||||
// En passant move? We assume that a pawn move is an en passant move
|
||||
// without further testing if the destination square is epSquare.
|
||||
if (to == pos.ep_square())
|
||||
return make_ep_move(from, to);
|
||||
}
|
||||
return make_move(from, to);
|
||||
}
|
||||
|
||||
|
||||
/// move_to_string() converts a move to a string in coordinate notation
|
||||
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we
|
||||
/// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
|
||||
/// Chess960 mode.
|
||||
|
||||
const std::string move_to_string(Move move) {
|
||||
|
||||
std::string str;
|
||||
Square from = move_from(move);
|
||||
Square to = move_to(move);
|
||||
|
||||
if (move == MOVE_NONE)
|
||||
str = "(none)";
|
||||
else if (move == MOVE_NULL)
|
||||
str = "0000";
|
||||
else
|
||||
{
|
||||
if (!Chess960)
|
||||
{
|
||||
if (move_is_short_castle(move))
|
||||
return (from == SQ_E1 ? "e1g1" : "e8g8");
|
||||
|
||||
if (move_is_long_castle(move))
|
||||
return (from == SQ_E1 ? "e1c1" : "e8c8");
|
||||
}
|
||||
str = square_to_string(from) + square_to_string(to);
|
||||
if (move_is_promotion(move))
|
||||
str += piece_type_to_char(move_promotion_piece(move), false);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/// Overload the << operator, to make it easier to print moves.
|
||||
|
||||
std::ostream &operator << (std::ostream& os, Move m) {
|
||||
|
||||
return os << move_to_string(m);
|
||||
}
|
||||
|
||||
|
||||
/// move_is_ok(), for debugging.
|
||||
|
||||
bool move_is_ok(Move m) {
|
||||
|
||||
return square_is_ok(move_from(m)) && square_is_ok(move_to(m));
|
||||
}
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(MOVE_H_INCLUDED)
|
||||
#define MOVE_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "misc.h"
|
||||
#include "piece.h"
|
||||
#include "square.h"
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
class Position;
|
||||
|
||||
/// A move needs 17 bits to be stored
|
||||
///
|
||||
/// bit 0- 5: destination square (from 0 to 63)
|
||||
/// bit 6-11: origin square (from 0 to 63)
|
||||
/// bit 12-14: promotion piece type
|
||||
/// bit 15: en passant flag
|
||||
/// bit 16: castle flag
|
||||
///
|
||||
/// 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, 0 and 1 respectively.
|
||||
|
||||
enum Move {
|
||||
MOVE_NONE = 0,
|
||||
MOVE_NULL = 65
|
||||
};
|
||||
|
||||
|
||||
struct MoveStack {
|
||||
Move move;
|
||||
int score;
|
||||
};
|
||||
|
||||
// Note that operator< is set up such that sorting will be in descending order
|
||||
inline bool operator<(const MoveStack& f, const MoveStack& s) { return s.score < f.score; }
|
||||
|
||||
// An helper insertion sort implementation
|
||||
template<typename T>
|
||||
inline void insertion_sort(T* firstMove, T* lastMove)
|
||||
{
|
||||
T value;
|
||||
T *cur, *p, *d;
|
||||
|
||||
if (firstMove != lastMove)
|
||||
for (cur = firstMove + 1; cur != lastMove; cur++)
|
||||
{
|
||||
p = d = cur;
|
||||
value = *p--;
|
||||
if (value < *p)
|
||||
{
|
||||
do *d = *p;
|
||||
while (--d != firstMove && value < *--p);
|
||||
*d = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Our dedicated sort in range [firstMove, lastMove), it is well
|
||||
// tuned for non-captures where we have a lot of zero scored moves.
|
||||
template<typename T>
|
||||
inline void sort_moves(T* firstMove, T* lastMove)
|
||||
{
|
||||
T tmp;
|
||||
T *p, *d;
|
||||
|
||||
d = lastMove;
|
||||
p = firstMove - 1;
|
||||
|
||||
d->score = -1; // right guard
|
||||
|
||||
// Split positives vs non-positives
|
||||
do {
|
||||
while ((++p)->score > 0);
|
||||
|
||||
if (p != d)
|
||||
{
|
||||
while (--d != p && d->score <= 0);
|
||||
|
||||
tmp = *p;
|
||||
*p = *d;
|
||||
*d = tmp;
|
||||
}
|
||||
|
||||
} while (p != d);
|
||||
|
||||
// Sort positives
|
||||
insertion_sort<T>(firstMove, p);
|
||||
|
||||
d = lastMove;
|
||||
p--;
|
||||
|
||||
// Split zero vs negatives
|
||||
do {
|
||||
while ((++p)->score == 0);
|
||||
|
||||
if (p != d)
|
||||
{
|
||||
while (--d != p && d->score < 0);
|
||||
|
||||
tmp = *p;
|
||||
*p = *d;
|
||||
*d = tmp;
|
||||
}
|
||||
|
||||
} while (p != d);
|
||||
|
||||
// Sort negatives
|
||||
insertion_sort<T>(p, lastMove);
|
||||
}
|
||||
|
||||
// Picks up the best move in range [curMove, lastMove), one per cycle.
|
||||
// It is faster then sorting all the moves in advance when moves are few,
|
||||
// as normally are the possible captures. Note that is not a stable alghoritm.
|
||||
template<typename T>
|
||||
inline T pick_best(T* curMove, T* lastMove)
|
||||
{
|
||||
T bestMove, tmp;
|
||||
|
||||
bestMove = *curMove;
|
||||
while (++curMove != lastMove)
|
||||
{
|
||||
if (*curMove < bestMove)
|
||||
{
|
||||
tmp = *curMove;
|
||||
*curMove = bestMove;
|
||||
bestMove = tmp;
|
||||
}
|
||||
}
|
||||
return bestMove;
|
||||
}
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
inline Square move_from(Move m) {
|
||||
return Square((int(m) >> 6) & 0x3F);
|
||||
}
|
||||
|
||||
inline Square move_to(Move m) {
|
||||
return Square(m & 0x3F);
|
||||
}
|
||||
|
||||
inline PieceType move_promotion_piece(Move m) {
|
||||
return PieceType((int(m) >> 12) & 7);
|
||||
}
|
||||
|
||||
inline int move_is_special(Move m) {
|
||||
return m & (0x1F << 12);
|
||||
}
|
||||
|
||||
inline int move_is_promotion(Move m) {
|
||||
return m & (7 << 12);
|
||||
}
|
||||
|
||||
inline int move_is_ep(Move m) {
|
||||
return m & (1 << 15);
|
||||
}
|
||||
|
||||
inline int move_is_castle(Move m) {
|
||||
return m & (1 << 16);
|
||||
}
|
||||
|
||||
inline bool move_is_short_castle(Move m) {
|
||||
return move_is_castle(m) && (move_to(m) > move_from(m));
|
||||
}
|
||||
|
||||
inline bool move_is_long_castle(Move m) {
|
||||
return move_is_castle(m) && (move_to(m) < move_from(m));
|
||||
}
|
||||
|
||||
inline Move make_promotion_move(Square from, Square to, PieceType promotion) {
|
||||
return Move(int(to) | (int(from) << 6) | (int(promotion) << 12));
|
||||
}
|
||||
|
||||
inline Move make_move(Square from, Square to) {
|
||||
return Move(int(to) | (int(from) << 6));
|
||||
}
|
||||
|
||||
inline Move make_castle_move(Square from, Square to) {
|
||||
return Move(int(to) | (int(from) << 6) | (1 << 16));
|
||||
}
|
||||
|
||||
inline Move make_ep_move(Square from, Square to) {
|
||||
return Move(int(to) | (int(from) << 6) | (1 << 15));
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern std::ostream& operator<<(std::ostream &os, Move m);
|
||||
extern Move move_from_string(const Position &pos, const std::string &str);
|
||||
extern const std::string move_to_string(Move m);
|
||||
extern bool move_is_ok(Move m);
|
||||
|
||||
|
||||
#endif // !defined(MOVE_H_INCLUDED)
|
||||
+620
-203
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,261 +17,677 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "bitcount.h"
|
||||
#include "movegen.h"
|
||||
#include "position.h"
|
||||
|
||||
namespace Stockfish {
|
||||
// Simple macro to wrap a very common while loop, no facny, no flexibility,
|
||||
// hardcoded list name 'mlist' and from square 'from'.
|
||||
#define SERIALIZE_MOVES(b) while (b) (*mlist++).move = make_move(from, pop_1st_bit(&b))
|
||||
|
||||
// Version used for pawns, where the 'from' square is given as a delta from the 'to' square
|
||||
#define SERIALIZE_MOVES_D(b, d) while (b) { to = pop_1st_bit(&b); (*mlist++).move = make_move(to + (d), to); }
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
template<GenType Type, Direction D>
|
||||
ExtMove* make_promotions(ExtMove* moveList, Square to) {
|
||||
enum CastlingSide {
|
||||
KING_SIDE,
|
||||
QUEEN_SIDE
|
||||
};
|
||||
|
||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
*moveList++ = make<PROMOTION>(to - D, to, QUEEN);
|
||||
enum MoveType {
|
||||
CAPTURE,
|
||||
NON_CAPTURE,
|
||||
CHECK,
|
||||
EVASION
|
||||
};
|
||||
|
||||
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
{
|
||||
*moveList++ = make<PROMOTION>(to - D, to, ROOK);
|
||||
*moveList++ = make<PROMOTION>(to - D, to, BISHOP);
|
||||
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
|
||||
}
|
||||
// Helper templates
|
||||
template<CastlingSide Side>
|
||||
MoveStack* generate_castle_moves(const Position&, MoveStack*);
|
||||
|
||||
return moveList;
|
||||
template<Color Us, MoveType Type>
|
||||
MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square);
|
||||
|
||||
// Template generate_piece_moves (captures and non-captures) with specializations and overloads
|
||||
template<PieceType>
|
||||
MoveStack* generate_piece_moves(const Position&, MoveStack*, Color, Bitboard);
|
||||
|
||||
template<>
|
||||
MoveStack* generate_piece_moves<KING>(const Position&, MoveStack*, Color, Bitboard);
|
||||
|
||||
template<PieceType Piece, MoveType Type>
|
||||
inline MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) {
|
||||
|
||||
assert(Piece == PAWN);
|
||||
assert(Type == CAPTURE || Type == NON_CAPTURE || Type == EVASION);
|
||||
|
||||
return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m, t, SQ_NONE)
|
||||
: generate_pawn_moves<BLACK, Type>(p, m, t, SQ_NONE));
|
||||
}
|
||||
|
||||
// Templates for non-capture checks generation
|
||||
|
||||
template<Color Us, GenType Type>
|
||||
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
|
||||
template<PieceType Piece>
|
||||
MoveStack* generate_discovered_checks(const Position&, MoveStack*, Square);
|
||||
|
||||
constexpr Color Them = ~Us;
|
||||
constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
|
||||
constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
|
||||
constexpr Direction Up = pawn_push(Us);
|
||||
constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
|
||||
constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
|
||||
template<PieceType>
|
||||
MoveStack* generate_direct_checks(const Position&, MoveStack*, Color, Bitboard, Square);
|
||||
|
||||
const Bitboard emptySquares = ~pos.pieces();
|
||||
const Bitboard enemies = Type == EVASIONS ? pos.checkers()
|
||||
: pos.pieces(Them);
|
||||
template<>
|
||||
inline MoveStack* generate_direct_checks<PAWN>(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) {
|
||||
|
||||
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
|
||||
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
|
||||
return (us == WHITE ? generate_pawn_moves<WHITE, CHECK>(p, m, dc, ksq)
|
||||
: generate_pawn_moves<BLACK, CHECK>(p, m, dc, ksq));
|
||||
}
|
||||
}
|
||||
|
||||
// Single and double pawn pushes, no promotions
|
||||
if (Type != CAPTURES)
|
||||
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
|
||||
/// generate_captures() generates all pseudo-legal captures and queen
|
||||
/// promotions. Returns a pointer to the end of the move list.
|
||||
|
||||
MoveStack* generate_captures(const Position& pos, MoveStack* mlist) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
assert(!pos.is_check());
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
Bitboard target = pos.pieces_of_color(opposite_color(us));
|
||||
|
||||
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<PAWN, CAPTURE>(pos, mlist, us, target);
|
||||
return generate_piece_moves<KING>(pos, mlist, us, target);
|
||||
}
|
||||
|
||||
|
||||
/// generate_noncaptures() generates all pseudo-legal non-captures and
|
||||
/// underpromotions. Returns a pointer to the end of the move list.
|
||||
|
||||
MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
assert(!pos.is_check());
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
Bitboard target = pos.empty_squares();
|
||||
|
||||
mlist = generate_piece_moves<PAWN, NON_CAPTURE>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<KING>(pos, mlist, us, target);
|
||||
mlist = generate_castle_moves<KING_SIDE>(pos, mlist);
|
||||
return generate_castle_moves<QUEEN_SIDE>(pos, mlist);
|
||||
}
|
||||
|
||||
|
||||
/// generate_non_capture_checks() generates all pseudo-legal non-captures and knight
|
||||
/// underpromotions that give check. Returns a pointer to the end of the move list.
|
||||
|
||||
MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
assert(!pos.is_check());
|
||||
|
||||
Bitboard b, dc;
|
||||
Square from;
|
||||
Color us = pos.side_to_move();
|
||||
Square ksq = pos.king_square(opposite_color(us));
|
||||
|
||||
assert(pos.piece_on(ksq) == piece_of_color_and_type(opposite_color(us), KING));
|
||||
|
||||
// Discovered non-capture checks
|
||||
b = dc = pos.discovered_check_candidates(us);
|
||||
|
||||
while (b)
|
||||
{
|
||||
from = pop_1st_bit(&b);
|
||||
switch (pos.type_of_piece_on(from))
|
||||
{
|
||||
case PAWN: /* Will be generated togheter with pawns direct checks */ break;
|
||||
case KNIGHT: mlist = generate_discovered_checks<KNIGHT>(pos, mlist, from); break;
|
||||
case BISHOP: mlist = generate_discovered_checks<BISHOP>(pos, mlist, from); break;
|
||||
case ROOK: mlist = generate_discovered_checks<ROOK>(pos, mlist, from); break;
|
||||
case KING: mlist = generate_discovered_checks<KING>(pos, mlist, from); break;
|
||||
default: assert(false); break;
|
||||
}
|
||||
}
|
||||
|
||||
// Direct non-capture checks
|
||||
mlist = generate_direct_checks<PAWN>(pos, mlist, us, dc, ksq);
|
||||
mlist = generate_direct_checks<KNIGHT>(pos, mlist, us, dc, ksq);
|
||||
mlist = generate_direct_checks<BISHOP>(pos, mlist, us, dc, ksq);
|
||||
mlist = generate_direct_checks<ROOK>(pos, mlist, us, dc, ksq);
|
||||
return generate_direct_checks<QUEEN>(pos, mlist, us, dc, ksq);
|
||||
}
|
||||
|
||||
|
||||
/// generate_evasions() generates all pseudo-legal check evasions when
|
||||
/// the side to move is in check. Returns a pointer to the end of the move list.
|
||||
|
||||
MoveStack* generate_evasions(const Position& pos, MoveStack* mlist) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
assert(pos.is_check());
|
||||
|
||||
Bitboard b, target;
|
||||
Square from, checksq;
|
||||
int checkersCnt = 0;
|
||||
Color us = pos.side_to_move();
|
||||
Square ksq = pos.king_square(us);
|
||||
Bitboard checkers = pos.checkers();
|
||||
Bitboard sliderAttacks = EmptyBoardBB;
|
||||
|
||||
assert(pos.piece_on(ksq) == piece_of_color_and_type(us, KING));
|
||||
assert(checkers);
|
||||
|
||||
// Find squares attacked by slider checkers, we will remove
|
||||
// them from the king evasions set so to early skip known
|
||||
// illegal moves and avoid an useless legality check later.
|
||||
b = checkers;
|
||||
do
|
||||
{
|
||||
checkersCnt++;
|
||||
checksq = pop_1st_bit(&b);
|
||||
|
||||
assert(pos.color_of_piece_on(checksq) == opposite_color(us));
|
||||
|
||||
switch (pos.type_of_piece_on(checksq))
|
||||
{
|
||||
case BISHOP: sliderAttacks |= BishopPseudoAttacks[checksq]; break;
|
||||
case ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break;
|
||||
case QUEEN:
|
||||
// In case of a queen remove also squares attacked in the other direction to
|
||||
// avoid possible illegal moves when queen and king are on adjacent squares.
|
||||
if (direction_is_straight(checksq, ksq))
|
||||
sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq);
|
||||
else
|
||||
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while (b);
|
||||
|
||||
// Generate evasions for king, capture and non capture moves
|
||||
b = pos.attacks_from<KING>(ksq) & ~pos.pieces_of_color(us) & ~sliderAttacks;
|
||||
from = ksq;
|
||||
SERIALIZE_MOVES(b);
|
||||
|
||||
// Generate evasions for other pieces only if not double check
|
||||
if (checkersCnt > 1)
|
||||
return mlist;
|
||||
|
||||
// Find squares where a blocking evasion or a capture of the
|
||||
// checker piece is possible.
|
||||
target = squares_between(checksq, ksq) | checkers;
|
||||
|
||||
mlist = generate_piece_moves<PAWN, EVASION>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
|
||||
return generate_piece_moves<QUEEN>(pos, mlist, us, target);
|
||||
}
|
||||
|
||||
|
||||
/// generate_moves() computes a complete list of legal or pseudo-legal moves in
|
||||
/// the current position. This function is not very fast, and should be used
|
||||
/// only in non time-critical paths.
|
||||
|
||||
MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLegal) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
|
||||
MoveStack *last, *cur = mlist;
|
||||
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
|
||||
|
||||
// Generate pseudo-legal moves
|
||||
if (pos.is_check())
|
||||
last = generate_evasions(pos, mlist);
|
||||
else
|
||||
last = generate_noncaptures(pos, generate_captures(pos, mlist));
|
||||
|
||||
if (pseudoLegal)
|
||||
return last;
|
||||
|
||||
// Remove illegal moves from the list
|
||||
while (cur != last)
|
||||
if (pos.pl_move_is_legal(cur->move, pinned))
|
||||
cur++;
|
||||
else
|
||||
cur->move = (--last)->move;
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
|
||||
/// move_is_legal() takes a position and a (not necessarily pseudo-legal)
|
||||
/// move and tests whether the move is legal. This version is not very fast
|
||||
/// and should be used only in non time-critical paths.
|
||||
|
||||
bool move_is_legal(const Position& pos, const Move m) {
|
||||
|
||||
MoveStack mlist[256];
|
||||
MoveStack *cur, *last = generate_moves(pos, mlist, true);
|
||||
|
||||
for (cur = mlist; cur != last; cur++)
|
||||
if (cur->move == m)
|
||||
return pos.pl_move_is_legal(m, pos.pinned_pieces(pos.side_to_move()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// Fast version of move_is_legal() that takes a position a move and a
|
||||
/// bitboard of pinned pieces as input, and tests whether the move is legal.
|
||||
|
||||
bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
assert(move_is_ok(m));
|
||||
assert(pinned == pos.pinned_pieces(pos.side_to_move()));
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
Color them = opposite_color(us);
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Piece pc = pos.piece_on(from);
|
||||
|
||||
// Use a slower but simpler function for uncommon cases
|
||||
if (move_is_ep(m) || move_is_castle(m))
|
||||
return move_is_legal(pos, m);
|
||||
|
||||
// If the from square is not occupied by a piece belonging to the side to
|
||||
// move, the move is obviously not legal.
|
||||
if (color_of_piece(pc) != us)
|
||||
return false;
|
||||
|
||||
// The destination square cannot be occupied by a friendly piece
|
||||
if (pos.color_of_piece_on(to) == us)
|
||||
return false;
|
||||
|
||||
// Handle the special case of a pawn move
|
||||
if (type_of_piece(pc) == PAWN)
|
||||
{
|
||||
// Move direction must be compatible with pawn color
|
||||
int direction = to - from;
|
||||
if ((us == WHITE) != (direction > 0))
|
||||
return false;
|
||||
|
||||
// A pawn move is a promotion iff the destination square is
|
||||
// on the 8/1th rank.
|
||||
if (( (square_rank(to) == RANK_8 && us == WHITE)
|
||||
||(square_rank(to) == RANK_1 && us != WHITE)) != bool(move_is_promotion(m)))
|
||||
return false;
|
||||
|
||||
// Proceed according to the square delta between the origin and
|
||||
// destination squares.
|
||||
switch (direction)
|
||||
{
|
||||
case DELTA_NW:
|
||||
case DELTA_NE:
|
||||
case DELTA_SW:
|
||||
case DELTA_SE:
|
||||
// Capture. The destination square must be occupied by an enemy
|
||||
// piece (en passant captures was handled earlier).
|
||||
if (pos.color_of_piece_on(to) != them)
|
||||
return false;
|
||||
break;
|
||||
|
||||
case DELTA_N:
|
||||
case DELTA_S:
|
||||
// Pawn push. The destination square must be empty.
|
||||
if (!pos.square_is_empty(to))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case DELTA_NN:
|
||||
// Double white pawn push. The destination square must be on the fourth
|
||||
// rank, and both the destination square and the square between the
|
||||
// source and destination squares must be empty.
|
||||
if ( square_rank(to) != RANK_4
|
||||
|| !pos.square_is_empty(to)
|
||||
|| !pos.square_is_empty(from + DELTA_N))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case DELTA_SS:
|
||||
// Double black pawn push. The destination square must be on the fifth
|
||||
// rank, and both the destination square and the square between the
|
||||
// source and destination squares must be empty.
|
||||
if ( square_rank(to) != RANK_5
|
||||
|| !pos.square_is_empty(to)
|
||||
|| !pos.square_is_empty(from + DELTA_S))
|
||||
return false;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
// The move is pseudo-legal, check if it is also legal
|
||||
return pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned);
|
||||
}
|
||||
|
||||
// Luckly we can handle all the other pieces in one go
|
||||
return bit_is_set(pos.attacks_from(pc, from), to)
|
||||
&& (pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned))
|
||||
&& !move_is_promotion(m);
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
template<PieceType Piece>
|
||||
MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
|
||||
|
||||
Bitboard b;
|
||||
Square from;
|
||||
const Square* ptr = pos.piece_list_begin(us, Piece);
|
||||
|
||||
while ((from = *ptr++) != SQ_NONE)
|
||||
{
|
||||
Bitboard b1 = shift<Up>(pawnsNotOn7) & emptySquares;
|
||||
Bitboard b2 = shift<Up>(b1 & TRank3BB) & emptySquares;
|
||||
b = pos.attacks_from<Piece>(from) & target;
|
||||
SERIALIZE_MOVES(b);
|
||||
}
|
||||
return mlist;
|
||||
}
|
||||
|
||||
if (Type == EVASIONS) // Consider only blocking squares
|
||||
template<>
|
||||
MoveStack* generate_piece_moves<KING>(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
|
||||
|
||||
Bitboard b;
|
||||
Square from = pos.king_square(us);
|
||||
|
||||
b = pos.attacks_from<KING>(from) & target;
|
||||
SERIALIZE_MOVES(b);
|
||||
return mlist;
|
||||
}
|
||||
|
||||
template<Color Us, SquareDelta Direction>
|
||||
inline Bitboard move_pawns(Bitboard p) {
|
||||
|
||||
if (Direction == DELTA_N)
|
||||
return Us == WHITE ? p << 8 : p >> 8;
|
||||
else if (Direction == DELTA_NE)
|
||||
return Us == WHITE ? p << 9 : p >> 7;
|
||||
else if (Direction == DELTA_NW)
|
||||
return Us == WHITE ? p << 7 : p >> 9;
|
||||
else
|
||||
return p;
|
||||
}
|
||||
|
||||
template<Color Us, MoveType Type, SquareDelta Diagonal>
|
||||
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces) {
|
||||
|
||||
// Calculate our parametrized parameters at compile time
|
||||
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
|
||||
const Bitboard TFileABB = (Diagonal == DELTA_NE ? FileABB : FileHBB);
|
||||
const SquareDelta TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE);
|
||||
const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
|
||||
const SquareDelta TTDELTA_NE = (Diagonal == DELTA_NE ? TDELTA_NE : TDELTA_NW);
|
||||
|
||||
Bitboard b1, b2;
|
||||
Square to;
|
||||
|
||||
// Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black)
|
||||
b1 = move_pawns<Us, Diagonal>(pawns) & ~TFileABB & enemyPieces;
|
||||
|
||||
// Capturing promotions and under-promotions
|
||||
if (b1 & TRank8BB)
|
||||
{
|
||||
b2 = b1 & TRank8BB;
|
||||
b1 &= ~TRank8BB;
|
||||
while (b2)
|
||||
{
|
||||
to = pop_1st_bit(&b2);
|
||||
|
||||
if (Type == CAPTURE || Type == EVASION)
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, QUEEN);
|
||||
|
||||
if (Type == NON_CAPTURE || Type == EVASION)
|
||||
{
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, ROOK);
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, BISHOP);
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT);
|
||||
}
|
||||
|
||||
// This is the only possible under promotion that can give a check
|
||||
// not already included in the queen-promotion. It is not sure that
|
||||
// the promoted knight will give check, but it doesn't worth to verify.
|
||||
if (Type == CHECK)
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize standard captures
|
||||
if (Type == CAPTURE || Type == EVASION)
|
||||
SERIALIZE_MOVES_D(b1, -TTDELTA_NE);
|
||||
|
||||
return mlist;
|
||||
}
|
||||
|
||||
template<Color Us, MoveType Type>
|
||||
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) {
|
||||
|
||||
// Calculate our parametrized parameters at compile time
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
|
||||
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
|
||||
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
|
||||
const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S);
|
||||
|
||||
Square to;
|
||||
Bitboard b1, b2, enemyPieces, emptySquares;
|
||||
Bitboard pawns = pos.pieces(PAWN, Us);
|
||||
|
||||
// Standard captures and capturing promotions and underpromotions
|
||||
if (Type == CAPTURE || Type == EVASION || (pawns & TRank7BB))
|
||||
{
|
||||
enemyPieces = (Type == CAPTURE ? target : pos.pieces_of_color(opposite_color(Us)));
|
||||
|
||||
if (Type == EVASION)
|
||||
enemyPieces &= target; // Capture only the checker piece
|
||||
|
||||
mlist = generate_pawn_captures<Us, Type, DELTA_NE>(mlist, pawns, enemyPieces);
|
||||
mlist = generate_pawn_captures<Us, Type, DELTA_NW>(mlist, pawns, enemyPieces);
|
||||
}
|
||||
|
||||
// Non-capturing promotions and underpromotions
|
||||
if (pawns & TRank7BB)
|
||||
{
|
||||
b1 = move_pawns<Us, DELTA_N>(pawns) & TRank8BB & pos.empty_squares();
|
||||
|
||||
if (Type == EVASION)
|
||||
b1 &= target; // Only blocking promotion pushes
|
||||
|
||||
while (b1)
|
||||
{
|
||||
to = pop_1st_bit(&b1);
|
||||
|
||||
if (Type == CAPTURE || Type == EVASION)
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, QUEEN);
|
||||
|
||||
if (Type == NON_CAPTURE || Type == EVASION)
|
||||
{
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, ROOK);
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, BISHOP);
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT);
|
||||
}
|
||||
|
||||
// This is the only possible under promotion that can give a check
|
||||
// not already included in the queen-promotion.
|
||||
if (Type == CHECK && bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(Them)))
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
// Standard pawn pushes and double pushes
|
||||
if (Type != CAPTURE)
|
||||
{
|
||||
emptySquares = (Type == NON_CAPTURE ? target : pos.empty_squares());
|
||||
|
||||
// Single and double pawn pushes
|
||||
b1 = move_pawns<Us, DELTA_N>(pawns) & emptySquares & ~TRank8BB;
|
||||
b2 = move_pawns<Us, DELTA_N>(b1 & TRank3BB) & emptySquares;
|
||||
|
||||
// Filter out unwanted pushes according to the move type
|
||||
if (Type == EVASION)
|
||||
{
|
||||
b1 &= target;
|
||||
b2 &= target;
|
||||
}
|
||||
|
||||
if (Type == QUIET_CHECKS)
|
||||
else if (Type == CHECK)
|
||||
{
|
||||
// To make a quiet check, you either make a direct check by pushing a pawn
|
||||
// or push a blocker pawn that is not on the same file as the enemy king.
|
||||
// Discovered check promotion has been already generated amongst the captures.
|
||||
Square ksq = pos.square<KING>(Them);
|
||||
Bitboard dcCandidatePawns = pos.blockers_for_king(Them) & ~file_bb(ksq);
|
||||
b1 &= pawn_attacks_bb(Them, ksq) | shift< Up>(dcCandidatePawns);
|
||||
b2 &= pawn_attacks_bb(Them, ksq) | shift<Up+Up>(dcCandidatePawns);
|
||||
// Pawn moves which give direct cheks
|
||||
b1 &= pos.attacks_from<PAWN>(ksq, Them);
|
||||
b2 &= pos.attacks_from<PAWN>(ksq, Them);
|
||||
|
||||
// Pawn moves which gives discovered check. This is possible only if
|
||||
// the pawn is not on the same file as the enemy king, because we
|
||||
// don't generate captures.
|
||||
if (pawns & target) // For CHECK type target is dc bitboard
|
||||
{
|
||||
Bitboard dc1 = move_pawns<Us, DELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares & ~TRank8BB;
|
||||
Bitboard dc2 = move_pawns<Us, DELTA_N>(dc1 & TRank3BB) & emptySquares;
|
||||
|
||||
b1 |= dc1;
|
||||
b2 |= dc2;
|
||||
}
|
||||
}
|
||||
SERIALIZE_MOVES_D(b1, -TDELTA_N);
|
||||
SERIALIZE_MOVES_D(b2, -TDELTA_N -TDELTA_N);
|
||||
}
|
||||
|
||||
// En passant captures
|
||||
if ((Type == CAPTURE || Type == EVASION) && pos.ep_square() != SQ_NONE)
|
||||
{
|
||||
assert(Us != WHITE || square_rank(pos.ep_square()) == RANK_6);
|
||||
assert(Us != BLACK || square_rank(pos.ep_square()) == RANK_3);
|
||||
|
||||
// An en passant capture can be an evasion only if the checking piece
|
||||
// is the double pushed pawn and so is in the target. Otherwise this
|
||||
// is a discovery check and we are forced to do otherwise.
|
||||
if (Type == EVASION && !bit_is_set(target, pos.ep_square() - TDELTA_N))
|
||||
return mlist;
|
||||
|
||||
b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them);
|
||||
|
||||
assert(b1 != EmptyBoardBB);
|
||||
|
||||
while (b1)
|
||||
{
|
||||
Square to = pop_lsb(b1);
|
||||
*moveList++ = make_move(to - Up, to);
|
||||
}
|
||||
|
||||
while (b2)
|
||||
{
|
||||
Square to = pop_lsb(b2);
|
||||
*moveList++ = make_move(to - Up - Up, to);
|
||||
to = pop_1st_bit(&b1);
|
||||
(*mlist++).move = make_ep_move(to, pos.ep_square());
|
||||
}
|
||||
}
|
||||
|
||||
// Promotions and underpromotions
|
||||
if (pawnsOn7)
|
||||
{
|
||||
Bitboard b1 = shift<UpRight>(pawnsOn7) & enemies;
|
||||
Bitboard b2 = shift<UpLeft >(pawnsOn7) & enemies;
|
||||
Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
|
||||
|
||||
if (Type == EVASIONS)
|
||||
b3 &= target;
|
||||
|
||||
while (b1)
|
||||
moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(b1));
|
||||
|
||||
while (b2)
|
||||
moveList = make_promotions<Type, UpLeft >(moveList, pop_lsb(b2));
|
||||
|
||||
while (b3)
|
||||
moveList = make_promotions<Type, Up >(moveList, pop_lsb(b3));
|
||||
}
|
||||
|
||||
// Standard and en passant captures
|
||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
{
|
||||
Bitboard b1 = shift<UpRight>(pawnsNotOn7) & enemies;
|
||||
Bitboard b2 = shift<UpLeft >(pawnsNotOn7) & enemies;
|
||||
|
||||
while (b1)
|
||||
{
|
||||
Square to = pop_lsb(b1);
|
||||
*moveList++ = make_move(to - UpRight, to);
|
||||
}
|
||||
|
||||
while (b2)
|
||||
{
|
||||
Square to = pop_lsb(b2);
|
||||
*moveList++ = make_move(to - UpLeft, to);
|
||||
}
|
||||
|
||||
if (pos.ep_square() != SQ_NONE)
|
||||
{
|
||||
assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6));
|
||||
|
||||
// An en passant capture cannot resolve a discovered check
|
||||
if (Type == EVASIONS && (target & (pos.ep_square() + Up)))
|
||||
return moveList;
|
||||
|
||||
b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square());
|
||||
|
||||
assert(b1);
|
||||
|
||||
while (b1)
|
||||
*moveList++ = make<EN_PASSANT>(pop_lsb(b1), pos.ep_square());
|
||||
}
|
||||
}
|
||||
|
||||
return moveList;
|
||||
return mlist;
|
||||
}
|
||||
|
||||
template<PieceType Piece>
|
||||
MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) {
|
||||
|
||||
template<Color Us, PieceType Pt, bool Checks>
|
||||
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
|
||||
assert(Piece != QUEEN);
|
||||
|
||||
static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()");
|
||||
|
||||
Bitboard bb = pos.pieces(Us, Pt);
|
||||
|
||||
while (bb)
|
||||
Bitboard b = pos.attacks_from<Piece>(from) & pos.empty_squares();
|
||||
if (Piece == KING)
|
||||
{
|
||||
Square from = pop_lsb(bb);
|
||||
Bitboard b = attacks_bb<Pt>(from, pos.pieces()) & target;
|
||||
|
||||
// To check, you either move freely a blocker or make a direct check.
|
||||
if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from)))
|
||||
b &= pos.check_squares(Pt);
|
||||
|
||||
while (b)
|
||||
*moveList++ = make_move(from, pop_lsb(b));
|
||||
Square ksq = pos.king_square(opposite_color(pos.side_to_move()));
|
||||
b &= ~QueenPseudoAttacks[ksq];
|
||||
}
|
||||
|
||||
return moveList;
|
||||
SERIALIZE_MOVES(b);
|
||||
return mlist;
|
||||
}
|
||||
|
||||
template<PieceType Piece>
|
||||
MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
|
||||
Bitboard dc, Square ksq) {
|
||||
assert(Piece != KING);
|
||||
|
||||
template<Color Us, GenType Type>
|
||||
ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
|
||||
Bitboard checkSqs, b;
|
||||
Square from;
|
||||
const Square* ptr = pos.piece_list_begin(us, Piece);
|
||||
|
||||
static_assert(Type != LEGAL, "Unsupported type in generate_all()");
|
||||
if ((from = *ptr++) == SQ_NONE)
|
||||
return mlist;
|
||||
|
||||
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations
|
||||
const Square ksq = pos.square<KING>(Us);
|
||||
Bitboard target;
|
||||
checkSqs = pos.attacks_from<Piece>(ksq) & pos.empty_squares();
|
||||
|
||||
// Skip generating non-king moves when in double check
|
||||
if (Type != EVASIONS || !more_than_one(pos.checkers()))
|
||||
do
|
||||
{
|
||||
target = Type == EVASIONS ? between_bb(ksq, lsb(pos.checkers()))
|
||||
: Type == NON_EVASIONS ? ~pos.pieces( Us)
|
||||
: Type == CAPTURES ? pos.pieces(~Us)
|
||||
: ~pos.pieces( ); // QUIETS || QUIET_CHECKS
|
||||
if ( (Piece == QUEEN && !(QueenPseudoAttacks[from] & checkSqs))
|
||||
|| (Piece == ROOK && !(RookPseudoAttacks[from] & checkSqs))
|
||||
|| (Piece == BISHOP && !(BishopPseudoAttacks[from] & checkSqs)))
|
||||
continue;
|
||||
|
||||
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, KNIGHT, Checks>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, BISHOP, Checks>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, ROOK, Checks>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, QUEEN, Checks>(pos, moveList, target);
|
||||
}
|
||||
if (dc && bit_is_set(dc, from))
|
||||
continue;
|
||||
|
||||
if (!Checks || pos.blockers_for_king(~Us) & ksq)
|
||||
{
|
||||
Bitboard b = attacks_bb<KING>(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target);
|
||||
if (Checks)
|
||||
b &= ~attacks_bb<QUEEN>(pos.square<KING>(~Us));
|
||||
b = pos.attacks_from<Piece>(from) & checkSqs;
|
||||
SERIALIZE_MOVES(b);
|
||||
|
||||
while (b)
|
||||
*moveList++ = make_move(ksq, pop_lsb(b));
|
||||
} while ((from = *ptr++) != SQ_NONE);
|
||||
|
||||
if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING))
|
||||
for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } )
|
||||
if (!pos.castling_impeded(cr) && pos.can_castle(cr))
|
||||
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(cr));
|
||||
}
|
||||
|
||||
return moveList;
|
||||
return mlist;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
template<CastlingSide Side>
|
||||
MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist) {
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
|
||||
/// <CAPTURES> Generates all pseudo-legal captures plus queen promotions
|
||||
/// <QUIETS> Generates all pseudo-legal non-captures and underpromotions
|
||||
/// <EVASIONS> Generates all pseudo-legal check evasions when the side to move is in check
|
||||
/// <QUIET_CHECKS> Generates all pseudo-legal non-captures giving check, except castling and promotions
|
||||
/// <NON_EVASIONS> Generates all pseudo-legal captures and non-captures
|
||||
///
|
||||
/// Returns a pointer to the end of the move list.
|
||||
if ( (Side == KING_SIDE && pos.can_castle_kingside(us))
|
||||
||(Side == QUEEN_SIDE && pos.can_castle_queenside(us)))
|
||||
{
|
||||
Color them = opposite_color(us);
|
||||
Square ksq = pos.king_square(us);
|
||||
|
||||
template<GenType Type>
|
||||
ExtMove* generate(const Position& pos, ExtMove* moveList) {
|
||||
assert(pos.piece_on(ksq) == piece_of_color_and_type(us, KING));
|
||||
|
||||
static_assert(Type != LEGAL, "Unsupported type in generate()");
|
||||
assert((Type == EVASIONS) == (bool)pos.checkers());
|
||||
Square rsq = (Side == KING_SIDE ? pos.initial_kr_square(us) : pos.initial_qr_square(us));
|
||||
Square s1 = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
|
||||
Square s2 = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1);
|
||||
Square s;
|
||||
bool illegal = false;
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
assert(pos.piece_on(rsq) == piece_of_color_and_type(us, ROOK));
|
||||
|
||||
return us == WHITE ? generate_all<WHITE, Type>(pos, moveList)
|
||||
: generate_all<BLACK, Type>(pos, moveList);
|
||||
// It is a bit complicated to correctly handle Chess960
|
||||
for (s = Min(ksq, s1); s <= Max(ksq, s1); s++)
|
||||
if ( (s != ksq && s != rsq && pos.square_is_occupied(s))
|
||||
||(pos.attackers_to(s) & pos.pieces_of_color(them)))
|
||||
illegal = true;
|
||||
|
||||
for (s = Min(rsq, s2); s <= Max(rsq, s2); s++)
|
||||
if (s != ksq && s != rsq && pos.square_is_occupied(s))
|
||||
illegal = true;
|
||||
|
||||
if ( Side == QUEEN_SIDE
|
||||
&& square_file(rsq) == FILE_B
|
||||
&& ( pos.piece_on(relative_square(us, SQ_A1)) == piece_of_color_and_type(them, ROOK)
|
||||
|| pos.piece_on(relative_square(us, SQ_A1)) == piece_of_color_and_type(them, QUEEN)))
|
||||
illegal = true;
|
||||
|
||||
if (!illegal)
|
||||
(*mlist++).move = make_castle_move(ksq, rsq);
|
||||
}
|
||||
return mlist;
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit template instantiations
|
||||
template ExtMove* generate<CAPTURES>(const Position&, ExtMove*);
|
||||
template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
|
||||
template ExtMove* generate<EVASIONS>(const Position&, ExtMove*);
|
||||
template ExtMove* generate<QUIET_CHECKS>(const Position&, ExtMove*);
|
||||
template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
|
||||
|
||||
|
||||
/// generate<LEGAL> generates all the legal moves in the given position
|
||||
|
||||
template<>
|
||||
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
Bitboard pinned = pos.blockers_for_king(us) & pos.pieces(us);
|
||||
Square ksq = pos.square<KING>(us);
|
||||
ExtMove* cur = moveList;
|
||||
|
||||
moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList)
|
||||
: generate<NON_EVASIONS>(pos, moveList);
|
||||
while (cur != moveList)
|
||||
if ( ((pinned && pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT)
|
||||
&& !pos.legal(*cur))
|
||||
*cur = (--moveList)->move;
|
||||
else
|
||||
++cur;
|
||||
|
||||
return moveList;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+19
-52
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,62 +17,28 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MOVEGEN_H_INCLUDED
|
||||
|
||||
#if !defined(MOVEGEN_H_INCLUDED)
|
||||
#define MOVEGEN_H_INCLUDED
|
||||
|
||||
#include <algorithm>
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "types.h"
|
||||
#include "position.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
class Position;
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
enum GenType {
|
||||
CAPTURES,
|
||||
QUIETS,
|
||||
QUIET_CHECKS,
|
||||
EVASIONS,
|
||||
NON_EVASIONS,
|
||||
LEGAL
|
||||
};
|
||||
extern MoveStack* generate_captures(const Position& pos, MoveStack* mlist);
|
||||
extern MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist);
|
||||
extern MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist);
|
||||
extern MoveStack* generate_evasions(const Position& pos, MoveStack* mlist);
|
||||
extern MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLegal = false);
|
||||
extern bool move_is_legal(const Position& pos, const Move m, Bitboard pinned);
|
||||
extern bool move_is_legal(const Position& pos, const Move m);
|
||||
|
||||
struct ExtMove {
|
||||
Move move;
|
||||
int value;
|
||||
|
||||
operator Move() const { return move; }
|
||||
void operator=(Move m) { move = m; }
|
||||
|
||||
// Inhibit unwanted implicit conversions to Move
|
||||
// with an ambiguity that yields to a compile error.
|
||||
operator float() const = delete;
|
||||
};
|
||||
|
||||
inline bool operator<(const ExtMove& f, const ExtMove& s) {
|
||||
return f.value < s.value;
|
||||
}
|
||||
|
||||
template<GenType>
|
||||
ExtMove* generate(const Position& pos, ExtMove* moveList);
|
||||
|
||||
/// The MoveList struct is a simple wrapper around generate(). It sometimes comes
|
||||
/// in handy to use this class instead of the low level generate() function.
|
||||
template<GenType T>
|
||||
struct MoveList {
|
||||
|
||||
explicit MoveList(const Position& pos) : last(generate<T>(pos, moveList)) {}
|
||||
const ExtMove* begin() const { return moveList; }
|
||||
const ExtMove* end() const { return last; }
|
||||
size_t size() const { return last - moveList; }
|
||||
bool contains(Move move) const {
|
||||
return std::find(begin(), end(), move) != end();
|
||||
}
|
||||
|
||||
private:
|
||||
ExtMove moveList[MAX_MOVES], *last;
|
||||
};
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef MOVEGEN_H_INCLUDED
|
||||
#endif // !defined(MOVEGEN_H_INCLUDED)
|
||||
|
||||
+303
-249
@@ -1,12 +1,14 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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
|
||||
@@ -16,291 +18,343 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "history.h"
|
||||
#include "movegen.h"
|
||||
#include "movepick.h"
|
||||
#include "search.h"
|
||||
#include "value.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
enum Stages {
|
||||
MAIN_TT, CAPTURE_INIT, GOOD_CAPTURE, REFUTATION, QUIET_INIT, QUIET, BAD_CAPTURE,
|
||||
EVASION_TT, EVASION_INIT, EVASION,
|
||||
PROBCUT_TT, PROBCUT_INIT, PROBCUT,
|
||||
QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK
|
||||
enum MovegenPhase {
|
||||
PH_TT_MOVES, // Transposition table move and mate killer
|
||||
PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= 0
|
||||
PH_KILLERS, // Killer moves from the current ply
|
||||
PH_NONCAPTURES, // Non-captures and underpromotions
|
||||
PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < 0
|
||||
PH_EVASIONS, // Check evasions
|
||||
PH_QCAPTURES, // Captures in quiescence search
|
||||
PH_QCHECKS, // Non-capture checks in quiescence search
|
||||
PH_STOP
|
||||
};
|
||||
|
||||
// partial_insertion_sort() sorts moves in descending order up to and including
|
||||
// a given limit. The order of moves smaller than the limit is left unspecified.
|
||||
void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
|
||||
|
||||
for (ExtMove *sortedEnd = begin, *p = begin + 1; p < end; ++p)
|
||||
if (p->value >= limit)
|
||||
{
|
||||
ExtMove tmp = *p, *q;
|
||||
*p = *++sortedEnd;
|
||||
for (q = sortedEnd; q != begin && *(q - 1) < tmp; --q)
|
||||
*q = *(q - 1);
|
||||
*q = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
CACHE_LINE_ALIGNMENT
|
||||
const uint8_t MainSearchPhaseTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
|
||||
const uint8_t EvasionsPhaseTable[] = { PH_TT_MOVES, PH_EVASIONS, PH_STOP};
|
||||
const uint8_t QsearchWithChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP};
|
||||
const uint8_t QsearchWithoutChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP};
|
||||
}
|
||||
|
||||
|
||||
/// Constructors of the MovePicker class. As arguments we pass information
|
||||
/// to help it to return the (presumably) good moves first, to decide which
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
|
||||
/// Constructor for the MovePicker class. Apart from the position for which
|
||||
/// it is asked to pick legal moves, MovePicker also wants some information
|
||||
/// to help it to return the presumably good moves first, to decide which
|
||||
/// moves to return (in the quiescence search, for instance, we only want to
|
||||
/// search captures, promotions, and some checks) and how important good move
|
||||
/// ordering is at the current node.
|
||||
/// search captures, promotions and some checks) and about how important good
|
||||
/// move ordering is at the current node.
|
||||
|
||||
/// MovePicker constructor for the main search
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
|
||||
const CapturePieceToHistory* cph,
|
||||
const PieceToHistory** ch,
|
||||
Move cm,
|
||||
const Move* killers)
|
||||
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch),
|
||||
ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d)
|
||||
{
|
||||
assert(d > 0);
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
|
||||
const History& h, SearchStack* ss) : pos(p), H(h) {
|
||||
int searchTT = ttm;
|
||||
ttMoves[0].move = ttm;
|
||||
finished = false;
|
||||
lastBadCapture = badCaptures;
|
||||
|
||||
stage = (pos.checkers() ? EVASION_TT : MAIN_TT) +
|
||||
!(ttm && pos.pseudo_legal(ttm));
|
||||
}
|
||||
pinned = p.pinned_pieces(pos.side_to_move());
|
||||
|
||||
/// MovePicker constructor for quiescence search
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
|
||||
const CapturePieceToHistory* cph,
|
||||
const PieceToHistory** ch,
|
||||
Square rs)
|
||||
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d)
|
||||
{
|
||||
assert(d <= 0);
|
||||
|
||||
stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) +
|
||||
!( ttm
|
||||
&& (pos.checkers() || depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare)
|
||||
&& pos.pseudo_legal(ttm));
|
||||
}
|
||||
|
||||
/// MovePicker constructor for ProbCut: we generate captures with SEE greater
|
||||
/// than or equal to the given threshold.
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Value th, Depth d, const CapturePieceToHistory* cph)
|
||||
: pos(p), captureHistory(cph), ttMove(ttm), threshold(th), depth(d)
|
||||
{
|
||||
assert(!pos.checkers());
|
||||
|
||||
stage = PROBCUT_TT + !(ttm && pos.capture(ttm)
|
||||
&& pos.pseudo_legal(ttm)
|
||||
&& pos.see_ge(ttm, threshold));
|
||||
}
|
||||
|
||||
/// MovePicker::score() assigns a numerical value to each move in a list, used
|
||||
/// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring
|
||||
/// captures with a good history. Quiets moves are ordered using the histories.
|
||||
template<GenType Type>
|
||||
void MovePicker::score() {
|
||||
|
||||
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
|
||||
|
||||
Bitboard threatened, threatenedByPawn, threatenedByMinor, threatenedByRook;
|
||||
if constexpr (Type == QUIETS)
|
||||
if (ss && !p.is_check())
|
||||
{
|
||||
Color us = pos.side_to_move();
|
||||
// squares threatened by pawns
|
||||
threatenedByPawn = pos.attacks_by<PAWN>(~us);
|
||||
// squares threatened by minors or pawns
|
||||
threatenedByMinor = pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatenedByPawn;
|
||||
// squares threatened by rooks, minors or pawns
|
||||
threatenedByRook = pos.attacks_by<ROOK>(~us) | threatenedByMinor;
|
||||
ttMoves[1].move = (ss->mateKiller == ttm)? MOVE_NONE : ss->mateKiller;
|
||||
searchTT |= ttMoves[1].move;
|
||||
killers[0].move = ss->killers[0];
|
||||
killers[1].move = ss->killers[1];
|
||||
} else
|
||||
ttMoves[1].move = killers[0].move = killers[1].move = MOVE_NONE;
|
||||
|
||||
// pieces threatened by pieces of lesser material value
|
||||
threatened = (pos.pieces(us, QUEEN) & threatenedByRook)
|
||||
| (pos.pieces(us, ROOK) & threatenedByMinor)
|
||||
| (pos.pieces(us, KNIGHT, BISHOP) & threatenedByPawn);
|
||||
}
|
||||
if (p.is_check())
|
||||
phasePtr = EvasionsPhaseTable;
|
||||
else if (d > Depth(0))
|
||||
phasePtr = MainSearchPhaseTable;
|
||||
else if (d == Depth(0))
|
||||
phasePtr = QsearchWithChecksPhaseTable;
|
||||
else
|
||||
phasePtr = QsearchWithoutChecksPhaseTable;
|
||||
|
||||
phasePtr += !searchTT - 1;
|
||||
go_next_phase();
|
||||
}
|
||||
|
||||
|
||||
/// MovePicker::go_next_phase() generates, scores and sorts the next bunch
|
||||
/// of moves when there are no more moves to try for the current phase.
|
||||
|
||||
void MovePicker::go_next_phase() {
|
||||
|
||||
curMove = moves;
|
||||
phase = *(++phasePtr);
|
||||
switch (phase) {
|
||||
|
||||
case PH_TT_MOVES:
|
||||
curMove = ttMoves;
|
||||
lastMove = curMove + 2;
|
||||
return;
|
||||
|
||||
case PH_GOOD_CAPTURES:
|
||||
lastMove = generate_captures(pos, moves);
|
||||
score_captures();
|
||||
return;
|
||||
|
||||
case PH_KILLERS:
|
||||
curMove = killers;
|
||||
lastMove = curMove + 2;
|
||||
return;
|
||||
|
||||
case PH_NONCAPTURES:
|
||||
lastMove = generate_noncaptures(pos, moves);
|
||||
score_noncaptures();
|
||||
sort_moves(moves, lastMove);
|
||||
return;
|
||||
|
||||
case PH_BAD_CAPTURES:
|
||||
// Bad captures SEE value is already calculated so just sort them
|
||||
// to get SEE move ordering.
|
||||
curMove = badCaptures;
|
||||
lastMove = lastBadCapture;
|
||||
return;
|
||||
|
||||
case PH_EVASIONS:
|
||||
assert(pos.is_check());
|
||||
lastMove = generate_evasions(pos, moves);
|
||||
score_evasions();
|
||||
return;
|
||||
|
||||
case PH_QCAPTURES:
|
||||
lastMove = generate_captures(pos, moves);
|
||||
score_captures();
|
||||
return;
|
||||
|
||||
case PH_QCHECKS:
|
||||
// Perhaps we should order moves move here? FIXME
|
||||
lastMove = generate_non_capture_checks(pos, moves);
|
||||
return;
|
||||
|
||||
case PH_STOP:
|
||||
lastMove = curMove + 1; // hack to be friendly for get_next_move()
|
||||
return;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// MovePicker::score_captures(), MovePicker::score_noncaptures() and
|
||||
/// MovePicker::score_evasions() assign a numerical move ordering score
|
||||
/// to each move in a move list. The moves with highest scores will be
|
||||
/// picked first by get_next_move().
|
||||
|
||||
void MovePicker::score_captures() {
|
||||
// Winning and equal captures in the main search are ordered by MVV/LVA.
|
||||
// Suprisingly, this appears to perform slightly better than SEE based
|
||||
// move ordering. The reason is probably that in a position with a winning
|
||||
// capture, capturing a more valuable (but sufficiently defended) piece
|
||||
// first usually doesn't hurt. The opponent will have to recapture, and
|
||||
// the hanging piece will still be hanging (except in the unusual cases
|
||||
// where it is possible to recapture with the hanging piece). Exchanging
|
||||
// big pieces before capturing a hanging piece probably helps to reduce
|
||||
// the subtree size.
|
||||
// In main search we want to push captures with negative SEE values to
|
||||
// badCaptures[] array, but instead of doing it now we delay till when
|
||||
// the move has been picked up in pick_move_from_list(), this way we save
|
||||
// some SEE calls in case we get a cutoff (idea from Pablo Vazquez).
|
||||
Move m;
|
||||
|
||||
// Use MVV/LVA ordering
|
||||
for (MoveStack* cur = moves; cur != lastMove; cur++)
|
||||
{
|
||||
// Silence unused variable warnings
|
||||
(void) threatened;
|
||||
(void) threatenedByPawn;
|
||||
(void) threatenedByMinor;
|
||||
(void) threatenedByRook;
|
||||
m = cur->move;
|
||||
if (move_is_promotion(m))
|
||||
cur->score = QueenValueMidgame;
|
||||
else
|
||||
cur->score = pos.midgame_value_of_piece_on(move_to(m))
|
||||
- pos.type_of_piece_on(move_from(m));
|
||||
}
|
||||
|
||||
for (auto& m : *this)
|
||||
if constexpr (Type == CAPTURES)
|
||||
m.value = 6 * int(PieceValue[MG][pos.piece_on(to_sq(m))])
|
||||
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))];
|
||||
|
||||
else if constexpr (Type == QUIETS)
|
||||
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
|
||||
+ 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (threatened & from_sq(m) ?
|
||||
(type_of(pos.moved_piece(m)) == QUEEN && !(to_sq(m) & threatenedByRook) ? 50000
|
||||
: type_of(pos.moved_piece(m)) == ROOK && !(to_sq(m) & threatenedByMinor) ? 25000
|
||||
: !(to_sq(m) & threatenedByPawn) ? 15000
|
||||
: 0)
|
||||
: 0);
|
||||
|
||||
else // Type == EVASIONS
|
||||
{
|
||||
if (pos.capture(m))
|
||||
m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
|
||||
- Value(type_of(pos.moved_piece(m)));
|
||||
else
|
||||
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
|
||||
+ 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
|
||||
- (1 << 28);
|
||||
}
|
||||
}
|
||||
|
||||
/// MovePicker::select() returns the next move satisfying a predicate function.
|
||||
/// It never returns the TT move.
|
||||
template<MovePicker::PickType T, typename Pred>
|
||||
Move MovePicker::select(Pred filter) {
|
||||
void MovePicker::score_noncaptures() {
|
||||
// First score by history, when no history is available then use
|
||||
// piece/square tables values. This seems to be better then a
|
||||
// random choice when we don't have an history for any move.
|
||||
Move m;
|
||||
Piece piece;
|
||||
Square from, to;
|
||||
int hs;
|
||||
|
||||
while (cur < endMoves)
|
||||
for (MoveStack* cur = moves; cur != lastMove; cur++)
|
||||
{
|
||||
if (T == Best)
|
||||
std::swap(*cur, *std::max_element(cur, endMoves));
|
||||
m = cur->move;
|
||||
from = move_from(m);
|
||||
to = move_to(m);
|
||||
piece = pos.piece_on(from);
|
||||
hs = H.move_ordering_score(piece, to);
|
||||
|
||||
if (*cur != ttMove && filter())
|
||||
return *cur++;
|
||||
// Ensure history is always preferred to pst
|
||||
if (hs > 0)
|
||||
hs += 1000;
|
||||
|
||||
cur++;
|
||||
// pst based scoring
|
||||
cur->score = hs + mg_value(pos.pst_delta(piece, from, to));
|
||||
}
|
||||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
/// MovePicker::next_move() is the most important method of the MovePicker class. It
|
||||
/// returns a new pseudo-legal move every time it is called until there are no more
|
||||
/// moves left, picking the move with the highest score from a list of generated moves.
|
||||
Move MovePicker::next_move(bool skipQuiets) {
|
||||
void MovePicker::score_evasions() {
|
||||
// Try good captures ordered by MVV/LVA, then non-captures if
|
||||
// destination square is not under attack, ordered by history
|
||||
// value, and at the end bad-captures and non-captures with a
|
||||
// negative SEE. This last group is ordered by the SEE score.
|
||||
Move m;
|
||||
int seeScore;
|
||||
|
||||
top:
|
||||
switch (stage) {
|
||||
for (MoveStack* cur = moves; cur != lastMove; cur++)
|
||||
{
|
||||
m = cur->move;
|
||||
if ((seeScore = pos.see_sign(m)) < 0)
|
||||
cur->score = seeScore;
|
||||
else if (pos.move_is_capture(m))
|
||||
cur->score = pos.midgame_value_of_piece_on(move_to(m))
|
||||
- pos.type_of_piece_on(move_from(m)) + HistoryMax;
|
||||
else
|
||||
cur->score = H.move_ordering_score(pos.piece_on(move_from(m)), move_to(m));
|
||||
}
|
||||
}
|
||||
|
||||
case MAIN_TT:
|
||||
case EVASION_TT:
|
||||
case QSEARCH_TT:
|
||||
case PROBCUT_TT:
|
||||
++stage;
|
||||
return ttMove;
|
||||
/// MovePicker::get_next_move() is the most important method of the MovePicker
|
||||
/// class. It returns a new legal move every time it is called, until there
|
||||
/// are no more moves left.
|
||||
/// It picks the move with the biggest score from a list of generated moves taking
|
||||
/// care not to return the tt move if has already been searched previously.
|
||||
|
||||
case CAPTURE_INIT:
|
||||
case PROBCUT_INIT:
|
||||
case QCAPTURE_INIT:
|
||||
cur = endBadCaptures = moves;
|
||||
endMoves = generate<CAPTURES>(pos, cur);
|
||||
Move MovePicker::get_next_move() {
|
||||
|
||||
score<CAPTURES>();
|
||||
partial_insertion_sort(cur, endMoves, -3000 * depth);
|
||||
++stage;
|
||||
goto top;
|
||||
Move move;
|
||||
|
||||
case GOOD_CAPTURE:
|
||||
if (select<Next>([&](){
|
||||
return pos.see_ge(*cur, Value(-69 * cur->value / 1024)) ?
|
||||
// Move losing capture to endBadCaptures to be tried later
|
||||
true : (*endBadCaptures++ = *cur, false); }))
|
||||
return *(cur - 1);
|
||||
|
||||
// Prepare the pointers to loop over the refutations array
|
||||
cur = std::begin(refutations);
|
||||
endMoves = std::end(refutations);
|
||||
|
||||
// If the countermove is the same as a killer, skip it
|
||||
if ( refutations[0].move == refutations[2].move
|
||||
|| refutations[1].move == refutations[2].move)
|
||||
--endMoves;
|
||||
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
|
||||
case REFUTATION:
|
||||
if (select<Next>([&](){ return *cur != MOVE_NONE
|
||||
&& !pos.capture(*cur)
|
||||
&& pos.pseudo_legal(*cur); }))
|
||||
return *(cur - 1);
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
|
||||
case QUIET_INIT:
|
||||
if (!skipQuiets)
|
||||
while (true)
|
||||
{
|
||||
while (curMove != lastMove)
|
||||
{
|
||||
cur = endBadCaptures;
|
||||
endMoves = generate<QUIETS>(pos, cur);
|
||||
switch (phase) {
|
||||
|
||||
score<QUIETS>();
|
||||
partial_insertion_sort(cur, endMoves, -3000 * depth);
|
||||
case PH_TT_MOVES:
|
||||
move = (curMove++)->move;
|
||||
if ( move != MOVE_NONE
|
||||
&& move_is_legal(pos, move, pinned))
|
||||
return move;
|
||||
break;
|
||||
|
||||
case PH_GOOD_CAPTURES:
|
||||
move = pick_best(curMove++, lastMove).move;
|
||||
if ( move != ttMoves[0].move
|
||||
&& move != ttMoves[1].move
|
||||
&& pos.pl_move_is_legal(move, pinned))
|
||||
{
|
||||
// Check for a non negative SEE now
|
||||
int seeValue = pos.see_sign(move);
|
||||
if (seeValue >= 0)
|
||||
return move;
|
||||
|
||||
// Losing capture, move it to the badCaptures[] array, note
|
||||
// that move has now been already checked for legality.
|
||||
assert(int(lastBadCapture - badCaptures) < 63);
|
||||
lastBadCapture->move = move;
|
||||
lastBadCapture->score = seeValue;
|
||||
lastBadCapture++;
|
||||
}
|
||||
break;
|
||||
|
||||
case PH_KILLERS:
|
||||
move = (curMove++)->move;
|
||||
if ( move != MOVE_NONE
|
||||
&& move != ttMoves[0].move
|
||||
&& move != ttMoves[1].move
|
||||
&& move_is_legal(pos, move, pinned)
|
||||
&& !pos.move_is_capture(move))
|
||||
return move;
|
||||
break;
|
||||
|
||||
case PH_NONCAPTURES:
|
||||
move = (curMove++)->move;
|
||||
if ( move != ttMoves[0].move
|
||||
&& move != ttMoves[1].move
|
||||
&& move != killers[0].move
|
||||
&& move != killers[1].move
|
||||
&& pos.pl_move_is_legal(move, pinned))
|
||||
return move;
|
||||
break;
|
||||
|
||||
case PH_BAD_CAPTURES:
|
||||
move = pick_best(curMove++, lastMove).move;
|
||||
return move;
|
||||
|
||||
case PH_EVASIONS:
|
||||
case PH_QCAPTURES:
|
||||
move = pick_best(curMove++, lastMove).move;
|
||||
if ( move != ttMoves[0].move
|
||||
&& pos.pl_move_is_legal(move, pinned))
|
||||
return move;
|
||||
break;
|
||||
|
||||
case PH_QCHECKS:
|
||||
move = (curMove++)->move;
|
||||
if ( move != ttMoves[0].move
|
||||
&& pos.pl_move_is_legal(move, pinned))
|
||||
return move;
|
||||
break;
|
||||
|
||||
case PH_STOP:
|
||||
return MOVE_NONE;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
|
||||
case QUIET:
|
||||
if ( !skipQuiets
|
||||
&& select<Next>([&](){return *cur != refutations[0].move
|
||||
&& *cur != refutations[1].move
|
||||
&& *cur != refutations[2].move;}))
|
||||
return *(cur - 1);
|
||||
|
||||
// Prepare the pointers to loop over the bad captures
|
||||
cur = moves;
|
||||
endMoves = endBadCaptures;
|
||||
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
|
||||
case BAD_CAPTURE:
|
||||
return select<Next>([](){ return true; });
|
||||
|
||||
case EVASION_INIT:
|
||||
cur = moves;
|
||||
endMoves = generate<EVASIONS>(pos, cur);
|
||||
|
||||
score<EVASIONS>();
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
|
||||
case EVASION:
|
||||
return select<Best>([](){ return true; });
|
||||
|
||||
case PROBCUT:
|
||||
return select<Next>([&](){ return pos.see_ge(*cur, threshold); });
|
||||
|
||||
case QCAPTURE:
|
||||
if (select<Next>([&](){ return depth > DEPTH_QS_RECAPTURES
|
||||
|| to_sq(*cur) == recaptureSquare; }))
|
||||
return *(cur - 1);
|
||||
|
||||
// If we did not find any move and we do not try checks, we have finished
|
||||
if (depth != DEPTH_QS_CHECKS)
|
||||
return MOVE_NONE;
|
||||
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
|
||||
case QCHECK_INIT:
|
||||
cur = moves;
|
||||
endMoves = generate<QUIET_CHECKS>(pos, cur);
|
||||
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
|
||||
case QCHECK:
|
||||
return select<Next>([](){ return true; });
|
||||
go_next_phase();
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return MOVE_NONE; // Silence warning
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
/// A variant of get_next_move() which takes a lock as a parameter, used to
|
||||
/// prevent multiple threads from picking the same move at a split point.
|
||||
|
||||
Move MovePicker::get_next_move(Lock &lock) {
|
||||
|
||||
lock_grab(&lock);
|
||||
if (finished)
|
||||
{
|
||||
lock_release(&lock);
|
||||
return MOVE_NONE;
|
||||
}
|
||||
Move m = get_next_move();
|
||||
if (m == MOVE_NONE)
|
||||
finished = true;
|
||||
|
||||
lock_release(&lock);
|
||||
return m;
|
||||
}
|
||||
|
||||
+56
-119
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,138 +17,74 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MOVEPICK_H_INCLUDED
|
||||
|
||||
#if !defined MOVEPICK_H_INCLUDED
|
||||
#define MOVEPICK_H_INCLUDED
|
||||
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "movegen.h"
|
||||
#include "depth.h"
|
||||
#include "history.h"
|
||||
#include "lock.h"
|
||||
#include "position.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
/// StatsEntry stores the stat table value. It is usually a number but could
|
||||
/// be a move or even a nested history. We use a class instead of naked value
|
||||
/// to directly call history update operator<<() on the entry so to use stats
|
||||
/// tables at caller sites as simple multi-dim arrays.
|
||||
template<typename T, int D>
|
||||
class StatsEntry {
|
||||
|
||||
T entry;
|
||||
|
||||
public:
|
||||
void operator=(const T& v) { entry = v; }
|
||||
T* operator&() { return &entry; }
|
||||
T* operator->() { return &entry; }
|
||||
operator const T&() const { return entry; }
|
||||
|
||||
void operator<<(int bonus) {
|
||||
assert(abs(bonus) <= D); // Ensure range is [-D, D]
|
||||
static_assert(D <= std::numeric_limits<T>::max(), "D overflows T");
|
||||
|
||||
entry += bonus - entry * abs(bonus) / D;
|
||||
|
||||
assert(abs(entry) <= D);
|
||||
}
|
||||
};
|
||||
|
||||
/// Stats is a generic N-dimensional array used to store various statistics.
|
||||
/// The first template parameter T is the base type of the array, the second
|
||||
/// template parameter D limits the range of updates in [-D, D] when we update
|
||||
/// values with the << operator, while the last parameters (Size and Sizes)
|
||||
/// encode the dimensions of the array.
|
||||
template <typename T, int D, int Size, int... Sizes>
|
||||
struct Stats : public std::array<Stats<T, D, Sizes...>, Size>
|
||||
{
|
||||
typedef Stats<T, D, Size, Sizes...> stats;
|
||||
|
||||
void fill(const T& v) {
|
||||
|
||||
// For standard-layout 'this' points to first struct member
|
||||
assert(std::is_standard_layout<stats>::value);
|
||||
|
||||
typedef StatsEntry<T, D> entry;
|
||||
entry* p = reinterpret_cast<entry*>(this);
|
||||
std::fill(p, p + sizeof(*this) / sizeof(entry), v);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, int D, int Size>
|
||||
struct Stats<T, D, Size> : public std::array<StatsEntry<T, D>, Size> {};
|
||||
|
||||
/// In stats table, D=0 means that the template parameter is not used
|
||||
enum StatsParams { NOT_USED = 0 };
|
||||
enum StatsType { NoCaptures, Captures };
|
||||
|
||||
/// ButterflyHistory records how often quiet moves have been successful or
|
||||
/// unsuccessful during the current search, and is used for reduction and move
|
||||
/// ordering decisions. It uses 2 tables (one for each color) indexed by
|
||||
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
|
||||
typedef Stats<int16_t, 14365, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
|
||||
|
||||
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
|
||||
/// move, see www.chessprogramming.org/Countermove_Heuristic
|
||||
typedef Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB> CounterMoveHistory;
|
||||
|
||||
/// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
|
||||
typedef Stats<int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB> CapturePieceToHistory;
|
||||
|
||||
/// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to]
|
||||
typedef Stats<int16_t, 29952, PIECE_NB, SQUARE_NB> PieceToHistory;
|
||||
|
||||
/// ContinuationHistory is the combined history of a given pair of moves, usually
|
||||
/// the current one given a previous one. The nested history table is based on
|
||||
/// PieceToHistory instead of ButterflyBoards.
|
||||
typedef Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB> ContinuationHistory;
|
||||
|
||||
|
||||
/// MovePicker class is used to pick one pseudo-legal move at a time from the
|
||||
/// current position. The most important method is next_move(), which returns a
|
||||
/// new pseudo-legal move each time it is called, until there are no moves left,
|
||||
/// when MOVE_NONE is returned. In order to improve the efficiency of the
|
||||
/// alpha-beta algorithm, MovePicker attempts to return the moves which are most
|
||||
/// likely to get a cut-off first.
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
struct SearchStack;
|
||||
|
||||
/// MovePicker is a class which is used to pick one legal move at a time from
|
||||
/// the current position. It is initialized with a Position object and a few
|
||||
/// moves we have reason to believe are good. The most important method is
|
||||
/// MovePicker::pick_next_move(), which returns a new legal move each time it
|
||||
/// is called, until there are no legal moves left, when MOVE_NONE is returned.
|
||||
/// In order to improve the efficiency of the alpha beta algorithm, MovePicker
|
||||
/// attempts to return the moves which are most likely to be strongest first.
|
||||
|
||||
class MovePicker {
|
||||
|
||||
enum PickType { Next, Best };
|
||||
MovePicker& operator=(const MovePicker&); // silence a warning under MSVC
|
||||
|
||||
public:
|
||||
MovePicker(const MovePicker&) = delete;
|
||||
MovePicker& operator=(const MovePicker&) = delete;
|
||||
MovePicker(const Position&, Move, Depth, const ButterflyHistory*,
|
||||
const CapturePieceToHistory*,
|
||||
const PieceToHistory**,
|
||||
Move,
|
||||
const Move*);
|
||||
MovePicker(const Position&, Move, Depth, const ButterflyHistory*,
|
||||
const CapturePieceToHistory*,
|
||||
const PieceToHistory**,
|
||||
Square);
|
||||
MovePicker(const Position&, Move, Value, Depth, const CapturePieceToHistory*);
|
||||
Move next_move(bool skipQuiets = false);
|
||||
MovePicker(const Position& p, Move ttm, Depth d, const History& h, SearchStack* ss = NULL);
|
||||
Move get_next_move();
|
||||
Move get_next_move(Lock& lock);
|
||||
int number_of_evasions() const;
|
||||
|
||||
private:
|
||||
template<PickType T, typename Pred> Move select(Pred);
|
||||
template<GenType> void score();
|
||||
ExtMove* begin() { return cur; }
|
||||
ExtMove* end() { return endMoves; }
|
||||
void score_captures();
|
||||
void score_noncaptures();
|
||||
void score_evasions();
|
||||
void go_next_phase();
|
||||
|
||||
const Position& pos;
|
||||
const ButterflyHistory* mainHistory;
|
||||
const CapturePieceToHistory* captureHistory;
|
||||
const PieceToHistory** continuationHistory;
|
||||
Move ttMove;
|
||||
ExtMove refutations[3], *cur, *endMoves, *endBadCaptures;
|
||||
int stage;
|
||||
Square recaptureSquare;
|
||||
Value threshold;
|
||||
Depth depth;
|
||||
ExtMove moves[MAX_MOVES];
|
||||
const History& H;
|
||||
MoveStack ttMoves[2], killers[2];
|
||||
bool finished;
|
||||
int phase;
|
||||
const uint8_t* phasePtr;
|
||||
MoveStack *curMove, *lastMove, *lastBadCapture;
|
||||
Bitboard pinned;
|
||||
MoveStack moves[256], badCaptures[64];
|
||||
};
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef MOVEPICK_H_INCLUDED
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
/// MovePicker::number_of_evasions() simply returns the number of moves in
|
||||
/// evasions phase. It is intended to be used in positions where the side to
|
||||
/// move is in check, for detecting checkmates or situations where there is
|
||||
/// only a single reply to check.
|
||||
/// WARNING: It works as long as PH_EVASIONS is the _only_ phase for evasions.
|
||||
|
||||
inline int MovePicker::number_of_evasions() const {
|
||||
return int(lastMove - moves);
|
||||
}
|
||||
|
||||
#endif // !defined(MOVEPICK_H_INCLUDED)
|
||||
|
||||
@@ -1,403 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Code for calculating NNUE evaluation function
|
||||
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
|
||||
#include "../evaluate.h"
|
||||
#include "../position.h"
|
||||
#include "../misc.h"
|
||||
#include "../uci.h"
|
||||
#include "../types.h"
|
||||
|
||||
#include "evaluate_nnue.h"
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
|
||||
// Input feature converter
|
||||
LargePagePtr<FeatureTransformer> featureTransformer;
|
||||
|
||||
// Evaluation function
|
||||
AlignedPtr<Network> network[LayerStacks];
|
||||
|
||||
// Evaluation function file name
|
||||
std::string fileName;
|
||||
std::string netDescription;
|
||||
|
||||
namespace Detail {
|
||||
|
||||
// Initialize the evaluation function parameters
|
||||
template <typename T>
|
||||
void initialize(AlignedPtr<T>& pointer) {
|
||||
|
||||
pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
|
||||
std::memset(pointer.get(), 0, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void initialize(LargePagePtr<T>& pointer) {
|
||||
|
||||
static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
|
||||
pointer.reset(reinterpret_cast<T*>(aligned_large_pages_alloc(sizeof(T))));
|
||||
std::memset(pointer.get(), 0, sizeof(T));
|
||||
}
|
||||
|
||||
// Read evaluation function parameters
|
||||
template <typename T>
|
||||
bool read_parameters(std::istream& stream, T& reference) {
|
||||
|
||||
std::uint32_t header;
|
||||
header = read_little_endian<std::uint32_t>(stream);
|
||||
if (!stream || header != T::get_hash_value()) return false;
|
||||
return reference.read_parameters(stream);
|
||||
}
|
||||
|
||||
// Write evaluation function parameters
|
||||
template <typename T>
|
||||
bool write_parameters(std::ostream& stream, const T& reference) {
|
||||
|
||||
write_little_endian<std::uint32_t>(stream, T::get_hash_value());
|
||||
return reference.write_parameters(stream);
|
||||
}
|
||||
|
||||
} // namespace Detail
|
||||
|
||||
// Initialize the evaluation function parameters
|
||||
void initialize() {
|
||||
|
||||
Detail::initialize(featureTransformer);
|
||||
for (std::size_t i = 0; i < LayerStacks; ++i)
|
||||
Detail::initialize(network[i]);
|
||||
}
|
||||
|
||||
// Read network header
|
||||
bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc)
|
||||
{
|
||||
std::uint32_t version, size;
|
||||
|
||||
version = read_little_endian<std::uint32_t>(stream);
|
||||
*hashValue = read_little_endian<std::uint32_t>(stream);
|
||||
size = read_little_endian<std::uint32_t>(stream);
|
||||
if (!stream || version != Version) return false;
|
||||
desc->resize(size);
|
||||
stream.read(&(*desc)[0], size);
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Write network header
|
||||
bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc)
|
||||
{
|
||||
write_little_endian<std::uint32_t>(stream, Version);
|
||||
write_little_endian<std::uint32_t>(stream, hashValue);
|
||||
write_little_endian<std::uint32_t>(stream, (std::uint32_t)desc.size());
|
||||
stream.write(&desc[0], desc.size());
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool read_parameters(std::istream& stream) {
|
||||
|
||||
std::uint32_t hashValue;
|
||||
if (!read_header(stream, &hashValue, &netDescription)) return false;
|
||||
if (hashValue != HashValue) return false;
|
||||
if (!Detail::read_parameters(stream, *featureTransformer)) return false;
|
||||
for (std::size_t i = 0; i < LayerStacks; ++i)
|
||||
if (!Detail::read_parameters(stream, *(network[i]))) return false;
|
||||
return stream && stream.peek() == std::ios::traits_type::eof();
|
||||
}
|
||||
|
||||
// Write network parameters
|
||||
bool write_parameters(std::ostream& stream) {
|
||||
|
||||
if (!write_header(stream, HashValue, netDescription)) return false;
|
||||
if (!Detail::write_parameters(stream, *featureTransformer)) return false;
|
||||
for (std::size_t i = 0; i < LayerStacks; ++i)
|
||||
if (!Detail::write_parameters(stream, *(network[i]))) return false;
|
||||
return (bool)stream;
|
||||
}
|
||||
|
||||
// Evaluation function. Perform differential calculation.
|
||||
Value evaluate(const Position& pos, bool adjusted) {
|
||||
|
||||
// We manually align the arrays on the stack because with gcc < 9.3
|
||||
// overaligning stack variables with alignas() doesn't work correctly.
|
||||
|
||||
constexpr uint64_t alignment = CacheLineSize;
|
||||
int delta = 10 - pos.non_pawn_material() / 1515;
|
||||
|
||||
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
|
||||
TransformedFeatureType transformedFeaturesUnaligned[
|
||||
FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)];
|
||||
|
||||
auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
|
||||
#else
|
||||
alignas(alignment)
|
||||
TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
|
||||
#endif
|
||||
|
||||
ASSERT_ALIGNED(transformedFeatures, alignment);
|
||||
|
||||
const int bucket = (pos.count<ALL_PIECES>() - 1) / 4;
|
||||
const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket);
|
||||
const auto positional = network[bucket]->propagate(transformedFeatures);
|
||||
|
||||
// Give more value to positional evaluation when adjusted flag is set
|
||||
if (adjusted)
|
||||
return static_cast<Value>(((128 - delta) * psqt + (128 + delta) * positional) / 128 / OutputScale);
|
||||
else
|
||||
return static_cast<Value>((psqt + positional) / OutputScale);
|
||||
}
|
||||
|
||||
struct NnueEvalTrace {
|
||||
static_assert(LayerStacks == PSQTBuckets);
|
||||
|
||||
Value psqt[LayerStacks];
|
||||
Value positional[LayerStacks];
|
||||
std::size_t correctBucket;
|
||||
};
|
||||
|
||||
static NnueEvalTrace trace_evaluate(const Position& pos) {
|
||||
|
||||
// We manually align the arrays on the stack because with gcc < 9.3
|
||||
// overaligning stack variables with alignas() doesn't work correctly.
|
||||
|
||||
constexpr uint64_t alignment = CacheLineSize;
|
||||
|
||||
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
|
||||
TransformedFeatureType transformedFeaturesUnaligned[
|
||||
FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)];
|
||||
|
||||
auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
|
||||
#else
|
||||
alignas(alignment)
|
||||
TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
|
||||
#endif
|
||||
|
||||
ASSERT_ALIGNED(transformedFeatures, alignment);
|
||||
|
||||
NnueEvalTrace t{};
|
||||
t.correctBucket = (pos.count<ALL_PIECES>() - 1) / 4;
|
||||
for (IndexType bucket = 0; bucket < LayerStacks; ++bucket) {
|
||||
const auto materialist = featureTransformer->transform(pos, transformedFeatures, bucket);
|
||||
const auto positional = network[bucket]->propagate(transformedFeatures);
|
||||
|
||||
t.psqt[bucket] = static_cast<Value>( materialist / OutputScale );
|
||||
t.positional[bucket] = static_cast<Value>( positional / OutputScale );
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static const std::string PieceToChar(" PNBRQK pnbrqk");
|
||||
|
||||
|
||||
// format_cp_compact() converts a Value into (centi)pawns and writes it in a buffer.
|
||||
// The buffer must have capacity for at least 5 chars.
|
||||
static void format_cp_compact(Value v, char* buffer) {
|
||||
|
||||
buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' ');
|
||||
|
||||
int cp = std::abs(100 * v / PawnValueEg);
|
||||
if (cp >= 10000)
|
||||
{
|
||||
buffer[1] = '0' + cp / 10000; cp %= 10000;
|
||||
buffer[2] = '0' + cp / 1000; cp %= 1000;
|
||||
buffer[3] = '0' + cp / 100;
|
||||
buffer[4] = ' ';
|
||||
}
|
||||
else if (cp >= 1000)
|
||||
{
|
||||
buffer[1] = '0' + cp / 1000; cp %= 1000;
|
||||
buffer[2] = '0' + cp / 100; cp %= 100;
|
||||
buffer[3] = '.';
|
||||
buffer[4] = '0' + cp / 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[1] = '0' + cp / 100; cp %= 100;
|
||||
buffer[2] = '.';
|
||||
buffer[3] = '0' + cp / 10; cp %= 10;
|
||||
buffer[4] = '0' + cp / 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// format_cp_aligned_dot() converts a Value into (centi)pawns and writes it in a buffer,
|
||||
// always keeping two decimals. The buffer must have capacity for at least 7 chars.
|
||||
static void format_cp_aligned_dot(Value v, char* buffer) {
|
||||
|
||||
buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' ');
|
||||
|
||||
double cp = 1.0 * std::abs(int(v)) / PawnValueEg;
|
||||
sprintf(&buffer[1], "%6.2f", cp);
|
||||
}
|
||||
|
||||
|
||||
// trace() returns a string with the value of each piece on a board,
|
||||
// and a table for (PSQT, Layers) values bucket by bucket.
|
||||
|
||||
std::string trace(Position& pos) {
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
char board[3*8+1][8*8+2];
|
||||
std::memset(board, ' ', sizeof(board));
|
||||
for (int row = 0; row < 3*8+1; ++row)
|
||||
board[row][8*8+1] = '\0';
|
||||
|
||||
// A lambda to output one box of the board
|
||||
auto writeSquare = [&board](File file, Rank rank, Piece pc, Value value) {
|
||||
|
||||
const int x = ((int)file) * 8;
|
||||
const int y = (7 - (int)rank) * 3;
|
||||
for (int i = 1; i < 8; ++i)
|
||||
board[y][x+i] = board[y+3][x+i] = '-';
|
||||
for (int i = 1; i < 3; ++i)
|
||||
board[y+i][x] = board[y+i][x+8] = '|';
|
||||
board[y][x] = board[y][x+8] = board[y+3][x+8] = board[y+3][x] = '+';
|
||||
if (pc != NO_PIECE)
|
||||
board[y+1][x+4] = PieceToChar[pc];
|
||||
if (value != VALUE_NONE)
|
||||
format_cp_compact(value, &board[y+2][x+2]);
|
||||
};
|
||||
|
||||
// We estimate the value of each piece by doing a differential evaluation from
|
||||
// the current base eval, simulating the removal of the piece from its square.
|
||||
Value base = evaluate(pos);
|
||||
base = pos.side_to_move() == WHITE ? base : -base;
|
||||
|
||||
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||
for (Rank r = RANK_1; r <= RANK_8; ++r)
|
||||
{
|
||||
Square sq = make_square(f, r);
|
||||
Piece pc = pos.piece_on(sq);
|
||||
Value v = VALUE_NONE;
|
||||
|
||||
if (pc != NO_PIECE && type_of(pc) != KING)
|
||||
{
|
||||
auto st = pos.state();
|
||||
|
||||
pos.remove_piece(sq);
|
||||
st->accumulator.computed[WHITE] = false;
|
||||
st->accumulator.computed[BLACK] = false;
|
||||
|
||||
Value eval = evaluate(pos);
|
||||
eval = pos.side_to_move() == WHITE ? eval : -eval;
|
||||
v = base - eval;
|
||||
|
||||
pos.put_piece(pc, sq);
|
||||
st->accumulator.computed[WHITE] = false;
|
||||
st->accumulator.computed[BLACK] = false;
|
||||
}
|
||||
|
||||
writeSquare(f, r, pc, v);
|
||||
}
|
||||
|
||||
ss << " NNUE derived piece values:\n";
|
||||
for (int row = 0; row < 3*8+1; ++row)
|
||||
ss << board[row] << '\n';
|
||||
ss << '\n';
|
||||
|
||||
auto t = trace_evaluate(pos);
|
||||
|
||||
ss << " NNUE network contributions "
|
||||
<< (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl
|
||||
<< "+------------+------------+------------+------------+\n"
|
||||
<< "| Bucket | Material | Positional | Total |\n"
|
||||
<< "| | (PSQT) | (Layers) | |\n"
|
||||
<< "+------------+------------+------------+------------+\n";
|
||||
|
||||
for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket)
|
||||
{
|
||||
char buffer[3][8];
|
||||
std::memset(buffer, '\0', sizeof(buffer));
|
||||
|
||||
format_cp_aligned_dot(t.psqt[bucket], buffer[0]);
|
||||
format_cp_aligned_dot(t.positional[bucket], buffer[1]);
|
||||
format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], buffer[2]);
|
||||
|
||||
ss << "| " << bucket << " "
|
||||
<< " | " << buffer[0] << " "
|
||||
<< " | " << buffer[1] << " "
|
||||
<< " | " << buffer[2] << " "
|
||||
<< " |";
|
||||
if (bucket == t.correctBucket)
|
||||
ss << " <-- this bucket is used";
|
||||
ss << '\n';
|
||||
}
|
||||
|
||||
ss << "+------------+------------+------------+------------+\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
// Load eval, from a file stream or a memory stream
|
||||
bool load_eval(std::string name, std::istream& stream) {
|
||||
|
||||
initialize();
|
||||
fileName = name;
|
||||
return read_parameters(stream);
|
||||
}
|
||||
|
||||
// Save eval, to a file stream or a memory stream
|
||||
bool save_eval(std::ostream& stream) {
|
||||
|
||||
if (fileName.empty())
|
||||
return false;
|
||||
|
||||
return write_parameters(stream);
|
||||
}
|
||||
|
||||
/// Save eval, to a file given by its name
|
||||
bool save_eval(const std::optional<std::string>& filename) {
|
||||
|
||||
std::string actualFilename;
|
||||
std::string msg;
|
||||
|
||||
if (filename.has_value())
|
||||
actualFilename = filename.value();
|
||||
else
|
||||
{
|
||||
if (currentEvalFileName != EvalFileDefaultName)
|
||||
{
|
||||
msg = "Failed to export a net. A non-embedded net can only be saved if the filename is specified";
|
||||
|
||||
sync_cout << msg << sync_endl;
|
||||
return false;
|
||||
}
|
||||
actualFilename = EvalFileDefaultName;
|
||||
}
|
||||
|
||||
std::ofstream stream(actualFilename, std::ios_base::binary);
|
||||
bool saved = save_eval(stream);
|
||||
|
||||
msg = saved ? "Network saved successfully to " + actualFilename
|
||||
: "Failed to export a net";
|
||||
|
||||
sync_cout << msg << sync_endl;
|
||||
return saved;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// header used in NNUE evaluation function
|
||||
|
||||
#ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
|
||||
#define NNUE_EVALUATE_NNUE_H_INCLUDED
|
||||
|
||||
#include "nnue_feature_transformer.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
|
||||
// Hash value of evaluation function structure
|
||||
constexpr std::uint32_t HashValue =
|
||||
FeatureTransformer::get_hash_value() ^ Network::get_hash_value();
|
||||
|
||||
// Deleter for automating release of memory area
|
||||
template <typename T>
|
||||
struct AlignedDeleter {
|
||||
void operator()(T* ptr) const {
|
||||
ptr->~T();
|
||||
std_aligned_free(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct LargePageDeleter {
|
||||
void operator()(T* ptr) const {
|
||||
ptr->~T();
|
||||
aligned_large_pages_free(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using AlignedPtr = std::unique_ptr<T, AlignedDeleter<T>>;
|
||||
|
||||
template <typename T>
|
||||
using LargePagePtr = std::unique_ptr<T, LargePageDeleter<T>>;
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
|
||||
#endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//Definition of input features HalfKAv2_hm of NNUE evaluation function
|
||||
|
||||
#include "half_ka_v2_hm.h"
|
||||
|
||||
#include "../../position.h"
|
||||
|
||||
namespace Stockfish::Eval::NNUE::Features {
|
||||
|
||||
// Orient a square according to perspective (rotates by 180 for black)
|
||||
inline Square HalfKAv2_hm::orient(Color perspective, Square s, Square ksq) {
|
||||
return Square(int(s) ^ (bool(perspective) * SQ_A8) ^ ((file_of(ksq) < FILE_E) * SQ_H1));
|
||||
}
|
||||
|
||||
// Index of a feature for a given king position and another piece on some square
|
||||
inline IndexType HalfKAv2_hm::make_index(Color perspective, Square s, Piece pc, Square ksq) {
|
||||
Square o_ksq = orient(perspective, ksq, ksq);
|
||||
return IndexType(orient(perspective, s, ksq) + PieceSquareIndex[perspective][pc] + PS_NB * KingBuckets[o_ksq]);
|
||||
}
|
||||
|
||||
// Get a list of indices for active features
|
||||
void HalfKAv2_hm::append_active_indices(
|
||||
const Position& pos,
|
||||
Color perspective,
|
||||
IndexList& active
|
||||
) {
|
||||
Square ksq = pos.square<KING>(perspective);
|
||||
Bitboard bb = pos.pieces();
|
||||
while (bb)
|
||||
{
|
||||
Square s = pop_lsb(bb);
|
||||
active.push_back(make_index(perspective, s, pos.piece_on(s), ksq));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// append_changed_indices() : get a list of indices for recently changed features
|
||||
|
||||
void HalfKAv2_hm::append_changed_indices(
|
||||
Square ksq,
|
||||
const DirtyPiece& dp,
|
||||
Color perspective,
|
||||
IndexList& removed,
|
||||
IndexList& added
|
||||
) {
|
||||
for (int i = 0; i < dp.dirty_num; ++i) {
|
||||
if (dp.from[i] != SQ_NONE)
|
||||
removed.push_back(make_index(perspective, dp.from[i], dp.piece[i], ksq));
|
||||
if (dp.to[i] != SQ_NONE)
|
||||
added.push_back(make_index(perspective, dp.to[i], dp.piece[i], ksq));
|
||||
}
|
||||
}
|
||||
|
||||
int HalfKAv2_hm::update_cost(const StateInfo* st) {
|
||||
return st->dirtyPiece.dirty_num;
|
||||
}
|
||||
|
||||
int HalfKAv2_hm::refresh_cost(const Position& pos) {
|
||||
return pos.count<ALL_PIECES>();
|
||||
}
|
||||
|
||||
bool HalfKAv2_hm::requires_refresh(const StateInfo* st, Color perspective) {
|
||||
return st->dirtyPiece.piece[0] == make_piece(perspective, KING);
|
||||
}
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE::Features
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//Definition of input features HalfKP of NNUE evaluation function
|
||||
|
||||
#ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED
|
||||
#define NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED
|
||||
|
||||
#include "../nnue_common.h"
|
||||
|
||||
#include "../../evaluate.h"
|
||||
#include "../../misc.h"
|
||||
|
||||
namespace Stockfish {
|
||||
struct StateInfo;
|
||||
}
|
||||
|
||||
namespace Stockfish::Eval::NNUE::Features {
|
||||
|
||||
// Feature HalfKAv2_hm: Combination of the position of own king
|
||||
// and the position of pieces. Position mirrored such that king always on e..h files.
|
||||
class HalfKAv2_hm {
|
||||
|
||||
// unique number for each piece type on each square
|
||||
enum {
|
||||
PS_NONE = 0,
|
||||
PS_W_PAWN = 0,
|
||||
PS_B_PAWN = 1 * SQUARE_NB,
|
||||
PS_W_KNIGHT = 2 * SQUARE_NB,
|
||||
PS_B_KNIGHT = 3 * SQUARE_NB,
|
||||
PS_W_BISHOP = 4 * SQUARE_NB,
|
||||
PS_B_BISHOP = 5 * SQUARE_NB,
|
||||
PS_W_ROOK = 6 * SQUARE_NB,
|
||||
PS_B_ROOK = 7 * SQUARE_NB,
|
||||
PS_W_QUEEN = 8 * SQUARE_NB,
|
||||
PS_B_QUEEN = 9 * SQUARE_NB,
|
||||
PS_KING = 10 * SQUARE_NB,
|
||||
PS_NB = 11 * SQUARE_NB
|
||||
};
|
||||
|
||||
static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = {
|
||||
// convention: W - us, B - them
|
||||
// viewed from other side, W and B are reversed
|
||||
{ PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE,
|
||||
PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE },
|
||||
{ PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE,
|
||||
PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE }
|
||||
};
|
||||
|
||||
// Orient a square according to perspective (rotates by 180 for black)
|
||||
static Square orient(Color perspective, Square s, Square ksq);
|
||||
|
||||
// Index of a feature for a given king position and another piece on some square
|
||||
static IndexType make_index(Color perspective, Square s, Piece pc, Square ksq);
|
||||
|
||||
public:
|
||||
// Feature name
|
||||
static constexpr const char* Name = "HalfKAv2_hm(Friend)";
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t HashValue = 0x7f234cb8u;
|
||||
|
||||
// Number of feature dimensions
|
||||
static constexpr IndexType Dimensions =
|
||||
static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_NB) / 2;
|
||||
|
||||
static constexpr int KingBuckets[64] = {
|
||||
-1, -1, -1, -1, 31, 30, 29, 28,
|
||||
-1, -1, -1, -1, 27, 26, 25, 24,
|
||||
-1, -1, -1, -1, 23, 22, 21, 20,
|
||||
-1, -1, -1, -1, 19, 18, 17, 16,
|
||||
-1, -1, -1, -1, 15, 14, 13, 12,
|
||||
-1, -1, -1, -1, 11, 10, 9, 8,
|
||||
-1, -1, -1, -1, 7, 6, 5, 4,
|
||||
-1, -1, -1, -1, 3, 2, 1, 0
|
||||
};
|
||||
|
||||
// Maximum number of simultaneously active features.
|
||||
static constexpr IndexType MaxActiveDimensions = 32;
|
||||
using IndexList = ValueList<IndexType, MaxActiveDimensions>;
|
||||
|
||||
// Get a list of indices for active features
|
||||
static void append_active_indices(
|
||||
const Position& pos,
|
||||
Color perspective,
|
||||
IndexList& active);
|
||||
|
||||
// Get a list of indices for recently changed features
|
||||
static void append_changed_indices(
|
||||
Square ksq,
|
||||
const DirtyPiece& dp,
|
||||
Color perspective,
|
||||
IndexList& removed,
|
||||
IndexList& added
|
||||
);
|
||||
|
||||
// Returns the cost of updating one perspective, the most costly one.
|
||||
// Assumes no refresh needed.
|
||||
static int update_cost(const StateInfo* st);
|
||||
static int refresh_cost(const Position& pos);
|
||||
|
||||
// Returns whether the change stored in this StateInfo means that
|
||||
// a full accumulator refresh is required.
|
||||
static bool requires_refresh(const StateInfo* st, Color perspective);
|
||||
};
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE::Features
|
||||
|
||||
#endif // #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED
|
||||
@@ -1,539 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Definition of layer AffineTransform of NNUE evaluation function
|
||||
|
||||
#ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
|
||||
#define NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include "../nnue_common.h"
|
||||
#include "../../simd.h"
|
||||
|
||||
/*
|
||||
This file contains the definition for a fully connected layer (aka affine transform).
|
||||
Two approaches are employed, depending on the sizes of the transform.
|
||||
|
||||
Approach 1:
|
||||
- used when the PaddedInputDimensions >= 128
|
||||
- uses AVX512 if possible
|
||||
- processes inputs in batches of 2*InputSimdWidth
|
||||
- so in batches of 128 for AVX512
|
||||
- the weight blocks of size InputSimdWidth are transposed such that
|
||||
access is sequential
|
||||
- N columns of the weight matrix are processed a time, where N
|
||||
depends on the architecture (the amount of registers)
|
||||
- accumulate + hadd is used
|
||||
|
||||
Approach 2:
|
||||
- used when the PaddedInputDimensions < 128
|
||||
- does not use AVX512
|
||||
- expected use-case is for when PaddedInputDimensions == 32 and InputDimensions <= 32.
|
||||
- that's why AVX512 is hard to implement
|
||||
- expected use-case is small layers
|
||||
- not optimized as well as the approach 1
|
||||
- inputs are processed in chunks of 4, weights are respectively transposed
|
||||
- accumulation happens directly to int32s
|
||||
*/
|
||||
|
||||
namespace Stockfish::Eval::NNUE::Layers {
|
||||
|
||||
// Fallback implementation for older/other architectures.
|
||||
// Identical for both approaches. Requires the input to be padded to at least 16 values.
|
||||
#if !defined(USE_SSSE3)
|
||||
template <IndexType InputDimensions, IndexType PaddedInputDimensions, IndexType OutputDimensions>
|
||||
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)
|
||||
{
|
||||
# if defined(USE_SSE2)
|
||||
// At least a multiple of 16, with SSE2.
|
||||
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 16) / 16;
|
||||
const __m128i Zeros = _mm_setzero_si128();
|
||||
const auto inputVector = reinterpret_cast<const __m128i*>(input);
|
||||
|
||||
# elif defined(USE_MMX)
|
||||
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / 8;
|
||||
const __m64 Zeros = _mm_setzero_si64();
|
||||
const auto inputVector = reinterpret_cast<const __m64*>(input);
|
||||
|
||||
# elif defined(USE_NEON)
|
||||
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 16) / 16;
|
||||
const auto inputVector = reinterpret_cast<const int8x8_t*>(input);
|
||||
# endif
|
||||
|
||||
for (IndexType i = 0; i < OutputDimensions; ++i) {
|
||||
const IndexType offset = i * PaddedInputDimensions;
|
||||
|
||||
# if defined(USE_SSE2)
|
||||
__m128i sumLo = _mm_cvtsi32_si128(biases[i]);
|
||||
__m128i sumHi = Zeros;
|
||||
const auto row = reinterpret_cast<const __m128i*>(&weights[offset]);
|
||||
for (IndexType j = 0; j < NumChunks; ++j) {
|
||||
__m128i row_j = _mm_load_si128(&row[j]);
|
||||
__m128i input_j = _mm_load_si128(&inputVector[j]);
|
||||
__m128i extendedRowLo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8);
|
||||
__m128i extendedRowHi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8);
|
||||
__m128i extendedInputLo = _mm_unpacklo_epi8(input_j, Zeros);
|
||||
__m128i extendedInputHi = _mm_unpackhi_epi8(input_j, Zeros);
|
||||
__m128i productLo = _mm_madd_epi16(extendedRowLo, extendedInputLo);
|
||||
__m128i productHi = _mm_madd_epi16(extendedRowHi, extendedInputHi);
|
||||
sumLo = _mm_add_epi32(sumLo, productLo);
|
||||
sumHi = _mm_add_epi32(sumHi, productHi);
|
||||
}
|
||||
__m128i sum = _mm_add_epi32(sumLo, sumHi);
|
||||
__m128i sumHigh_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
sum = _mm_add_epi32(sum, sumHigh_64);
|
||||
__m128i sum_second_32 = _mm_shufflelo_epi16(sum, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
sum = _mm_add_epi32(sum, sum_second_32);
|
||||
output[i] = _mm_cvtsi128_si32(sum);
|
||||
|
||||
# elif defined(USE_MMX)
|
||||
__m64 sumLo = _mm_cvtsi32_si64(biases[i]);
|
||||
__m64 sumHi = Zeros;
|
||||
const auto row = reinterpret_cast<const __m64*>(&weights[offset]);
|
||||
for (IndexType j = 0; j < NumChunks; ++j) {
|
||||
__m64 row_j = row[j];
|
||||
__m64 input_j = inputVector[j];
|
||||
__m64 extendedRowLo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8);
|
||||
__m64 extendedRowHi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8);
|
||||
__m64 extendedInputLo = _mm_unpacklo_pi8(input_j, Zeros);
|
||||
__m64 extendedInputHi = _mm_unpackhi_pi8(input_j, Zeros);
|
||||
__m64 productLo = _mm_madd_pi16(extendedRowLo, extendedInputLo);
|
||||
__m64 productHi = _mm_madd_pi16(extendedRowHi, extendedInputHi);
|
||||
sumLo = _mm_add_pi32(sumLo, productLo);
|
||||
sumHi = _mm_add_pi32(sumHi, productHi);
|
||||
}
|
||||
__m64 sum = _mm_add_pi32(sumLo, sumHi);
|
||||
sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum));
|
||||
output[i] = _mm_cvtsi64_si32(sum);
|
||||
|
||||
# elif defined(USE_NEON)
|
||||
int32x4_t sum = {biases[i]};
|
||||
const auto row = reinterpret_cast<const int8x8_t*>(&weights[offset]);
|
||||
for (IndexType j = 0; j < NumChunks; ++j) {
|
||||
int16x8_t product = vmull_s8(inputVector[j * 2], row[j * 2]);
|
||||
product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]);
|
||||
sum = vpadalq_s16(sum, product);
|
||||
}
|
||||
output[i] = sum[0] + sum[1] + sum[2] + sum[3];
|
||||
|
||||
# else
|
||||
std::int32_t sum = biases[i];
|
||||
for (IndexType j = 0; j < InputDimensions; ++j) {
|
||||
sum += weights[offset + j] * input[j];
|
||||
}
|
||||
output[i] = sum;
|
||||
# endif
|
||||
}
|
||||
|
||||
# if defined(USE_MMX)
|
||||
_mm_empty();
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
template <IndexType InDims, IndexType OutDims, typename Enabled = void>
|
||||
class AffineTransform;
|
||||
|
||||
// A specialization for large inputs.
|
||||
template <IndexType InDims, IndexType OutDims>
|
||||
class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) >= 2*64)>> {
|
||||
public:
|
||||
// Input/output type
|
||||
using InputType = std::uint8_t;
|
||||
using OutputType = std::int32_t;
|
||||
|
||||
// Number of input/output dimensions
|
||||
static constexpr IndexType InputDimensions = InDims;
|
||||
static constexpr IndexType OutputDimensions = OutDims;
|
||||
|
||||
static constexpr IndexType PaddedInputDimensions =
|
||||
ceil_to_multiple<IndexType>(InputDimensions, MaxSimdWidth);
|
||||
static constexpr IndexType PaddedOutputDimensions =
|
||||
ceil_to_multiple<IndexType>(OutputDimensions, MaxSimdWidth);
|
||||
|
||||
using OutputBuffer = OutputType[PaddedOutputDimensions];
|
||||
|
||||
static_assert(PaddedInputDimensions >= 128, "Something went wrong. This specialization should not have been chosen.");
|
||||
|
||||
#if defined (USE_AVX512)
|
||||
static constexpr const IndexType InputSimdWidth = 64;
|
||||
static constexpr const IndexType MaxNumOutputRegs = 16;
|
||||
#elif defined (USE_AVX2)
|
||||
static constexpr const IndexType InputSimdWidth = 32;
|
||||
static constexpr const IndexType MaxNumOutputRegs = 8;
|
||||
#elif defined (USE_SSSE3)
|
||||
static constexpr const IndexType InputSimdWidth = 16;
|
||||
static constexpr const IndexType MaxNumOutputRegs = 8;
|
||||
#elif defined (USE_NEON)
|
||||
static constexpr const IndexType InputSimdWidth = 8;
|
||||
static constexpr const IndexType MaxNumOutputRegs = 8;
|
||||
#else
|
||||
// The fallback implementation will not have permuted weights.
|
||||
// We define these to avoid a lot of ifdefs later.
|
||||
static constexpr const IndexType InputSimdWidth = 1;
|
||||
static constexpr const IndexType MaxNumOutputRegs = 1;
|
||||
#endif
|
||||
|
||||
// A big block is a region in the weight matrix of the size [PaddedInputDimensions, NumOutputRegs].
|
||||
// A small block is a region of size [InputSimdWidth, 1]
|
||||
|
||||
static constexpr const IndexType NumOutputRegs = std::min(MaxNumOutputRegs, OutputDimensions);
|
||||
static constexpr const IndexType SmallBlockSize = InputSimdWidth;
|
||||
static constexpr const IndexType BigBlockSize = NumOutputRegs * PaddedInputDimensions;
|
||||
static constexpr const IndexType NumSmallBlocksInBigBlock = BigBlockSize / SmallBlockSize;
|
||||
static constexpr const IndexType NumSmallBlocksPerOutput = PaddedInputDimensions / SmallBlockSize;
|
||||
static constexpr const IndexType NumBigBlocks = OutputDimensions / NumOutputRegs;
|
||||
|
||||
static_assert(OutputDimensions % NumOutputRegs == 0);
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
|
||||
std::uint32_t hashValue = 0xCC03DAE4u;
|
||||
hashValue += OutputDimensions;
|
||||
hashValue ^= prevHash >> 1;
|
||||
hashValue ^= prevHash << 31;
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
/*
|
||||
Transposes the small blocks within a block.
|
||||
Effectively means that weights can be traversed sequentially during inference.
|
||||
*/
|
||||
static IndexType get_weight_index(IndexType i)
|
||||
{
|
||||
const IndexType smallBlock = (i / SmallBlockSize) % NumSmallBlocksInBigBlock;
|
||||
const IndexType smallBlockCol = smallBlock / NumSmallBlocksPerOutput;
|
||||
const IndexType smallBlockRow = smallBlock % NumSmallBlocksPerOutput;
|
||||
const IndexType bigBlock = i / BigBlockSize;
|
||||
const IndexType rest = i % SmallBlockSize;
|
||||
|
||||
const IndexType idx =
|
||||
bigBlock * BigBlockSize
|
||||
+ smallBlockRow * SmallBlockSize * NumOutputRegs
|
||||
+ smallBlockCol * SmallBlockSize
|
||||
+ rest;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool read_parameters(std::istream& stream) {
|
||||
for (IndexType i = 0; i < OutputDimensions; ++i)
|
||||
biases[i] = read_little_endian<BiasType>(stream);
|
||||
|
||||
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
|
||||
weights[get_weight_index(i)] = read_little_endian<WeightType>(stream);
|
||||
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Write network parameters
|
||||
bool write_parameters(std::ostream& stream) const {
|
||||
for (IndexType i = 0; i < OutputDimensions; ++i)
|
||||
write_little_endian<BiasType>(stream, biases[i]);
|
||||
|
||||
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
|
||||
write_little_endian<WeightType>(stream, weights[get_weight_index(i)]);
|
||||
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Forward propagation
|
||||
const OutputType* propagate(
|
||||
const InputType* input, OutputType* output) const {
|
||||
|
||||
#if defined (USE_AVX512)
|
||||
using acc_vec_t = __m512i;
|
||||
using bias_vec_t = __m128i;
|
||||
using weight_vec_t = __m512i;
|
||||
using in_vec_t = __m512i;
|
||||
#define vec_zero _mm512_setzero_si512()
|
||||
#define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2
|
||||
#define vec_hadd Simd::m512_hadd
|
||||
#define vec_haddx4 Simd::m512_haddx4
|
||||
#elif defined (USE_AVX2)
|
||||
using acc_vec_t = __m256i;
|
||||
using bias_vec_t = __m128i;
|
||||
using weight_vec_t = __m256i;
|
||||
using in_vec_t = __m256i;
|
||||
#define vec_zero _mm256_setzero_si256()
|
||||
#define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2
|
||||
#define vec_hadd Simd::m256_hadd
|
||||
#define vec_haddx4 Simd::m256_haddx4
|
||||
#elif defined (USE_SSSE3)
|
||||
using acc_vec_t = __m128i;
|
||||
using bias_vec_t = __m128i;
|
||||
using weight_vec_t = __m128i;
|
||||
using in_vec_t = __m128i;
|
||||
#define vec_zero _mm_setzero_si128()
|
||||
#define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2
|
||||
#define vec_hadd Simd::m128_hadd
|
||||
#define vec_haddx4 Simd::m128_haddx4
|
||||
#elif defined (USE_NEON)
|
||||
using acc_vec_t = int32x4_t;
|
||||
using bias_vec_t = int32x4_t;
|
||||
using weight_vec_t = int8x8_t;
|
||||
using in_vec_t = int8x8_t;
|
||||
#define vec_zero {0}
|
||||
#define vec_add_dpbusd_32x2 Simd::neon_m128_add_dpbusd_epi32x2
|
||||
#define vec_hadd Simd::neon_m128_hadd
|
||||
#define vec_haddx4 Simd::neon_m128_haddx4
|
||||
#endif
|
||||
|
||||
#if defined (USE_SSSE3) || defined (USE_NEON)
|
||||
const in_vec_t* invec = reinterpret_cast<const in_vec_t*>(input);
|
||||
|
||||
// Perform accumulation to registers for each big block
|
||||
for (IndexType bigBlock = 0; bigBlock < NumBigBlocks; ++bigBlock)
|
||||
{
|
||||
acc_vec_t acc[NumOutputRegs] = { vec_zero };
|
||||
|
||||
// Each big block has NumOutputRegs small blocks in each "row", one per register.
|
||||
// We process two small blocks at a time to save on one addition without VNNI.
|
||||
for (IndexType smallBlock = 0; smallBlock < NumSmallBlocksPerOutput; smallBlock += 2)
|
||||
{
|
||||
const weight_vec_t* weightvec =
|
||||
reinterpret_cast<const weight_vec_t*>(
|
||||
weights
|
||||
+ bigBlock * BigBlockSize
|
||||
+ smallBlock * SmallBlockSize * NumOutputRegs);
|
||||
|
||||
const in_vec_t in0 = invec[smallBlock + 0];
|
||||
const in_vec_t in1 = invec[smallBlock + 1];
|
||||
|
||||
for (IndexType k = 0; k < NumOutputRegs; ++k)
|
||||
vec_add_dpbusd_32x2(acc[k], in0, weightvec[k], in1, weightvec[k + NumOutputRegs]);
|
||||
}
|
||||
|
||||
// Horizontally add all accumulators.
|
||||
if constexpr (NumOutputRegs % 4 == 0)
|
||||
{
|
||||
bias_vec_t* outputvec = reinterpret_cast<bias_vec_t*>(output);
|
||||
const bias_vec_t* biasvec = reinterpret_cast<const bias_vec_t*>(biases);
|
||||
|
||||
for (IndexType k = 0; k < NumOutputRegs; k += 4)
|
||||
{
|
||||
const IndexType idx = (bigBlock * NumOutputRegs + k) / 4;
|
||||
outputvec[idx] = vec_haddx4(acc[k+0], acc[k+1], acc[k+2], acc[k+3], biasvec[idx]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (IndexType k = 0; k < NumOutputRegs; ++k)
|
||||
{
|
||||
const IndexType idx = (bigBlock * NumOutputRegs + k);
|
||||
output[idx] = vec_hadd(acc[k], biases[idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# undef vec_zero
|
||||
# undef vec_add_dpbusd_32x2
|
||||
# undef vec_hadd
|
||||
# undef vec_haddx4
|
||||
#else
|
||||
// Use old implementation for the other architectures.
|
||||
affine_transform_non_ssse3<
|
||||
InputDimensions,
|
||||
PaddedInputDimensions,
|
||||
OutputDimensions>(output, weights, biases, input);
|
||||
|
||||
#endif
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private:
|
||||
using BiasType = OutputType;
|
||||
using WeightType = std::int8_t;
|
||||
|
||||
alignas(CacheLineSize) BiasType biases[OutputDimensions];
|
||||
alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions];
|
||||
};
|
||||
|
||||
template <IndexType InDims, IndexType OutDims>
|
||||
class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) < 2*64)>> {
|
||||
public:
|
||||
// Input/output type
|
||||
// Input/output type
|
||||
using InputType = std::uint8_t;
|
||||
using OutputType = std::int32_t;
|
||||
|
||||
// Number of input/output dimensions
|
||||
static constexpr IndexType InputDimensions = InDims;
|
||||
static constexpr IndexType OutputDimensions = OutDims;
|
||||
|
||||
static constexpr IndexType PaddedInputDimensions =
|
||||
ceil_to_multiple<IndexType>(InputDimensions, MaxSimdWidth);
|
||||
static constexpr IndexType PaddedOutputDimensions =
|
||||
ceil_to_multiple<IndexType>(OutputDimensions, MaxSimdWidth);
|
||||
|
||||
using OutputBuffer = OutputType[PaddedOutputDimensions];
|
||||
|
||||
static_assert(PaddedInputDimensions < 128, "Something went wrong. This specialization should not have been chosen.");
|
||||
|
||||
#if defined (USE_SSSE3)
|
||||
static constexpr const IndexType OutputSimdWidth = SimdWidth / 4;
|
||||
static constexpr const IndexType InputSimdWidth = SimdWidth;
|
||||
#endif
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
|
||||
std::uint32_t hashValue = 0xCC03DAE4u;
|
||||
hashValue += OutputDimensions;
|
||||
hashValue ^= prevHash >> 1;
|
||||
hashValue ^= prevHash << 31;
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
static IndexType get_weight_index_scrambled(IndexType i)
|
||||
{
|
||||
return
|
||||
(i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 +
|
||||
i / PaddedInputDimensions * 4 +
|
||||
i % 4;
|
||||
}
|
||||
|
||||
static IndexType get_weight_index(IndexType i)
|
||||
{
|
||||
#if defined (USE_SSSE3)
|
||||
return get_weight_index_scrambled(i);
|
||||
#else
|
||||
return i;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool read_parameters(std::istream& stream) {
|
||||
for (IndexType i = 0; i < OutputDimensions; ++i)
|
||||
biases[i] = read_little_endian<BiasType>(stream);
|
||||
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
|
||||
weights[get_weight_index(i)] = read_little_endian<WeightType>(stream);
|
||||
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Write network parameters
|
||||
bool write_parameters(std::ostream& stream) const {
|
||||
for (IndexType i = 0; i < OutputDimensions; ++i)
|
||||
write_little_endian<BiasType>(stream, biases[i]);
|
||||
|
||||
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
|
||||
write_little_endian<WeightType>(stream, weights[get_weight_index(i)]);
|
||||
|
||||
return !stream.fail();
|
||||
}
|
||||
// Forward propagation
|
||||
const OutputType* propagate(
|
||||
const InputType* input, OutputType* output) const {
|
||||
|
||||
#if defined (USE_AVX2)
|
||||
using vec_t = __m256i;
|
||||
#define vec_setzero _mm256_setzero_si256
|
||||
#define vec_set_32 _mm256_set1_epi32
|
||||
#define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32
|
||||
#define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2
|
||||
#define vec_add_dpbusd_32x4 Simd::m256_add_dpbusd_epi32x4
|
||||
#define vec_hadd Simd::m256_hadd
|
||||
#define vec_haddx4 Simd::m256_haddx4
|
||||
#elif defined (USE_SSSE3)
|
||||
using vec_t = __m128i;
|
||||
#define vec_setzero _mm_setzero_si128
|
||||
#define vec_set_32 _mm_set1_epi32
|
||||
#define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32
|
||||
#define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2
|
||||
#define vec_add_dpbusd_32x4 Simd::m128_add_dpbusd_epi32x4
|
||||
#define vec_hadd Simd::m128_hadd
|
||||
#define vec_haddx4 Simd::m128_haddx4
|
||||
#endif
|
||||
|
||||
#if defined (USE_SSSE3)
|
||||
const auto inputVector = reinterpret_cast<const vec_t*>(input);
|
||||
|
||||
static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1);
|
||||
|
||||
if constexpr (OutputDimensions % OutputSimdWidth == 0)
|
||||
{
|
||||
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / 4;
|
||||
constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth;
|
||||
|
||||
const auto input32 = reinterpret_cast<const std::int32_t*>(input);
|
||||
const vec_t* biasvec = reinterpret_cast<const vec_t*>(biases);
|
||||
vec_t acc[NumRegs];
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
acc[k] = biasvec[k];
|
||||
|
||||
for (IndexType i = 0; i < NumChunks; i += 2)
|
||||
{
|
||||
const vec_t in0 = vec_set_32(input32[i + 0]);
|
||||
const vec_t in1 = vec_set_32(input32[i + 1]);
|
||||
const auto col0 = reinterpret_cast<const vec_t*>(&weights[(i + 0) * OutputDimensions * 4]);
|
||||
const auto col1 = reinterpret_cast<const vec_t*>(&weights[(i + 1) * OutputDimensions * 4]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
vec_add_dpbusd_32x2(acc[k], in0, col0[k], in1, col1[k]);
|
||||
}
|
||||
|
||||
vec_t* outptr = reinterpret_cast<vec_t*>(output);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
outptr[k] = acc[k];
|
||||
}
|
||||
else if constexpr (OutputDimensions == 1)
|
||||
{
|
||||
constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth;
|
||||
vec_t sum0 = vec_setzero();
|
||||
const auto row0 = reinterpret_cast<const vec_t*>(&weights[0]);
|
||||
|
||||
for (int j = 0; j < (int)NumChunks; ++j)
|
||||
{
|
||||
const vec_t in = inputVector[j];
|
||||
vec_add_dpbusd_32(sum0, in, row0[j]);
|
||||
}
|
||||
output[0] = vec_hadd(sum0, biases[0]);
|
||||
}
|
||||
|
||||
# undef vec_setzero
|
||||
# undef vec_set_32
|
||||
# undef vec_add_dpbusd_32
|
||||
# undef vec_add_dpbusd_32x2
|
||||
# undef vec_add_dpbusd_32x4
|
||||
# undef vec_hadd
|
||||
# undef vec_haddx4
|
||||
#else
|
||||
// Use old implementation for the other architectures.
|
||||
affine_transform_non_ssse3<
|
||||
InputDimensions,
|
||||
PaddedInputDimensions,
|
||||
OutputDimensions>(output, weights, biases, input);
|
||||
#endif
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private:
|
||||
using BiasType = OutputType;
|
||||
using WeightType = std::int8_t;
|
||||
|
||||
alignas(CacheLineSize) BiasType biases[OutputDimensions];
|
||||
alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions];
|
||||
};
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE::Layers
|
||||
|
||||
#endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Definition of layer ClippedReLU of NNUE evaluation function
|
||||
|
||||
#ifndef NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED
|
||||
#define NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED
|
||||
|
||||
#include "../nnue_common.h"
|
||||
|
||||
namespace Stockfish::Eval::NNUE::Layers {
|
||||
|
||||
// Clipped ReLU
|
||||
template <IndexType InDims>
|
||||
class ClippedReLU {
|
||||
public:
|
||||
// Input/output type
|
||||
using InputType = std::int32_t;
|
||||
using OutputType = std::uint8_t;
|
||||
|
||||
// Number of input/output dimensions
|
||||
static constexpr IndexType InputDimensions = InDims;
|
||||
static constexpr IndexType OutputDimensions = InputDimensions;
|
||||
static constexpr IndexType PaddedOutputDimensions =
|
||||
ceil_to_multiple<IndexType>(OutputDimensions, 32);
|
||||
|
||||
using OutputBuffer = OutputType[PaddedOutputDimensions];
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
|
||||
std::uint32_t hashValue = 0x538D24C7u;
|
||||
hashValue += prevHash;
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool read_parameters(std::istream&) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write network parameters
|
||||
bool write_parameters(std::ostream&) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Forward propagation
|
||||
const OutputType* propagate(
|
||||
const InputType* input, OutputType* output) const {
|
||||
|
||||
#if defined(USE_AVX2)
|
||||
if constexpr (InputDimensions % SimdWidth == 0) {
|
||||
constexpr IndexType NumChunks = InputDimensions / SimdWidth;
|
||||
const __m256i Zero = _mm256_setzero_si256();
|
||||
const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0);
|
||||
const auto in = reinterpret_cast<const __m256i*>(input);
|
||||
const auto out = reinterpret_cast<__m256i*>(output);
|
||||
for (IndexType i = 0; i < NumChunks; ++i) {
|
||||
const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32(
|
||||
_mm256_load_si256(&in[i * 4 + 0]),
|
||||
_mm256_load_si256(&in[i * 4 + 1])), WeightScaleBits);
|
||||
const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32(
|
||||
_mm256_load_si256(&in[i * 4 + 2]),
|
||||
_mm256_load_si256(&in[i * 4 + 3])), WeightScaleBits);
|
||||
_mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8(
|
||||
_mm256_packs_epi16(words0, words1), Zero), Offsets));
|
||||
}
|
||||
} else {
|
||||
constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2);
|
||||
const __m128i Zero = _mm_setzero_si128();
|
||||
const auto in = reinterpret_cast<const __m128i*>(input);
|
||||
const auto out = reinterpret_cast<__m128i*>(output);
|
||||
for (IndexType i = 0; i < NumChunks; ++i) {
|
||||
const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32(
|
||||
_mm_load_si128(&in[i * 4 + 0]),
|
||||
_mm_load_si128(&in[i * 4 + 1])), WeightScaleBits);
|
||||
const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32(
|
||||
_mm_load_si128(&in[i * 4 + 2]),
|
||||
_mm_load_si128(&in[i * 4 + 3])), WeightScaleBits);
|
||||
const __m128i packedbytes = _mm_packs_epi16(words0, words1);
|
||||
_mm_store_si128(&out[i], _mm_max_epi8(packedbytes, Zero));
|
||||
}
|
||||
}
|
||||
constexpr IndexType Start =
|
||||
InputDimensions % SimdWidth == 0
|
||||
? InputDimensions / SimdWidth * SimdWidth
|
||||
: InputDimensions / (SimdWidth / 2) * (SimdWidth / 2);
|
||||
|
||||
#elif defined(USE_SSE2)
|
||||
constexpr IndexType NumChunks = InputDimensions / SimdWidth;
|
||||
|
||||
#ifdef USE_SSE41
|
||||
const __m128i Zero = _mm_setzero_si128();
|
||||
#else
|
||||
const __m128i k0x80s = _mm_set1_epi8(-128);
|
||||
#endif
|
||||
|
||||
const auto in = reinterpret_cast<const __m128i*>(input);
|
||||
const auto out = reinterpret_cast<__m128i*>(output);
|
||||
for (IndexType i = 0; i < NumChunks; ++i) {
|
||||
const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32(
|
||||
_mm_load_si128(&in[i * 4 + 0]),
|
||||
_mm_load_si128(&in[i * 4 + 1])), WeightScaleBits);
|
||||
const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32(
|
||||
_mm_load_si128(&in[i * 4 + 2]),
|
||||
_mm_load_si128(&in[i * 4 + 3])), WeightScaleBits);
|
||||
const __m128i packedbytes = _mm_packs_epi16(words0, words1);
|
||||
_mm_store_si128(&out[i],
|
||||
|
||||
#ifdef USE_SSE41
|
||||
_mm_max_epi8(packedbytes, Zero)
|
||||
#else
|
||||
_mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)
|
||||
#endif
|
||||
|
||||
);
|
||||
}
|
||||
constexpr IndexType Start = NumChunks * SimdWidth;
|
||||
|
||||
#elif defined(USE_MMX)
|
||||
constexpr IndexType NumChunks = InputDimensions / SimdWidth;
|
||||
const __m64 k0x80s = _mm_set1_pi8(-128);
|
||||
const auto in = reinterpret_cast<const __m64*>(input);
|
||||
const auto out = reinterpret_cast<__m64*>(output);
|
||||
for (IndexType i = 0; i < NumChunks; ++i) {
|
||||
const __m64 words0 = _mm_srai_pi16(
|
||||
_mm_packs_pi32(in[i * 4 + 0], in[i * 4 + 1]),
|
||||
WeightScaleBits);
|
||||
const __m64 words1 = _mm_srai_pi16(
|
||||
_mm_packs_pi32(in[i * 4 + 2], in[i * 4 + 3]),
|
||||
WeightScaleBits);
|
||||
const __m64 packedbytes = _mm_packs_pi16(words0, words1);
|
||||
out[i] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s);
|
||||
}
|
||||
_mm_empty();
|
||||
constexpr IndexType Start = NumChunks * SimdWidth;
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2);
|
||||
const int8x8_t Zero = {0};
|
||||
const auto in = reinterpret_cast<const int32x4_t*>(input);
|
||||
const auto out = reinterpret_cast<int8x8_t*>(output);
|
||||
for (IndexType i = 0; i < NumChunks; ++i) {
|
||||
int16x8_t shifted;
|
||||
const auto pack = reinterpret_cast<int16x4_t*>(&shifted);
|
||||
pack[0] = vqshrn_n_s32(in[i * 2 + 0], WeightScaleBits);
|
||||
pack[1] = vqshrn_n_s32(in[i * 2 + 1], WeightScaleBits);
|
||||
out[i] = vmax_s8(vqmovn_s16(shifted), Zero);
|
||||
}
|
||||
constexpr IndexType Start = NumChunks * (SimdWidth / 2);
|
||||
#else
|
||||
constexpr IndexType Start = 0;
|
||||
#endif
|
||||
|
||||
for (IndexType i = Start; i < InputDimensions; ++i) {
|
||||
output[i] = static_cast<OutputType>(
|
||||
std::max(0, std::min(127, input[i] >> WeightScaleBits)));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE::Layers
|
||||
|
||||
#endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Input features and network structure used in NNUE evaluation function
|
||||
|
||||
#ifndef NNUE_ARCHITECTURE_H_INCLUDED
|
||||
#define NNUE_ARCHITECTURE_H_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "nnue_common.h"
|
||||
|
||||
#include "features/half_ka_v2_hm.h"
|
||||
|
||||
#include "layers/affine_transform.h"
|
||||
#include "layers/clipped_relu.h"
|
||||
|
||||
#include "../misc.h"
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
|
||||
// Input features used in evaluation function
|
||||
using FeatureSet = Features::HalfKAv2_hm;
|
||||
|
||||
// Number of input feature dimensions after conversion
|
||||
constexpr IndexType TransformedFeatureDimensions = 1024;
|
||||
constexpr IndexType PSQTBuckets = 8;
|
||||
constexpr IndexType LayerStacks = 8;
|
||||
|
||||
struct Network
|
||||
{
|
||||
static constexpr int FC_0_OUTPUTS = 15;
|
||||
static constexpr int FC_1_OUTPUTS = 32;
|
||||
|
||||
Layers::AffineTransform<TransformedFeatureDimensions, FC_0_OUTPUTS + 1> fc_0;
|
||||
Layers::ClippedReLU<FC_0_OUTPUTS + 1> ac_0;
|
||||
Layers::AffineTransform<FC_0_OUTPUTS, FC_1_OUTPUTS> fc_1;
|
||||
Layers::ClippedReLU<FC_1_OUTPUTS> ac_1;
|
||||
Layers::AffineTransform<FC_1_OUTPUTS, 1> fc_2;
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t get_hash_value() {
|
||||
// input slice hash
|
||||
std::uint32_t hashValue = 0xEC42E90Du;
|
||||
hashValue ^= TransformedFeatureDimensions * 2;
|
||||
|
||||
hashValue = decltype(fc_0)::get_hash_value(hashValue);
|
||||
hashValue = decltype(ac_0)::get_hash_value(hashValue);
|
||||
hashValue = decltype(fc_1)::get_hash_value(hashValue);
|
||||
hashValue = decltype(ac_1)::get_hash_value(hashValue);
|
||||
hashValue = decltype(fc_2)::get_hash_value(hashValue);
|
||||
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool read_parameters(std::istream& stream) {
|
||||
if (!fc_0.read_parameters(stream)) return false;
|
||||
if (!ac_0.read_parameters(stream)) return false;
|
||||
if (!fc_1.read_parameters(stream)) return false;
|
||||
if (!ac_1.read_parameters(stream)) return false;
|
||||
if (!fc_2.read_parameters(stream)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool write_parameters(std::ostream& stream) const {
|
||||
if (!fc_0.write_parameters(stream)) return false;
|
||||
if (!ac_0.write_parameters(stream)) return false;
|
||||
if (!fc_1.write_parameters(stream)) return false;
|
||||
if (!ac_1.write_parameters(stream)) return false;
|
||||
if (!fc_2.write_parameters(stream)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::int32_t propagate(const TransformedFeatureType* transformedFeatures)
|
||||
{
|
||||
struct alignas(CacheLineSize) Buffer
|
||||
{
|
||||
alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out;
|
||||
alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out;
|
||||
alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out;
|
||||
alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out;
|
||||
alignas(CacheLineSize) decltype(fc_2)::OutputBuffer fc_2_out;
|
||||
|
||||
Buffer()
|
||||
{
|
||||
std::memset(this, 0, sizeof(*this));
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(__clang__) && (__APPLE__)
|
||||
// workaround for a bug reported with xcode 12
|
||||
static thread_local auto tlsBuffer = std::make_unique<Buffer>();
|
||||
// Access TLS only once, cache result.
|
||||
Buffer& buffer = *tlsBuffer;
|
||||
#else
|
||||
alignas(CacheLineSize) static thread_local Buffer buffer;
|
||||
#endif
|
||||
|
||||
fc_0.propagate(transformedFeatures, buffer.fc_0_out);
|
||||
ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out);
|
||||
fc_1.propagate(buffer.ac_0_out, buffer.fc_1_out);
|
||||
ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out);
|
||||
fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out);
|
||||
|
||||
// buffer.fc_0_out[FC_0_OUTPUTS] is such that 1.0 is equal to 127*(1<<WeightScaleBits) in quantized form
|
||||
// but we want 1.0 to be equal to 600*OutputScale
|
||||
std::int32_t fwdOut = int(buffer.fc_0_out[FC_0_OUTPUTS]) * (600*OutputScale) / (127*(1<<WeightScaleBits));
|
||||
std::int32_t outputValue = buffer.fc_2_out[0] + fwdOut;
|
||||
|
||||
return outputValue;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
|
||||
#endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Constants used in NNUE evaluation function
|
||||
|
||||
#ifndef NNUE_COMMON_H_INCLUDED
|
||||
#define NNUE_COMMON_H_INCLUDED
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include "../misc.h" // for IsLittleEndian
|
||||
|
||||
#if defined(USE_AVX2)
|
||||
#include <immintrin.h>
|
||||
|
||||
#elif defined(USE_SSE41)
|
||||
#include <smmintrin.h>
|
||||
|
||||
#elif defined(USE_SSSE3)
|
||||
#include <tmmintrin.h>
|
||||
|
||||
#elif defined(USE_SSE2)
|
||||
#include <emmintrin.h>
|
||||
|
||||
#elif defined(USE_MMX)
|
||||
#include <mmintrin.h>
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
|
||||
// Version of the evaluation file
|
||||
constexpr std::uint32_t Version = 0x7AF32F20u;
|
||||
|
||||
// Constant used in evaluation value calculation
|
||||
constexpr int OutputScale = 16;
|
||||
constexpr int WeightScaleBits = 6;
|
||||
|
||||
// Size of cache line (in bytes)
|
||||
constexpr std::size_t CacheLineSize = 64;
|
||||
|
||||
// SIMD width (in bytes)
|
||||
#if defined(USE_AVX2)
|
||||
constexpr std::size_t SimdWidth = 32;
|
||||
|
||||
#elif defined(USE_SSE2)
|
||||
constexpr std::size_t SimdWidth = 16;
|
||||
|
||||
#elif defined(USE_MMX)
|
||||
constexpr std::size_t SimdWidth = 8;
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
constexpr std::size_t SimdWidth = 16;
|
||||
#endif
|
||||
|
||||
constexpr std::size_t MaxSimdWidth = 32;
|
||||
|
||||
// Type of input feature after conversion
|
||||
using TransformedFeatureType = std::uint8_t;
|
||||
using IndexType = std::uint32_t;
|
||||
|
||||
// Round n up to be a multiple of base
|
||||
template <typename IntType>
|
||||
constexpr IntType ceil_to_multiple(IntType n, IntType base) {
|
||||
return (n + base - 1) / base * base;
|
||||
}
|
||||
|
||||
// read_little_endian() is our utility to read an integer (signed or unsigned, any size)
|
||||
// from a stream in little-endian order. We swap the byte order after the read if
|
||||
// necessary to return a result with the byte ordering of the compiling machine.
|
||||
template <typename IntType>
|
||||
inline IntType read_little_endian(std::istream& stream) {
|
||||
IntType result;
|
||||
|
||||
if (IsLittleEndian)
|
||||
stream.read(reinterpret_cast<char*>(&result), sizeof(IntType));
|
||||
else
|
||||
{
|
||||
std::uint8_t u[sizeof(IntType)];
|
||||
typename std::make_unsigned<IntType>::type v = 0;
|
||||
|
||||
stream.read(reinterpret_cast<char*>(u), sizeof(IntType));
|
||||
for (std::size_t i = 0; i < sizeof(IntType); ++i)
|
||||
v = (v << 8) | u[sizeof(IntType) - i - 1];
|
||||
|
||||
std::memcpy(&result, &v, sizeof(IntType));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// write_little_endian() is our utility to write an integer (signed or unsigned, any size)
|
||||
// to a stream in little-endian order. We swap the byte order before the write if
|
||||
// necessary to always write in little endian order, independently of the byte
|
||||
// ordering of the compiling machine.
|
||||
template <typename IntType>
|
||||
inline void write_little_endian(std::ostream& stream, IntType value) {
|
||||
|
||||
if (IsLittleEndian)
|
||||
stream.write(reinterpret_cast<const char*>(&value), sizeof(IntType));
|
||||
else
|
||||
{
|
||||
std::uint8_t u[sizeof(IntType)];
|
||||
typename std::make_unsigned<IntType>::type v = value;
|
||||
|
||||
std::size_t i = 0;
|
||||
// if constexpr to silence the warning about shift by 8
|
||||
if constexpr (sizeof(IntType) > 1)
|
||||
{
|
||||
for (; i + 1 < sizeof(IntType); ++i)
|
||||
{
|
||||
u[i] = (std::uint8_t)v;
|
||||
v >>= 8;
|
||||
}
|
||||
}
|
||||
u[i] = (std::uint8_t)v;
|
||||
|
||||
stream.write(reinterpret_cast<char*>(u), sizeof(IntType));
|
||||
}
|
||||
}
|
||||
|
||||
// read_little_endian(s, out, N) : read integers in bulk from a little indian stream.
|
||||
// This reads N integers from stream s and put them in array out.
|
||||
template <typename IntType>
|
||||
inline void read_little_endian(std::istream& stream, IntType* out, std::size_t count) {
|
||||
if (IsLittleEndian)
|
||||
stream.read(reinterpret_cast<char*>(out), sizeof(IntType) * count);
|
||||
else
|
||||
for (std::size_t i = 0; i < count; ++i)
|
||||
out[i] = read_little_endian<IntType>(stream);
|
||||
}
|
||||
|
||||
// write_little_endian(s, values, N) : write integers in bulk to a little indian stream.
|
||||
// This takes N integers from array values and writes them on stream s.
|
||||
template <typename IntType>
|
||||
inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) {
|
||||
if (IsLittleEndian)
|
||||
stream.write(reinterpret_cast<const char*>(values), sizeof(IntType) * count);
|
||||
else
|
||||
for (std::size_t i = 0; i < count; ++i)
|
||||
write_little_endian<IntType>(stream, values[i]);
|
||||
}
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
|
||||
#endif // #ifndef NNUE_COMMON_H_INCLUDED
|
||||
@@ -1,588 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// A class that converts the input features of the NNUE evaluation function
|
||||
|
||||
#ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED
|
||||
#define NNUE_FEATURE_TRANSFORMER_H_INCLUDED
|
||||
|
||||
#include "nnue_common.h"
|
||||
#include "nnue_architecture.h"
|
||||
|
||||
#include <cstring> // std::memset()
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
|
||||
using BiasType = std::int16_t;
|
||||
using WeightType = std::int16_t;
|
||||
using PSQTWeightType = std::int32_t;
|
||||
|
||||
// If vector instructions are enabled, we update and refresh the
|
||||
// accumulator tile by tile such that each tile fits in the CPU's
|
||||
// vector registers.
|
||||
#define VECTOR
|
||||
|
||||
static_assert(PSQTBuckets % 8 == 0,
|
||||
"Per feature PSQT values cannot be processed at granularity lower than 8 at a time.");
|
||||
|
||||
#ifdef USE_AVX512
|
||||
typedef __m512i vec_t;
|
||||
typedef __m256i psqt_vec_t;
|
||||
#define vec_load(a) _mm512_load_si512(a)
|
||||
#define vec_store(a,b) _mm512_store_si512(a,b)
|
||||
#define vec_add_16(a,b) _mm512_add_epi16(a,b)
|
||||
#define vec_sub_16(a,b) _mm512_sub_epi16(a,b)
|
||||
#define vec_mul_16(a,b) _mm512_mullo_epi16(a,b)
|
||||
#define vec_zero() _mm512_setzero_epi32()
|
||||
#define vec_set_16(a) _mm512_set1_epi16(a)
|
||||
#define vec_max_16(a,b) _mm512_max_epi16(a,b)
|
||||
#define vec_min_16(a,b) _mm512_min_epi16(a,b)
|
||||
inline vec_t vec_msb_pack_16(vec_t a, vec_t b){
|
||||
vec_t compacted = _mm512_packs_epi16(_mm512_srli_epi16(a,7),_mm512_srli_epi16(b,7));
|
||||
return _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7), compacted);
|
||||
}
|
||||
#define vec_load_psqt(a) _mm256_load_si256(a)
|
||||
#define vec_store_psqt(a,b) _mm256_store_si256(a,b)
|
||||
#define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b)
|
||||
#define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b)
|
||||
#define vec_zero_psqt() _mm256_setzero_si256()
|
||||
#define NumRegistersSIMD 32
|
||||
#define MaxChunkSize 64
|
||||
|
||||
#elif USE_AVX2
|
||||
typedef __m256i vec_t;
|
||||
typedef __m256i psqt_vec_t;
|
||||
#define vec_load(a) _mm256_load_si256(a)
|
||||
#define vec_store(a,b) _mm256_store_si256(a,b)
|
||||
#define vec_add_16(a,b) _mm256_add_epi16(a,b)
|
||||
#define vec_sub_16(a,b) _mm256_sub_epi16(a,b)
|
||||
#define vec_mul_16(a,b) _mm256_mullo_epi16(a,b)
|
||||
#define vec_zero() _mm256_setzero_si256()
|
||||
#define vec_set_16(a) _mm256_set1_epi16(a)
|
||||
#define vec_max_16(a,b) _mm256_max_epi16(a,b)
|
||||
#define vec_min_16(a,b) _mm256_min_epi16(a,b)
|
||||
inline vec_t vec_msb_pack_16(vec_t a, vec_t b){
|
||||
vec_t compacted = _mm256_packs_epi16(_mm256_srli_epi16(a,7), _mm256_srli_epi16(b,7));
|
||||
return _mm256_permute4x64_epi64(compacted, 0b11011000);
|
||||
}
|
||||
#define vec_load_psqt(a) _mm256_load_si256(a)
|
||||
#define vec_store_psqt(a,b) _mm256_store_si256(a,b)
|
||||
#define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b)
|
||||
#define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b)
|
||||
#define vec_zero_psqt() _mm256_setzero_si256()
|
||||
#define NumRegistersSIMD 16
|
||||
#define MaxChunkSize 32
|
||||
|
||||
#elif USE_SSE2
|
||||
typedef __m128i vec_t;
|
||||
typedef __m128i psqt_vec_t;
|
||||
#define vec_load(a) (*(a))
|
||||
#define vec_store(a,b) *(a)=(b)
|
||||
#define vec_add_16(a,b) _mm_add_epi16(a,b)
|
||||
#define vec_sub_16(a,b) _mm_sub_epi16(a,b)
|
||||
#define vec_mul_16(a,b) _mm_mullo_epi16(a,b)
|
||||
#define vec_zero() _mm_setzero_si128()
|
||||
#define vec_set_16(a) _mm_set1_epi16(a)
|
||||
#define vec_max_16(a,b) _mm_max_epi16(a,b)
|
||||
#define vec_min_16(a,b) _mm_min_epi16(a,b)
|
||||
#define vec_msb_pack_16(a,b) _mm_packs_epi16(_mm_srli_epi16(a,7),_mm_srli_epi16(b,7))
|
||||
#define vec_load_psqt(a) (*(a))
|
||||
#define vec_store_psqt(a,b) *(a)=(b)
|
||||
#define vec_add_psqt_32(a,b) _mm_add_epi32(a,b)
|
||||
#define vec_sub_psqt_32(a,b) _mm_sub_epi32(a,b)
|
||||
#define vec_zero_psqt() _mm_setzero_si128()
|
||||
#define NumRegistersSIMD (Is64Bit ? 16 : 8)
|
||||
#define MaxChunkSize 16
|
||||
|
||||
#elif USE_MMX
|
||||
typedef __m64 vec_t;
|
||||
typedef __m64 psqt_vec_t;
|
||||
#define vec_load(a) (*(a))
|
||||
#define vec_store(a,b) *(a)=(b)
|
||||
#define vec_add_16(a,b) _mm_add_pi16(a,b)
|
||||
#define vec_sub_16(a,b) _mm_sub_pi16(a,b)
|
||||
#define vec_mul_16(a,b) _mm_mullo_pi16(a,b)
|
||||
#define vec_zero() _mm_setzero_si64()
|
||||
#define vec_set_16(a) _mm_set1_pi16(a)
|
||||
inline vec_t vec_max_16(vec_t a,vec_t b){
|
||||
vec_t comparison = _mm_cmpgt_pi16(a,b);
|
||||
return _mm_or_si64(_mm_and_si64(comparison, a), _mm_andnot_si64(comparison, b));
|
||||
}
|
||||
inline vec_t vec_min_16(vec_t a,vec_t b){
|
||||
vec_t comparison = _mm_cmpgt_pi16(a,b);
|
||||
return _mm_or_si64(_mm_and_si64(comparison, b), _mm_andnot_si64(comparison, a));
|
||||
}
|
||||
#define vec_msb_pack_16(a,b) _mm_packs_pi16(_mm_srli_pi16(a,7),_mm_srli_pi16(b,7))
|
||||
#define vec_load_psqt(a) (*(a))
|
||||
#define vec_store_psqt(a,b) *(a)=(b)
|
||||
#define vec_add_psqt_32(a,b) _mm_add_pi32(a,b)
|
||||
#define vec_sub_psqt_32(a,b) _mm_sub_pi32(a,b)
|
||||
#define vec_zero_psqt() _mm_setzero_si64()
|
||||
#define vec_cleanup() _mm_empty()
|
||||
#define NumRegistersSIMD 8
|
||||
#define MaxChunkSize 8
|
||||
|
||||
#elif USE_NEON
|
||||
typedef int16x8_t vec_t;
|
||||
typedef int32x4_t psqt_vec_t;
|
||||
#define vec_load(a) (*(a))
|
||||
#define vec_store(a,b) *(a)=(b)
|
||||
#define vec_add_16(a,b) vaddq_s16(a,b)
|
||||
#define vec_sub_16(a,b) vsubq_s16(a,b)
|
||||
#define vec_mul_16(a,b) vmulq_s16(a,b)
|
||||
#define vec_zero() vec_t{0}
|
||||
#define vec_set_16(a) vdupq_n_s16(a)
|
||||
#define vec_max_16(a,b) vmaxq_s16(a,b)
|
||||
#define vec_min_16(a,b) vminq_s16(a,b)
|
||||
inline vec_t vec_msb_pack_16(vec_t a, vec_t b){
|
||||
const int8x8_t shifta = vshrn_n_s16(a, 7);
|
||||
const int8x8_t shiftb = vshrn_n_s16(b, 7);
|
||||
const int8x16_t compacted = vcombine_s8(shifta,shiftb);
|
||||
return *reinterpret_cast<const vec_t*> (&compacted);
|
||||
}
|
||||
#define vec_load_psqt(a) (*(a))
|
||||
#define vec_store_psqt(a,b) *(a)=(b)
|
||||
#define vec_add_psqt_32(a,b) vaddq_s32(a,b)
|
||||
#define vec_sub_psqt_32(a,b) vsubq_s32(a,b)
|
||||
#define vec_zero_psqt() psqt_vec_t{0}
|
||||
#define NumRegistersSIMD 16
|
||||
#define MaxChunkSize 16
|
||||
|
||||
#else
|
||||
#undef VECTOR
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef VECTOR
|
||||
|
||||
// Compute optimal SIMD register count for feature transformer accumulation.
|
||||
|
||||
// We use __m* types as template arguments, which causes GCC to emit warnings
|
||||
// about losing some attribute information. This is irrelevant to us as we
|
||||
// only take their size, so the following pragma are harmless.
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wignored-attributes"
|
||||
#endif
|
||||
|
||||
template <typename SIMDRegisterType,
|
||||
typename LaneType,
|
||||
int NumLanes,
|
||||
int MaxRegisters>
|
||||
static constexpr int BestRegisterCount()
|
||||
{
|
||||
#define RegisterSize sizeof(SIMDRegisterType)
|
||||
#define LaneSize sizeof(LaneType)
|
||||
|
||||
static_assert(RegisterSize >= LaneSize);
|
||||
static_assert(MaxRegisters <= NumRegistersSIMD);
|
||||
static_assert(MaxRegisters > 0);
|
||||
static_assert(NumRegistersSIMD > 0);
|
||||
static_assert(RegisterSize % LaneSize == 0);
|
||||
static_assert((NumLanes * LaneSize) % RegisterSize == 0);
|
||||
|
||||
const int ideal = (NumLanes * LaneSize) / RegisterSize;
|
||||
if (ideal <= MaxRegisters)
|
||||
return ideal;
|
||||
|
||||
// Look for the largest divisor of the ideal register count that is smaller than MaxRegisters
|
||||
for (int divisor = MaxRegisters; divisor > 1; --divisor)
|
||||
if (ideal % divisor == 0)
|
||||
return divisor;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static constexpr int NumRegs = BestRegisterCount<vec_t, WeightType, TransformedFeatureDimensions, NumRegistersSIMD>();
|
||||
static constexpr int NumPsqtRegs = BestRegisterCount<psqt_vec_t, PSQTWeightType, PSQTBuckets, NumRegistersSIMD>();
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Input feature converter
|
||||
class FeatureTransformer {
|
||||
|
||||
private:
|
||||
// Number of output dimensions for one side
|
||||
static constexpr IndexType HalfDimensions = TransformedFeatureDimensions;
|
||||
|
||||
#ifdef VECTOR
|
||||
static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2;
|
||||
static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4;
|
||||
static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions");
|
||||
static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets");
|
||||
#endif
|
||||
|
||||
public:
|
||||
// Output type
|
||||
using OutputType = TransformedFeatureType;
|
||||
|
||||
// Number of input/output dimensions
|
||||
static constexpr IndexType InputDimensions = FeatureSet::Dimensions;
|
||||
static constexpr IndexType OutputDimensions = HalfDimensions;
|
||||
|
||||
// Size of forward propagation buffer
|
||||
static constexpr std::size_t BufferSize =
|
||||
OutputDimensions * sizeof(OutputType);
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t get_hash_value() {
|
||||
return FeatureSet::HashValue ^ (OutputDimensions * 2);
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool read_parameters(std::istream& stream) {
|
||||
|
||||
read_little_endian<BiasType >(stream, biases , HalfDimensions );
|
||||
read_little_endian<WeightType >(stream, weights , HalfDimensions * InputDimensions);
|
||||
read_little_endian<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
|
||||
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Write network parameters
|
||||
bool write_parameters(std::ostream& stream) const {
|
||||
|
||||
write_little_endian<BiasType >(stream, biases , HalfDimensions );
|
||||
write_little_endian<WeightType >(stream, weights , HalfDimensions * InputDimensions);
|
||||
write_little_endian<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
|
||||
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Convert input features
|
||||
std::int32_t transform(const Position& pos, OutputType* output, int bucket) const {
|
||||
update_accumulator(pos, WHITE);
|
||||
update_accumulator(pos, BLACK);
|
||||
|
||||
const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()};
|
||||
const auto& accumulation = pos.state()->accumulator.accumulation;
|
||||
const auto& psqtAccumulation = pos.state()->accumulator.psqtAccumulation;
|
||||
|
||||
const auto psqt = (
|
||||
psqtAccumulation[perspectives[0]][bucket]
|
||||
- psqtAccumulation[perspectives[1]][bucket]
|
||||
) / 2;
|
||||
|
||||
|
||||
for (IndexType p = 0; p < 2; ++p)
|
||||
{
|
||||
const IndexType offset = (HalfDimensions / 2) * p;
|
||||
|
||||
#if defined(VECTOR)
|
||||
|
||||
constexpr IndexType OutputChunkSize = MaxChunkSize;
|
||||
static_assert((HalfDimensions / 2) % OutputChunkSize == 0);
|
||||
constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize;
|
||||
|
||||
vec_t Zero = vec_zero();
|
||||
vec_t One = vec_set_16(127);
|
||||
|
||||
const vec_t* in0 = reinterpret_cast<const vec_t*>(&(accumulation[perspectives[p]][0]));
|
||||
const vec_t* in1 = reinterpret_cast<const vec_t*>(&(accumulation[perspectives[p]][HalfDimensions / 2]));
|
||||
vec_t* out = reinterpret_cast< vec_t*>(output + offset);
|
||||
|
||||
for (IndexType j = 0; j < NumOutputChunks; j += 1)
|
||||
{
|
||||
const vec_t sum0a = vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero);
|
||||
const vec_t sum0b = vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero);
|
||||
const vec_t sum1a = vec_max_16(vec_min_16(in1[j * 2 + 0], One), Zero);
|
||||
const vec_t sum1b = vec_max_16(vec_min_16(in1[j * 2 + 1], One), Zero);
|
||||
|
||||
const vec_t pa = vec_mul_16(sum0a, sum1a);
|
||||
const vec_t pb = vec_mul_16(sum0b, sum1b);
|
||||
|
||||
out[j] = vec_msb_pack_16(pa, pb);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
for (IndexType j = 0; j < HalfDimensions / 2; ++j) {
|
||||
BiasType sum0 = accumulation[static_cast<int>(perspectives[p])][j + 0];
|
||||
BiasType sum1 = accumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
|
||||
sum0 = std::max<int>(0, std::min<int>(127, sum0));
|
||||
sum1 = std::max<int>(0, std::min<int>(127, sum1));
|
||||
output[offset + j] = static_cast<OutputType>(sum0 * sum1 / 128);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(vec_cleanup)
|
||||
vec_cleanup();
|
||||
#endif
|
||||
|
||||
return psqt;
|
||||
|
||||
} // end of function transform()
|
||||
|
||||
|
||||
|
||||
private:
|
||||
void update_accumulator(const Position& pos, const Color perspective) const {
|
||||
|
||||
// The size must be enough to contain the largest possible update.
|
||||
// That might depend on the feature set and generally relies on the
|
||||
// feature set's update cost calculation to be correct and never
|
||||
// allow updates with more added/removed features than MaxActiveDimensions.
|
||||
|
||||
#ifdef VECTOR
|
||||
// Gcc-10.2 unnecessarily spills AVX2 registers if this array
|
||||
// is defined in the VECTOR code below, once in each branch
|
||||
vec_t acc[NumRegs];
|
||||
psqt_vec_t psqt[NumPsqtRegs];
|
||||
#endif
|
||||
|
||||
// Look for a usable accumulator of an earlier position. We keep track
|
||||
// of the estimated gain in terms of features to be added/subtracted.
|
||||
StateInfo *st = pos.state(), *next = nullptr;
|
||||
int gain = FeatureSet::refresh_cost(pos);
|
||||
while (st->previous && !st->accumulator.computed[perspective])
|
||||
{
|
||||
// This governs when a full feature refresh is needed and how many
|
||||
// updates are better than just one full refresh.
|
||||
if ( FeatureSet::requires_refresh(st, perspective)
|
||||
|| (gain -= FeatureSet::update_cost(st) + 1) < 0)
|
||||
break;
|
||||
next = st;
|
||||
st = st->previous;
|
||||
}
|
||||
|
||||
if (st->accumulator.computed[perspective])
|
||||
{
|
||||
if (next == nullptr)
|
||||
return;
|
||||
|
||||
// Update incrementally in two steps. First, we update the "next"
|
||||
// accumulator. Then, we update the current accumulator (pos.state()).
|
||||
|
||||
// Gather all features to be updated.
|
||||
const Square ksq = pos.square<KING>(perspective);
|
||||
FeatureSet::IndexList removed[2], added[2];
|
||||
FeatureSet::append_changed_indices(
|
||||
ksq, next->dirtyPiece, perspective, removed[0], added[0]);
|
||||
for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous)
|
||||
FeatureSet::append_changed_indices(
|
||||
ksq, st2->dirtyPiece, perspective, removed[1], added[1]);
|
||||
|
||||
// Mark the accumulators as computed.
|
||||
next->accumulator.computed[perspective] = true;
|
||||
pos.state()->accumulator.computed[perspective] = true;
|
||||
|
||||
// Now update the accumulators listed in states_to_update[], where the last element is a sentinel.
|
||||
StateInfo *states_to_update[3] =
|
||||
{ next, next == pos.state() ? nullptr : pos.state(), nullptr };
|
||||
#ifdef VECTOR
|
||||
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
|
||||
{
|
||||
// Load accumulator
|
||||
auto accTile = reinterpret_cast<vec_t*>(
|
||||
&st->accumulator.accumulation[perspective][j * TileHeight]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
acc[k] = vec_load(&accTile[k]);
|
||||
|
||||
for (IndexType i = 0; states_to_update[i]; ++i)
|
||||
{
|
||||
// Difference calculation for the deactivated features
|
||||
for (const auto index : removed[i])
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
||||
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
acc[k] = vec_sub_16(acc[k], column[k]);
|
||||
}
|
||||
|
||||
// Difference calculation for the activated features
|
||||
for (const auto index : added[i])
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
||||
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
acc[k] = vec_add_16(acc[k], column[k]);
|
||||
}
|
||||
|
||||
// Store accumulator
|
||||
accTile = reinterpret_cast<vec_t*>(
|
||||
&states_to_update[i]->accumulator.accumulation[perspective][j * TileHeight]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
vec_store(&accTile[k], acc[k]);
|
||||
}
|
||||
}
|
||||
|
||||
for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
|
||||
{
|
||||
// Load accumulator
|
||||
auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
|
||||
&st->accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_load_psqt(&accTilePsqt[k]);
|
||||
|
||||
for (IndexType i = 0; states_to_update[i]; ++i)
|
||||
{
|
||||
// Difference calculation for the deactivated features
|
||||
for (const auto index : removed[i])
|
||||
{
|
||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]);
|
||||
}
|
||||
|
||||
// Difference calculation for the activated features
|
||||
for (const auto index : added[i])
|
||||
{
|
||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
|
||||
}
|
||||
|
||||
// Store accumulator
|
||||
accTilePsqt = reinterpret_cast<psqt_vec_t*>(
|
||||
&states_to_update[i]->accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
vec_store_psqt(&accTilePsqt[k], psqt[k]);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
for (IndexType i = 0; states_to_update[i]; ++i)
|
||||
{
|
||||
std::memcpy(states_to_update[i]->accumulator.accumulation[perspective],
|
||||
st->accumulator.accumulation[perspective],
|
||||
HalfDimensions * sizeof(BiasType));
|
||||
|
||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||
states_to_update[i]->accumulator.psqtAccumulation[perspective][k] = st->accumulator.psqtAccumulation[perspective][k];
|
||||
|
||||
st = states_to_update[i];
|
||||
|
||||
// Difference calculation for the deactivated features
|
||||
for (const auto index : removed[i])
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index;
|
||||
|
||||
for (IndexType j = 0; j < HalfDimensions; ++j)
|
||||
st->accumulator.accumulation[perspective][j] -= weights[offset + j];
|
||||
|
||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||
st->accumulator.psqtAccumulation[perspective][k] -= psqtWeights[index * PSQTBuckets + k];
|
||||
}
|
||||
|
||||
// Difference calculation for the activated features
|
||||
for (const auto index : added[i])
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index;
|
||||
|
||||
for (IndexType j = 0; j < HalfDimensions; ++j)
|
||||
st->accumulator.accumulation[perspective][j] += weights[offset + j];
|
||||
|
||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||
st->accumulator.psqtAccumulation[perspective][k] += psqtWeights[index * PSQTBuckets + k];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// Refresh the accumulator
|
||||
auto& accumulator = pos.state()->accumulator;
|
||||
accumulator.computed[perspective] = true;
|
||||
FeatureSet::IndexList active;
|
||||
FeatureSet::append_active_indices(pos, perspective, active);
|
||||
|
||||
#ifdef VECTOR
|
||||
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
|
||||
{
|
||||
auto biasesTile = reinterpret_cast<const vec_t*>(
|
||||
&biases[j * TileHeight]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
acc[k] = biasesTile[k];
|
||||
|
||||
for (const auto index : active)
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
||||
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
||||
|
||||
for (unsigned k = 0; k < NumRegs; ++k)
|
||||
acc[k] = vec_add_16(acc[k], column[k]);
|
||||
}
|
||||
|
||||
auto accTile = reinterpret_cast<vec_t*>(
|
||||
&accumulator.accumulation[perspective][j * TileHeight]);
|
||||
for (unsigned k = 0; k < NumRegs; k++)
|
||||
vec_store(&accTile[k], acc[k]);
|
||||
}
|
||||
|
||||
for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
|
||||
{
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_zero_psqt();
|
||||
|
||||
for (const auto index : active)
|
||||
{
|
||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
||||
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
|
||||
}
|
||||
|
||||
auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
|
||||
&accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
vec_store_psqt(&accTilePsqt[k], psqt[k]);
|
||||
}
|
||||
|
||||
#else
|
||||
std::memcpy(accumulator.accumulation[perspective], biases,
|
||||
HalfDimensions * sizeof(BiasType));
|
||||
|
||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||
accumulator.psqtAccumulation[perspective][k] = 0;
|
||||
|
||||
for (const auto index : active)
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index;
|
||||
|
||||
for (IndexType j = 0; j < HalfDimensions; ++j)
|
||||
accumulator.accumulation[perspective][j] += weights[offset + j];
|
||||
|
||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||
accumulator.psqtAccumulation[perspective][k] += psqtWeights[index * PSQTBuckets + k];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(USE_MMX)
|
||||
_mm_empty();
|
||||
#endif
|
||||
}
|
||||
|
||||
alignas(CacheLineSize) BiasType biases[HalfDimensions];
|
||||
alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions];
|
||||
alignas(CacheLineSize) PSQTWeightType psqtWeights[InputDimensions * PSQTBuckets];
|
||||
};
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
|
||||
#endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED
|
||||
+305
-242
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,290 +17,352 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "bitboard.h"
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include "bitcount.h"
|
||||
#include "pawns.h"
|
||||
#include "position.h"
|
||||
#include "thread.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
#define V Value
|
||||
/// Constants and variables
|
||||
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
// Pawn penalties
|
||||
constexpr Score Backward = S( 6, 19);
|
||||
constexpr Score Doubled = S(11, 51);
|
||||
constexpr Score DoubledEarly = S(17, 7);
|
||||
constexpr Score Isolated = S( 1, 20);
|
||||
constexpr Score WeakLever = S( 2, 57);
|
||||
constexpr Score WeakUnopposed = S(15, 18);
|
||||
|
||||
// Bonus for blocked pawns at 5th or 6th rank
|
||||
constexpr Score BlockedPawn[2] = { S(-19, -8), S(-7, 3) };
|
||||
|
||||
constexpr Score BlockedStorm[RANK_NB] = {
|
||||
S(0, 0), S(0, 0), S(64, 75), S(-3, 14), S(-12, 19), S(-7, 4), S(-10, 5)
|
||||
// Doubled pawn penalty by file
|
||||
const Score DoubledPawnPenalty[8] = {
|
||||
S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
||||
S(23, 48), S(23, 48), S(20, 48), S(13, 43)
|
||||
};
|
||||
|
||||
// Connected pawn bonus
|
||||
constexpr int Connected[RANK_NB] = { 0, 3, 7, 7, 15, 54, 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(-2), V(85), V(95), V(53), V(39), V(23), V(25) },
|
||||
{ V(-55), V(64), V(32), V(-55), V(-30), V(-11), V(-61) },
|
||||
{ V(-11), V(75), V(19), V(-6), V(26), V(9), V(-47) },
|
||||
{ V(-41), V(-11), V(-27), V(-58), V(-42), V(-66), V(-163) }
|
||||
// Isolated pawn penalty by file
|
||||
const Score IsolatedPawnPenalty[8] = {
|
||||
S(25, 30), S(36, 35), S(40, 35), S(40, 35),
|
||||
S(40, 35), S(40, 35), S(36, 35), S(25, 30)
|
||||
};
|
||||
|
||||
// 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(94), V(-280), V(-170), V(90), V(59), V(47), V(53) },
|
||||
{ V(43), V(-17), V(128), V(39), V(26), V(-17), V(15) },
|
||||
{ V(-9), V(62), V(170), V(34), V(-5), V(-20), V(-11) },
|
||||
{ V(-27), V(-19), V(106), V(10), V(2), V(-13), V(-24) }
|
||||
// Backward pawn penalty by file
|
||||
const Score BackwardPawnPenalty[8] = {
|
||||
S(20, 28), S(29, 31), S(33, 31), S(33, 31),
|
||||
S(33, 31), S(33, 31), S(29, 31), S(20, 28)
|
||||
};
|
||||
|
||||
// Pawn chain membership bonus by file
|
||||
const Score ChainBonus[8] = {
|
||||
S(11,-1), S(13,-1), S(13,-1), S(14,-1),
|
||||
S(14,-1), S(13,-1), S(13,-1), S(11,-1)
|
||||
};
|
||||
|
||||
// KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties
|
||||
// for king when the king is on a semi-open or open file.
|
||||
constexpr Score KingOnFile[2][2] = {{ S(-18,11), S(-6,-3) },
|
||||
{ S( 0, 0), S( 5,-4) }};
|
||||
// Candidate passed pawn bonus by rank
|
||||
const Score CandidateBonus[8] = {
|
||||
S( 0, 0), S( 6, 13), S(6,13), S(14,29),
|
||||
S(34,68), S(83,166), S(0, 0), S( 0, 0)
|
||||
};
|
||||
|
||||
#undef S
|
||||
#undef V
|
||||
// Pawn storm tables for positions with opposite castling
|
||||
const int QStormTable[64] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
-22,-22,-22,-14,-6, 0, 0, 0,
|
||||
-6,-10,-10,-10,-6, 0, 0, 0,
|
||||
4, 12, 16, 12, 4, 0, 0, 0,
|
||||
16, 23, 23, 16, 0, 0, 0, 0,
|
||||
23, 31, 31, 23, 0, 0, 0, 0,
|
||||
23, 31, 31, 23, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
const int KStormTable[64] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0,-10,-19,-28,-33,-33,
|
||||
0, 0, 0,-10,-15,-19,-24,-24,
|
||||
0, 0, 0, 0, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 1, 10, 19, 19,
|
||||
0, 0, 0, 0, 1, 19, 31, 27,
|
||||
0, 0, 0, 0, 0, 22, 31, 22,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
/// evaluate() calculates a score for the static pawn structure of the given position.
|
||||
/// We cannot use the location of pieces or king in this function, as the evaluation
|
||||
/// of the pawn structure will be stored in a small cache for speed reasons, and will
|
||||
/// be re-used even when the pieces have moved.
|
||||
// Pawn storm open file bonuses by file
|
||||
const int16_t KStormOpenFileBonus[8] = { 31, 31, 18, 0, 0, 0, 0, 0 };
|
||||
const int16_t QStormOpenFileBonus[8] = { 0, 0, 0, 0, 0, 26, 42, 26 };
|
||||
|
||||
template<Color Us>
|
||||
Score evaluate(const Position& pos, Pawns::Entry* e) {
|
||||
// Pawn storm lever bonuses by file
|
||||
const int StormLeverBonus[8] = { -8, -8, -13, 0, 0, -13, -8, -8 };
|
||||
|
||||
constexpr Color Them = ~Us;
|
||||
constexpr Direction Up = pawn_push(Us);
|
||||
constexpr Direction Down = -Up;
|
||||
|
||||
Bitboard neighbours, stoppers, support, phalanx, opposed;
|
||||
Bitboard lever, leverPush, blocked;
|
||||
Square s;
|
||||
bool backward, passed, doubled;
|
||||
Score score = SCORE_ZERO;
|
||||
Bitboard b = pos.pieces(Us, PAWN);
|
||||
|
||||
Bitboard ourPawns = pos.pieces( Us, PAWN);
|
||||
Bitboard theirPawns = pos.pieces(Them, PAWN);
|
||||
|
||||
Bitboard doubleAttackThem = pawn_double_attacks_bb<Them>(theirPawns);
|
||||
|
||||
e->passedPawns[Us] = 0;
|
||||
e->kingSquares[Us] = SQ_NONE;
|
||||
e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb<Us>(ourPawns);
|
||||
e->blockedCount += popcount(shift<Up>(ourPawns) & (theirPawns | doubleAttackThem));
|
||||
|
||||
// Loop through all pawns of the current color and score each pawn
|
||||
while (b)
|
||||
{
|
||||
s = pop_lsb(b);
|
||||
|
||||
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
||||
|
||||
Rank r = relative_rank(Us, s);
|
||||
|
||||
// Flag the pawn
|
||||
opposed = theirPawns & forward_file_bb(Us, s);
|
||||
blocked = theirPawns & (s + Up);
|
||||
stoppers = theirPawns & passed_pawn_span(Us, s);
|
||||
lever = theirPawns & pawn_attacks_bb(Us, s);
|
||||
leverPush = theirPawns & pawn_attacks_bb(Us, s + Up);
|
||||
doubled = ourPawns & (s - Up);
|
||||
neighbours = ourPawns & adjacent_files_bb(s);
|
||||
phalanx = neighbours & rank_bb(s);
|
||||
support = neighbours & rank_bb(s - Up);
|
||||
|
||||
if (doubled)
|
||||
{
|
||||
// Additional doubled penalty if none of their pawns is fixed
|
||||
if (!(ourPawns & shift<Down>(theirPawns | pawn_attacks_bb<Them>(theirPawns))))
|
||||
score -= DoubledEarly;
|
||||
}
|
||||
|
||||
// A pawn is backward when it is behind all pawns of the same color on
|
||||
// the adjacent files and cannot safely advance.
|
||||
backward = !(neighbours & forward_ranks_bb(Them, s + Up))
|
||||
&& (leverPush | blocked);
|
||||
|
||||
// Compute additional span if pawn is not backward nor blocked
|
||||
if (!backward && !blocked)
|
||||
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
|
||||
|
||||
// A pawn is passed if one of the three following conditions is true:
|
||||
// (a) there is no stoppers except some levers
|
||||
// (b) the only stoppers are the leverPush, but we outnumber them
|
||||
// (c) there is only one front stopper which can be levered.
|
||||
// (Refined in Evaluation::passed)
|
||||
passed = !(stoppers ^ lever)
|
||||
|| ( !(stoppers ^ leverPush)
|
||||
&& popcount(phalanx) >= popcount(leverPush))
|
||||
|| ( stoppers == blocked && r >= RANK_5
|
||||
&& (shift<Up>(support) & ~(theirPawns | doubleAttackThem)));
|
||||
|
||||
passed &= !(forward_file_bb(Us, s) & ourPawns);
|
||||
|
||||
// Passed pawns will be properly scored later in evaluation when we have
|
||||
// full attack info.
|
||||
if (passed)
|
||||
e->passedPawns[Us] |= s;
|
||||
|
||||
// Score this pawn
|
||||
if (support | phalanx)
|
||||
{
|
||||
int v = Connected[r] * (2 + bool(phalanx) - bool(opposed))
|
||||
+ 22 * popcount(support);
|
||||
|
||||
score += make_score(v, v * (r - 2) / 4);
|
||||
}
|
||||
|
||||
else if (!neighbours)
|
||||
{
|
||||
if ( opposed
|
||||
&& (ourPawns & forward_file_bb(Them, s))
|
||||
&& !(theirPawns & adjacent_files_bb(s)))
|
||||
score -= Doubled;
|
||||
else
|
||||
score -= Isolated
|
||||
+ WeakUnopposed * !opposed;
|
||||
}
|
||||
|
||||
else if (backward)
|
||||
score -= Backward
|
||||
+ WeakUnopposed * !opposed * bool(~(FileABB | FileHBB) & s);
|
||||
|
||||
if (!support)
|
||||
score -= Doubled * doubled
|
||||
+ WeakLever * more_than_one(lever);
|
||||
|
||||
if (blocked && r >= RANK_5)
|
||||
score += BlockedPawn[r - RANK_5];
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Pawns {
|
||||
|
||||
|
||||
/// Pawns::probe() looks up the current position's pawns configuration in
|
||||
/// the pawns hash table. It returns a pointer to the Entry if the position
|
||||
/// is found. Otherwise a new Entry is computed and stored there, so we don't
|
||||
/// have to recompute all when the same pawns configuration occurs again.
|
||||
|
||||
Entry* probe(const Position& pos) {
|
||||
|
||||
Key key = pos.pawn_key();
|
||||
Entry* e = pos.this_thread()->pawnsTable[key];
|
||||
|
||||
if (e->key == key)
|
||||
return e;
|
||||
|
||||
e->key = key;
|
||||
e->blockedCount = 0;
|
||||
e->scores[WHITE] = evaluate<WHITE>(pos, e);
|
||||
e->scores[BLACK] = evaluate<BLACK>(pos, e);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
/// Entry::evaluate_shelter() calculates the shelter bonus and the storm
|
||||
/// penalty for a king, looking at the king file and the two closest files.
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
template<Color Us>
|
||||
Score Entry::evaluate_shelter(const Position& pos, Square ksq) const {
|
||||
/// Constructor
|
||||
|
||||
constexpr Color Them = ~Us;
|
||||
PawnInfoTable::PawnInfoTable(unsigned numOfEntries) {
|
||||
|
||||
Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
|
||||
Bitboard ourPawns = b & pos.pieces(Us) & ~pawnAttacks[Them];
|
||||
Bitboard theirPawns = b & pos.pieces(Them);
|
||||
|
||||
Score bonus = make_score(5, 5);
|
||||
|
||||
File center = std::clamp(file_of(ksq), FILE_B, FILE_G);
|
||||
for (File f = File(center - 1); f <= File(center + 1); ++f)
|
||||
size = numOfEntries;
|
||||
entries = new PawnInfo[size];
|
||||
if (!entries)
|
||||
{
|
||||
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 = edge_distance(f);
|
||||
bonus += make_score(ShelterStrength[d][ourRank], 0);
|
||||
|
||||
if (ourRank && (ourRank == theirRank - 1))
|
||||
bonus -= BlockedStorm[theirRank];
|
||||
else
|
||||
bonus -= make_score(UnblockedStorm[d][theirRank], 0);
|
||||
std::cerr << "Failed to allocate " << (numOfEntries * sizeof(PawnInfo))
|
||||
<< " bytes for pawn hash table." << std::endl;
|
||||
Application::exit_with_failure();
|
||||
}
|
||||
|
||||
// King On File
|
||||
bonus -= KingOnFile[pos.is_on_semiopen_file(Us, ksq)][pos.is_on_semiopen_file(Them, ksq)];
|
||||
|
||||
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.
|
||||
/// Destructor
|
||||
|
||||
PawnInfoTable::~PawnInfoTable() {
|
||||
delete [] entries;
|
||||
}
|
||||
|
||||
|
||||
/// PawnInfo::clear() resets to zero the PawnInfo entry. Note that
|
||||
/// kingSquares[] is initialized to SQ_NONE instead.
|
||||
|
||||
void PawnInfo::clear() {
|
||||
|
||||
memset(this, 0, sizeof(PawnInfo));
|
||||
kingSquares[WHITE] = kingSquares[BLACK] = SQ_NONE;
|
||||
}
|
||||
|
||||
|
||||
/// PawnInfoTable::get_pawn_info() takes a position object as input, computes
|
||||
/// a PawnInfo object, and returns a pointer to it. The result is also
|
||||
/// stored in a hash table, so we don't have to recompute everything when
|
||||
/// the same pawn structure occurs again.
|
||||
|
||||
PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
|
||||
Key key = pos.get_pawn_key();
|
||||
int index = int(key & (size - 1));
|
||||
PawnInfo* pi = entries + index;
|
||||
|
||||
// If pi->key matches the position's pawn hash key, it means that we
|
||||
// have analysed this pawn structure before, and we can simply return
|
||||
// the information we found the last time instead of recomputing it.
|
||||
if (pi->key == key)
|
||||
return pi;
|
||||
|
||||
// Clear the PawnInfo object, and set the key
|
||||
pi->clear();
|
||||
pi->key = key;
|
||||
|
||||
// Calculate pawn attacks
|
||||
Bitboard whitePawns = pos.pieces(PAWN, WHITE);
|
||||
Bitboard blackPawns = pos.pieces(PAWN, BLACK);
|
||||
pi->pawnAttacks[WHITE] = ((whitePawns << 9) & ~FileABB) | ((whitePawns << 7) & ~FileHBB);
|
||||
pi->pawnAttacks[BLACK] = ((blackPawns >> 7) & ~FileABB) | ((blackPawns >> 9) & ~FileHBB);
|
||||
|
||||
// Evaluate pawns for both colors
|
||||
pi->value = evaluate_pawns<WHITE>(pos, whitePawns, blackPawns, pi)
|
||||
- evaluate_pawns<BLACK>(pos, blackPawns, whitePawns, pi);
|
||||
return pi;
|
||||
}
|
||||
|
||||
|
||||
/// PawnInfoTable::evaluate_pawns() evaluates each pawn of the given color
|
||||
|
||||
template<Color Us>
|
||||
Score Entry::do_king_safety(const Position& pos) {
|
||||
Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
|
||||
Bitboard theirPawns, PawnInfo* pi) {
|
||||
Square s;
|
||||
File f;
|
||||
Rank r;
|
||||
bool passed, isolated, doubled, chain, backward, candidate;
|
||||
int bonus;
|
||||
Score value = make_score(0, 0);
|
||||
const Square* ptr = pos.piece_list_begin(Us, PAWN);
|
||||
|
||||
Square ksq = pos.square<KING>(Us);
|
||||
kingSquares[Us] = ksq;
|
||||
castlingRights[Us] = pos.castling_rights(Us);
|
||||
auto compare = [](Score a, Score b) { return mg_value(a) < mg_value(b); };
|
||||
// Initialize pawn storm scores by giving bonuses for open files
|
||||
for (f = FILE_A; f <= FILE_H; f++)
|
||||
if (!(ourPawns & file_bb(f)))
|
||||
{
|
||||
pi->ksStormValue[Us] += KStormOpenFileBonus[f];
|
||||
pi->qsStormValue[Us] += QStormOpenFileBonus[f];
|
||||
pi->halfOpenFiles[Us] |= (1 << f);
|
||||
}
|
||||
|
||||
Score shelter = evaluate_shelter<Us>(pos, ksq);
|
||||
// Loop through all pawns of the current color and score each pawn
|
||||
while ((s = *ptr++) != SQ_NONE)
|
||||
{
|
||||
f = square_file(s);
|
||||
r = square_rank(s);
|
||||
|
||||
// If we can castle use the bonus after castling if it is bigger
|
||||
assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN));
|
||||
|
||||
if (pos.can_castle(Us & KING_SIDE))
|
||||
shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)), compare);
|
||||
// Passed, isolated or doubled pawn?
|
||||
passed = Position::pawn_is_passed(theirPawns, Us, s);
|
||||
isolated = Position::pawn_is_isolated(ourPawns, s);
|
||||
doubled = Position::pawn_is_doubled(ourPawns, Us, s);
|
||||
|
||||
if (pos.can_castle(Us & QUEEN_SIDE))
|
||||
shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)), compare);
|
||||
// We calculate kingside and queenside pawn storm
|
||||
// scores for both colors. These are used when evaluating
|
||||
// middle game positions with opposite side castling.
|
||||
//
|
||||
// Each pawn is given a base score given by a piece square table
|
||||
// (KStormTable[] or QStormTable[]). Pawns which seem to have good
|
||||
// chances of creating an open file by exchanging itself against an
|
||||
// enemy pawn on an adjacent file gets an additional bonus.
|
||||
|
||||
// In endgame we like to bring our king near our closest pawn
|
||||
Bitboard pawns = pos.pieces(Us, PAWN);
|
||||
int minPawnDist = 6;
|
||||
// Kingside pawn storms
|
||||
bonus = KStormTable[relative_square(Us, s)];
|
||||
if (f >= FILE_F)
|
||||
{
|
||||
Bitboard b = outpost_mask(Us, s) & theirPawns & (FileFBB | FileGBB | FileHBB);
|
||||
while (b)
|
||||
{
|
||||
Square s2 = pop_1st_bit(&b);
|
||||
if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2)))
|
||||
{
|
||||
// The enemy pawn has no pawn beside itself, which makes it
|
||||
// particularly vulnerable. Big bonus, especially against a
|
||||
// weakness on the rook file.
|
||||
if (square_file(s2) == FILE_H)
|
||||
bonus += 4*StormLeverBonus[f] - 8*square_distance(s, s2);
|
||||
else
|
||||
bonus += 2*StormLeverBonus[f] - 4*square_distance(s, s2);
|
||||
} else
|
||||
// There is at least one enemy pawn beside the enemy pawn we look
|
||||
// at, which means that the pawn has somewhat better chances of
|
||||
// defending itself by advancing. Smaller bonus.
|
||||
bonus += StormLeverBonus[f] - 2*square_distance(s, s2);
|
||||
}
|
||||
}
|
||||
pi->ksStormValue[Us] += bonus;
|
||||
|
||||
if (pawns & attacks_bb<KING>(ksq))
|
||||
minPawnDist = 1;
|
||||
else while (pawns)
|
||||
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(pawns)));
|
||||
// Queenside pawn storms
|
||||
bonus = QStormTable[relative_square(Us, s)];
|
||||
if (f <= FILE_C)
|
||||
{
|
||||
Bitboard b = outpost_mask(Us, s) & theirPawns & (FileABB | FileBBB | FileCBB);
|
||||
while (b)
|
||||
{
|
||||
Square s2 = pop_1st_bit(&b);
|
||||
if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2)))
|
||||
{
|
||||
// The enemy pawn has no pawn beside itself, which makes it
|
||||
// particularly vulnerable. Big bonus, especially against a
|
||||
// weakness on the rook file.
|
||||
if (square_file(s2) == FILE_A)
|
||||
bonus += 4*StormLeverBonus[f] - 16*square_distance(s, s2);
|
||||
else
|
||||
bonus += 2*StormLeverBonus[f] - 8*square_distance(s, s2);
|
||||
} else
|
||||
// There is at least one enemy pawn beside the enemy pawn we look
|
||||
// at, which means that the pawn has somewhat better chances of
|
||||
// defending itself by advancing. Smaller bonus.
|
||||
bonus += StormLeverBonus[f] - 4*square_distance(s, s2);
|
||||
}
|
||||
}
|
||||
pi->qsStormValue[Us] += bonus;
|
||||
|
||||
return shelter - make_score(0, 16 * minPawnDist);
|
||||
// Member of a pawn chain (but not the backward one)? We could speed up
|
||||
// the test a little by introducing an array of masks indexed by color
|
||||
// and square for doing the test, but because everything is hashed,
|
||||
// it probably won't make any noticable difference.
|
||||
chain = ourPawns
|
||||
& neighboring_files_bb(f)
|
||||
& (rank_bb(r) | rank_bb(r - (Us == WHITE ? 1 : -1)));
|
||||
|
||||
// Test for backward pawn
|
||||
//
|
||||
// If the pawn is passed, isolated, or member of a pawn chain
|
||||
// it cannot be backward. If can capture an enemy pawn or if
|
||||
// there are friendly pawns behind on neighboring files it cannot
|
||||
// be backward either.
|
||||
if ( (passed | isolated | chain)
|
||||
|| (ourPawns & behind_bb(Us, r) & neighboring_files_bb(f))
|
||||
|| (pos.attacks_from<PAWN>(s, Us) & theirPawns))
|
||||
backward = false;
|
||||
else
|
||||
{
|
||||
// We now know that there are no friendly pawns beside or behind this
|
||||
// pawn on neighboring files. We now check whether the pawn is
|
||||
// backward by looking in the forward direction on the neighboring
|
||||
// files, and seeing whether we meet a friendly or an enemy pawn first.
|
||||
Bitboard b = pos.attacks_from<PAWN>(s, Us);
|
||||
|
||||
// Note that we are sure to find something because pawn is not passed
|
||||
// nor isolated, so loop is potentially infinite, but it isn't.
|
||||
while (!(b & (ourPawns | theirPawns)))
|
||||
Us == WHITE ? b <<= 8 : b >>= 8;
|
||||
|
||||
// The friendly pawn needs to be at least two ranks closer than the enemy
|
||||
// pawn in order to help the potentially backward pawn advance.
|
||||
backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns;
|
||||
}
|
||||
|
||||
// Test for candidate passed pawn
|
||||
candidate = !passed
|
||||
&& !(theirPawns & file_bb(f))
|
||||
&& ( count_1s_max_15(neighboring_files_bb(f) & (behind_bb(Us, r) | rank_bb(r)) & ourPawns)
|
||||
- count_1s_max_15(neighboring_files_bb(f) & in_front_bb(Us, r) & theirPawns)
|
||||
>= 0);
|
||||
|
||||
// In order to prevent doubled passed pawns from receiving a too big
|
||||
// bonus, only the frontmost passed pawn on each file is considered as
|
||||
// a true passed pawn.
|
||||
if (passed && (ourPawns & squares_in_front_of(Us, s)))
|
||||
passed = false;
|
||||
|
||||
// Score this pawn
|
||||
if (passed)
|
||||
set_bit(&(pi->passedPawns), s);
|
||||
|
||||
if (isolated)
|
||||
{
|
||||
value -= IsolatedPawnPenalty[f];
|
||||
if (!(theirPawns & file_bb(f)))
|
||||
value -= IsolatedPawnPenalty[f] / 2;
|
||||
}
|
||||
if (doubled)
|
||||
value -= DoubledPawnPenalty[f];
|
||||
|
||||
if (backward)
|
||||
{
|
||||
value -= BackwardPawnPenalty[f];
|
||||
if (!(theirPawns & file_bb(f)))
|
||||
value -= BackwardPawnPenalty[f] / 2;
|
||||
}
|
||||
if (chain)
|
||||
value += ChainBonus[f];
|
||||
|
||||
if (candidate)
|
||||
value += CandidateBonus[relative_rank(Us, s)];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Explicit template instantiation
|
||||
template Score Entry::do_king_safety<WHITE>(const Position& pos);
|
||||
template Score Entry::do_king_safety<BLACK>(const Position& pos);
|
||||
|
||||
} // namespace Pawns
|
||||
/// PawnInfo::updateShelter calculates and caches king shelter. It is called
|
||||
/// only when king square changes, about 20% of total get_king_shelter() calls.
|
||||
int PawnInfo::updateShelter(const Position& pos, Color c, Square ksq) {
|
||||
|
||||
} // namespace Stockfish
|
||||
unsigned shelter = 0;
|
||||
Bitboard pawns = pos.pieces(PAWN, c) & this_and_neighboring_files_bb(ksq);
|
||||
unsigned r = ksq & (7 << 3);
|
||||
for (int i = 1, k = (c ? -8 : 8); i < 4; i++)
|
||||
{
|
||||
r += k;
|
||||
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (128 >> i);
|
||||
}
|
||||
kingSquares[c] = ksq;
|
||||
kingShelters[c] = shelter;
|
||||
return shelter;
|
||||
}
|
||||
|
||||
+102
-37
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,55 +17,119 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PAWNS_H_INCLUDED
|
||||
|
||||
#if !defined(PAWNS_H_INCLUDED)
|
||||
#define PAWNS_H_INCLUDED
|
||||
|
||||
#include "misc.h"
|
||||
#include "position.h"
|
||||
#include "types.h"
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
namespace Stockfish::Pawns {
|
||||
#include "bitboard.h"
|
||||
#include "value.h"
|
||||
|
||||
/// 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.
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
struct Entry {
|
||||
/// PawnInfo is a class which contains various information about a pawn
|
||||
/// structure. Currently, it only includes a middle game and an end game
|
||||
/// pawn structure evaluation, and a bitboard of passed pawns. We may want
|
||||
/// to add further information in the future. A lookup to the pawn hash table
|
||||
/// (performed by calling the get_pawn_info method in a PawnInfoTable object)
|
||||
/// returns a pointer to a PawnInfo object.
|
||||
class Position;
|
||||
|
||||
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]); }
|
||||
int blocked_count() const { return blockedCount; }
|
||||
class PawnInfo {
|
||||
|
||||
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));
|
||||
}
|
||||
friend class PawnInfoTable;
|
||||
|
||||
template<Color Us>
|
||||
Score do_king_safety(const Position& pos);
|
||||
public:
|
||||
PawnInfo() { clear(); }
|
||||
|
||||
template<Color Us>
|
||||
Score evaluate_shelter(const Position& pos, Square ksq) const;
|
||||
Score pawns_value() const;
|
||||
Value kingside_storm_value(Color c) const;
|
||||
Value queenside_storm_value(Color c) const;
|
||||
Bitboard pawn_attacks(Color c) const;
|
||||
Bitboard passed_pawns() const;
|
||||
int file_is_half_open(Color c, File f) const;
|
||||
int has_open_file_to_left(Color c, File f) const;
|
||||
int has_open_file_to_right(Color c, File f) const;
|
||||
int get_king_shelter(const Position& pos, Color c, Square ksq);
|
||||
|
||||
private:
|
||||
void clear();
|
||||
int updateShelter(const Position& pos, Color c, 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];
|
||||
int blockedCount;
|
||||
Bitboard passedPawns;
|
||||
Bitboard pawnAttacks[2];
|
||||
Score value;
|
||||
int16_t ksStormValue[2], qsStormValue[2];
|
||||
uint8_t halfOpenFiles[2];
|
||||
Square kingSquares[2];
|
||||
uint8_t kingShelters[2];
|
||||
};
|
||||
|
||||
typedef HashTable<Entry, 131072> Table;
|
||||
/// The PawnInfoTable class represents a pawn hash table. It is basically
|
||||
/// just an array of PawnInfo objects and a few methods for accessing these
|
||||
/// objects. The most important method is get_pawn_info, which looks up a
|
||||
/// position in the table and returns a pointer to a PawnInfo object.
|
||||
|
||||
Entry* probe(const Position& pos);
|
||||
class PawnInfoTable {
|
||||
|
||||
} // namespace Stockfish::Pawns
|
||||
public:
|
||||
PawnInfoTable(unsigned numOfEntries);
|
||||
~PawnInfoTable();
|
||||
PawnInfo* get_pawn_info(const Position& pos);
|
||||
|
||||
#endif // #ifndef PAWNS_H_INCLUDED
|
||||
private:
|
||||
template<Color Us>
|
||||
Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi);
|
||||
|
||||
unsigned size;
|
||||
PawnInfo* entries;
|
||||
};
|
||||
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
inline Score PawnInfo::pawns_value() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
inline Bitboard PawnInfo::passed_pawns() const {
|
||||
return passedPawns;
|
||||
}
|
||||
|
||||
inline Bitboard PawnInfo::pawn_attacks(Color c) const {
|
||||
return pawnAttacks[c];
|
||||
}
|
||||
|
||||
inline Value PawnInfo::kingside_storm_value(Color c) const {
|
||||
return Value(ksStormValue[c]);
|
||||
}
|
||||
|
||||
inline Value PawnInfo::queenside_storm_value(Color c) const {
|
||||
return Value(qsStormValue[c]);
|
||||
}
|
||||
|
||||
inline int PawnInfo::file_is_half_open(Color c, File f) const {
|
||||
return (halfOpenFiles[c] & (1 << int(f)));
|
||||
}
|
||||
|
||||
inline int PawnInfo::has_open_file_to_left(Color c, File f) const {
|
||||
return halfOpenFiles[c] & ((1 << int(f)) - 1);
|
||||
}
|
||||
|
||||
inline int PawnInfo::has_open_file_to_right(Color c, File f) const {
|
||||
return halfOpenFiles[c] & ~((1 << int(f+1)) - 1);
|
||||
}
|
||||
|
||||
inline int PawnInfo::get_king_shelter(const Position& pos, Color c, Square ksq) {
|
||||
return (kingSquares[c] == ksq ? kingShelters[c] : updateShelter(pos, c, ksq));
|
||||
}
|
||||
|
||||
#endif // !defined(PAWNS_H_INCLUDED)
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "piece.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
/// Translating piece types to/from English piece letters
|
||||
|
||||
static const string PieceChars(" pnbrqk PNBRQK");
|
||||
|
||||
char piece_type_to_char(PieceType pt, bool upcase) {
|
||||
|
||||
return PieceChars[pt + upcase * 7];
|
||||
}
|
||||
|
||||
PieceType piece_type_from_char(char c) {
|
||||
|
||||
size_t idx = PieceChars.find(c);
|
||||
|
||||
return idx != string::npos ? PieceType(idx % 7) : NO_PIECE_TYPE;
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(PIECE_H_INCLUDED)
|
||||
#define PIECE_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "color.h"
|
||||
#include "square.h"
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
enum PieceType {
|
||||
NO_PIECE_TYPE = 0,
|
||||
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
|
||||
};
|
||||
|
||||
enum Piece {
|
||||
NO_PIECE = 0, WP = 1, WN = 2, WB = 3, WR = 4, WQ = 5, WK = 6,
|
||||
BP = 9, BN = 10, BB = 11, BR = 12, BQ = 13, BK = 14,
|
||||
EMPTY = 16, OUTSIDE = 17
|
||||
};
|
||||
|
||||
|
||||
////
|
||||
//// Constants
|
||||
////
|
||||
|
||||
const int SlidingArray[18] = {
|
||||
0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
inline Piece operator+ (Piece p, int i) { return Piece(int(p) + i); }
|
||||
inline void operator++ (Piece &p, int) { p = Piece(int(p) + 1); }
|
||||
inline Piece operator- (Piece p, int i) { return Piece(int(p) - i); }
|
||||
inline void operator-- (Piece &p, int) { p = Piece(int(p) - 1); }
|
||||
inline PieceType operator+ (PieceType p, int i) {return PieceType(int(p) + i);}
|
||||
inline void operator++ (PieceType &p, int) { p = PieceType(int(p) + 1); }
|
||||
inline PieceType operator- (PieceType p, int i) {return PieceType(int(p) - i);}
|
||||
inline void operator-- (PieceType &p, int) { p = PieceType(int(p) - 1); }
|
||||
|
||||
inline PieceType type_of_piece(Piece p) {
|
||||
return PieceType(int(p) & 7);
|
||||
}
|
||||
|
||||
inline Color color_of_piece(Piece p) {
|
||||
return Color(int(p) >> 3);
|
||||
}
|
||||
|
||||
inline Piece piece_of_color_and_type(Color c, PieceType pt) {
|
||||
return Piece((int(c) << 3) | int(pt));
|
||||
}
|
||||
|
||||
inline int piece_is_slider(Piece p) {
|
||||
return SlidingArray[int(p)];
|
||||
}
|
||||
|
||||
inline SquareDelta pawn_push(Color c) {
|
||||
return (c == WHITE ? DELTA_N : DELTA_S);
|
||||
}
|
||||
|
||||
inline bool piece_type_is_ok(PieceType pc) {
|
||||
return pc >= PAWN && pc <= KING;
|
||||
}
|
||||
|
||||
inline bool piece_is_ok(Piece pc) {
|
||||
return piece_type_is_ok(type_of_piece(pc)) && color_is_ok(color_of_piece(pc));
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern char piece_type_to_char(PieceType pt, bool upcase = false);
|
||||
extern PieceType piece_type_from_char(char c);
|
||||
|
||||
|
||||
#endif // !defined(PIECE_H_INCLUDED)
|
||||
+1780
-1105
File diff suppressed because it is too large
Load Diff
+421
-281
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,415 +17,554 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef POSITION_H_INCLUDED
|
||||
|
||||
#if !defined(POSITION_H_INCLUDED)
|
||||
#define POSITION_H_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <deque>
|
||||
#include <memory> // For std::unique_ptr
|
||||
#include <string>
|
||||
// Disable some silly and noisy warning from MSVC compiler
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
// Forcing value to bool 'true' or 'false' (performance warning)
|
||||
#pragma warning(disable: 4800)
|
||||
|
||||
// Conditional expression is constant
|
||||
#pragma warning(disable: 4127)
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "evaluate.h"
|
||||
#include "psqt.h"
|
||||
#include "types.h"
|
||||
#include "color.h"
|
||||
#include "direction.h"
|
||||
#include "move.h"
|
||||
#include "piece.h"
|
||||
#include "square.h"
|
||||
#include "value.h"
|
||||
|
||||
#include "nnue/nnue_accumulator.h"
|
||||
|
||||
namespace Stockfish {
|
||||
////
|
||||
//// Constants
|
||||
////
|
||||
|
||||
/// 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.
|
||||
/// FEN string for the initial position
|
||||
const std::string StartPosition = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
/// Maximum number of plies per game (220 should be enough, because the
|
||||
/// maximum search depth is 100, and during position setup we reset the
|
||||
/// move counter for every non-reversible move).
|
||||
const int MaxGameLength = 220;
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
/// struct checkInfo is initialized at c'tor time and keeps
|
||||
/// info used to detect if a move gives check.
|
||||
|
||||
struct CheckInfo {
|
||||
|
||||
CheckInfo(const Position&);
|
||||
|
||||
Square ksq;
|
||||
Bitboard dcCandidates;
|
||||
Bitboard checkSq[8];
|
||||
};
|
||||
|
||||
/// Castle rights, encoded as bit fields
|
||||
|
||||
enum CastleRights {
|
||||
NO_CASTLES = 0,
|
||||
WHITE_OO = 1,
|
||||
BLACK_OO = 2,
|
||||
WHITE_OOO = 4,
|
||||
BLACK_OOO = 8,
|
||||
ALL_CASTLES = 15
|
||||
};
|
||||
|
||||
/// Game phase
|
||||
enum Phase {
|
||||
PHASE_ENDGAME = 0,
|
||||
PHASE_MIDGAME = 128
|
||||
};
|
||||
|
||||
|
||||
/// The StateInfo struct stores information we need 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), an StateInfo object
|
||||
/// must be passed as a parameter.
|
||||
|
||||
struct StateInfo {
|
||||
|
||||
// Copied when making a move
|
||||
Key pawnKey;
|
||||
Key materialKey;
|
||||
Value nonPawnMaterial[COLOR_NB];
|
||||
int castlingRights;
|
||||
int rule50;
|
||||
int pliesFromNull;
|
||||
Key pawnKey, materialKey;
|
||||
int castleRights, rule50, pliesFromNull;
|
||||
Square epSquare;
|
||||
Score value;
|
||||
Value npMaterial[2];
|
||||
|
||||
// Not copied when making a move (will be recomputed anyhow)
|
||||
Key key;
|
||||
Bitboard checkersBB;
|
||||
Key key;
|
||||
PieceType capture;
|
||||
Bitboard checkersBB;
|
||||
StateInfo* previous;
|
||||
Bitboard blockersForKing[COLOR_NB];
|
||||
Bitboard pinners[COLOR_NB];
|
||||
Bitboard checkSquares[PIECE_TYPE_NB];
|
||||
Piece capturedPiece;
|
||||
int repetition;
|
||||
|
||||
// Used by NNUE
|
||||
Eval::NNUE::Accumulator accumulator;
|
||||
DirtyPiece dirtyPiece;
|
||||
};
|
||||
|
||||
|
||||
/// 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;
|
||||
/// The position data structure. A position consists of the following data:
|
||||
///
|
||||
/// * For each piece type, a bitboard representing the squares occupied
|
||||
/// by pieces of that type.
|
||||
/// * For each color, a bitboard representing the squares occupied by
|
||||
/// pieces of that color.
|
||||
/// * A bitboard of all occupied squares.
|
||||
/// * A bitboard of all checking pieces.
|
||||
/// * A 64-entry array of pieces, indexed by the squares of the board.
|
||||
/// * The current side to move.
|
||||
/// * Information about the castling rights for both sides.
|
||||
/// * The initial files of the kings and both pairs of rooks. This is
|
||||
/// used to implement the Chess960 castling rules.
|
||||
/// * The en passant square (which is SQ_NONE if no en passant capture is
|
||||
/// possible).
|
||||
/// * The squares of the kings for both sides.
|
||||
/// * Hash keys for the position itself, the current pawn structure, and
|
||||
/// the current material situation.
|
||||
/// * Hash keys for all previous positions in the game for detecting
|
||||
/// repetition draws.
|
||||
/// * A counter for detecting 50 move rule draws.
|
||||
|
||||
class Position {
|
||||
|
||||
friend class MaterialInfo;
|
||||
friend class EndgameFunctions;
|
||||
|
||||
public:
|
||||
static void init();
|
||||
enum GamePhase {
|
||||
MidGame,
|
||||
EndGame
|
||||
};
|
||||
|
||||
Position() = default;
|
||||
Position(const Position&) = delete;
|
||||
Position& operator=(const Position&) = delete;
|
||||
// Constructors
|
||||
Position() {}
|
||||
Position(const Position& pos);
|
||||
Position(const std::string& fen);
|
||||
|
||||
// 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);
|
||||
std::string fen() const;
|
||||
// Text input/output
|
||||
void from_fen(const std::string& fen);
|
||||
const std::string to_fen() const;
|
||||
void print(Move m = MOVE_NONE) const;
|
||||
|
||||
// Position representation
|
||||
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;
|
||||
// Copying
|
||||
void copy(const Position& pos);
|
||||
void flipped_copy(const Position& pos);
|
||||
|
||||
// The piece on a given square
|
||||
Piece piece_on(Square s) const;
|
||||
PieceType type_of_piece_on(Square s) const;
|
||||
Color color_of_piece_on(Square s) const;
|
||||
bool square_is_empty(Square s) const;
|
||||
bool square_is_occupied(Square s) const;
|
||||
Value midgame_value_of_piece_on(Square s) const;
|
||||
Value endgame_value_of_piece_on(Square s) const;
|
||||
|
||||
// Side to move
|
||||
Color side_to_move() const;
|
||||
|
||||
// Bitboard representation of the position
|
||||
Bitboard empty_squares() const;
|
||||
Bitboard occupied_squares() const;
|
||||
Bitboard pieces_of_color(Color c) const;
|
||||
Bitboard pieces(PieceType pt) const;
|
||||
Bitboard pieces(PieceType pt, Color c) const;
|
||||
Bitboard pieces(PieceType pt1, PieceType pt2) const;
|
||||
Bitboard pieces(PieceType pt1, PieceType pt2, Color c) const;
|
||||
|
||||
// Number of pieces of each color and type
|
||||
int piece_count(Color c, PieceType pt) const;
|
||||
|
||||
// The en passant square
|
||||
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> Square square(Color c) const;
|
||||
bool is_on_semiopen_file(Color c, Square s) const;
|
||||
|
||||
// Castling
|
||||
CastlingRights castling_rights(Color c) const;
|
||||
bool can_castle(CastlingRights cr) const;
|
||||
bool castling_impeded(CastlingRights cr) const;
|
||||
Square castling_rook_square(CastlingRights cr) const;
|
||||
// Current king position for each color
|
||||
Square king_square(Color c) const;
|
||||
|
||||
// Checking
|
||||
// Castling rights
|
||||
bool can_castle_kingside(Color c) const;
|
||||
bool can_castle_queenside(Color c) const;
|
||||
bool can_castle(Color c) const;
|
||||
Square initial_kr_square(Color c) const;
|
||||
Square initial_qr_square(Color c) const;
|
||||
|
||||
// Bitboards for pinned pieces and discovered check candidates
|
||||
Bitboard discovered_check_candidates(Color c) const;
|
||||
Bitboard pinned_pieces(Color c) const;
|
||||
|
||||
// Checking pieces and under check information
|
||||
Bitboard checkers() const;
|
||||
Bitboard blockers_for_king(Color c) const;
|
||||
Bitboard check_squares(PieceType pt) const;
|
||||
Bitboard pinners(Color c) const;
|
||||
bool is_check() const;
|
||||
|
||||
// Attacks to/from a given square
|
||||
// Piece lists
|
||||
Square piece_list(Color c, PieceType pt, int index) const;
|
||||
const Square* piece_list_begin(Color c, PieceType pt) const;
|
||||
|
||||
// Information about attacks to or from a given square
|
||||
Bitboard attackers_to(Square s) const;
|
||||
Bitboard attackers_to(Square s, Bitboard occupied) const;
|
||||
Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const;
|
||||
template<PieceType Pt> Bitboard attacks_by(Color c) const;
|
||||
Bitboard attacks_from(Piece p, Square s) const;
|
||||
template<PieceType> Bitboard attacks_from(Square s) const;
|
||||
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
|
||||
|
||||
// Properties of moves
|
||||
bool legal(Move m) const;
|
||||
bool pseudo_legal(const Move m) const;
|
||||
bool capture(Move m) const;
|
||||
bool gives_check(Move m) const;
|
||||
Piece moved_piece(Move m) const;
|
||||
Piece captured_piece() const;
|
||||
bool pl_move_is_legal(Move m, Bitboard pinned) const;
|
||||
bool pl_move_is_evasion(Move m, Bitboard pinned) const;
|
||||
bool move_is_check(Move m) const;
|
||||
bool move_is_check(Move m, const CheckInfo& ci) const;
|
||||
bool move_is_capture(Move m) const;
|
||||
bool move_is_capture_or_promotion(Move m) const;
|
||||
bool move_is_passed_pawn_push(Move m) const;
|
||||
bool move_attacks_square(Move m, Square s) 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;
|
||||
// Information about pawns
|
||||
bool pawn_is_passed(Color c, Square s) const;
|
||||
static bool pawn_is_passed(Bitboard theirPawns, Color c, Square s);
|
||||
static bool pawn_is_isolated(Bitboard ourPawns, Square s);
|
||||
static bool pawn_is_doubled(Bitboard ourPawns, Color c, Square s);
|
||||
|
||||
// Weak squares
|
||||
bool square_is_weak(Square s, Color c) const;
|
||||
|
||||
// Doing and undoing moves
|
||||
void do_move(Move m, StateInfo& newSt);
|
||||
void do_move(Move m, StateInfo& newSt, bool givesCheck);
|
||||
void saveState();
|
||||
void do_move(Move m, StateInfo& st);
|
||||
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
|
||||
void undo_move(Move m);
|
||||
void do_null_move(StateInfo& newSt);
|
||||
void do_null_move(StateInfo& st);
|
||||
void undo_null_move();
|
||||
|
||||
// Static Exchange Evaluation
|
||||
bool see_ge(Move m, Value threshold = VALUE_ZERO) const;
|
||||
// Static exchange evaluation
|
||||
int see(Square from, Square to) const;
|
||||
int see(Move m) const;
|
||||
int see(Square to) const;
|
||||
int see_sign(Move m) const;
|
||||
|
||||
// Accessing hash keys
|
||||
Key key() const;
|
||||
Key key_after(Move m) const;
|
||||
Key material_key() const;
|
||||
Key pawn_key() const;
|
||||
Key get_key() const;
|
||||
Key get_exclusion_key() const;
|
||||
Key get_pawn_key() const;
|
||||
Key get_material_key() const;
|
||||
|
||||
// Incremental evaluation
|
||||
Score value() const;
|
||||
Value non_pawn_material(Color c) const;
|
||||
Score pst_delta(Piece piece, Square from, Square to) const;
|
||||
|
||||
// Game termination checks
|
||||
bool is_mate() const;
|
||||
bool is_draw() const;
|
||||
|
||||
// Check if one side threatens a mate in one
|
||||
bool has_mate_threat(Color c);
|
||||
|
||||
// Number of plies since the last non-reversible move
|
||||
int rule_50_counter() 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;
|
||||
bool opposite_colored_bishops() const;
|
||||
bool has_pawn_on_7th(Color c) const;
|
||||
|
||||
// Reset the gamePly variable to 0
|
||||
void reset_game_ply();
|
||||
|
||||
// Position consistency check, for debugging
|
||||
bool pos_is_ok() const;
|
||||
void flip();
|
||||
bool is_ok(int* failedStep = NULL) const;
|
||||
|
||||
// Used by NNUE
|
||||
StateInfo* state() const;
|
||||
|
||||
void put_piece(Piece pc, Square s);
|
||||
void remove_piece(Square s);
|
||||
// Static member functions
|
||||
static void init_zobrist();
|
||||
static void init_piece_square_tables();
|
||||
|
||||
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 move_piece(Square from, Square to);
|
||||
template<bool Do>
|
||||
void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto);
|
||||
// Initialization helper functions (used while setting up a position)
|
||||
void clear();
|
||||
void put_piece(Piece p, Square s);
|
||||
void allow_oo(Color c);
|
||||
void allow_ooo(Color c);
|
||||
|
||||
// Data members
|
||||
Piece board[SQUARE_NB];
|
||||
Bitboard byTypeBB[PIECE_TYPE_NB];
|
||||
Bitboard byColorBB[COLOR_NB];
|
||||
int pieceCount[PIECE_NB];
|
||||
int castlingRightsMask[SQUARE_NB];
|
||||
Square castlingRookSquare[CASTLING_RIGHT_NB];
|
||||
Bitboard castlingPath[CASTLING_RIGHT_NB];
|
||||
Thread* thisThread;
|
||||
StateInfo* st;
|
||||
int gamePly;
|
||||
// Helper functions for doing and undoing moves
|
||||
void do_capture_move(Bitboard& key, PieceType capture, Color them, Square to, bool ep);
|
||||
void do_castle_move(Move m);
|
||||
void undo_castle_move(Move m);
|
||||
void find_checkers();
|
||||
|
||||
template<bool FindPinned>
|
||||
Bitboard hidden_checkers(Color c) const;
|
||||
|
||||
// Computing hash keys from scratch (for initialization and debugging)
|
||||
Key compute_key() const;
|
||||
Key compute_pawn_key() const;
|
||||
Key compute_material_key() const;
|
||||
|
||||
// Computing incremental evaluation scores and material counts
|
||||
Score pst(Color c, PieceType pt, Square s) const;
|
||||
Score compute_value() const;
|
||||
Value compute_non_pawn_material(Color c) const;
|
||||
|
||||
// Board
|
||||
Piece board[64];
|
||||
|
||||
// Bitboards
|
||||
Bitboard byTypeBB[8], byColorBB[2];
|
||||
|
||||
// Piece counts
|
||||
int pieceCount[2][8]; // [color][pieceType]
|
||||
|
||||
// Piece lists
|
||||
Square pieceList[2][8][16]; // [color][pieceType][index]
|
||||
int index[64]; // [square]
|
||||
|
||||
// Other info
|
||||
Color sideToMove;
|
||||
Score psq;
|
||||
bool chess960;
|
||||
int gamePly;
|
||||
Key history[MaxGameLength];
|
||||
File initialKFile, initialKRFile, initialQRFile;
|
||||
StateInfo startState;
|
||||
StateInfo* st;
|
||||
|
||||
// Static variables
|
||||
static int castleRightsMask[64];
|
||||
static Key zobrist[2][8][64];
|
||||
static Key zobEp[64];
|
||||
static Key zobCastle[16];
|
||||
static Key zobMaterial[2][8][16];
|
||||
static Key zobSideToMove;
|
||||
static Score PieceSquareTable[16][64];
|
||||
static Key zobExclusion;
|
||||
};
|
||||
|
||||
extern std::ostream& operator<<(std::ostream& os, const Position& pos);
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
inline Piece Position::piece_on(Square s) const {
|
||||
return board[s];
|
||||
}
|
||||
|
||||
inline Color Position::color_of_piece_on(Square s) const {
|
||||
return color_of_piece(piece_on(s));
|
||||
}
|
||||
|
||||
inline PieceType Position::type_of_piece_on(Square s) const {
|
||||
return type_of_piece(piece_on(s));
|
||||
}
|
||||
|
||||
inline bool Position::square_is_empty(Square s) const {
|
||||
return piece_on(s) == EMPTY;
|
||||
}
|
||||
|
||||
inline bool Position::square_is_occupied(Square s) const {
|
||||
return !square_is_empty(s);
|
||||
}
|
||||
|
||||
inline Value Position::midgame_value_of_piece_on(Square s) const {
|
||||
return piece_value_midgame(piece_on(s));
|
||||
}
|
||||
|
||||
inline Value Position::endgame_value_of_piece_on(Square s) const {
|
||||
return piece_value_endgame(piece_on(s));
|
||||
}
|
||||
|
||||
inline Color Position::side_to_move() const {
|
||||
return sideToMove;
|
||||
}
|
||||
|
||||
inline Piece Position::piece_on(Square s) const {
|
||||
assert(is_ok(s));
|
||||
return board[s];
|
||||
inline Bitboard Position::occupied_squares() const {
|
||||
return byTypeBB[0];
|
||||
}
|
||||
|
||||
inline bool Position::empty(Square s) const {
|
||||
return piece_on(s) == NO_PIECE;
|
||||
inline Bitboard Position::empty_squares() const {
|
||||
return ~(occupied_squares());
|
||||
}
|
||||
|
||||
inline Piece Position::moved_piece(Move m) const {
|
||||
return piece_on(from_sq(m));
|
||||
}
|
||||
|
||||
inline Bitboard Position::pieces(PieceType pt = ALL_PIECES) const {
|
||||
return byTypeBB[pt];
|
||||
}
|
||||
|
||||
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const {
|
||||
return pieces(pt1) | pieces(pt2);
|
||||
}
|
||||
|
||||
inline Bitboard Position::pieces(Color c) const {
|
||||
inline Bitboard Position::pieces_of_color(Color c) const {
|
||||
return byColorBB[c];
|
||||
}
|
||||
|
||||
inline Bitboard Position::pieces(Color c, PieceType pt) const {
|
||||
return pieces(c) & pieces(pt);
|
||||
inline Bitboard Position::pieces(PieceType pt) const {
|
||||
return byTypeBB[pt];
|
||||
}
|
||||
|
||||
inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const {
|
||||
return pieces(c) & (pieces(pt1) | pieces(pt2));
|
||||
inline Bitboard Position::pieces(PieceType pt, Color c) const {
|
||||
return byTypeBB[pt] & byColorBB[c];
|
||||
}
|
||||
|
||||
template<PieceType Pt> inline int Position::count(Color c) const {
|
||||
return pieceCount[make_piece(c, Pt)];
|
||||
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const {
|
||||
return byTypeBB[pt1] | byTypeBB[pt2];
|
||||
}
|
||||
|
||||
template<PieceType Pt> inline int Position::count() const {
|
||||
return count<Pt>(WHITE) + count<Pt>(BLACK);
|
||||
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2, Color c) const {
|
||||
return (byTypeBB[pt1] | byTypeBB[pt2]) & byColorBB[c];
|
||||
}
|
||||
|
||||
template<PieceType Pt> inline Square Position::square(Color c) const {
|
||||
assert(count<Pt>(c) == 1);
|
||||
return lsb(pieces(c, Pt));
|
||||
inline int Position::piece_count(Color c, PieceType pt) const {
|
||||
return pieceCount[c][pt];
|
||||
}
|
||||
|
||||
inline Square Position::piece_list(Color c, PieceType pt, int index) const {
|
||||
return pieceList[c][pt][index];
|
||||
}
|
||||
|
||||
inline const Square* Position::piece_list_begin(Color c, PieceType pt) const {
|
||||
return pieceList[c][pt];
|
||||
}
|
||||
|
||||
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 Square Position::king_square(Color c) const {
|
||||
return pieceList[c][KING][0];
|
||||
}
|
||||
|
||||
inline bool Position::can_castle(CastlingRights cr) const {
|
||||
return st->castlingRights & cr;
|
||||
inline bool Position::can_castle_kingside(Color side) const {
|
||||
return st->castleRights & (1+int(side));
|
||||
}
|
||||
|
||||
inline CastlingRights Position::castling_rights(Color c) const {
|
||||
return c & CastlingRights(st->castlingRights);
|
||||
inline bool Position::can_castle_queenside(Color side) const {
|
||||
return st->castleRights & (4+4*int(side));
|
||||
}
|
||||
|
||||
inline bool Position::castling_impeded(CastlingRights cr) const {
|
||||
assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO);
|
||||
|
||||
return pieces() & castlingPath[cr];
|
||||
inline bool Position::can_castle(Color side) const {
|
||||
return can_castle_kingside(side) || can_castle_queenside(side);
|
||||
}
|
||||
|
||||
inline Square Position::castling_rook_square(CastlingRights cr) const {
|
||||
assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO);
|
||||
|
||||
return castlingRookSquare[cr];
|
||||
inline Square Position::initial_kr_square(Color c) const {
|
||||
return relative_square(c, make_square(initialKRFile, RANK_1));
|
||||
}
|
||||
|
||||
inline Bitboard Position::attackers_to(Square s) const {
|
||||
return attackers_to(s, pieces());
|
||||
inline Square Position::initial_qr_square(Color c) const {
|
||||
return relative_square(c, make_square(initialQRFile, RANK_1));
|
||||
}
|
||||
|
||||
template<PieceType Pt>
|
||||
inline Bitboard Position::attacks_by(Color c) const {
|
||||
template<>
|
||||
inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
|
||||
return StepAttackBB[piece_of_color_and_type(c, PAWN)][s];
|
||||
}
|
||||
|
||||
if constexpr (Pt == PAWN)
|
||||
return c == WHITE ? pawn_attacks_bb<WHITE>(pieces(WHITE, PAWN))
|
||||
: pawn_attacks_bb<BLACK>(pieces(BLACK, PAWN));
|
||||
else
|
||||
{
|
||||
Bitboard threats = 0;
|
||||
Bitboard attackers = pieces(c, Pt);
|
||||
while (attackers)
|
||||
threats |= attacks_bb<Pt>(pop_lsb(attackers), pieces());
|
||||
return threats;
|
||||
}
|
||||
template<PieceType Piece> // Knight and King and white pawns
|
||||
inline Bitboard Position::attacks_from(Square s) const {
|
||||
return StepAttackBB[Piece][s];
|
||||
}
|
||||
|
||||
template<>
|
||||
inline Bitboard Position::attacks_from<BISHOP>(Square s) const {
|
||||
return bishop_attacks_bb(s, occupied_squares());
|
||||
}
|
||||
|
||||
template<>
|
||||
inline Bitboard Position::attacks_from<ROOK>(Square s) const {
|
||||
return rook_attacks_bb(s, occupied_squares());
|
||||
}
|
||||
|
||||
template<>
|
||||
inline Bitboard Position::attacks_from<QUEEN>(Square s) const {
|
||||
return attacks_from<ROOK>(s) | attacks_from<BISHOP>(s);
|
||||
}
|
||||
|
||||
inline Bitboard Position::checkers() const {
|
||||
return st->checkersBB;
|
||||
}
|
||||
|
||||
inline Bitboard Position::blockers_for_king(Color c) const {
|
||||
return st->blockersForKing[c];
|
||||
inline bool Position::is_check() const {
|
||||
return st->checkersBB != EmptyBoardBB;
|
||||
}
|
||||
|
||||
inline Bitboard Position::pinners(Color c) const {
|
||||
return st->pinners[c];
|
||||
inline bool Position::pawn_is_passed(Color c, Square s) const {
|
||||
return !(pieces(PAWN, opposite_color(c)) & passed_pawn_mask(c, s));
|
||||
}
|
||||
|
||||
inline Bitboard Position::check_squares(PieceType pt) const {
|
||||
return st->checkSquares[pt];
|
||||
inline bool Position::pawn_is_passed(Bitboard theirPawns, Color c, Square s) {
|
||||
return !(theirPawns & passed_pawn_mask(c, s));
|
||||
}
|
||||
|
||||
inline bool Position::pawn_passed(Color c, Square s) const {
|
||||
return !(pieces(~c, PAWN) & passed_pawn_span(c, s));
|
||||
inline bool Position::pawn_is_isolated(Bitboard ourPawns, Square s) {
|
||||
return !(ourPawns & neighboring_files_bb(s));
|
||||
}
|
||||
|
||||
inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
|
||||
return popcount(pieces(c, PAWN) & ((DarkSquares & s) ? DarkSquares : ~DarkSquares));
|
||||
inline bool Position::pawn_is_doubled(Bitboard ourPawns, Color c, Square s) {
|
||||
return ourPawns & squares_behind(c, s);
|
||||
}
|
||||
|
||||
inline Key Position::key() const {
|
||||
return st->rule50 < 14 ? st->key
|
||||
: st->key ^ make_key((st->rule50 - 14) / 8);
|
||||
inline bool Position::square_is_weak(Square s, Color c) const {
|
||||
return !(pieces(PAWN, c) & outpost_mask(opposite_color(c), s));
|
||||
}
|
||||
|
||||
inline Key Position::pawn_key() const {
|
||||
inline Key Position::get_key() const {
|
||||
return st->key;
|
||||
}
|
||||
|
||||
inline Key Position::get_exclusion_key() const {
|
||||
return st->key ^ zobExclusion;
|
||||
}
|
||||
|
||||
inline Key Position::get_pawn_key() const {
|
||||
return st->pawnKey;
|
||||
}
|
||||
|
||||
inline Key Position::material_key() const {
|
||||
inline Key Position::get_material_key() const {
|
||||
return st->materialKey;
|
||||
}
|
||||
|
||||
inline Score Position::psq_score() const {
|
||||
return psq;
|
||||
inline Score Position::pst(Color c, PieceType pt, Square s) const {
|
||||
return PieceSquareTable[piece_of_color_and_type(c, pt)][s];
|
||||
}
|
||||
|
||||
inline Score Position::pst_delta(Piece piece, Square from, Square to) const {
|
||||
return PieceSquareTable[piece][to] - PieceSquareTable[piece][from];
|
||||
}
|
||||
|
||||
inline Score Position::value() const {
|
||||
return st->value;
|
||||
}
|
||||
|
||||
inline Value Position::non_pawn_material(Color c) const {
|
||||
return st->nonPawnMaterial[c];
|
||||
return st->npMaterial[c];
|
||||
}
|
||||
|
||||
inline Value Position::non_pawn_material() const {
|
||||
return non_pawn_material(WHITE) + non_pawn_material(BLACK);
|
||||
inline bool Position::move_is_passed_pawn_push(Move m) const {
|
||||
|
||||
Color c = side_to_move();
|
||||
return piece_on(move_from(m)) == piece_of_color_and_type(c, PAWN)
|
||||
&& pawn_is_passed(c, move_to(m));
|
||||
}
|
||||
|
||||
inline int Position::game_ply() const {
|
||||
return gamePly;
|
||||
}
|
||||
inline int Position::rule_50_counter() const {
|
||||
|
||||
inline int Position::rule50_count() const {
|
||||
return st->rule50;
|
||||
}
|
||||
|
||||
inline bool Position::opposite_bishops() const {
|
||||
return count<BISHOP>(WHITE) == 1
|
||||
&& count<BISHOP>(BLACK) == 1
|
||||
&& opposite_colors(square<BISHOP>(WHITE), square<BISHOP>(BLACK));
|
||||
inline bool Position::opposite_colored_bishops() const {
|
||||
|
||||
return piece_count(WHITE, BISHOP) == 1
|
||||
&& piece_count(BLACK, BISHOP) == 1
|
||||
&& square_color(piece_list(WHITE, BISHOP, 0)) != square_color(piece_list(BLACK, BISHOP, 0));
|
||||
}
|
||||
|
||||
inline bool Position::is_chess960() const {
|
||||
return chess960;
|
||||
inline bool Position::has_pawn_on_7th(Color c) const {
|
||||
|
||||
return pieces(PAWN, c) & relative_rank_bb(c, RANK_7);
|
||||
}
|
||||
|
||||
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) == EN_PASSANT;
|
||||
inline bool Position::move_is_capture(Move m) const {
|
||||
|
||||
// Move must not be MOVE_NONE !
|
||||
return (m & (3 << 15)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
|
||||
}
|
||||
|
||||
inline Piece Position::captured_piece() const {
|
||||
return st->capturedPiece;
|
||||
inline bool Position::move_is_capture_or_promotion(Move m) const {
|
||||
|
||||
// Move must not be MOVE_NONE !
|
||||
return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
|
||||
}
|
||||
|
||||
inline Thread* Position::this_thread() const {
|
||||
return thisThread;
|
||||
}
|
||||
|
||||
inline void Position::put_piece(Piece pc, Square s) {
|
||||
|
||||
board[s] = pc;
|
||||
byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s;
|
||||
byColorBB[color_of(pc)] |= s;
|
||||
pieceCount[pc]++;
|
||||
pieceCount[make_piece(color_of(pc), ALL_PIECES)]++;
|
||||
psq += PSQT::psq[pc][s];
|
||||
}
|
||||
|
||||
inline void Position::remove_piece(Square s) {
|
||||
|
||||
Piece pc = board[s];
|
||||
byTypeBB[ALL_PIECES] ^= s;
|
||||
byTypeBB[type_of(pc)] ^= s;
|
||||
byColorBB[color_of(pc)] ^= s;
|
||||
board[s] = NO_PIECE;
|
||||
pieceCount[pc]--;
|
||||
pieceCount[make_piece(color_of(pc), ALL_PIECES)]--;
|
||||
psq -= PSQT::psq[pc][s];
|
||||
}
|
||||
|
||||
inline void Position::move_piece(Square from, Square to) {
|
||||
|
||||
Piece pc = board[from];
|
||||
Bitboard fromTo = from | to;
|
||||
byTypeBB[ALL_PIECES] ^= fromTo;
|
||||
byTypeBB[type_of(pc)] ^= fromTo;
|
||||
byColorBB[color_of(pc)] ^= fromTo;
|
||||
board[from] = NO_PIECE;
|
||||
board[to] = pc;
|
||||
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));
|
||||
}
|
||||
|
||||
inline StateInfo* Position::state() const {
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef POSITION_H_INCLUDED
|
||||
#endif // !defined(POSITION_H_INCLUDED)
|
||||
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "psqt.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
auto constexpr S = make_score;
|
||||
|
||||
// 'Bonus' contains Piece-Square parameters.
|
||||
// Scores are explicit for files A to D, implicitly mirrored for E to H.
|
||||
constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
|
||||
{ },
|
||||
{ },
|
||||
{ // Knight
|
||||
{ S(-175, -96), S(-92,-65), S(-74,-49), S(-73,-21) },
|
||||
{ S( -77, -67), S(-41,-54), S(-27,-18), S(-15, 8) },
|
||||
{ S( -61, -40), S(-17,-27), S( 6, -8), S( 12, 29) },
|
||||
{ S( -35, -35), S( 8, -2), S( 40, 13), S( 49, 28) },
|
||||
{ S( -34, -45), S( 13,-16), S( 44, 9), S( 51, 39) },
|
||||
{ S( -9, -51), S( 22,-44), S( 58,-16), S( 53, 17) },
|
||||
{ S( -67, -69), S(-27,-50), S( 4,-51), S( 37, 12) },
|
||||
{ S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) }
|
||||
},
|
||||
{ // Bishop
|
||||
{ S(-37,-40), S(-4 ,-21), S( -6,-26), S(-16, -8) },
|
||||
{ S(-11,-26), S( 6, -9), S( 13,-12), S( 3, 1) },
|
||||
{ S(-5 ,-11), S( 15, -1), S( -4, -1), S( 12, 7) },
|
||||
{ S(-4 ,-14), S( 8, -4), S( 18, 0), S( 27, 12) },
|
||||
{ S(-8 ,-12), S( 20, -1), S( 15,-10), S( 22, 11) },
|
||||
{ S(-11,-21), S( 4, 4), S( 1, 3), S( 8, 4) },
|
||||
{ S(-12,-22), S(-10,-14), S( 4, -1), S( 0, 1) },
|
||||
{ S(-34,-32), S( 1,-29), S(-10,-26), S(-16,-17) }
|
||||
},
|
||||
{ // Rook
|
||||
{ S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) },
|
||||
{ S(-21,-12), S(-13, -9), S( -8, -1), S( 6, -2) },
|
||||
{ S(-25, 6), S(-11, -8), S( -1, -2), S( 3, -6) },
|
||||
{ S(-13, -6), S( -5, 1), S( -4, -9), S(-6, 7) },
|
||||
{ S(-27, -5), S(-15, 8), S( -4, 7), S( 3, -6) },
|
||||
{ S(-22, 6), S( -2, 1), S( 6, -7), S(12, 10) },
|
||||
{ S( -2, 4), S( 12, 5), S( 16, 20), S(18, -5) },
|
||||
{ S(-17, 18), S(-19, 0), S( -1, 19), S( 9, 13) }
|
||||
},
|
||||
{ // Queen
|
||||
{ S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) },
|
||||
{ S(-3,-54), S( 5,-31), S( 8,-22), S(12, -4) },
|
||||
{ S(-3,-39), S( 6,-18), S(13, -9), S( 7, 3) },
|
||||
{ S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) },
|
||||
{ S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) },
|
||||
{ S(-4,-38), S(10,-18), S( 6,-11), S( 8, 1) },
|
||||
{ S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) },
|
||||
{ S(-2,-74), S(-2,-52), S( 1,-43), S(-2,-34) }
|
||||
},
|
||||
{ // King
|
||||
{ S(271, 1), S(327, 45), S(271, 85), S(198, 76) },
|
||||
{ S(278, 53), S(303,100), S(234,133), S(179,135) },
|
||||
{ S(195, 88), S(258,130), S(169,169), S(120,175) },
|
||||
{ S(164,103), S(190,156), S(138,172), S( 98,172) },
|
||||
{ S(154, 96), S(179,166), S(105,199), S( 70,199) },
|
||||
{ S(123, 92), S(145,172), S( 81,184), S( 31,191) },
|
||||
{ S( 88, 47), S(120,121), S( 65,116), S( 33,131) },
|
||||
{ S( 59, 11), S( 89, 59), S( 45, 73), S( -1, 78) }
|
||||
}
|
||||
};
|
||||
|
||||
constexpr Score PBonus[RANK_NB][FILE_NB] =
|
||||
{ // Pawn (asymmetric distribution)
|
||||
{ },
|
||||
{ S( 2, -8), S( 4, -6), S( 11, 9), S( 18, 5), S( 16, 16), S( 21, 6), S( 9, -6), S( -3,-18) },
|
||||
{ S( -9, -9), S(-15, -7), S( 11,-10), S( 15, 5), S( 31, 2), S( 23, 3), S( 6, -8), S(-20, -5) },
|
||||
{ S( -3, 7), S(-20, 1), S( 8, -8), S( 19, -2), S( 39,-14), S( 17,-13), S( 2,-11), S( -5, -6) },
|
||||
{ S( 11, 12), S( -4, 6), S(-11, 2), S( 2, -6), S( 11, -5), S( 0, -4), S(-12, 14), S( 5, 9) },
|
||||
{ S( 3, 27), S(-11, 18), S( -6, 19), S( 22, 29), S( -8, 30), S( -5, 9), S(-14, 8), S(-11, 14) },
|
||||
{ S( -7, -1), S( 6,-14), S( -2, 13), S(-11, 22), S( 4, 24), S(-14, 17), S( 10, 7), S( -9, 7) }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
namespace PSQT
|
||||
{
|
||||
|
||||
Score psq[PIECE_NB][SQUARE_NB];
|
||||
|
||||
// PSQT::init() initializes piece-square tables: the white halves of the tables are
|
||||
// copied from Bonus[] and PBonus[], adding the piece value, then the black halves of
|
||||
// the tables are initialized by flipping and changing the sign of the white scores.
|
||||
void init() {
|
||||
|
||||
for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING})
|
||||
{
|
||||
Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
|
||||
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
{
|
||||
File f = File(edge_distance(file_of(s)));
|
||||
psq[ pc][s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)]
|
||||
: Bonus[pc][rank_of(s)][f]);
|
||||
psq[~pc][flip_rank(s)] = -psq[pc][s];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace PSQT
|
||||
|
||||
} // namespace Stockfish
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(PSQTAB_H_INCLUDED)
|
||||
#define PSQTAB_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "value.h"
|
||||
|
||||
|
||||
////
|
||||
//// Constants modified by Joona Kiiski
|
||||
////
|
||||
|
||||
static const Value MP = PawnValueMidgame;
|
||||
static const Value MK = KnightValueMidgame;
|
||||
static const Value MB = BishopValueMidgame;
|
||||
static const Value MR = RookValueMidgame;
|
||||
static const Value MQ = QueenValueMidgame;
|
||||
|
||||
static const int MgPST[][64] = {
|
||||
{ },
|
||||
{// Pawn
|
||||
// A B C D E F G H
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
MP-28, MP-6, MP+ 4, MP+14, MP+14, MP+ 4, MP-6, MP-28,
|
||||
MP-28, MP-6, MP+ 9, MP+36, MP+36, MP+ 9, MP-6, MP-28,
|
||||
MP-28, MP-6, MP+17, MP+58, MP+58, MP+17, MP-6, MP-28,
|
||||
MP-28, MP-6, MP+17, MP+36, MP+36, MP+17, MP-6, MP-28,
|
||||
MP-28, MP-6, MP+ 9, MP+14, MP+14, MP+ 9, MP-6, MP-28,
|
||||
MP-28, MP-6, MP+ 4, MP+14, MP+14, MP+ 4, MP-6, MP-28,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
},
|
||||
{// Knight
|
||||
// A B C D E F G H
|
||||
MK-135, MK-107, MK-80, MK-67, MK-67, MK-80, MK-107, MK-135,
|
||||
MK- 93, MK- 67, MK-39, MK-25, MK-25, MK-39, MK- 67, MK- 93,
|
||||
MK- 53, MK- 25, MK+ 1, MK+13, MK+13, MK+ 1, MK- 25, MK- 53,
|
||||
MK- 25, MK+ 1, MK+27, MK+41, MK+41, MK+27, MK+ 1, MK- 25,
|
||||
MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11,
|
||||
MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11,
|
||||
MK- 53, MK- 25, MK+ 1, MK+13, MK+13, MK+ 1, MK- 25, MK- 53,
|
||||
MK-193, MK- 67, MK-39, MK-25, MK-25, MK-39, MK- 67, MK-193
|
||||
},
|
||||
{// Bishop
|
||||
// A B C D E F G H
|
||||
MB-40, MB-40, MB-35, MB-30, MB-30, MB-35, MB-40, MB-40,
|
||||
MB-17, MB+ 0, MB- 4, MB+ 0, MB+ 0, MB- 4, MB+ 0, MB-17,
|
||||
MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13,
|
||||
MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8,
|
||||
MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8,
|
||||
MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13,
|
||||
MB-17, MB+ 0, MB- 4, MB+ 0, MB+ 0, MB- 4, MB+ 0, MB-17,
|
||||
MB-17, MB-17, MB-13, MB- 8, MB- 8, MB-13, MB-17, MB-17
|
||||
},
|
||||
{// Rook
|
||||
// A B C D E F G H
|
||||
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
|
||||
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
|
||||
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
|
||||
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
|
||||
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
|
||||
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
|
||||
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
|
||||
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12
|
||||
},
|
||||
{// Queen
|
||||
// A B C D E F G H
|
||||
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
|
||||
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
|
||||
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
|
||||
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
|
||||
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
|
||||
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
|
||||
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
|
||||
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8
|
||||
},
|
||||
{// King
|
||||
//A B C D E F G H
|
||||
287, 311, 262, 214, 214, 262, 311, 287,
|
||||
262, 287, 238, 190, 190, 238, 287, 262,
|
||||
214, 238, 190, 142, 142, 190, 238, 214,
|
||||
190, 214, 167, 119, 119, 167, 214, 190,
|
||||
167, 190, 142, 94, 94, 142, 190, 167,
|
||||
142, 167, 119, 69, 69, 119, 167, 142,
|
||||
119, 142, 94, 46, 46, 94, 142, 119,
|
||||
94, 119, 69, 21, 21, 69, 119, 94
|
||||
}
|
||||
};
|
||||
|
||||
static const Value EP = PawnValueEndgame;
|
||||
static const Value EK = KnightValueEndgame;
|
||||
static const Value EB = BishopValueEndgame;
|
||||
static const Value ER = RookValueEndgame;
|
||||
static const Value EQ = QueenValueEndgame;
|
||||
|
||||
static const int EgPST[][64] = {
|
||||
{ },
|
||||
{// Pawn
|
||||
// A B C D E F G H
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
|
||||
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
|
||||
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
|
||||
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
|
||||
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
|
||||
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
},
|
||||
{// Knight
|
||||
// A B C D E F G H
|
||||
EK-104, EK-79, EK-55, EK-42, EK-42, EK-55, EK-79, EK-104,
|
||||
EK- 79, EK-55, EK-30, EK-17, EK-17, EK-30, EK-55, EK- 79,
|
||||
EK- 55, EK-30, EK- 6, EK+ 5, EK+ 5, EK- 6, EK-30, EK- 55,
|
||||
EK- 42, EK-17, EK+ 5, EK+18, EK+18, EK+ 5, EK-17, EK- 42,
|
||||
EK- 42, EK-17, EK+ 5, EK+18, EK+18, EK+ 5, EK-17, EK- 42,
|
||||
EK- 55, EK-30, EK- 6, EK+ 5, EK+ 5, EK- 6, EK-30, EK- 55,
|
||||
EK- 79, EK-55, EK-30, EK-17, EK-17, EK-30, EK-55, EK- 79,
|
||||
EK-104, EK-79, EK-55, EK-42, EK-42, EK-55, EK-79, EK-104
|
||||
},
|
||||
{// Bishop
|
||||
// A B C D E F G H
|
||||
EB-59, EB-42, EB-35, EB-26, EB-26, EB-35, EB-42, EB-59,
|
||||
EB-42, EB-26, EB-18, EB-11, EB-11, EB-18, EB-26, EB-42,
|
||||
EB-35, EB-18, EB-11, EB- 4, EB- 4, EB-11, EB-18, EB-35,
|
||||
EB-26, EB-11, EB- 4, EB+ 4, EB+ 4, EB- 4, EB-11, EB-26,
|
||||
EB-26, EB-11, EB- 4, EB+ 4, EB+ 4, EB- 4, EB-11, EB-26,
|
||||
EB-35, EB-18, EB-11, EB- 4, EB- 4, EB-11, EB-18, EB-35,
|
||||
EB-42, EB-26, EB-18, EB-11, EB-11, EB-18, EB-26, EB-42,
|
||||
EB-59, EB-42, EB-35, EB-26, EB-26, EB-35, EB-42, EB-59
|
||||
},
|
||||
{// Rook
|
||||
// A B C D E F G H
|
||||
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
|
||||
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
|
||||
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
|
||||
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
|
||||
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
|
||||
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
|
||||
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
|
||||
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3
|
||||
},
|
||||
{// Queen
|
||||
// A B C D E F G H
|
||||
EQ-80, EQ-54, EQ-42, EQ-30, EQ-30, EQ-42, EQ-54, EQ-80,
|
||||
EQ-54, EQ-30, EQ-18, EQ- 6, EQ- 6, EQ-18, EQ-30, EQ-54,
|
||||
EQ-42, EQ-18, EQ- 6, EQ+ 6, EQ+ 6, EQ- 6, EQ-18, EQ-42,
|
||||
EQ-30, EQ- 6, EQ+ 6, EQ+18, EQ+18, EQ+ 6, EQ- 6, EQ-30,
|
||||
EQ-30, EQ- 6, EQ+ 6, EQ+18, EQ+18, EQ+ 6, EQ- 6, EQ-30,
|
||||
EQ-42, EQ-18, EQ- 6, EQ+ 6, EQ+ 6, EQ- 6, EQ-18, EQ-42,
|
||||
EQ-54, EQ-30, EQ-18, EQ- 6, EQ- 6, EQ-18, EQ-30, EQ-54,
|
||||
EQ-80, EQ-54, EQ-42, EQ-30, EQ-30, EQ-42, EQ-54, EQ-80
|
||||
},
|
||||
{// King
|
||||
//A B C D E F G H
|
||||
18, 77, 105, 135, 135, 105, 77, 18,
|
||||
77, 135, 165, 193, 193, 165, 135, 77,
|
||||
105, 165, 193, 222, 222, 193, 165, 105,
|
||||
135, 193, 222, 251, 251, 222, 193, 135,
|
||||
135, 193, 222, 251, 251, 222, 193, 135,
|
||||
105, 165, 193, 222, 222, 193, 165, 105,
|
||||
77, 135, 165, 193, 193, 165, 135, 77,
|
||||
18, 77, 105, 135, 135, 105, 77, 18
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // !defined(PSQTAB_H_INCLUDED)
|
||||
+436
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "history.h"
|
||||
#include "movepick.h"
|
||||
#include "san.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
enum Ambiguity {
|
||||
AMBIGUITY_NONE,
|
||||
AMBIGUITY_FILE,
|
||||
AMBIGUITY_RANK,
|
||||
AMBIGUITY_BOTH
|
||||
};
|
||||
|
||||
const History H; // used as dummy argument for MovePicker c'tor
|
||||
|
||||
Ambiguity move_ambiguity(const Position& pos, Move m);
|
||||
const string time_string(int milliseconds);
|
||||
const string score_string(Value v);
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
/// move_to_san() takes a position and a move as input, where it is assumed
|
||||
/// that the move is a legal move from the position. The return value is
|
||||
/// a string containing the move in short algebraic notation.
|
||||
|
||||
const string move_to_san(const Position& pos, Move m) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
assert(move_is_ok(m));
|
||||
|
||||
Square from, to;
|
||||
PieceType pt;
|
||||
|
||||
from = move_from(m);
|
||||
to = move_to(m);
|
||||
pt = type_of_piece(pos.piece_on(move_from(m)));
|
||||
|
||||
string san = "";
|
||||
|
||||
if (m == MOVE_NONE)
|
||||
return "(none)";
|
||||
else if (m == MOVE_NULL)
|
||||
return "(null)";
|
||||
else if (move_is_long_castle(m) || (int(to - from) == -2 && pt == KING))
|
||||
san = "O-O-O";
|
||||
else if (move_is_short_castle(m) || (int(to - from) == 2 && pt == KING))
|
||||
san = "O-O";
|
||||
else
|
||||
{
|
||||
if (pt != PAWN)
|
||||
{
|
||||
san += piece_type_to_char(pt, true);
|
||||
switch (move_ambiguity(pos, m)) {
|
||||
case AMBIGUITY_NONE:
|
||||
break;
|
||||
case AMBIGUITY_FILE:
|
||||
san += file_to_char(square_file(from));
|
||||
break;
|
||||
case AMBIGUITY_RANK:
|
||||
san += rank_to_char(square_rank(from));
|
||||
break;
|
||||
case AMBIGUITY_BOTH:
|
||||
san += square_to_string(from);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
if (pos.move_is_capture(m))
|
||||
{
|
||||
if (pt == PAWN)
|
||||
san += file_to_char(square_file(move_from(m)));
|
||||
san += "x";
|
||||
}
|
||||
san += square_to_string(move_to(m));
|
||||
if (move_is_promotion(m))
|
||||
{
|
||||
san += '=';
|
||||
san += piece_type_to_char(move_promotion_piece(m), true);
|
||||
}
|
||||
}
|
||||
// Is the move check? We don't use pos.move_is_check(m) here, because
|
||||
// Position::move_is_check doesn't detect all checks (not castling moves,
|
||||
// promotions and en passant captures).
|
||||
StateInfo st;
|
||||
Position p(pos);
|
||||
p.do_move(m, st);
|
||||
if (p.is_check())
|
||||
san += p.is_mate()? "#" : "+";
|
||||
|
||||
return san;
|
||||
}
|
||||
|
||||
|
||||
/// move_from_san() takes a position and a string as input, and tries to
|
||||
/// interpret the string as a move in short algebraic notation. On success,
|
||||
/// the move is returned. On failure (i.e. if the string is unparsable, or
|
||||
/// if the move is illegal or ambiguous), MOVE_NONE is returned.
|
||||
|
||||
Move move_from_san(const Position& pos, const string& movestr) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
|
||||
MovePicker mp = MovePicker(pos, MOVE_NONE, OnePly, H);
|
||||
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
|
||||
|
||||
// Castling moves
|
||||
if (movestr == "O-O-O" || movestr == "O-O-O+")
|
||||
{
|
||||
Move m;
|
||||
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||
if (move_is_long_castle(m) && pos.pl_move_is_legal(m, pinned))
|
||||
return m;
|
||||
|
||||
return MOVE_NONE;
|
||||
}
|
||||
else if (movestr == "O-O" || movestr == "O-O+")
|
||||
{
|
||||
Move m;
|
||||
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||
if (move_is_short_castle(m) && pos.pl_move_is_legal(m, pinned))
|
||||
return m;
|
||||
|
||||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
// Normal moves. We use a simple FSM to parse the san string.
|
||||
enum { START, TO_FILE, TO_RANK, PROMOTION_OR_CHECK, PROMOTION, CHECK, END };
|
||||
static const string pieceLetters = "KQRBN";
|
||||
PieceType pt = NO_PIECE_TYPE, promotion = NO_PIECE_TYPE;
|
||||
File fromFile = FILE_NONE, toFile = FILE_NONE;
|
||||
Rank fromRank = RANK_NONE, toRank = RANK_NONE;
|
||||
Square to;
|
||||
int state = START;
|
||||
|
||||
for (size_t i = 0; i < movestr.length(); i++)
|
||||
{
|
||||
char type, c = movestr[i];
|
||||
if (pieceLetters.find(c) != string::npos)
|
||||
type = 'P';
|
||||
else if (c >= 'a' && c <= 'h')
|
||||
type = 'F';
|
||||
else if (c >= '1' && c <= '8')
|
||||
type = 'R';
|
||||
else
|
||||
type = c;
|
||||
|
||||
switch (type) {
|
||||
case 'P':
|
||||
if (state == START)
|
||||
{
|
||||
pt = piece_type_from_char(c);
|
||||
state = TO_FILE;
|
||||
}
|
||||
else if (state == PROMOTION)
|
||||
{
|
||||
promotion = piece_type_from_char(c);
|
||||
state = (i < movestr.length() - 1) ? CHECK : END;
|
||||
}
|
||||
else
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
case 'F':
|
||||
if (state == START)
|
||||
{
|
||||
pt = PAWN;
|
||||
fromFile = toFile = file_from_char(c);
|
||||
state = TO_RANK;
|
||||
}
|
||||
else if (state == TO_FILE)
|
||||
{
|
||||
toFile = file_from_char(c);
|
||||
state = TO_RANK;
|
||||
}
|
||||
else if (state == TO_RANK && toFile != FILE_NONE)
|
||||
{
|
||||
// Previous file was for disambiguation
|
||||
fromFile = toFile;
|
||||
toFile = file_from_char(c);
|
||||
}
|
||||
else
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
case 'R':
|
||||
if (state == TO_RANK)
|
||||
{
|
||||
toRank = rank_from_char(c);
|
||||
state = (i < movestr.length() - 1) ? PROMOTION_OR_CHECK : END;
|
||||
}
|
||||
else if (state == TO_FILE && fromRank == RANK_NONE)
|
||||
{
|
||||
// It's a disambiguation rank instead of a file
|
||||
fromRank = rank_from_char(c);
|
||||
}
|
||||
else
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
case 'x': case 'X':
|
||||
if (state == TO_RANK)
|
||||
{
|
||||
// Previous file was for disambiguation, or it's a pawn capture
|
||||
fromFile = toFile;
|
||||
state = TO_FILE;
|
||||
}
|
||||
else if (state != TO_FILE)
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
case '=':
|
||||
if (state == PROMOTION_OR_CHECK)
|
||||
state = PROMOTION;
|
||||
else
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
case '+': case '#':
|
||||
if (state == PROMOTION_OR_CHECK || state == CHECK)
|
||||
state = END;
|
||||
else
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
default:
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state != END)
|
||||
return MOVE_NONE;
|
||||
|
||||
// Look for a matching move
|
||||
Move m, move = MOVE_NONE;
|
||||
to = make_square(toFile, toRank);
|
||||
int matches = 0;
|
||||
|
||||
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||
if ( pos.type_of_piece_on(move_from(m)) == pt
|
||||
&& move_to(m) == to
|
||||
&& move_promotion_piece(m) == promotion
|
||||
&& (fromFile == FILE_NONE || fromFile == square_file(move_from(m)))
|
||||
&& (fromRank == RANK_NONE || fromRank == square_rank(move_from(m))))
|
||||
{
|
||||
move = m;
|
||||
matches++;
|
||||
}
|
||||
return (matches == 1 ? move : MOVE_NONE);
|
||||
}
|
||||
|
||||
|
||||
/// line_to_san() takes a position and a line (an array of moves representing
|
||||
/// a sequence of legal moves from the position) as input, and returns a
|
||||
/// string containing the line in short algebraic notation. If the boolean
|
||||
/// parameter 'breakLines' is true, line breaks are inserted, with a line
|
||||
/// length of 80 characters. After a line break, 'startColumn' spaces are
|
||||
/// inserted at the beginning of the new line.
|
||||
|
||||
const string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines) {
|
||||
|
||||
StateInfo st;
|
||||
std::stringstream s;
|
||||
string moveStr;
|
||||
size_t length = 0;
|
||||
size_t maxLength = 80 - startColumn;
|
||||
Position p(pos);
|
||||
|
||||
for (int i = 0; line[i] != MOVE_NONE; i++)
|
||||
{
|
||||
moveStr = move_to_san(p, line[i]);
|
||||
length += moveStr.length() + 1;
|
||||
if (breakLines && length > maxLength)
|
||||
{
|
||||
s << '\n' << std::setw(startColumn) << ' ';
|
||||
length = moveStr.length() + 1;
|
||||
}
|
||||
s << moveStr << ' ';
|
||||
|
||||
if (line[i] == MOVE_NULL)
|
||||
p.do_null_move(st);
|
||||
else
|
||||
p.do_move(line[i], st);
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
|
||||
/// pretty_pv() creates a human-readable string from a position and a PV.
|
||||
/// It is used to write search information to the log file (which is created
|
||||
/// when the UCI parameter "Use Search Log" is "true").
|
||||
|
||||
const string pretty_pv(const Position& pos, int time, int depth,
|
||||
uint64_t nodes, Value score, ValueType type, Move pv[]) {
|
||||
std::stringstream s;
|
||||
|
||||
// Depth
|
||||
s << std::setw(2) << depth << " ";
|
||||
|
||||
// Score
|
||||
s << ((type == VALUE_TYPE_LOWER)? ">" : ((type == VALUE_TYPE_UPPER)? "<" : " "));
|
||||
s << std::setw(7) << score_string(score);
|
||||
|
||||
// Time
|
||||
s << std::setw(8) << time_string(time) << " ";
|
||||
|
||||
// Nodes
|
||||
if (nodes < 1000000ULL)
|
||||
s << std::setw(8) << nodes << " ";
|
||||
else if (nodes < 1000000000ULL)
|
||||
s << std::setw(7) << nodes/1000ULL << 'k' << " ";
|
||||
else
|
||||
s << std::setw(7) << nodes/1000000ULL << 'M' << " ";
|
||||
|
||||
// PV
|
||||
s << line_to_san(pos, pv, 30, true);
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
Ambiguity move_ambiguity(const Position& pos, Move m) {
|
||||
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Piece pc = pos.piece_on(from);
|
||||
|
||||
// King moves are never ambiguous, because there is never two kings of
|
||||
// the same color.
|
||||
if (type_of_piece(pc) == KING)
|
||||
return AMBIGUITY_NONE;
|
||||
|
||||
MovePicker mp = MovePicker(pos, MOVE_NONE, OnePly, H);
|
||||
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
|
||||
Move mv, moveList[8];
|
||||
|
||||
int n = 0;
|
||||
while ((mv = mp.get_next_move()) != MOVE_NONE)
|
||||
if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv, pinned))
|
||||
moveList[n++] = mv;
|
||||
|
||||
if (n == 1)
|
||||
return AMBIGUITY_NONE;
|
||||
|
||||
int f = 0, r = 0;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (square_file(move_from(moveList[i])) == square_file(from))
|
||||
f++;
|
||||
|
||||
if (square_rank(move_from(moveList[i])) == square_rank(from))
|
||||
r++;
|
||||
}
|
||||
if (f == 1)
|
||||
return AMBIGUITY_FILE;
|
||||
|
||||
if (r == 1)
|
||||
return AMBIGUITY_RANK;
|
||||
|
||||
return AMBIGUITY_BOTH;
|
||||
}
|
||||
|
||||
|
||||
const string time_string(int milliseconds) {
|
||||
|
||||
std::stringstream s;
|
||||
s << std::setfill('0');
|
||||
|
||||
int hours = milliseconds / (1000*60*60);
|
||||
int minutes = (milliseconds - hours*1000*60*60) / (1000*60);
|
||||
int seconds = (milliseconds - hours*1000*60*60 - minutes*1000*60) / 1000;
|
||||
|
||||
if (hours)
|
||||
s << hours << ':';
|
||||
|
||||
s << std::setw(2) << minutes << ':' << std::setw(2) << seconds;
|
||||
return s.str();
|
||||
}
|
||||
|
||||
|
||||
const string score_string(Value v) {
|
||||
|
||||
std::stringstream s;
|
||||
|
||||
if (v >= VALUE_MATE - 200)
|
||||
s << "#" << (VALUE_MATE - v + 1) / 2;
|
||||
else if(v <= -VALUE_MATE + 200)
|
||||
s << "-#" << (VALUE_MATE + v) / 2;
|
||||
else
|
||||
{
|
||||
float floatScore = float(v) / float(PawnValueMidgame);
|
||||
if (v >= 0)
|
||||
s << '+';
|
||||
|
||||
s << std::setprecision(2) << std::fixed << floatScore;
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(SAN_H_INCLUDED)
|
||||
#define SAN_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "move.h"
|
||||
#include "position.h"
|
||||
#include "value.h"
|
||||
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern const std::string move_to_san(const Position& pos, Move m);
|
||||
extern Move move_from_san(const Position& pos, const std::string& str);
|
||||
extern const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines);
|
||||
extern const std::string pretty_pv(const Position& pos, int time, int depth, uint64_t nodes, Value score, ValueType type, Move pv[]);
|
||||
|
||||
#endif // !defined(SAN_H_INCLUDED)
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(SCALE_H_INCLUDED)
|
||||
#define SCALE_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "value.h"
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
enum ScaleFactor {
|
||||
SCALE_FACTOR_ZERO = 0,
|
||||
SCALE_FACTOR_NORMAL = 64,
|
||||
SCALE_FACTOR_MAX = 128,
|
||||
SCALE_FACTOR_NONE = 255
|
||||
};
|
||||
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
inline Value apply_scale_factor(Value v, ScaleFactor f) {
|
||||
return Value((v * f) / int(SCALE_FACTOR_NORMAL));
|
||||
}
|
||||
|
||||
|
||||
#endif // !defined(SCALE_H_INCLUDED)
|
||||
+2799
-1686
File diff suppressed because it is too large
Load Diff
+46
-86
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,101 +17,60 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SEARCH_H_INCLUDED
|
||||
|
||||
#if !defined(SEARCH_H_INCLUDED)
|
||||
#define SEARCH_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "misc.h"
|
||||
#include "movepick.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
class Position;
|
||||
|
||||
namespace Search {
|
||||
|
||||
/// Threshold used for countermoves based pruning
|
||||
constexpr int CounterMovePruneThreshold = 0;
|
||||
#include "depth.h"
|
||||
#include "move.h"
|
||||
|
||||
|
||||
/// Stack struct keeps track of the information we need to remember from nodes
|
||||
/// shallower and deeper in the tree during the search. Each search thread has
|
||||
/// its own array of Stack objects, indexed by the current ply.
|
||||
////
|
||||
//// Constants
|
||||
////
|
||||
|
||||
struct Stack {
|
||||
Move* pv;
|
||||
PieceToHistory* continuationHistory;
|
||||
int ply;
|
||||
const int PLY_MAX = 100;
|
||||
const int PLY_MAX_PLUS_2 = 102;
|
||||
const int KILLER_MAX = 2;
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
/// The SearchStack struct keeps track of the information we need to remember
|
||||
/// from nodes shallower and deeper in the tree during the search. Each
|
||||
/// search thread has its own array of SearchStack objects, indexed by the
|
||||
/// current ply.
|
||||
|
||||
struct SearchStack {
|
||||
Move pv[PLY_MAX_PLUS_2];
|
||||
Move currentMove;
|
||||
Move excludedMove;
|
||||
Move killers[2];
|
||||
Value staticEval;
|
||||
Depth depth;
|
||||
int statScore;
|
||||
int moveCount;
|
||||
bool inCheck;
|
||||
bool ttPv;
|
||||
bool ttHit;
|
||||
int doubleExtensions;
|
||||
Move mateKiller;
|
||||
Move threatMove;
|
||||
Move killers[KILLER_MAX];
|
||||
Depth reduction;
|
||||
|
||||
void init(int ply);
|
||||
void initKillers();
|
||||
};
|
||||
|
||||
|
||||
/// RootMove struct is used for moves at the root of the tree. For each root move
|
||||
/// we store a score and a PV (really a refutation in the case of moves which
|
||||
/// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves.
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
struct RootMove {
|
||||
|
||||
explicit RootMove(Move m) : pv(1, m) {}
|
||||
bool extract_ponder_from_tt(Position& pos);
|
||||
bool operator==(const Move& m) const { return pv[0] == m; }
|
||||
bool operator<(const RootMove& m) const { // Sort in descending order
|
||||
return m.score != score ? m.score < score
|
||||
: m.previousScore < previousScore;
|
||||
}
|
||||
|
||||
Value score = -VALUE_INFINITE;
|
||||
Value previousScore = -VALUE_INFINITE;
|
||||
Value averageScore = -VALUE_INFINITE;
|
||||
int selDepth = 0;
|
||||
int tbRank = 0;
|
||||
Value tbScore;
|
||||
std::vector<Move> pv;
|
||||
};
|
||||
|
||||
typedef std::vector<RootMove> RootMoves;
|
||||
extern void init_threads();
|
||||
extern void stop_threads();
|
||||
extern bool think(const Position &pos, bool infinite, bool ponder, int side_to_move,
|
||||
int time[], int increment[], int movesToGo, int maxDepth,
|
||||
int maxNodes, int maxTime, Move searchMoves[]);
|
||||
extern int perft(Position &pos, Depth depth);
|
||||
extern int64_t nodes_searched();
|
||||
|
||||
|
||||
/// LimitsType struct stores information sent by GUI about available time to
|
||||
/// search the current move, maximum depth/time, or if we are in analysis mode.
|
||||
|
||||
struct LimitsType {
|
||||
|
||||
LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC
|
||||
time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0);
|
||||
movestogo = depth = mate = perft = infinite = 0;
|
||||
nodes = 0;
|
||||
}
|
||||
|
||||
bool use_time_management() const {
|
||||
return time[WHITE] || time[BLACK];
|
||||
}
|
||||
|
||||
std::vector<Move> searchmoves;
|
||||
TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime;
|
||||
int movestogo, depth, mate, perft, infinite;
|
||||
int64_t nodes;
|
||||
};
|
||||
|
||||
extern LimitsType Limits;
|
||||
|
||||
void init();
|
||||
void clear();
|
||||
|
||||
} // namespace Search
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef SEARCH_H_INCLUDED
|
||||
#endif // !defined(SEARCH_H_INCLUDED)
|
||||
|
||||
-387
@@ -1,387 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef STOCKFISH_SIMD_H_INCLUDED
|
||||
#define STOCKFISH_SIMD_H_INCLUDED
|
||||
|
||||
#if defined(USE_AVX2)
|
||||
# include <immintrin.h>
|
||||
|
||||
#elif defined(USE_SSE41)
|
||||
# include <smmintrin.h>
|
||||
|
||||
#elif defined(USE_SSSE3)
|
||||
# include <tmmintrin.h>
|
||||
|
||||
#elif defined(USE_SSE2)
|
||||
# include <emmintrin.h>
|
||||
|
||||
#elif defined(USE_MMX)
|
||||
# include <mmintrin.h>
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
# include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
// The inline asm is only safe for GCC, where it is necessary to get good codegen.
|
||||
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101693
|
||||
// Clang does fine without it.
|
||||
// Play around here: https://godbolt.org/z/7EWqrYq51
|
||||
#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER))
|
||||
#define USE_INLINE_ASM
|
||||
#endif
|
||||
|
||||
// Use either the AVX512 or AVX-VNNI version of the VNNI instructions.
|
||||
#if defined(USE_AVXVNNI)
|
||||
#define VNNI_PREFIX "%{vex%} "
|
||||
#else
|
||||
#define VNNI_PREFIX ""
|
||||
#endif
|
||||
|
||||
namespace Stockfish::Simd {
|
||||
|
||||
#if defined (USE_AVX512)
|
||||
|
||||
[[maybe_unused]] static int m512_hadd(__m512i sum, int bias) {
|
||||
return _mm512_reduce_add_epi32(sum) + bias;
|
||||
}
|
||||
|
||||
/*
|
||||
Parameters:
|
||||
sum0 = [zmm0.i128[0], zmm0.i128[1], zmm0.i128[2], zmm0.i128[3]]
|
||||
sum1 = [zmm1.i128[0], zmm1.i128[1], zmm1.i128[2], zmm1.i128[3]]
|
||||
sum2 = [zmm2.i128[0], zmm2.i128[1], zmm2.i128[2], zmm2.i128[3]]
|
||||
sum3 = [zmm3.i128[0], zmm3.i128[1], zmm3.i128[2], zmm3.i128[3]]
|
||||
|
||||
Returns:
|
||||
ret = [
|
||||
reduce_add_epi32(zmm0.i128[0]), reduce_add_epi32(zmm1.i128[0]), reduce_add_epi32(zmm2.i128[0]), reduce_add_epi32(zmm3.i128[0]),
|
||||
reduce_add_epi32(zmm0.i128[1]), reduce_add_epi32(zmm1.i128[1]), reduce_add_epi32(zmm2.i128[1]), reduce_add_epi32(zmm3.i128[1]),
|
||||
reduce_add_epi32(zmm0.i128[2]), reduce_add_epi32(zmm1.i128[2]), reduce_add_epi32(zmm2.i128[2]), reduce_add_epi32(zmm3.i128[2]),
|
||||
reduce_add_epi32(zmm0.i128[3]), reduce_add_epi32(zmm1.i128[3]), reduce_add_epi32(zmm2.i128[3]), reduce_add_epi32(zmm3.i128[3])
|
||||
]
|
||||
*/
|
||||
[[maybe_unused]] static __m512i m512_hadd128x16_interleave(
|
||||
__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) {
|
||||
|
||||
__m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1);
|
||||
__m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1);
|
||||
|
||||
__m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3);
|
||||
__m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3);
|
||||
|
||||
__m512i sum01 = _mm512_add_epi32(sum01a, sum01b);
|
||||
__m512i sum23 = _mm512_add_epi32(sum23a, sum23b);
|
||||
|
||||
__m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23);
|
||||
__m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23);
|
||||
|
||||
return _mm512_add_epi32(sum0123a, sum0123b);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static __m128i m512_haddx4(
|
||||
__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3,
|
||||
__m128i bias) {
|
||||
|
||||
__m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3);
|
||||
|
||||
__m256i sum256lo = _mm512_castsi512_si256(sum);
|
||||
__m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1);
|
||||
|
||||
sum256lo = _mm256_add_epi32(sum256lo, sum256hi);
|
||||
|
||||
__m128i sum128lo = _mm256_castsi256_si128(sum256lo);
|
||||
__m128i sum128hi = _mm256_extracti128_si256(sum256lo, 1);
|
||||
|
||||
return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void m512_add_dpbusd_epi32(
|
||||
__m512i& acc,
|
||||
__m512i a,
|
||||
__m512i b) {
|
||||
|
||||
# if defined (USE_VNNI)
|
||||
# if defined (USE_INLINE_ASM)
|
||||
asm(
|
||||
"vpdpbusd %[b], %[a], %[acc]\n\t"
|
||||
: [acc]"+v"(acc)
|
||||
: [a]"v"(a), [b]"vm"(b)
|
||||
);
|
||||
# else
|
||||
acc = _mm512_dpbusd_epi32(acc, a, b);
|
||||
# endif
|
||||
# else
|
||||
# if defined (USE_INLINE_ASM)
|
||||
__m512i tmp = _mm512_maddubs_epi16(a, b);
|
||||
asm(
|
||||
"vpmaddwd %[tmp], %[ones], %[tmp]\n\t"
|
||||
"vpaddd %[acc], %[tmp], %[acc]\n\t"
|
||||
: [acc]"+v"(acc), [tmp]"+&v"(tmp)
|
||||
: [ones]"v"(_mm512_set1_epi16(1))
|
||||
);
|
||||
# else
|
||||
__m512i product0 = _mm512_maddubs_epi16(a, b);
|
||||
product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1));
|
||||
acc = _mm512_add_epi32(acc, product0);
|
||||
# endif
|
||||
# endif
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void m512_add_dpbusd_epi32x2(
|
||||
__m512i& acc,
|
||||
__m512i a0, __m512i b0,
|
||||
__m512i a1, __m512i b1) {
|
||||
|
||||
# if defined (USE_VNNI)
|
||||
# if defined (USE_INLINE_ASM)
|
||||
asm(
|
||||
"vpdpbusd %[b0], %[a0], %[acc]\n\t"
|
||||
"vpdpbusd %[b1], %[a1], %[acc]\n\t"
|
||||
: [acc]"+v"(acc)
|
||||
: [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1)
|
||||
);
|
||||
# else
|
||||
acc = _mm512_dpbusd_epi32(acc, a0, b0);
|
||||
acc = _mm512_dpbusd_epi32(acc, a1, b1);
|
||||
# endif
|
||||
# else
|
||||
# if defined (USE_INLINE_ASM)
|
||||
__m512i tmp0 = _mm512_maddubs_epi16(a0, b0);
|
||||
__m512i tmp1 = _mm512_maddubs_epi16(a1, b1);
|
||||
asm(
|
||||
"vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t"
|
||||
"vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t"
|
||||
"vpaddd %[acc], %[tmp0], %[acc]\n\t"
|
||||
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0)
|
||||
: [tmp1]"v"(tmp1), [ones]"v"(_mm512_set1_epi16(1))
|
||||
);
|
||||
# else
|
||||
__m512i product0 = _mm512_maddubs_epi16(a0, b0);
|
||||
__m512i product1 = _mm512_maddubs_epi16(a1, b1);
|
||||
product0 = _mm512_adds_epi16(product0, product1);
|
||||
product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1));
|
||||
acc = _mm512_add_epi32(acc, product0);
|
||||
# endif
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (USE_AVX2)
|
||||
|
||||
[[maybe_unused]] static int m256_hadd(__m256i sum, int bias) {
|
||||
__m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1));
|
||||
sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC));
|
||||
sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB));
|
||||
return _mm_cvtsi128_si32(sum128) + bias;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static __m128i m256_haddx4(
|
||||
__m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3,
|
||||
__m128i bias) {
|
||||
|
||||
sum0 = _mm256_hadd_epi32(sum0, sum1);
|
||||
sum2 = _mm256_hadd_epi32(sum2, sum3);
|
||||
|
||||
sum0 = _mm256_hadd_epi32(sum0, sum2);
|
||||
|
||||
__m128i sum128lo = _mm256_castsi256_si128(sum0);
|
||||
__m128i sum128hi = _mm256_extracti128_si256(sum0, 1);
|
||||
|
||||
return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void m256_add_dpbusd_epi32(
|
||||
__m256i& acc,
|
||||
__m256i a,
|
||||
__m256i b) {
|
||||
|
||||
# if defined (USE_VNNI)
|
||||
# if defined (USE_INLINE_ASM)
|
||||
asm(
|
||||
VNNI_PREFIX "vpdpbusd %[b], %[a], %[acc]\n\t"
|
||||
: [acc]"+v"(acc)
|
||||
: [a]"v"(a), [b]"vm"(b)
|
||||
);
|
||||
# else
|
||||
acc = _mm256_dpbusd_epi32(acc, a, b);
|
||||
# endif
|
||||
# else
|
||||
# if defined (USE_INLINE_ASM)
|
||||
__m256i tmp = _mm256_maddubs_epi16(a, b);
|
||||
asm(
|
||||
"vpmaddwd %[tmp], %[ones], %[tmp]\n\t"
|
||||
"vpaddd %[acc], %[tmp], %[acc]\n\t"
|
||||
: [acc]"+v"(acc), [tmp]"+&v"(tmp)
|
||||
: [ones]"v"(_mm256_set1_epi16(1))
|
||||
);
|
||||
# else
|
||||
__m256i product0 = _mm256_maddubs_epi16(a, b);
|
||||
product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1));
|
||||
acc = _mm256_add_epi32(acc, product0);
|
||||
# endif
|
||||
# endif
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void m256_add_dpbusd_epi32x2(
|
||||
__m256i& acc,
|
||||
__m256i a0, __m256i b0,
|
||||
__m256i a1, __m256i b1) {
|
||||
|
||||
# if defined (USE_VNNI)
|
||||
# if defined (USE_INLINE_ASM)
|
||||
asm(
|
||||
VNNI_PREFIX "vpdpbusd %[b0], %[a0], %[acc]\n\t"
|
||||
VNNI_PREFIX "vpdpbusd %[b1], %[a1], %[acc]\n\t"
|
||||
: [acc]"+v"(acc)
|
||||
: [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1)
|
||||
);
|
||||
# else
|
||||
acc = _mm256_dpbusd_epi32(acc, a0, b0);
|
||||
acc = _mm256_dpbusd_epi32(acc, a1, b1);
|
||||
# endif
|
||||
# else
|
||||
# if defined (USE_INLINE_ASM)
|
||||
__m256i tmp0 = _mm256_maddubs_epi16(a0, b0);
|
||||
__m256i tmp1 = _mm256_maddubs_epi16(a1, b1);
|
||||
asm(
|
||||
"vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t"
|
||||
"vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t"
|
||||
"vpaddd %[acc], %[tmp0], %[acc]\n\t"
|
||||
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0)
|
||||
: [tmp1]"v"(tmp1), [ones]"v"(_mm256_set1_epi16(1))
|
||||
);
|
||||
# else
|
||||
__m256i product0 = _mm256_maddubs_epi16(a0, b0);
|
||||
__m256i product1 = _mm256_maddubs_epi16(a1, b1);
|
||||
product0 = _mm256_adds_epi16(product0, product1);
|
||||
product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1));
|
||||
acc = _mm256_add_epi32(acc, product0);
|
||||
# endif
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (USE_SSSE3)
|
||||
|
||||
[[maybe_unused]] static int m128_hadd(__m128i sum, int bias) {
|
||||
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC
|
||||
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB
|
||||
return _mm_cvtsi128_si32(sum) + bias;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static __m128i m128_haddx4(
|
||||
__m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3,
|
||||
__m128i bias) {
|
||||
|
||||
sum0 = _mm_hadd_epi32(sum0, sum1);
|
||||
sum2 = _mm_hadd_epi32(sum2, sum3);
|
||||
sum0 = _mm_hadd_epi32(sum0, sum2);
|
||||
return _mm_add_epi32(sum0, bias);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void m128_add_dpbusd_epi32(
|
||||
__m128i& acc,
|
||||
__m128i a,
|
||||
__m128i b) {
|
||||
|
||||
# if defined (USE_INLINE_ASM)
|
||||
__m128i tmp = _mm_maddubs_epi16(a, b);
|
||||
asm(
|
||||
"pmaddwd %[ones], %[tmp]\n\t"
|
||||
"paddd %[tmp], %[acc]\n\t"
|
||||
: [acc]"+v"(acc), [tmp]"+&v"(tmp)
|
||||
: [ones]"v"(_mm_set1_epi16(1))
|
||||
);
|
||||
# else
|
||||
__m128i product0 = _mm_maddubs_epi16(a, b);
|
||||
product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1));
|
||||
acc = _mm_add_epi32(acc, product0);
|
||||
# endif
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void m128_add_dpbusd_epi32x2(
|
||||
__m128i& acc,
|
||||
__m128i a0, __m128i b0,
|
||||
__m128i a1, __m128i b1) {
|
||||
|
||||
# if defined (USE_INLINE_ASM)
|
||||
__m128i tmp0 = _mm_maddubs_epi16(a0, b0);
|
||||
__m128i tmp1 = _mm_maddubs_epi16(a1, b1);
|
||||
asm(
|
||||
"paddsw %[tmp1], %[tmp0]\n\t"
|
||||
"pmaddwd %[ones], %[tmp0]\n\t"
|
||||
"paddd %[tmp0], %[acc]\n\t"
|
||||
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0)
|
||||
: [tmp1]"v"(tmp1), [ones]"v"(_mm_set1_epi16(1))
|
||||
);
|
||||
# else
|
||||
__m128i product0 = _mm_maddubs_epi16(a0, b0);
|
||||
__m128i product1 = _mm_maddubs_epi16(a1, b1);
|
||||
product0 = _mm_adds_epi16(product0, product1);
|
||||
product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1));
|
||||
acc = _mm_add_epi32(acc, product0);
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (USE_NEON)
|
||||
|
||||
[[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) {
|
||||
# if USE_NEON >= 8
|
||||
return vaddvq_s32(s);
|
||||
# else
|
||||
return s[0] + s[1] + s[2] + s[3];
|
||||
# endif
|
||||
}
|
||||
|
||||
[[maybe_unused]] static int neon_m128_hadd(int32x4_t sum, int bias) {
|
||||
return neon_m128_reduce_add_epi32(sum) + bias;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static int32x4_t neon_m128_haddx4(
|
||||
int32x4_t sum0, int32x4_t sum1, int32x4_t sum2, int32x4_t sum3,
|
||||
int32x4_t bias) {
|
||||
|
||||
int32x4_t hsums {
|
||||
neon_m128_reduce_add_epi32(sum0),
|
||||
neon_m128_reduce_add_epi32(sum1),
|
||||
neon_m128_reduce_add_epi32(sum2),
|
||||
neon_m128_reduce_add_epi32(sum3)
|
||||
};
|
||||
return vaddq_s32(hsums, bias);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void neon_m128_add_dpbusd_epi32x2(
|
||||
int32x4_t& acc,
|
||||
int8x8_t a0, int8x8_t b0,
|
||||
int8x8_t a1, int8x8_t b1) {
|
||||
|
||||
int16x8_t product = vmull_s8(a0, b0);
|
||||
product = vmlal_s8(product, a1, b1);
|
||||
acc = vpadalq_s16(acc, product);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif // STOCKFISH_SIMD_H_INCLUDED
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(SQUARE_H_INCLUDED)
|
||||
#define SQUARE_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <cstdlib> // for abs()
|
||||
#include <string>
|
||||
|
||||
#include "color.h"
|
||||
#include "misc.h"
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
enum Square {
|
||||
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
|
||||
};
|
||||
|
||||
enum File {
|
||||
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NONE
|
||||
};
|
||||
|
||||
enum Rank {
|
||||
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NONE
|
||||
};
|
||||
|
||||
enum SquareDelta {
|
||||
DELTA_SSW = -021, DELTA_SS = -020, DELTA_SSE = -017, DELTA_SWW = -012,
|
||||
DELTA_SW = -011, DELTA_S = -010, DELTA_SE = -07, DELTA_SEE = -06,
|
||||
DELTA_W = -01, DELTA_ZERO = 0, DELTA_E = 01, DELTA_NWW = 06, DELTA_NW = 07,
|
||||
DELTA_N = 010, DELTA_NE = 011, DELTA_NEE = 012, DELTA_NNW = 017,
|
||||
DELTA_NN = 020, DELTA_NNE = 021
|
||||
};
|
||||
|
||||
|
||||
////
|
||||
//// Constants
|
||||
////
|
||||
|
||||
const int FlipMask = 070;
|
||||
const int FlopMask = 07;
|
||||
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
inline File operator+ (File x, int i) { return File(int(x) + i); }
|
||||
inline File operator+ (File x, File y) { return x + int(y); }
|
||||
inline void operator++ (File &x, int) { x = File(int(x) + 1); }
|
||||
inline void operator+= (File &x, int i) { x = File(int(x) + i); }
|
||||
inline File operator- (File x, int i) { return File(int(x) - i); }
|
||||
inline void operator-- (File &x, int) { x = File(int(x) - 1); }
|
||||
inline void operator-= (File &x, int i) { x = File(int(x) - i); }
|
||||
|
||||
inline Rank operator+ (Rank x, int i) { return Rank(int(x) + i); }
|
||||
inline Rank operator+ (Rank x, Rank y) { return x + int(y); }
|
||||
inline void operator++ (Rank &x, int) { x = Rank(int(x) + 1); }
|
||||
inline void operator+= (Rank &x, int i) { x = Rank(int(x) + i); }
|
||||
inline Rank operator- (Rank x, int i) { return Rank(int(x) - i); }
|
||||
inline void operator-- (Rank &x, int) { x = Rank(int(x) - 1); }
|
||||
inline void operator-= (Rank &x, int i) { x = Rank(int(x) - i); }
|
||||
|
||||
inline Square operator+ (Square x, int i) { return Square(int(x) + i); }
|
||||
inline void operator++ (Square &x, int) { x = Square(int(x) + 1); }
|
||||
inline void operator+= (Square &x, int i) { x = Square(int(x) + i); }
|
||||
inline Square operator- (Square x, int i) { return Square(int(x) - i); }
|
||||
inline void operator-- (Square &x, int) { x = Square(int(x) - 1); }
|
||||
inline void operator-= (Square &x, int i) { x = Square(int(x) - i); }
|
||||
inline Square operator+ (Square x, SquareDelta i) { return Square(int(x) + i); }
|
||||
inline void operator+= (Square &x, SquareDelta i) { x = Square(int(x) + i); }
|
||||
inline Square operator- (Square x, SquareDelta i) { return Square(int(x) - i); }
|
||||
inline void operator-= (Square &x, SquareDelta i) { x = Square(int(x) - i); }
|
||||
inline SquareDelta operator- (Square x, Square y) {
|
||||
return SquareDelta(int(x) - int(y));
|
||||
}
|
||||
|
||||
inline Square make_square(File f, Rank r) {
|
||||
return Square(int(f) | (int(r) << 3));
|
||||
}
|
||||
|
||||
inline File square_file(Square s) {
|
||||
return File(int(s) & 7);
|
||||
}
|
||||
|
||||
inline Rank square_rank(Square s) {
|
||||
return Rank(int(s) >> 3);
|
||||
}
|
||||
|
||||
inline Square flip_square(Square s) {
|
||||
return Square(int(s) ^ FlipMask);
|
||||
}
|
||||
|
||||
inline Square flop_square(Square s) {
|
||||
return Square(int(s) ^ FlopMask);
|
||||
}
|
||||
|
||||
inline Square relative_square(Color c, Square s) {
|
||||
return Square(int(s) ^ (int(c) * FlipMask));
|
||||
}
|
||||
|
||||
inline Rank relative_rank(Color c, Square s) {
|
||||
return square_rank(relative_square(c, s));
|
||||
}
|
||||
|
||||
inline Color square_color(Square s) {
|
||||
return Color((int(square_file(s)) + int(square_rank(s))) & 1);
|
||||
}
|
||||
|
||||
inline int file_distance(File f1, File f2) {
|
||||
return abs(int(f1) - int(f2));
|
||||
}
|
||||
|
||||
inline int file_distance(Square s1, Square s2) {
|
||||
return file_distance(square_file(s1), square_file(s2));
|
||||
}
|
||||
|
||||
inline int rank_distance(Rank r1, Rank r2) {
|
||||
return abs(int(r1) - int(r2));
|
||||
}
|
||||
|
||||
inline int rank_distance(Square s1, Square s2) {
|
||||
return rank_distance(square_rank(s1), square_rank(s2));
|
||||
}
|
||||
|
||||
inline int square_distance(Square s1, Square s2) {
|
||||
return Max(file_distance(s1, s2), rank_distance(s1, s2));
|
||||
}
|
||||
|
||||
inline File file_from_char(char c) {
|
||||
return File(c - 'a') + FILE_A;
|
||||
}
|
||||
|
||||
inline char file_to_char(File f) {
|
||||
return char(f - FILE_A + int('a'));
|
||||
}
|
||||
|
||||
inline Rank rank_from_char(char c) {
|
||||
return Rank(c - '1') + RANK_1;
|
||||
}
|
||||
|
||||
inline char rank_to_char(Rank r) {
|
||||
return char(r - RANK_1 + int('1'));
|
||||
}
|
||||
|
||||
inline Square square_from_string(const std::string& str) {
|
||||
return make_square(file_from_char(str[0]), rank_from_char(str[1]));
|
||||
}
|
||||
|
||||
inline const std::string square_to_string(Square s) {
|
||||
std::string str;
|
||||
str += file_to_char(square_file(s));
|
||||
str += rank_to_char(square_rank(s));
|
||||
return str;
|
||||
}
|
||||
|
||||
inline bool file_is_ok(File f) {
|
||||
return f >= FILE_A && f <= FILE_H;
|
||||
}
|
||||
|
||||
inline bool rank_is_ok(Rank r) {
|
||||
return r >= RANK_1 && r <= RANK_8;
|
||||
}
|
||||
|
||||
inline bool square_is_ok(Square s) {
|
||||
return file_is_ok(square_file(s)) && rank_is_ok(square_rank(s));
|
||||
}
|
||||
|
||||
#endif // !defined(SQUARE_H_INCLUDED)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TBPROBE_H
|
||||
#define TBPROBE_H
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "../search.h"
|
||||
|
||||
namespace Stockfish::Tablebases {
|
||||
|
||||
enum WDLScore {
|
||||
WDLLoss = -2, // Loss
|
||||
WDLBlessedLoss = -1, // Loss, but draw under 50-move rule
|
||||
WDLDraw = 0, // Draw
|
||||
WDLCursedWin = 1, // Win, but draw under 50-move rule
|
||||
WDLWin = 2, // Win
|
||||
|
||||
WDLScoreNone = -1000
|
||||
};
|
||||
|
||||
// Possible states after a probing operation
|
||||
enum ProbeState {
|
||||
FAIL = 0, // Probe failed (missing file table)
|
||||
OK = 1, // Probe successful
|
||||
CHANGE_STM = -1, // DTZ should check the other side
|
||||
ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move)
|
||||
};
|
||||
|
||||
extern int MaxCardinality;
|
||||
|
||||
void init(const std::string& paths);
|
||||
WDLScore probe_wdl(Position& pos, ProbeState* result);
|
||||
int probe_dtz(Position& pos, ProbeState* result);
|
||||
bool root_probe(Position& pos, Search::RootMoves& rootMoves);
|
||||
bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves);
|
||||
void rank_root_moves(Position& pos, Search::RootMoves& rootMoves);
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const WDLScore v) {
|
||||
|
||||
os << (v == WDLLoss ? "Loss" :
|
||||
v == WDLBlessedLoss ? "Blessed loss" :
|
||||
v == WDLDraw ? "Draw" :
|
||||
v == WDLCursedWin ? "Cursed win" :
|
||||
v == WDLWin ? "Win" : "None");
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const ProbeState v) {
|
||||
|
||||
os << (v == FAIL ? "Failed" :
|
||||
v == OK ? "Success" :
|
||||
v == CHANGE_STM ? "Probed opponent side" :
|
||||
v == ZEROING_BEST_MOVE ? "Best move zeroes DTZ" : "None");
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace Stockfish::Tablebases
|
||||
|
||||
#endif
|
||||
-266
@@ -1,266 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <algorithm> // For std::count
|
||||
#include "movegen.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "uci.h"
|
||||
#include "syzygy/tbprobe.h"
|
||||
#include "tt.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
ThreadPool Threads; // Global object
|
||||
|
||||
|
||||
/// Thread constructor launches the thread and waits until it goes to sleep
|
||||
/// in idle_loop(). Note that 'searching' and 'exit' should be already set.
|
||||
|
||||
Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) {
|
||||
|
||||
wait_for_search_finished();
|
||||
}
|
||||
|
||||
|
||||
/// Thread destructor wakes up the thread in idle_loop() and waits
|
||||
/// for its termination. Thread should be already waiting.
|
||||
|
||||
Thread::~Thread() {
|
||||
|
||||
assert(!searching);
|
||||
|
||||
exit = true;
|
||||
start_searching();
|
||||
stdThread.join();
|
||||
}
|
||||
|
||||
|
||||
/// Thread::clear() reset histories, usually before a new game
|
||||
|
||||
void Thread::clear() {
|
||||
|
||||
counterMoves.fill(MOVE_NONE);
|
||||
mainHistory.fill(0);
|
||||
captureHistory.fill(0);
|
||||
|
||||
for (bool inCheck : { false, true })
|
||||
for (StatsType c : { NoCaptures, Captures })
|
||||
{
|
||||
for (auto& to : continuationHistory[inCheck][c])
|
||||
for (auto& h : to)
|
||||
h->fill(-71);
|
||||
continuationHistory[inCheck][c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Thread::start_searching() wakes up the thread that will start the search
|
||||
|
||||
void Thread::start_searching() {
|
||||
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
searching = true;
|
||||
cv.notify_one(); // Wake up the thread in idle_loop()
|
||||
}
|
||||
|
||||
|
||||
/// Thread::wait_for_search_finished() blocks on the condition variable
|
||||
/// until the thread has finished searching.
|
||||
|
||||
void Thread::wait_for_search_finished() {
|
||||
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
cv.wait(lk, [&]{ return !searching; });
|
||||
}
|
||||
|
||||
|
||||
/// Thread::idle_loop() is where the thread is parked, blocked on the
|
||||
/// condition variable, when it has no work to do.
|
||||
|
||||
void Thread::idle_loop() {
|
||||
|
||||
// If OS already scheduled us on a different group than 0 then don't overwrite
|
||||
// the choice, eventually we are one of many one-threaded processes running on
|
||||
// some Windows NUMA hardware, for instance in fishtest. To make it simple,
|
||||
// just check if running threads are below a threshold, in this case all this
|
||||
// NUMA machinery is not needed.
|
||||
if (Options["Threads"] > 8)
|
||||
WinProcGroup::bindThisThread(idx);
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
searching = false;
|
||||
cv.notify_one(); // Wake up anyone waiting for search finished
|
||||
cv.wait(lk, [&]{ return searching; });
|
||||
|
||||
if (exit)
|
||||
return;
|
||||
|
||||
lk.unlock();
|
||||
|
||||
search();
|
||||
}
|
||||
}
|
||||
|
||||
/// ThreadPool::set() creates/destroys threads to match the requested number.
|
||||
/// Created and launched threads will immediately go to sleep in idle_loop.
|
||||
/// Upon resizing, threads are recreated to allow for binding if necessary.
|
||||
|
||||
void ThreadPool::set(size_t requested) {
|
||||
|
||||
if (size() > 0) // destroy any existing thread(s)
|
||||
{
|
||||
main()->wait_for_search_finished();
|
||||
|
||||
while (size() > 0)
|
||||
delete back(), pop_back();
|
||||
}
|
||||
|
||||
if (requested > 0) // create new thread(s)
|
||||
{
|
||||
push_back(new MainThread(0));
|
||||
|
||||
while (size() < requested)
|
||||
push_back(new Thread(size()));
|
||||
clear();
|
||||
|
||||
// Reallocate the hash with the new threadpool size
|
||||
TT.resize(size_t(Options["Hash"]));
|
||||
|
||||
// Init thread number dependent search params.
|
||||
Search::init();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ThreadPool::clear() sets threadPool data to initial values
|
||||
|
||||
void ThreadPool::clear() {
|
||||
|
||||
for (Thread* th : *this)
|
||||
th->clear();
|
||||
|
||||
main()->callsCnt = 0;
|
||||
main()->bestPreviousScore = VALUE_INFINITE;
|
||||
main()->bestPreviousAverageScore = VALUE_INFINITE;
|
||||
main()->previousTimeReduction = 1.0;
|
||||
}
|
||||
|
||||
|
||||
/// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and
|
||||
/// returns immediately. Main thread will wake up other threads and start the search.
|
||||
|
||||
void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
|
||||
const Search::LimitsType& limits, bool ponderMode) {
|
||||
|
||||
main()->wait_for_search_finished();
|
||||
|
||||
main()->stopOnPonderhit = stop = false;
|
||||
increaseDepth = true;
|
||||
main()->ponder = ponderMode;
|
||||
Search::Limits = limits;
|
||||
Search::RootMoves rootMoves;
|
||||
|
||||
for (const auto& m : MoveList<LEGAL>(pos))
|
||||
if ( limits.searchmoves.empty()
|
||||
|| std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
|
||||
rootMoves.emplace_back(m);
|
||||
|
||||
if (!rootMoves.empty())
|
||||
Tablebases::rank_root_moves(pos, rootMoves);
|
||||
|
||||
// After ownership transfer 'states' becomes empty, so if we stop the search
|
||||
// and call 'go' again without setting a new position states.get() == NULL.
|
||||
assert(states.get() || setupStates.get());
|
||||
|
||||
if (states.get())
|
||||
setupStates = std::move(states); // Ownership transfer, states is now empty
|
||||
|
||||
// We use Position::set() to set root position across threads. But there are
|
||||
// some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot
|
||||
// be deduced from a fen string, so set() clears them and they are set from
|
||||
// setupStates->back() later. The rootState is per thread, earlier states are shared
|
||||
// since they are read-only.
|
||||
for (Thread* th : *this)
|
||||
{
|
||||
th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0;
|
||||
th->rootDepth = th->completedDepth = 0;
|
||||
th->rootMoves = rootMoves;
|
||||
th->rootPos.set(pos.fen(), pos.is_chess960(), &th->rootState, th);
|
||||
th->rootState = setupStates->back();
|
||||
}
|
||||
|
||||
main()->start_searching();
|
||||
}
|
||||
|
||||
Thread* ThreadPool::get_best_thread() const {
|
||||
|
||||
Thread* bestThread = front();
|
||||
std::map<Move, int64_t> votes;
|
||||
Value minScore = VALUE_NONE;
|
||||
|
||||
// Find minimum score of all threads
|
||||
for (Thread* th: *this)
|
||||
minScore = std::min(minScore, th->rootMoves[0].score);
|
||||
|
||||
// Vote according to score and depth, and select the best thread
|
||||
for (Thread* th : *this)
|
||||
{
|
||||
votes[th->rootMoves[0].pv[0]] +=
|
||||
(th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
|
||||
|
||||
if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY)
|
||||
{
|
||||
// Make sure we pick the shortest mate / TB conversion or stave off mate the longest
|
||||
if (th->rootMoves[0].score > bestThread->rootMoves[0].score)
|
||||
bestThread = th;
|
||||
}
|
||||
else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY
|
||||
|| ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY
|
||||
&& votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]))
|
||||
bestThread = th;
|
||||
}
|
||||
|
||||
return bestThread;
|
||||
}
|
||||
|
||||
|
||||
/// Start non-main threads
|
||||
|
||||
void ThreadPool::start_searching() {
|
||||
|
||||
for (Thread* th : *this)
|
||||
if (th != front())
|
||||
th->start_searching();
|
||||
}
|
||||
|
||||
|
||||
/// Wait for non-main threads
|
||||
|
||||
void ThreadPool::wait_for_search_finished() const {
|
||||
|
||||
for (Thread* th : *this)
|
||||
if (th != front())
|
||||
th->wait_for_search_finished();
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
+45
-101
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,121 +17,64 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef THREAD_H_INCLUDED
|
||||
|
||||
#if !defined(THREAD_H_INCLUDED)
|
||||
#define THREAD_H_INCLUDED
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "material.h"
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "lock.h"
|
||||
#include "movepick.h"
|
||||
#include "pawns.h"
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
#include "thread_win32_osx.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
/// Thread class keeps together all the thread-related stuff. We use
|
||||
/// per-thread pawn and material hash tables so that once we get a
|
||||
/// pointer to an entry its life time is unlimited and we don't have
|
||||
/// to care about someone changing the entry under our feet.
|
||||
////
|
||||
//// Constants and variables
|
||||
////
|
||||
|
||||
class Thread {
|
||||
const int THREAD_MAX = 8;
|
||||
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
size_t idx;
|
||||
bool exit = false, searching = true; // Set before starting std::thread
|
||||
NativeThread stdThread;
|
||||
|
||||
public:
|
||||
explicit Thread(size_t);
|
||||
virtual ~Thread();
|
||||
virtual void search();
|
||||
void clear();
|
||||
void idle_loop();
|
||||
void start_searching();
|
||||
void wait_for_search_finished();
|
||||
size_t id() const { return idx; }
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
Pawns::Table pawnsTable;
|
||||
Material::Table materialTable;
|
||||
size_t pvIdx, pvLast;
|
||||
RunningAverage complexityAverage;
|
||||
std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
|
||||
int selDepth, nmpMinPly;
|
||||
Color nmpColor;
|
||||
Value bestValue, optimism[COLOR_NB];
|
||||
|
||||
Position rootPos;
|
||||
StateInfo rootState;
|
||||
Search::RootMoves rootMoves;
|
||||
Depth rootDepth, completedDepth, depth;
|
||||
Value rootDelta;
|
||||
CounterMoveHistory counterMoves;
|
||||
ButterflyHistory mainHistory;
|
||||
CapturePieceToHistory captureHistory;
|
||||
ContinuationHistory continuationHistory[2][2];
|
||||
Score trend;
|
||||
struct SplitPoint {
|
||||
SplitPoint *parent;
|
||||
Position pos;
|
||||
SearchStack sstack[THREAD_MAX][PLY_MAX_PLUS_2];
|
||||
SearchStack *parentSstack;
|
||||
int ply;
|
||||
Depth depth;
|
||||
volatile Value alpha, beta, bestValue, futilityValue;
|
||||
Value approximateEval;
|
||||
bool pvNode;
|
||||
int master, slaves[THREAD_MAX];
|
||||
Lock lock;
|
||||
MovePicker *mp;
|
||||
volatile int moves;
|
||||
volatile int cpus;
|
||||
bool finished;
|
||||
};
|
||||
|
||||
|
||||
/// MainThread is a derived class specific for main thread
|
||||
|
||||
struct MainThread : public Thread {
|
||||
|
||||
using Thread::Thread;
|
||||
|
||||
void search() override;
|
||||
void check_time();
|
||||
|
||||
double previousTimeReduction;
|
||||
Value bestPreviousScore;
|
||||
Value bestPreviousAverageScore;
|
||||
Value iterValue[4];
|
||||
int callsCnt;
|
||||
bool stopOnPonderhit;
|
||||
std::atomic_bool ponder;
|
||||
struct Thread {
|
||||
SplitPoint *splitPoint;
|
||||
volatile int activeSplitPoints;
|
||||
uint64_t nodes;
|
||||
uint64_t betaCutOffs[2];
|
||||
bool failHighPly1;
|
||||
volatile bool stop;
|
||||
volatile bool running;
|
||||
volatile bool idle;
|
||||
volatile bool workIsWaiting;
|
||||
volatile bool printCurrentLine;
|
||||
unsigned char pad[64]; // set some distance among local data for each thread
|
||||
};
|
||||
|
||||
|
||||
/// ThreadPool struct handles all the threads-related stuff like init, starting,
|
||||
/// parking and, most importantly, launching a thread. All the access to threads
|
||||
/// is done through this class.
|
||||
|
||||
struct ThreadPool : public std::vector<Thread*> {
|
||||
|
||||
void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false);
|
||||
void clear();
|
||||
void set(size_t);
|
||||
|
||||
MainThread* main() const { return static_cast<MainThread*>(front()); }
|
||||
uint64_t nodes_searched() const { return accumulate(&Thread::nodes); }
|
||||
uint64_t tb_hits() const { return accumulate(&Thread::tbHits); }
|
||||
Thread* get_best_thread() const;
|
||||
void start_searching();
|
||||
void wait_for_search_finished() const;
|
||||
|
||||
std::atomic_bool stop, increaseDepth;
|
||||
|
||||
private:
|
||||
StateListPtr setupStates;
|
||||
|
||||
uint64_t accumulate(std::atomic<uint64_t> Thread::* member) const {
|
||||
|
||||
uint64_t sum = 0;
|
||||
for (Thread* th : *this)
|
||||
sum += (th->*member).load(std::memory_order_relaxed);
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
|
||||
extern ThreadPool Threads;
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef THREAD_H_INCLUDED
|
||||
#endif // !defined(THREAD_H_INCLUDED)
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef THREAD_WIN32_OSX_H_INCLUDED
|
||||
#define THREAD_WIN32_OSX_H_INCLUDED
|
||||
|
||||
#include <thread>
|
||||
|
||||
/// On OSX threads other than the main thread are created with a reduced stack
|
||||
/// size of 512KB by default, this is too low for deep searches, which require
|
||||
/// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE.
|
||||
/// The implementation calls pthread_create() with the stack size parameter
|
||||
/// equal to the linux 8MB default, on platforms that support it.
|
||||
|
||||
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS)
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
static const size_t TH_STACK_SIZE = 8 * 1024 * 1024;
|
||||
|
||||
template <class T, class P = std::pair<T*, void(T::*)()>>
|
||||
void* start_routine(void* ptr)
|
||||
{
|
||||
P* p = reinterpret_cast<P*>(ptr);
|
||||
(p->first->*(p->second))(); // Call member function pointer
|
||||
delete p;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
class NativeThread {
|
||||
|
||||
pthread_t thread;
|
||||
|
||||
public:
|
||||
template<class T, class P = std::pair<T*, void(T::*)()>>
|
||||
explicit NativeThread(void(T::*fun)(), T* obj) {
|
||||
pthread_attr_t attr_storage, *attr = &attr_storage;
|
||||
pthread_attr_init(attr);
|
||||
pthread_attr_setstacksize(attr, TH_STACK_SIZE);
|
||||
pthread_create(&thread, attr, start_routine<T>, new P(obj, fun));
|
||||
}
|
||||
void join() { pthread_join(thread, NULL); }
|
||||
};
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#else // Default case: use STL classes
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
typedef std::thread NativeThread;
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif
|
||||
|
||||
#endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
|
||||
#include "search.h"
|
||||
#include "timeman.h"
|
||||
#include "uci.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
TimeManagement Time; // Our global time management object
|
||||
|
||||
|
||||
/// TimeManagement::init() is called at the beginning of the search and calculates
|
||||
/// the bounds of time allowed for the current game ply. We currently support:
|
||||
// 1) x basetime (+ z increment)
|
||||
// 2) x moves in y seconds (+ z increment)
|
||||
|
||||
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
|
||||
|
||||
TimePoint moveOverhead = TimePoint(Options["Move Overhead"]);
|
||||
TimePoint slowMover = TimePoint(Options["Slow Mover"]);
|
||||
TimePoint npmsec = TimePoint(Options["nodestime"]);
|
||||
|
||||
// optScale is a percentage of available time to use for the current move.
|
||||
// maxScale is a multiplier applied to optimumTime.
|
||||
double optScale, maxScale;
|
||||
|
||||
// If we have to play in 'nodes as time' mode, then convert from time
|
||||
// to nodes, and use resulting values in time management formulas.
|
||||
// WARNING: to avoid time losses, the given npmsec (nodes per millisecond)
|
||||
// must be much lower than the real engine speed.
|
||||
if (npmsec)
|
||||
{
|
||||
if (!availableNodes) // Only once at game start
|
||||
availableNodes = npmsec * limits.time[us]; // Time is in msec
|
||||
|
||||
// Convert from milliseconds to nodes
|
||||
limits.time[us] = TimePoint(availableNodes);
|
||||
limits.inc[us] *= npmsec;
|
||||
limits.npmsec = npmsec;
|
||||
}
|
||||
|
||||
startTime = limits.startTime;
|
||||
|
||||
// Maximum move horizon of 50 moves
|
||||
int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50;
|
||||
|
||||
// Make sure timeLeft is > 0 since we may use it as a divisor
|
||||
TimePoint timeLeft = std::max(TimePoint(1),
|
||||
limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg));
|
||||
|
||||
// Use extra time with larger increments
|
||||
double optExtra = std::clamp(1.0 + 12.0 * limits.inc[us] / limits.time[us], 1.0, 1.12);
|
||||
|
||||
// A user may scale time usage by setting UCI option "Slow Mover"
|
||||
// Default is 100 and changing this value will probably lose elo.
|
||||
timeLeft = slowMover * timeLeft / 100;
|
||||
|
||||
// x basetime (+ z increment)
|
||||
// If there is a healthy increment, timeLeft can exceed actual available
|
||||
// game time for the current move, so also cap to 20% of available game time.
|
||||
if (limits.movestogo == 0)
|
||||
{
|
||||
optScale = std::min(0.0084 + std::pow(ply + 3.0, 0.5) * 0.0042,
|
||||
0.2 * limits.time[us] / double(timeLeft))
|
||||
* optExtra;
|
||||
maxScale = std::min(7.0, 4.0 + ply / 12.0);
|
||||
}
|
||||
|
||||
// x moves in y seconds (+ z increment)
|
||||
else
|
||||
{
|
||||
optScale = std::min((0.88 + ply / 116.4) / mtg,
|
||||
0.88 * limits.time[us] / double(timeLeft));
|
||||
maxScale = std::min(6.3, 1.5 + 0.11 * mtg);
|
||||
}
|
||||
|
||||
// Never use more than 80% of the available time for this move
|
||||
optimumTime = TimePoint(optScale * timeLeft);
|
||||
maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime));
|
||||
|
||||
if (Options["Ponder"])
|
||||
optimumTime += optimumTime / 4;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TIMEMAN_H_INCLUDED
|
||||
#define TIMEMAN_H_INCLUDED
|
||||
|
||||
#include "misc.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
/// The TimeManagement class computes the optimal time to think depending on
|
||||
/// the maximum available time, the game move number and other parameters.
|
||||
|
||||
class TimeManagement {
|
||||
public:
|
||||
void init(Search::LimitsType& limits, Color us, int ply);
|
||||
TimePoint optimum() const { return optimumTime; }
|
||||
TimePoint maximum() const { return maximumTime; }
|
||||
TimePoint elapsed() const { return Search::Limits.npmsec ?
|
||||
TimePoint(Threads.nodes_searched()) : now() - startTime; }
|
||||
|
||||
int64_t availableNodes; // When in 'nodes as time' mode
|
||||
|
||||
private:
|
||||
TimePoint startTime;
|
||||
TimePoint optimumTime;
|
||||
TimePoint maximumTime;
|
||||
};
|
||||
|
||||
extern TimeManagement Time;
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef TIMEMAN_H_INCLUDED
|
||||
+210
-114
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,147 +17,242 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstring> // For std::memset
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "misc.h"
|
||||
#include "thread.h"
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#include "movegen.h"
|
||||
#include "tt.h"
|
||||
#include "uci.h"
|
||||
|
||||
namespace Stockfish {
|
||||
// The main transposition table
|
||||
TranspositionTable TT;
|
||||
|
||||
TranspositionTable TT; // Our global transposition table
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
/// TTEntry::save() populates the TTEntry with a new node's data, possibly
|
||||
/// overwriting an old position. Update is not atomic and can be racy.
|
||||
TranspositionTable::TranspositionTable() {
|
||||
|
||||
void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) {
|
||||
size = writes = 0;
|
||||
entries = 0;
|
||||
generation = 0;
|
||||
}
|
||||
|
||||
// Preserve any existing move for the same position
|
||||
if (m || (uint16_t)k != key16)
|
||||
move16 = (uint16_t)m;
|
||||
TranspositionTable::~TranspositionTable() {
|
||||
|
||||
// Overwrite less valuable entries (cheapest checks first)
|
||||
if ( b == BOUND_EXACT
|
||||
|| (uint16_t)k != key16
|
||||
|| d - DEPTH_OFFSET + 2 * pv > depth8 - 4)
|
||||
delete [] entries;
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::set_size sets the size of the transposition table,
|
||||
/// measured in megabytes.
|
||||
|
||||
void TranspositionTable::set_size(size_t mbSize) {
|
||||
|
||||
assert(mbSize >= 4 && mbSize <= 8192);
|
||||
|
||||
size_t newSize = 1024;
|
||||
|
||||
// We store a cluster of ClusterSize number of TTEntry for each position
|
||||
// and newSize is the maximum number of storable positions.
|
||||
while ((2 * newSize) * sizeof(TTCluster) <= (mbSize << 20))
|
||||
newSize *= 2;
|
||||
|
||||
if (newSize != size)
|
||||
{
|
||||
assert(d > DEPTH_OFFSET);
|
||||
assert(d < 256 + DEPTH_OFFSET);
|
||||
|
||||
key16 = (uint16_t)k;
|
||||
depth8 = (uint8_t)(d - DEPTH_OFFSET);
|
||||
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
|
||||
value16 = (int16_t)v;
|
||||
eval16 = (int16_t)ev;
|
||||
size = newSize;
|
||||
delete [] entries;
|
||||
entries = new TTCluster[size];
|
||||
if (!entries)
|
||||
{
|
||||
std::cerr << "Failed to allocate " << mbSize
|
||||
<< " MB for transposition table." << std::endl;
|
||||
Application::exit_with_failure();
|
||||
}
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::resize() sets the size of the transposition table,
|
||||
/// measured in megabytes. Transposition table consists of a power of 2 number
|
||||
/// of clusters and each cluster consists of ClusterSize number of TTEntry.
|
||||
|
||||
void TranspositionTable::resize(size_t mbSize) {
|
||||
|
||||
Threads.main()->wait_for_search_finished();
|
||||
|
||||
aligned_large_pages_free(table);
|
||||
|
||||
clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
|
||||
|
||||
table = static_cast<Cluster*>(aligned_large_pages_alloc(clusterCount * sizeof(Cluster)));
|
||||
if (!table)
|
||||
{
|
||||
std::cerr << "Failed to allocate " << mbSize
|
||||
<< "MB for transposition table." << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::clear() initializes the entire transposition table to zero,
|
||||
// in a multi-threaded way.
|
||||
/// TranspositionTable::clear overwrites the entire transposition table
|
||||
/// with zeroes. It is called whenever the table is resized, or when the
|
||||
/// user asks the program to clear the table (from the UCI interface).
|
||||
/// Perhaps we should also clear it when the "ucinewgame" command is recieved?
|
||||
|
||||
void TranspositionTable::clear() {
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
memset(entries, 0, size * sizeof(TTCluster));
|
||||
}
|
||||
|
||||
for (size_t idx = 0; idx < Options["Threads"]; ++idx)
|
||||
|
||||
/// TranspositionTable::first_entry returns a pointer to the first
|
||||
/// entry of a cluster given a position. The low 32 bits of the key
|
||||
/// are used to get the index in the table.
|
||||
|
||||
inline TTEntry* TranspositionTable::first_entry(const Key posKey) const {
|
||||
|
||||
return entries[uint32_t(posKey) & (size - 1)].data;
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::store writes a new entry containing a position,
|
||||
/// a value, a value type, a search depth, and a best move to the
|
||||
/// transposition table. Transposition table is organized in clusters of
|
||||
/// four TTEntry objects, and when a new entry is written, it replaces
|
||||
/// the least valuable of the four entries in a cluster. A TTEntry t1 is
|
||||
/// considered to be more valuable than a TTEntry t2 if t1 is from the
|
||||
/// current search and t2 is from a previous search, or if the depth of t1
|
||||
/// is bigger than the depth of t2. A TTEntry of type VALUE_TYPE_EVAL
|
||||
/// never replaces another entry for the same position.
|
||||
|
||||
void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m) {
|
||||
|
||||
TTEntry *tte, *replace;
|
||||
uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key
|
||||
|
||||
tte = replace = first_entry(posKey);
|
||||
for (int i = 0; i < ClusterSize; i++, tte++)
|
||||
{
|
||||
threads.emplace_back([this, idx]() {
|
||||
|
||||
// Thread binding gives faster search on systems with a first-touch policy
|
||||
if (Options["Threads"] > 8)
|
||||
WinProcGroup::bindThisThread(idx);
|
||||
|
||||
// Each thread will zero its part of the hash table
|
||||
const size_t stride = size_t(clusterCount / Options["Threads"]),
|
||||
start = size_t(stride * idx),
|
||||
len = idx != Options["Threads"] - 1 ?
|
||||
stride : clusterCount - start;
|
||||
|
||||
std::memset(&table[start], 0, len * sizeof(Cluster));
|
||||
});
|
||||
}
|
||||
|
||||
for (std::thread& th : threads)
|
||||
th.join();
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::probe() looks up the current position in the transposition
|
||||
/// table. It returns true and a pointer to the TTEntry if the position is found.
|
||||
/// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry
|
||||
/// to be replaced later. The replace value of an entry is calculated as its depth
|
||||
/// minus 8 times its relative age. TTEntry t1 is considered more valuable than
|
||||
/// TTEntry t2 if its replace value is greater than that of t2.
|
||||
|
||||
TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
|
||||
|
||||
TTEntry* const tte = first_entry(key);
|
||||
const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster
|
||||
|
||||
for (int i = 0; i < ClusterSize; ++i)
|
||||
if (tte[i].key16 == key16 || !tte[i].depth8)
|
||||
if (!tte->key() || tte->key() == posKey32) // empty or overwrite old
|
||||
{
|
||||
tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & (GENERATION_DELTA - 1))); // Refresh
|
||||
// Do not overwrite when new type is VALUE_TYPE_EV_LO
|
||||
if (tte->key() && t == VALUE_TYPE_EV_LO)
|
||||
return;
|
||||
|
||||
return found = (bool)tte[i].depth8, &tte[i];
|
||||
if (m == MOVE_NONE)
|
||||
m = tte->move();
|
||||
|
||||
*tte = TTEntry(posKey32, v, t, d, m, generation);
|
||||
return;
|
||||
}
|
||||
else if (i == 0) // replace would be a no-op in this common case
|
||||
continue;
|
||||
|
||||
// Find an entry to be replaced according to the replacement strategy
|
||||
TTEntry* replace = tte;
|
||||
for (int i = 1; i < ClusterSize; ++i)
|
||||
// Due to our packed storage format for generation and its cyclic
|
||||
// nature we add GENERATION_CYCLE (256 is the modulus, plus what
|
||||
// is needed to keep the unrelated lowest n bits from affecting
|
||||
// the result) to calculate the entry age correctly even after
|
||||
// generation8 overflows into the next cycle.
|
||||
if ( replace->depth8 - ((GENERATION_CYCLE + generation8 - replace->genBound8) & GENERATION_MASK)
|
||||
> tte[i].depth8 - ((GENERATION_CYCLE + generation8 - tte[i].genBound8) & GENERATION_MASK))
|
||||
replace = &tte[i];
|
||||
int c1 = (replace->generation() == generation ? 2 : 0);
|
||||
int c2 = (tte->generation() == generation ? -2 : 0);
|
||||
int c3 = (tte->depth() < replace->depth() ? 1 : 0);
|
||||
|
||||
return found = false, replace;
|
||||
if (c1 + c2 + c3 > 0)
|
||||
replace = tte;
|
||||
}
|
||||
*replace = TTEntry(posKey32, v, t, d, m, generation);
|
||||
writes++;
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::hashfull() returns an approximation of the hashtable
|
||||
/// occupation during a search. The hash is x permill full, as per UCI protocol.
|
||||
/// TranspositionTable::retrieve looks up the current position in the
|
||||
/// transposition table. Returns a pointer to the TTEntry or NULL
|
||||
/// if position is not found.
|
||||
|
||||
int TranspositionTable::hashfull() const {
|
||||
TTEntry* TranspositionTable::retrieve(const Key posKey) const {
|
||||
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
for (int j = 0; j < ClusterSize; ++j)
|
||||
cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8;
|
||||
uint32_t posKey32 = posKey >> 32;
|
||||
TTEntry* tte = first_entry(posKey);
|
||||
|
||||
return cnt / ClusterSize;
|
||||
for (int i = 0; i < ClusterSize; i++, tte++)
|
||||
if (tte->key() == posKey32)
|
||||
return tte;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
/// TranspositionTable::prefetch looks up the current position in the
|
||||
/// transposition table and load it in L1/L2 cache. This is a non
|
||||
/// blocking function and do not stalls the CPU waiting for data
|
||||
/// to be loaded from RAM, that can be very slow. When we will
|
||||
/// subsequently call retrieve() the TT data will be already
|
||||
/// quickly accessible in L1/L2 CPU cache.
|
||||
|
||||
void TranspositionTable::prefetch(const Key posKey) const {
|
||||
|
||||
#if defined(__INTEL_COMPILER) || defined(__ICL)
|
||||
// This hack prevents prefetches to be optimized away by the
|
||||
// Intel compiler. Both MSVC and gcc seems not affected.
|
||||
__asm__ ("");
|
||||
#endif
|
||||
|
||||
char const* addr = (char*)first_entry(posKey);
|
||||
_mm_prefetch(addr, _MM_HINT_T2);
|
||||
_mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::new_search() is called at the beginning of every new
|
||||
/// search. It increments the "generation" variable, which is used to
|
||||
/// distinguish transposition table entries from previous searches from
|
||||
/// entries from the current search.
|
||||
|
||||
void TranspositionTable::new_search() {
|
||||
|
||||
generation++;
|
||||
writes = 0;
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::insert_pv() is called at the end of a search
|
||||
/// iteration, and inserts the PV back into the PV. This makes sure
|
||||
/// the old PV moves are searched first, even if the old TT entries
|
||||
/// have been overwritten.
|
||||
|
||||
void TranspositionTable::insert_pv(const Position& pos, Move pv[]) {
|
||||
|
||||
StateInfo st;
|
||||
Position p(pos);
|
||||
|
||||
for (int i = 0; pv[i] != MOVE_NONE; i++)
|
||||
{
|
||||
TTEntry *tte = retrieve(p.get_key());
|
||||
if (!tte || tte->move() != pv[i])
|
||||
store(p.get_key(), VALUE_NONE, VALUE_TYPE_NONE, Depth(-127*OnePly), pv[i]);
|
||||
p.do_move(pv[i], st);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::extract_pv() extends a PV by adding moves from the
|
||||
/// transposition table at the end. This should ensure that the PV is almost
|
||||
/// always at least two plies long, which is important, because otherwise we
|
||||
/// will often get single-move PVs when the search stops while failing high,
|
||||
/// and a single-move PV means that we don't have a ponder move.
|
||||
|
||||
void TranspositionTable::extract_pv(const Position& pos, Move pv[], const int PLY_MAX) {
|
||||
|
||||
const TTEntry* tte;
|
||||
StateInfo st;
|
||||
Position p(pos);
|
||||
int ply = 0;
|
||||
|
||||
// Update position to the end of current PV
|
||||
while (pv[ply] != MOVE_NONE)
|
||||
p.do_move(pv[ply++], st);
|
||||
|
||||
// Try to add moves from TT while possible
|
||||
while ( (tte = retrieve(p.get_key())) != NULL
|
||||
&& tte->move() != MOVE_NONE
|
||||
&& move_is_legal(p, tte->move())
|
||||
&& (!p.is_draw() || ply < 2)
|
||||
&& ply < PLY_MAX)
|
||||
{
|
||||
pv[ply] = tte->move();
|
||||
p.do_move(pv[ply++], st);
|
||||
}
|
||||
pv[ply] = MOVE_NONE;
|
||||
}
|
||||
|
||||
|
||||
/// TranspositionTable::full() returns the permill of all transposition table
|
||||
/// entries which have received at least one write during the current search.
|
||||
/// It is used to display the "info hashfull ..." information in UCI.
|
||||
|
||||
int TranspositionTable::full() const {
|
||||
|
||||
double N = double(size) * ClusterSize;
|
||||
return int(1000 * (1 - exp(writes * log(1.0 - 1.0/N))));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,92 +17,108 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TT_H_INCLUDED
|
||||
|
||||
#if !defined(TT_H_INCLUDED)
|
||||
#define TT_H_INCLUDED
|
||||
|
||||
#include "misc.h"
|
||||
#include "types.h"
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
namespace Stockfish {
|
||||
#include "depth.h"
|
||||
#include "position.h"
|
||||
#include "value.h"
|
||||
|
||||
/// TTEntry struct is the 10 bytes transposition table entry, defined as below:
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
/// The TTEntry class is the class of transposition table entries
|
||||
///
|
||||
/// key 16 bit
|
||||
/// depth 8 bit
|
||||
/// generation 5 bit
|
||||
/// pv node 1 bit
|
||||
/// bound type 2 bit
|
||||
/// move 16 bit
|
||||
/// value 16 bit
|
||||
/// eval value 16 bit
|
||||
/// A TTEntry needs 96 bits to be stored
|
||||
///
|
||||
/// bit 0-31: key
|
||||
/// bit 32-63: data
|
||||
/// bit 64-79: value
|
||||
/// bit 80-95: depth
|
||||
///
|
||||
/// the 32 bits of the data field are so defined
|
||||
///
|
||||
/// bit 0-16: move
|
||||
/// bit 17-19: not used
|
||||
/// bit 20-22: value type
|
||||
/// bit 23-31: generation
|
||||
|
||||
struct TTEntry {
|
||||
class TTEntry {
|
||||
|
||||
Move move() const { return (Move )move16; }
|
||||
Value value() const { return (Value)value16; }
|
||||
Value eval() const { return (Value)eval16; }
|
||||
Depth depth() const { return (Depth)depth8 + DEPTH_OFFSET; }
|
||||
bool is_pv() const { return (bool)(genBound8 & 0x4); }
|
||||
Bound bound() const { return (Bound)(genBound8 & 0x3); }
|
||||
void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev);
|
||||
public:
|
||||
TTEntry() {}
|
||||
TTEntry(uint32_t k, Value v, ValueType t, Depth d, Move m, int generation)
|
||||
: key_ (k), data((m & 0x1FFFF) | (t << 20) | (generation << 23)),
|
||||
value_(int16_t(v)), depth_(int16_t(d)) {}
|
||||
|
||||
uint32_t key() const { return key_; }
|
||||
Depth depth() const { return Depth(depth_); }
|
||||
Move move() const { return Move(data & 0x1FFFF); }
|
||||
Value value() const { return Value(value_); }
|
||||
ValueType type() const { return ValueType((data >> 20) & 7); }
|
||||
int generation() const { return (data >> 23); }
|
||||
|
||||
private:
|
||||
friend class TranspositionTable;
|
||||
|
||||
uint16_t key16;
|
||||
uint8_t depth8;
|
||||
uint8_t genBound8;
|
||||
uint16_t move16;
|
||||
int16_t value16;
|
||||
int16_t eval16;
|
||||
uint32_t key_;
|
||||
uint32_t data;
|
||||
int16_t value_;
|
||||
int16_t depth_;
|
||||
};
|
||||
|
||||
|
||||
/// A TranspositionTable is an array of Cluster, of size clusterCount. Each
|
||||
/// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry
|
||||
/// contains information on exactly one position. The size of a Cluster should
|
||||
/// divide the size of a cache line for best performance, as the cacheline is
|
||||
/// prefetched when possible.
|
||||
/// This is the number of TTEntry slots for each position
|
||||
const int ClusterSize = 5;
|
||||
|
||||
/// Each group of ClusterSize number of TTEntry form a TTCluster
|
||||
/// that is indexed by a single position key. Cluster is padded
|
||||
/// to a cache line size so to guarantee always aligned accesses.
|
||||
|
||||
struct TTCluster {
|
||||
TTEntry data[ClusterSize];
|
||||
char cache_line_padding[64 - sizeof(TTEntry[ClusterSize])];
|
||||
};
|
||||
|
||||
|
||||
/// The transposition table class. This is basically just a huge array
|
||||
/// containing TTEntry objects, and a few methods for writing new entries
|
||||
/// and reading new ones.
|
||||
|
||||
class TranspositionTable {
|
||||
|
||||
static constexpr int ClusterSize = 3;
|
||||
|
||||
struct Cluster {
|
||||
TTEntry entry[ClusterSize];
|
||||
char padding[2]; // Pad to 32 bytes
|
||||
};
|
||||
|
||||
static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size");
|
||||
|
||||
// Constants used to refresh the hash table periodically
|
||||
static constexpr unsigned GENERATION_BITS = 3; // nb of bits reserved for other things
|
||||
static constexpr int GENERATION_DELTA = (1 << GENERATION_BITS); // increment for generation field
|
||||
static constexpr int GENERATION_CYCLE = 255 + (1 << GENERATION_BITS); // cycle length
|
||||
static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; // mask to pull out generation number
|
||||
|
||||
public:
|
||||
~TranspositionTable() { aligned_large_pages_free(table); }
|
||||
void new_search() { generation8 += GENERATION_DELTA; } // Lower bits are used for other things
|
||||
TTEntry* probe(const Key key, bool& found) const;
|
||||
int hashfull() const;
|
||||
void resize(size_t mbSize);
|
||||
TranspositionTable();
|
||||
~TranspositionTable();
|
||||
void set_size(size_t mbSize);
|
||||
void clear();
|
||||
|
||||
TTEntry* first_entry(const Key key) const {
|
||||
return &table[mul_hi64(key, clusterCount)].entry[0];
|
||||
}
|
||||
void store(const Key posKey, Value v, ValueType type, Depth d, Move m);
|
||||
TTEntry* retrieve(const Key posKey) const;
|
||||
void prefetch(const Key posKey) const;
|
||||
void new_search();
|
||||
void insert_pv(const Position& pos, Move pv[]);
|
||||
void extract_pv(const Position& pos, Move pv[], const int PLY_MAX);
|
||||
int full() const;
|
||||
|
||||
private:
|
||||
friend struct TTEntry;
|
||||
inline TTEntry* first_entry(const Key posKey) const;
|
||||
|
||||
size_t clusterCount;
|
||||
Cluster* table;
|
||||
uint8_t generation8; // Size must be not bigger than TTEntry::genBound8
|
||||
// Be sure 'writes' is at least one cache line away
|
||||
// from read only variables.
|
||||
unsigned char pad_before[64 - sizeof(unsigned)];
|
||||
unsigned writes; // heavy SMP read/write access here
|
||||
unsigned char pad_after[64];
|
||||
|
||||
size_t size;
|
||||
TTCluster* entries;
|
||||
uint8_t generation;
|
||||
};
|
||||
|
||||
extern TranspositionTable TT;
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef TT_H_INCLUDED
|
||||
#endif // !defined(TT_H_INCLUDED)
|
||||
|
||||
-133
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "types.h"
|
||||
#include "misc.h"
|
||||
#include "uci.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
bool Tune::update_on_last;
|
||||
const UCI::Option* LastOption = nullptr;
|
||||
static std::map<std::string, int> TuneResults;
|
||||
|
||||
string Tune::next(string& names, bool pop) {
|
||||
|
||||
string name;
|
||||
|
||||
do {
|
||||
string token = names.substr(0, names.find(','));
|
||||
|
||||
if (pop)
|
||||
names.erase(0, token.size() + 1);
|
||||
|
||||
std::stringstream ws(token);
|
||||
name += (ws >> token, token); // Remove trailing whitespace
|
||||
|
||||
} while ( std::count(name.begin(), name.end(), '(')
|
||||
- std::count(name.begin(), name.end(), ')'));
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static void on_tune(const UCI::Option& o) {
|
||||
|
||||
if (!Tune::update_on_last || LastOption == &o)
|
||||
Tune::read_options();
|
||||
}
|
||||
|
||||
static void make_option(const string& n, int v, const SetRange& r) {
|
||||
|
||||
// Do not generate option when there is nothing to tune (ie. min = max)
|
||||
if (r(v).first == r(v).second)
|
||||
return;
|
||||
|
||||
if (TuneResults.count(n))
|
||||
v = TuneResults[n];
|
||||
|
||||
Options[n] << UCI::Option(v, r(v).first, r(v).second, on_tune);
|
||||
LastOption = &Options[n];
|
||||
|
||||
// Print formatted parameters, ready to be copy-pasted in Fishtest
|
||||
std::cout << n << ","
|
||||
<< v << ","
|
||||
<< r(v).first << "," << r(v).second << ","
|
||||
<< (r(v).second - r(v).first) / 20.0 << ","
|
||||
<< "0.0020"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
template<> void Tune::Entry<int>::init_option() { make_option(name, value, range); }
|
||||
|
||||
template<> void Tune::Entry<int>::read_option() {
|
||||
if (Options.count(name))
|
||||
value = int(Options[name]);
|
||||
}
|
||||
|
||||
template<> void Tune::Entry<Value>::init_option() { make_option(name, value, range); }
|
||||
|
||||
template<> void Tune::Entry<Value>::read_option() {
|
||||
if (Options.count(name))
|
||||
value = Value(int(Options[name]));
|
||||
}
|
||||
|
||||
template<> void Tune::Entry<Score>::init_option() {
|
||||
make_option("m" + name, mg_value(value), range);
|
||||
make_option("e" + name, eg_value(value), range);
|
||||
}
|
||||
|
||||
template<> void Tune::Entry<Score>::read_option() {
|
||||
if (Options.count("m" + name))
|
||||
value = make_score(int(Options["m" + name]), eg_value(value));
|
||||
|
||||
if (Options.count("e" + name))
|
||||
value = make_score(mg_value(value), int(Options["e" + name]));
|
||||
}
|
||||
|
||||
// Instead of a variable here we have a PostUpdate function: just call it
|
||||
template<> void Tune::Entry<Tune::PostUpdate>::init_option() {}
|
||||
template<> void Tune::Entry<Tune::PostUpdate>::read_option() { value(); }
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
|
||||
// Init options with tuning session results instead of default values. Useful to
|
||||
// get correct bench signature after a tuning session or to test tuned values.
|
||||
// Just copy fishtest tuning results in a result.txt file and extract the
|
||||
// values with:
|
||||
//
|
||||
// cat results.txt | sed 's/^param: \([^,]*\), best: \([^,]*\).*/ TuneResults["\1"] = int(round(\2));/'
|
||||
//
|
||||
// Then paste the output below, as the function body
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
void Tune::read_results() {
|
||||
|
||||
/* ...insert your values here... */
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
-163
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TUNE_H_INCLUDED
|
||||
#define TUNE_H_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
typedef std::pair<int, int> Range; // Option's min-max values
|
||||
typedef Range (RangeFun) (int);
|
||||
|
||||
// Default Range function, to calculate Option's min-max values
|
||||
inline Range default_range(int v) {
|
||||
return v > 0 ? Range(0, 2 * v) : Range(2 * v, 0);
|
||||
}
|
||||
|
||||
struct SetRange {
|
||||
explicit SetRange(RangeFun f) : fun(f) {}
|
||||
SetRange(int min, int max) : fun(nullptr), range(min, max) {}
|
||||
Range operator()(int v) const { return fun ? fun(v) : range; }
|
||||
|
||||
RangeFun* fun;
|
||||
Range range;
|
||||
};
|
||||
|
||||
#define SetDefaultRange SetRange(default_range)
|
||||
|
||||
|
||||
/// Tune class implements the 'magic' code that makes the setup of a fishtest
|
||||
/// tuning session as easy as it can be. Mainly you have just to remove const
|
||||
/// qualifiers from the variables you want to tune and flag them for tuning, so
|
||||
/// if you have:
|
||||
///
|
||||
/// const Score myScore = S(10, 15);
|
||||
/// const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } };
|
||||
///
|
||||
/// If you have a my_post_update() function to run after values have been updated,
|
||||
/// and a my_range() function to set custom Option's min-max values, then you just
|
||||
/// remove the 'const' qualifiers and write somewhere below in the file:
|
||||
///
|
||||
/// TUNE(SetRange(my_range), myScore, myValue, my_post_update);
|
||||
///
|
||||
/// You can also set the range directly, and restore the default at the end
|
||||
///
|
||||
/// TUNE(SetRange(-100, 100), myScore, SetDefaultRange);
|
||||
///
|
||||
/// In case update function is slow and you have many parameters, you can add:
|
||||
///
|
||||
/// UPDATE_ON_LAST();
|
||||
///
|
||||
/// And the values update, including post update function call, will be done only
|
||||
/// once, after the engine receives the last UCI option, that is the one defined
|
||||
/// and created as the last one, so the GUI should send the options in the same
|
||||
/// order in which have been defined.
|
||||
|
||||
class Tune {
|
||||
|
||||
typedef void (PostUpdate) (); // Post-update function
|
||||
|
||||
Tune() { read_results(); }
|
||||
Tune(const Tune&) = delete;
|
||||
void operator=(const Tune&) = delete;
|
||||
void read_results();
|
||||
|
||||
static Tune& instance() { static Tune t; return t; } // Singleton
|
||||
|
||||
// Use polymorphism to accommodate Entry of different types in the same vector
|
||||
struct EntryBase {
|
||||
virtual ~EntryBase() = default;
|
||||
virtual void init_option() = 0;
|
||||
virtual void read_option() = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Entry : public EntryBase {
|
||||
|
||||
static_assert(!std::is_const<T>::value, "Parameter cannot be const!");
|
||||
|
||||
static_assert( std::is_same<T, int>::value
|
||||
|| std::is_same<T, Value>::value
|
||||
|| std::is_same<T, Score>::value
|
||||
|| std::is_same<T, PostUpdate>::value, "Parameter type not supported!");
|
||||
|
||||
Entry(const std::string& n, T& v, const SetRange& r) : name(n), value(v), range(r) {}
|
||||
void operator=(const Entry&) = delete; // Because 'value' is a reference
|
||||
void init_option() override;
|
||||
void read_option() override;
|
||||
|
||||
std::string name;
|
||||
T& value;
|
||||
SetRange range;
|
||||
};
|
||||
|
||||
// Our facility to fill the container, each Entry corresponds to a parameter
|
||||
// to tune. We use variadic templates to deal with an unspecified number of
|
||||
// entries, each one of a possible different type.
|
||||
static std::string next(std::string& names, bool pop = true);
|
||||
|
||||
int add(const SetRange&, std::string&&) { return 0; }
|
||||
|
||||
template<typename T, typename... Args>
|
||||
int add(const SetRange& range, std::string&& names, T& value, Args&&... args) {
|
||||
list.push_back(std::unique_ptr<EntryBase>(new Entry<T>(next(names), value, range)));
|
||||
return add(range, std::move(names), args...);
|
||||
}
|
||||
|
||||
// Template specialization for arrays: recursively handle multi-dimensional arrays
|
||||
template<typename T, size_t N, typename... Args>
|
||||
int add(const SetRange& range, std::string&& names, T (&value)[N], Args&&... args) {
|
||||
for (size_t i = 0; i < N; i++)
|
||||
add(range, next(names, i == N - 1) + "[" + std::to_string(i) + "]", value[i]);
|
||||
return add(range, std::move(names), args...);
|
||||
}
|
||||
|
||||
// Template specialization for SetRange
|
||||
template<typename... Args>
|
||||
int add(const SetRange&, std::string&& names, SetRange& value, Args&&... args) {
|
||||
return add(value, (next(names), std::move(names)), args...);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<EntryBase>> list;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
static int add(const std::string& names, Args&&... args) {
|
||||
return instance().add(SetDefaultRange, names.substr(1, names.size() - 2), args...); // Remove trailing parenthesis
|
||||
}
|
||||
static void init() { for (auto& e : instance().list) e->init_option(); read_options(); } // Deferred, due to UCI::Options access
|
||||
static void read_options() { for (auto& e : instance().list) e->read_option(); }
|
||||
static bool update_on_last;
|
||||
};
|
||||
|
||||
// Some macro magic :-) we define a dummy int variable that compiler initializes calling Tune::add()
|
||||
#define STRINGIFY(x) #x
|
||||
#define UNIQUE2(x, y) x ## y
|
||||
#define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__
|
||||
#define TUNE(...) int UNIQUE(p, __LINE__) = Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__)
|
||||
|
||||
#define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef TUNE_H_INCLUDED
|
||||
+41
-451
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,471 +17,60 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TYPES_H_INCLUDED
|
||||
|
||||
#if !defined(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.
|
||||
#if !defined(_MSC_VER)
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <inttypes.h>
|
||||
|
||||
#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(__GNUC__ ) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) && defined(_WIN32) && !defined(__clang__)
|
||||
#define ALIGNAS_ON_STACK_VARIABLES_BROKEN
|
||||
#endif
|
||||
|
||||
#define ASSERT_ALIGNED(ptr, alignment) assert(reinterpret_cast<uintptr_t>(ptr) % alignment == 0)
|
||||
|
||||
#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
|
||||
|
||||
namespace Stockfish {
|
||||
typedef __int8 int8_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef __int16 int16;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
#ifdef USE_POPCNT
|
||||
constexpr bool HasPopCnt = true;
|
||||
#else
|
||||
constexpr bool HasPopCnt = false;
|
||||
#endif
|
||||
typedef __int16 int16_t;
|
||||
typedef __int64 int64_t;
|
||||
|
||||
#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
|
||||
#endif // !defined(_MSC_VER)
|
||||
|
||||
// Hash keys
|
||||
typedef uint64_t Key;
|
||||
|
||||
// Bitboard type
|
||||
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.
|
||||
////
|
||||
//// Compiler specific defines
|
||||
////
|
||||
|
||||
enum Move : int {
|
||||
MOVE_NONE,
|
||||
MOVE_NULL = 65
|
||||
};
|
||||
// Quiet a warning on Intel compiler
|
||||
#if !defined(__SIZEOF_INT__ )
|
||||
#define __SIZEOF_INT__ 0
|
||||
#endif
|
||||
|
||||
enum MoveType {
|
||||
NORMAL,
|
||||
PROMOTION = 1 << 14,
|
||||
EN_PASSANT = 2 << 14,
|
||||
CASTLING = 3 << 14
|
||||
};
|
||||
// Check for 64 bits for different compilers: Intel, MSVC and gcc
|
||||
#if defined(__x86_64) || defined(_M_X64) || defined(_WIN64) || (__SIZEOF_INT__ > 4)
|
||||
#define IS_64BIT
|
||||
#endif
|
||||
|
||||
enum Color {
|
||||
WHITE, BLACK, COLOR_NB = 2
|
||||
};
|
||||
#if defined(IS_64BIT) && (defined(__GNUC__) || defined(__INTEL_COMPILER))
|
||||
#define USE_BSFQ
|
||||
#endif
|
||||
|
||||
enum CastlingRights {
|
||||
NO_CASTLING,
|
||||
WHITE_OO,
|
||||
WHITE_OOO = WHITE_OO << 1,
|
||||
BLACK_OO = WHITE_OO << 2,
|
||||
BLACK_OOO = WHITE_OO << 3,
|
||||
// Cache line alignment specification
|
||||
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
|
||||
#define CACHE_LINE_ALIGNMENT __declspec(align(64))
|
||||
#else
|
||||
#define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
|
||||
#endif
|
||||
|
||||
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_TB_WIN_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY,
|
||||
VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_MAX_PLY,
|
||||
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
|
||||
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY,
|
||||
|
||||
PawnValueMg = 126, PawnValueEg = 208,
|
||||
KnightValueMg = 781, KnightValueEg = 854,
|
||||
BishopValueMg = 825, BishopValueEg = 915,
|
||||
RookValueMg = 1276, RookValueEg = 1380,
|
||||
QueenValueMg = 2538, QueenValueEg = 2682,
|
||||
|
||||
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 = PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
|
||||
B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
|
||||
PIECE_NB = 16
|
||||
};
|
||||
|
||||
constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
|
||||
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO,
|
||||
VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO },
|
||||
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO,
|
||||
VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO }
|
||||
};
|
||||
|
||||
typedef int Depth;
|
||||
|
||||
enum : int {
|
||||
DEPTH_QS_CHECKS = 0,
|
||||
DEPTH_QS_NO_CHECKS = -1,
|
||||
DEPTH_QS_RECAPTURES = -5,
|
||||
|
||||
DEPTH_NONE = -6,
|
||||
|
||||
DEPTH_OFFSET = -7 // value used only for TT entry occupancy check
|
||||
};
|
||||
|
||||
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_ZERO = 0,
|
||||
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
|
||||
};
|
||||
|
||||
// Keep track of what a move changes on the board (used by NNUE)
|
||||
struct DirtyPiece {
|
||||
|
||||
// Number of changed pieces
|
||||
int dirty_num;
|
||||
|
||||
// Max 3 pieces can change in one move. A promotion with capture moves
|
||||
// both the pawn and the captured piece to SQ_NONE and the piece promoted
|
||||
// to from SQ_NONE to the capture square.
|
||||
Piece piece[3];
|
||||
|
||||
// From and to squares, which may be SQ_NONE
|
||||
Square from[3];
|
||||
Square to[3];
|
||||
};
|
||||
|
||||
/// 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, int d2) { return T(int(d1) + d2); } \
|
||||
constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \
|
||||
constexpr T operator-(T d) { return T(-int(d)); } \
|
||||
inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \
|
||||
inline T& operator-=(T& d1, int 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(Direction)
|
||||
|
||||
ENABLE_INCR_OPERATORS_ON(Piece)
|
||||
ENABLE_INCR_OPERATORS_ON(PieceType)
|
||||
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 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 a boolean
|
||||
inline Score operator*(Score s, bool b) {
|
||||
return b ? s : SCORE_ZERO;
|
||||
}
|
||||
|
||||
constexpr Color operator~(Color c) {
|
||||
return Color(c ^ BLACK); // Toggle color
|
||||
}
|
||||
|
||||
constexpr Square flip_rank(Square s) { // Swap A1 <-> A8
|
||||
return Square(s ^ SQ_A8);
|
||||
}
|
||||
|
||||
constexpr Square flip_file(Square s) { // Swap A1 <-> H1
|
||||
return Square(s ^ SQ_H1);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/// Based on a congruential pseudo random number generator
|
||||
constexpr Key make_key(uint64_t seed) {
|
||||
return seed * 6364136223846793005ULL + 1442695040888963407ULL;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef TYPES_H_INCLUDED
|
||||
|
||||
#include "tune.h" // Global visibility to tuning setup
|
||||
#endif // !defined(TYPES_H_INCLUDED)
|
||||
|
||||
+256
-314
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,368 +17,309 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "book.h"
|
||||
#include "evaluate.h"
|
||||
#include "misc.h"
|
||||
#include "move.h"
|
||||
#include "movegen.h"
|
||||
#include "position.h"
|
||||
#include "san.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "timeman.h"
|
||||
#include "tt.h"
|
||||
#include "uci.h"
|
||||
#include "syzygy/tbprobe.h"
|
||||
#include "ucioption.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
extern vector<string> setup_bench(const Position&, istream&);
|
||||
////
|
||||
//// Local definitions:
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
// FEN string of the initial position, normal chess
|
||||
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
// UCIInputParser is a class for parsing UCI input. The class
|
||||
// is actually a string stream built on a given input string.
|
||||
|
||||
typedef istringstream UCIInputParser;
|
||||
|
||||
// The root position. This is set up when the user (or in practice, the GUI)
|
||||
// sends the "position" UCI command. The root position is sent to the think()
|
||||
// function when the program receives the "go" command.
|
||||
Position RootPosition;
|
||||
|
||||
// Local functions
|
||||
bool handle_command(const string& command);
|
||||
void set_option(UCIInputParser& uip);
|
||||
void set_position(UCIInputParser& uip);
|
||||
bool go(UCIInputParser& uip);
|
||||
void perft(UCIInputParser& uip);
|
||||
}
|
||||
|
||||
|
||||
// position() is called when engine receives the "position" UCI command.
|
||||
// The function sets up the position described in the given FEN string ("fen")
|
||||
// or the starting position ("startpos") and then makes the moves given in the
|
||||
// following move list ("moves").
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
void position(Position& pos, istringstream& is, StateListPtr& states) {
|
||||
/// uci_main_loop() is the only global function in this file. It is
|
||||
/// called immediately after the program has finished initializing.
|
||||
/// The program remains in this loop until it receives the "quit" UCI
|
||||
/// command. It waits for a command from the user, and passes this
|
||||
/// command to handle_command and also intercepts EOF from stdin,
|
||||
/// by translating EOF to the "quit" command. This ensures that Stockfish
|
||||
/// exits gracefully if the GUI dies unexpectedly.
|
||||
|
||||
Move m;
|
||||
string token, fen;
|
||||
void uci_main_loop() {
|
||||
|
||||
is >> token;
|
||||
RootPosition.from_fen(StartPosition);
|
||||
string command;
|
||||
|
||||
if (token == "startpos")
|
||||
do {
|
||||
// Wait for a command from stdin
|
||||
if (!getline(cin, command))
|
||||
command = "quit";
|
||||
|
||||
} while (handle_command(command));
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// Local functions
|
||||
////
|
||||
|
||||
namespace {
|
||||
|
||||
// handle_command() takes a text string as input, uses a
|
||||
// UCIInputParser object to parse this text string as a UCI command,
|
||||
// and calls the appropriate functions. In addition to the UCI
|
||||
// commands, the function also supports a few debug commands.
|
||||
|
||||
bool handle_command(const string& command) {
|
||||
|
||||
UCIInputParser uip(command);
|
||||
string token;
|
||||
|
||||
if (!(uip >> token)) // operator>>() skips any whitespace
|
||||
return true;
|
||||
|
||||
if (token == "quit")
|
||||
return false;
|
||||
|
||||
if (token == "go")
|
||||
return go(uip);
|
||||
|
||||
if (token == "uci")
|
||||
{
|
||||
fen = StartFEN;
|
||||
is >> token; // Consume "moves" token if any
|
||||
cout << "id name " << engine_name()
|
||||
<< "\nid author Tord Romstad, Marco Costalba, Joona Kiiski\n";
|
||||
print_uci_options();
|
||||
cout << "uciok" << endl;
|
||||
}
|
||||
else if (token == "fen")
|
||||
while (is >> token && token != "moves")
|
||||
fen += token + " ";
|
||||
else if (token == "ucinewgame")
|
||||
{
|
||||
push_button("New Game");
|
||||
Position::init_piece_square_tables();
|
||||
RootPosition.from_fen(StartPosition);
|
||||
}
|
||||
else if (token == "isready")
|
||||
cout << "readyok" << endl;
|
||||
else if (token == "position")
|
||||
set_position(uip);
|
||||
else if (token == "setoption")
|
||||
set_option(uip);
|
||||
|
||||
// The remaining commands are for debugging purposes only.
|
||||
// Perhaps they should be removed later in order to reduce the
|
||||
// size of the program binary.
|
||||
else if (token == "d")
|
||||
RootPosition.print();
|
||||
else if (token == "flip")
|
||||
{
|
||||
Position p(RootPosition);
|
||||
RootPosition.flipped_copy(p);
|
||||
}
|
||||
else if (token == "eval")
|
||||
{
|
||||
EvalInfo ei;
|
||||
cout << "Incremental mg: " << mg_value(RootPosition.value())
|
||||
<< "\nIncremental eg: " << eg_value(RootPosition.value())
|
||||
<< "\nFull eval: " << evaluate(RootPosition, ei, 0) << endl;
|
||||
}
|
||||
else if (token == "key")
|
||||
cout << "key: " << hex << RootPosition.get_key()
|
||||
<< "\nmaterial key: " << RootPosition.get_material_key()
|
||||
<< "\npawn key: " << RootPosition.get_pawn_key() << endl;
|
||||
else if (token == "perft")
|
||||
perft(uip);
|
||||
else
|
||||
cout << "Unknown command: " << command << endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// set_position() is called when Stockfish receives the "position" UCI
|
||||
// command. The input parameter is a UCIInputParser. It is assumed
|
||||
// that this parser has consumed the first token of the UCI command
|
||||
// ("position"), and is ready to read the second token ("startpos"
|
||||
// or "fen", if the input is well-formed).
|
||||
|
||||
void set_position(UCIInputParser& uip) {
|
||||
|
||||
string token;
|
||||
|
||||
if (!(uip >> token)) // operator>>() skips any whitespace
|
||||
return;
|
||||
|
||||
states = StateListPtr(new std::deque<StateInfo>(1)); // Drop old and create a new one
|
||||
pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main());
|
||||
|
||||
// Parse move list (if any)
|
||||
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
|
||||
if (token == "startpos")
|
||||
RootPosition.from_fen(StartPosition);
|
||||
else if (token == "fen")
|
||||
{
|
||||
states->emplace_back();
|
||||
pos.do_move(m, states->back());
|
||||
string fen;
|
||||
while (uip >> token && token != "moves")
|
||||
{
|
||||
fen += token;
|
||||
fen += ' ';
|
||||
}
|
||||
RootPosition.from_fen(fen);
|
||||
}
|
||||
|
||||
if (uip.good())
|
||||
{
|
||||
if (token != "moves")
|
||||
uip >> token;
|
||||
|
||||
if (token == "moves")
|
||||
{
|
||||
Move move;
|
||||
StateInfo st;
|
||||
while (uip >> token)
|
||||
{
|
||||
move = move_from_string(RootPosition, token);
|
||||
RootPosition.do_move(move, st);
|
||||
if (RootPosition.rule_50_counter() == 0)
|
||||
RootPosition.reset_game_ply();
|
||||
}
|
||||
// Our StateInfo st is about going out of scope so copy
|
||||
// its content inside RootPosition before they disappear.
|
||||
RootPosition.saveState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// trace_eval() prints the evaluation for the current position, consistent with the UCI
|
||||
// options set so far.
|
||||
|
||||
void trace_eval(Position& pos) {
|
||||
// set_option() is called when Stockfish receives the "setoption" UCI
|
||||
// command. The input parameter is a UCIInputParser. It is assumed
|
||||
// that this parser has consumed the first token of the UCI command
|
||||
// ("setoption"), and is ready to read the second token ("name", if
|
||||
// the input is well-formed).
|
||||
|
||||
StateListPtr states(new std::deque<StateInfo>(1));
|
||||
Position p;
|
||||
p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main());
|
||||
|
||||
Eval::NNUE::verify();
|
||||
|
||||
sync_cout << "\n" << Eval::trace(p) << sync_endl;
|
||||
}
|
||||
|
||||
|
||||
// setoption() is called when engine receives the "setoption" UCI command. The
|
||||
// function updates the UCI option ("name") to the given value ("value").
|
||||
|
||||
void setoption(istringstream& is) {
|
||||
void set_option(UCIInputParser& uip) {
|
||||
|
||||
string token, name, value;
|
||||
|
||||
is >> token; // Consume "name" token
|
||||
if (!(uip >> token)) // operator>>() skips any whitespace
|
||||
return;
|
||||
|
||||
// Read option name (can contain spaces)
|
||||
while (is >> token && token != "value")
|
||||
name += (name.empty() ? "" : " ") + token;
|
||||
|
||||
// Read option value (can contain spaces)
|
||||
while (is >> token)
|
||||
value += (value.empty() ? "" : " ") + token;
|
||||
|
||||
if (Options.count(name))
|
||||
Options[name] = value;
|
||||
else
|
||||
sync_cout << "No such option: " << name << sync_endl;
|
||||
}
|
||||
|
||||
|
||||
// go() is called when engine receives the "go" UCI command. The function sets
|
||||
// the thinking time and other parameters from the input string, then starts
|
||||
// the search.
|
||||
|
||||
void go(Position& pos, istringstream& is, StateListPtr& states) {
|
||||
|
||||
Search::LimitsType limits;
|
||||
string token;
|
||||
bool ponderMode = false;
|
||||
|
||||
limits.startTime = now(); // As early as possible!
|
||||
|
||||
while (is >> token)
|
||||
if (token == "searchmoves") // Needs to be the last command on the line
|
||||
while (is >> token)
|
||||
limits.searchmoves.push_back(UCI::to_move(pos, token));
|
||||
|
||||
else if (token == "wtime") is >> limits.time[WHITE];
|
||||
else if (token == "btime") is >> limits.time[BLACK];
|
||||
else if (token == "winc") is >> limits.inc[WHITE];
|
||||
else if (token == "binc") is >> limits.inc[BLACK];
|
||||
else if (token == "movestogo") is >> limits.movestogo;
|
||||
else if (token == "depth") is >> limits.depth;
|
||||
else if (token == "nodes") is >> limits.nodes;
|
||||
else if (token == "movetime") is >> limits.movetime;
|
||||
else if (token == "mate") is >> limits.mate;
|
||||
else if (token == "perft") is >> limits.perft;
|
||||
else if (token == "infinite") limits.infinite = 1;
|
||||
else if (token == "ponder") ponderMode = true;
|
||||
|
||||
Threads.start_thinking(pos, states, limits, ponderMode);
|
||||
}
|
||||
|
||||
|
||||
// bench() is called when engine receives the "bench" command. Firstly
|
||||
// a list of UCI commands is setup according to bench parameters, then
|
||||
// it is run one by one printing a summary at the end.
|
||||
|
||||
void bench(Position& pos, istream& args, StateListPtr& states) {
|
||||
|
||||
string token;
|
||||
uint64_t num, nodes = 0, cnt = 1;
|
||||
|
||||
vector<string> list = setup_bench(pos, args);
|
||||
num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0 || s.find("eval") == 0; });
|
||||
|
||||
TimePoint elapsed = now();
|
||||
|
||||
for (const auto& cmd : list)
|
||||
if (token == "name" && uip >> name)
|
||||
{
|
||||
istringstream is(cmd);
|
||||
is >> skipws >> token;
|
||||
while (uip >> token && token != "value")
|
||||
name += (" " + token);
|
||||
|
||||
if (token == "go" || token == "eval")
|
||||
if (token == "value" && uip >> value)
|
||||
{
|
||||
cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")" << endl;
|
||||
if (token == "go")
|
||||
{
|
||||
go(pos, is, states);
|
||||
Threads.main()->wait_for_search_finished();
|
||||
nodes += Threads.nodes_searched();
|
||||
}
|
||||
else
|
||||
trace_eval(pos);
|
||||
while (uip >> token)
|
||||
value += (" " + token);
|
||||
|
||||
set_option_value(name, value);
|
||||
} else
|
||||
push_button(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// go() is called when Stockfish receives the "go" UCI command. The
|
||||
// input parameter is a UCIInputParser. It is assumed that this
|
||||
// parser has consumed the first token of the UCI command ("go"),
|
||||
// and is ready to read the second token. The function sets the
|
||||
// thinking time and other parameters from the input string, and
|
||||
// calls think() (defined in search.cpp) with the appropriate
|
||||
// parameters. Returns false if a quit command is received while
|
||||
// thinking, returns true otherwise.
|
||||
|
||||
bool go(UCIInputParser& uip) {
|
||||
|
||||
string token;
|
||||
|
||||
int time[2] = {0, 0}, inc[2] = {0, 0};
|
||||
int movesToGo = 0, depth = 0, nodes = 0, moveTime = 0;
|
||||
bool infinite = false, ponder = false;
|
||||
Move searchMoves[500];
|
||||
|
||||
searchMoves[0] = MOVE_NONE;
|
||||
|
||||
while (uip >> token)
|
||||
{
|
||||
if (token == "infinite")
|
||||
infinite = true;
|
||||
else if (token == "ponder")
|
||||
ponder = true;
|
||||
else if (token == "wtime")
|
||||
uip >> time[0];
|
||||
else if (token == "btime")
|
||||
uip >> time[1];
|
||||
else if (token == "winc")
|
||||
uip >> inc[0];
|
||||
else if (token == "binc")
|
||||
uip >> inc[1];
|
||||
else if (token == "movestogo")
|
||||
uip >> movesToGo;
|
||||
else if (token == "depth")
|
||||
uip >> depth;
|
||||
else if (token == "nodes")
|
||||
uip >> nodes;
|
||||
else if (token == "movetime")
|
||||
uip >> moveTime;
|
||||
else if (token == "searchmoves")
|
||||
{
|
||||
int numOfMoves = 0;
|
||||
while (uip >> token)
|
||||
searchMoves[numOfMoves++] = move_from_string(RootPosition, token);
|
||||
|
||||
searchMoves[numOfMoves] = MOVE_NONE;
|
||||
}
|
||||
else if (token == "setoption") setoption(is);
|
||||
else if (token == "position") position(pos, is, states);
|
||||
else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take some while
|
||||
}
|
||||
|
||||
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
|
||||
assert(RootPosition.is_ok());
|
||||
|
||||
dbg_print(); // Just before exiting
|
||||
|
||||
cerr << "\n==========================="
|
||||
<< "\nTotal time (ms) : " << elapsed
|
||||
<< "\nNodes searched : " << nodes
|
||||
<< "\nNodes/second : " << 1000 * nodes / elapsed << endl;
|
||||
return think(RootPosition, infinite, ponder, RootPosition.side_to_move(),
|
||||
time, inc, movesToGo, depth, nodes, moveTime, searchMoves);
|
||||
}
|
||||
|
||||
// The win rate model returns the probability (per mille) of winning given an eval
|
||||
// and a game-ply. The model fits rather accurately the LTC fishtest statistics.
|
||||
int win_rate_model(Value v, int ply) {
|
||||
void perft(UCIInputParser& uip) {
|
||||
|
||||
// The model captures only up to 240 plies, so limit input (and rescale)
|
||||
double m = std::min(240, ply) / 64.0;
|
||||
string token;
|
||||
int depth, tm, n;
|
||||
Position pos = RootPosition;
|
||||
|
||||
// Coefficients of a 3rd order polynomial fit based on fishtest data
|
||||
// for two parameters needed to transform eval to the argument of a
|
||||
// logistic function.
|
||||
double as[] = {-1.17202460e-01, 5.94729104e-01, 1.12065546e+01, 1.22606222e+02};
|
||||
double bs[] = {-1.79066759, 11.30759193, -17.43677612, 36.47147479};
|
||||
double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3];
|
||||
double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3];
|
||||
if (!(uip >> depth))
|
||||
return;
|
||||
|
||||
// Transform eval to centipawns with limited range
|
||||
double x = std::clamp(double(100 * v) / PawnValueEg, -2000.0, 2000.0);
|
||||
tm = get_system_time();
|
||||
|
||||
// Return win rate in per mille (rounded to nearest)
|
||||
return int(0.5 + 1000 / (1 + std::exp((a - x) / b)));
|
||||
n = perft(pos, depth * OnePly);
|
||||
|
||||
tm = get_system_time() - tm;
|
||||
std::cout << "\nNodes " << n
|
||||
<< "\nTime (ms) " << tm
|
||||
<< "\nNodes/second " << (int)(n/(tm/1000.0)) << std::endl;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
/// UCI::loop() waits for a command from stdin, parses it and calls the appropriate
|
||||
/// function. Also intercepts EOF from stdin to ensure gracefully exiting if the
|
||||
/// GUI dies unexpectedly. When called with some command line arguments, e.g. to
|
||||
/// run 'bench', once the command is executed the function returns immediately.
|
||||
/// In addition to the UCI ones, also some additional debug commands are supported.
|
||||
|
||||
void UCI::loop(int argc, char* argv[]) {
|
||||
|
||||
Position pos;
|
||||
string token, cmd;
|
||||
StateListPtr states(new std::deque<StateInfo>(1));
|
||||
|
||||
pos.set(StartFEN, false, &states->back(), Threads.main());
|
||||
|
||||
for (int i = 1; i < argc; ++i)
|
||||
cmd += std::string(argv[i]) + " ";
|
||||
|
||||
do {
|
||||
if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF
|
||||
cmd = "quit";
|
||||
|
||||
istringstream is(cmd);
|
||||
|
||||
token.clear(); // Avoid a stale if getline() returns empty or blank line
|
||||
is >> skipws >> token;
|
||||
|
||||
if ( token == "quit"
|
||||
|| token == "stop")
|
||||
Threads.stop = true;
|
||||
|
||||
// The GUI sends 'ponderhit' to tell us the user has played the expected move.
|
||||
// So 'ponderhit' will be sent if we were told to ponder on the same move the
|
||||
// user has played. We should continue searching but switch from pondering to
|
||||
// normal search.
|
||||
else if (token == "ponderhit")
|
||||
Threads.main()->ponder = false; // Switch to normal search
|
||||
|
||||
else if (token == "uci")
|
||||
sync_cout << "id name " << engine_info(true)
|
||||
<< "\n" << Options
|
||||
<< "\nuciok" << sync_endl;
|
||||
|
||||
else if (token == "setoption") setoption(is);
|
||||
else if (token == "go") go(pos, is, states);
|
||||
else if (token == "position") position(pos, is, states);
|
||||
else if (token == "ucinewgame") Search::clear();
|
||||
else if (token == "isready") sync_cout << "readyok" << sync_endl;
|
||||
|
||||
// Additional custom non-UCI commands, mainly for debugging.
|
||||
// Do not use these commands during a search!
|
||||
else if (token == "flip") pos.flip();
|
||||
else if (token == "bench") bench(pos, is, states);
|
||||
else if (token == "d") sync_cout << pos << sync_endl;
|
||||
else if (token == "eval") trace_eval(pos);
|
||||
else if (token == "compiler") sync_cout << compiler_info() << sync_endl;
|
||||
else if (token == "export_net")
|
||||
{
|
||||
std::optional<std::string> filename;
|
||||
std::string f;
|
||||
if (is >> skipws >> f)
|
||||
filename = f;
|
||||
Eval::NNUE::save_eval(filename);
|
||||
}
|
||||
else if (!token.empty() && token[0] != '#')
|
||||
sync_cout << "Unknown command: " << cmd << sync_endl;
|
||||
|
||||
} while (token != "quit" && argc == 1); // Command line args are one-shot
|
||||
}
|
||||
|
||||
|
||||
/// UCI::value() converts a Value to a string suitable for use with the UCI
|
||||
/// protocol specification:
|
||||
///
|
||||
/// cp <x> The score from the engine's point of view in centipawns.
|
||||
/// mate <y> Mate in y moves, not plies. If the engine is getting mated
|
||||
/// use negative values for y.
|
||||
|
||||
string UCI::value(Value v) {
|
||||
|
||||
assert(-VALUE_INFINITE < v && v < VALUE_INFINITE);
|
||||
|
||||
stringstream ss;
|
||||
|
||||
if (abs(v) < VALUE_MATE_IN_MAX_PLY)
|
||||
ss << "cp " << v * 100 / PawnValueEg;
|
||||
else
|
||||
ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
/// UCI::wdl() report WDL statistics given an evaluation and a game ply, based on
|
||||
/// data gathered for fishtest LTC games.
|
||||
|
||||
string UCI::wdl(Value v, int ply) {
|
||||
|
||||
stringstream ss;
|
||||
|
||||
int wdl_w = win_rate_model( v, ply);
|
||||
int wdl_l = win_rate_model(-v, ply);
|
||||
int wdl_d = 1000 - wdl_w - wdl_l;
|
||||
ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.)
|
||||
|
||||
std::string UCI::square(Square s) {
|
||||
return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) };
|
||||
}
|
||||
|
||||
|
||||
/// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q).
|
||||
/// The only special case is castling, where we print in the e1g1 notation in
|
||||
/// normal chess mode, and in e1h1 notation in chess960 mode. Internally all
|
||||
/// castling moves are always encoded as 'king captures rook'.
|
||||
|
||||
string UCI::move(Move m, bool chess960) {
|
||||
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
|
||||
if (m == MOVE_NONE)
|
||||
return "(none)";
|
||||
|
||||
if (m == MOVE_NULL)
|
||||
return "0000";
|
||||
|
||||
if (type_of(m) == CASTLING && !chess960)
|
||||
to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
|
||||
|
||||
string move = UCI::square(from) + UCI::square(to);
|
||||
|
||||
if (type_of(m) == PROMOTION)
|
||||
move += " pnbrqk"[promotion_type(m)];
|
||||
|
||||
return move;
|
||||
}
|
||||
|
||||
|
||||
/// UCI::to_move() converts a string representing a move in coordinate notation
|
||||
/// (g1f3, a7a8q) to the corresponding legal Move, if any.
|
||||
|
||||
Move UCI::to_move(const Position& pos, string& str) {
|
||||
|
||||
if (str.length() == 5) // Junior could send promotion piece in uppercase
|
||||
str[4] = char(tolower(str[4]));
|
||||
|
||||
for (const auto& m : MoveList<LEGAL>(pos))
|
||||
if (str == UCI::move(m, pos.is_chess960()))
|
||||
return m;
|
||||
|
||||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,70 +17,15 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UCI_H_INCLUDED
|
||||
|
||||
#if !defined(UCI_H_INCLUDED)
|
||||
#define UCI_H_INCLUDED
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
#include "types.h"
|
||||
extern void uci_main_loop();
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
class Position;
|
||||
|
||||
namespace UCI {
|
||||
|
||||
class Option;
|
||||
|
||||
/// Custom comparator because UCI options should be case insensitive
|
||||
struct CaseInsensitiveLess {
|
||||
bool operator() (const std::string&, const std::string&) const;
|
||||
};
|
||||
|
||||
/// Our options container is actually a std::map
|
||||
typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap;
|
||||
|
||||
/// Option class implements an option as defined by UCI protocol
|
||||
class Option {
|
||||
|
||||
typedef void (*OnChange)(const Option&);
|
||||
|
||||
public:
|
||||
Option(OnChange = nullptr);
|
||||
Option(bool v, OnChange = nullptr);
|
||||
Option(const char* v, OnChange = nullptr);
|
||||
Option(double v, int minv, int maxv, OnChange = nullptr);
|
||||
Option(const char* v, const char* cur, OnChange = nullptr);
|
||||
|
||||
Option& operator=(const std::string&);
|
||||
void operator<<(const Option&);
|
||||
operator double() const;
|
||||
operator std::string() const;
|
||||
bool operator==(const char*) const;
|
||||
|
||||
private:
|
||||
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
|
||||
|
||||
std::string defaultValue, currentValue, type;
|
||||
int min, max;
|
||||
size_t idx;
|
||||
OnChange on_change;
|
||||
};
|
||||
|
||||
void init(OptionsMap&);
|
||||
void loop(int argc, char* argv[]);
|
||||
std::string value(Value v);
|
||||
std::string square(Square s);
|
||||
std::string move(Move m, bool chess960);
|
||||
std::string pv(const Position& pos, Depth depth, Value alpha, Value beta);
|
||||
std::string wdl(Value v, int ply);
|
||||
Move to_move(const Position& pos, std::string& str);
|
||||
|
||||
} // namespace UCI
|
||||
|
||||
extern UCI::OptionsMap Options;
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef UCI_H_INCLUDED
|
||||
#endif // !defined(UCI_H_INCLUDED)
|
||||
|
||||
+312
-145
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,179 +17,345 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "evaluate.h"
|
||||
#include "misc.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "tt.h"
|
||||
#include "uci.h"
|
||||
#include "syzygy/tbprobe.h"
|
||||
#include "ucioption.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace Stockfish {
|
||||
////
|
||||
//// Local definitions
|
||||
////
|
||||
|
||||
UCI::OptionsMap Options; // Global object
|
||||
namespace {
|
||||
|
||||
namespace UCI {
|
||||
///
|
||||
/// Types
|
||||
///
|
||||
|
||||
/// 'On change' actions, triggered by an option's value change
|
||||
void on_clear_hash(const Option&) { Search::clear(); }
|
||||
void on_hash_size(const Option& o) { TT.resize(size_t(o)); }
|
||||
void on_logger(const Option& o) { start_logger(o); }
|
||||
void on_threads(const Option& o) { Threads.set(size_t(o)); }
|
||||
void on_tb_path(const Option& o) { Tablebases::init(o); }
|
||||
void on_use_NNUE(const Option& ) { Eval::NNUE::init(); }
|
||||
void on_eval_file(const Option& ) { Eval::NNUE::init(); }
|
||||
enum OptionType { SPIN, COMBO, CHECK, STRING, BUTTON };
|
||||
|
||||
/// Our case insensitive less() function as required by UCI protocol
|
||||
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
|
||||
typedef std::vector<string> ComboValues;
|
||||
|
||||
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(),
|
||||
[](char c1, char c2) { return tolower(c1) < tolower(c2); });
|
||||
}
|
||||
struct Option {
|
||||
|
||||
string name, defaultValue, currentValue;
|
||||
OptionType type;
|
||||
size_t idx;
|
||||
int minValue, maxValue;
|
||||
ComboValues comboValues;
|
||||
|
||||
/// UCI::init() initializes the UCI options to their hard-coded default values
|
||||
Option();
|
||||
Option(const char* defaultValue, OptionType = STRING);
|
||||
Option(bool defaultValue, OptionType = CHECK);
|
||||
Option(int defaultValue, int minValue, int maxValue);
|
||||
|
||||
void init(OptionsMap& o) {
|
||||
bool operator<(const Option& o) const { return this->idx < o.idx; }
|
||||
};
|
||||
|
||||
constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048;
|
||||
typedef std::map<string, Option> Options;
|
||||
|
||||
o["Debug Log File"] << Option("", on_logger);
|
||||
o["Threads"] << Option(1, 1, 512, on_threads);
|
||||
o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size);
|
||||
o["Clear Hash"] << Option(on_clear_hash);
|
||||
o["Ponder"] << Option(false);
|
||||
o["MultiPV"] << Option(1, 1, 500);
|
||||
o["Skill Level"] << Option(20, 0, 20);
|
||||
o["Move Overhead"] << Option(10, 0, 5000);
|
||||
o["Slow Mover"] << Option(100, 10, 1000);
|
||||
o["nodestime"] << Option(0, 0, 10000);
|
||||
o["UCI_Chess960"] << Option(false);
|
||||
o["UCI_AnalyseMode"] << Option(false);
|
||||
o["UCI_LimitStrength"] << Option(false);
|
||||
o["UCI_Elo"] << Option(1350, 1350, 2850);
|
||||
o["UCI_ShowWDL"] << Option(false);
|
||||
o["SyzygyPath"] << Option("<empty>", on_tb_path);
|
||||
o["SyzygyProbeDepth"] << Option(1, 1, 100);
|
||||
o["Syzygy50MoveRule"] << Option(true);
|
||||
o["SyzygyProbeLimit"] << Option(7, 0, 7);
|
||||
o["Use NNUE"] << Option(true, on_use_NNUE);
|
||||
o["EvalFile"] << Option(EvalFileDefaultName, on_eval_file);
|
||||
}
|
||||
///
|
||||
/// Constants
|
||||
///
|
||||
|
||||
// load_defaults populates the options map with the hard
|
||||
// coded names and default values.
|
||||
|
||||
/// operator<<() is used to print all the options default values in chronological
|
||||
/// insertion order (the idx field) and in the format defined by the UCI protocol.
|
||||
void load_defaults(Options& o) {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
|
||||
o["Use Search Log"] = Option(false);
|
||||
o["Search Log Filename"] = Option("SearchLog.txt");
|
||||
o["Book File"] = Option("book.bin");
|
||||
o["Mobility (Middle Game)"] = Option(100, 0, 200);
|
||||
o["Mobility (Endgame)"] = Option(100, 0, 200);
|
||||
o["Pawn Structure (Middle Game)"] = Option(100, 0, 200);
|
||||
o["Pawn Structure (Endgame)"] = Option(100, 0, 200);
|
||||
o["Passed Pawns (Middle Game)"] = Option(100, 0, 200);
|
||||
o["Passed Pawns (Endgame)"] = Option(100, 0, 200);
|
||||
o["Space"] = Option(100, 0, 200);
|
||||
o["Aggressiveness"] = Option(100, 0, 200);
|
||||
o["Cowardice"] = Option(100, 0, 200);
|
||||
o["King Safety Curve"] = Option("Quadratic", COMBO);
|
||||
|
||||
for (size_t idx = 0; idx < om.size(); ++idx)
|
||||
for (const auto& it : om)
|
||||
if (it.second.idx == idx)
|
||||
{
|
||||
const Option& o = it.second;
|
||||
os << "\noption name " << it.first << " type " << o.type;
|
||||
o["King Safety Curve"].comboValues.push_back("Quadratic");
|
||||
o["King Safety Curve"].comboValues.push_back("Linear"); /*, "From File"*/
|
||||
|
||||
if (o.type == "string" || o.type == "check" || o.type == "combo")
|
||||
os << " default " << o.defaultValue;
|
||||
o["King Safety Coefficient"] = Option(40, 1, 100);
|
||||
o["King Safety X Intercept"] = Option(0, 0, 20);
|
||||
o["King Safety Max Slope"] = Option(30, 10, 100);
|
||||
o["King Safety Max Value"] = Option(500, 100, 1000);
|
||||
o["Queen Contact Check Bonus"] = Option(3, 0, 8);
|
||||
o["Queen Check Bonus"] = Option(2, 0, 4);
|
||||
o["Rook Check Bonus"] = Option(1, 0, 4);
|
||||
o["Bishop Check Bonus"] = Option(1, 0, 4);
|
||||
o["Knight Check Bonus"] = Option(1, 0, 4);
|
||||
o["Discovered Check Bonus"] = Option(3, 0, 8);
|
||||
o["Mate Threat Bonus"] = Option(3, 0, 8);
|
||||
o["Check Extension (PV nodes)"] = Option(2, 0, 2);
|
||||
o["Check Extension (non-PV nodes)"] = Option(1, 0, 2);
|
||||
o["Single Reply Extension (PV nodes)"] = Option(2, 0, 2);
|
||||
o["Single Reply Extension (non-PV nodes)"] = Option(2, 0, 2);
|
||||
o["Mate Threat Extension (PV nodes)"] = Option(0, 0, 2);
|
||||
o["Mate Threat Extension (non-PV nodes)"] = Option(0, 0, 2);
|
||||
o["Pawn Push to 7th Extension (PV nodes)"] = Option(1, 0, 2);
|
||||
o["Pawn Push to 7th Extension (non-PV nodes)"] = Option(1, 0, 2);
|
||||
o["Passed Pawn Extension (PV nodes)"] = Option(1, 0, 2);
|
||||
o["Passed Pawn Extension (non-PV nodes)"] = Option(0, 0, 2);
|
||||
o["Pawn Endgame Extension (PV nodes)"] = Option(2, 0, 2);
|
||||
o["Pawn Endgame Extension (non-PV nodes)"] = Option(2, 0, 2);
|
||||
o["Full Depth Moves (PV nodes)"] = Option(10, 1, 100);
|
||||
o["Full Depth Moves (non-PV nodes)"] = Option(3, 1, 100);
|
||||
o["Threat Depth"] = Option(5, 0, 100);
|
||||
o["Randomness"] = Option(0, 0, 10);
|
||||
o["Minimum Split Depth"] = Option(4, 4, 7);
|
||||
o["Maximum Number of Threads per Split Point"] = Option(5, 4, 8);
|
||||
o["Threads"] = Option(1, 1, THREAD_MAX);
|
||||
o["Hash"] = Option(32, 4, 8192);
|
||||
o["Clear Hash"] = Option(false, BUTTON);
|
||||
o["New Game"] = Option(false, BUTTON);
|
||||
o["Ponder"] = Option(true);
|
||||
o["OwnBook"] = Option(true);
|
||||
o["MultiPV"] = Option(1, 1, 500);
|
||||
o["UCI_ShowCurrLine"] = Option(false);
|
||||
o["UCI_Chess960"] = Option(false);
|
||||
o["UCI_AnalyseMode"] = Option(false);
|
||||
|
||||
if (o.type == "spin")
|
||||
os << " default " << int(stof(o.defaultValue))
|
||||
<< " min " << o.min
|
||||
<< " max " << o.max;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
/// Option class constructors and conversion operators
|
||||
|
||||
Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f)
|
||||
{ defaultValue = currentValue = v; }
|
||||
|
||||
Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), on_change(f)
|
||||
{ defaultValue = currentValue = (v ? "true" : "false"); }
|
||||
|
||||
Option::Option(OnChange f) : type("button"), min(0), max(0), on_change(f)
|
||||
{}
|
||||
|
||||
Option::Option(double v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f)
|
||||
{ defaultValue = currentValue = std::to_string(v); }
|
||||
|
||||
Option::Option(const char* v, const char* cur, OnChange f) : type("combo"), min(0), max(0), on_change(f)
|
||||
{ defaultValue = v; currentValue = cur; }
|
||||
|
||||
Option::operator double() const {
|
||||
assert(type == "check" || type == "spin");
|
||||
return (type == "spin" ? stof(currentValue) : currentValue == "true");
|
||||
}
|
||||
|
||||
Option::operator std::string() const {
|
||||
assert(type == "string");
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
bool Option::operator==(const char* s) const {
|
||||
assert(type == "combo");
|
||||
return !CaseInsensitiveLess()(currentValue, s)
|
||||
&& !CaseInsensitiveLess()(s, currentValue);
|
||||
}
|
||||
|
||||
|
||||
/// operator<<() inits options and assigns idx in the correct printing order
|
||||
|
||||
void Option::operator<<(const Option& o) {
|
||||
|
||||
static size_t insert_order = 0;
|
||||
|
||||
*this = o;
|
||||
idx = insert_order++;
|
||||
}
|
||||
|
||||
|
||||
/// operator=() updates currentValue and triggers on_change() action. It's up to
|
||||
/// the GUI to check for option's limits, but we could receive the new value
|
||||
/// from the user by console window, so let's check the bounds anyway.
|
||||
|
||||
Option& Option::operator=(const string& v) {
|
||||
|
||||
assert(!type.empty());
|
||||
|
||||
if ( (type != "button" && type != "string" && v.empty())
|
||||
|| (type == "check" && v != "true" && v != "false")
|
||||
|| (type == "spin" && (stof(v) < min || stof(v) > max)))
|
||||
return *this;
|
||||
|
||||
if (type == "combo")
|
||||
{
|
||||
OptionsMap comboMap; // To have case insensitive compare
|
||||
string token;
|
||||
std::istringstream ss(defaultValue);
|
||||
while (ss >> token)
|
||||
comboMap[token] << Option();
|
||||
if (!comboMap.count(v) || v == "var")
|
||||
return *this;
|
||||
// Any option should know its name so to be easily printed
|
||||
for (Options::iterator it = o.begin(); it != o.end(); ++it)
|
||||
it->second.name = it->first;
|
||||
}
|
||||
|
||||
if (type != "button")
|
||||
currentValue = v;
|
||||
///
|
||||
/// Variables
|
||||
///
|
||||
|
||||
if (on_change)
|
||||
on_change(*this);
|
||||
Options options;
|
||||
|
||||
// stringify converts a value of type T to a std::string
|
||||
template<typename T>
|
||||
string stringify(const T& v) {
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << v;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
// get_option_value implements the various get_option_value_<type>
|
||||
// functions defined later, because only the option value
|
||||
// type changes a template seems a proper solution.
|
||||
|
||||
template<typename T>
|
||||
T get_option_value(const string& optionName) {
|
||||
|
||||
T ret = T();
|
||||
if (options.find(optionName) == options.end())
|
||||
return ret;
|
||||
|
||||
std::istringstream ss(options[optionName].currentValue);
|
||||
ss >> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Specialization for std::string where instruction 'ss >> ret;'
|
||||
// would erroneusly tokenize a string with spaces.
|
||||
|
||||
template<>
|
||||
string get_option_value<string>(const string& optionName) {
|
||||
|
||||
if (options.find(optionName) == options.end())
|
||||
return string();
|
||||
|
||||
return options[optionName].currentValue;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace UCI
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
} // namespace Stockfish
|
||||
/// init_uci_options() initializes the UCI options. Currently, the only
|
||||
/// thing this function does is to initialize the default value of the
|
||||
/// "Threads" parameter to the number of available CPU cores.
|
||||
|
||||
void init_uci_options() {
|
||||
|
||||
load_defaults(options);
|
||||
|
||||
// Set optimal value for parameter "Minimum Split Depth"
|
||||
// according to number of available cores.
|
||||
assert(options.find("Threads") != options.end());
|
||||
assert(options.find("Minimum Split Depth") != options.end());
|
||||
|
||||
Option& thr = options["Threads"];
|
||||
Option& msd = options["Minimum Split Depth"];
|
||||
|
||||
thr.defaultValue = thr.currentValue = stringify(cpu_count());
|
||||
|
||||
if (cpu_count() >= 8)
|
||||
msd.defaultValue = msd.currentValue = stringify(7);
|
||||
}
|
||||
|
||||
|
||||
/// print_uci_options() prints all the UCI options to the standard output,
|
||||
/// in the format defined by the UCI protocol.
|
||||
|
||||
void print_uci_options() {
|
||||
|
||||
static const char optionTypeName[][16] = {
|
||||
"spin", "combo", "check", "string", "button"
|
||||
};
|
||||
|
||||
// Build up a vector out of the options map and sort it according to idx
|
||||
// field, that is the chronological insertion order in options map.
|
||||
std::vector<Option> vec;
|
||||
for (Options::const_iterator it = options.begin(); it != options.end(); ++it)
|
||||
vec.push_back(it->second);
|
||||
|
||||
std::sort(vec.begin(), vec.end());
|
||||
|
||||
for (std::vector<Option>::const_iterator it = vec.begin(); it != vec.end(); ++it)
|
||||
{
|
||||
std::cout << "\noption name " << it->name
|
||||
<< " type " << optionTypeName[it->type];
|
||||
|
||||
if (it->type == BUTTON)
|
||||
continue;
|
||||
|
||||
if (it->type == CHECK)
|
||||
std::cout << " default " << (it->defaultValue == "1" ? "true" : "false");
|
||||
else
|
||||
std::cout << " default " << it->defaultValue;
|
||||
|
||||
if (it->type == SPIN)
|
||||
std::cout << " min " << it->minValue << " max " << it->maxValue;
|
||||
else if (it->type == COMBO)
|
||||
for (ComboValues::const_iterator itc = it->comboValues.begin();
|
||||
itc != it->comboValues.end(); ++itc)
|
||||
std::cout << " var " << *itc;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
|
||||
/// get_option_value_bool() returns the current value of a UCI parameter of
|
||||
/// type "check".
|
||||
|
||||
bool get_option_value_bool(const string& optionName) {
|
||||
|
||||
return get_option_value<bool>(optionName);
|
||||
}
|
||||
|
||||
|
||||
/// get_option_value_int() returns the value of a UCI parameter as an integer.
|
||||
/// Normally, this function will be used for a parameter of type "spin", but
|
||||
/// it could also be used with a "combo" parameter, where all the available
|
||||
/// values are integers.
|
||||
|
||||
int get_option_value_int(const string& optionName) {
|
||||
|
||||
return get_option_value<int>(optionName);
|
||||
}
|
||||
|
||||
|
||||
/// get_option_value_string() returns the current value of a UCI parameter as
|
||||
/// a string. It is used with parameters of type "combo" and "string".
|
||||
|
||||
string get_option_value_string(const string& optionName) {
|
||||
|
||||
return get_option_value<string>(optionName);
|
||||
}
|
||||
|
||||
|
||||
/// set_option_value() inserts a new value for a UCI parameter. Note that
|
||||
/// the function does not check that the new value is legal for the given
|
||||
/// parameter: This is assumed to be the responsibility of the GUI.
|
||||
|
||||
void set_option_value(const string& name, const string& value) {
|
||||
|
||||
// UCI protocol uses "true" and "false" instead of "1" and "0", so convert
|
||||
// value according to standard C++ convention before to store it.
|
||||
string v(value);
|
||||
if (v == "true")
|
||||
v = "1";
|
||||
else if (v == "false")
|
||||
v = "0";
|
||||
|
||||
if (options.find(name) == options.end())
|
||||
{
|
||||
std::cout << "No such option: " << name << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Normally it's up to the GUI to check for option's limits,
|
||||
// but we could receive the new value directly from the user
|
||||
// by teminal window. So let's check the bounds anyway.
|
||||
Option& opt = options[name];
|
||||
|
||||
if (opt.type == CHECK && v != "0" && v != "1")
|
||||
return;
|
||||
|
||||
else if (opt.type == SPIN)
|
||||
{
|
||||
int val = atoi(v.c_str());
|
||||
if (val < opt.minValue || val > opt.maxValue)
|
||||
return;
|
||||
}
|
||||
|
||||
opt.currentValue = v;
|
||||
}
|
||||
|
||||
|
||||
/// push_button() is used to tell the engine that a UCI parameter of type
|
||||
/// "button" has been selected:
|
||||
|
||||
void push_button(const string& buttonName) {
|
||||
|
||||
set_option_value(buttonName, "true");
|
||||
}
|
||||
|
||||
|
||||
/// button_was_pressed() tests whether a UCI parameter of type "button" has
|
||||
/// been selected since the last time the function was called, in this case
|
||||
/// it also resets the button.
|
||||
|
||||
bool button_was_pressed(const string& buttonName) {
|
||||
|
||||
if (!get_option_value<bool>(buttonName))
|
||||
return false;
|
||||
|
||||
set_option_value(buttonName, "false");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
// Define constructors of Option class.
|
||||
|
||||
Option::Option() {} // To allow insertion in a std::map
|
||||
|
||||
Option::Option(const char* def, OptionType t)
|
||||
: defaultValue(def), currentValue(def), type(t), idx(options.size()), minValue(0), maxValue(0) {}
|
||||
|
||||
Option::Option(bool def, OptionType t)
|
||||
: defaultValue(stringify(def)), currentValue(stringify(def)), type(t), idx(options.size()), minValue(0), maxValue(0) {}
|
||||
|
||||
Option::Option(int def, int minv, int maxv)
|
||||
: defaultValue(stringify(def)), currentValue(stringify(def)), type(SPIN), idx(options.size()), minValue(minv), maxValue(maxv) {}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(UCIOPTION_H_INCLUDED)
|
||||
#define UCIOPTION_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <string>
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern void init_uci_options();
|
||||
extern void print_uci_options();
|
||||
extern bool get_option_value_bool(const std::string& optionName);
|
||||
extern int get_option_value_int(const std::string& optionName);
|
||||
extern std::string get_option_value_string(const std::string& optionName);
|
||||
extern bool button_was_pressed(const std::string& buttonName);
|
||||
extern void set_option_value(const std::string& optionName,const std::string& newValue);
|
||||
extern void push_button(const std::string& buttonName);
|
||||
|
||||
|
||||
#endif // !defined(UCIOPTION_H_INCLUDED)
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "value.h"
|
||||
|
||||
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
/// value_to_tt() adjusts a mate score from "plies to mate from the root" to
|
||||
/// "plies to mate from the current ply". Non-mate scores are unchanged.
|
||||
/// The function is called before storing a value to the transposition table.
|
||||
|
||||
Value value_to_tt(Value v, int ply) {
|
||||
if(v >= value_mate_in(100))
|
||||
return v + ply;
|
||||
else if(v <= value_mated_in(100))
|
||||
return v - ply;
|
||||
else
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/// value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score
|
||||
/// from the transposition table to a mate score corrected for the current
|
||||
/// ply depth.
|
||||
|
||||
Value value_from_tt(Value v, int ply) {
|
||||
if(v >= value_mate_in(100))
|
||||
return v - ply;
|
||||
else if(v <= value_mated_in(100))
|
||||
return v + ply;
|
||||
else
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/// value_to_centipawns() converts a value from Stockfish's somewhat unusual
|
||||
/// scale of pawn = 256 to the more conventional pawn = 100.
|
||||
|
||||
int value_to_centipawns(Value v) {
|
||||
return (int(v) * 100) / int(PawnValueMidgame);
|
||||
}
|
||||
|
||||
|
||||
/// value_from_centipawns() converts a centipawn value to Stockfish's internal
|
||||
/// evaluation scale. It's used when reading the values of UCI options
|
||||
/// containing material values (e.g. futility pruning margins).
|
||||
|
||||
Value value_from_centipawns(int cp) {
|
||||
return Value((cp * 256) / 100);
|
||||
}
|
||||
|
||||
|
||||
/// value_to_string() converts a value to a string suitable for use with the
|
||||
/// UCI protocol.
|
||||
|
||||
const std::string value_to_string(Value v) {
|
||||
std::stringstream s;
|
||||
|
||||
if(abs(v) < VALUE_MATE - 200)
|
||||
s << "cp " << value_to_centipawns(v);
|
||||
else {
|
||||
s << "mate ";
|
||||
if(v > 0)
|
||||
s << (VALUE_MATE - v + 1) / 2;
|
||||
else
|
||||
s << -(VALUE_MATE + v) / 2;
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2009 Marco Costalba
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
|
||||
#if !defined(VALUE_H_INCLUDED)
|
||||
#define VALUE_H_INCLUDED
|
||||
|
||||
////
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "piece.h"
|
||||
|
||||
|
||||
////
|
||||
//// Types
|
||||
////
|
||||
|
||||
enum ValueType {
|
||||
VALUE_TYPE_NONE = 0,
|
||||
VALUE_TYPE_UPPER = 1, // Upper bound
|
||||
VALUE_TYPE_LOWER = 2, // Lower bound
|
||||
VALUE_TYPE_EXACT = 3, // Exact score
|
||||
VALUE_TYPE_EVAL = 4, // Evaluation cache
|
||||
VALUE_TYPE_EV_UP = 5, // Evaluation cache for upper bound
|
||||
VALUE_TYPE_EV_LO = 6 // Evaluation cache for lower bound
|
||||
};
|
||||
|
||||
|
||||
enum Value {
|
||||
VALUE_DRAW = 0,
|
||||
VALUE_KNOWN_WIN = 15000,
|
||||
VALUE_MATE = 30000,
|
||||
VALUE_INFINITE = 30001,
|
||||
VALUE_NONE = 30002,
|
||||
VALUE_ENSURE_SIGNED = -1
|
||||
};
|
||||
|
||||
|
||||
/// Score enum keeps a midgame and an endgame value in a single
|
||||
/// integer (enum), first LSB 16 bits are used to store endgame
|
||||
/// value, while upper bits are used for midgame value.
|
||||
|
||||
// Compiler is free to choose the enum type as long as can keep
|
||||
// its data, so ensure Score to be an integer type.
|
||||
enum Score { ENSURE_32_BITS_SIZE_P = (1 << 16), ENSURE_32_BITS_SIZE_N = -(1 << 16)};
|
||||
|
||||
// Extracting the _signed_ lower and upper 16 bits it 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 mg_value(Score s) { return Value(((int(s) + 32768) & ~0xffff) / 0x10000); }
|
||||
|
||||
// Unfortunatly on Intel 64 bit we have a small speed regression, so use a faster code in
|
||||
// this case, although not 100% standard compliant it seems to work for Intel and MSVC.
|
||||
#if defined(IS_64BIT) && (!defined(__GNUC__) || defined(__INTEL_COMPILER))
|
||||
inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); }
|
||||
#else
|
||||
inline Value eg_value(Score s) { return Value((int)(unsigned(s) & 0x7fffu) - (int)(unsigned(s) & 0x8000u)); }
|
||||
#endif
|
||||
|
||||
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
|
||||
|
||||
inline Score operator-(Score s) { return Score(-int(s)); }
|
||||
inline Score operator+(Score s1, Score s2) { return Score(int(s1) + int(s2)); }
|
||||
inline Score operator-(Score s1, Score s2) { return Score(int(s1) - int(s2)); }
|
||||
inline void operator+=(Score& s1, Score s2) { s1 = Score(int(s1) + int(s2)); }
|
||||
inline void operator-=(Score& s1, Score s2) { s1 = Score(int(s1) - int(s2)); }
|
||||
inline Score operator*(int i, Score s) { return Score(i * int(s)); }
|
||||
|
||||
// Division 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); }
|
||||
|
||||
// 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.
|
||||
inline Score operator*(Score s1, Score s2);
|
||||
|
||||
|
||||
////
|
||||
//// Constants and variables
|
||||
////
|
||||
|
||||
/// Piece values, middle game and endgame
|
||||
|
||||
/// Important: If the material values are changed, one must also
|
||||
/// adjust the piece square tables, and the method game_phase() in the
|
||||
/// Position class!
|
||||
///
|
||||
/// Values modified by Joona Kiiski
|
||||
|
||||
const Value PawnValueMidgame = Value(0x0C6);
|
||||
const Value PawnValueEndgame = Value(0x102);
|
||||
const Value KnightValueMidgame = Value(0x331);
|
||||
const Value KnightValueEndgame = Value(0x34E);
|
||||
const Value BishopValueMidgame = Value(0x344);
|
||||
const Value BishopValueEndgame = Value(0x359);
|
||||
const Value RookValueMidgame = Value(0x4F6);
|
||||
const Value RookValueEndgame = Value(0x4FE);
|
||||
const Value QueenValueMidgame = Value(0x9D9);
|
||||
const Value QueenValueEndgame = Value(0x9FE);
|
||||
|
||||
const Value PieceValueMidgame[17] = {
|
||||
Value(0),
|
||||
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
|
||||
RookValueMidgame, QueenValueMidgame,
|
||||
Value(0), Value(0), Value(0),
|
||||
PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
|
||||
RookValueMidgame, QueenValueMidgame,
|
||||
Value(0), Value(0), Value(0)
|
||||
};
|
||||
|
||||
const Value PieceValueEndgame[17] = {
|
||||
Value(0),
|
||||
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
|
||||
RookValueEndgame, QueenValueEndgame,
|
||||
Value(0), Value(0), Value(0),
|
||||
PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
|
||||
RookValueEndgame, QueenValueEndgame,
|
||||
Value(0), Value(0), Value(0)
|
||||
};
|
||||
|
||||
/// Bonus for having the side to move (modified by Joona Kiiski)
|
||||
|
||||
const Score TempoValue = make_score(48, 22);
|
||||
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
inline Value operator+ (Value v, int i) { return Value(int(v) + i); }
|
||||
inline Value operator+ (Value v1, Value v2) { return Value(int(v1) + int(v2)); }
|
||||
inline void operator+= (Value &v1, Value v2) {
|
||||
v1 = Value(int(v1) + int(v2));
|
||||
}
|
||||
inline Value operator- (Value v, int i) { return Value(int(v) - i); }
|
||||
inline Value operator- (Value v) { return Value(-int(v)); }
|
||||
inline Value operator- (Value v1, Value v2) { return Value(int(v1) - int(v2)); }
|
||||
inline void operator-= (Value &v1, Value v2) {
|
||||
v1 = Value(int(v1) - int(v2));
|
||||
}
|
||||
inline Value operator* (Value v, int i) { return Value(int(v) * i); }
|
||||
inline void operator*= (Value &v, int i) { v = Value(int(v) * i); }
|
||||
inline Value operator* (int i, Value v) { return Value(int(v) * i); }
|
||||
inline Value operator/ (Value v, int i) { return Value(int(v) / i); }
|
||||
inline void operator/= (Value &v, int i) { v = Value(int(v) / i); }
|
||||
|
||||
|
||||
inline Value value_mate_in(int ply) {
|
||||
return Value(VALUE_MATE - Value(ply));
|
||||
}
|
||||
|
||||
inline Value value_mated_in(int ply) {
|
||||
return Value(-VALUE_MATE + Value(ply));
|
||||
}
|
||||
|
||||
inline bool is_upper_bound(ValueType vt) {
|
||||
return (int(vt) & int(VALUE_TYPE_UPPER)) != 0;
|
||||
}
|
||||
|
||||
inline bool is_lower_bound(ValueType vt) {
|
||||
return (int(vt) & int(VALUE_TYPE_LOWER)) != 0;
|
||||
}
|
||||
|
||||
inline Value piece_value_midgame(PieceType pt) {
|
||||
return PieceValueMidgame[pt];
|
||||
}
|
||||
|
||||
inline Value piece_value_endgame(PieceType pt) {
|
||||
return PieceValueEndgame[pt];
|
||||
}
|
||||
|
||||
inline Value piece_value_midgame(Piece p) {
|
||||
return PieceValueMidgame[p];
|
||||
}
|
||||
|
||||
inline Value piece_value_endgame(Piece p) {
|
||||
return PieceValueEndgame[p];
|
||||
}
|
||||
|
||||
|
||||
////
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern Value value_to_tt(Value v, int ply);
|
||||
extern Value value_from_tt(Value v, int ply);
|
||||
extern int value_to_centipawns(Value v);
|
||||
extern Value value_from_centipawns(int cp);
|
||||
extern const std::string value_to_string(Value v);
|
||||
|
||||
|
||||
#endif // !defined(VALUE_H_INCLUDED)
|
||||
@@ -1,145 +0,0 @@
|
||||
#!/bin/bash
|
||||
# check for errors under valgrind or sanitizers.
|
||||
|
||||
error()
|
||||
{
|
||||
echo "instrumented testing failed on line $1"
|
||||
exit 1
|
||||
}
|
||||
trap 'error ${LINENO}' ERR
|
||||
|
||||
# define suitable post and prefixes for testing options
|
||||
case $1 in
|
||||
--valgrind)
|
||||
echo "valgrind testing started"
|
||||
prefix=''
|
||||
exeprefix='valgrind --error-exitcode=42 --errors-for-leak-kinds=all --leak-check=full'
|
||||
postfix='1>/dev/null'
|
||||
threads="1"
|
||||
;;
|
||||
--valgrind-thread)
|
||||
echo "valgrind-thread testing started"
|
||||
prefix=''
|
||||
exeprefix='valgrind --fair-sched=try --error-exitcode=42'
|
||||
postfix='1>/dev/null'
|
||||
threads="2"
|
||||
;;
|
||||
--sanitizer-undefined)
|
||||
echo "sanitizer-undefined testing started"
|
||||
prefix='!'
|
||||
exeprefix=''
|
||||
postfix='2>&1 | grep -A50 "runtime error:"'
|
||||
threads="1"
|
||||
;;
|
||||
--sanitizer-thread)
|
||||
echo "sanitizer-thread testing started"
|
||||
prefix='!'
|
||||
exeprefix=''
|
||||
postfix='2>&1 | grep -A50 "WARNING: ThreadSanitizer:"'
|
||||
threads="2"
|
||||
|
||||
cat << EOF > tsan.supp
|
||||
race:Stockfish::TTEntry::move
|
||||
race:Stockfish::TTEntry::depth
|
||||
race:Stockfish::TTEntry::bound
|
||||
race:Stockfish::TTEntry::save
|
||||
race:Stockfish::TTEntry::value
|
||||
race:Stockfish::TTEntry::eval
|
||||
race:Stockfish::TTEntry::is_pv
|
||||
|
||||
race:Stockfish::TranspositionTable::probe
|
||||
race:Stockfish::TranspositionTable::hashfull
|
||||
|
||||
EOF
|
||||
|
||||
export TSAN_OPTIONS="suppressions=./tsan.supp"
|
||||
|
||||
;;
|
||||
*)
|
||||
echo "unknown testing started"
|
||||
prefix=''
|
||||
exeprefix=''
|
||||
postfix=''
|
||||
threads="1"
|
||||
;;
|
||||
esac
|
||||
|
||||
# simple command line testing
|
||||
for args in "eval" \
|
||||
"go nodes 1000" \
|
||||
"go depth 10" \
|
||||
"go movetime 1000" \
|
||||
"go wtime 8000 btime 8000 winc 500 binc 500" \
|
||||
"bench 128 $threads 8 default depth"
|
||||
do
|
||||
|
||||
echo "$prefix $exeprefix ./stockfish $args $postfix"
|
||||
eval "$prefix $exeprefix ./stockfish $args $postfix"
|
||||
|
||||
done
|
||||
|
||||
# more general testing, following an uci protocol exchange
|
||||
cat << EOF > game.exp
|
||||
set timeout 240
|
||||
spawn $exeprefix ./stockfish
|
||||
|
||||
send "uci\n"
|
||||
expect "uciok"
|
||||
|
||||
send "setoption name Threads value $threads\n"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position startpos\n"
|
||||
send "go nodes 1000\n"
|
||||
expect "bestmove"
|
||||
|
||||
send "position startpos moves e2e4 e7e6\n"
|
||||
send "go nodes 1000\n"
|
||||
expect "bestmove"
|
||||
|
||||
send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n"
|
||||
send "go depth 10\n"
|
||||
expect "bestmove"
|
||||
|
||||
send "quit\n"
|
||||
expect eof
|
||||
|
||||
# return error code of the spawned program, useful for valgrind
|
||||
lassign [wait] pid spawnid os_error_flag value
|
||||
exit \$value
|
||||
EOF
|
||||
|
||||
#download TB as needed
|
||||
if [ ! -d ../tests/syzygy ]; then
|
||||
curl -sL https://api.github.com/repos/niklasf/python-chess/tarball/9b9aa13f9f36d08aadfabff872882f4ab1494e95 | tar -xzf -
|
||||
mv niklasf-python-chess-9b9aa13 ../tests/syzygy
|
||||
fi
|
||||
|
||||
cat << EOF > syzygy.exp
|
||||
set timeout 240
|
||||
spawn $exeprefix ./stockfish
|
||||
send "uci\n"
|
||||
send "setoption name SyzygyPath value ../tests/syzygy/\n"
|
||||
expect "info string Found 35 tablebases" {} timeout {exit 1}
|
||||
send "bench 128 1 8 default depth\n"
|
||||
send "quit\n"
|
||||
expect eof
|
||||
|
||||
# return error code of the spawned program, useful for valgrind
|
||||
lassign [wait] pid spawnid os_error_flag value
|
||||
exit \$value
|
||||
EOF
|
||||
|
||||
for exp in game.exp syzygy.exp
|
||||
do
|
||||
|
||||
echo "$prefix expect $exp $postfix"
|
||||
eval "$prefix expect $exp $postfix"
|
||||
|
||||
rm $exp
|
||||
|
||||
done
|
||||
|
||||
rm -f tsan.supp
|
||||
|
||||
echo "instrumented testing OK"
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/bin/bash
|
||||
# verify perft numbers (positions from www.chessprogramming.org/Perft_Results)
|
||||
|
||||
error()
|
||||
{
|
||||
echo "perft testing failed on line $1"
|
||||
exit 1
|
||||
}
|
||||
trap 'error ${LINENO}' ERR
|
||||
|
||||
echo "perft testing started"
|
||||
|
||||
cat << EOF > perft.exp
|
||||
set timeout 10
|
||||
lassign \$argv pos depth result
|
||||
spawn ./stockfish
|
||||
send "position \$pos\\ngo perft \$depth\\n"
|
||||
expect "Nodes searched? \$result" {} timeout {exit 1}
|
||||
send "quit\\n"
|
||||
expect eof
|
||||
EOF
|
||||
|
||||
expect perft.exp startpos 5 4865609 > /dev/null
|
||||
expect perft.exp "fen r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -" 5 193690690 > /dev/null
|
||||
expect perft.exp "fen 8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -" 6 11030083 > /dev/null
|
||||
expect perft.exp "fen r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1" 5 15833292 > /dev/null
|
||||
expect perft.exp "fen rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8" 5 89941194 > /dev/null
|
||||
expect perft.exp "fen r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10" 5 164075551 > /dev/null
|
||||
|
||||
rm perft.exp
|
||||
|
||||
echo "perft testing OK"
|
||||
@@ -1,61 +0,0 @@
|
||||
#!/bin/bash
|
||||
# verify reproducible search
|
||||
|
||||
error()
|
||||
{
|
||||
echo "reprosearch testing failed on line $1"
|
||||
exit 1
|
||||
}
|
||||
trap 'error ${LINENO}' ERR
|
||||
|
||||
echo "reprosearch testing started"
|
||||
|
||||
# repeat two short games, separated by ucinewgame.
|
||||
# with go nodes $nodes they should result in exactly
|
||||
# the same node count for each iteration.
|
||||
cat << EOF > repeat.exp
|
||||
set timeout 10
|
||||
spawn ./stockfish
|
||||
lassign \$argv nodes
|
||||
|
||||
send "uci\n"
|
||||
expect "uciok"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position startpos\n"
|
||||
send "go nodes \$nodes\n"
|
||||
expect "bestmove"
|
||||
|
||||
send "position startpos moves e2e4 e7e6\n"
|
||||
send "go nodes \$nodes\n"
|
||||
expect "bestmove"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position startpos\n"
|
||||
send "go nodes \$nodes\n"
|
||||
expect "bestmove"
|
||||
|
||||
send "position startpos moves e2e4 e7e6\n"
|
||||
send "go nodes \$nodes\n"
|
||||
expect "bestmove"
|
||||
|
||||
send "quit\n"
|
||||
expect eof
|
||||
EOF
|
||||
|
||||
# to increase the likelihood of finding a non-reproducible case,
|
||||
# the allowed number of nodes are varied systematically
|
||||
for i in `seq 1 20`
|
||||
do
|
||||
|
||||
nodes=$((100*3**i/2**i))
|
||||
echo "reprosearch testing with $nodes nodes"
|
||||
|
||||
# each line should appear exactly an even number of times
|
||||
expect repeat.exp $nodes 2>&1 | grep -o "nodes [0-9]*" | sort | uniq -c | awk '{if ($1%2!=0) exit(1)}'
|
||||
|
||||
done
|
||||
|
||||
rm repeat.exp
|
||||
|
||||
echo "reprosearch testing OK"
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/bin/bash
|
||||
# obtain and optionally verify Bench / signature
|
||||
# if no reference is given, the output is deliberately limited to just the signature
|
||||
|
||||
error()
|
||||
{
|
||||
echo "running bench for signature failed on line $1"
|
||||
exit 1
|
||||
}
|
||||
trap 'error ${LINENO}' ERR
|
||||
|
||||
# obtain
|
||||
|
||||
signature=`./stockfish bench 2>&1 | grep "Nodes searched : " | awk '{print $4}'`
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
# compare to given reference
|
||||
if [ "$1" != "$signature" ]; then
|
||||
if [ -z "$signature" ]; then
|
||||
echo "No signature obtained from bench. Code crashed or assert triggered ?"
|
||||
else
|
||||
echo "signature mismatch: reference $1 obtained: $signature ."
|
||||
fi
|
||||
exit 1
|
||||
else
|
||||
echo "signature OK: $signature"
|
||||
fi
|
||||
else
|
||||
# just report signature
|
||||
echo $signature
|
||||
fi
|
||||
Reference in New Issue
Block a user