Compare commits

..

23 Commits

Author SHA1 Message Date
Marco Costalba 48cfdfcc46 Fix threads count setting
Was broken after "Optimal tune for 8 cores" patch.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 20:57:33 +01:00
Marco Costalba fa7b244dc9 Optimal tune for 8 cores
After deep tests Louis Zulli found on his OCTAL machine that
best setup for an 8 core CPU is as following

"Threads" = 8
"Minimum Split Depth" = 6 or 7 (mSD)
"Maximum Number of Threads per Split Point" = not important (MNTpSP)

Here are testing results:

mSD7 (8 threads) vs mSD4 (8 threads): 291 - 120 - 589
mSD6 vs mSD7: 168 - 188 - 644
mSD6-MNTpSP5 vs mSD6-MNTpSP6: 172 - 172 - 656
SF-7threads vs SF-8threads: 179 - 204 - 617

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 20:57:17 +01:00
Marco Costalba 29ad6a73fc Fix duplicated scaling function
We erroneusly added two times the same scaling function
to endgame's map.

Fix detected by valgrind becasue resulted in a memleak
of the first added scaling function.

Bug introduced by 30e8f0c9ad6a473 of 13/02/2009

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 19:40:43 +01:00
Marco Costalba ac48b16708 Update release number
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 14:59:55 +01:00
Marco Costalba 38b1c4b6b8 Another TT size limit fix attempt
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 14:17:00 +01:00
Marco Costalba 162dbeaee8 Remove a bogus assert
It is not true with old 1.6.xx code

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 14:09:23 +01:00
Marco Costalba 85146ca0a9 Check bounds in set_option_value()
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.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 14:06:59 +01:00
Joona Kiiski 02e12a69a7 Remove InfiniteSearch hack
With current search control system, I can see absolutely no
reason to classify fixed time search as infinite search.

So remove old dated hack

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 13:15:28 +01:00
Tord Romstad 6e8116e38f Make sure we make a move at the end of the search when reaching
maximum depth during a "go movetime ..." search. This prevents
Stockfish from hanging forever after finding a mate in two or
three while running a test suite at a level of a few seconds
per move.

No functional change when playing games at normal time controls.
2010-02-01 13:13:58 +01:00
Marco Costalba 29f7fab2a9 Do not wait when AbortSearch is set
It means we have already received "stop" or "quit" commands.

This fixes an hang in tactical test in Fritz GUI. Bug
introduced by previous bug fix :-(

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 13:13:20 +01:00
Marco Costalba 2af986bf31 Fix sending of best move during an infinite search
According to UCI standard once engine receives 'go infinite'
command it should search until the "stop" command and do not exit
the search without being told so, even if PLY_MAX has been reached.

Patch is quite invasive because it cleanups some hacks used
by fixed depth and fixed nodes modes, mainly during benchmarks.

Bug found by Pascal Georges.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 13:10:21 +01:00
Marco Costalba b67146b100 Add hardware POPCNT support for gcc
With new target 'make gcc-popcnt' it is now
possible to compile with enabled hardware POPCNT
support also with gcc. Until now was possible only
for Intel and MSVC compilers.

When this instruction is supported by CPU, for instance
on Intel i7 or i5 family, produced binary is a bit faster.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:48:49 +01:00
Joona Kiiski c1b1a94d81 Standardize set_option function
Previously input like "setoption name Use Search Log value true "
(note space at the end of the line) didn't work.

Now parse value same way as option name. This way we implicitly
left- and right-trim value.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:48:11 +01:00
Joona Kiiski 17212e5fcc Remove last use of uip.eof()
Value of uip.eof() should not be trusted.
input like "go infinite searchmoves " (note space in the end of line)
causes problems.

Check the return value of (uip >> token) instead

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:46:03 +01:00
Marco Costalba 46921dff27 Fix a couple of MSVC casting warnings
Also removed some trailing whitespaces and aligned
indentation to current standard.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:45:04 +01:00
Marco Costalba 941016e7a2 Check for thread creation successful completion
It is a good programming practice to verify a system
call has indeed succeed.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:44:11 +01:00
Tord Romstad 290caf9960 Fixes a Chess960 bug when playing with more than one search thread.
The init_eval() function corrupted the static array castleRightsMask[]
in the Position class, resulting in instant crashes in most Chess960
games. Fixed by repairing the damage directly after the function is
called. Also modified the Position::to_fen() function to display
castle rights correctly for Chess960 positions, and added sanity checks
for uncastled rook files in Position::is_ok().
2010-02-01 12:40:09 +01:00
Marco Costalba 43fa3a4d64 Fix some races in SMP code
When a search fails high then sp->alpha is increased and
slave threads are requested to stop.

So we have to check for a stop request before to start a search
otherwise we could end up with sp->alpha >= sp->beta
leading to an assert in debug run in search_pv().

This patch fixes the assert and get rid of some of possible races.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:39:53 +01:00
Marco Costalba 64b4836d12 Fix enum Value issue with gcc 4.4
Louis Zulli reports a miscompile with g++-4.4 from MacPorts.

Namely enum Value is compiled as unsigned instead of signed integer
and this yields an issue in score_string() where float(v) is incorrectly
casted when Value v is negative.

This patch ensure that compiler choses a signed variable to store a Value.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:39:21 +01:00
Marco Costalba 5df7d62eb9 Fix 'position ..... moves ' parsing bug
If after 'moves' there is a space then we crash.

The problem is that operator>>() trims whitespaces so that
after 'moves' has been extract we are still not at eof()
but remaining string contains only spaces. So that the next
extarction operation uip >> token ends up with unchanged token
value that remains 'moves', this garbage value is then feeded
to RootPosition.do_move() through move_from_string() that does
not detect the invalid move value leading to a crash.

This bug is triggered by Shredder 12 interface under Mac that
puts a space after 'moves' without any actual move list.

Bug fixed by Justin Blanchard

After reviewing UCI parsing code I spotted other possible weak
points due to the fact that we don't test if the last extract
operation has been succesful. So I have extended Justing patch
to fix the remaining possible holes in uci.cpp

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:36:30 +01:00
Marco Costalba 82179c70dc Fix en-passant parsing from fen string
According to standard en-passant is recorded in fen string regardless
of whether there is a pawn in position to make an en passant capture.

Instead internally we set ep square only if the pawn can be captured.
So teach from_fen() to correctly handle this difference.

Bug reported and fixed by Justin Blanchard.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:36:06 +01:00
Marco Costalba de17652e47 Fix a possible crash in thread_is_available()
When we have more then 2 threads then we do an array
access with index 'Threads[slave].activeSplitPoints - 1'
This should be >= 0 because we tested the variable just
few statements before, but because is a shared variable
it could be that the 'slave' thread set the value to zero
just after we test it, so that when we use the decremented
variable for array access we crash.

Bug spotted by Bruno Causse.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:34:31 +01:00
Marco Costalba 647b79b556 Extend maximum hash size to 8 GB
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2010-02-01 12:14:37 +01:00
74 changed files with 15704 additions and 10829 deletions
-49
View File
@@ -1,49 +0,0 @@
language: cpp
matrix:
include:
- os: linux
compiler: gcc
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-4.8']
env:
- COMPILER=g++-4.8
- COMP=gcc
- os: linux
compiler: clang
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7']
packages: ['clang-3.7']
env:
- COMPILER=clang++-3.7
- COMP=clang
- os: osx
compiler: gcc
env:
- COMPILER=g++-4.8
- COMP=gcc
- os: osx
compiler: clang
env:
- COMPILER=clang++ V='Apple LLVM 6.0' # Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn)
- COMP=clang
branches:
only:
- master
before_script:
- cd src
script:
- make clean && make build ARCH=x86-64 && ./stockfish bench 2>&1 >/dev/null | grep 'Nodes searched' | tee bench1
- make clean && make build ARCH=x86-32 && ./stockfish bench 2>&1 >/dev/null | grep 'Nodes searched' | tee bench2
- echo "Checking for same bench numbers..."
- diff bench1 bench2 > result
- test ! -s result
-98
View File
@@ -1,98 +0,0 @@
# Generated with git shortlog -sn | cut -c8-', which sorts by commits (manually ordered the first four authors)
Tord Romstad
Marco Costalba
Joona Kiiski
Gary Linscott
lucasart
mstembera
Lucas Braesch
Stefan Geschwentner
Reuven Peleg
Chris Caino
joergoster
VoyagerOne
Jean-Francois Romang
homoSapiensSapiens
Alain SAVARD
Arjun Temurnikar
Stéphane Nicolet
Uri Blass
jundery
Ralph Stößer
Ajith
Leonid Pechenik
Stefano80
Tom Vijlbrief
hxim
snicolet
Daylen Yang
Henri Wiechers
Jonathan Calovski
mbootsector
David Zar
Eelco de Groot
Jerry Donald
Joerg Oster
Jörg Oster
Ryan Schmitt
mcostalba
Alexander Kure
Dan Schmidt
H. Felix Wittmann
Joseph R. Prostko
Justin Blanchard
Linus Arver
NicklasPersson
Rodrigo Exterckötter Tjäder
Ron Britvich
Ronald de Man
RyanTaker
Vince Negri
ceebo
jhellis3
ppigazzini
shane31
Andy Duplain
Auguste Pop
Balint Pfliegel
Chris Cain
DU-jdto
Dariusz Orzechowski
DiscanX
Ernesto Gatti
Gregor Cramer
Guenther Demetz
Hiraoka Takuya
Hongzhi Cheng
Joseph Hellis
Kelly Wilson
Ken T Takusagawa
Kojirion
Luca Brivio
Matt Sullivan
Matthew Lai
Matthew Sullivan
Michel Van den Bergh
Mysseno
Oskar Werkelin Ahlin
Pablo Vazquez
Pascal Romaret
Ralph Stoesser
Ralph Stößer
Raminder Singh
Richard Lloyd
Ryan Takker
Stephane Nicolet
Thanar2
absimaldata
braich
gguliash
kinderchocolate
loco-loco
pellanda
renouve
sf-x
thaspel
unknown
uriblass
-114
View File
@@ -1,114 +0,0 @@
### Overview
[![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish)
Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
not a complete chess program and requires some UCI-compatible GUI
(e.g. XBoard with PolyGlot, 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.
This version of Stockfish supports up to 128 cores. The engine defaults
to one search thread, so 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.
This version of Stockfish has support for Syzygybases.
### Files
This distribution of Stockfish consists of the following files:
* Readme.md, 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.
### Syzygybases
**Configuration**
Syzygybases are configured using the UCI options "SyzygyPath",
"SyzygyProbeDepth", "Syzygy50MoveRule" and "SyzygyProbeLimit".
The option "SyzygyPath" should be set to the directory or directories that
contain the .rtbw and .rtbz files. Multiple directories should 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 HD.
Increasing the "SyzygyProbeDepth" option lets the engine probe less
aggressively. Set this option to a higher value if you experience too much
slowdown (in terms of nps) due to TB probing.
Set the "Syzygy50MoveRule" option to false if you want tablebase positions
that are drawn by the 50-move rule to count as win or loss. This may be useful
for correspondence games (because of tablebase adjudication).
The "SyzygyProbeLimit" option should normally be left at its default value.
**What to expect**
If the engine is searching a position that is not in the tablebases (e.g.
a position with 7 pieces), it will access the tablebases during the search.
If the engine reports a very large score (typically 123.xx), this means
that 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 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 Syzygybases 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 Syzygybases 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.
### Compiling it yourself
On Unix-like systems, it should be possible to compile Stockfish
directly from the source code with the included Makefile.
Stockfish has support for 32 or 64-bit CPUs, the hardware POPCNT
instruction, big-endian machines such as Power PC, and other platforms.
In general it is recommended to run `make help` to see a list of make
targets with corresponding descriptions. 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.
### 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*
+90
View File
@@ -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
+63
View File
@@ -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
View File
@@ -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>.
+263 -472
View File
@@ -1,7 +1,8 @@
# Stockfish, a UCI chess playing engine derived from Glaurung 2.1 # Stockfish, a UCI chess playing engine derived from Glaurung 2.1
# Copyright (C) 2004-2008 Tord Romstad (Glaurung author) # Copyright (C) 2004-2007 Tord Romstad
# Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad # Copyright (C) 2008 Marco Costalba
# Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
# This file is part of Stockfish.
# #
# Stockfish is free software: you can redistribute it and/or modify # Stockfish is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@@ -17,507 +18,297 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
### ========================================================================== ### Executable name. Do not change
### Section 1. General Configuration
### ==========================================================================
### Establish the operating system name
UNAME = $(shell uname)
### Executable name
EXE = stockfish EXE = stockfish
### Installation dir definitions
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
### Built-in benchmark for pgo-builds
PGOBENCH = ./$(EXE) bench 16 1 1000 default time
### Object files
OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \
material.o misc.o movegen.o movepick.o pawns.o position.o psqt.o \
search.o thread.o timeman.o tt.o uci.o ucioption.o syzygy/tbprobe.o
### ========================================================================== ### ==========================================================================
### Section 2. High-level Configuration ### Compiler speed switches for both GCC and ICC. These settings are generally
### fast on a broad range of systems, but may be changed experimentally
### ========================================================================== ### ==========================================================================
# GCCFLAGS = -O3 -msse
# flag --- Comp switch --- Description ICCFLAGS = -fast -msse
# ---------------------------------------------------------------------------- ICCFLAGS-OSX = -fast -mdynamic-no-pic
#
# debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode
# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations
# arch = (name) --- (-arch) --- Target architecture
# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch x86 asm-instruction
# bsfq = yes/no --- -DUSE_BSFQ --- Use bsfq x86_64 asm-instruction (only
# with GCC and ICC 64-bit)
# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt x86_64 asm-instruction
# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions
# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction
#
# Note that Makefile is space sensitive, so when adding new architectures
# or modifying existing flags, you have to make sure there are no extra spaces
# at the end of the line for flag values.
### 2.1. General and architecture defaults
optimize = yes
debug = no
bits = 32
prefetch = no
bsfq = no
popcnt = no
sse = no
pext = no
### 2.2 Architecture specific
ifeq ($(ARCH),general-32)
arch = any
endif
ifeq ($(ARCH),x86-32-old)
arch = i386
endif
ifeq ($(ARCH),x86-32)
arch = i386
prefetch = yes
sse = yes
endif
ifeq ($(ARCH),general-64)
arch = any
bits = 64
endif
ifeq ($(ARCH),x86-64)
arch = x86_64
bits = 64
prefetch = yes
bsfq = yes
sse = yes
endif
ifeq ($(ARCH),x86-64-modern)
arch = x86_64
bits = 64
prefetch = yes
bsfq = yes
popcnt = yes
sse = yes
endif
ifeq ($(ARCH),x86-64-bmi2)
arch = x86_64
bits = 64
prefetch = yes
bsfq = yes
popcnt = yes
sse = yes
pext = yes
endif
ifeq ($(ARCH),armv7)
arch = armv7
prefetch = yes
bsfq = yes
endif
ifeq ($(ARCH),ppc-32)
arch = ppc
endif
ifeq ($(ARCH),ppc-64)
arch = ppc64
bits = 64
endif
### ========================================================================== ### ==========================================================================
### Section 3. Low-level configuration ### Enable/disable debugging, disabled by default
### ========================================================================== ### ==========================================================================
GCCFLAGS += -DNDEBUG
### 3.1 Selecting compiler (default = gcc) ICCFLAGS += -DNDEBUG
ICCFLAGS-OSX += -DNDEBUG
CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -fno-rtti -std=c++11 $(EXTRACXXFLAGS)
DEPENDFLAGS += -std=c++11
LDFLAGS += $(EXTRALDFLAGS)
ifeq ($(COMP),)
COMP=gcc
endif
ifeq ($(COMP),gcc)
comp=gcc
CXX=g++
CXXFLAGS += -pedantic -Wextra -Wshadow
ifneq ($(UNAME),Darwin)
LDFLAGS += -Wl,--no-as-needed
endif
endif
ifeq ($(COMP),mingw)
comp=mingw
ifeq ($(UNAME),Linux)
ifeq ($(bits),64)
ifeq ($(shell which x86_64-w64-mingw32-c++-posix),)
CXX=x86_64-w64-mingw32-c++
else
CXX=x86_64-w64-mingw32-c++-posix
endif
else
ifeq ($(shell which i686-w64-mingw32-c++-posix),)
CXX=i686-w64-mingw32-c++
else
CXX=i686-w64-mingw32-c++-posix
endif
endif
else
CXX=g++
endif
CXXFLAGS += -Wextra -Wshadow
LDFLAGS += -static
endif
ifeq ($(COMP),icc)
comp=icc
CXX=icpc
CXXFLAGS += -diag-disable 1476,10120 -Wcheck -Wabi -Wdeprecated -strict-ansi
endif
ifeq ($(COMP),clang)
comp=clang
CXX=clang++
CXXFLAGS += -pedantic -Wextra -Wshadow
ifeq ($(UNAME),Darwin)
CXXFLAGS += -std=c++0x -stdlib=libc++
DEPENDFLAGS += -std=c++0x -stdlib=libc++
endif
endif
ifeq ($(comp),icc)
profile_prepare = icc-profile-prepare
profile_make = icc-profile-make
profile_use = icc-profile-use
profile_clean = icc-profile-clean
else
profile_prepare = gcc-profile-prepare
profile_make = gcc-profile-make
profile_use = gcc-profile-use
profile_clean = gcc-profile-clean
endif
ifeq ($(UNAME),Darwin)
CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.9
LDFLAGS += -arch $(arch) -mmacosx-version-min=10.9
endif
### Travis CI script uses COMPILER to overwrite CXX
ifdef COMPILER
CXX=$(COMPILER)
endif
### On mingw use Windows threads, otherwise POSIX
ifneq ($(comp),mingw)
# On Android Bionic's C library comes with its own pthread implementation bundled in
ifneq ($(arch),armv7)
# Haiku has pthreads in its libroot, so only link it in on other platforms
ifneq ($(UNAME),Haiku)
LDFLAGS += -lpthread
endif
endif
endif
### 3.4 Debugging
ifeq ($(debug),no)
CXXFLAGS += -DNDEBUG
else
CXXFLAGS += -g
endif
### 3.5 Optimization
ifeq ($(optimize),yes)
ifeq ($(comp),gcc)
CXXFLAGS += -O3
ifeq ($(UNAME),Darwin)
ifeq ($(arch),i386)
CXXFLAGS += -mdynamic-no-pic
endif
ifeq ($(arch),x86_64)
CXXFLAGS += -mdynamic-no-pic
endif
endif
ifeq ($(arch),armv7)
CXXFLAGS += -fno-gcse -mthumb -march=armv7-a -mfloat-abi=softfp
endif
endif
ifeq ($(comp),mingw)
CXXFLAGS += -O3
endif
ifeq ($(comp),icc)
ifeq ($(UNAME),Darwin)
CXXFLAGS += -fast -mdynamic-no-pic
else
CXXFLAGS += -fast
endif
endif
ifeq ($(comp),clang)
CXXFLAGS += -O3
ifeq ($(UNAME),Darwin)
ifeq ($(pext),no)
CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS)
endif
ifeq ($(arch),i386)
CXXFLAGS += -mdynamic-no-pic
endif
ifeq ($(arch),x86_64)
CXXFLAGS += -mdynamic-no-pic
endif
endif
endif
endif
### 3.6. Bits
ifeq ($(bits),64)
CXXFLAGS += -DIS_64BIT
endif
### 3.7 prefetch
ifeq ($(prefetch),yes)
ifeq ($(sse),yes)
CXXFLAGS += -msse
DEPENDFLAGS += -msse
endif
else
CXXFLAGS += -DNO_PREFETCH
endif
### 3.8 bsfq
ifeq ($(bsfq),yes)
CXXFLAGS += -DUSE_BSFQ
endif
### 3.9 popcnt
ifeq ($(popcnt),yes)
ifeq ($(comp),icc)
CXXFLAGS += -msse3 -DUSE_POPCNT
else
CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT
endif
endif
### 3.10 pext
ifeq ($(pext),yes)
CXXFLAGS += -DUSE_PEXT
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -mbmi -mbmi2
endif
endif
### 3.11 Link Time Optimization, it works since gcc 4.5 but not on mingw under Windows.
### This is a mix of compile and link time options because the lto link phase
### needs access to the optimization flags.
ifeq ($(comp),gcc)
ifeq ($(optimize),yes)
ifeq ($(debug),no)
CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS)
endif
endif
endif
ifeq ($(comp),mingw)
ifeq ($(UNAME),Linux)
ifeq ($(optimize),yes)
ifeq ($(debug),no)
CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS)
endif
endif
endif
endif
### 3.12 Android 5 can only run position independent executables. Note that this
### breaks Android 4.0 and earlier.
ifeq ($(arch),armv7)
CXXFLAGS += -fPIE
LDFLAGS += -fPIE -pie
endif
### ========================================================================== ### ==========================================================================
### Section 4. Public targets ### Remove below comments to compile for a big-endian machine
### ========================================================================== ### ==========================================================================
#GCCFLAGS += -DBIGENDIAN
#ICCFLAGS += -DBIGENDIAN
#ICCFLAGS-OSX += -DBIGENDIAN
### ==========================================================================
### Run built-in benchmark for pgo-builds with: 32MB hash 1 thread 10 depth
### These settings are generally fast, but may be changed experimentally
### ==========================================================================
PGOBENCH = ./$(EXE) bench 32 1 10 default depth
### General compiler settings. Do not change
GCCFLAGS += -g -Wall -fno-exceptions -fno-rtti
ICCFLAGS += -g -Wall -fno-exceptions -fno-rtti -wd383,869,981,10187,10188,11505,11503
ICCFLAGS-OSX += -g -Wall -fno-exceptions -fno-rtti -wd383,869,981,10187,10188,11505,11503
### General linker settings. Do not change
LDFLAGS = -lpthread
### Object files. Do not change
OBJS = application.o bitboard.o pawns.o material.o endgame.o evaluate.o main.o \
misc.o move.o movegen.o history.o movepick.o search.o piece.o \
position.o direction.o tt.o value.o uci.o ucioption.o \
mersenne.o book.o bitbase.o san.o benchmark.o
### General rules. Do not change
default:
$(MAKE) gcc
help: help:
@echo "" @echo ""
@echo "To compile stockfish, type: " @echo "Makefile options:"
@echo "" @echo ""
@echo "make target ARCH=arch [COMP=comp]" @echo "make > Default: Compiler = g++"
@echo "" @echo "make gcc-popcnt > Compiler = g++ + popcnt-support"
@echo "Supported targets:" @echo "make icc > Compiler = icpc"
@echo "" @echo "make icc-profile > Compiler = icpc + automatic pgo-build"
@echo "build > Standard build" @echo "make icc-profile-popcnt > Compiler = icpc + automatic pgo-build + popcnt-support"
@echo "profile-build > PGO build" @echo "make osx-ppc32 > PPC-Mac OS X 32 bit. Compiler = g++"
@echo "strip > Strip executable" @echo "make osx-ppc64 > PPC-Mac OS X 64 bit. Compiler = g++"
@echo "install > Install executable" @echo "make osx-x86 > x86-Mac OS X 32 bit. Compiler = g++"
@echo "clean > Clean up" @echo "make osx-x86_64 > x86-Mac OS X 64 bit. Compiler = g++"
@echo "" @echo "make osx-icc32 > x86-Mac OS X 32 bit. Compiler = icpc"
@echo "Supported archs:" @echo "make osx-icc64 > x86-Mac OS X 64 bit. Compiler = icpc"
@echo "" @echo "make osx-icc32-profile > OSX 32 bit. Compiler = icpc + automatic pgo-build"
@echo "x86-64 > x86 64-bit" @echo "make osx-icc64-profile > OSX 64 bit. Compiler = icpc + automatic pgo-build"
@echo "x86-64-modern > x86 64-bit with popcnt support" @echo "make strip > Strip executable"
@echo "x86-64-bmi2 > x86 64-bit with pext support" @echo "make clean > Clean up"
@echo "x86-32 > x86 32-bit with SSE support"
@echo "x86-32-old > x86 32-bit fall back for old hardware"
@echo "ppc-64 > PPC 64-bit"
@echo "ppc-32 > PPC 32-bit"
@echo "armv7 > ARMv7 32-bit"
@echo "general-64 > unspecified 64-bit"
@echo "general-32 > unspecified 32-bit"
@echo ""
@echo "Supported compilers:"
@echo ""
@echo "gcc > Gnu compiler (default)"
@echo "mingw > Gnu compiler with MinGW under Windows"
@echo "clang > LLVM Clang compiler"
@echo "icc > Intel compiler"
@echo ""
@echo "Examples. If you don't know what to do, you likely want to run: "
@echo ""
@echo "make build ARCH=x86-64 (This is for 64-bit systems)"
@echo "make build ARCH=x86-32 (This is for 32-bit systems)"
@echo "" @echo ""
.PHONY: build profile-build all: $(EXE) .depend
build:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
profile-build: clean:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity $(RM) *.o .depend *~ $(EXE)
### Possible targets. You may add your own ones here
gcc:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS)" \
all
gcc-popcnt:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS) -DUSE_POPCNT" \
all
icc:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS)" \
all
icc-profile-make:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS)" \
CXXFLAGS+='-prof-gen=srcpos -prof_dir ./profdir' \
all
icc-profile-use:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS)" \
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
all
icc-profile:
@rm -rf profdir
@mkdir profdir
@touch *.cpp *.h
$(MAKE) icc-profile-make
@echo "" @echo ""
@echo "Step 0/4. Preparing for profile build." @echo "Running benchmark for pgo-build ..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_prepare) @$(PGOBENCH) > /dev/null
@echo "Benchmark finished. Build final executable now ..."
@echo "" @echo ""
@echo "Step 1/4. Building executable for benchmark ..." @touch *.cpp *.h
@touch *.cpp *.h syzygy/*.cpp syzygy/*.h $(MAKE) icc-profile-use
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make) @rm -rf profdir bench.txt
icc-profile-make-with-popcnt:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS) -DUSE_POPCNT" \
CXXFLAGS+='-prof-gen=srcpos -prof_dir ./profdir' \
all
icc-profile-use-with-popcnt:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS) -DUSE_POPCNT" \
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
all
icc-profile-popcnt:
@rm -rf profdir
@mkdir profdir
@touch *.cpp *.h
$(MAKE) icc-profile-make
@echo "" @echo ""
@echo "Step 2/4. Running benchmark for pgo-build ..." @echo "Running benchmark for pgo-build (popcnt disabled)..."
$(PGOBENCH) > /dev/null @$(PGOBENCH) > /dev/null
@touch *.cpp *.h
$(MAKE) icc-profile-make-with-popcnt
@echo "" @echo ""
@echo "Step 3/4. Building final executable ..." @echo "Running benchmark for pgo-build (popcnt enabled)..."
@touch *.cpp *.h syzygy/*.cpp syzygy/*.h @$(PGOBENCH) > /dev/null
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use) @echo "Benchmarks finished. Build final executable now ..."
@echo "" @echo ""
@echo "Step 4/4. Deleting profile data ..." @touch *.cpp *.h
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean) $(MAKE) icc-profile-use-with-popcnt
@rm -rf profdir bench.txt
osx-ppc32:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS)" \
CXXFLAGS+='-arch ppc' \
LDFLAGS+='-arch ppc' \
all
osx-ppc64:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS)" \
CXXFLAGS+='-arch ppc64' \
LDFLAGS+='-arch ppc64' \
all
osx-x86:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS)" \
CXXFLAGS+='-arch i386' \
LDFLAGS+='-arch i386' \
all
osx-x86_64:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS)" \
CXXFLAGS+='-arch x86_64' \
LDFLAGS+='-arch x86_64' \
all
osx-icc32:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch i386' \
LDFLAGS+='-arch i386' \
all
osx-icc64:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch x86_64' \
LDFLAGS+='-arch x86_64' \
all
osx-icc32-profile-make:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch i386' \
CXXFLAGS+='-prof_gen -prof_dir ./profdir' \
LDFLAGS+='-arch i386' \
all
osx-icc32-profile-use:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch i386' \
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
LDFLAGS+='-arch i386' \
all
osx-icc32-profile:
@rm -rf profdir
@mkdir profdir
@touch *.cpp *.h
$(MAKE) osx-icc32-profile-make
@echo ""
@echo "Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null
@echo "Benchmark finished. Build final executable now ..."
@echo ""
@touch *.cpp *.h
$(MAKE) osx-icc32-profile-use
@rm -rf profdir bench.txt
osx-icc64-profile-make:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch x86_64' \
CXXFLAGS+='-prof_gen -prof_dir ./profdir' \
LDFLAGS+='-arch x86_64' \
all
osx-icc64-profile-use:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS-OSX)" \
CXXFLAGS+='-arch x86_64' \
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
LDFLAGS+='-arch x86_64' \
all
osx-icc64-profile:
@rm -rf profdir
@mkdir profdir
@touch *.cpp *.h
$(MAKE) osx-icc64-profile-make
@echo ""
@echo "Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null
@echo "Benchmark finished. Build final executable now ..."
@echo ""
@touch *.cpp *.h
$(MAKE) osx-icc64-profile-use
@rm -rf profdir bench.txt
strip: strip:
strip $(EXE) strip $(EXE)
install:
-mkdir -p -m 755 $(BINDIR)
-cp $(EXE) $(BINDIR)
-strip $(BINDIR)/$(EXE)
clean:
$(RM) $(EXE) $(EXE).exe *.o .depend *~ core bench.txt *.gcda ./syzygy/*.o ./syzygy/*.gcda
default:
help
### ==========================================================================
### Section 5. Private targets
### ==========================================================================
all: $(EXE) .depend
config-sanity:
@echo ""
@echo "Config:"
@echo "debug: '$(debug)'"
@echo "optimize: '$(optimize)'"
@echo "arch: '$(arch)'"
@echo "bits: '$(bits)'"
@echo "prefetch: '$(prefetch)'"
@echo "bsfq: '$(bsfq)'"
@echo "popcnt: '$(popcnt)'"
@echo "sse: '$(sse)'"
@echo "pext: '$(pext)'"
@echo ""
@echo "Flags:"
@echo "CXX: $(CXX)"
@echo "CXXFLAGS: $(CXXFLAGS)"
@echo "LDFLAGS: $(LDFLAGS)"
@echo ""
@echo "Testing config sanity. If this fails, try 'make help' ..."
@echo ""
@test "$(debug)" = "yes" || test "$(debug)" = "no"
@test "$(optimize)" = "yes" || test "$(optimize)" = "no"
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "armv7"
@test "$(bits)" = "32" || test "$(bits)" = "64"
@test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
@test "$(bsfq)" = "yes" || test "$(bsfq)" = "no"
@test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
@test "$(sse)" = "yes" || test "$(sse)" = "no"
@test "$(pext)" = "yes" || test "$(pext)" = "no"
@test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang"
### Compilation. Do not change
$(EXE): $(OBJS) $(EXE): $(OBJS)
$(CXX) -o $@ $(OBJS) $(LDFLAGS) $(CXX) $(LDFLAGS) -o $@ $(OBJS)
gcc-profile-prepare:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) gcc-profile-clean
gcc-profile-make:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-generate' \
EXTRALDFLAGS='-lgcov' \
all
gcc-profile-use:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-use -fno-peel-loops -fno-tracer' \
EXTRALDFLAGS='-lgcov' \
all
gcc-profile-clean:
@rm -rf *.gcda *.gcno syzygy/*.gcda syzygy/*.gcno bench.txt
icc-profile-prepare:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) icc-profile-clean
@mkdir profdir
icc-profile-make:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-prof-gen=srcpos -prof_dir ./profdir' \
all
icc-profile-use:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \
all
icc-profile-clean:
@rm -rf profdir bench.txt
### Dependencies. Do not change
.depend: .depend:
-@$(CXX) $(DEPENDFLAGS) -MM $(OBJS:.o=.cpp) > $@ 2> /dev/null $(CXX) -msse -MM $(OBJS:.o=.cpp) > $@
-include .depend
include .depend
+75
View File
@@ -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
}
+39
View File
@@ -0,0 +1,39 @@
/*
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(APPLICATION_H_INCLUDED)
#define APPLICATION_H_INCLUDED
/// Singleton class used to housekeep memory and global resources
/// so to be sure we always leave in a clean state.
class Application {
Application();
Application(const Application&);
~Application();
public:
static void initialize();
static void exit_with_failure();
};
#endif // !defined(APPLICATION_H_INCLUDED)
+114 -108
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,25 +17,29 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
////
//// Includes
////
#include <fstream> #include <fstream>
#include <iostream> #include <sstream>
#include <istream>
#include <vector> #include <vector>
#include "misc.h" #include "benchmark.h"
#include "position.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
#include "uci.h" #include "ucioption.h"
using namespace std; using namespace std;
namespace { ////
//// Variables
////
const vector<string> Defaults = { const string BenchmarkPositions[] = {
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10", "r4rk1/1b2qppp/p1n1p3/1p6/1b1PN3/3BRN2/PP3PPP/R2Q2K1 b - - 7 16",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11", "4r1k1/ppq3pp/3b4/2pP4/2Q1p3/4B1P1/PP5P/R5K1 b - - 0 20",
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14", "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14",
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14", "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14",
@@ -48,128 +51,131 @@ const vector<string> Defaults = {
"r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16", "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16",
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - 3 22",
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26", "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",
"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",
// 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
}; };
} // namespace
////
//// Functions
////
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set /// benchmark() runs a simple benchmark by letting Stockfish analyze a set
/// of positions for a given limit each. There are five parameters: the /// of positions for a given time each. There are four parameters; the
/// transposition table size, the number of search threads that should /// transposition table size, the number of search threads that should
/// be used, the limit value spent for each position (optional, default is /// be used, the time in seconds spent for each position (optional, default
/// depth 13), an optional file name where to look for positions in FEN /// is 60) and an optional file name where to look for positions in fen
/// format (defaults are the positions defined above) and the type of the /// format (default are the BenchmarkPositions defined above).
/// limit value: depth (default), time in millisecs or number of nodes. /// The analysis is written to a file named bench.txt.
void benchmark(const Position& current, istream& is) { void benchmark(const string& commandLine) {
string token; istringstream csVal(commandLine);
vector<string> fens; istringstream csStr(commandLine);
Search::LimitsType limits; string ttSize, threads, fileName, limitType, timFile;
int val, secsPerPos, maxDepth, maxNodes;
// Assign default values to missing arguments csStr >> ttSize;
string ttSize = (is >> token) ? token : "16"; csVal >> val;
string threads = (is >> token) ? token : "1"; if (val < 4 || val > 1024)
string limit = (is >> token) ? token : "13"; {
string fenFile = (is >> token) ? token : "default"; cerr << "The hash table size must be between 4 and 1024" << endl;
string limitType = (is >> token) ? token : "depth"; 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");
Options["Hash"] = ttSize; csVal >> val;
Options["Threads"] = threads; csVal >> fileName;
Search::clear(); csVal >> limitType;
csVal >> timFile;
secsPerPos = maxDepth = maxNodes = 0;
if (limitType == "time") if (limitType == "time")
limits.movetime = stoi(limit); // movetime is in millisecs secsPerPos = val * 1000;
else if (limitType == "depth" || limitType == "perft")
else if (limitType == "nodes") maxDepth = val;
limits.nodes = stoi(limit);
else if (limitType == "mate")
limits.mate = stoi(limit);
else else
limits.depth = stoi(limit); maxNodes = val;
if (fenFile == "default") vector<string> positions;
fens = Defaults;
else if (fenFile == "current") if (fileName != "default")
fens.push_back(current.fen());
else
{ {
string fen; ifstream fenFile(fileName.c_str());
ifstream file(fenFile); if (!fenFile.is_open())
if (!file.is_open())
{ {
cerr << "Unable to open file " << fenFile << endl; cerr << "Unable to open positions file " << fileName << endl;
return; 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)) ofstream timingFile;
if (!fen.empty()) if (!timFile.empty())
fens.push_back(fen); {
timingFile.open(timFile.c_str(), ios::out | ios::app);
file.close(); if (!timingFile.is_open())
{
cerr << "Unable to open timing file " << timFile << endl;
Application::exit_with_failure();
}
} }
uint64_t nodes = 0; vector<string>::iterator it;
TimePoint elapsed = now(); int cnt = 1;
int64_t totalNodes = 0;
int startTime = get_system_time();
for (size_t i = 0; i < fens.size(); ++i) for (it = positions.begin(); it != positions.end(); ++it, ++cnt)
{ {
Position pos(fens[i], Options["UCI_Chess960"], Threads.main()); Move moves[1] = {MOVE_NONE};
int dummy[2] = {0, 0};
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; Position pos(*it);
cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl;
if (limitType == "perft") if (limitType == "perft")
nodes += Search::perft(pos, limits.depth * ONE_PLY); totalNodes += perft(pos, maxDepth * OnePly);
else if (!think(pos, false, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
else break;
{ totalNodes += nodes_searched();
Search::StateStackPtr st;
limits.startTime = now();
Threads.start_thinking(pos, limits, st);
Threads.main()->wait_for_search_finished();
nodes += Threads.nodes_searched();
}
} }
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' cnt = get_system_time() - startTime;
cerr << "==============================="
<< "\nTotal time (ms) : " << cnt
<< "\nNodes searched : " << totalNodes
<< "\nNodes/second : " << (int)(totalNodes/(cnt/1000.0)) << endl << endl;
dbg_print(); // Just before to exit if (!timFile.empty())
{
timingFile << cnt << endl << endl;
timingFile.close();
}
cerr << "\n===========================" // Under MS Visual C++ debug window always unconditionally closes
<< "\nTotal time (ms) : " << elapsed // when program exits, this is bad because we want to read results before.
<< "\nNodes searched : " << nodes #if (defined(WINDOWS) || defined(WIN32) || defined(WIN64))
<< "\nNodes/second : " << 1000 * nodes / elapsed << endl; cerr << "Press any key to exit" << endl;
cin >> fileName;
#endif
} }
+37
View File
@@ -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 -123
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,163 +17,334 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert>
#include <numeric>
#include <vector>
////
//// Includes
////
#include <cassert>
#include "bitbase.h"
#include "bitboard.h" #include "bitboard.h"
#include "types.h" #include "move.h"
#include "square.h"
////
//// Local definitions
////
namespace { namespace {
// There are 24 possible pawn squares: the first 4 files and ranks from 2 to 7
const unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608
// Each uint32_t stores results of 32 positions, one per bit
uint32_t KPKBitbase[MAX_INDEX / 32];
// 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 us, Square bksq, Square wksq, Square psq) {
return wksq | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15);
}
enum Result { enum Result {
INVALID = 0, RESULT_UNKNOWN,
UNKNOWN = 1, RESULT_INVALID,
DRAW = 2, RESULT_WIN,
WIN = 4 RESULT_LOSS,
RESULT_DRAW
}; };
Result& operator|=(Result& r, Result v) { return r = Result(r | v); }
struct KPKPosition { struct KPKPosition {
KPKPosition() = default; void from_index(int index);
explicit KPKPosition(unsigned idx); int to_index() const;
operator Result() const { return result; } bool is_legal() const;
Result classify(const std::vector<KPKPosition>& db) bool is_immediate_draw() const;
{ return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); } bool is_immediate_win() const;
Bitboard wk_attacks() const;
Bitboard bk_attacks() const;
Bitboard pawn_attacks() const;
template<Color Us> Result classify(const std::vector<KPKPosition>& db); Square whiteKingSquare, blackKingSquare, pawnSquare;
Color sideToMove;
Color us;
Square ksq[COLOR_NB], psq;
Result result;
}; };
} // namespace
Result *Bitbase;
const int IndexMax = 2*24*64*64;
int UnknownCount = 0;
bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color us) { 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);
assert(file_of(wpsq) <= FILE_D);
unsigned idx = index(us, bksq, wksq, wpsq);
return KPKBitbase[idx / 32] & (1 << (idx & 0x1F));
} }
void Bitbases::init() { ////
//// Functions
////
std::vector<KPKPosition> db(MAX_INDEX); void generate_kpk_bitbase(uint8_t bitbase[]) {
unsigned idx, repeat = 1; // Allocate array and initialize:
Bitbase = new Result[IndexMax];
initialize();
// Initialize db with known win / draw positions // Iterate until all positions are classified:
for (idx = 0; idx < MAX_INDEX; ++idx) while(next_iteration());
db[idx] = KPKPosition(idx);
// Iterate through the positions until none of the unknown positions can be // Compress bitbase into the supplied parameter:
// changed to either wins or draws (15 cycles needed). int i, j, b;
while (repeat) for(i = 0; i < 24576; i++) {
for (repeat = idx = 0; idx < MAX_INDEX; ++idx) for(b = 0, j = 0; j < 8; b |= (compress_result(Bitbase[8*i+j]) << j), j++);
repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN); assert(b == int(uint8_t(b)));
bitbase[i] = (uint8_t)b;
}
// Map 32 results into one KPKBitbase[] entry // Release allocated memory:
for (idx = 0; idx < MAX_INDEX; ++idx) delete [] Bitbase;
if (db[idx] == WIN)
KPKBitbase[idx / 32] |= 1 << (idx & 0x1F);
} }
namespace { namespace {
KPKPosition::KPKPosition(unsigned idx) { void KPKPosition::from_index(int index) {
int s;
ksq[WHITE] = Square((idx >> 0) & 0x3F); sideToMove = Color(index % 2);
ksq[BLACK] = Square((idx >> 6) & 0x3F); blackKingSquare = Square((index / 2) % 64);
us = Color ((idx >> 12) & 0x01); whiteKingSquare = Square((index / 128) % 64);
psq = make_square(File((idx >> 13) & 0x3), RANK_7 - Rank((idx >> 15) & 0x7)); s = (index / 8192) % 24;
pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1));
// Check 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
|| (us == WHITE && (StepAttacksBB[PAWN][psq] & ksq[BLACK])))
result = INVALID;
// Immediate win if a pawn can be promoted without getting captured
else if ( us == WHITE
&& rank_of(psq) == RANK_7
&& ksq[us] != psq + DELTA_N
&& ( distance(ksq[~us], psq + DELTA_N) > 1
|| (StepAttacksBB[KING][ksq[us]] & (psq + DELTA_N))))
result = WIN;
// Immediate draw if it is a stalemate or a king captures undefended pawn
else if ( us == BLACK
&& ( !(StepAttacksBB[KING][ksq[us]] & ~(StepAttacksBB[KING][ksq[~us]] | StepAttacksBB[PAWN][psq]))
|| (StepAttacksBB[KING][ksq[us]] & psq & ~StepAttacksBB[KING][ksq[~us]])))
result = DRAW;
// Position will be classified later
else
result = UNKNOWN;
} }
template<Color Us>
Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
// White to move: If one move leads to a position classified as WIN, the result int KPKPosition::to_index() const {
// of the current position is WIN. If all moves lead to positions classified return compute_index(whiteKingSquare, blackKingSquare, pawnSquare,
// as DRAW, the current position is classified as DRAW, otherwise the current sideToMove);
// 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 Color Them = (Us == WHITE ? BLACK : WHITE);
const Result Good = (Us == WHITE ? WIN : DRAW);
const Result Bad = (Us == WHITE ? DRAW : WIN);
Result r = INVALID; bool KPKPosition::is_legal() const {
Bitboard b = StepAttacksBB[KING][ksq[Us]]; 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;
}
while (b)
r |= Us == WHITE ? db[index(Them, ksq[Them] , pop_lsb(&b), psq)]
: db[index(Them, pop_lsb(&b), ksq[Them] , psq)];
if (Us == WHITE) bool KPKPosition::is_immediate_draw() const {
{ if(sideToMove == BLACK) {
if (rank_of(psq) < RANK_7) // Single push Bitboard wka = this->wk_attacks();
r |= db[index(Them, ksq[Them], ksq[Us], psq + DELTA_N)]; Bitboard bka = this->bk_attacks();
if ( rank_of(psq) == RANK_2 // Double push // Case 1: Stalemate
&& psq + DELTA_N != ksq[Us] if((bka & ~(wka | this->pawn_attacks())) == EmptyBoardBB)
&& psq + DELTA_N != ksq[Them]) return true;
r |= db[index(Them, ksq[Them], ksq[Us], psq + DELTA_N + DELTA_N)];
// 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
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;
}
}
+38
View File
@@ -0,0 +1,38 @@
/*
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(BITBASE_H_INCLUDED)
#define BITBASE_H_INCLUDED
////
//// Includes
////
#include "types.h"
////
//// Prototypes
////
extern void generate_kpk_bitbase(uint8_t bitbase[]);
#endif // !defined(BITBASE_H_INCLUDED)
+492 -266
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,301 +17,528 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
////
//// Includes
////
#include <iostream>
#include "bitboard.h" #include "bitboard.h"
#include "bitcount.h" #include "bitcount.h"
#include "misc.h" #include "direction.h"
int SquareDistance[SQUARE_NB][SQUARE_NB];
Bitboard RookMasks [SQUARE_NB]; #if defined(IS_64BIT)
Bitboard RookMagics [SQUARE_NB];
Bitboard* RookAttacks[SQUARE_NB];
unsigned RookShifts [SQUARE_NB];
Bitboard BishopMasks [SQUARE_NB]; const uint64_t BMult[64] = {
Bitboard BishopMagics [SQUARE_NB]; 0x440049104032280ULL, 0x1021023c82008040ULL, 0x404040082000048ULL,
Bitboard* BishopAttacks[SQUARE_NB]; 0x48c4440084048090ULL, 0x2801104026490000ULL, 0x4100880442040800ULL,
unsigned BishopShifts [SQUARE_NB]; 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
};
Bitboard SquareBB[SQUARE_NB]; const uint64_t RMult[64] = {
Bitboard FileBB[FILE_NB]; 0xa8002c000108020ULL, 0x4440200140003000ULL, 0x8080200010011880ULL,
Bitboard RankBB[RANK_NB]; 0x380180080141000ULL, 0x1a00060008211044ULL, 0x410001000a0c0008ULL,
Bitboard AdjacentFilesBB[FILE_NB]; 0x9500060004008100ULL, 0x100024284a20700ULL, 0x802140008000ULL,
Bitboard InFrontBB[COLOR_NB][RANK_NB]; 0x80c01002a00840ULL, 0x402004282011020ULL, 0x9862000820420050ULL,
Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; 0x1001448011100ULL, 0x6432800200800400ULL, 0x40100010002000cULL,
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; 0x2800d0010c080ULL, 0x90c0008000803042ULL, 0x4010004000200041ULL,
Bitboard LineBB[SQUARE_NB][SQUARE_NB]; 0x3010010200040ULL, 0xa40828028001000ULL, 0x123010008000430ULL,
Bitboard DistanceRingBB[SQUARE_NB][8]; 0x24008004020080ULL, 0x60040001104802ULL, 0x582200028400d1ULL,
Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; 0x4000802080044000ULL, 0x408208200420308ULL, 0x610038080102000ULL,
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; 0x3601000900100020ULL, 0x80080040180ULL, 0xc2020080040080ULL,
Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; 0x80084400100102ULL, 0x4022408200014401ULL, 0x40052040800082ULL,
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; 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 { namespace {
// De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan void init_masks();
const uint64_t DeBruijn64 = 0x3F79D71B4CB0A89ULL; void init_ray_bitboards();
const uint32_t DeBruijn32 = 0x783A9B23; void init_attacks();
void init_between_bitboards();
int MSBTable[256]; // To implement software msb() Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
Square BSFTable[SQUARE_NB]; // To implement software bitscan int fmin, int fmax, int rmin, int rmax);
Bitboard RookTable[0x19000]; // To store rook attacks Bitboard index_to_bitboard(int index, Bitboard mask);
Bitboard BishopTable[0x1480]; // To store bishop attacks void init_sliding_attacks(Bitboard attacks[],
int attackIndex[], Bitboard mask[],
typedef unsigned (Fn)(Square, Bitboard); const int shift[2], const Bitboard mult[],
int deltas[][2]);
void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], void init_pseudo_attacks();
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index);
// bsf_index() returns the index into BSFTable[] to look up the bitscan. Uses
// Matt Taylor's folding for 32 bit case, extended to 64 bit by Kim Walisch.
unsigned bsf_index(Bitboard b) {
b ^= b - 1;
return Is64Bit ? (b * DeBruijn64) >> 58
: ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn32) >> 26;
}
} }
#ifndef USE_BSFQ
/// Software fall-back of lsb() and msb() for CPU lacking hardware support ////
//// Functions
////
Square lsb(Bitboard b) { /// print_bitboard() prints a bitboard in an easily readable format to the
return BSFTable[bsf_index(b)]; /// standard output. This is sometimes useful for debugging.
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;
}
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
} }
Square msb(Bitboard b) {
unsigned b32; /// init_bitboards() initializes various bitboard arrays. It is called during
int result = 0; /// program initialization.
if (b > 0xFFFFFFFF) void init_bitboards() {
{ int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
b >>= 32; int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
result = 32; 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();
}
b32 = unsigned(b);
if (b32 > 0xFFFF) /// 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
b32 >>= 16; /// nonzero bitboard.
#if defined(IS_64BIT) && !defined(USE_BSFQ)
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
};
Square first_1(Bitboard b) {
return Square(BitTable[((b & -b) * 0x218a392cd3d5dbfULL) >> 58]);
}
Square pop_1st_bit(Bitboard* b) {
Bitboard bb = *b;
*b &= (*b - 1);
return Square(BitTable[((bb & -bb) * 0x218a392cd3d5dbfULL) >> 58]);
}
#elif !defined(USE_BSFQ)
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
};
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; result += 16;
} }
if (b > 0xFF) {
if (b32 > 0xFF) b >>= 8;
{
b32 >>= 8;
result += 8; result += 8;
} }
if (b > 0xF) {
return Square(result + MSBTable[b32]); b >>= 4;
result += 4;
}
if (b > 0x3) {
b >>= 2;
result += 2;
}
return result + (b > 0) + (b > 1);
} }
#endif // ifndef USE_BSFQ namespace {
// 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.
/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable void init_masks() {
/// to be printed to standard output. Useful for debugging. SetMaskBB[SQ_NONE] = 0ULL;
ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE];
const std::string Bitboards::pretty(Bitboard b) { for(Square s = SQ_A1; s <= SQ_H8; s++) {
SetMaskBB[s] = (1ULL << s);
std::string s = "+---+---+---+---+---+---+---+---+\n"; ClearMaskBB[s] = ~SetMaskBB[s];
}
for (Rank r = RANK_8; r >= RANK_1; --r) for(Color c = WHITE; c <= BLACK; c++)
{ for(Square s = SQ_A1; s <= SQ_H8; s++) {
for (File f = FILE_A; f <= FILE_H; ++f) PassedPawnMask[c][s] =
s += b & make_square(f, r) ? "| X " : "| "; in_front_bb(c, s) & this_and_neighboring_files_bb(s);
OutpostMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
s += "|\n+---+---+---+---+---+---+---+---+\n";
}
return s;
}
/// Bitboards::init() initializes various bitboard tables. It is called at
/// startup and relies on global objects to be already zero-initialized.
void Bitboards::init() {
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
SquareBB[s] = 1ULL << s;
BSFTable[bsf_index(SquareBB[s])] = s;
}
for (Bitboard b = 2; b < 256; ++b)
MSBTable[b] = MSBTable[b - 1] + !more_than_one(b);
for (File f = FILE_A; f <= FILE_H; ++f)
FileBB[f] = f > FILE_A ? FileBB[f - 1] << 1 : FileABB;
for (Rank r = RANK_1; r <= RANK_8; ++r)
RankBB[r] = r > RANK_1 ? RankBB[r - 1] << 8 : Rank1BB;
for (File f = FILE_A; f <= FILE_H; ++f)
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
for (Rank r = RANK_1; r < RANK_8; ++r)
InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]);
for (Color c = WHITE; c <= BLACK; ++c)
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)];
PawnAttackSpan[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
PassedPawnMask[c][s] = ForwardBB[c][s] | PawnAttackSpan[c][s];
} }
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Bitboard b = 0ULL; b < 256ULL; b++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) BitCount8Bit[b] = (uint8_t)count_1s(b);
if (s1 != s2)
{
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
DistanceRingBB[s1][SquareDistance[s1][s2] - 1] |= s2;
}
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= KING; ++pt)
for (Square s = SQ_A1; s <= SQ_H8; ++s)
for (int i = 0; steps[pt][i]; ++i)
{
Square to = s + Square(c == WHITE ? steps[pt][i] : -steps[pt][i]);
if (is_ok(to) && distance(s, to) < 3)
StepAttacksBB[make_piece(c, pt)][s] |= to;
}
Square RookDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W };
Square BishopDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW };
init_magics(RookTable, RookAttacks, RookMagics, RookMasks, RookShifts, RookDeltas, magic_index<ROOK>);
init_magics(BishopTable, BishopAttacks, BishopMagics, BishopMasks, BishopShifts, BishopDeltas, magic_index<BISHOP>);
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
{
PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb<BISHOP>(s1, 0);
PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0);
for (Piece pc = W_BISHOP; pc <= W_ROOK; ++pc)
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
{
if (!(PseudoAttacks[pc][s1] & s2))
continue;
LineBB[s1][s2] = (attacks_bb(pc, s1, 0) & attacks_bb(pc, s2, 0)) | s1 | s2;
BetweenBB[s1][s2] = attacks_bb(pc, s1, SquareBB[s2]) & attacks_bb(pc, s2, SquareBB[s1]);
}
}
}
namespace {
Bitboard sliding_attack(Square deltas[], Square sq, Bitboard occupied) {
Bitboard attack = 0;
for (int i = 0; i < 4; ++i)
for (Square s = sq + deltas[i];
is_ok(s) && distance(s, s - deltas[i]) == 1;
s += deltas[i])
{
attack |= s;
if (occupied & s)
break;
}
return attack;
} }
// init_magics() computes all rook and bishop attacks at startup. Magic void init_ray_bitboards() {
// bitboards are used to look up attacks of sliding pieces. As a reference see int d[8] = {1, -1, 16, -16, 17, -17, 15, -15};
// chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we for(int i = 0; i < 128; i = (i + 9) & ~8) {
// use the so called "fancy" approach. for(int j = 0; j < 8; j++) {
RayBB[(i&7)|((i>>4)<<3)][j] = EmptyBoardBB;
void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], for(int k = i + d[j]; (k & 0x88) == 0; k += d[j])
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) { set_bit(&(RayBB[(i&7)|((i>>4)<<3)][j]), Square((k&7)|((k>>4)<<3)));
}
int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 },
{ 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } };
Bitboard occupancy[4096], reference[4096], edges, b;
int age[4096] = {0}, current = 0, i, size;
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
attacks[SQ_A1] = table;
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.
masks[s] = sliding_attack(deltas, s, 0) & ~edges;
shifts[s] = (Is64Bit ? 64 : 32) - popcount<Max15>(masks[s]);
// 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(deltas, s, b);
if (HasPext)
attacks[s][pext(b, masks[s])] = reference[size];
size++;
b = (b - masks[s]) & masks[s];
} while (b);
// Set the offset for the table of the next square. We have individual
// table sizes for each square with "Fancy Magic Bitboards".
if (s < SQ_H8)
attacks[s + 1] = attacks[s] + size;
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.
do {
do
magics[s] = rng.sparse_rand<Bitboard>();
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
// 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.
for (++current, i = 0; i < size; ++i)
{
unsigned idx = index(s, occupancy[i]);
if (age[idx] < current)
{
age[idx] = current;
attacks[s][idx] = reference[i];
}
else if (attacks[s][idx] != reference[i])
break;
}
} while (i < size);
} }
} }
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}
};
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);
}
}
}
}
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);
}
}
} }
+244 -221
View File
@@ -1,14 +1,14 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Stockfish is distributed in the hope that it will be useful, Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@@ -18,111 +18,125 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define BITBOARD_H_INCLUDED
#include <string> ////
//// Includes
////
#include "direction.h"
#include "piece.h"
#include "square.h"
#include "types.h" #include "types.h"
namespace Bitbases {
void init(); ////
bool probe(Square wksq, Square wpsq, Square bksq, Color us); //// Constants and variables
////
} const Bitboard EmptyBoardBB = 0ULL;
namespace Bitboards { const Bitboard WhiteSquaresBB = 0x55AA55AA55AA55AAULL;
const Bitboard BlackSquaresBB = 0xAA55AA55AA55AA55ULL;
void init();
const std::string pretty(Bitboard b);
}
const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
const Bitboard FileABB = 0x0101010101010101ULL; const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1; const Bitboard FileBBB = 0x0202020202020202ULL;
const Bitboard FileCBB = FileABB << 2; const Bitboard FileCBB = 0x0404040404040404ULL;
const Bitboard FileDBB = FileABB << 3; const Bitboard FileDBB = 0x0808080808080808ULL;
const Bitboard FileEBB = FileABB << 4; const Bitboard FileEBB = 0x1010101010101010ULL;
const Bitboard FileFBB = FileABB << 5; const Bitboard FileFBB = 0x2020202020202020ULL;
const Bitboard FileGBB = FileABB << 6; const Bitboard FileGBB = 0x4040404040404040ULL;
const Bitboard FileHBB = FileABB << 7; const Bitboard FileHBB = 0x8080808080808080ULL;
const Bitboard Rank1BB = 0xFF; const Bitboard Rank1BB = 0xFFULL;
const Bitboard Rank2BB = Rank1BB << (8 * 1); const Bitboard Rank2BB = 0xFF00ULL;
const Bitboard Rank3BB = Rank1BB << (8 * 2); const Bitboard Rank3BB = 0xFF0000ULL;
const Bitboard Rank4BB = Rank1BB << (8 * 3); const Bitboard Rank4BB = 0xFF000000ULL;
const Bitboard Rank5BB = Rank1BB << (8 * 4); const Bitboard Rank5BB = 0xFF00000000ULL;
const Bitboard Rank6BB = Rank1BB << (8 * 5); const Bitboard Rank6BB = 0xFF0000000000ULL;
const Bitboard Rank7BB = Rank1BB << (8 * 6); const Bitboard Rank7BB = 0xFF000000000000ULL;
const Bitboard Rank8BB = Rank1BB << (8 * 7); const Bitboard Rank8BB = 0xFF00000000000000ULL;
extern int SquareDistance[SQUARE_NB][SQUARE_NB]; 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];
extern Bitboard RookMasks [SQUARE_NB]; extern Bitboard SetMaskBB[65];
extern Bitboard RookMagics [SQUARE_NB]; extern Bitboard ClearMaskBB[65];
extern Bitboard* RookAttacks[SQUARE_NB];
extern unsigned RookShifts [SQUARE_NB];
extern Bitboard BishopMasks [SQUARE_NB]; extern Bitboard StepAttackBB[16][64];
extern Bitboard BishopMagics [SQUARE_NB]; extern Bitboard RayBB[64][8];
extern Bitboard* BishopAttacks[SQUARE_NB]; extern Bitboard BetweenBB[64][64];
extern unsigned BishopShifts [SQUARE_NB];
extern Bitboard SquareBB[SQUARE_NB]; extern Bitboard PassedPawnMask[2][64];
extern Bitboard FileBB[FILE_NB]; extern Bitboard OutpostMask[2][64];
extern Bitboard RankBB[RANK_NB];
extern Bitboard AdjacentFilesBB[FILE_NB]; extern const uint64_t RMult[64];
extern Bitboard InFrontBB[COLOR_NB][RANK_NB]; extern const int RShift[64];
extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; extern Bitboard RMask[64];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern int RAttackIndex[64];
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard RAttacks[0x19000];
extern Bitboard DistanceRingBB[SQUARE_NB][8];
extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; extern const uint64_t BMult[64];
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; extern const int BShift[64];
extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; extern Bitboard BMask[64];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern int BAttackIndex[64];
extern Bitboard BAttacks[0x1480];
extern Bitboard BishopPseudoAttacks[64];
extern Bitboard RookPseudoAttacks[64];
extern Bitboard QueenPseudoAttacks[64];
extern uint8_t BitCount8Bit[256];
/// Overloads of bitwise operators between a Bitboard and a Square for testing ////
/// whether a given bit is set in a bitboard, and for setting and clearing bits. //// Inline functions
////
inline Bitboard operator&(Bitboard b, Square s) { /// Functions for testing whether a given bit is set in a bitboard, and for
return b & SquareBB[s]; /// setting and clearing bits.
inline Bitboard bit_is_set(Bitboard b, Square s) {
return b & SetMaskBB[s];
} }
inline Bitboard operator|(Bitboard b, Square s) { inline void set_bit(Bitboard *b, Square s) {
return b | SquareBB[s]; *b |= SetMaskBB[s];
} }
inline Bitboard operator^(Bitboard b, Square s) { inline void clear_bit(Bitboard *b, Square s) {
return b ^ SquareBB[s]; *b &= ClearMaskBB[s];
}
inline Bitboard& operator|=(Bitboard& b, Square s) {
return b |= SquareBB[s];
}
inline Bitboard& operator^=(Bitboard& b, Square s) {
return b ^= SquareBB[s];
}
inline bool more_than_one(Bitboard b) {
return b & (b - 1);
} }
/// rank_bb() and file_bb() return a bitboard representing all the squares on /// Functions used to update a bitboard after a move. This is faster
/// the given file or rank. /// then calling a sequence of clear_bit() + set_bit()
inline Bitboard make_move_bb(Square from, Square to) {
return SetMaskBB[from] | SetMaskBB[to];
}
inline void do_move_bb(Bitboard *b, Bitboard move_bb) {
*b ^= move_bb;
}
/// 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.
inline Bitboard rank_bb(Rank r) { inline Bitboard rank_bb(Rank r) {
return RankBB[r]; return RankBB[r];
} }
inline Bitboard rank_bb(Square s) { inline Bitboard rank_bb(Square s) {
return RankBB[rank_of(s)]; return rank_bb(square_rank(s));
} }
inline Bitboard file_bb(File f) { inline Bitboard file_bb(File f) {
@@ -130,203 +144,212 @@ inline Bitboard file_bb(File f) {
} }
inline Bitboard file_bb(Square s) { inline Bitboard file_bb(Square s) {
return FileBB[file_of(s)]; return file_bb(square_file(s));
} }
/// shift_bb() moves a bitboard one step along direction Delta. Mainly for pawns /// neighboring_files_bb takes a file or a square as input, and returns a
/// bitboard representing all squares on the neighboring files.
template<Square Delta> inline Bitboard neighboring_files_bb(File f) {
inline Bitboard shift_bb(Bitboard b) { return NeighboringFilesBB[f];
return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8 }
: Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7
: Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9 inline Bitboard neighboring_files_bb(Square s) {
: 0; return neighboring_files_bb(square_file(s));
} }
/// adjacent_files_bb() returns a bitboard representing all the squares on the /// this_and_neighboring_files_bb takes a file or a square as input, and
/// adjacent files of the given one. /// returns a bitboard representing all squares on the given and neighboring
/// files.
inline Bitboard adjacent_files_bb(File f) { inline Bitboard this_and_neighboring_files_bb(File f) {
return AdjacentFilesBB[f]; return ThisAndNeighboringFilesBB[f];
}
inline Bitboard this_and_neighboring_files_bb(Square s) {
return this_and_neighboring_files_bb(square_file(s));
} }
/// between_bb() returns a bitboard representing all the squares between the two /// relative_rank_bb() takes a color and a rank as input, and returns a bitboard
/// given ones. For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with /// representing all squares on the given rank from the given color's point of
/// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank, file /// view. For instance, relative_rank_bb(WHITE, 7) gives all squares on the
/// or diagonal, 0 is returned. /// 7th rank, while relative_rank_bb(BLACK, 7) gives all squares on the 2nd
/// rank.
inline Bitboard between_bb(Square s1, Square s2) { inline Bitboard relative_rank_bb(Color c, Rank r) {
return BetweenBB[s1][s2]; return RelativeRankBB[c][r];
} }
/// in_front_bb() returns a bitboard representing all the squares on all the ranks /// in_front_bb() takes a color and a rank or square as input, and returns a
/// in front of the given one, from the point of view of the given color. For /// bitboard representing all the squares on all ranks in front of the rank
/// instance, in_front_bb(BLACK, RANK_3) will return the squares on ranks 1 and 2. /// (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.
inline Bitboard in_front_bb(Color c, Rank r) { inline Bitboard in_front_bb(Color c, Rank r) {
return InFrontBB[c][r]; return InFrontBB[c][r];
} }
inline Bitboard in_front_bb(Color c, Square s) {
/// forward_bb() returns a bitboard representing all the squares along the line return in_front_bb(c, square_rank(s));
/// in front of the given one, from the point of view of the given color:
/// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s)
inline Bitboard forward_bb(Color c, Square s) {
return ForwardBB[c][s];
} }
/// pawn_attack_span() returns a bitboard representing all the squares that can be /// behind_bb() takes a color and a rank or square as input, and returns a
/// attacked by a pawn of the given color when it moves along its file, starting /// bitboard representing all the squares on all ranks behind of the rank
/// from the given square: /// (or square), from the given color's point of view.
/// PawnAttackSpan[c][s] = in_front_bb(c, s) & adjacent_files_bb(s);
inline Bitboard pawn_attack_span(Color c, Square s) { inline Bitboard behind_bb(Color c, Rank r) {
return PawnAttackSpan[c][s]; return InFrontBB[opposite_color(c)][r];
}
inline Bitboard behind_bb(Color c, Square s) {
return in_front_bb(opposite_color(c), square_rank(s));
} }
/// passed_pawn_mask() returns a bitboard mask which can be used to test if a /// ray_bb() gives a bitboard representing all squares along the ray in a
/// pawn of the given color and on the given square is a passed pawn: /// given direction from a given square.
/// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_bb(c, s)
inline Bitboard ray_bb(Square s, SignedDirection d) {
return RayBB[s][d];
}
/// 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.
#if defined(IS_64BIT)
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] + ((b * RMult[s]) >> RShift[s])];
}
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);
}
/// 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];
}
/// 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.
inline Bitboard squares_in_front_of(Color c, Square s) {
return in_front_bb(c, s) & file_bb(s);
}
/// squares_behind is similar to squares_in_front, but returns the squares
/// behind the square instead of in front of the square.
inline Bitboard squares_behind(Color c, Square s) {
return in_front_bb(opposite_color(c), s) & file_bb(s);
}
/// 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.
inline Bitboard passed_pawn_mask(Color c, Square s) { inline Bitboard passed_pawn_mask(Color c, Square s) {
return PassedPawnMask[c][s]; return PassedPawnMask[c][s];
} }
/// aligned() returns true if the squares s1, s2 and s3 are aligned either on a /// outpost_mask takes a color and a square as input, and returns a bitboard
/// straight or on a diagonal line. /// mask which can be used to test whether a piece on the square can possibly
/// be driven away by an enemy pawn.
inline bool aligned(Square s1, Square s2, Square s3) { inline Bitboard outpost_mask(Color c, Square s) {
return LineBB[s1][s2] & s3; return OutpostMask[c][s];
} }
/// distance() functions return the distance between x and y, defined as the /// isolated_pawn_mask takes a square as input, and returns a bitboard mask
/// number of steps for a king in x to reach y. Works with squares, ranks, files. /// which can be used to test whether a pawn on the given square is isolated.
template<typename T> inline int distance(T x, T y) { return x < y ? y - x : x - y; } inline Bitboard isolated_pawn_mask(Square s) {
template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; } return neighboring_files_bb(s);
template<typename T1, typename T2> inline int distance(T2 x, T2 y);
template<> inline int distance<File>(Square x, Square y) { return distance(file_of(x), file_of(y)); }
template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_of(x), rank_of(y)); }
/// attacks_bb() returns a bitboard representing all the squares attacked by a
/// piece of type Pt (bishop or rook) placed on 's'. The helper magic_index()
/// looks up the index using the 'magic bitboards' approach.
template<PieceType Pt>
inline unsigned magic_index(Square s, Bitboard occupied) {
Bitboard* const Masks = Pt == ROOK ? RookMasks : BishopMasks;
Bitboard* const Magics = Pt == ROOK ? RookMagics : BishopMagics;
unsigned* const Shifts = Pt == ROOK ? RookShifts : BishopShifts;
if (HasPext)
return unsigned(pext(occupied, Masks[s]));
if (Is64Bit)
return unsigned(((occupied & Masks[s]) * Magics[s]) >> Shifts[s]);
unsigned lo = unsigned(occupied) & unsigned(Masks[s]);
unsigned hi = unsigned(occupied >> 32) & unsigned(Masks[s] >> 32);
return (lo * unsigned(Magics[s]) ^ hi * unsigned(Magics[s] >> 32)) >> Shifts[s];
}
template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
return (Pt == ROOK ? RookAttacks : BishopAttacks)[s][magic_index<Pt>(s, occupied)];
}
inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) {
switch (type_of(pc))
{
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 StepAttacksBB[pc][s];
}
} }
/// lsb() and msb() return the least/most significant bit in a non-zero bitboard /// 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.
#ifdef USE_BSFQ #if defined(USE_BSFQ) // Assembly code by Heinz van Saanen
# if defined(_MSC_VER) && !defined(__INTEL_COMPILER) inline Square first_1(Bitboard b) {
Bitboard dummy;
inline Square lsb(Bitboard b) { __asm__("bsfq %1, %0": "=r"(dummy): "rm"(b) );
unsigned long idx; return (Square)(dummy);
_BitScanForward64(&idx, b);
return (Square) idx;
} }
inline Square msb(Bitboard b) { inline Square pop_1st_bit(Bitboard* b) {
unsigned long idx; const Square s = first_1(*b);
_BitScanReverse64(&idx, b); *b &= ~(1ULL<<s);
return (Square) idx; return s;
} }
# elif defined(__arm__) #else // if !defined(USE_BSFQ)
inline int lsb32(uint32_t v) { extern Square first_1(Bitboard b);
__asm__("rbit %0, %1" : "=r"(v) : "r"(v)); extern Square pop_1st_bit(Bitboard* b);
return __builtin_clz(v);
}
inline Square msb(Bitboard b) {
return (Square) (63 - __builtin_clzll(b));
}
inline Square lsb(Bitboard b) {
return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32)));
}
# else // Assumed gcc or compatible compiler
inline Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen
Bitboard idx;
__asm__("bsfq %1, %0": "=r"(idx): "rm"(b) );
return (Square) idx;
}
inline Square msb(Bitboard b) {
Bitboard idx;
__asm__("bsrq %1, %0": "=r"(idx): "rm"(b) );
return (Square) idx;
}
# endif
#else // ifdef(USE_BSFQ)
Square lsb(Bitboard b);
Square msb(Bitboard b);
#endif #endif
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard ////
//// Prototypes
////
inline Square pop_lsb(Bitboard* b) { extern void print_bitboard(Bitboard b);
const Square s = lsb(*b); extern void init_bitboards();
*b &= *b - 1; extern int bitScanReverse32(uint32_t b);
return s;
}
/// frontmost_sq() and backmost_sq() return the square corresponding to the #endif // !defined(BITBOARD_H_INCLUDED)
/// most/least advanced bit relative to the given color.
inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); }
inline Square backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); }
#endif // #ifndef BITBOARD_H_INCLUDED
+120 -58
View File
@@ -1,14 +1,14 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Stockfish is distributed in the hope that it will be useful, Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@@ -18,88 +18,150 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef BITCOUNT_H_INCLUDED
#define BITCOUNT_H_INCLUDED
#include <cassert> #if !defined(BITCOUNT_H_INCLUDED)
#define BITCOUNT_H_INCLUDED
#include "types.h" #include "types.h"
enum BitCountType { // Select type of intrinsic bit count instruction to use, see
CNT_64, // README.txt on how to pgo compile with POPCNT support.
CNT_64_MAX15,
CNT_32,
CNT_32_MAX15,
CNT_HW_POPCNT
};
/// Determine at compile time the best popcount<> specialization according to #if defined(__INTEL_COMPILER) && defined(USE_POPCNT) // Intel compiler
/// whether the platform is 32 or 64 bit, the maximum number of non-zero
/// bits to count and if the hardware popcnt instruction is available.
const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
#include <nmmintrin.h>
/// popcount() counts the number of non-zero bits in a bitboard inline bool cpu_has_popcnt() {
template<BitCountType> inline int popcount(Bitboard);
template<> int CPUInfo[4] = {-1};
inline int popcount<CNT_64>(Bitboard b) { __cpuid(CPUInfo, 0x00000001);
b -= (b >> 1) & 0x5555555555555555ULL; return (CPUInfo[2] >> 23) & 1;
b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b >> 4) + b) & 0x0F0F0F0F0F0F0F0FULL;
return (b * 0x0101010101010101ULL) >> 56;
} }
template<> #define POPCNT_INTRINSIC(x) _mm_popcnt_u64(x)
inline int popcount<CNT_64_MAX15>(Bitboard b) {
b -= (b >> 1) & 0x5555555555555555ULL; #elif defined(_MSC_VER) && defined(USE_POPCNT) // Microsoft compiler
b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
return (b * 0x1111111111111111ULL) >> 60; #include <intrin.h>
inline bool cpu_has_popcnt() {
int CPUInfo[4] = {-1};
__cpuid(CPUInfo, 0x00000001);
return (CPUInfo[2] >> 23) & 1;
} }
template<> #define POPCNT_INTRINSIC(x) __popcnt64(x)
inline int popcount<CNT_32>(Bitboard b) {
#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); unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555; w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333); w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
v = ((v >> 4) + v + (w >> 4) + w) & 0x0F0F0F0F; v = ((v >> 4) + v) & 0x0F0F0F0F; // 0-8 in 8 bits
return (v * 0x01010101) >> 24; v += (((w >> 4) + w) & 0x0F0F0F0F); // 0-16 in 8 bits
v *= 0x01010101; // mul is fast on amd procs
return int(v >> 24);
} }
template<> inline int count_1s_max_15(Bitboard b) {
inline int popcount<CNT_32_MAX15>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b); unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555; w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333); w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
return ((v + w) * 0x11111111) >> 28; v += w; // 0-8 in 4 bits
v *= 0x11111111;
return int(v >> 28);
} }
template<> #endif // BITCOUNT
inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#ifndef USE_POPCNT
assert(false); /// count_1s() counts the number of nonzero bits in a bitboard.
return b != 0; // Avoid 'b not used' warning /// If template parameter is true an intrinsic is called, otherwise
/// we fallback on a software implementation.
#elif defined(_MSC_VER) && defined(__INTEL_COMPILER) template<bool UseIntrinsic>
inline int count_1s(Bitboard b) {
return _mm_popcnt_u64(b); return UseIntrinsic ? POPCNT_INTRINSIC(b) : count_1s(b);
}
#elif defined(_MSC_VER) template<bool UseIntrinsic>
inline int count_1s_max_15(Bitboard b) {
return (int)__popcnt64(b); return UseIntrinsic ? POPCNT_INTRINSIC(b) : count_1s_max_15(b);
}
#else // Assumed gcc or compatible compiler
return __builtin_popcountll(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
}
#endif // #ifndef BITCOUNT_H_INCLUDED #endif // !defined(BITCOUNT_H_INCLUDED)
+583
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+87
View File
@@ -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;
}
}
}
}
+92
View File
@@ -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)
+578 -511
View File
File diff suppressed because it is too large Load Diff
+70 -89
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,108 +17,90 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define ENDGAME_H_INCLUDED
#include <map> ////
#include <memory> //// Includes
#include <string> ////
#include <type_traits>
#include <utility>
#include "position.h" #include "position.h"
#include "types.h" #include "scale.h"
#include "value.h"
/// EndgameType lists all supported endgames ////
//// Types
////
enum EndgameType { enum EndgameType {
// Evaluation functions // 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
KNNK, // KNN vs K // Scaling functions
KXK, // Generic "mate lone king" eval KBPsK, // KB+pawns vs K
KBNK, // KBN vs K KQKRPs, // KQ vs KR+pawns
KPK, // KP vs K KRPKR, // KRP vs KR
KRKP, // KR vs KP KRPPKRP, // KRPP vs KRP
KRKB, // KR vs KB KPsK, // King and pawns vs king
KRKN, // KR vs KN KBPKB, // KBP vs KB
KQKP, // KQ vs KP KBPPKB, // KBPP vs KB
KQKR, // KQ vs KR KBPKN, // KBP vs KN
KNPK, // KNP vs K
KPKP // KP vs KP
// Scaling functions
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
KNPK, // KNP vs K
KNPKB, // KNP vs KB
KPKP // KP vs KP
}; };
/// Template abstract base class for all special endgame functions
/// Endgame functions can be of two types depending on whether they return a
/// Value or a ScaleFactor.
template<EndgameType E> using
eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type;
/// Base and derived templates for endgame evaluation and scaling functions
template<typename T> template<typename T>
struct EndgameBase { class EndgameFunctionBase {
virtual ~EndgameBase() = default;
virtual Color strong_side() const = 0;
virtual T operator()(const Position&) const = 0;
};
template<EndgameType E, typename T = eg_type<E>>
struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : strongSide(c), weakSide(~c) {}
Color strong_side() const { return strongSide; }
T operator()(const Position&) const;
private:
Color strongSide, weakSide;
};
/// The Endgames class stores 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().
class Endgames {
template<typename T> using Map = std::map<Key, std::unique_ptr<EndgameBase<T>>>;
template<EndgameType E, typename T = eg_type<E>>
void add(const std::string& code);
template<typename T>
Map<T>& map() {
return std::get<std::is_same<T, ScaleFactor>::value>(maps);
}
std::pair<Map<Value>, Map<ScaleFactor>> maps;
public: public:
Endgames(); EndgameFunctionBase(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {}
virtual ~EndgameFunctionBase() {}
virtual T apply(const Position&) = 0;
Color color() const { return strongerSide; }
template<typename T> protected:
EndgameBase<T>* probe(Key key) { Color strongerSide, weakerSide;
return map<T>().count(key) ? map<T>()[key].get() : nullptr;
}
}; };
#endif // #ifndef ENDGAME_H_INCLUDED 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&);
};
////
//// Prototypes
////
extern void init_bitbases();
#endif // !defined(ENDGAME_H_INCLUDED)
+1067 -720
View File
File diff suppressed because it is too large Load Diff
+85 -13
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,24 +17,97 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define EVALUATE_H_INCLUDED
#include <string> ////
//// Includes
////
#include "types.h" #include <iostream>
#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; class Position;
namespace Eval { struct EvalInfo {
const Value Tempo = Value(20); // Must be visible to search // Middle game and endgame evaluations
Score value;
void init(); // Pointers to material and pawn hash table entries
std::string trace(const Position& pos); MaterialInfo* mi;
PawnInfo* pi;
template<bool DoTrace = false> // attackedBy[color][piece type] is a bitboard representing all squares
Value evaluate(const Position& pos); // 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]; }
#endif // #ifndef EVALUATE_H_INCLUDED // 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];
// 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];
// 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];
// 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];
// mateThreat[color] is a move for the given side which gives a direct mate.
Move mateThreat[2];
// Middle game and endgame mobility scores.
Score mobility;
// Extra futility margin. This is added to the standard futility margin
// in the quiescence search.
Value futilityMargin;
};
////
//// 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)
+92
View File
@@ -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];
}
+73
View File
@@ -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)
+103
View File
@@ -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)
+61 -24
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,35 +17,73 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
// To profile with callgrind uncomment following line
//#define USE_CALLGRIND
////
//// Includes
////
#include <iostream> #include <iostream>
#include <string>
#include "bitboard.h" #include "benchmark.h"
#include "evaluate.h" #include "bitcount.h"
#include "position.h" #include "misc.h"
#include "search.h"
#include "thread.h"
#include "tt.h"
#include "uci.h" #include "uci.h"
#include "syzygy/tbprobe.h"
int main(int argc, char* argv[]) { #ifdef USE_CALLGRIND
#include <valgrind/callgrind.h>
#endif
std::cout << engine_info() << std::endl; using namespace std;
UCI::init(Options);
PSQT::init();
Bitboards::init();
Position::init();
Bitbases::init();
Search::init();
Eval::init();
Pawns::init();
Threads.init();
Tablebases::init(Options["SyzygyPath"]);
TT.resize(Options["Hash"]);
UCI::loop(argc, argv); ////
//// Functions
////
Threads.exit(); int main(int argc, char *argv[]) {
// 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; return 0;
} }
+357 -155
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,211 +17,414 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm> // For std::min
////
//// Includes
////
#include <cassert> #include <cassert>
#include <cstring> // For std::memset #include <sstream>
#include <map>
#include "material.h" #include "material.h"
#include "thread.h"
using namespace std; using namespace std;
////
//// Local definitions
////
namespace { namespace {
// Polynomial material imbalance parameters // Values modified by Joona Kiiski
const Value MidgameLimit = Value(15581);
const Value EndgameLimit = Value(3998);
// pair pawn knight bishop rook queen // Polynomial material balance parameters
const int Linear[6] = { 1667, -168, -1027, -166, 238, -138 }; const Value RedundantQueenPenalty = Value(320);
const Value RedundantRookPenalty = Value(554);
const int QuadraticOurs[][PIECE_TYPE_NB] = { const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
// OUR PIECES
// pair pawn knight bishop rook queen
{ 0 }, // Bishop pair
{ 40, 2 }, // Pawn
{ 32, 255, -3 }, // Knight OUR PIECES
{ 0, 104, 4, 0 }, // Bishop
{ -26, -2, 47, 105, -149 }, // Rook
{-185, 24, 122, 137, -134, 0 } // Queen
};
const int QuadraticTheirs[][PIECE_TYPE_NB] = { const int QuadraticCoefficientsSameColor[][6] = {
// THEIR PIECES { 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 },
// pair pawn knight bishop rook queen { 7, 25, 4, 7, 7, 7 }, { -27, -2, 46, 100, 56, 7 }, { 58, 29, 83, 148, -3, -25 } };
{ 0 }, // Bishop pair
{ 36, 0 }, // Pawn
{ 9, 63, 0 }, // Knight OUR PIECES
{ 59, 65, 42, 0 }, // Bishop
{ 46, 39, 24, -24, 0 }, // Rook
{ 101, 100, -37, 141, 268, 0 } // Queen
};
// Endgame evaluation and scaling functions are accessed directly and not through const int QuadraticCoefficientsOppositeColor[][6] = {
// the function maps because they correspond to more than one material hash key. { 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 },
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) }; { 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) }; // Named endgame evaluation and scaling functions, these
Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) }; // are accessed direcly and not through the function maps.
Endgame<KPsK> ScaleKPsK[] = { Endgame<KPsK>(WHITE), Endgame<KPsK>(BLACK) }; EvaluationFunction<KmmKm> EvaluateKmmKm(WHITE);
Endgame<KPKP> ScaleKPKP[] = { Endgame<KPKP>(WHITE), Endgame<KPKP>(BLACK) }; 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 typedef EndgameEvaluationFunctionBase EF;
bool is_KXK(const Position& pos, Color us) { typedef EndgameScalingFunctionBase SF;
return !more_than_one(pos.pieces(~us)) }
&& pos.non_pawn_material(us) >= RookValueMg;
}
bool is_KBPsKs(const Position& pos, Color us) {
return pos.non_pawn_material(us) == BishopValueMg
&& pos.count<BISHOP>(us) == 1
&& pos.count<PAWN >(us) >= 1;
}
bool is_KQKRPs(const Position& pos, Color us) { ////
return !pos.count<PAWN>(us) //// Classes
&& pos.non_pawn_material(us) == QueenValueMg ////
&& pos.count<QUEEN>(us) == 1
&& pos.count<ROOK>(~us) == 1
&& pos.count<PAWN>(~us) >= 1;
}
/// imbalance() calculates the imbalance by comparing the piece count of each /// EndgameFunctions class stores endgame evaluation and scaling functions
/// piece type for both colors. /// in two std::map. Because STL library is not guaranteed to be thread
template<Color Us> /// safe even for read access, the maps, although with identical content,
int imbalance(const int pieceCount[][PIECE_TYPE_NB]) { /// are replicated for each thread. This is faster then using locks.
const Color Them = (Us == WHITE ? BLACK : WHITE); class EndgameFunctions {
public:
EndgameFunctions();
~EndgameFunctions();
template<class T> T* get(Key key) const;
int bonus = 0; private:
template<class T> void add(const string& keyCode);
// Second-degree polynomial material imbalance by Tord Romstad static Key buildKey(const string& keyCode);
for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1) static const string swapColors(const string& keyCode);
{
if (!pieceCount[Us][pt1])
continue;
int v = Linear[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) // Maps accessing functions returning const and non-const references
v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2] template<typename T> const map<Key, T*>& get() const { return maps.first; }
+ QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2]; 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; template<> map<Key, SF*>&
} EndgameFunctions::get<SF>() { return maps.second; }
} // namespace
namespace Material { ////
//// Functions
////
/// Material::probe() looks up the current position's material configuration in /// MaterialInfoTable c'tor and d'tor, called once by each thread
/// 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.
Entry* probe(const Position& pos) { MaterialInfoTable::MaterialInfoTable(unsigned int numOfEntries) {
Key key = pos.material_key(); size = numOfEntries;
Entry* e = pos.this_thread()->materialTable[key]; entries = new MaterialInfo[size];
funcs = new EndgameFunctions();
if (e->key == key) if (!entries || !funcs)
return e;
std::memset(e, 0, sizeof(Entry));
e->key = key;
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
e->gamePhase = pos.game_phase();
// 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 = pos.this_thread()->endgames.probe<Value>(key)) != nullptr)
return e;
for (Color c = WHITE; c <= BLACK; ++c)
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?
EndgameBase<ScaleFactor>* sf;
if ((sf = pos.this_thread()->endgames.probe<ScaleFactor>(key)) != nullptr)
{ {
e->scalingFunction[sf->strong_side()] = sf; // Only strong color assigned cerr << "Failed to allocate " << numOfEntries * sizeof(MaterialInfo)
return e; << " 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 // OK, we didn't find any special evaluation function for the current
// ones that refer to more than one material distribution. Note that in this // material configuration. Is there a suitable scaling function?
// case we don't return after setting the function. //
for (Color c = WHITE; c <= BLACK; ++c) // 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
if (is_KBPsKs(pos, c)) // are several conflicting applicable scaling functions and we need to
e->scalingFunction[c] = &ScaleKBPsK[c]; // decide which one to use.
SF* sf;
else if (is_KQKRPs(pos, c)) if ((sf = funcs->get<SF>(key)) != NULL)
e->scalingFunction[c] = &ScaleKQKRPs[c]; {
mi->scalingFunction[sf->color()] = sf;
return mi;
} }
Value npm_w = pos.non_pawn_material(WHITE); // Generic scaling functions that refer to more then one material
Value npm_b = pos.non_pawn_material(BLACK); // 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 (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board 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); assert(pos.piece_count(WHITE, PAWN) >= 2);
mi->scalingFunction[WHITE] = &ScaleKPsK;
e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
} }
else if (!pos.count<PAWN>(WHITE)) else if (pos.piece_count(WHITE, PAWN) == 0)
{ {
assert(pos.count<PAWN>(BLACK) >= 2); assert(pos.piece_count(BLACK, PAWN) >= 2);
mi->scalingFunction[BLACK] = &ScaleKKPs;
e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
} }
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 // This is a special case because we set scaling functions
// for both colors instead of only one. // for both colors instead of only one.
e->scalingFunction[WHITE] = &ScaleKPKP[WHITE]; mi->scalingFunction[WHITE] = &ScaleKPKPw;
e->scalingFunction[BLACK] = &ScaleKPKP[BLACK]; mi->scalingFunction[BLACK] = &ScaleKPKPb;
} }
} }
// Zero or just one pawn makes it difficult to win, even with a small material // Compute the space weight
// advantage. This catches some trivial draws like KK, KBK and KNK and gives a if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >=
// drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN). 2*QueenValueMidgame + 4*RookValueMidgame + 2*KnightValueMidgame)
if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg) {
e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW : int minorPieceCount = pos.piece_count(WHITE, KNIGHT)
npm_b <= BishopValueMg ? 4 : 14); + 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) mi->spaceWeight = minorPieceCount * minorPieceCount;
e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW : }
npm_w <= BishopValueMg ? 4 : 14);
if (pos.count<PAWN>(WHITE) == 1 && npm_w - npm_b <= BishopValueMg) // Evaluate the material balance
e->factor[WHITE] = (uint8_t) SCALE_FACTOR_ONEPAWN; 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;
if (pos.count<PAWN>(BLACK) == 1 && npm_b - npm_w <= BishopValueMg) for (c = WHITE, sign = 1; c <= BLACK; c++, sign = -sign)
e->factor[BLACK] = (uint8_t) SCALE_FACTOR_ONEPAWN; {
// 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;
}
}
}
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // Redundancy of major pieces, formula based on Kaufman's paper
// for the bishop pair "extended piece", which allows us to be more flexible // "The Evaluation of Material Imbalances in Chess"
// in defining bishop pair bonuses. // http://mywebpages.comcast.net/danheisman/Articles/evaluation_of_material_imbalance.htm
const int PieceCount[COLOR_NB][PIECE_TYPE_NB] = { if (pieceCount[c][ROOK] >= 1)
{ pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE), matValue -= sign * ((pieceCount[c][ROOK] - 1) * RedundantRookPenalty + pieceCount[c][QUEEN] * RedundantQueenPenalty);
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) } };
e->value = int16_t((imbalance<WHITE>(PieceCount) - imbalance<BLACK>(PieceCount)) / 16); them = opposite_color(c);
return e; 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
/// 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);
}
+140 -36
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,56 +17,161 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define MATERIAL_H_INCLUDED
////
//// Includes
////
#include "endgame.h" #include "endgame.h"
#include "misc.h"
#include "position.h" #include "position.h"
#include "types.h" #include "scale.h"
namespace Material {
/// Material::Entry contains various information about a material configuration. ////
/// It contains a material imbalance evaluation, a function pointer to a special //// Types
/// endgame evaluation function (which in most cases is NULL, meaning that the ////
/// standard evaluation function will be used), and scale factors.
/// 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 /// The scale factors are used to scale the evaluation score up or down.
/// instance, in KRB vs KR endgames, the score is scaled down by a factor of 4, /// For instance, in KRB vs KR endgames, the score is scaled down by a factor
/// which will result in scores of absolute value less than one pawn. /// of 4, which will result in scores of absolute value less than one pawn.
struct Entry { class MaterialInfo {
Score imbalance() const { return make_score(value, value); } friend class MaterialInfoTable;
Phase game_phase() const { return gamePhase; }
bool specialized_eval_exists() const { return evaluationFunction != nullptr; }
Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); }
// scale_factor takes a position and a color as input and returns a scale factor public:
// for the given color. We have to provide the position in addition to the color MaterialInfo() : key(0) { clear(); }
// 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 Score material_value() const;
// for rook pawns and wrong-colored bishops. ScaleFactor scale_factor(const Position& pos, Color c) const;
ScaleFactor scale_factor(const Position& pos, Color c) const { int space_weight() const;
return !scalingFunction[c] Phase game_phase() const;
|| (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) bool specialized_eval_exists() const;
: (*scalingFunction[c])(pos); Value evaluate(const Position& pos) const;
}
private:
inline void clear();
Key key; Key key;
int16_t value; int16_t value;
uint8_t factor[COLOR_NB]; uint8_t factor[2];
EndgameBase<Value>* evaluationFunction; EndgameEvaluationFunctionBase* evaluationFunction;
EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each EndgameScalingFunctionBase* scalingFunction[2];
// side (e.g. KPKP, KBPsKs) int spaceWeight;
Phase gamePhase; 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 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)
+149
View File
@@ -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);
}
+40
View File
@@ -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)
+211 -120
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,170 +17,262 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <fstream>
////
//// 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>
#include <sys/timeb.h>
#endif
#include <cassert>
#include <cstdio>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include "bitcount.h"
#include "misc.h" #include "misc.h"
#include "thread.h"
using namespace std; using namespace std;
namespace { /// Version number. If this is left empty, the current date (in the format
/// YYMMDD) is used as a version number.
/// Version number. If Version is left empty, then compile date in the format static const string EngineVersion = "1.6.3";
/// DD-MM-YY and show in engine_info. static const string AppName = "Stockfish";
const string Version = "7"; static const string AppTag = "";
/// 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 ////
//// Variables
////
Tie(streambuf* b, streambuf* l) : buf(b), logBuf(l) {} bool Chess960;
int sync() { return logBuf->pubsync(), buf->pubsync(); } uint64_t dbg_cnt0 = 0;
int overflow(int c) { return log(buf->sputc((char)c), "<< "); } uint64_t dbg_cnt1 = 0;
int underflow() { return buf->sgetc(); }
int uflow() { return log(buf->sbumpc(), ">> "); }
streambuf *buf, *logBuf; bool dbg_show_mean = false;
bool dbg_show_hit_rate = false;
int log(int c, const char* prefix) {
static int last = '\n'; // Single log file ////
//// Functions
////
if (last == '\n') void dbg_hit_on(bool b) {
logBuf->sputn(prefix, 3);
return last = logBuf->sputc((char)c); assert(!dbg_show_mean);
} dbg_show_hit_rate = true;
}; dbg_cnt0++;
if (b)
dbg_cnt1++;
}
class Logger { void dbg_hit_on_c(bool c, bool b) {
Logger() : in(cin.rdbuf(), file.rdbuf()), out(cout.rdbuf(), file.rdbuf()) {} if (c)
~Logger() { start(false); } dbg_hit_on(b);
}
ofstream file; void dbg_before() {
Tie in, out;
public: assert(!dbg_show_mean);
static void start(bool b) { dbg_show_hit_rate = true;
dbg_cnt0++;
}
static Logger l; void dbg_after() {
if (b && !l.file.is_open()) assert(!dbg_show_mean);
{ dbg_show_hit_rate = true;
l.file.open("io_log.txt", ifstream::out); dbg_cnt1++;
cin.rdbuf(&l.in); }
cout.rdbuf(&l.out);
}
else if (!b && l.file.is_open())
{
cout.rdbuf(l.out.buf);
cin.rdbuf(l.in.buf);
l.file.close();
}
}
};
} // namespace void dbg_mean_of(int v) {
/// engine_info() returns the full name of the current Stockfish version. This assert(!dbg_show_hit_rate);
/// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when dbg_show_mean = true;
/// the program was compiled) or "Stockfish <Version>", depending on whether dbg_cnt0++;
/// Version is empty. dbg_cnt1 += v;
}
const string engine_info(bool to_uci) { void dbg_print_hit_rate() {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); cout << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
string month, day, year; << " hit rate (%) " << (dbg_cnt1*100)/(dbg_cnt0 ? dbg_cnt0 : 1) << endl;
stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008" }
ss << "Stockfish " << Version << setfill('0'); void dbg_print_mean() {
if (Version.empty()) cout << "Total " << dbg_cnt0 << " Mean "
{ << (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl;
date >> month >> day >> year; }
ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
}
ss << (Is64Bit ? " 64" : "") void dbg_print_hit_rate(ofstream& logFile) {
<< (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : ""))
<< (to_uci ? "\nid author ": " by ")
<< "T. Romstad, M. Costalba, J. Kiiski, G. Linscott";
return ss.str(); 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();
} }
/// Debug functions used mainly to collect run-time statistics /// get_system_time() returns the current system time, measured in
static int64_t hits[2], means[2]; /// milliseconds.
void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; } int get_system_time() {
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 defined(_MSC_VER)
struct _timeb t;
if (hits[0]) _ftime(&t);
cerr << "Total " << hits[0] << " Hits " << hits[1] return int(t.time*1000 + t.millitm);
<< " hit rate (%) " << 100 * hits[1] / hits[0] << endl; #else
struct timeval t;
if (means[0]) gettimeofday(&t, NULL);
cerr << "Total " << means[0] << " Mean " return t.tv_sec*1000 + t.tv_usec/1000;
<< (double)means[1] / means[0] << endl; #endif
} }
/// Used to serialize access to std::cout to avoid multiple threads writing at /// cpu_count() tries to detect the number of CPU cores.
/// the same time.
std::ostream& operator<<(std::ostream& os, SyncCout sc) { #if !defined(_MSC_VER)
static Mutex m; # if defined(_SC_NPROCESSORS_ONLN)
int cpu_count() {
if (sc == IO_LOCK) return Min(sysconf(_SC_NPROCESSORS_ONLN), 8);
m.lock();
if (sc == IO_UNLOCK)
m.unlock();
return os;
} }
# else
int cpu_count() {
/// Trampoline helper to avoid moving Logger to misc.h return 1;
void start_logger(bool b) { Logger::start(b); } }
# endif
/// 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*) {}
#else #else
void prefetch(void* addr) { int cpu_count() {
SYSTEM_INFO s;
# if defined(__INTEL_COMPILER) GetSystemInfo(&s);
// This hack prevents prefetches from being optimized away by return Min(s.dwNumberOfProcessors, 8);
// Intel compiler. Both MSVC and gcc seem not be affected by this.
__asm__ ("");
# endif
# if defined(__INTEL_COMPILER) || defined(_MSC_VER)
_mm_prefetch((char*)addr, _MM_HINT_T0);
# else
__builtin_prefetch(addr);
# endif
} }
#endif #endif
/*
From Beowulf, from Olithink
*/
#ifndef _WIN32
/* Non-windows version */
int Bioskey()
{
fd_set readfds;
struct timeval timeout;
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);
return (FD_ISSET(fileno(stdin), &readfds));
}
#else
/* 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;
// 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;
// Search for at least one keyboard event
for (DWORD i = 0; i < recCnt; i++)
if (rec[i].EventType == KEY_EVENT)
return 1;
return 0;
}
}
#endif
+44 -64
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,83 +17,64 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define MISC_H_INCLUDED
#include <cassert>
#include <chrono>
#include <ostream>
#include <string>
#include <vector>
////
//// Includes
////
#include <fstream>
#include <string>
#include "application.h"
#include "types.h" #include "types.h"
const std::string engine_info(bool to_uci = false); ////
void prefetch(void* addr); //// Macros
void start_logger(bool b); ////
void dbg_hit_on(bool b); #define Min(x, y) (((x) < (y))? (x) : (y))
void dbg_hit_on(bool c, bool b); #define Max(x, y) (((x) < (y))? (y) : (x))
void dbg_mean_of(int v);
void dbg_print();
typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds
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);
};
enum SyncCout { IO_LOCK, IO_UNLOCK }; ////
std::ostream& operator<<(std::ostream&, SyncCout); //// Variables
////
#define sync_cout std::cout << IO_LOCK extern bool Chess960;
#define sync_endl std::endl << IO_UNLOCK
/// xorshift64star Pseudo-Random Number Generator ////
/// This class is based on original code written and dedicated //// Prototypes
/// 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 { extern const std::string engine_name();
extern int get_system_time();
extern int cpu_count();
extern int Bioskey();
uint64_t s;
uint64_t rand64() { ////
//// Debug
////
s ^= s >> 12, s ^= s << 25, s ^= s >> 27; extern bool dbg_show_mean;
return s * 2685821657736338717LL; extern bool dbg_show_hit_rate;
}
public: extern uint64_t dbg_cnt0;
PRNG(uint64_t seed) : s(seed) { assert(seed); } extern uint64_t dbg_cnt1;
template<typename T> T rand() { return T(rand64()); } 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);
/// Special generator used to fast init magic numbers. #endif // !defined(MISC_H_INCLUDED)
/// Output values only have 1/8th of their bits set on average.
template<typename T> T sparse_rand()
{ return T(rand64() & rand64() & rand64()); }
};
#endif // #ifndef MISC_H_INCLUDED
+152
View File
@@ -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
View File
@@ -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)
+602 -329
View File
File diff suppressed because it is too large Load Diff
+18 -44
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,53 +17,28 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define MOVEGEN_H_INCLUDED
#include "types.h" ////
//// Includes
////
class Position; #include "position.h"
enum GenType {
CAPTURES,
QUIETS,
QUIET_CHECKS,
EVASIONS,
NON_EVASIONS,
LEGAL
};
struct ExtMove { ////
Move move; //// Prototypes
Value value; ////
operator Move() const { return move; } extern MoveStack* generate_captures(const Position& pos, MoveStack* mlist);
void operator=(Move m) { move = m; } 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);
inline bool operator<(const ExtMove& f, const ExtMove& s) {
return f.value < s.value;
}
template<GenType> #endif // !defined(MOVEGEN_H_INCLUDED)
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 {
for (const auto& m : *this) if (m == move) return true;
return false;
}
private:
ExtMove moveList[MAX_MOVES], *last;
};
#endif // #ifndef MOVEGEN_H_INCLUDED
+292 -250
View File
@@ -1,14 +1,14 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
Stockfish is distributed in the hope that it will be useful, Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@@ -18,301 +18,343 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
////
//// Includes
////
#include <cassert> #include <cassert>
#include "history.h"
#include "movegen.h"
#include "movepick.h" #include "movepick.h"
#include "thread.h" #include "search.h"
#include "value.h"
////
//// Local definitions
////
namespace { namespace {
enum Stages { enum MovegenPhase {
MAIN_SEARCH, GOOD_CAPTURES, KILLERS, GOOD_QUIETS, BAD_QUIETS, BAD_CAPTURES, PH_TT_MOVES, // Transposition table move and mate killer
EVASION, ALL_EVASIONS, PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= 0
QSEARCH_WITH_CHECKS, QCAPTURES_1, CHECKS, PH_KILLERS, // Killer moves from the current ply
QSEARCH_WITHOUT_CHECKS, QCAPTURES_2, PH_NONCAPTURES, // Non-captures and underpromotions
PROBCUT, PROBCUT_CAPTURES, PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < 0
RECAPTURE, RECAPTURES, PH_EVASIONS, // Check evasions
STOP PH_QCAPTURES, // Captures in quiescence search
PH_QCHECKS, // Non-capture checks in quiescence search
PH_STOP
}; };
// Our insertion sort, which is guaranteed to be stable, as it should be CACHE_LINE_ALIGNMENT
void insertion_sort(ExtMove* begin, ExtMove* end) 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};
ExtMove tmp, *p, *q; const uint8_t QsearchWithChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP};
const uint8_t QsearchWithoutChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP};
for (p = begin + 1; p < end; ++p)
{
tmp = *p;
for (q = p; q != begin && *(q-1) < tmp; --q)
*q = *(q-1);
*q = tmp;
}
}
// pick_best() finds the best move in the range (begin, end) and moves it to
// the front. It's faster than sorting all the moves in advance when there
// are few moves e.g. the possible captures.
Move pick_best(ExtMove* begin, ExtMove* end)
{
std::swap(*begin, *std::max_element(begin, end));
return *begin;
}
} // namespace
/// Constructors of the MovePicker class. As arguments we pass 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.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
const CounterMovesStats& cmh, Move cm, Search::Stack* s)
: pos(p), history(h), counterMovesHistory(&cmh), ss(s), countermove(cm), depth(d) {
assert(d > DEPTH_ZERO);
stage = pos.checkers() ? EVASION : MAIN_SEARCH;
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
endMoves += (ttMove != MOVE_NONE);
} }
////
//// 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 about how important good
/// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
const HistoryStats& h, Square s) const History& h, SearchStack* ss) : pos(p), H(h) {
: pos(p), history(h), counterMovesHistory(nullptr) { int searchTT = ttm;
ttMoves[0].move = ttm;
finished = false;
lastBadCapture = badCaptures;
assert(d <= DEPTH_ZERO); pinned = p.pinned_pieces(pos.side_to_move());
if (pos.checkers()) if (ss && !p.is_check())
stage = EVASION;
else if (d > DEPTH_QS_NO_CHECKS)
stage = QSEARCH_WITH_CHECKS;
else if (d > DEPTH_QS_RECAPTURES)
stage = QSEARCH_WITHOUT_CHECKS;
else
{ {
stage = RECAPTURE; ttMoves[1].move = (ss->mateKiller == ttm)? MOVE_NONE : ss->mateKiller;
recaptureSquare = s; searchTT |= ttMoves[1].move;
ttm = MOVE_NONE; killers[0].move = ss->killers[0];
} killers[1].move = ss->killers[1];
} else
ttMoves[1].move = killers[0].move = killers[1].move = MOVE_NONE;
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; if (p.is_check())
endMoves += (ttMove != MOVE_NONE); phasePtr = EvasionsPhaseTable;
} else if (d > Depth(0))
phasePtr = MainSearchPhaseTable;
else if (d == Depth(0))
phasePtr = QsearchWithChecksPhaseTable;
else
phasePtr = QsearchWithoutChecksPhaseTable;
MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, Value th) phasePtr += !searchTT - 1;
: pos(p), history(h), counterMovesHistory(nullptr), threshold(th) { go_next_phase();
assert(!pos.checkers());
stage = PROBCUT;
// In ProbCut we generate captures with SEE higher than the given threshold
ttMove = ttm
&& pos.pseudo_legal(ttm)
&& pos.capture(ttm)
&& pos.see(ttm) > threshold ? ttm : MOVE_NONE;
endMoves += (ttMove != MOVE_NONE);
} }
/// score() assigns a numerical value to each move in a move list. The moves with /// MovePicker::go_next_phase() generates, scores and sorts the next bunch
/// highest values will be picked first. /// of moves when there are no more moves to try for the current phase.
template<>
void MovePicker::score<CAPTURES>() {
// Winning and equal captures in the main search are ordered by MVV, preferring
// captures near our home rank. Surprisingly, this appears to perform slightly
// better than SEE based move ordering: 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 the
// badCaptures[] array, but instead of doing it now we delay until the move
// has been picked up, saving some SEE calls in case we get a cutoff.
for (auto& m : *this)
m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
- Value(200 * relative_rank(pos.side_to_move(), to_sq(m)));
}
template<> void MovePicker::go_next_phase() {
void MovePicker::score<QUIETS>() {
for (auto& m : *this) curMove = moves;
m.value = history[pos.moved_piece(m)][to_sq(m)] phase = *(++phasePtr);
+ (*counterMovesHistory)[pos.moved_piece(m)][to_sq(m)]; switch (phase) {
}
template<> case PH_TT_MOVES:
void MovePicker::score<EVASIONS>() { curMove = ttMoves;
// Try winning and equal captures captures ordered by MVV/LVA, then non-captures lastMove = curMove + 2;
// ordered by history value, then bad-captures and quiet moves with a negative return;
// SEE ordered by SEE value.
Value see;
for (auto& m : *this) case PH_GOOD_CAPTURES:
if ((see = pos.see_sign(m)) < VALUE_ZERO) lastMove = generate_captures(pos, moves);
m.value = see - HistoryStats::Max; // At the bottom score_captures();
return;
else if (pos.capture(m)) case PH_KILLERS:
m.value = PieceValue[MG][pos.piece_on(to_sq(m))] curMove = killers;
- Value(type_of(pos.moved_piece(m))) + HistoryStats::Max; lastMove = curMove + 2;
else return;
m.value = history[pos.moved_piece(m)][to_sq(m)];
}
case PH_NONCAPTURES:
lastMove = generate_noncaptures(pos, moves);
score_noncaptures();
sort_moves(moves, lastMove);
return;
/// generate_next_stage() generates, scores and sorts the next bunch of moves, case PH_BAD_CAPTURES:
/// when there are no more moves to try for the current stage. // Bad captures SEE value is already calculated so just sort them
// to get SEE move ordering.
curMove = badCaptures;
lastMove = lastBadCapture;
return;
void MovePicker::generate_next_stage() { case PH_EVASIONS:
assert(pos.is_check());
lastMove = generate_evasions(pos, moves);
score_evasions();
return;
assert(stage != STOP); case PH_QCAPTURES:
lastMove = generate_captures(pos, moves);
score_captures();
return;
cur = moves; case PH_QCHECKS:
// Perhaps we should order moves move here? FIXME
lastMove = generate_non_capture_checks(pos, moves);
return;
switch (++stage) { case PH_STOP:
lastMove = curMove + 1; // hack to be friendly for get_next_move()
case GOOD_CAPTURES: case QCAPTURES_1: case QCAPTURES_2: return;
case PROBCUT_CAPTURES: case RECAPTURES:
endMoves = generate<CAPTURES>(pos, moves);
score<CAPTURES>();
break;
case KILLERS:
killers[0] = ss->killers[0];
killers[1] = ss->killers[1];
killers[2] = countermove;
cur = killers;
endMoves = cur + 2 + (countermove != killers[0] && countermove != killers[1]);
break;
case GOOD_QUIETS:
endQuiets = endMoves = generate<QUIETS>(pos, moves);
score<QUIETS>();
endMoves = std::partition(cur, endMoves, [](const ExtMove& m) { return m.value > VALUE_ZERO; });
insertion_sort(cur, endMoves);
break;
case BAD_QUIETS:
cur = endMoves;
endMoves = endQuiets;
if (depth >= 3 * ONE_PLY)
insertion_sort(cur, endMoves);
break;
case BAD_CAPTURES:
// Just pick them in reverse order to get correct ordering
cur = moves + MAX_MOVES - 1;
endMoves = endBadCaptures;
break;
case ALL_EVASIONS:
endMoves = generate<EVASIONS>(pos, moves);
if (endMoves - moves > 1)
score<EVASIONS>();
break;
case CHECKS:
endMoves = generate<QUIET_CHECKS>(pos, moves);
break;
case EVASION: case QSEARCH_WITH_CHECKS: case QSEARCH_WITHOUT_CHECKS:
case PROBCUT: case RECAPTURE: case STOP:
stage = STOP;
break;
default: default:
assert(false); assert(false);
return;
} }
} }
/// next_move() is the most important method of the MovePicker class. It returns /// MovePicker::score_captures(), MovePicker::score_noncaptures() and
/// a new pseudo legal move every time it is called, until there are no more moves /// MovePicker::score_evasions() assign a numerical move ordering score
/// left. It picks the move with the biggest value from a list of generated moves /// to each move in a move list. The moves with highest scores will be
/// taking care not to return the ttMove if it has already been searched. /// picked first by get_next_move().
Move MovePicker::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++)
{
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));
}
}
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;
for (MoveStack* cur = moves; cur != lastMove; cur++)
{
m = cur->move;
from = move_from(m);
to = move_to(m);
piece = pos.piece_on(from);
hs = H.move_ordering_score(piece, to);
// Ensure history is always preferred to pst
if (hs > 0)
hs += 1000;
// pst based scoring
cur->score = hs + mg_value(pos.pst_delta(piece, from, to));
}
}
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;
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));
}
}
/// 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.
Move MovePicker::get_next_move() {
Move move; Move move;
while (true) while (true)
{ {
while (cur == endMoves && stage != STOP) while (curMove != lastMove)
generate_next_stage(); {
switch (phase) {
switch (stage) { case PH_TT_MOVES:
move = (curMove++)->move;
case MAIN_SEARCH: case EVASION: case QSEARCH_WITH_CHECKS: if ( move != MOVE_NONE
case QSEARCH_WITHOUT_CHECKS: case PROBCUT: && move_is_legal(pos, move, pinned))
++cur;
return ttMove;
case GOOD_CAPTURES:
move = pick_best(cur++, endMoves);
if (move != ttMove)
{
if (pos.see_sign(move) >= VALUE_ZERO)
return move; return move;
break;
// Losing capture, move it to the tail of the array case PH_GOOD_CAPTURES:
*endBadCaptures-- = move; 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;
} }
break;
case KILLERS:
move = *cur++;
if ( move != MOVE_NONE
&& move != ttMove
&& pos.pseudo_legal(move)
&& !pos.capture(move))
return move;
break;
case GOOD_QUIETS: case BAD_QUIETS:
move = *cur++;
if ( move != ttMove
&& move != killers[0]
&& move != killers[1]
&& move != killers[2])
return move;
break;
case BAD_CAPTURES:
return *cur--;
case ALL_EVASIONS: case QCAPTURES_1: case QCAPTURES_2:
move = pick_best(cur++, endMoves);
if (move != ttMove)
return move;
break;
case PROBCUT_CAPTURES:
move = pick_best(cur++, endMoves);
if (move != ttMove && pos.see(move) > threshold)
return move;
break;
case RECAPTURES:
move = pick_best(cur++, endMoves);
if (to_sq(move) == recaptureSquare)
return move;
break;
case CHECKS:
move = *cur++;
if (move != ttMove)
return move;
break;
case STOP:
return MOVE_NONE;
default:
assert(false);
} }
go_next_phase();
} }
} }
/// 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;
}
+55 -78
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,96 +17,74 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define MOVEPICK_H_INCLUDED
#include <algorithm> // For std::max ////
#include <cstring> // For std::memset //// Includes
////
#include "movegen.h" #include "depth.h"
#include "history.h"
#include "lock.h"
#include "position.h" #include "position.h"
#include "search.h"
#include "types.h"
/// The Stats struct stores moves statistics. According to the template parameter ////
/// the class can store History and Countermoves. History records how often //// Types
/// different moves have been successful or unsuccessful during the current search ////
/// and is used for reduction and move ordering decisions.
/// Countermoves store the move that refute a previous one. Entries are stored
/// using only the moving piece and destination square, hence two moves with
/// different origin but same destination and piece will be considered identical.
template<typename T, bool CM = false>
struct Stats {
static const Value Max = Value(1 << 28); struct SearchStack;
const T* operator[](Piece pc) const { return table[pc]; } /// MovePicker is a class which is used to pick one legal move at a time from
T* operator[](Piece pc) { return table[pc]; } /// the current position. It is initialized with a Position object and a few
void clear() { std::memset(table, 0, sizeof(table)); } /// 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
void update(Piece pc, Square to, Move m) { /// 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
if (m != table[pc][to]) /// attempts to return the moves which are most likely to be strongest first.
table[pc][to] = m;
}
void update(Piece pc, Square to, Value v) {
if (abs(int(v)) >= 324)
return;
table[pc][to] -= table[pc][to] * abs(int(v)) / (CM ? 512 : 324);
table[pc][to] += int(v) * (CM ? 64 : 32);
}
private:
T table[PIECE_NB][SQUARE_NB];
};
typedef Stats<Move> MovesStats;
typedef Stats<Value, false> HistoryStats;
typedef Stats<Value, true> CounterMovesStats;
typedef Stats<CounterMovesStats> CounterMovesHistoryStats;
/// 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.
class MovePicker { class MovePicker {
MovePicker& operator=(const MovePicker&); // silence a warning under MSVC
public: public:
MovePicker(const MovePicker&) = delete; MovePicker(const Position& p, Move ttm, Depth d, const History& h, SearchStack* ss = NULL);
MovePicker& operator=(const MovePicker&) = delete; Move get_next_move();
Move get_next_move(Lock& lock);
MovePicker(const Position&, Move, Depth, const HistoryStats&, Square); int number_of_evasions() const;
MovePicker(const Position&, Move, const HistoryStats&, Value);
MovePicker(const Position&, Move, Depth, const HistoryStats&, const CounterMovesStats&, Move, Search::Stack*);
Move next_move();
private: private:
template<GenType> void score(); void score_captures();
void generate_next_stage(); void score_noncaptures();
ExtMove* begin() { return moves; } void score_evasions();
ExtMove* end() { return endMoves; } void go_next_phase();
const Position& pos; const Position& pos;
const HistoryStats& history; const History& H;
const CounterMovesStats* counterMovesHistory; MoveStack ttMoves[2], killers[2];
Search::Stack* ss; bool finished;
Move countermove; int phase;
Depth depth; const uint8_t* phasePtr;
Move ttMove; MoveStack *curMove, *lastMove, *lastBadCapture;
ExtMove killers[3]; Bitboard pinned;
Square recaptureSquare; MoveStack moves[256], badCaptures[64];
Value threshold;
int stage;
ExtMove *endQuiets, *endBadCaptures = moves + MAX_MOVES - 1;
ExtMove moves[MAX_MOVES], *cur = moves, *endMoves = moves;
}; };
#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)
+302 -253
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,302 +17,352 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 "bitcount.h"
#include "pawns.h" #include "pawns.h"
#include "position.h" #include "position.h"
#include "thread.h"
////
//// Local definitions
////
namespace { namespace {
#define V Value /// Constants and variables
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Isolated pawn penalty by opposed flag and file
const Score Isolated[2][FILE_NB] = {
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52),
S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35), S(25, 30) } };
// Backward pawn penalty by opposed flag
const Score Backward[2] = { S(67, 42), S(49, 24) };
// Unsupported pawn penalty, for pawns which are neither isolated or backward
const Score Unsupported = S(20, 10);
// Connected pawn bonus by opposed, phalanx, twice supported and rank
Score Connected[2][2][2][RANK_NB];
// Doubled pawn penalty by file // Doubled pawn penalty by file
const Score Doubled[FILE_NB] = { const Score DoubledPawnPenalty[8] = {
S(13, 43), S(20, 48), S(23, 48), S(23, 48), S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }; S(23, 48), S(23, 48), S(20, 48), S(13, 43)
};
// Lever bonus by rank // Isolated pawn penalty by file
const Score Lever[RANK_NB] = { const Score IsolatedPawnPenalty[8] = {
S( 0, 0), S( 0, 0), S(0, 0), S(0, 0), S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(20, 20), S(40, 40), S(0, 0), S(0, 0) }; S(40, 35), S(40, 35), S(36, 35), S(25, 30)
};
// Center bind bonus, when two pawns controls the same central square // Backward pawn penalty by file
const Score CenterBind = S(16, 0); 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)
};
// Weakness of our pawn shelter in front of the king by [distance from edge][rank] // Pawn chain membership bonus by file
const Value ShelterWeakness[][RANK_NB] = { const Score ChainBonus[8] = {
{ V( 97), V(21), V(26), V(51), V(87), V( 89), V( 99) }, S(11,-1), S(13,-1), S(13,-1), S(14,-1),
{ V(120), V( 0), V(28), V(76), V(88), V(103), V(104) }, S(14,-1), S(13,-1), S(13,-1), S(11,-1)
{ V(101), V( 7), V(54), V(78), V(77), V( 92), V(101) }, };
{ V( 80), V(11), V(44), V(68), V(87), V( 90), V(119) } };
// Danger of enemy pawns moving toward our king by [type][distance from edge][rank] // Candidate passed pawn bonus by rank
const Value StormDanger[][4][RANK_NB] = { const Score CandidateBonus[8] = {
{ { V( 0), V( 67), V( 134), V(38), V(32) }, S( 0, 0), S( 6, 13), S(6,13), S(14,29),
{ V( 0), V( 57), V( 139), V(37), V(22) }, S(34,68), S(83,166), S(0, 0), S( 0, 0)
{ V( 0), V( 43), V( 115), V(43), V(27) }, };
{ V( 0), V( 68), V( 124), V(57), V(32) } },
{ { V(20), V( 43), V( 100), V(56), V(20) },
{ V(23), V( 20), V( 98), V(40), V(15) },
{ V(23), V( 39), V( 103), V(36), V(18) },
{ V(28), V( 19), V( 108), V(42), V(26) } },
{ { V( 0), V( 0), V( 75), V(14), V( 2) },
{ V( 0), V( 0), V( 150), V(30), V( 4) },
{ V( 0), V( 0), V( 160), V(22), V( 5) },
{ V( 0), V( 0), V( 166), V(24), V(13) } },
{ { V( 0), V(-283), V(-281), V(57), V(31) },
{ V( 0), V( 58), V( 141), V(39), V(18) },
{ V( 0), V( 65), V( 142), V(48), V(32) },
{ V( 0), V( 60), V( 126), V(51), V(19) } } };
// Max bonus for king safety. Corresponds to start position with all the pawns // Pawn storm tables for positions with opposite castling
// in front of the king and no enemy pawn on the horizon. const int QStormTable[64] = {
const Value MaxSafetyBonus = V(258); 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
};
#undef S const int KStormTable[64] = {
#undef V 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
};
template<Color Us> // Pawn storm open file bonuses by file
Score evaluate(const Position& pos, Pawns::Entry* e) { 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 };
const Color Them = (Us == WHITE ? BLACK : WHITE); // Pawn storm lever bonuses by file
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S); const int StormLeverBonus[8] = { -8, -8, -13, 0, 0, -13, -8, -8 };
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
const Bitboard CenterBindMask = }
Us == WHITE ? (FileDBB | FileEBB) & (Rank5BB | Rank6BB | Rank7BB)
: (FileDBB | FileEBB) & (Rank4BB | Rank3BB | Rank2BB);
Bitboard b, neighbours, doubled, supported, phalanx;
Square s;
bool passed, isolated, opposed, backward, lever, connected;
Score score = SCORE_ZERO;
const Square* pl = pos.squares<PAWN>(Us);
const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)];
Bitboard ourPawns = pos.pieces(Us , PAWN); ////
Bitboard theirPawns = pos.pieces(Them, PAWN); //// Functions
////
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0; /// Constructor
e->kingSquares[Us] = SQ_NONE;
e->semiopenFiles[Us] = 0xFF;
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns);
e->pawnsOnSquares[Us][BLACK] = popcount<Max15>(ourPawns & DarkSquares);
e->pawnsOnSquares[Us][WHITE] = pos.count<PAWN>(Us) - e->pawnsOnSquares[Us][BLACK];
// Loop through all pawns of the current color and score each pawn PawnInfoTable::PawnInfoTable(unsigned numOfEntries) {
while ((s = *pl++) != SQ_NONE)
{
assert(pos.piece_on(s) == make_piece(Us, PAWN));
File f = file_of(s); size = numOfEntries;
entries = new PawnInfo[size];
e->semiopenFiles[Us] &= ~(1 << f); if (!entries)
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
// Flag the pawn
neighbours = ourPawns & adjacent_files_bb(f);
doubled = ourPawns & forward_bb(Us, s);
opposed = theirPawns & forward_bb(Us, s);
passed = !(theirPawns & passed_pawn_mask(Us, s));
lever = theirPawns & pawnAttacksBB[s];
phalanx = neighbours & rank_bb(s);
supported = neighbours & rank_bb(s - Up);
connected = supported | phalanx;
isolated = !neighbours;
// Test for backward pawn.
// If the pawn is passed, isolated, lever or connected it cannot be
// backward. If there are friendly pawns behind on adjacent files
// or if it is sufficiently advanced, it cannot be backward either.
if ( (passed | isolated | lever | connected)
|| (ourPawns & pawn_attack_span(Them, s))
|| (relative_rank(Us, s) >= RANK_5))
backward = false;
else
{
// We now know there are no friendly pawns beside or behind this
// pawn on adjacent files. We now check whether the pawn is
// backward by looking in the forward direction on the adjacent
// files, and picking the closest pawn there.
b = pawn_attack_span(Us, s) & (ourPawns | theirPawns);
b = pawn_attack_span(Us, s) & rank_bb(backmost_sq(Us, b));
// If we have an enemy pawn in the same or next rank, the pawn is
// backward because it cannot advance without being captured.
backward = (b | shift_bb<Up>(b)) & theirPawns;
}
assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns));
// Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate them. Only the frontmost passed
// pawn on each file is considered a true passed pawn.
if (passed && !doubled)
e->passedPawns[Us] |= s;
// Score this pawn
if (isolated)
score -= Isolated[opposed][f];
else if (backward)
score -= Backward[opposed];
else if (!supported)
score -= Unsupported;
if (connected)
score += Connected[opposed][!!phalanx][more_than_one(supported)][relative_rank(Us, s)];
if (doubled)
score -= Doubled[f] / distance<Rank>(s, frontmost_sq(Us, doubled));
if (lever)
score += Lever[relative_rank(Us, s)];
}
b = e->semiopenFiles[Us] ^ 0xFF;
e->pawnSpan[Us] = b ? int(msb(b) - lsb(b)) : 0;
b = shift_bb<Right>(ourPawns) & shift_bb<Left>(ourPawns) & CenterBindMask;
score += CenterBind * popcount<Max15>(b);
return score;
}
} // namespace
namespace Pawns {
/// Pawns::init() initializes some tables needed by evaluation. Instead of using
/// hard-coded tables, when makes sense, we prefer to calculate them with a formula
/// to reduce independent parameters and to allow easier tuning and better insight.
void init()
{
static const int Seed[RANK_NB] = { 0, 6, 15, 10, 57, 75, 135, 258 };
for (int opposed = 0; opposed <= 1; ++opposed)
for (int phalanx = 0; phalanx <= 1; ++phalanx)
for (int apex = 0; apex <= 1; ++apex)
for (Rank r = RANK_2; r < RANK_8; ++r)
{ {
int v = (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed; std::cerr << "Failed to allocate " << (numOfEntries * sizeof(PawnInfo))
v += (apex ? v / 2 : 0); << " bytes for pawn hash table." << std::endl;
Connected[opposed][phalanx][apex][r] = make_score(3 * v / 2, v); Application::exit_with_failure();
} }
} }
/// Pawns::probe() looks up the current position's pawns configuration in /// Destructor
/// 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) { PawnInfoTable::~PawnInfoTable() {
delete [] entries;
Key key = pos.pawn_key();
Entry* e = pos.this_thread()->pawnsTable[key];
if (e->key == key)
return e;
e->key = key;
e->score = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e);
e->asymmetry = popcount<Max15>(e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]);
return e;
} }
/// Entry::shelter_storm() calculates shelter and storm penalties for the file /// PawnInfo::clear() resets to zero the PawnInfo entry. Note that
/// the king is on, as well as the two adjacent files. /// 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> template<Color Us>
Value Entry::shelter_storm(const Position& pos, Square ksq) { 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);
const Color Them = (Us == WHITE ? BLACK : WHITE); // 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);
}
enum { NoFriendlyPawn, Unblocked, BlockedByPawn, BlockedByKing }; // Loop through all pawns of the current color and score each pawn
while ((s = *ptr++) != SQ_NONE)
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq));
Bitboard ourPawns = b & pos.pieces(Us);
Bitboard theirPawns = b & pos.pieces(Them);
Value safety = MaxSafetyBonus;
File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
for (File f = center - File(1); f <= center + File(1); ++f)
{ {
b = ourPawns & file_bb(f); f = square_file(s);
Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1; r = square_rank(s);
b = theirPawns & file_bb(f); assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN));
Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
safety -= ShelterWeakness[std::min(f, FILE_H - f)][rkUs] // Passed, isolated or doubled pawn?
+ StormDanger passed = Position::pawn_is_passed(theirPawns, Us, s);
[f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing : isolated = Position::pawn_is_isolated(ourPawns, s);
rkUs == RANK_1 ? NoFriendlyPawn : doubled = Position::pawn_is_doubled(ourPawns, Us, s);
rkThem == rkUs + 1 ? BlockedByPawn : Unblocked]
[std::min(f, FILE_H - f)][rkThem]; // 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.
// 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;
// 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;
// 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 safety; return value;
} }
/// Entry::do_king_safety() calculates a bonus for king safety. It is called only /// PawnInfo::updateShelter calculates and caches king shelter. It is called
/// when king square changes, which is about 20% of total king_safety() calls. /// only when king square changes, about 20% of total get_king_shelter() calls.
int PawnInfo::updateShelter(const Position& pos, Color c, Square ksq) {
template<Color Us> unsigned shelter = 0;
Score Entry::do_king_safety(const Position& pos, Square ksq) { Bitboard pawns = pos.pieces(PAWN, c) & this_and_neighboring_files_bb(ksq);
unsigned r = ksq & (7 << 3);
kingSquares[Us] = ksq; for (int i = 1, k = (c ? -8 : 8); i < 4; i++)
castlingRights[Us] = pos.can_castle(Us); {
int minKingPawnDistance = 0; r += k;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (128 >> i);
Bitboard pawns = pos.pieces(Us, PAWN); }
if (pawns) kingSquares[c] = ksq;
while (!(DistanceRingBB[ksq][minKingPawnDistance++] & pawns)) {} kingShelters[c] = shelter;
return shelter;
if (relative_rank(Us, ksq) > RANK_4)
return make_score(0, -16 * minKingPawnDistance);
Value bonus = shelter_storm<Us>(pos, ksq);
// If we can castle use the bonus after the castling if it is bigger
if (pos.can_castle(MakeCastling<Us, KING_SIDE>::right))
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1)));
if (pos.can_castle(MakeCastling<Us, QUEEN_SIDE>::right))
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1)));
return make_score(bonus, -16 * minKingPawnDistance);
} }
// Explicit template instantiation
template Score Entry::do_king_safety<WHITE>(const Position& pos, Square ksq);
template Score Entry::do_king_safety<BLACK>(const Position& pos, Square ksq);
} // namespace Pawns
+100 -53
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,71 +17,119 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define PAWNS_H_INCLUDED
#include "misc.h" ////
#include "position.h" //// Includes
#include "types.h" ////
namespace 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 //// Types
/// pointer to an Entry object. ////
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 pawns_score() const { return score; } class PawnInfo {
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 pawn_span(Color c) const { return pawnSpan[c]; }
int pawn_asymmetry() const { return asymmetry; }
int semiopen_file(Color c, File f) const { friend class PawnInfoTable;
return semiopenFiles[c] & (1 << f);
}
int semiopen_side(Color c, File f, bool leftSide) const { public:
return semiopenFiles[c] & (leftSide ? (1 << f) - 1 : ~((1 << (f + 1)) - 1)); PawnInfo() { clear(); }
}
int pawns_on_same_color_squares(Color c, Square s) const { Score pawns_value() const;
return pawnsOnSquares[c][!!(DarkSquares & s)]; 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);
template<Color Us> private:
Score king_safety(const Position& pos, Square ksq) { void clear();
return kingSquares[Us] == ksq && castlingRights[Us] == pos.can_castle(Us) int updateShelter(const Position& pos, Color c, Square ksq);
? kingSafety[Us] : (kingSafety[Us] = do_king_safety<Us>(pos, ksq));
}
template<Color Us>
Score do_king_safety(const Position& pos, Square ksq);
template<Color Us>
Value shelter_storm(const Position& pos, Square ksq);
Key key; Key key;
Score score; Bitboard passedPawns;
Bitboard passedPawns[COLOR_NB]; Bitboard pawnAttacks[2];
Bitboard pawnAttacks[COLOR_NB]; Score value;
Bitboard pawnAttacksSpan[COLOR_NB]; int16_t ksStormValue[2], qsStormValue[2];
Square kingSquares[COLOR_NB]; uint8_t halfOpenFiles[2];
Score kingSafety[COLOR_NB]; Square kingSquares[2];
int castlingRights[COLOR_NB]; uint8_t kingShelters[2];
int semiopenFiles[COLOR_NB];
int pawnSpan[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
int asymmetry;
}; };
typedef HashTable<Entry, 16384> 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.
void init(); class PawnInfoTable {
Entry* probe(const Position& pos);
} // namespace 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)
+49
View File
@@ -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
View File
@@ -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)
+1727 -901
View File
File diff suppressed because it is too large Load Diff
+400 -267
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,420 +17,554 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define POSITION_H_INCLUDED
#include <cassert> // Disable some silly and noisy warning from MSVC compiler
#include <cstddef> // For offsetof() #if defined(_MSC_VER)
#include <string>
// 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 "bitboard.h"
#include "types.h" #include "color.h"
#include "direction.h"
#include "move.h"
#include "piece.h"
#include "square.h"
#include "value.h"
class Position;
class Thread;
namespace PSQT { ////
//// Constants
////
extern Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; /// FEN string for the initial position
const std::string StartPosition = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
void init(); /// 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;
/// CheckInfo struct is initialized at constructor time and keeps info used to
/// detect if a move gives check. ////
//// Types
////
/// struct checkInfo is initialized at c'tor time and keeps
/// info used to detect if a move gives check.
struct CheckInfo { struct CheckInfo {
explicit CheckInfo(const Position&); CheckInfo(const Position&);
Bitboard dcCandidates; Square ksq;
Bitboard pinned; Bitboard dcCandidates;
Bitboard checkSquares[PIECE_TYPE_NB]; Bitboard checkSq[8];
Square ksq; };
/// 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
}; };
/// StateInfo struct stores information needed to restore a Position object to /// The StateInfo struct stores information we need to restore a Position
/// its previous state when we retract a move. Whenever a move is made on the /// object to its previous state when we retract a move. Whenever a move
/// board (by calling Position::do_move), a StateInfo object must be passed. /// is made on the board (by calling Position::do_move), an StateInfo object
/// must be passed as a parameter.
struct StateInfo { struct StateInfo {
Key pawnKey, materialKey;
// Copied when making a move int castleRights, rule50, pliesFromNull;
Key pawnKey;
Key materialKey;
Value nonPawnMaterial[COLOR_NB];
int castlingRights;
int rule50;
int pliesFromNull;
Score psq;
Square epSquare; Square epSquare;
Score value;
Value npMaterial[2];
// Not copied when making a move Key key;
Key key; PieceType capture;
Bitboard checkersBB; Bitboard checkersBB;
PieceType capturedType;
StateInfo* previous; StateInfo* previous;
}; };
/// Position class stores information regarding the board representation as /// The position data structure. A position consists of the following data:
/// 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 /// * For each piece type, a bitboard representing the squares occupied
/// traversing the search tree. /// 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 { class Position {
friend class MaterialInfo;
friend class EndgameFunctions;
public: public:
static void init(); enum GamePhase {
MidGame,
EndGame
};
Position() = default; // To define the global object RootPos // Constructors
Position(const Position&) = delete; Position() {}
Position(const Position& pos, Thread* th) { *this = pos; thisThread = th; } Position(const Position& pos);
Position(const std::string& f, bool c960, Thread* th) { set(f, c960, th); } Position(const std::string& fen);
Position& operator=(const Position&); // To assign RootPos from UCI
// FEN string input/output // Text input/output
void set(const std::string& fenStr, bool isChess960, Thread* th); void from_fen(const std::string& fen);
const std::string fen() const; const std::string to_fen() const;
void print(Move m = MOVE_NONE) const;
// Position representation // Copying
Bitboard pieces() const; void copy(const Position& pos);
Bitboard pieces(PieceType pt) const; void flipped_copy(const Position& pos);
Bitboard pieces(PieceType pt1, PieceType pt2) const;
Bitboard pieces(Color c) const; // The piece on a given square
Bitboard pieces(Color c, PieceType pt) const;
Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const;
Piece piece_on(Square s) const; 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; Square ep_square() const;
bool empty(Square s) const;
template<PieceType Pt> int count(Color c) const;
template<PieceType Pt> const Square* squares(Color c) const;
template<PieceType Pt> Square square(Color c) const;
// Castling // Current king position for each color
int can_castle(Color c) const; Square king_square(Color c) const;
int can_castle(CastlingRight cr) const;
bool castling_impeded(CastlingRight cr) const;
Square castling_rook_square(CastlingRight cr) const;
// Checking // Castling rights
Bitboard checkers() const; bool can_castle_kingside(Color c) const;
Bitboard discovered_check_candidates() 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; Bitboard pinned_pieces(Color c) const;
// Attacks to/from a given square // Checking pieces and under check information
Bitboard checkers() const;
bool is_check() const;
// 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) const;
Bitboard attackers_to(Square s, Bitboard occupied) const; Bitboard attacks_from(Piece p, Square s) const;
Bitboard attacks_from(Piece pc, Square s) const;
template<PieceType> Bitboard attacks_from(Square s) const; template<PieceType> Bitboard attacks_from(Square s) const;
template<PieceType> Bitboard attacks_from(Square s, Color c) const; template<PieceType> Bitboard attacks_from(Square s, Color c) const;
// Properties of moves // Properties of moves
bool legal(Move m, Bitboard pinned) const; bool pl_move_is_legal(Move m, Bitboard pinned) const;
bool pseudo_legal(const Move m) const; bool pl_move_is_evasion(Move m, Bitboard pinned) const;
bool capture(Move m) const; bool move_is_check(Move m) const;
bool capture_or_promotion(Move m) const; bool move_is_check(Move m, const CheckInfo& ci) const;
bool gives_check(Move m, const CheckInfo& ci) const; bool move_is_capture(Move m) const;
bool advanced_pawn_push(Move m) const; bool move_is_capture_or_promotion(Move m) const;
Piece moved_piece(Move m) const; bool move_is_passed_pawn_push(Move m) const;
PieceType captured_piece_type() const; bool move_attacks_square(Move m, Square s) const;
// Piece specific // Information about pawns
bool pawn_passed(Color c, Square s) const; bool pawn_is_passed(Color c, Square s) const;
bool opposite_bishops() 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 // Doing and undoing moves
void do_move(Move m, StateInfo& st, 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 undo_move(Move m);
void do_null_move(StateInfo& st); void do_null_move(StateInfo& st);
void undo_null_move(); void undo_null_move();
// Static exchange evaluation // Static exchange evaluation
Value see(Move m) const; int see(Square from, Square to) const;
Value see_sign(Move m) const; int see(Move m) const;
int see(Square to) const;
int see_sign(Move m) const;
// Accessing hash keys // Accessing hash keys
Key key() const; Key get_key() const;
Key key_after(Move m) const; Key get_exclusion_key() const;
Key exclusion_key() const; Key get_pawn_key() const;
Key material_key() const; Key get_material_key() const;
Key pawn_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 // Other properties of the position
Color side_to_move() const; bool opposite_colored_bishops() const;
Phase game_phase() const; bool has_pawn_on_7th(Color c) const;
int game_ply() const;
bool is_chess960() const; // Reset the gamePly variable to 0
Thread* this_thread() const; void reset_game_ply();
uint64_t nodes_searched() const;
void set_nodes_searched(uint64_t n);
bool is_draw() const;
int rule50_count() const;
Score psq_score() const;
Value non_pawn_material(Color c) const;
// Position consistency check, for debugging // Position consistency check, for debugging
bool pos_is_ok(int* failedStep = nullptr) const; bool is_ok(int* failedStep = NULL) const;
void flip();
// Static member functions
static void init_zobrist();
static void init_piece_square_tables();
private: private:
// Initialization helpers (used while setting up a position)
// Initialization helper functions (used while setting up a position)
void clear(); void clear();
void set_castling_right(Color c, Square rfrom); void put_piece(Piece p, Square s);
void set_state(StateInfo* si) const; void allow_oo(Color c);
void allow_ooo(Color c);
// Other helpers // Helper functions for doing and undoing moves
Bitboard check_blockers(Color c, Color kingColor) const; void do_capture_move(Bitboard& key, PieceType capture, Color them, Square to, bool ep);
void put_piece(Color c, PieceType pt, Square s); void do_castle_move(Move m);
void remove_piece(Color c, PieceType pt, Square s); void undo_castle_move(Move m);
void move_piece(Color c, PieceType pt, Square from, Square to); void find_checkers();
template<bool Do>
void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto);
// Data members template<bool FindPinned>
Piece board[SQUARE_NB]; Bitboard hidden_checkers(Color c) const;
Bitboard byTypeBB[PIECE_TYPE_NB];
Bitboard byColorBB[COLOR_NB]; // Computing hash keys from scratch (for initialization and debugging)
int pieceCount[COLOR_NB][PIECE_TYPE_NB]; Key compute_key() const;
Square pieceList[COLOR_NB][PIECE_TYPE_NB][16]; Key compute_pawn_key() const;
int index[SQUARE_NB]; Key compute_material_key() const;
int castlingRightsMask[SQUARE_NB];
Square castlingRookSquare[CASTLING_RIGHT_NB]; // Computing incremental evaluation scores and material counts
Bitboard castlingPath[CASTLING_RIGHT_NB]; Score pst(Color c, PieceType pt, Square s) const;
StateInfo startState; Score compute_value() const;
uint64_t nodes; Value compute_non_pawn_material(Color c) const;
int gamePly;
// 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; Color sideToMove;
Thread* thisThread; int gamePly;
Key history[MaxGameLength];
File initialKFile, initialKRFile, initialQRFile;
StateInfo startState;
StateInfo* st; StateInfo* st;
bool chess960;
// 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 Color Position::side_to_move() const { ////
return sideToMove; //// Inline functions
} ////
inline bool Position::empty(Square s) const {
return board[s] == NO_PIECE;
}
inline Piece Position::piece_on(Square s) const { inline Piece Position::piece_on(Square s) const {
return board[s]; return board[s];
} }
inline Piece Position::moved_piece(Move m) const { inline Color Position::color_of_piece_on(Square s) const {
return board[from_sq(m)]; return color_of_piece(piece_on(s));
} }
inline Bitboard Position::pieces() const { inline PieceType Position::type_of_piece_on(Square s) const {
return byTypeBB[ALL_PIECES]; 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 Bitboard Position::occupied_squares() const {
return byTypeBB[0];
}
inline Bitboard Position::empty_squares() const {
return ~(occupied_squares());
}
inline Bitboard Position::pieces_of_color(Color c) const {
return byColorBB[c];
} }
inline Bitboard Position::pieces(PieceType pt) const { inline Bitboard Position::pieces(PieceType pt) const {
return byTypeBB[pt]; return byTypeBB[pt];
} }
inline Bitboard Position::pieces(PieceType pt, Color c) const {
return byTypeBB[pt] & byColorBB[c];
}
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const { inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const {
return byTypeBB[pt1] | byTypeBB[pt2]; return byTypeBB[pt1] | byTypeBB[pt2];
} }
inline Bitboard Position::pieces(Color c) const { inline Bitboard Position::pieces(PieceType pt1, PieceType pt2, Color c) const {
return byColorBB[c]; return (byTypeBB[pt1] | byTypeBB[pt2]) & byColorBB[c];
} }
inline Bitboard Position::pieces(Color c, PieceType pt) const { inline int Position::piece_count(Color c, PieceType pt) const {
return byColorBB[c] & byTypeBB[pt]; return pieceCount[c][pt];
} }
inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const { inline Square Position::piece_list(Color c, PieceType pt, int index) const {
return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]); return pieceList[c][pt][index];
} }
template<PieceType Pt> inline int Position::count(Color c) const { inline const Square* Position::piece_list_begin(Color c, PieceType pt) const {
return pieceCount[c][Pt]; return pieceList[c][pt];
}
template<PieceType Pt> inline const Square* Position::squares(Color c) const {
return pieceList[c][Pt];
}
template<PieceType Pt> inline Square Position::square(Color c) const {
assert(pieceCount[c][Pt] == 1);
return pieceList[c][Pt][0];
} }
inline Square Position::ep_square() const { inline Square Position::ep_square() const {
return st->epSquare; return st->epSquare;
} }
inline int Position::can_castle(CastlingRight cr) const { inline Square Position::king_square(Color c) const {
return st->castlingRights & cr; return pieceList[c][KING][0];
} }
inline int Position::can_castle(Color c) const { inline bool Position::can_castle_kingside(Color side) const {
return st->castlingRights & ((WHITE_OO | WHITE_OOO) << (2 * c)); return st->castleRights & (1+int(side));
} }
inline bool Position::castling_impeded(CastlingRight cr) const { inline bool Position::can_castle_queenside(Color side) const {
return byTypeBB[ALL_PIECES] & castlingPath[cr]; return st->castleRights & (4+4*int(side));
} }
inline Square Position::castling_rook_square(CastlingRight cr) const { inline bool Position::can_castle(Color side) const {
return castlingRookSquare[cr]; return can_castle_kingside(side) || can_castle_queenside(side);
} }
template<PieceType Pt> inline Square Position::initial_kr_square(Color c) const {
inline Bitboard Position::attacks_from(Square s) const { return relative_square(c, make_square(initialKRFile, RANK_1));
return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES]) }
: Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
: StepAttacksBB[Pt][s]; inline Square Position::initial_qr_square(Color c) const {
return relative_square(c, make_square(initialQRFile, RANK_1));
} }
template<> template<>
inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const { inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
return StepAttacksBB[make_piece(c, PAWN)][s]; return StepAttackBB[piece_of_color_and_type(c, PAWN)][s];
} }
inline Bitboard Position::attacks_from(Piece pc, Square s) const { template<PieceType Piece> // Knight and King and white pawns
return attacks_bb(pc, s, byTypeBB[ALL_PIECES]); inline Bitboard Position::attacks_from(Square s) const {
return StepAttackBB[Piece][s];
} }
inline Bitboard Position::attackers_to(Square s) const { template<>
return attackers_to(s, byTypeBB[ALL_PIECES]); 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 { inline Bitboard Position::checkers() const {
return st->checkersBB; return st->checkersBB;
} }
inline Bitboard Position::discovered_check_candidates() const { inline bool Position::is_check() const {
return check_blockers(sideToMove, ~sideToMove); return st->checkersBB != EmptyBoardBB;
} }
inline Bitboard Position::pinned_pieces(Color c) const { inline bool Position::pawn_is_passed(Color c, Square s) const {
return check_blockers(c, c); return !(pieces(PAWN, opposite_color(c)) & passed_pawn_mask(c, s));
} }
inline bool Position::pawn_passed(Color c, Square s) const { inline bool Position::pawn_is_passed(Bitboard theirPawns, Color c, Square s) {
return !(pieces(~c, PAWN) & passed_pawn_mask(c, s)); return !(theirPawns & passed_pawn_mask(c, s));
} }
inline bool Position::advanced_pawn_push(Move m) const { inline bool Position::pawn_is_isolated(Bitboard ourPawns, Square s) {
return type_of(moved_piece(m)) == PAWN return !(ourPawns & neighboring_files_bb(s));
&& relative_rank(sideToMove, from_sq(m)) > RANK_4;
} }
inline Key Position::key() const { inline bool Position::pawn_is_doubled(Bitboard ourPawns, Color c, Square s) {
return ourPawns & squares_behind(c, s);
}
inline bool Position::square_is_weak(Square s, Color c) const {
return !(pieces(PAWN, c) & outpost_mask(opposite_color(c), s));
}
inline Key Position::get_key() const {
return st->key; return st->key;
} }
inline Key Position::pawn_key() const { inline Key Position::get_exclusion_key() const {
return st->key ^ zobExclusion;
}
inline Key Position::get_pawn_key() const {
return st->pawnKey; return st->pawnKey;
} }
inline Key Position::material_key() const { inline Key Position::get_material_key() const {
return st->materialKey; return st->materialKey;
} }
inline Score Position::psq_score() const { inline Score Position::pst(Color c, PieceType pt, Square s) const {
return st->psq; 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 { inline Value Position::non_pawn_material(Color c) const {
return st->nonPawnMaterial[c]; return st->npMaterial[c];
} }
inline int Position::game_ply() const { inline bool Position::move_is_passed_pawn_push(Move m) const {
return gamePly;
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::rule50_count() const { inline int Position::rule_50_counter() const {
return st->rule50; return st->rule50;
} }
inline uint64_t Position::nodes_searched() const { inline bool Position::opposite_colored_bishops() const {
return nodes;
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 void Position::set_nodes_searched(uint64_t n) { inline bool Position::has_pawn_on_7th(Color c) const {
nodes = n;
return pieces(PAWN, c) & relative_rank_bb(c, RANK_7);
} }
inline bool Position::opposite_bishops() const { inline bool Position::move_is_capture(Move m) const {
return pieceCount[WHITE][BISHOP] == 1
&& pieceCount[BLACK][BISHOP] == 1 // Move must not be MOVE_NONE !
&& opposite_colors(square<BISHOP>(WHITE), square<BISHOP>(BLACK)); return (m & (3 << 15)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
} }
inline bool Position::is_chess960() const { inline bool Position::move_is_capture_or_promotion(Move m) const {
return chess960;
// Move must not be MOVE_NONE !
return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
} }
inline bool Position::capture_or_promotion(Move m) const { #endif // !defined(POSITION_H_INCLUDED)
assert(is_ok(m));
return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m));
}
inline bool Position::capture(Move m) const {
// Castling is encoded as "king captures the rook"
assert(is_ok(m));
return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT;
}
inline PieceType Position::captured_piece_type() const {
return st->capturedType;
}
inline Thread* Position::this_thread() const {
return thisThread;
}
inline void Position::put_piece(Color c, PieceType pt, Square s) {
board[s] = make_piece(c, pt);
byTypeBB[ALL_PIECES] |= s;
byTypeBB[pt] |= s;
byColorBB[c] |= s;
index[s] = pieceCount[c][pt]++;
pieceList[c][pt][index[s]] = s;
pieceCount[c][ALL_PIECES]++;
}
inline void Position::remove_piece(Color c, PieceType pt, Square s) {
// WARNING: This is not a reversible operation. If we remove a piece in
// do_move() and then replace it in undo_move() we will put it at the end of
// the list and not in its original place, it means index[] and pieceList[]
// are not guaranteed to be invariant to a do_move() + undo_move() sequence.
byTypeBB[ALL_PIECES] ^= s;
byTypeBB[pt] ^= s;
byColorBB[c] ^= s;
/* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */
Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]];
index[lastSquare] = index[s];
pieceList[c][pt][index[lastSquare]] = lastSquare;
pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE;
pieceCount[c][ALL_PIECES]--;
}
inline void Position::move_piece(Color c, PieceType pt, Square from, Square to) {
// index[from] is not updated and becomes stale. This works as long as index[]
// is accessed just by known occupied squares.
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[c] ^= from_to_bb;
board[from] = NO_PIECE;
board[to] = make_piece(c, pt);
index[to] = index[from];
pieceList[c][pt][index[to]] = to;
}
#endif // #ifndef POSITION_H_INCLUDED
-119
View File
@@ -1,119 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "types.h"
namespace PSQT {
#define S(mg, eg) make_score(mg, eg)
// Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece
// type on a given square a (middlegame, endgame) score pair is assigned. Table
// is defined for files A..D and white side: it is symmetric for black side and
// second half of the files.
const Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ },
{ // Pawn
{ S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },
{ S(-19, 5), S( 1,-4), S( 7, 8), S( 3,-2) },
{ S(-26,-6), S( -7,-5), S( 19, 5), S(24, 4) },
{ S(-25, 1), S(-14, 3), S( 16,-8), S(31,-3) },
{ S(-14, 6), S( 0, 9), S( -1, 7), S(17,-6) },
{ S(-14, 6), S(-13,-5), S(-10, 2), S(-6, 4) },
{ S(-12, 1), S( 15,-9), S( -8, 1), S(-4,18) },
{ S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }
},
{ // Knight
{ S(-143, -97), S(-96,-82), S(-80,-46), S(-73,-14) },
{ S( -83, -69), S(-43,-55), S(-21,-17), S(-10, 9) },
{ S( -71, -50), S(-22,-39), S( 0, -8), S( 9, 28) },
{ S( -25, -41), S( 18,-25), S( 43, 7), S( 47, 38) },
{ S( -26, -46), S( 16,-25), S( 38, 2), S( 50, 41) },
{ S( -11, -55), S( 37,-38), S( 56, -8), S( 71, 27) },
{ S( -62, -64), S(-17,-50), S( 5,-24), S( 14, 13) },
{ S(-195,-110), S(-66,-90), S(-42,-50), S(-29,-13) }
},
{ // Bishop
{ S(-54,-68), S(-23,-40), S(-35,-46), S(-44,-28) },
{ S(-30,-43), S( 10,-17), S( 2,-23), S( -9, -5) },
{ S(-19,-32), S( 17, -9), S( 11,-13), S( 1, 8) },
{ S(-21,-36), S( 18,-13), S( 11,-15), S( 0, 7) },
{ S(-21,-36), S( 14,-14), S( 6,-17), S( -1, 3) },
{ S(-27,-35), S( 6,-13), S( 2,-10), S( -8, 1) },
{ S(-33,-44), S( 7,-21), S( -4,-22), S(-12, -4) },
{ S(-45,-65), S(-21,-42), S(-29,-46), S(-39,-27) }
},
{ // Rook
{ S(-25, 0), S(-16, 0), S(-16, 0), S(-9, 0) },
{ S(-21, 0), S( -8, 0), S( -3, 0), S( 0, 0) },
{ S(-21, 0), S( -9, 0), S( -4, 0), S( 2, 0) },
{ S(-22, 0), S( -6, 0), S( -1, 0), S( 2, 0) },
{ S(-22, 0), S( -7, 0), S( 0, 0), S( 1, 0) },
{ S(-21, 0), S( -7, 0), S( 0, 0), S( 2, 0) },
{ S(-12, 0), S( 4, 0), S( 8, 0), S(12, 0) },
{ S(-23, 0), S(-15, 0), S(-11, 0), S(-5, 0) }
},
{ // Queen
{ S( 0,-70), S(-3,-57), S(-4,-41), S(-1,-29) },
{ S(-4,-58), S( 6,-30), S( 9,-21), S( 8, -4) },
{ S(-2,-39), S( 6,-17), S( 9, -7), S( 9, 5) },
{ S(-1,-29), S( 8, -5), S(10, 9), S( 7, 17) },
{ S(-3,-27), S( 9, -5), S( 8, 10), S( 7, 23) },
{ S(-2,-40), S( 6,-16), S( 8,-11), S(10, 3) },
{ S(-2,-54), S( 7,-30), S( 7,-21), S( 6, -7) },
{ S(-1,-75), S(-4,-54), S(-1,-44), S( 0,-30) }
},
{ // King
{ S(291, 28), S(344, 76), S(294,103), S(219,112) },
{ S(289, 70), S(329,119), S(263,170), S(205,159) },
{ S(226,109), S(271,164), S(202,195), S(136,191) },
{ S(204,131), S(212,194), S(175,194), S(137,204) },
{ S(177,132), S(205,187), S(143,224), S( 94,227) },
{ S(147,118), S(188,178), S(113,199), S( 70,197) },
{ S(116, 72), S(158,121), S( 93,142), S( 48,161) },
{ S( 94, 30), S(120, 76), S( 78,101), S( 31,111) }
}
};
#undef S
Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
// init() initializes piece square tables: the white halves of the tables are
// copied from Bonus[] 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 (PieceType pt = PAWN; pt <= KING; ++pt)
{
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
int edgeDistance = file_of(s) < FILE_E ? file_of(s) : FILE_H - file_of(s);
psq[BLACK][pt][~s] = -(psq[WHITE][pt][s] = v + Bonus[pt][rank_of(s)][edgeDistance]);
}
}
}
} // namespace PSQT
+188
View File
@@ -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
View File
@@ -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();
}
}
+44
View File
@@ -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
View File
@@ -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)
+2853 -1390
View File
File diff suppressed because it is too large Load Diff
+45 -79
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,93 +17,60 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define SEARCH_H_INCLUDED
#include <atomic> ////
#include <memory> // For std::unique_ptr //// Includes
#include <stack> ////
#include <vector>
#include "misc.h" #include "depth.h"
#include "position.h" #include "move.h"
#include "types.h"
namespace Search {
/// 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 //// Constants
/// its own array of Stack objects, indexed by the current ply. ////
struct Stack { const int PLY_MAX = 100;
Move* pv; const int PLY_MAX_PLUS_2 = 102;
int ply; 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 currentMove;
Move excludedMove; Move mateKiller;
Move killers[2]; Move threatMove;
Value staticEval; Move killers[KILLER_MAX];
bool skipEarlyPruning; Depth reduction;
int moveCount;
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.
struct RootMove { ////
//// Prototypes
////
explicit RootMove(Move m) : pv(1, m) {} 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();
bool operator<(const RootMove& m) const { return m.score < score; } // Descending sort
bool operator==(const Move& m) const { return pv[0] == m; }
void insert_pv_in_tt(Position& pos);
bool extract_ponder_from_tt(Position& pos);
Value score = -VALUE_INFINITE; #endif // !defined(SEARCH_H_INCLUDED)
Value previousScore = -VALUE_INFINITE;
std::vector<Move> pv;
};
typedef std::vector<RootMove> RootMoveVector;
/// LimitsType struct stores information sent by GUI about available time to
/// search the current move, maximum depth/time, if we are in analysis mode or
/// if we have to ponder while it's our opponent's turn to move.
struct LimitsType {
LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC
nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movestogo =
depth = movetime = mate = infinite = ponder = 0;
}
bool use_time_management() const {
return !(mate | movetime | depth | nodes | infinite);
}
std::vector<Move> searchmoves;
int time[COLOR_NB], inc[COLOR_NB], npmsec, movestogo, depth, movetime, mate, infinite, ponder;
int64_t nodes;
TimePoint startTime;
};
/// The SignalsType struct stores atomic flags updated during the search
/// typically in an async fashion e.g. to stop the search by the GUI.
struct SignalsType {
std::atomic_bool stop, stopOnPonderhit;
};
typedef std::unique_ptr<std::stack<StateInfo>> StateStackPtr;
extern SignalsType Signals;
extern LimitsType Limits;
extern StateStackPtr SetupStates;
void init();
void clear();
template<bool Root = true> uint64_t perft(Position& pos, Depth depth);
} // namespace Search
#endif // #ifndef SEARCH_H_INCLUDED
+201
View File
@@ -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
-169
View File
@@ -1,169 +0,0 @@
/*
Copyright (c) 2011-2013 Ronald de Man
*/
#ifndef TBCORE_H
#define TBCORE_H
#ifndef _WIN32
#include <pthread.h>
#define SEP_CHAR ':'
#define FD int
#define FD_ERR -1
#else
#include <windows.h>
#define SEP_CHAR ';'
#define FD HANDLE
#define FD_ERR INVALID_HANDLE_VALUE
#endif
#ifndef _WIN32
#define LOCK_T pthread_mutex_t
#define LOCK_INIT(x) pthread_mutex_init(&(x), NULL)
#define LOCK(x) pthread_mutex_lock(&(x))
#define UNLOCK(x) pthread_mutex_unlock(&(x))
#else
#define LOCK_T HANDLE
#define LOCK_INIT(x) do { x = CreateMutex(NULL, FALSE, NULL); } while (0)
#define LOCK(x) WaitForSingleObject(x, INFINITE)
#define UNLOCK(x) ReleaseMutex(x)
#endif
#ifndef _MSC_VER
#define BSWAP32(v) __builtin_bswap32(v)
#define BSWAP64(v) __builtin_bswap64(v)
#else
#define BSWAP32(v) _byteswap_ulong(v)
#define BSWAP64(v) _byteswap_uint64(v)
#endif
#define WDLSUFFIX ".rtbw"
#define DTZSUFFIX ".rtbz"
#define WDLDIR "RTBWDIR"
#define DTZDIR "RTBZDIR"
#define TBPIECES 6
typedef unsigned long long uint64;
typedef unsigned int uint32;
typedef unsigned char ubyte;
typedef unsigned short ushort;
const ubyte WDL_MAGIC[4] = { 0x71, 0xe8, 0x23, 0x5d };
const ubyte DTZ_MAGIC[4] = { 0xd7, 0x66, 0x0c, 0xa5 };
#define TBHASHBITS 10
struct TBHashEntry;
typedef uint64 base_t;
struct PairsData {
char *indextable;
ushort *sizetable;
ubyte *data;
ushort *offset;
ubyte *symlen;
ubyte *sympat;
int blocksize;
int idxbits;
int min_len;
base_t base[1]; // C++ complains about base[]...
};
struct TBEntry {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
}
#ifndef _WIN32
__attribute__((__may_alias__))
#endif
;
struct TBEntry_piece {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte enc_type;
struct PairsData *precomp[2];
int factor[2][TBPIECES];
ubyte pieces[2][TBPIECES];
ubyte norm[2][TBPIECES];
};
struct TBEntry_pawn {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte pawns[2];
struct {
struct PairsData *precomp[2];
int factor[2][TBPIECES];
ubyte pieces[2][TBPIECES];
ubyte norm[2][TBPIECES];
} file[4];
};
struct DTZEntry_piece {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte enc_type;
struct PairsData *precomp;
int factor[TBPIECES];
ubyte pieces[TBPIECES];
ubyte norm[TBPIECES];
ubyte flags; // accurate, mapped, side
ushort map_idx[4];
ubyte *map;
};
struct DTZEntry_pawn {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte pawns[2];
struct {
struct PairsData *precomp;
int factor[TBPIECES];
ubyte pieces[TBPIECES];
ubyte norm[TBPIECES];
} file[4];
ubyte flags[4];
ushort map_idx[4][4];
ubyte *map;
};
struct TBHashEntry {
uint64 key;
struct TBEntry *ptr;
};
struct DTZTableEntry {
uint64 key1;
uint64 key2;
struct TBEntry *entry;
};
#endif
-833
View File
@@ -1,833 +0,0 @@
/*
Copyright (c) 2013 Ronald de Man
This file may be redistributed and/or modified without restrictions.
tbprobe.cpp contains the Stockfish-specific routines of the
tablebase probing code. It should be relatively easy to adapt
this code to other chess engines.
*/
#define NOMINMAX
#include <algorithm>
#include "../position.h"
#include "../movegen.h"
#include "../bitboard.h"
#include "../search.h"
#include "../bitcount.h"
#include "tbprobe.h"
#include "tbcore.h"
#include "tbcore.cpp"
namespace Zobrist {
extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
}
int Tablebases::MaxCardinality = 0;
// Given a position with 6 or fewer pieces, produce a text string
// of the form KQPvKRP, where "KQP" represents the white pieces if
// mirror == 0 and the black pieces if mirror == 1.
static void prt_str(Position& pos, char *str, int mirror)
{
Color color;
PieceType pt;
int i;
color = !mirror ? WHITE : BLACK;
for (pt = KING; pt >= PAWN; --pt)
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
*str++ = pchr[6 - pt];
*str++ = 'v';
color = ~color;
for (pt = KING; pt >= PAWN; --pt)
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
*str++ = pchr[6 - pt];
*str++ = 0;
}
// Given a position, produce a 64-bit material signature key.
// If the engine supports such a key, it should equal the engine's key.
static uint64 calc_key(Position& pos, int mirror)
{
Color color;
PieceType pt;
int i;
uint64 key = 0;
color = !mirror ? WHITE : BLACK;
for (pt = PAWN; pt <= KING; ++pt)
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
key ^= Zobrist::psq[WHITE][pt][i - 1];
color = ~color;
for (pt = PAWN; pt <= KING; ++pt)
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
key ^= Zobrist::psq[BLACK][pt][i - 1];
return key;
}
// Produce a 64-bit material key corresponding to the material combination
// defined by pcs[16], where pcs[1], ..., pcs[6] is the number of white
// pawns, ..., kings and pcs[9], ..., pcs[14] is the number of black
// pawns, ..., kings.
static uint64 calc_key_from_pcs(int *pcs, int mirror)
{
int color;
PieceType pt;
int i;
uint64 key = 0;
color = !mirror ? 0 : 8;
for (pt = PAWN; pt <= KING; ++pt)
for (i = 0; i < pcs[color + pt]; i++)
key ^= Zobrist::psq[WHITE][pt][i];
color ^= 8;
for (pt = PAWN; pt <= KING; ++pt)
for (i = 0; i < pcs[color + pt]; i++)
key ^= Zobrist::psq[BLACK][pt][i];
return key;
}
bool is_little_endian() {
union {
int i;
char c[sizeof(int)];
} x;
x.i = 1;
return x.c[0] == 1;
}
static ubyte decompress_pairs(struct PairsData *d, uint64 idx)
{
static const bool isLittleEndian = is_little_endian();
return isLittleEndian ? decompress_pairs<true >(d, idx)
: decompress_pairs<false>(d, idx);
}
// probe_wdl_table and probe_dtz_table require similar adaptations.
static int probe_wdl_table(Position& pos, int *success)
{
struct TBEntry *ptr;
struct TBHashEntry *ptr2;
uint64 idx;
uint64 key;
int i;
ubyte res;
int p[TBPIECES];
// Obtain the position's material signature key.
key = pos.material_key();
// Test for KvK.
if (key == (Zobrist::psq[WHITE][KING][0] ^ Zobrist::psq[BLACK][KING][0]))
return 0;
ptr2 = TB_hash[key >> (64 - TBHASHBITS)];
for (i = 0; i < HSHMAX; i++)
if (ptr2[i].key == key) break;
if (i == HSHMAX) {
*success = 0;
return 0;
}
ptr = ptr2[i].ptr;
if (!ptr->ready) {
LOCK(TB_mutex);
if (!ptr->ready) {
char str[16];
prt_str(pos, str, ptr->key != key);
if (!init_table_wdl(ptr, str)) {
ptr2[i].key = 0ULL;
*success = 0;
UNLOCK(TB_mutex);
return 0;
}
// Memory barrier to ensure ptr->ready = 1 is not reordered.
#ifdef _MSC_VER
_ReadWriteBarrier();
#else
__asm__ __volatile__ ("" ::: "memory");
#endif
ptr->ready = 1;
}
UNLOCK(TB_mutex);
}
int bside, mirror, cmirror;
if (!ptr->symmetric) {
if (key != ptr->key) {
cmirror = 8;
mirror = 0x38;
bside = (pos.side_to_move() == WHITE);
} else {
cmirror = mirror = 0;
bside = !(pos.side_to_move() == WHITE);
}
} else {
cmirror = pos.side_to_move() == WHITE ? 0 : 8;
mirror = pos.side_to_move() == WHITE ? 0 : 0x38;
bside = 0;
}
// p[i] is to contain the square 0-63 (A1-H8) for a piece of type
// pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king.
// Pieces of the same type are guaranteed to be consecutive.
if (!ptr->has_pawns) {
struct TBEntry_piece *entry = (struct TBEntry_piece *)ptr;
ubyte *pc = entry->pieces[bside];
for (i = 0; i < entry->num;) {
Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07));
do {
p[i++] = pop_lsb(&bb);
} while (bb);
}
idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]);
res = decompress_pairs(entry->precomp[bside], idx);
} else {
struct TBEntry_pawn *entry = (struct TBEntry_pawn *)ptr;
int k = entry->file[0].pieces[0][0] ^ cmirror;
Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07));
i = 0;
do {
p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb);
int f = pawn_file(entry, p);
ubyte *pc = entry->file[f].pieces[bside];
for (; i < entry->num;) {
bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07));
do {
p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb);
}
idx = encode_pawn(entry, entry->file[f].norm[bside], p, entry->file[f].factor[bside]);
res = decompress_pairs(entry->file[f].precomp[bside], idx);
}
return ((int)res) - 2;
}
static int probe_dtz_table(Position& pos, int wdl, int *success)
{
struct TBEntry *ptr;
uint64 idx;
int i, res;
int p[TBPIECES];
// Obtain the position's material signature key.
uint64 key = pos.material_key();
if (DTZ_table[0].key1 != key && DTZ_table[0].key2 != key) {
for (i = 1; i < DTZ_ENTRIES; i++)
if (DTZ_table[i].key1 == key) break;
if (i < DTZ_ENTRIES) {
struct DTZTableEntry table_entry = DTZ_table[i];
for (; i > 0; i--)
DTZ_table[i] = DTZ_table[i - 1];
DTZ_table[0] = table_entry;
} else {
struct TBHashEntry *ptr2 = TB_hash[key >> (64 - TBHASHBITS)];
for (i = 0; i < HSHMAX; i++)
if (ptr2[i].key == key) break;
if (i == HSHMAX) {
*success = 0;
return 0;
}
ptr = ptr2[i].ptr;
char str[16];
int mirror = (ptr->key != key);
prt_str(pos, str, mirror);
if (DTZ_table[DTZ_ENTRIES - 1].entry)
free_dtz_entry(DTZ_table[DTZ_ENTRIES-1].entry);
for (i = DTZ_ENTRIES - 1; i > 0; i--)
DTZ_table[i] = DTZ_table[i - 1];
load_dtz_table(str, calc_key(pos, mirror), calc_key(pos, !mirror));
}
}
ptr = DTZ_table[0].entry;
if (!ptr) {
*success = 0;
return 0;
}
int bside, mirror, cmirror;
if (!ptr->symmetric) {
if (key != ptr->key) {
cmirror = 8;
mirror = 0x38;
bside = (pos.side_to_move() == WHITE);
} else {
cmirror = mirror = 0;
bside = !(pos.side_to_move() == WHITE);
}
} else {
cmirror = pos.side_to_move() == WHITE ? 0 : 8;
mirror = pos.side_to_move() == WHITE ? 0 : 0x38;
bside = 0;
}
if (!ptr->has_pawns) {
struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr;
if ((entry->flags & 1) != bside && !entry->symmetric) {
*success = -1;
return 0;
}
ubyte *pc = entry->pieces;
for (i = 0; i < entry->num;) {
Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07));
do {
p[i++] = pop_lsb(&bb);
} while (bb);
}
idx = encode_piece((struct TBEntry_piece *)entry, entry->norm, p, entry->factor);
res = decompress_pairs(entry->precomp, idx);
if (entry->flags & 2)
res = entry->map[entry->map_idx[wdl_to_map[wdl + 2]] + res];
if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1))
res *= 2;
} else {
struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr;
int k = entry->file[0].pieces[0] ^ cmirror;
Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07));
i = 0;
do {
p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb);
int f = pawn_file((struct TBEntry_pawn *)entry, p);
if ((entry->flags[f] & 1) != bside) {
*success = -1;
return 0;
}
ubyte *pc = entry->file[f].pieces;
for (; i < entry->num;) {
bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07));
do {
p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb);
}
idx = encode_pawn((struct TBEntry_pawn *)entry, entry->file[f].norm, p, entry->file[f].factor);
res = decompress_pairs(entry->file[f].precomp, idx);
if (entry->flags[f] & 2)
res = entry->map[entry->map_idx[f][wdl_to_map[wdl + 2]] + res];
if (!(entry->flags[f] & pa_flags[wdl + 2]) || (wdl & 1))
res *= 2;
}
return res;
}
// Add underpromotion captures to list of captures.
static ExtMove *add_underprom_caps(Position& pos, ExtMove *stack, ExtMove *end)
{
ExtMove *moves, *extra = end;
for (moves = stack; moves < end; moves++) {
Move move = moves->move;
if (type_of(move) == PROMOTION && !pos.empty(to_sq(move))) {
(*extra++).move = (Move)(move - (1 << 12));
(*extra++).move = (Move)(move - (2 << 12));
(*extra++).move = (Move)(move - (3 << 12));
}
}
return extra;
}
static int probe_ab(Position& pos, int alpha, int beta, int *success)
{
int v;
ExtMove stack[64];
ExtMove *moves, *end;
StateInfo st;
// Generate (at least) all legal non-ep captures including (under)promotions.
// It is OK to generate more, as long as they are filtered out below.
if (!pos.checkers()) {
end = generate<CAPTURES>(pos, stack);
// Since underpromotion captures are not included, we need to add them.
end = add_underprom_caps(pos, stack, end);
} else
end = generate<EVASIONS>(pos, stack);
CheckInfo ci(pos);
for (moves = stack; moves < end; moves++) {
Move capture = moves->move;
if (!pos.capture(capture) || type_of(capture) == ENPASSANT
|| !pos.legal(capture, ci.pinned))
continue;
pos.do_move(capture, st, pos.gives_check(capture, ci));
v = -probe_ab(pos, -beta, -alpha, success);
pos.undo_move(capture);
if (*success == 0) return 0;
if (v > alpha) {
if (v >= beta) {
*success = 2;
return v;
}
alpha = v;
}
}
v = probe_wdl_table(pos, success);
if (*success == 0) return 0;
if (alpha >= v) {
*success = 1 + (alpha > 0);
return alpha;
} else {
*success = 1;
return v;
}
}
// Probe the WDL table for a particular position.
// If *success != 0, the probe was successful.
// The return value is from the point of view of the side to move:
// -2 : loss
// -1 : loss, but draw under 50-move rule
// 0 : draw
// 1 : win, but draw under 50-move rule
// 2 : win
int Tablebases::probe_wdl(Position& pos, int *success)
{
int v;
*success = 1;
v = probe_ab(pos, -2, 2, success);
// If en passant is not possible, we are done.
if (pos.ep_square() == SQ_NONE)
return v;
if (!(*success)) return 0;
// Now handle en passant.
int v1 = -3;
// Generate (at least) all legal en passant captures.
ExtMove stack[192];
ExtMove *moves, *end;
StateInfo st;
if (!pos.checkers())
end = generate<CAPTURES>(pos, stack);
else
end = generate<EVASIONS>(pos, stack);
CheckInfo ci(pos);
for (moves = stack; moves < end; moves++) {
Move capture = moves->move;
if (type_of(capture) != ENPASSANT
|| !pos.legal(capture, ci.pinned))
continue;
pos.do_move(capture, st, pos.gives_check(capture, ci));
int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture);
if (*success == 0) return 0;
if (v0 > v1) v1 = v0;
}
if (v1 > -3) {
if (v1 >= v) v = v1;
else if (v == 0) {
// Check whether there is at least one legal non-ep move.
for (moves = stack; moves < end; moves++) {
Move capture = moves->move;
if (type_of(capture) == ENPASSANT) continue;
if (pos.legal(capture, ci.pinned)) break;
}
if (moves == end && !pos.checkers()) {
end = generate<QUIETS>(pos, end);
for (; moves < end; moves++) {
Move move = moves->move;
if (pos.legal(move, ci.pinned))
break;
}
}
// If not, then we are forced to play the losing ep capture.
if (moves == end)
v = v1;
}
}
return v;
}
// This routine treats a position with en passant captures as one without.
static int probe_dtz_no_ep(Position& pos, int *success)
{
int wdl, dtz;
wdl = probe_ab(pos, -2, 2, success);
if (*success == 0) return 0;
if (wdl == 0) return 0;
if (*success == 2)
return wdl == 2 ? 1 : 101;
ExtMove stack[192];
ExtMove *moves, *end = NULL;
StateInfo st;
CheckInfo ci(pos);
if (wdl > 0) {
// Generate at least all legal non-capturing pawn moves
// including non-capturing promotions.
if (!pos.checkers())
end = generate<NON_EVASIONS>(pos, stack);
else
end = generate<EVASIONS>(pos, stack);
for (moves = stack; moves < end; moves++) {
Move move = moves->move;
if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move)
|| !pos.legal(move, ci.pinned))
continue;
pos.do_move(move, st, pos.gives_check(move, ci));
int v = -probe_ab(pos, -2, -wdl + 1, success);
pos.undo_move(move);
if (*success == 0) return 0;
if (v == wdl)
return v == 2 ? 1 : 101;
}
}
dtz = 1 + probe_dtz_table(pos, wdl, success);
if (*success >= 0) {
if (wdl & 1) dtz += 100;
return wdl >= 0 ? dtz : -dtz;
}
if (wdl > 0) {
int best = 0xffff;
for (moves = stack; moves < end; moves++) {
Move move = moves->move;
if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN
|| !pos.legal(move, ci.pinned))
continue;
pos.do_move(move, st, pos.gives_check(move, ci));
int v = -Tablebases::probe_dtz(pos, success);
pos.undo_move(move);
if (*success == 0) return 0;
if (v > 0 && v + 1 < best)
best = v + 1;
}
return best;
} else {
int best = -1;
if (!pos.checkers())
end = generate<NON_EVASIONS>(pos, stack);
else
end = generate<EVASIONS>(pos, stack);
for (moves = stack; moves < end; moves++) {
int v;
Move move = moves->move;
if (!pos.legal(move, ci.pinned))
continue;
pos.do_move(move, st, pos.gives_check(move, ci));
if (st.rule50 == 0) {
if (wdl == -2) v = -1;
else {
v = probe_ab(pos, 1, 2, success);
v = (v == 2) ? 0 : -101;
}
} else {
v = -Tablebases::probe_dtz(pos, success) - 1;
}
pos.undo_move(move);
if (*success == 0) return 0;
if (v < best)
best = v;
}
return best;
}
}
static int wdl_to_dtz[] = {
-1, -101, 0, 101, 1
};
// Probe the DTZ table for a particular position.
// If *success != 0, the probe was successful.
// The return value is from the point of view of the side to move:
// n < -100 : loss, but draw under 50-move rule
// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0)
// 0 : draw
// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0)
// 100 < n : win, but draw under 50-move rule
//
// The return value n can be off by 1: a return value -n can mean a loss
// in n+1 ply and a return value +n can mean a win in n+1 ply. This
// cannot happen for tables with positions exactly on the "edge" of
// the 50-move rule.
//
// This implies that if dtz > 0 is returned, the position is certainly
// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine
// picks moves that preserve dtz + 50-move-counter <= 99.
//
// If n = 100 immediately after a capture or pawn move, then the position
// is also certainly a win, and during the whole phase until the next
// capture or pawn move, the inequality to be preserved is
// dtz + 50-movecounter <= 100.
//
// In short, if a move is available resulting in dtz + 50-move-counter <= 99,
// then do not accept moves leading to dtz + 50-move-counter == 100.
//
int Tablebases::probe_dtz(Position& pos, int *success)
{
*success = 1;
int v = probe_dtz_no_ep(pos, success);
if (pos.ep_square() == SQ_NONE)
return v;
if (*success == 0) return 0;
// Now handle en passant.
int v1 = -3;
ExtMove stack[192];
ExtMove *moves, *end;
StateInfo st;
if (!pos.checkers())
end = generate<CAPTURES>(pos, stack);
else
end = generate<EVASIONS>(pos, stack);
CheckInfo ci(pos);
for (moves = stack; moves < end; moves++) {
Move capture = moves->move;
if (type_of(capture) != ENPASSANT
|| !pos.legal(capture, ci.pinned))
continue;
pos.do_move(capture, st, pos.gives_check(capture, ci));
int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture);
if (*success == 0) return 0;
if (v0 > v1) v1 = v0;
}
if (v1 > -3) {
v1 = wdl_to_dtz[v1 + 2];
if (v < -100) {
if (v1 >= 0)
v = v1;
} else if (v < 0) {
if (v1 >= 0 || v1 < -100)
v = v1;
} else if (v > 100) {
if (v1 > 0)
v = v1;
} else if (v > 0) {
if (v1 == 1)
v = v1;
} else if (v1 >= 0) {
v = v1;
} else {
for (moves = stack; moves < end; moves++) {
Move move = moves->move;
if (type_of(move) == ENPASSANT) continue;
if (pos.legal(move, ci.pinned)) break;
}
if (moves == end && !pos.checkers()) {
end = generate<QUIETS>(pos, end);
for (; moves < end; moves++) {
Move move = moves->move;
if (pos.legal(move, ci.pinned))
break;
}
}
if (moves == end)
v = v1;
}
}
return v;
}
// Check whether there has been at least one repetition of positions
// since the last capture or pawn move.
static int has_repeated(StateInfo *st)
{
while (1) {
int i = 4, e = std::min(st->rule50, st->pliesFromNull);
if (e < i)
return 0;
StateInfo *stp = st->previous->previous;
do {
stp = stp->previous->previous;
if (stp->key == st->key)
return 1;
i += 2;
} while (i <= e);
st = st->previous;
}
}
static Value wdl_to_Value[5] = {
-VALUE_MATE + MAX_PLY + 1,
VALUE_DRAW - 2,
VALUE_DRAW,
VALUE_DRAW + 2,
VALUE_MATE - MAX_PLY - 1
};
// Use the DTZ tables to filter out moves that don't preserve the win or draw.
// If the position is lost, but DTZ is fairly high, only keep moves that
// maximise DTZ.
//
// A return value false indicates that not all probes were successful and that
// no moves were filtered out.
bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Value& score)
{
int success;
int dtz = probe_dtz(pos, &success);
if (!success) return false;
StateInfo st;
CheckInfo ci(pos);
// Probe each move.
for (size_t i = 0; i < rootMoves.size(); i++) {
Move move = rootMoves[i].pv[0];
pos.do_move(move, st, pos.gives_check(move, ci));
int v = 0;
if (pos.checkers() && dtz > 0) {
ExtMove s[192];
if (generate<LEGAL>(pos, s) == s)
v = 1;
}
if (!v) {
if (st.rule50 != 0) {
v = -Tablebases::probe_dtz(pos, &success);
if (v > 0) v++;
else if (v < 0) v--;
} else {
v = -Tablebases::probe_wdl(pos, &success);
v = wdl_to_dtz[v + 2];
}
}
pos.undo_move(move);
if (!success) return false;
rootMoves[i].score = (Value)v;
}
// Obtain 50-move counter for the root position.
// In Stockfish there seems to be no clean way, so we do it like this:
int cnt50 = st.previous->rule50;
// Use 50-move counter to determine whether the root position is
// won, lost or drawn.
int wdl = 0;
if (dtz > 0)
wdl = (dtz + cnt50 <= 100) ? 2 : 1;
else if (dtz < 0)
wdl = (-dtz + cnt50 <= 100) ? -2 : -1;
// Determine the score to report to the user.
score = wdl_to_Value[wdl + 2];
// If the position is winning or losing, but too few moves left, adjust the
// score to show how close it is to winning or losing.
// NOTE: int(PawnValueEg) is used as scaling factor in score_to_uci().
if (wdl == 1 && dtz <= 100)
score = (Value)(((200 - dtz - cnt50) * int(PawnValueEg)) / 200);
else if (wdl == -1 && dtz >= -100)
score = -(Value)(((200 + dtz - cnt50) * int(PawnValueEg)) / 200);
// Now be a bit smart about filtering out moves.
size_t j = 0;
if (dtz > 0) { // winning (or 50-move rule draw)
int best = 0xffff;
for (size_t i = 0; i < rootMoves.size(); i++) {
int v = rootMoves[i].score;
if (v > 0 && v < best)
best = v;
}
int max = best;
// If the current phase has not seen repetitions, then try all moves
// that stay safely within the 50-move budget, if there are any.
if (!has_repeated(st.previous) && best + cnt50 <= 99)
max = 99 - cnt50;
for (size_t i = 0; i < rootMoves.size(); i++) {
int v = rootMoves[i].score;
if (v > 0 && v <= max)
rootMoves[j++] = rootMoves[i];
}
} else if (dtz < 0) { // losing (or 50-move rule draw)
int best = 0;
for (size_t i = 0; i < rootMoves.size(); i++) {
int v = rootMoves[i].score;
if (v < best)
best = v;
}
// Try all moves, unless we approach or have a 50-move rule draw.
if (-best * 2 + cnt50 < 100)
return true;
for (size_t i = 0; i < rootMoves.size(); i++) {
if (rootMoves[i].score == best)
rootMoves[j++] = rootMoves[i];
}
} else { // drawing
// Try all moves that preserve the draw.
for (size_t i = 0; i < rootMoves.size(); i++) {
if (rootMoves[i].score == 0)
rootMoves[j++] = rootMoves[i];
}
}
rootMoves.resize(j, Search::RootMove(MOVE_NONE));
return true;
}
// Use the WDL tables to filter out moves that don't preserve the win or draw.
// This is a fallback for the case that some or all DTZ tables are missing.
//
// A return value false indicates that not all probes were successful and that
// no moves were filtered out.
bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoveVector& rootMoves, Value& score)
{
int success;
int wdl = Tablebases::probe_wdl(pos, &success);
if (!success) return false;
score = wdl_to_Value[wdl + 2];
StateInfo st;
CheckInfo ci(pos);
int best = -2;
// Probe each move.
for (size_t i = 0; i < rootMoves.size(); i++) {
Move move = rootMoves[i].pv[0];
pos.do_move(move, st, pos.gives_check(move, ci));
int v = -Tablebases::probe_wdl(pos, &success);
pos.undo_move(move);
if (!success) return false;
rootMoves[i].score = (Value)v;
if (v > best)
best = v;
}
size_t j = 0;
for (size_t i = 0; i < rootMoves.size(); i++) {
if (rootMoves[i].score == best)
rootMoves[j++] = rootMoves[i];
}
rootMoves.resize(j, Search::RootMove(MOVE_NONE));
return true;
}
-18
View File
@@ -1,18 +0,0 @@
#ifndef TBPROBE_H
#define TBPROBE_H
#include "../search.h"
namespace Tablebases {
extern int MaxCardinality;
void init(const std::string& path);
int probe_wdl(Position& pos, int *success);
int probe_dtz(Position& pos, int *success);
bool root_probe(Position& pos, Search::RootMoveVector& rootMoves, Value& score);
bool root_probe_wdl(Position& pos, Search::RootMoveVector& rootMoves, Value& score);
}
#endif
-195
View File
@@ -1,195 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm> // For std::count
#include <cassert>
#include "movegen.h"
#include "search.h"
#include "thread.h"
#include "uci.h"
using namespace Search;
ThreadPool Threads; // Global object
/// Thread constructor launch the thread and then wait until it goes to sleep
/// in idle_loop().
Thread::Thread() {
resetCalls = exit = false;
maxPly = callsCnt = 0;
history.clear();
counterMoves.clear();
idx = Threads.size(); // Start from 0
std::unique_lock<Mutex> lk(mutex);
searching = true;
nativeThread = std::thread(&Thread::idle_loop, this);
sleepCondition.wait(lk, [&]{ return !searching; });
}
/// Thread destructor wait for thread termination before returning
Thread::~Thread() {
mutex.lock();
exit = true;
sleepCondition.notify_one();
mutex.unlock();
nativeThread.join();
}
/// Thread::wait_for_search_finished() wait on sleep condition until not searching
void Thread::wait_for_search_finished() {
std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return !searching; });
}
/// Thread::wait() wait on sleep condition until condition is true
void Thread::wait(std::atomic_bool& condition) {
std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return bool(condition); });
}
/// Thread::start_searching() wake up the thread that will start the search
void Thread::start_searching(bool resume) {
std::unique_lock<Mutex> lk(mutex);
if (!resume)
searching = true;
sleepCondition.notify_one();
}
/// Thread::idle_loop() is where the thread is parked when it has no work to do
void Thread::idle_loop() {
while (!exit)
{
std::unique_lock<Mutex> lk(mutex);
searching = false;
while (!searching && !exit)
{
sleepCondition.notify_one(); // Wake up any waiting thread
sleepCondition.wait(lk);
}
lk.unlock();
if (!exit)
search();
}
}
/// ThreadPool::init() create and launch requested threads, that will go
/// immediately to sleep. We cannot use a constructor because Threads is a
/// static object and we need a fully initialized engine at this point due to
/// allocation of Endgames in the Thread constructor.
void ThreadPool::init() {
push_back(new MainThread);
read_uci_options();
}
/// ThreadPool::exit() terminate threads before the program exits. Cannot be
/// done in destructor because threads must be terminated before deleting any
/// static objects, so while still in main().
void ThreadPool::exit() {
while (size())
delete back(), pop_back();
}
/// ThreadPool::read_uci_options() updates internal threads parameters from the
/// corresponding UCI options and creates/destroys threads to match requested
/// number. Thread objects are dynamically allocated.
void ThreadPool::read_uci_options() {
size_t requested = Options["Threads"];
assert(requested > 0);
while (size() < requested)
push_back(new Thread);
while (size() > requested)
delete back(), pop_back();
}
/// ThreadPool::nodes_searched() return the number of nodes searched
int64_t ThreadPool::nodes_searched() {
int64_t nodes = 0;
for (Thread* th : *this)
nodes += th->rootPos.nodes_searched();
return nodes;
}
/// ThreadPool::start_thinking() wake up the main thread sleeping in idle_loop()
/// and start a new search, then return immediately.
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
StateStackPtr& states) {
main()->wait_for_search_finished();
Signals.stopOnPonderhit = Signals.stop = false;
main()->rootMoves.clear();
main()->rootPos = pos;
Limits = limits;
if (states.get()) // If we don't set a new position, preserve current state
{
SetupStates = std::move(states); // Ownership transfer here
assert(!states.get());
}
for (const auto& m : MoveList<LEGAL>(pos))
if ( limits.searchmoves.empty()
|| std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
main()->rootMoves.push_back(RootMove(m));
main()->start_searching();
}
+44 -67
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,86 +17,64 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define THREAD_H_INCLUDED
#include <atomic>
#include <bitset>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>
#include "material.h" ////
//// Includes
////
#include "lock.h"
#include "movepick.h" #include "movepick.h"
#include "pawns.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread_win32.h"
/// Thread struct keeps together all the thread related stuff. We also use ////
/// per-thread pawn and material hash tables so that once we get a pointer to an //// Constants and variables
/// entry its life time is unlimited and we don't have to care about someone ////
/// changing the entry under our feet.
class Thread { const int THREAD_MAX = 8;
std::thread nativeThread;
Mutex mutex;
ConditionVariable sleepCondition;
bool exit, searching;
public: ////
Thread(); //// Types
virtual ~Thread(); ////
virtual void search();
void idle_loop();
void start_searching(bool resume = false);
void wait_for_search_finished();
void wait(std::atomic_bool& b);
Pawns::Table pawnsTable; struct SplitPoint {
Material::Table materialTable; SplitPoint *parent;
Endgames endgames; Position pos;
size_t idx, PVIdx; SearchStack sstack[THREAD_MAX][PLY_MAX_PLUS_2];
int maxPly, callsCnt; SearchStack *parentSstack;
int ply;
Position rootPos; Depth depth;
Search::RootMoveVector rootMoves; volatile Value alpha, beta, bestValue, futilityValue;
Depth rootDepth; Value approximateEval;
HistoryStats history; bool pvNode;
MovesStats counterMoves; int master, slaves[THREAD_MAX];
Depth completedDepth; Lock lock;
std::atomic_bool resetCalls; MovePicker *mp;
volatile int moves;
volatile int cpus;
bool finished;
}; };
/// MainThread is a derived class with a specific overload for the main thread struct Thread {
SplitPoint *splitPoint;
struct MainThread : public Thread { volatile int activeSplitPoints;
virtual void search(); uint64_t nodes;
uint64_t betaCutOffs[2];
bool easyMovePlayed, failedLow; bool failHighPly1;
double bestMoveChanges; 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, #endif // !defined(THREAD_H_INCLUDED)
/// parking and, most importantly, launching a thread. All the access to threads
/// data is done through this class.
struct ThreadPool : public std::vector<Thread*> {
void init(); // No constructor and destructor, threads rely on globals that should
void exit(); // be initialized and valid during the whole thread lifetime.
MainThread* main() { return static_cast<MainThread*>(at(0)); }
void start_thinking(const Position&, const Search::LimitsType&, Search::StateStackPtr&);
void read_uci_options();
int64_t nodes_searched();
};
extern ThreadPool Threads;
#endif // #ifndef THREAD_H_INCLUDED
-70
View File
@@ -1,70 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef THREAD_WIN32_H_INCLUDED
#define THREAD_WIN32_H_INCLUDED
/// STL thread library used by mingw and gcc when cross compiling for Windows
/// relies on libwinpthread. Currently libwinpthread implements mutexes directly
/// on top of Windows semaphores. Semaphores, being kernel objects, require kernel
/// mode transition in order to lock or unlock, which is very slow compared to
/// interlocked operations (about 30% slower on bench test). To workaround this
/// issue, we define our wrappers to the low level Win32 calls. We use critical
/// sections to support Windows XP and older versions. Unfortunately, cond_wait()
/// is racy between unlock() and WaitForSingleObject() but they have the same
/// speed performance of SRW locks.
#include <condition_variable>
#include <mutex>
#if defined(_WIN32) && !defined(_MSC_VER)
#ifndef NOMINMAX
# define NOMINMAX // Disable macros min() and max()
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
/// Mutex and ConditionVariable struct are wrappers of the low level locking
/// machinery and are modeled after the corresponding C++11 classes.
struct Mutex {
Mutex() { InitializeCriticalSection(&cs); }
~Mutex() { DeleteCriticalSection(&cs); }
void lock() { EnterCriticalSection(&cs); }
void unlock() { LeaveCriticalSection(&cs); }
private:
CRITICAL_SECTION cs;
};
typedef std::condition_variable_any ConditionVariable;
#else // Default case: use STL classes
typedef std::mutex Mutex;
typedef std::condition_variable ConditionVariable;
#endif
#endif // #ifndef THREAD_WIN32_H_INCLUDED
-133
View File
@@ -1,133 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cfloat>
#include <cmath>
#include "search.h"
#include "timeman.h"
#include "uci.h"
TimeManagement Time; // Our global time management object
namespace {
enum TimeType { OptimumTime, MaxTime };
const int MoveHorizon = 50; // Plan time management at most this many moves ahead
const double MaxRatio = 6.93; // When in trouble, we can step over reserved time with this ratio
const double StealRatio = 0.36; // However we must not steal time from remaining moves over this ratio
// move_importance() is a skew-logistic function based on naive statistical
// analysis of "how many games are still undecided after n half-moves". Game
// is considered "undecided" as long as neither side has >275cp advantage.
// Data was extracted from CCRL game database with some simple filtering criteria.
double move_importance(int ply) {
const double XScale = 8.27;
const double XShift = 59.;
const double Skew = 0.179;
return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero
}
template<TimeType T>
int remaining(int myTime, int movesToGo, int ply, int slowMover)
{
const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
const double TStealRatio = (T == OptimumTime ? 0 : StealRatio);
double moveImportance = (move_importance(ply) * slowMover) / 100;
double otherMovesImportance = 0;
for (int i = 1; i < movesToGo; ++i)
otherMovesImportance += move_importance(ply + 2 * i);
double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance);
double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance);
return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks an explicit cast
}
} // namespace
/// init() is called at the beginning of the search and calculates the allowed
/// thinking time out of the time control and current game ply. We support four
/// different kinds of time controls, passed in 'limits':
///
/// inc == 0 && movestogo == 0 means: x basetime [sudden death!]
/// inc == 0 && movestogo != 0 means: x moves in y minutes
/// inc > 0 && movestogo == 0 means: x basetime + z increment
/// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
{
int minThinkingTime = Options["Minimum Thinking Time"];
int moveOverhead = Options["Move Overhead"];
int slowMover = Options["Slow Mover"];
int npmsec = Options["nodestime"];
// 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: Given npms (nodes per millisecond) must be much lower then
// real engine speed to avoid time losses.
if (npmsec)
{
if (!availableNodes) // Only once at game start
availableNodes = npmsec * limits.time[us]; // Time is in msec
// Convert from millisecs to nodes
limits.time[us] = (int)availableNodes;
limits.inc[us] *= npmsec;
limits.npmsec = npmsec;
}
startTime = limits.startTime;
unstablePvFactor = 1;
optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime);
const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon;
// We calculate optimum time usage for different hypothetical "moves to go"-values
// and choose the minimum of calculated search time values. Usually the greatest
// hypMTG gives the minimum values.
for (int hypMTG = 1; hypMTG <= MaxMTG; ++hypMTG)
{
// Calculate thinking time for hypothetical "moves to go"-value
int hypMyTime = limits.time[us]
+ limits.inc[us] * (hypMTG - 1)
- moveOverhead * (2 + std::min(hypMTG, 40));
hypMyTime = std::max(hypMyTime, 0);
int t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, ply, slowMover);
int t2 = minThinkingTime + remaining<MaxTime >(hypMyTime, hypMTG, ply, slowMover);
optimumTime = std::min(t1, optimumTime);
maximumTime = std::min(t2, maximumTime);
}
if (Options["Ponder"])
optimumTime += optimumTime / 4;
}
-50
View File
@@ -1,50 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TIMEMAN_H_INCLUDED
#define TIMEMAN_H_INCLUDED
#include "misc.h"
#include "search.h"
#include "thread.h"
/// 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);
void pv_instability(double bestMoveChanges) { unstablePvFactor = 1 + bestMoveChanges; }
int available() const { return int(optimumTime * unstablePvFactor * 1.016); }
int maximum() const { return maximumTime; }
int elapsed() const { return int(Search::Limits.npmsec ? Threads.nodes_searched() : now() - startTime); }
int64_t availableNodes; // When in 'nodes as time' mode
private:
TimePoint startTime;
int optimumTime;
int maximumTime;
double unstablePvFactor;
};
extern TimeManagement Time;
#endif // #ifndef TIMEMAN_H_INCLUDED
+213 -72
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,100 +17,242 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cstring> // For std::memset
#include <iostream>
#include "bitboard.h" ////
//// Includes
////
#include <cassert>
#include <cmath>
#include <cstring>
#include <xmmintrin.h>
#include "movegen.h"
#include "tt.h" #include "tt.h"
TranspositionTable TT; // Our global transposition table // The main transposition table
TranspositionTable TT;
////
//// Functions
////
/// TranspositionTable::resize() sets the size of the transposition table, TranspositionTable::TranspositionTable() {
/// 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) { size = writes = 0;
entries = 0;
generation = 0;
}
size_t newClusterCount = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(Cluster)); TranspositionTable::~TranspositionTable() {
if (newClusterCount == clusterCount) delete [] entries;
return;
clusterCount = newClusterCount;
free(mem);
mem = calloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1, 1);
if (!mem)
{
std::cerr << "Failed to allocate " << mbSize
<< "MB for transposition table." << std::endl;
exit(EXIT_FAILURE);
}
table = (Cluster*)((uintptr_t(mem) + CacheLineSize - 1) & ~(CacheLineSize - 1));
} }
/// TranspositionTable::clear() overwrites the entire transposition table /// TranspositionTable::set_size sets the size of the transposition table,
/// with zeros. It is called whenever the table is resized, or when the /// 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)
{
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::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). /// 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() { void TranspositionTable::clear() {
std::memset(table, 0, clusterCount * sizeof(Cluster)); memset(entries, 0, size * sizeof(TTCluster));
} }
/// TranspositionTable::probe() looks up the current position in the transposition /// TranspositionTable::first_entry returns a pointer to the first
/// table. It returns true and a pointer to the TTEntry if the position is found. /// entry of a cluster given a position. The low 32 bits of the key
/// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry /// are used to get the index in the table.
/// 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 { inline TTEntry* TranspositionTable::first_entry(const Key posKey) const {
TTEntry* const tte = first_entry(key); return entries[uint32_t(posKey) & (size - 1)].data;
const uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster
for (int i = 0; i < ClusterSize; ++i)
if (!tte[i].key16 || tte[i].key16 == key16)
{
if ((tte[i].genBound8 & 0xFC) != generation8 && tte[i].key16)
tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh
return found = (bool)tte[i].key16, &tte[i];
}
// 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 259 (256 is the modulus plus 3 to keep the lowest
// two bound bits from affecting the result) to calculate the entry
// age correctly even after generation8 overflows into the next cycle.
if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2 * ONE_PLY
> tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2 * ONE_PLY)
replace = &tte[i];
return found = false, replace;
} }
/// Returns an approximation of the hashtable occupation during a search. The /// TranspositionTable::store writes a new entry containing a position,
/// hash is x permill full, as per UCI protocol. /// 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.
int TranspositionTable::hashfull() const void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m) {
{
int cnt = 0; TTEntry *tte, *replace;
for (int i = 0; i < 1000 / ClusterSize; i++) 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++)
{ {
const TTEntry* tte = &table[i].entry[0]; if (!tte->key() || tte->key() == posKey32) // empty or overwrite old
for (int j = 0; j < ClusterSize; j++) {
if ((tte[j].genBound8 & 0xFC) == generation8) // Do not overwrite when new type is VALUE_TYPE_EV_LO
cnt++; if (tte->key() && t == VALUE_TYPE_EV_LO)
return;
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;
int c1 = (replace->generation() == generation ? 2 : 0);
int c2 = (tte->generation() == generation ? -2 : 0);
int c3 = (tte->depth() < replace->depth() ? 1 : 0);
if (c1 + c2 + c3 > 0)
replace = tte;
} }
return cnt; *replace = TTEntry(posKey32, v, t, d, m, generation);
writes++;
}
/// TranspositionTable::retrieve looks up the current position in the
/// transposition table. Returns a pointer to the TTEntry or NULL
/// if position is not found.
TTEntry* TranspositionTable::retrieve(const Key posKey) const {
uint32_t posKey32 = posKey >> 32;
TTEntry* tte = first_entry(posKey);
for (int i = 0; i < ClusterSize; i++, tte++)
if (tte->key() == posKey32)
return tte;
return NULL;
}
/// 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))));
} }
+83 -78
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,102 +17,108 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define TT_H_INCLUDED
#include "misc.h" ////
#include "types.h" //// Includes
////
/// TTEntry struct is the 10 bytes transposition table entry, defined as below: #include "depth.h"
#include "position.h"
#include "value.h"
////
//// Types
////
/// The TTEntry class is the class of transposition table entries
/// ///
/// key 16 bit /// A TTEntry needs 96 bits to be stored
/// move 16 bit ///
/// value 16 bit /// bit 0-31: key
/// eval value 16 bit /// bit 32-63: data
/// generation 6 bit /// bit 64-79: value
/// bound type 2 bit /// bit 80-95: depth
/// depth 8 bit ///
/// 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; } public:
Value value() const { return (Value)value16; } TTEntry() {}
Value eval() const { return (Value)eval16; } TTEntry(uint32_t k, Value v, ValueType t, Depth d, Move m, int generation)
Depth depth() const { return (Depth)depth8; } : key_ (k), data((m & 0x1FFFF) | (t << 20) | (generation << 23)),
Bound bound() const { return (Bound)(genBound8 & 0x3); } value_(int16_t(v)), depth_(int16_t(d)) {}
void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) { uint32_t key() const { return key_; }
Depth depth() const { return Depth(depth_); }
// Preserve any existing move for the same position Move move() const { return Move(data & 0x1FFFF); }
if (m || (k >> 48) != key16) Value value() const { return Value(value_); }
move16 = (uint16_t)m; ValueType type() const { return ValueType((data >> 20) & 7); }
int generation() const { return (data >> 23); }
// Don't overwrite more valuable entries
if ( (k >> 48) != key16
|| d > depth8 - 2
/* || g != (genBound8 & 0xFC) // Matching non-zero keys are already refreshed by probe() */
|| b == BOUND_EXACT)
{
key16 = (uint16_t)(k >> 48);
value16 = (int16_t)v;
eval16 = (int16_t)ev;
genBound8 = (uint8_t)(g | b);
depth8 = (int8_t)d;
}
}
private: private:
friend class TranspositionTable; uint32_t key_;
uint32_t data;
uint16_t key16; int16_t value_;
uint16_t move16; int16_t depth_;
int16_t value16;
int16_t eval16;
uint8_t genBound8;
int8_t depth8;
}; };
/// A TranspositionTable consists of a power of 2 number of clusters and each /// This is the number of TTEntry slots for each position
/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry const int ClusterSize = 5;
/// contains information of exactly one position. The size of a cluster should
/// divide the size of a cache line size, to ensure that clusters never cross /// Each group of ClusterSize number of TTEntry form a TTCluster
/// cache lines. This ensures best cache performance, as the cacheline is /// that is indexed by a single position key. Cluster is padded
/// prefetched, as soon as possible. /// 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 { class TranspositionTable {
static const int CacheLineSize = 64;
static const int ClusterSize = 3;
struct Cluster {
TTEntry entry[ClusterSize];
char padding[2]; // Align to a divisor of the cache line size
};
static_assert(CacheLineSize % sizeof(Cluster) == 0, "Cluster size incorrect");
public: public:
~TranspositionTable() { free(mem); } TranspositionTable();
void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound ~TranspositionTable();
uint8_t generation() const { return generation8; } void set_size(size_t mbSize);
TTEntry* probe(const Key key, bool& found) const;
int hashfull() const;
void resize(size_t mbSize);
void clear(); void clear();
void store(const Key posKey, Value v, ValueType type, Depth d, Move m);
// The lowest order bits of the key are used to get the index of the cluster TTEntry* retrieve(const Key posKey) const;
TTEntry* first_entry(const Key key) const { void prefetch(const Key posKey) const;
return &table[(size_t)key & (clusterCount - 1)].entry[0]; 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: private:
size_t clusterCount; inline TTEntry* first_entry(const Key posKey) const;
Cluster* table;
void* mem; // Be sure 'writes' is at least one cache line away
uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 // 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; extern TranspositionTable TT;
#endif // #ifndef TT_H_INCLUDED #endif // !defined(TT_H_INCLUDED)
+40 -393
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,412 +17,60 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define TYPES_H_INCLUDED
/// When compiling with provided Makefile (e.g. for Linux and OSX), configuration #if !defined(_MSC_VER)
/// is done automatically. To get started type 'make help'.
///
/// When Makefile is not used (e.g. with Microsoft Visual Studio) some switches
/// need to be set manually:
///
/// -DNDEBUG | Disable debugging mode. Always use this for release.
///
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to
/// | run on some very old machines.
///
/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
/// | only in 64-bit mode and requires hardware with popcnt support.
///
/// -DUSE_PEXT | Add runtime support for use of pext asm-instruction. Works
/// | only in 64-bit mode and requires hardware with pext support.
#include <cassert> #include <inttypes.h>
#include <cctype>
#include <climits>
#include <cstdint>
#include <cstdlib>
#if defined(_MSC_VER)
// Disable some silly and noisy warning from MSVC compiler
#pragma warning(disable: 4127) // Conditional expression is constant
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
#endif
/// Predefined macros hell:
///
/// __GNUC__ Compiler is gcc, Clang or Intel on Linux
/// __INTEL_COMPILER Compiler is Intel
/// _MSC_VER Compiler is MSVC or Intel on Windows
/// _WIN32 Building on Windows (any)
/// _WIN64 Building on Windows 64 bit
#if defined(_WIN64) && defined(_MSC_VER) // No Makefile used
# include <intrin.h> // MSVC popcnt and bsfq instrinsics
# define IS_64BIT
# define USE_BSFQ
#endif
#if defined(USE_POPCNT) && defined(__INTEL_COMPILER) && defined(_MSC_VER)
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
#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 #else
# define pext(b, m) (0)
#endif
#ifdef USE_POPCNT typedef __int8 int8_t;
const bool HasPopCnt = true; typedef unsigned __int8 uint8_t;
#else typedef __int16 int16;
const bool HasPopCnt = false; typedef unsigned __int16 uint16_t;
#endif typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#ifdef USE_PEXT typedef __int16 int16_t;
const bool HasPext = true; typedef __int64 int64_t;
#else
const bool HasPext = false;
#endif
#ifdef IS_64BIT #endif // !defined(_MSC_VER)
const bool Is64Bit = true;
#else
const bool Is64Bit = false;
#endif
// Hash keys
typedef uint64_t Key; typedef uint64_t Key;
// Bitboard type
typedef uint64_t Bitboard; typedef uint64_t Bitboard;
const int MAX_MOVES = 256;
const int MAX_PLY = 128;
/// A move needs 16 bits to be stored ////
/// //// Compiler specific defines
/// bit 0- 5: destination square (from 0 to 63) ////
/// bit 6-11: origin square (from 0 to 63)
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
/// bit 14-15: special move flag: promotion (1), en passant (2), castling (3)
/// NOTE: EN-PASSANT bit is set only when a pawn can be captured
///
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in
/// any normal move destination square is always different from origin square
/// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
enum Move { // Quiet a warning on Intel compiler
MOVE_NONE, #if !defined(__SIZEOF_INT__ )
MOVE_NULL = 65 #define __SIZEOF_INT__ 0
}; #endif
enum MoveType { // Check for 64 bits for different compilers: Intel, MSVC and gcc
NORMAL, #if defined(__x86_64) || defined(_M_X64) || defined(_WIN64) || (__SIZEOF_INT__ > 4)
PROMOTION = 1 << 14, #define IS_64BIT
ENPASSANT = 2 << 14, #endif
CASTLING = 3 << 14
};
enum Color { #if defined(IS_64BIT) && (defined(__GNUC__) || defined(__INTEL_COMPILER))
WHITE, BLACK, NO_COLOR, COLOR_NB = 2 #define USE_BSFQ
}; #endif
enum CastlingSide { // Cache line alignment specification
KING_SIDE, QUEEN_SIDE, CASTLING_SIDE_NB = 2 #if defined(_MSC_VER) || defined(__INTEL_COMPILER)
}; #define CACHE_LINE_ALIGNMENT __declspec(align(64))
#else
#define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
#endif
enum CastlingRight { #endif // !defined(TYPES_H_INCLUDED)
NO_CASTLING,
WHITE_OO,
WHITE_OOO = WHITE_OO << 1,
BLACK_OO = WHITE_OO << 2,
BLACK_OOO = WHITE_OO << 3,
ANY_CASTLING = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO,
CASTLING_RIGHT_NB = 16
};
template<Color C, CastlingSide S> struct MakeCastling {
static const CastlingRight
right = C == WHITE ? S == QUEEN_SIDE ? WHITE_OOO : WHITE_OO
: S == QUEEN_SIDE ? BLACK_OOO : BLACK_OO;
};
enum Phase {
PHASE_ENDGAME,
PHASE_MIDGAME = 128,
MG = 0, EG = 1, PHASE_NB = 2
};
enum ScaleFactor {
SCALE_FACTOR_DRAW = 0,
SCALE_FACTOR_ONEPAWN = 48,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
enum Bound {
BOUND_NONE,
BOUND_UPPER,
BOUND_LOWER,
BOUND_EXACT = BOUND_UPPER | BOUND_LOWER
};
enum Value : int {
VALUE_ZERO = 0,
VALUE_DRAW = 0,
VALUE_KNOWN_WIN = 10000,
VALUE_MATE = 32000,
VALUE_INFINITE = 32001,
VALUE_NONE = 32002,
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY,
PawnValueMg = 198, PawnValueEg = 258,
KnightValueMg = 817, KnightValueEg = 846,
BishopValueMg = 836, BishopValueEg = 857,
RookValueMg = 1270, RookValueEg = 1281,
QueenValueMg = 2521, QueenValueEg = 2558,
MidgameLimit = 15581, EndgameLimit = 3998
};
enum PieceType {
NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING,
ALL_PIECES = 0,
PIECE_TYPE_NB = 8
};
enum Piece {
NO_PIECE,
W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
PIECE_NB = 16
};
enum Depth {
ONE_PLY = 1,
DEPTH_ZERO = 0,
DEPTH_QS_CHECKS = 0,
DEPTH_QS_NO_CHECKS = -1,
DEPTH_QS_RECAPTURES = -5,
DEPTH_NONE = -6,
DEPTH_MAX = MAX_PLY
};
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,
SQUARE_NB = 64,
DELTA_N = 8,
DELTA_E = 1,
DELTA_S = -8,
DELTA_W = -1,
DELTA_NN = DELTA_N + DELTA_N,
DELTA_NE = DELTA_N + DELTA_E,
DELTA_SE = DELTA_S + DELTA_E,
DELTA_SS = DELTA_S + DELTA_S,
DELTA_SW = DELTA_S + DELTA_W,
DELTA_NW = DELTA_N + DELTA_W
};
enum File {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB
};
enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB
};
/// Score enum stores a middlegame and an endgame value in a single integer
/// (enum). The least significant 16 bits are used to store the endgame value
/// and the upper 16 bits are used to store the middlegame value.
enum Score : int { SCORE_ZERO };
inline Score make_score(int mg, int eg) {
return Score((mg << 16) + eg);
}
/// 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 mg_value(Score s) {
union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s + 0x8000) >> 16) };
return Value(mg.s);
}
inline Value eg_value(Score s) {
union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s)) };
return Value(eg.s);
}
#define ENABLE_BASE_OPERATORS_ON(T) \
inline T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \
inline T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \
inline T operator*(int i, T d) { return T(i * int(d)); } \
inline T operator*(T d, int i) { return T(int(d) * i); } \
inline T operator-(T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \
inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; } \
inline T& operator*=(T& d, int i) { return d = T(int(d) * i); }
#define ENABLE_FULL_OPERATORS_ON(T) \
ENABLE_BASE_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); } \
inline T operator/(T d, int i) { return T(int(d) / i); } \
inline int operator/(T d1, T d2) { return int(d1) / int(d2); } \
inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
ENABLE_FULL_OPERATORS_ON(Value)
ENABLE_FULL_OPERATORS_ON(PieceType)
ENABLE_FULL_OPERATORS_ON(Piece)
ENABLE_FULL_OPERATORS_ON(Color)
ENABLE_FULL_OPERATORS_ON(Depth)
ENABLE_FULL_OPERATORS_ON(Square)
ENABLE_FULL_OPERATORS_ON(File)
ENABLE_FULL_OPERATORS_ON(Rank)
ENABLE_BASE_OPERATORS_ON(Score)
#undef ENABLE_FULL_OPERATORS_ON
#undef ENABLE_BASE_OPERATORS_ON
/// Additional operators to add integers to a Value
inline Value operator+(Value v, int i) { return Value(int(v) + i); }
inline Value operator-(Value v, int i) { return Value(int(v) - i); }
inline Value& operator+=(Value& v, int i) { return v = v + i; }
inline Value& operator-=(Value& v, int i) { return v = v - i; }
/// 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);
/// 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);
}
extern Value PieceValue[PHASE_NB][PIECE_NB];
inline Color operator~(Color c) {
return Color(c ^ BLACK);
}
inline Square operator~(Square s) {
return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8
}
inline CastlingRight operator|(Color c, CastlingSide s) {
return CastlingRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c));
}
inline Value mate_in(int ply) {
return VALUE_MATE - ply;
}
inline Value mated_in(int ply) {
return -VALUE_MATE + ply;
}
inline Square make_square(File f, Rank r) {
return Square((r << 3) | f);
}
inline Piece make_piece(Color c, PieceType pt) {
return Piece((c << 3) | pt);
}
inline PieceType type_of(Piece pc) {
return PieceType(pc & 7);
}
inline Color color_of(Piece pc) {
assert(pc != NO_PIECE);
return Color(pc >> 3);
}
inline bool is_ok(Square s) {
return s >= SQ_A1 && s <= SQ_H8;
}
inline File file_of(Square s) {
return File(s & 7);
}
inline Rank rank_of(Square s) {
return Rank(s >> 3);
}
inline Square relative_square(Color c, Square s) {
return Square(s ^ (c * 56));
}
inline Rank relative_rank(Color c, Rank r) {
return Rank(r ^ (c * 7));
}
inline Rank relative_rank(Color c, Square s) {
return relative_rank(c, rank_of(s));
}
inline bool opposite_colors(Square s1, Square s2) {
int s = int(s1) ^ int(s2);
return ((s >> 3) ^ s) & 1;
}
inline Square pawn_push(Color c) {
return c == WHITE ? DELTA_N : DELTA_S;
}
inline Square from_sq(Move m) {
return Square((m >> 6) & 0x3F);
}
inline Square to_sq(Move m) {
return Square(m & 0x3F);
}
inline MoveType type_of(Move m) {
return MoveType(m & (3 << 14));
}
inline PieceType promotion_type(Move m) {
return PieceType(((m >> 12) & 3) + KNIGHT);
}
inline Move make_move(Square from, Square to) {
return Move(to | (from << 6));
}
template<MoveType T>
inline Move make(Square from, Square to, PieceType pt = KNIGHT) {
return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12));
}
inline bool is_ok(Move m) {
return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE
}
#endif // #ifndef TYPES_H_INCLUDED
+250 -212
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,270 +17,309 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
////
//// Includes
////
#include <cassert>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include "book.h"
#include "evaluate.h" #include "evaluate.h"
#include "misc.h"
#include "move.h"
#include "movegen.h" #include "movegen.h"
#include "position.h" #include "position.h"
#include "san.h"
#include "search.h" #include "search.h"
#include "thread.h"
#include "timeman.h"
#include "uci.h" #include "uci.h"
#include "ucioption.h"
using namespace std; using namespace std;
extern void benchmark(const Position& pos, istream& is); ////
//// Local definitions:
////
namespace { namespace {
// FEN string of the initial position, normal chess // UCIInputParser is a class for parsing UCI input. The class
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; // is actually a string stream built on a given input string.
// Stack to keep track of the position states along the setup moves (from the typedef istringstream UCIInputParser;
// start position to the position just before the search starts). Needed by
// 'draw by repetition' detection. // The root position. This is set up when the user (or in practice, the GUI)
Search::StateStackPtr SetupStates; // 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") //// Functions
// or the starting position ("startpos") and then makes the moves given in the ////
// following move list ("moves").
void position(Position& pos, istringstream& is) { /// 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; void uci_main_loop() {
string token, fen;
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; cout << "id name " << engine_name()
is >> token; // Consume "moves" token if any << "\nid author Tord Romstad, Marco Costalba, Joona Kiiski\n";
print_uci_options();
cout << "uciok" << endl;
} }
else if (token == "fen") else if (token == "ucinewgame")
while (is >> token && token != "moves") {
fen += token + " "; 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 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; return;
pos.set(fen, Options["UCI_Chess960"], Threads.main()); if (token == "startpos")
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>); RootPosition.from_fen(StartPosition);
else if (token == "fen")
// Parse move list (if any)
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
{ {
SetupStates->push(StateInfo()); string fen;
pos.do_move(m, SetupStates->top(), pos.gives_check(m, CheckInfo(pos))); 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();
}
} }
} }
// setoption() is called when engine receives the "setoption" UCI command. The // set_option() is called when Stockfish receives the "setoption" UCI
// function updates the UCI option ("name") to the given value ("value"). // 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).
void setoption(istringstream& is) { void set_option(UCIInputParser& uip) {
string token, name, value; string token, name, value;
is >> token; // Consume "name" token if (!(uip >> token)) // operator>>() skips any whitespace
return;
// Read option name (can contain spaces) if (token == "name" && uip >> name)
while (is >> token && token != "value") {
name += string(" ", name.empty() ? 0 : 1) + token; while (uip >> token && token != "value")
name += (" " + token);
// Read option value (can contain spaces) if (token == "value" && uip >> value)
while (is >> token) {
value += string(" ", value.empty() ? 0 : 1) + token; while (uip >> token)
value += (" " + token);
if (Options.count(name)) set_option_value(name, value);
Options[name] = value; } else
else push_button(name);
sync_cout << "No such option: " << name << sync_endl; }
} }
// go() is called when engine receives the "go" UCI command. The function sets // go() is called when Stockfish receives the "go" UCI command. The
// the thinking time and other parameters from the input string, then starts // input parameter is a UCIInputParser. It is assumed that this
// the search. // 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.
void go(const Position& pos, istringstream& is) { bool go(UCIInputParser& uip) {
Search::LimitsType limits;
string token; string token;
limits.startTime = now(); // As early as possible! 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];
while (is >> token) searchMoves[0] = MOVE_NONE;
if (token == "searchmoves")
while (is >> token)
limits.searchmoves.push_back(UCI::to_move(pos, token));
else if (token == "wtime") is >> limits.time[WHITE]; while (uip >> token)
else if (token == "btime") is >> limits.time[BLACK]; {
else if (token == "winc") is >> limits.inc[WHITE]; if (token == "infinite")
else if (token == "binc") is >> limits.inc[BLACK]; infinite = true;
else if (token == "movestogo") is >> limits.movestogo; else if (token == "ponder")
else if (token == "depth") is >> limits.depth; ponder = true;
else if (token == "nodes") is >> limits.nodes; else if (token == "wtime")
else if (token == "movetime") is >> limits.movetime; uip >> time[0];
else if (token == "mate") is >> limits.mate; else if (token == "btime")
else if (token == "infinite") limits.infinite = 1; uip >> time[1];
else if (token == "ponder") limits.ponder = 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);
Threads.start_thinking(pos, limits, SetupStates); searchMoves[numOfMoves] = MOVE_NONE;
}
}
assert(RootPosition.is_ok());
return think(RootPosition, infinite, ponder, RootPosition.side_to_move(),
time, inc, movesToGo, depth, nodes, moveTime, searchMoves);
} }
} // namespace void perft(UCIInputParser& uip) {
string token;
int depth, tm, n;
Position pos = RootPosition;
/// UCI::loop() waits for a command from stdin, parses it and calls the appropriate if (!(uip >> depth))
/// function. Also intercepts EOF from stdin to ensure gracefully exiting if the return;
/// 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[]) { tm = get_system_time();
Position pos(StartFEN, false, Threads.main()); // The root position n = perft(pos, depth * OnePly);
string token, cmd;
for (int i = 1; i < argc; ++i) tm = get_system_time() - tm;
cmd += std::string(argv[i]) + " "; std::cout << "\nNodes " << n
<< "\nTime (ms) " << tm
do { << "\nNodes/second " << (int)(n/(tm/1000.0)) << std::endl;
if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF }
cmd = "quit";
istringstream is(cmd);
token.clear(); // getline() could return empty or blank line
is >> skipws >> token;
// The GUI sends 'ponderhit' to tell us to ponder on the same move the
// opponent has played. In case Signals.stopOnPonderhit is set we are
// waiting for 'ponderhit' to stop the search (for instance because we
// already ran out of time), otherwise we should continue searching but
// switching from pondering to normal search.
if ( token == "quit"
|| token == "stop"
|| (token == "ponderhit" && Search::Signals.stopOnPonderhit))
{
Search::Signals.stop = true;
Threads.main()->start_searching(true); // Could be sleeping
}
else if (token == "ponderhit")
Search::Limits.ponder = 0; // Switch to normal search
else if (token == "uci")
sync_cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << sync_endl;
else if (token == "ucinewgame")
{
Search::clear();
Time.availableNodes = 0;
}
else if (token == "isready") sync_cout << "readyok" << sync_endl;
else if (token == "go") go(pos, is);
else if (token == "position") position(pos, is);
else if (token == "setoption") setoption(is);
// Additional custom non-UCI commands, useful for debugging
else if (token == "flip") pos.flip();
else if (token == "bench") benchmark(pos, is);
else if (token == "d") sync_cout << pos << sync_endl;
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
else if (token == "perft")
{
int depth;
stringstream ss;
is >> depth;
ss << Options["Hash"] << " "
<< Options["Threads"] << " " << depth << " current perft";
benchmark(pos, ss);
}
else
sync_cout << "Unknown command: " << cmd << sync_endl;
} while (token != "quit" && argc == 1); // Passed args have one-shot behaviour
Threads.main()->wait_for_search_finished();
}
/// 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) {
stringstream ss;
if (abs(v) < VALUE_MATE - MAX_PLY)
ss << "cp " << v * 100 / PawnValueEg;
else
ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
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;
} }
+8 -57
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,63 +17,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 #define UCI_H_INCLUDED
#include <map> ////
#include <string> //// Prototypes
////
#include "types.h" extern void uci_main_loop();
class Position;
namespace UCI { #endif // !defined(UCI_H_INCLUDED)
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(int v, int min, int max, OnChange = nullptr);
Option& operator=(const std::string&);
void operator<<(const Option&);
operator int() const;
operator std::string() 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);
Move to_move(const Position& pos, std::string& str);
} // namespace UCI
extern UCI::OptionsMap Options;
#endif // #ifndef UCI_H_INCLUDED
+298 -100
View File
@@ -1,8 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,146 +17,345 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
////
//// Includes
////
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <ostream> #include <map>
#include <string>
#include <sstream>
#include <vector>
#include "misc.h" #include "misc.h"
#include "search.h"
#include "thread.h" #include "thread.h"
#include "tt.h" #include "ucioption.h"
#include "uci.h"
#include "syzygy/tbprobe.h"
using std::string; using std::string;
UCI::OptionsMap Options; // Global object ////
//// Local definitions
////
namespace UCI { namespace {
/// 'On change' actions, triggered by an option's value change ///
void on_clear_hash(const Option&) { Search::clear(); } /// Types
void on_hash_size(const Option& o) { TT.resize(o); } ///
void on_logger(const Option& o) { start_logger(o); }
void on_threads(const Option&) { Threads.read_uci_options(); } enum OptionType { SPIN, COMBO, CHECK, STRING, BUTTON };
void on_tb_path(const Option& o) { Tablebases::init(o); }
typedef std::vector<string> ComboValues;
struct Option {
string name, defaultValue, currentValue;
OptionType type;
size_t idx;
int minValue, maxValue;
ComboValues comboValues;
Option();
Option(const char* defaultValue, OptionType = STRING);
Option(bool defaultValue, OptionType = CHECK);
Option(int defaultValue, int minValue, int maxValue);
bool operator<(const Option& o) const { return this->idx < o.idx; }
};
typedef std::map<string, Option> Options;
///
/// Constants
///
// load_defaults populates the options map with the hard
// coded names and default values.
void load_defaults(Options& o) {
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);
o["King Safety Curve"].comboValues.push_back("Quadratic");
o["King Safety Curve"].comboValues.push_back("Linear"); /*, "From File"*/
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);
// 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;
}
///
/// Variables
///
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();
}
/// Our case insensitive less() function as required by UCI protocol // get_option_value implements the various get_option_value_<type>
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { // functions defined later, because only the option value
// type changes a template seems a proper solution.
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), template<typename T>
[](char c1, char c2) { return tolower(c1) < tolower(c2); }); 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;
}
}
////
//// Functions
////
/// 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);
} }
/// init() initializes the UCI options to their hard-coded default values /// print_uci_options() prints all the UCI options to the standard output,
/// in the format defined by the UCI protocol.
void init(OptionsMap& o) { void print_uci_options() {
const int MaxHashMB = Is64Bit ? 1024 * 1024 : 2048; static const char optionTypeName[][16] = {
"spin", "combo", "check", "string", "button"
};
o["Write Debug Log"] << Option(false, on_logger); // Build up a vector out of the options map and sort it according to idx
o["Contempt"] << Option(0, -100, 100); // field, that is the chronological insertion order in options map.
o["Threads"] << Option(1, 1, 128, on_threads); std::vector<Option> vec;
o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); for (Options::const_iterator it = options.begin(); it != options.end(); ++it)
o["Clear Hash"] << Option(on_clear_hash); vec.push_back(it->second);
o["Ponder"] << Option(false);
o["MultiPV"] << Option(1, 1, 500); std::sort(vec.begin(), vec.end());
o["Skill Level"] << Option(20, 0, 20);
o["Move Overhead"] << Option(30, 0, 5000); for (std::vector<Option>::const_iterator it = vec.begin(); it != vec.end(); ++it)
o["Minimum Thinking Time"] << Option(20, 0, 5000); {
o["Slow Mover"] << Option(84, 10, 1000); std::cout << "\noption name " << it->name
o["nodestime"] << Option(0, 0, 10000); << " type " << optionTypeName[it->type];
o["UCI_Chess960"] << Option(false);
o["SyzygyPath"] << Option("<empty>", on_tb_path); if (it->type == BUTTON)
o["SyzygyProbeDepth"] << Option(1, 1, 100); continue;
o["Syzygy50MoveRule"] << Option(true);
o["SyzygyProbeLimit"] << Option(6, 0, 6); 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;
} }
/// operator<<() is used to print all the options default values in chronological /// get_option_value_bool() returns the current value of a UCI parameter of
/// insertion order (the idx field) and in the format defined by the UCI protocol. /// type "check".
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { bool get_option_value_bool(const string& optionName) {
for (size_t idx = 0; idx < om.size(); ++idx) return get_option_value<bool>(optionName);
for (const auto& it : om)
if (it.second.idx == idx)
{
const Option& o = it.second;
os << "\noption name " << it.first << " type " << o.type;
if (o.type != "button")
os << " default " << o.defaultValue;
if (o.type == "spin")
os << " min " << o.min << " max " << o.max;
break;
}
return os;
} }
/// Option class constructors and conversion operators /// 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.
Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f) int get_option_value_int(const string& optionName) {
{ defaultValue = currentValue = v; }
Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), on_change(f) return get_option_value<int>(optionName);
{ defaultValue = currentValue = (v ? "true" : "false"); }
Option::Option(OnChange f) : type("button"), min(0), max(0), on_change(f)
{}
Option::Option(int v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f)
{ defaultValue = currentValue = std::to_string(v); }
Option::operator int() const {
assert(type == "check" || type == "spin");
return (type == "spin" ? stoi(currentValue) : currentValue == "true");
}
Option::operator std::string() const {
assert(type == "string");
return currentValue;
} }
/// operator<<() inits options and assigns idx in the correct printing order /// 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".
void Option::operator<<(const Option& o) { string get_option_value_string(const string& optionName) {
static size_t insert_order = 0; return get_option_value<string>(optionName);
*this = o;
idx = insert_order++;
} }
/// operator=() updates currentValue and triggers on_change() action. It's up to /// set_option_value() inserts a new value for a UCI parameter. Note that
/// the GUI to check for option's limits, but we could receive the new value from /// the function does not check that the new value is legal for the given
/// the user by console window, so let's check the bounds anyway. /// parameter: This is assumed to be the responsibility of the GUI.
Option& Option::operator=(const string& v) { void set_option_value(const string& name, const string& value) {
assert(!type.empty()); // 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 ( (type != "button" && v.empty()) if (options.find(name) == options.end())
|| (type == "check" && v != "true" && v != "false") {
|| (type == "spin" && (stoi(v) < min || stoi(v) > max))) std::cout << "No such option: " << name << std::endl;
return *this; return;
}
if (type != "button") // Normally it's up to the GUI to check for option's limits,
currentValue = v; // 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 (on_change) if (opt.type == CHECK && v != "0" && v != "1")
on_change(*this); return;
return *this; else if (opt.type == SPIN)
{
int val = atoi(v.c_str());
if (val < opt.minValue || val > opt.maxValue)
return;
}
opt.currentValue = v;
} }
} // namespace UCI
/// 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) {}
}
+44
View File
@@ -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)
+96
View File
@@ -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
View File
@@ -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)