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
65 changed files with 12974 additions and 8399 deletions
+39 -28
View File
@@ -1,20 +1,22 @@
1. Introduction 1. Introduction
--------------- ---------------
Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is not a Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
complete chess program, but requires some UCI compatible GUI (like XBoard not a complete chess program, but requires some UCI compatible GUI
with PolyGlot, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) (like XBoard with PolyGlot, eboard, Jos, Arena, Sigma Chess, Shredder,
in order to be used comfortably. Read the documentation for your GUI of choice Chess Partner, or Fritz) in order to be used comfortably. Read the
for information about how to use Stockfish with your GUI. documentation for your GUI of choice for information about how to use
Stockfish with your GUI.
This version of Stockfish supports up to 32 CPUs, but has not been tested This version of Stockfish supports up to 8 CPUs, but has not been
thoroughly with more than 4. The program tries to detect the number of tested thoroughly with more than 2. The program tries to detect the
CPUs on your computer and set the number of search threads accordingly, but number of CPUs on your computer and set the number of search threads
please be aware that the detection is not always correct. It is therefore accordingly, but please be aware that the detection is not always
recommended to inspect the value of the "Threads" UCI parameter, and to correct. It is therefore recommended to inspect the value of the
make sure it equals the number of CPU cores on your computer. If you are "Threads" UCI parameter, and to make sure it equals the number of CPU
using more than eight threads, it is recommended to raise the value of cores on your computer. If you are using more than four threads, it
"Min Split Depth" UCI parameter to 7. is recommended to raise the value of "Minimum Split Depth" UCI parameter
to 6.
2. Files 2. Files
@@ -28,9 +30,9 @@ This distribution of Stockfish consists of the following files:
License. License.
* src/, a subdirectory containing the full source code, including a * src/, a subdirectory containing the full source code, including a
Makefile that can be used to compile Stockfish on Unix-like systems. Makefile that can be used to compile Stockfish on Unix-like
For further information about how to compile Stockfish yourself systems. For further information about how to compile Stockfish
read section 4 below. yourself, read section 4 below.
* polyglot.ini, for using Stockfish with Fabien Letouzey's PolyGlot * polyglot.ini, for using Stockfish with Fabien Letouzey's PolyGlot
adapter. adapter.
@@ -39,25 +41,28 @@ This distribution of Stockfish consists of the following files:
3. Opening books 3. Opening books
---------------- ----------------
This version of Stockfish has support for PolyGlot opening books. This version of Stockfish has experimental support for PolyGlot opening
For information about how to create such books, consult the PolyGlot books. For information about how to create such books, consult the
documentation. The book file can be selected by setting the UCI PolyGlot documentation. The book file can be selected by setting the
parameter "Book File". UCI parameter "Book File".
4. Compiling it yourself 4. Compiling it yourself
------------------------ ------------------------
On Unix-like systems, it should usually be possible to compile Stockfish On Unix-like systems, it should usually be possible to compile
directly from the source code with the included Makefile. Stockfish directly from the source code with the included Makefile.
Stockfish has support for 32 or 64 bits CPUS, big-endian machines, like For big-endian machines like Power PC you need to enable the proper
Power PC, hardware POPCNT instruction and other platforms. flag changing from -DNBIGENDIAN to -DBIGENDIAN in the Makefile.
In general is recommended to run 'make help' to see a list of make targets Stockfish has POPCNT instruction runtime detection and support. This can
with corresponding descriptions. When not using Makefile to compile, for give an extra speed on Core i7 or similar systems. To enable this feature
instance with Microsoft MSVC, you need to manually set/unset in the compiler compile with 'make icc-profile-popcnt'
command line some swicthes, see file types.h for a quick reference.
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 5. Terms of use
@@ -77,3 +82,9 @@ 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 For full details, read the copy of the GPL found in the file named
Copying.txt. Copying.txt.
6. Feedback
-----------
The author's e-mail address is mcostalba@gmail.com
+40 -20
View File
@@ -7,7 +7,7 @@ EngineCommand = ./stockfish
Book = false Book = false
BookFile = book.bin BookFile = book.bin
Log = false Log = true
LogFile = stockfish.log LogFile = stockfish.log
Resign = true Resign = true
@@ -15,29 +15,49 @@ ResignScore = 600
[Engine] [Engine]
Use Search Log = false Hash = 128
Search Log Filename = SearchLog.txt Threads = 1
OwnBook = false
Book File = book.bin Book File = book.bin
Best Book Move = false Use Search Log = false
Mobility (Middle Game) = 100 Mobility (Middle Game) = 100
Mobility (Endgame) = 100 Mobility (Endgame) = 100
Pawn Structure (Middle Game) = 100
Pawn Structure (Endgame) = 100
Passed Pawns (Middle Game) = 100 Passed Pawns (Middle Game) = 100
Passed Pawns (Endgame) = 100 Passed Pawns (Endgame) = 100
Space = 100
Aggressiveness = 100 Aggressiveness = 100
Cowardice = 100 Cowardice = 100
Min Split Depth = 4 King Safety Curve = Quadratic
Max Threads per Split Point = 5 Quadratic = Linear
Threads = 1 King Safety Coefficient = 40
Use Sleeping Threads = false King Safety X Intercept = 0
Hash = 128 King Safety Max Slope = 30
Ponder = true King Safety Max Value = 500
OwnBook = false Queen Contact Check Bonus = 3
MultiPV = 1 Queen Check Bonus = 2
Skill Level = 20 Rook Check Bonus = 1
Emergency Move Horizon = 40 Bishop Check Bonus = 1
Emergency Base Time = 200 Knight Check Bonus = 1
Emergency Move Time = 70 Discovered Check Bonus = 3
Minimum Thinking Time = 20 Mate Threat Bonus = 3
UCI_Chess960 = false Check Extension (PV nodes) = 2
UCI_AnalyseMode = false 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>.
+258 -482
View File
@@ -1,6 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad # Copyright (C) 2008 Marco Costalba
# 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
@@ -16,523 +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
### ==========================================================================
### Executable name
EXE = stockfish EXE = stockfish
### Installation dir definitions
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
### Built-in benchmark for pgo-builds ### ==========================================================================
### 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
ICCFLAGS = -fast -msse
ICCFLAGS-OSX = -fast -mdynamic-no-pic
### ==========================================================================
### Enable/disable debugging, disabled by default
### ==========================================================================
GCCFLAGS += -DNDEBUG
ICCFLAGS += -DNDEBUG
ICCFLAGS-OSX += -DNDEBUG
### ==========================================================================
### 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 PGOBENCH = ./$(EXE) bench 32 1 10 default depth
### Object files
OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \
material.o misc.o move.o movegen.o movepick.o pawns.o position.o \
search.o thread.o timeman.o tt.o uci.o ucioption.o
### ========================================================================== ### General compiler settings. Do not change
### Section 2. High-level Configuration 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
# flag --- Comp switch --- Description
# ----------------------------------------------------------------------------
#
# debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode
# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations
# arch = (name) --- (-arch) --- Target architecture
# os = (name) --- --- Target operating system
# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
# bigendian = yes/no --- -DBIGENDIAN --- big/little-endian byte order
# 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
#
# 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
debug = no
optimize = yes
### 2.2 Architecture specific
# General-section
ifeq ($(ARCH),general-64)
arch = any
os = any
bits = 64
bigendian = no
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),general-32)
arch = any
os = any
bits = 32
bigendian = no
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),bigendian-64)
arch = any
os = any
bits = 64
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),bigendian-32)
arch = any
os = any
bits = 32
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
# x86-section
ifeq ($(ARCH),x86-64)
arch = x86_64
os = any
bits = 64
bigendian = no
prefetch = yes
bsfq = yes
popcnt = no
endif
ifeq ($(ARCH),x86-64-modern)
arch = x86_64
os = any
bits = 64
bigendian = no
prefetch = yes
bsfq = yes
popcnt = yes
endif
ifeq ($(ARCH),x86-32)
arch = i386
os = any
bits = 32
bigendian = no
prefetch = yes
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),x86-32-old)
arch = i386
os = any
bits = 32
bigendian = no
prefetch = no
bsfq = no
popcnt = no
endif
# osx-section
ifeq ($(ARCH),osx-ppc-64)
arch = ppc64
os = osx
bits = 64
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),osx-ppc-32)
arch = ppc
os = osx
bits = 32
bigendian = yes
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),osx-x86-64)
arch = x86_64
os = osx
bits = 64
bigendian = no
prefetch = yes
bsfq = yes
popcnt = no
endif
ifeq ($(ARCH),osx-x86-32)
arch = i386
os = osx
bits = 32
bigendian = no
prefetch = yes
bsfq = no
popcnt = no
endif
### ========================================================================== ### General linker settings. Do not change
### Section 3. Low-level configuration LDFLAGS = -lpthread
### ==========================================================================
### 3.1 Selecting compiler (default = gcc)
ifeq ($(COMP),)
COMP=gcc
endif
ifeq ($(COMP),mingw) ### Object files. Do not change
comp=mingw OBJS = application.o bitboard.o pawns.o material.o endgame.o evaluate.o main.o \
CXX=g++ misc.o move.o movegen.o history.o movepick.o search.o piece.o \
profile_prepare = gcc-profile-prepare position.o direction.o tt.o value.o uci.o ucioption.o \
profile_make = gcc-profile-make mersenne.o book.o bitbase.o san.o benchmark.o
profile_use = gcc-profile-use
profile_clean = gcc-profile-clean
endif
ifeq ($(COMP),gcc)
comp=gcc
CXX=g++
profile_prepare = gcc-profile-prepare
profile_make = gcc-profile-make
profile_use = gcc-profile-use
profile_clean = gcc-profile-clean
endif
ifeq ($(COMP),icc) ### General rules. Do not change
comp=icc default:
CXX=icpc $(MAKE) gcc
profile_prepare = icc-profile-prepare
profile_make = icc-profile-make
profile_use = icc-profile-use
profile_clean = icc-profile-clean
endif
### 3.2 General compiler settings
CXXFLAGS = -g -Wall -Wcast-qual -fno-exceptions -fno-rtti $(EXTRACXXFLAGS)
ifeq ($(comp),gcc)
CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra -Wshadow
endif
ifeq ($(comp),mingw)
CXXFLAGS += -Wextra -Wshadow
endif
ifeq ($(comp),icc)
CXXFLAGS += -wd383,981,1418,1419,10187,10188,11505,11503 -Wcheck -Wabi -Wdeprecated -strict-ansi
endif
ifeq ($(os),osx)
CXXFLAGS += -arch $(arch)
endif
### 3.3 General linker settings
LDFLAGS = -lpthread $(EXTRALDFLAGS)
ifeq ($(os),osx)
LDFLAGS += -arch $(arch)
endif
### 3.4 Debugging
ifeq ($(debug),no)
CXXFLAGS += -DNDEBUG
endif
### 3.5 Optimization
ifeq ($(optimize),yes)
ifeq ($(comp),gcc)
CXXFLAGS += -O3
ifeq ($(os),osx)
ifeq ($(arch),i386)
CXXFLAGS += -mdynamic-no-pic
endif
ifeq ($(arch),x86_64)
CXXFLAGS += -mdynamic-no-pic
endif
endif
endif
ifeq ($(comp),mingw)
CXXFLAGS += -O3
endif
ifeq ($(comp),icc)
ifeq ($(os),osx)
CXXFLAGS += -fast -mdynamic-no-pic
else
CXXFLAGS += -O3
endif
endif
endif
### 3.6. Bits
ifeq ($(bits),64)
CXXFLAGS += -DIS_64BIT
endif
### 3.7 Endianess
ifeq ($(bigendian),yes)
CXXFLAGS += -DBIGENDIAN
endif
### 3.8 prefetch
ifeq ($(prefetch),yes)
CXXFLAGS += -msse
DEPENDFLAGS += -msse
else
CXXFLAGS += -DNO_PREFETCH
endif
### 3.9 bsfq
ifeq ($(bsfq),yes)
CXXFLAGS += -DUSE_BSFQ
endif
### 3.10 popcnt
ifeq ($(popcnt),yes)
CXXFLAGS += -msse3 -DUSE_POPCNT
endif
### 3.11 Link Time Optimization, it works since gcc 4.5 but not on mingw.
### This is a mix of compile and link time options because the lto link phase
### needs access to the optimization flags.
ifeq ($(comp),gcc)
GCC_MAJOR := `gcc -dumpversion | cut -f1 -d.`
GCC_MINOR := `gcc -dumpversion | cut -f2 -d.`
ifeq (1,$(shell expr \( $(GCC_MAJOR) \> 4 \) \| \( $(GCC_MAJOR) \= 4 \& $(GCC_MINOR) \>= 5 \)))
CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS)
endif
endif
### ==========================================================================
### Section 4. Public targets
### ==========================================================================
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 > Build unoptimized version" @echo "make icc-profile-popcnt > Compiler = icpc + automatic pgo-build + popcnt-support"
@echo "profile-build > Build PGO-optimized version" @echo "make osx-ppc32 > PPC-Mac OS X 32 bit. Compiler = g++"
@echo "double-profile-build > Build PGO-optimized version with and without popcnt support" @echo "make osx-ppc64 > PPC-Mac OS X 64 bit. Compiler = g++"
@echo "strip > Strip executable" @echo "make osx-x86 > x86-Mac OS X 32 bit. Compiler = g++"
@echo "install > Install executable" @echo "make osx-x86_64 > x86-Mac OS X 64 bit. Compiler = g++"
@echo "clean > Clean up" @echo "make osx-icc32 > x86-Mac OS X 32 bit. Compiler = icpc"
@echo "testrun > Make sample run" @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 "Supported archs:" @echo "make osx-icc64-profile > OSX 64 bit. Compiler = icpc + automatic pgo-build"
@echo "" @echo "make strip > Strip executable"
@echo "x86-64 > x86 64-bit" @echo "make clean > Clean up"
@echo "x86-64-modern > x86 64-bit with runtime support for popcnt instruction"
@echo "x86-32 > x86 32-bit excluding very old hardware without SSE-support"
@echo "x86-32-old > x86 32-bit including also very old hardware"
@echo "osx-ppc-64 > PPC-Mac OS X 64 bit"
@echo "osx-ppc-32 > PPC-Mac OS X 32 bit"
@echo "osx-x86-64 > x86-Mac OS X 64 bit"
@echo "osx-x86-32 > x86-Mac OS X 32 bit"
@echo "general-64 > unspecified 64-bit"
@echo "general-32 > unspecified 32-bit"
@echo "bigendian-64 > unspecified 64-bit with bigendian byte order"
@echo "bigendian-32 > unspecified 32-bit with bigendian byte order"
@echo ""
@echo "Supported comps:"
@echo ""
@echo "gcc > Gnu compiler (default)"
@echo "icc > Intel compiler"
@echo "mingw > Gnu compiler with MinGW under Windows"
@echo ""
@echo "Non-standard targets:"
@echo ""
@echo "make hpux > Compile for HP-UX. Compiler = aCC"
@echo ""
@echo "Examples. If you don't know what to do, you likely want to run: "
@echo ""
@echo "make profile-build ARCH=x86-64 (This is for 64-bit systems)"
@echo "make profile-build ARCH=x86-32 (This is for 32-bit systems)"
@echo "" @echo ""
build: all: $(EXE) .depend
$(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)
@echo ""
@echo "Step 0/4. Preparing for profile build."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_prepare)
@echo ""
@echo "Step 1/4. Building executable for benchmark ..."
@touch *.cpp *.h
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make)
@echo ""
@echo "Step 2/4. Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 3/4. Building final executable ..."
@touch *.cpp
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use)
@echo ""
@echo "Step 4/4. Deleting profile data ..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean)
double-profile-build:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity ### Possible targets. You may add your own ones here
@echo "" gcc:
@echo "Step 0/6. Preparing for profile build." $(MAKE) \
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_prepare) CXX='g++' \
@echo "" CXXFLAGS="$(GCCFLAGS)" \
@echo "Step 1/6. Building executable for benchmark (popcnt disabled)..." 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 @touch *.cpp *.h
$(MAKE) ARCH=x86-64 COMP=$(COMP) $(profile_make) $(MAKE) icc-profile-make
@echo "" @echo ""
@echo "Step 2/6. Running benchmark for pgo-build (popcnt disabled)..." @echo "Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null @$(PGOBENCH) > /dev/null
@echo "Benchmark finished. Build final executable now ..."
@echo "" @echo ""
@echo "Step 3/6. Building executable for benchmark (popcnt enabled)..."
@touch *.cpp *.h @touch *.cpp *.h
$(MAKE) ARCH=x86-64-modern COMP=$(COMP) $(profile_make) $(MAKE) icc-profile-use
@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 4/6. Running benchmark for pgo-build (popcnt enabled)..." @echo "Running benchmark for pgo-build (popcnt disabled)..."
@$(PGOBENCH) > /dev/null @$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 5/6. Building final executable ..."
@touch *.cpp *.h @touch *.cpp *.h
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use) $(MAKE) icc-profile-make-with-popcnt
@echo "" @echo ""
@echo "Step 6/6. Deleting profile data ..." @echo "Running benchmark for pgo-build (popcnt enabled)..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean) @$(PGOBENCH) > /dev/null
@echo "Benchmarks finished. Build final executable now ..."
@echo "" @echo ""
@touch *.cpp *.h
$(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
testrun:
@$(PGOBENCH)
default:
help
### ==========================================================================
### Section 5. Private targets
### ==========================================================================
all: $(EXE) .depend
config-sanity:
@echo ""
@echo "Config:"
@echo "debug: '$(debug)'"
@echo "optimize: '$(optimize)'"
@echo "arch: '$(arch)'"
@echo "os: '$(os)'"
@echo "bits: '$(bits)'"
@echo "bigendian: '$(bigendian)'"
@echo "prefetch: '$(prefetch)'"
@echo "bsfq: '$(bsfq)'"
@echo "popcnt: '$(popcnt)'"
@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 "$(os)" = "any" || test "$(os)" = "osx"
@test "$(bits)" = "32" || test "$(bits)" = "64"
@test "$(bigendian)" = "yes" || test "$(bigendian)" = "no"
@test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
@test "$(bsfq)" = "yes" || test "$(bsfq)" = "no"
@test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
@test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw"
### 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' \
EXTRALDFLAGS='-lgcov' \
all
gcc-profile-clean:
@rm -rf *.gcda *.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
### ==========================================================================
### Section 6. Non-standard targets
### ==========================================================================
hpux:
$(MAKE) \
CXX='/opt/aCC/bin/aCC -AA +hpxstd98 -DBIGENDIAN -mt +O3 -DNDEBUG -DNO_PREFETCH' \
CXXFLAGS="" \
LDFLAGS="" \
all
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
}
+16 -16
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,23 +17,23 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(TIMEMAN_H_INCLUDED)
#define TIMEMAN_H_INCLUDED
/// The TimeManager class computes the optimal time to think depending on the #if !defined(APPLICATION_H_INCLUDED)
/// maximum available time, the move game number and other parameters. #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();
class TimeManager {
public: public:
void init(const Search::LimitsType& limits, int currentPly); static void initialize();
void pv_instability(int curChanges, int prevChanges); static void exit_with_failure();
int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
int maximum_time() const { return maximumSearchTime; }
private:
int optimumSearchTime;
int maximumSearchTime;
int unstablePVExtraTime;
}; };
#endif // !defined(TIMEMAN_H_INCLUDED) #endif // !defined(APPLICATION_H_INCLUDED)
+116 -70
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,23 +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 <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 "ucioption.h" #include "ucioption.h"
using namespace std; using namespace std;
using namespace Search;
static const char* Defaults[] = { ////
//// Variables
////
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",
@@ -45,91 +51,131 @@ static const char* 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"
}; };
////
//// 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 12), 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 secs or number of nodes. /// The analysis is written to a file named bench.txt.
void benchmark(int argc, char* argv[]) { void benchmark(const string& commandLine) {
vector<string> fens; istringstream csVal(commandLine);
LimitsType limits; istringstream csStr(commandLine);
int time; string ttSize, threads, fileName, limitType, timFile;
int64_t nodes = 0; int val, secsPerPos, maxDepth, maxNodes;
// Assign default values to missing arguments csStr >> ttSize;
string ttSize = argc > 2 ? argv[2] : "128"; csVal >> val;
string threads = argc > 3 ? argv[3] : "1"; if (val < 4 || val > 1024)
string valStr = argc > 4 ? argv[4] : "12"; {
string fenFile = argc > 5 ? argv[5] : "default"; cerr << "The hash table size must be between 4 and 1024" << endl;
string valType = argc > 6 ? argv[6] : "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;
Options["OwnBook"] = false; csVal >> limitType;
csVal >> timFile;
if (valType == "time") secsPerPos = maxDepth = maxNodes = 0;
limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms
else if (valType == "nodes")
limits.maxNodes = atoi(valStr.c_str());
if (limitType == "time")
secsPerPos = val * 1000;
else if (limitType == "depth" || limitType == "perft")
maxDepth = val;
else else
limits.maxDepth = atoi(valStr.c_str()); maxNodes = val;
if (fenFile != "default") vector<string> positions;
if (fileName != "default")
{ {
string fen; ifstream fenFile(fileName.c_str());
ifstream file(fenFile.c_str()); if (!fenFile.is_open())
if (!file.is_open())
{ {
cerr << "Unable to open file " << fenFile << endl; cerr << "Unable to open positions file " << fileName << endl;
exit(EXIT_FAILURE); Application::exit_with_failure();
} }
string pos;
while (getline(file, fen)) while (fenFile.good())
if (!fen.empty())
fens.push_back(fen);
file.close();
}
else
fens.assign(Defaults, Defaults + 16);
time = system_time();
for (size_t i = 0; i < fens.size(); i++)
{ {
Position pos(fens[i], false, 0); 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]));
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; ofstream timingFile;
if (!timFile.empty())
if (valType == "perft")
{ {
int64_t cnt = perft(pos, limits.maxDepth * ONE_PLY); timingFile.open(timFile.c_str(), ios::out | ios::app);
cerr << "\nPerft " << limits.maxDepth << " leaf nodes: " << cnt << endl; if (!timingFile.is_open())
nodes += cnt;
}
else
{ {
Threads.start_thinking(pos, limits, vector<Move>(), false); cerr << "Unable to open timing file " << timFile << endl;
nodes += RootPosition.nodes_searched(); Application::exit_with_failure();
} }
} }
time = system_time() - time; vector<string>::iterator it;
int cnt = 1;
int64_t totalNodes = 0;
int startTime = get_system_time();
cerr << "\n===========================" for (it = positions.begin(); it != positions.end(); ++it, ++cnt)
<< "\nTotal time (ms) : " << time {
<< "\nNodes searched : " << nodes Move moves[1] = {MOVE_NONE};
<< "\nNodes/second : " << int(nodes / (time / 1000.0)) << endl; int dummy[2] = {0, 0};
Position pos(*it);
cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl;
if (limitType == "perft")
totalNodes += perft(pos, maxDepth * OnePly);
else if (!think(pos, false, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
break;
totalNodes += nodes_searched();
}
cnt = get_system_time() - startTime;
cerr << "==============================="
<< "\nTotal time (ms) : " << cnt
<< "\nNodes searched : " << totalNodes
<< "\nNodes/second : " << (int)(totalNodes/(cnt/1000.0)) << endl << endl;
if (!timFile.empty())
{
timingFile << cnt << endl << endl;
timingFile.close();
}
// Under MS Visual C++ debug window always unconditionally closes
// when program exits, this is bad because we want to read results before.
#if (defined(WINDOWS) || defined(WIN32) || defined(WIN64))
cerr << "Press any key to exit" << endl;
cin >> fileName;
#endif
} }
+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)
+238 -159
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,10 +17,22 @@
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 "bitbase.h"
#include "bitboard.h" #include "bitboard.h"
#include "types.h" #include "move.h"
#include "square.h"
////
//// Local definitions
////
namespace { namespace {
@@ -28,244 +40,311 @@ namespace {
RESULT_UNKNOWN, RESULT_UNKNOWN,
RESULT_INVALID, RESULT_INVALID,
RESULT_WIN, RESULT_WIN,
RESULT_LOSS,
RESULT_DRAW RESULT_DRAW
}; };
struct KPKPosition { struct KPKPosition {
Result classify_knowns(int index);
Result classify(int index, Result db[]);
private:
void from_index(int index); void from_index(int index);
Result classify_white(const Result db[]); int to_index() const;
Result classify_black(const Result db[]); bool is_legal() const;
Bitboard wk_attacks() const { return StepAttacksBB[W_KING][whiteKingSquare]; } bool is_immediate_draw() const;
Bitboard bk_attacks() const { return StepAttacksBB[B_KING][blackKingSquare]; } bool is_immediate_win() const;
Bitboard pawn_attacks() const { return StepAttacksBB[W_PAWN][pawnSquare]; } Bitboard wk_attacks() const;
Bitboard bk_attacks() const;
Bitboard pawn_attacks() const;
Square whiteKingSquare, blackKingSquare, pawnSquare; Square whiteKingSquare, blackKingSquare, pawnSquare;
Color sideToMove; Color sideToMove;
}; };
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7
const int IndexMax = 2 * 24 * 64 * 64; // color * wp_sq * wk_sq * bk_sq = 196608
// Each uint32_t stores results of 32 positions, one per bit Result *Bitbase;
uint32_t KPKBitbase[IndexMax / 32]; const int IndexMax = 2*24*64*64;
int UnknownCount = 0;
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);
int compute_index(Square wksq, Square bksq, Square wpsq, Color stm);
} }
uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm) { ////
//// Functions
////
int index = compute_index(wksq, bksq, wpsq, stm); void generate_kpk_bitbase(uint8_t bitbase[]) {
// Allocate array and initialize:
Bitbase = new Result[IndexMax];
initialize();
return KPKBitbase[index / 32] & (1 << (index & 31)); // Iterate until all positions are classified:
while(next_iteration());
// Compress bitbase into the supplied parameter:
int i, j, b;
for(i = 0; i < 24576; i++) {
for(b = 0, j = 0; j < 8; b |= (compress_result(Bitbase[8*i+j]) << j), j++);
assert(b == int(uint8_t(b)));
bitbase[i] = (uint8_t)b;
} }
// Release allocated memory:
void kpk_bitbase_init() { delete [] Bitbase;
Result db[IndexMax];
KPKPosition pos;
int index, bit, repeat = 1;
// Initialize table
for (index = 0; index < IndexMax; index++)
db[index] = pos.classify_knowns(index);
// Iterate until all positions are classified (30 cycles needed)
while (repeat)
for (repeat = index = 0; index < IndexMax; index++)
if ( db[index] == RESULT_UNKNOWN
&& pos.classify(index, db) != RESULT_UNKNOWN)
repeat = 1;
// Map 32 position results into one KPKBitbase[] entry
for (index = 0; index < IndexMax / 32; index++)
for (bit = 0; bit < 32; bit++)
if (db[32 * index + bit] == RESULT_WIN)
KPKBitbase[index] |= (1 << bit);
} }
namespace { namespace {
// A KPK bitbase index is an integer in [0, IndexMax] range
//
// Information is mapped in this way
//
// bit 0: side to move (WHITE or BLACK)
// bit 1- 6: black king square (from SQ_A1 to SQ_H8)
// bit 7-12: white king square (from SQ_A1 to SQ_H8)
// bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn rank - 1 (from RANK_2 - 1 to RANK_7 - 1)
int compute_index(Square wksq, Square bksq, Square wpsq, Color stm) {
assert(file_of(wpsq) <= FILE_D);
int p = file_of(wpsq) + 4 * (rank_of(wpsq) - 1);
int r = stm + 2 * bksq + 128 * wksq + 8192 * p;
assert(r >= 0 && r < IndexMax);
return r;
}
void KPKPosition::from_index(int index) { void KPKPosition::from_index(int index) {
int s;
int s = index >> 13; sideToMove = Color(index % 2);
sideToMove = Color(index & 1); blackKingSquare = Square((index / 2) % 64);
blackKingSquare = Square((index >> 1) & 63); whiteKingSquare = Square((index / 128) % 64);
whiteKingSquare = Square((index >> 7) & 63); s = (index / 8192) % 24;
pawnSquare = make_square(File(s & 3), Rank((s >> 2) + 1)); pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1));
} }
Result KPKPosition::classify_knowns(int index) {
from_index(index); int KPKPosition::to_index() const {
return compute_index(whiteKingSquare, blackKingSquare, pawnSquare,
sideToMove);
}
// Check if two pieces are on the same square
if ( whiteKingSquare == pawnSquare
|| whiteKingSquare == blackKingSquare
|| blackKingSquare == pawnSquare)
return RESULT_INVALID;
// Check if a king can be captured bool KPKPosition::is_legal() const {
if ( bit_is_set(wk_attacks(), blackKingSquare) if(whiteKingSquare == pawnSquare || whiteKingSquare == blackKingSquare ||
|| (bit_is_set(pawn_attacks(), blackKingSquare) && sideToMove == WHITE)) pawnSquare == blackKingSquare)
return RESULT_INVALID; 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;
}
// The position is an immediate win if it is white to move and the
// white pawn can be promoted without getting captured.
if ( rank_of(pawnSquare) == RANK_7
&& sideToMove == WHITE
&& whiteKingSquare != pawnSquare + DELTA_N
&& ( square_distance(blackKingSquare, pawnSquare + DELTA_N) > 1
|| bit_is_set(wk_attacks(), pawnSquare + DELTA_N)))
return RESULT_WIN;
// Check for known draw positions bool KPKPosition::is_immediate_draw() const {
// if(sideToMove == BLACK) {
Bitboard wka = this->wk_attacks();
Bitboard bka = this->bk_attacks();
// Case 1: Stalemate // Case 1: Stalemate
if ( sideToMove == BLACK if((bka & ~(wka | this->pawn_attacks())) == EmptyBoardBB)
&& !(bk_attacks() & ~(wk_attacks() | pawn_attacks()))) return true;
return RESULT_DRAW;
// Case 2: King can capture pawn // Case 2: King can capture pawn
if ( sideToMove == BLACK if(bit_is_set(bka, pawnSquare) && !bit_is_set(wka, pawnSquare))
&& bit_is_set(bk_attacks(), pawnSquare) && !bit_is_set(wk_attacks(), pawnSquare)) return true;
return RESULT_DRAW; }
else {
// Case 3: Black king in front of white pawn // Case 1: Stalemate
if ( blackKingSquare == pawnSquare + DELTA_N if(whiteKingSquare == SQ_A8 && pawnSquare == SQ_A7 &&
&& rank_of(pawnSquare) < RANK_7) (blackKingSquare == SQ_C7 || blackKingSquare == SQ_C8))
return RESULT_DRAW; return true;
// Case 4: White king in front of pawn and black has opposition
if ( whiteKingSquare == pawnSquare + DELTA_N
&& blackKingSquare == pawnSquare + DELTA_N + DELTA_N + DELTA_N
&& rank_of(pawnSquare) < RANK_5
&& sideToMove == WHITE)
return RESULT_DRAW;
// Case 5: Stalemate with rook pawn
if ( blackKingSquare == SQ_A8
&& file_of(pawnSquare) == FILE_A)
return RESULT_DRAW;
return RESULT_UNKNOWN;
} }
Result KPKPosition::classify(int index, Result db[]) { return false;
from_index(index);
db[index] = (sideToMove == WHITE ? classify_white(db) : classify_black(db));
return db[index];
} }
Result KPKPosition::classify_white(const Result db[]) {
// If one move leads to a position classified as RESULT_WIN, the result 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 // of the current position is RESULT_WIN. If all moves lead to positions
// classified as RESULT_DRAW, the current position is classified RESULT_DRAW // classified as RESULT_DRAW, the current position is classified as
// otherwise the current position is classified as RESULT_UNKNOWN. // RESULT_DRAW. Otherwise, the current position is classified as
// RESULT_UNKNOWN.
bool unknownFound = false; bool unknownFound = false;
Bitboard b; Bitboard b;
Square s; Square s;
Result r;
// King moves // King moves
b = wk_attacks(); b = p.wk_attacks();
while (b) while(b) {
{
s = pop_1st_bit(&b); s = pop_1st_bit(&b);
r = db[compute_index(s, blackKingSquare, pawnSquare, BLACK)]; switch(Bitbase[compute_index(s, p.blackKingSquare, p.pawnSquare,
BLACK)]) {
if (r == RESULT_WIN) case RESULT_LOSS:
return RESULT_WIN; return RESULT_WIN;
if (r == RESULT_UNKNOWN) case RESULT_UNKNOWN:
unknownFound = true; unknownFound = true;
break;
case RESULT_DRAW: case RESULT_INVALID:
break;
default:
assert(false);
}
} }
// Pawn moves // Pawn moves
if (rank_of(pawnSquare) < RANK_7) if(square_rank(p.pawnSquare) < RANK_7) {
{ s = p.pawnSquare + DELTA_N;
s = pawnSquare + DELTA_N; switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s,
r = db[compute_index(whiteKingSquare, blackKingSquare, s, BLACK)]; BLACK)]) {
case RESULT_LOSS:
if (r == RESULT_WIN)
return RESULT_WIN; return RESULT_WIN;
if (r == RESULT_UNKNOWN) case RESULT_UNKNOWN:
unknownFound = true; unknownFound = true;
break;
// Double pawn push case RESULT_DRAW: case RESULT_INVALID:
if (rank_of(s) == RANK_3 && r != RESULT_INVALID) break;
{
default:
assert(false);
}
if(square_rank(s) == RANK_3 &&
s != p.whiteKingSquare && s != p.blackKingSquare) {
s += DELTA_N; s += DELTA_N;
r = db[compute_index(whiteKingSquare, blackKingSquare, s, BLACK)]; switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s,
BLACK)]) {
if (r == RESULT_WIN) case RESULT_LOSS:
return RESULT_WIN; return RESULT_WIN;
if (r == RESULT_UNKNOWN) case RESULT_UNKNOWN:
unknownFound = true; unknownFound = true;
break;
case RESULT_DRAW: case RESULT_INVALID:
break;
default:
assert(false);
} }
} }
}
return unknownFound? RESULT_UNKNOWN : RESULT_DRAW; return unknownFound? RESULT_UNKNOWN : RESULT_DRAW;
} }
Result KPKPosition::classify_black(const Result db[]) {
Result classify_btm(const KPKPosition &p) {
// If one move leads to a position classified as RESULT_DRAW, the result // 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 // of the current position is RESULT_DRAW. If all moves lead to positions
// classified as RESULT_WIN, the position is classified as RESULT_WIN. // classified as RESULT_WIN, the current position is classified as
// Otherwise, the current position is classified as RESULT_UNKNOWN. // RESULT_LOSS. Otherwise, the current position is classified as
// RESULT_UNKNOWN.
bool unknownFound = false; bool unknownFound = false;
Bitboard b; Bitboard b;
Square s; Square s;
Result r;
// King moves // King moves
b = bk_attacks(); b = p.bk_attacks();
while (b) while(b) {
{
s = pop_1st_bit(&b); s = pop_1st_bit(&b);
r = db[compute_index(whiteKingSquare, s, pawnSquare, WHITE)]; switch(Bitbase[compute_index(p.whiteKingSquare, s, p.pawnSquare,
WHITE)]) {
if (r == RESULT_DRAW) case RESULT_DRAW:
return RESULT_DRAW; return RESULT_DRAW;
if (r == RESULT_UNKNOWN) case RESULT_UNKNOWN:
unknownFound = true; unknownFound = true;
break;
case RESULT_WIN: case RESULT_INVALID:
break;
default:
assert(false);
} }
return unknownFound ? RESULT_UNKNOWN : RESULT_WIN; }
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)
+413 -235
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,97 +17,318 @@
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 <cstring> ////
//// Includes
////
#include <iostream> #include <iostream>
#include "bitboard.h" #include "bitboard.h"
#include "bitcount.h" #include "bitcount.h"
#include "rkiss.h" #include "direction.h"
Bitboard RMasks[64];
Bitboard RMagics[64];
Bitboard* RAttacks[64];
int RShifts[64];
Bitboard BMasks[64]; #if defined(IS_64BIT)
Bitboard BMagics[64];
Bitboard* BAttacks[64]; const uint64_t BMult[64] = {
int BShifts[64]; 0x440049104032280ULL, 0x1021023c82008040ULL, 0x404040082000048ULL,
0x48c4440084048090ULL, 0x2801104026490000ULL, 0x4100880442040800ULL,
0x181011002e06040ULL, 0x9101004104200e00ULL, 0x1240848848310401ULL,
0x2000142828050024ULL, 0x1004024d5000ULL, 0x102044400800200ULL,
0x8108108820112000ULL, 0xa880818210c00046ULL, 0x4008008801082000ULL,
0x60882404049400ULL, 0x104402004240810ULL, 0xa002084250200ULL,
0x100b0880801100ULL, 0x4080201220101ULL, 0x44008080a00000ULL,
0x202200842000ULL, 0x5006004882d00808ULL, 0x200045080802ULL,
0x86100020200601ULL, 0xa802080a20112c02ULL, 0x80411218080900ULL,
0x200a0880080a0ULL, 0x9a01010000104000ULL, 0x28008003100080ULL,
0x211021004480417ULL, 0x401004188220806ULL, 0x825051400c2006ULL,
0x140c0210943000ULL, 0x242800300080ULL, 0xc2208120080200ULL,
0x2430008200002200ULL, 0x1010100112008040ULL, 0x8141050100020842ULL,
0x822081014405ULL, 0x800c049e40400804ULL, 0x4a0404028a000820ULL,
0x22060201041200ULL, 0x360904200840801ULL, 0x881a08208800400ULL,
0x60202c00400420ULL, 0x1204440086061400ULL, 0x8184042804040ULL,
0x64040315300400ULL, 0xc01008801090a00ULL, 0x808010401140c00ULL,
0x4004830c2020040ULL, 0x80005002020054ULL, 0x40000c14481a0490ULL,
0x10500101042048ULL, 0x1010100200424000ULL, 0x640901901040ULL,
0xa0201014840ULL, 0x840082aa011002ULL, 0x10010840084240aULL,
0x420400810420608ULL, 0x8d40230408102100ULL, 0x4a00200612222409ULL,
0xa08520292120600ULL
};
const uint64_t RMult[64] = {
0xa8002c000108020ULL, 0x4440200140003000ULL, 0x8080200010011880ULL,
0x380180080141000ULL, 0x1a00060008211044ULL, 0x410001000a0c0008ULL,
0x9500060004008100ULL, 0x100024284a20700ULL, 0x802140008000ULL,
0x80c01002a00840ULL, 0x402004282011020ULL, 0x9862000820420050ULL,
0x1001448011100ULL, 0x6432800200800400ULL, 0x40100010002000cULL,
0x2800d0010c080ULL, 0x90c0008000803042ULL, 0x4010004000200041ULL,
0x3010010200040ULL, 0xa40828028001000ULL, 0x123010008000430ULL,
0x24008004020080ULL, 0x60040001104802ULL, 0x582200028400d1ULL,
0x4000802080044000ULL, 0x408208200420308ULL, 0x610038080102000ULL,
0x3601000900100020ULL, 0x80080040180ULL, 0xc2020080040080ULL,
0x80084400100102ULL, 0x4022408200014401ULL, 0x40052040800082ULL,
0xb08200280804000ULL, 0x8a80a008801000ULL, 0x4000480080801000ULL,
0x911808800801401ULL, 0x822a003002001894ULL, 0x401068091400108aULL,
0x4a10a00004cULL, 0x2000800640008024ULL, 0x1486408102020020ULL,
0x100a000d50041ULL, 0x810050020b0020ULL, 0x204000800808004ULL,
0x20048100a000cULL, 0x112000831020004ULL, 0x9000040810002ULL,
0x440490200208200ULL, 0x8910401000200040ULL, 0x6404200050008480ULL,
0x4b824a2010010100ULL, 0x4080801810c0080ULL, 0x400802a0080ULL,
0x8224080110026400ULL, 0x40002c4104088200ULL, 0x1002100104a0282ULL,
0x1208400811048021ULL, 0x3201014a40d02001ULL, 0x5100019200501ULL,
0x101000208001005ULL, 0x2008450080702ULL, 0x1002080301d00cULL,
0x410201ce5c030092ULL
};
const int BShift[64] = {
58, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 59, 59,
59, 59, 57, 57, 57, 57, 59, 59, 59, 59, 57, 55, 55, 57, 59, 59,
59, 59, 57, 55, 55, 57, 59, 59, 59, 59, 57, 57, 57, 57, 59, 59,
59, 59, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 58
};
const int RShift[64] = {
52, 53, 53, 53, 53, 53, 53, 52, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 52, 53, 53, 53, 53, 53, 53, 52
};
#else // if !defined(IS_64BIT)
const uint64_t BMult[64] = {
0x54142844c6a22981ULL, 0x710358a6ea25c19eULL, 0x704f746d63a4a8dcULL,
0xbfed1a0b80f838c5ULL, 0x90561d5631e62110ULL, 0x2804260376e60944ULL,
0x84a656409aa76871ULL, 0xf0267f64c28b6197ULL, 0x70764ebb762f0585ULL,
0x92aa09e0cfe161deULL, 0x41ee1f6bb266f60eULL, 0xddcbf04f6039c444ULL,
0x5a3fab7bac0d988aULL, 0xd3727877fa4eaa03ULL, 0xd988402d868ddaaeULL,
0x812b291afa075c7cULL, 0x94faf987b685a932ULL, 0x3ed867d8470d08dbULL,
0x92517660b8901de8ULL, 0x2d97e43e058814b4ULL, 0x880a10c220b25582ULL,
0xc7c6520d1f1a0477ULL, 0xdbfc7fbcd7656aa6ULL, 0x78b1b9bfb1a2b84fULL,
0x2f20037f112a0bc1ULL, 0x657171ea2269a916ULL, 0xc08302b07142210eULL,
0x880a4403064080bULL, 0x3602420842208c00ULL, 0x852800dc7e0b6602ULL,
0x595a3fbbaa0f03b2ULL, 0x9f01411558159d5eULL, 0x2b4a4a5f88b394f2ULL,
0x4afcbffc292dd03aULL, 0x4a4094a3b3f10522ULL, 0xb06f00b491f30048ULL,
0xd5b3820280d77004ULL, 0x8b2e01e7c8e57a75ULL, 0x2d342794e886c2e6ULL,
0xc302c410cde21461ULL, 0x111f426f1379c274ULL, 0xe0569220abb31588ULL,
0x5026d3064d453324ULL, 0xe2076040c343cd8aULL, 0x93efd1e1738021eeULL,
0xb680804bed143132ULL, 0x44e361b21986944cULL, 0x44c60170ef5c598cULL,
0xf4da475c195c9c94ULL, 0xa3afbb5f72060b1dULL, 0xbc75f410e41c4ffcULL,
0xb51c099390520922ULL, 0x902c011f8f8ec368ULL, 0x950b56b3d6f5490aULL,
0x3909e0635bf202d0ULL, 0x5744f90206ec10ccULL, 0xdc59fd76317abbc1ULL,
0x881c7c67fcbfc4f6ULL, 0x47ca41e7e440d423ULL, 0xeb0c88112048d004ULL,
0x51c60e04359aef1aULL, 0x1aa1fe0e957a5554ULL, 0xdd9448db4f5e3104ULL,
0xdc01f6dca4bebbdcULL,
};
const uint64_t RMult[64] = {
0xd7445cdec88002c0ULL, 0xd0a505c1f2001722ULL, 0xe065d1c896002182ULL,
0x9a8c41e75a000892ULL, 0x8900b10c89002aa8ULL, 0x9b28d1c1d60005a2ULL,
0x15d6c88de002d9aULL, 0xb1dbfc802e8016a9ULL, 0x149a1042d9d60029ULL,
0xb9c08050599e002fULL, 0x132208c3af300403ULL, 0xc1000ce2e9c50070ULL,
0x9d9aa13c99020012ULL, 0xb6b078daf71e0046ULL, 0x9d880182fb6e002eULL,
0x52889f467e850037ULL, 0xda6dc008d19a8480ULL, 0x468286034f902420ULL,
0x7140ac09dc54c020ULL, 0xd76ffffa39548808ULL, 0xea901c4141500808ULL,
0xc91004093f953a02ULL, 0x2882afa8f6bb402ULL, 0xaebe335692442c01ULL,
0xe904a22079fb91eULL, 0x13a514851055f606ULL, 0x76c782018c8fe632ULL,
0x1dc012a9d116da06ULL, 0x3c9e0037264fffa6ULL, 0x2036002853c6e4a2ULL,
0xe3fe08500afb47d4ULL, 0xf38af25c86b025c2ULL, 0xc0800e2182cf9a40ULL,
0x72002480d1f60673ULL, 0x2500200bae6e9b53ULL, 0xc60018c1eefca252ULL,
0x600590473e3608aULL, 0x46002c4ab3fe51b2ULL, 0xa200011486bcc8d2ULL,
0xb680078095784c63ULL, 0x2742002639bf11aeULL, 0xc7d60021a5bdb142ULL,
0xc8c04016bb83d820ULL, 0xbd520028123b4842ULL, 0x9d1600344ac2a832ULL,
0x6a808005631c8a05ULL, 0x604600a148d5389aULL, 0xe2e40103d40dea65ULL,
0x945b5a0087c62a81ULL, 0x12dc200cd82d28eULL, 0x2431c600b5f9ef76ULL,
0xfb142a006a9b314aULL, 0x6870e00a1c97d62ULL, 0x2a9db2004a2689a2ULL,
0xd3594600caf5d1a2ULL, 0xee0e4900439344a7ULL, 0x89c4d266ca25007aULL,
0x3e0013a2743f97e3ULL, 0x180e31a0431378aULL, 0x3a9e465a4d42a512ULL,
0x98d0a11a0c0d9cc2ULL, 0x8e711c1aba19b01eULL, 0x8dcdc836dd201142ULL,
0x5ac08a4735370479ULL,
};
const int BShift[64] = {
26, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 25, 25, 25, 25, 27, 27, 27, 27, 25, 23, 23, 25, 27, 27,
27, 27, 25, 23, 23, 25, 27, 27, 27, 27, 25, 25, 25, 25, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 26
};
const int RShift[64] = {
20, 21, 21, 21, 21, 21, 21, 20, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 20, 21, 21, 21, 21, 21, 21, 20
};
#endif // defined(IS_64BIT)
const Bitboard SquaresByColorBB[2] = { BlackSquaresBB, WhiteSquaresBB };
const Bitboard FileBB[8] = {
FileABB, FileBBB, FileCBB, FileDBB, FileEBB, FileFBB, FileGBB, FileHBB
};
const Bitboard NeighboringFilesBB[8] = {
FileBBB, FileABB|FileCBB, FileBBB|FileDBB, FileCBB|FileEBB,
FileDBB|FileFBB, FileEBB|FileGBB, FileFBB|FileHBB, FileGBB
};
const Bitboard ThisAndNeighboringFilesBB[8] = {
FileABB|FileBBB, FileABB|FileBBB|FileCBB,
FileBBB|FileCBB|FileDBB, FileCBB|FileDBB|FileEBB,
FileDBB|FileEBB|FileFBB, FileEBB|FileFBB|FileGBB,
FileFBB|FileGBB|FileHBB, FileGBB|FileHBB
};
const Bitboard RankBB[8] = {
Rank1BB, Rank2BB, Rank3BB, Rank4BB, Rank5BB, Rank6BB, Rank7BB, Rank8BB
};
const Bitboard RelativeRankBB[2][8] = {
{ Rank1BB, Rank2BB, Rank3BB, Rank4BB, Rank5BB, Rank6BB, Rank7BB, Rank8BB },
{ Rank8BB, Rank7BB, Rank6BB, Rank5BB, Rank4BB, Rank3BB, Rank2BB, Rank1BB }
};
const Bitboard InFrontBB[2][8] = {
{ Rank2BB | Rank3BB | Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank3BB | Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank6BB | Rank7BB | Rank8BB,
Rank7BB | Rank8BB,
Rank8BB,
EmptyBoardBB
},
{ EmptyBoardBB,
Rank1BB,
Rank2BB | Rank1BB,
Rank3BB | Rank2BB | Rank1BB,
Rank4BB | Rank3BB | Rank2BB | Rank1BB,
Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB,
Rank6BB | Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB,
Rank7BB | Rank6BB | Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB
}
};
Bitboard RMask[64];
int RAttackIndex[64];
Bitboard RAttacks[0x19000];
Bitboard BMask[64];
int BAttackIndex[64];
Bitboard BAttacks[0x1480];
Bitboard SetMaskBB[65]; Bitboard SetMaskBB[65];
Bitboard ClearMaskBB[65]; Bitboard ClearMaskBB[65];
Bitboard FileBB[8]; Bitboard StepAttackBB[16][64];
Bitboard RankBB[8]; Bitboard RayBB[64][8];
Bitboard NeighboringFilesBB[8];
Bitboard ThisAndNeighboringFilesBB[8];
Bitboard InFrontBB[2][8];
Bitboard StepAttacksBB[16][64];
Bitboard BetweenBB[64][64]; Bitboard BetweenBB[64][64];
Bitboard SquaresInFrontMask[2][64];
Bitboard PassedPawnMask[2][64]; Bitboard PassedPawnMask[2][64];
Bitboard AttackSpanMask[2][64]; Bitboard OutpostMask[2][64];
Bitboard BishopPseudoAttacks[64]; Bitboard BishopPseudoAttacks[64];
Bitboard RookPseudoAttacks[64]; Bitboard RookPseudoAttacks[64];
Bitboard QueenPseudoAttacks[64]; Bitboard QueenPseudoAttacks[64];
uint8_t BitCount8Bit[256]; uint8_t BitCount8Bit[256];
int SquareDistance[64][64];
////
//// Local definitions
////
namespace { namespace {
CACHE_LINE_ALIGNMENT void init_masks();
void init_ray_bitboards();
int BSFTable[64]; void init_attacks();
Bitboard RookTable[0x19000]; // Storage space for rook attacks void init_between_bitboards();
Bitboard BishopTable[0x1480]; // Storage space for bishop attacks Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
int fmin, int fmax, int rmin, int rmax);
void init_magic_bitboards(PieceType pt, Bitboard* attacks[], Bitboard magics[], Bitboard index_to_bitboard(int index, Bitboard mask);
Bitboard masks[], int shifts[]); void init_sliding_attacks(Bitboard attacks[],
int attackIndex[], Bitboard mask[],
const int shift[2], const Bitboard mult[],
int deltas[][2]);
void init_pseudo_attacks();
} }
////
//// Functions
////
/// print_bitboard() prints a bitboard in an easily readable format to the /// print_bitboard() prints a bitboard in an easily readable format to the
/// standard output. This is sometimes useful for debugging. /// standard output. This is sometimes useful for debugging.
void print_bitboard(Bitboard b) { void print_bitboard(Bitboard b) {
for(Rank r = RANK_8; r >= RANK_1; r--) {
for (Rank r = RANK_8; r >= RANK_1; r--) std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
{
std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
for(File f = FILE_A; f <= FILE_H; f++) for(File f = FILE_A; f <= FILE_H; f++)
std::cout << "| " << (bit_is_set(b, make_square(f, r)) ? "X " : " "); std::cout << "| " << (bit_is_set(b, make_square(f, r))? 'X' : ' ') << ' ';
std::cout << "|" << std::endl;
std::cout << "|\n";
} }
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl; std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
} }
/// init_bitboards() initializes various bitboard arrays. It is called during
/// program initialization.
void init_bitboards() {
int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
init_masks();
init_ray_bitboards();
init_attacks();
init_between_bitboards();
init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas);
init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift, BMult, bishopDeltas);
init_pseudo_attacks();
}
/// first_1() finds the least significant nonzero bit in a nonzero 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 /// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard. /// nonzero bitboard.
#if defined(IS_64BIT) && !defined(USE_BSFQ) #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) { Square first_1(Bitboard b) {
return Square(BSFTable[((b & -b) * 0x218A392CD3D5DBFULL) >> 58]); return Square(BitTable[((b & -b) * 0x218a392cd3d5dbfULL) >> 58]);
} }
Square pop_1st_bit(Bitboard* b) { Square pop_1st_bit(Bitboard* b) {
Bitboard bb = *b; Bitboard bb = *b;
*b &= (*b - 1); *b &= (*b - 1);
return Square(BSFTable[((bb & -bb) * 0x218A392CD3D5DBFULL) >> 58]); return Square(BitTable[((bb & -bb) * 0x218a392cd3d5dbfULL) >> 58]);
} }
#elif !defined(USE_BSFQ) #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) { Square first_1(Bitboard b) {
b ^= (b - 1); b ^= (b - 1);
uint32_t fold = unsigned(b) ^ unsigned(b >> 32); uint32_t fold = int(b) ^ int(b >> 32);
return Square(BSFTable[(fold * 0x783A9B23) >> 26]); return Square(BitTable[(fold * 0x783a9b23) >> 26]);
} }
// Use type-punning // Use type-punning
@@ -134,233 +355,190 @@ Square pop_1st_bit(Bitboard* bb) {
if (u.dw.l) if (u.dw.l)
{ {
ret = Square(BSFTable[((u.dw.l ^ (u.dw.l - 1)) * 0x783A9B23) >> 26]); ret = Square(BitTable[((u.dw.l ^ (u.dw.l - 1)) * 0x783a9b23) >> 26]);
u.dw.l &= (u.dw.l - 1); u.dw.l &= (u.dw.l - 1);
*bb = u.b; *bb = u.b;
return ret; return ret;
} }
ret = Square(BSFTable[((~(u.dw.h ^ (u.dw.h - 1))) * 0x783A9B23) >> 26]); ret = Square(BitTable[((~(u.dw.h ^ (u.dw.h - 1))) * 0x783a9b23) >> 26]);
u.dw.h &= (u.dw.h - 1); u.dw.h &= (u.dw.h - 1);
*bb = u.b; *bb = u.b;
return ret; return ret;
} }
#endif // !defined(USE_BSFQ) #endif
int bitScanReverse32(uint32_t b)
/// bitboards_init() initializes various bitboard arrays. It is called during
/// program initialization.
void bitboards_init() {
for (Bitboard b = 0; b < 256; b++)
BitCount8Bit[b] = (uint8_t)popcount<Max15>(b);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{ {
SetMaskBB[s] = 1ULL << s; int result = 0;
ClearMaskBB[s] = ~SetMaskBB[s];
if (b > 0xFFFF) {
b >>= 16;
result += 16;
} }
if (b > 0xFF) {
ClearMaskBB[SQ_NONE] = ~0ULL; b >>= 8;
result += 8;
FileBB[FILE_A] = FileABB;
RankBB[RANK_1] = Rank1BB;
for (int f = FILE_B; f <= FILE_H; f++)
{
FileBB[f] = FileBB[f - 1] << 1;
RankBB[f] = RankBB[f - 1] << 8;
} }
if (b > 0xF) {
for (int f = FILE_A; f <= FILE_H; f++) b >>= 4;
{ result += 4;
NeighboringFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndNeighboringFilesBB[f] = FileBB[f] | NeighboringFilesBB[f];
} }
if (b > 0x3) {
for (int rw = RANK_7, rb = RANK_2; rw >= RANK_1; rw--, rb++) b >>= 2;
{ result += 2;
InFrontBB[WHITE][rw] = InFrontBB[WHITE][rw + 1] | RankBB[rw + 1];
InFrontBB[BLACK][rb] = InFrontBB[BLACK][rb - 1] | RankBB[rb - 1];
} }
return result + (b > 0) + (b > 1);
for (Color c = WHITE; c <= BLACK; c++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SquaresInFrontMask[c][s] = in_front_bb(c, s) & file_bb(s);
PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(file_of(s));
AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(file_of(s));
} }
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
for (int i = 0; i < 64; i++)
if (!Is64Bit) // Matt Taylor's folding trick for 32 bit systems
{
Bitboard b = 1ULL << i;
b ^= b - 1;
b ^= b >> 32;
BSFTable[uint32_t(b * 0x783A9B23) >> 26] = i;
}
else
BSFTable[((1ULL << i) * 0x218A392CD3D5DBFULL) >> 58] = i;
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 k = 0; steps[pt][k]; k++)
{
Square to = s + Square(c == WHITE ? steps[pt][k] : -steps[pt][k]);
if (square_is_ok(to) && square_distance(s, to) < 3)
set_bit(&StepAttacksBB[make_piece(c, pt)][s], to);
}
init_magic_bitboards(ROOK, RAttacks, RMagics, RMasks, RShifts);
init_magic_bitboards(BISHOP, BAttacks, BMagics, BMasks, BShifts);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
BishopPseudoAttacks[s] = bishop_attacks_bb(s, 0);
RookPseudoAttacks[s] = rook_attacks_bb(s, 0);
QueenPseudoAttacks[s] = queen_attacks_bb(s, 0);
}
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (bit_is_set(QueenPseudoAttacks[s1], s2))
{
Square delta = (s2 - s1) / square_distance(s1, s2);
for (Square s = s1 + delta; s != s2; s += delta)
set_bit(&BetweenBB[s1][s2], s);
}
}
namespace { namespace {
Bitboard sliding_attacks(PieceType pt, Square sq, Bitboard occupied) { // All functions below are used to precompute various bitboards during
// program initialization. Some of the functions may be difficult to
// understand, but they all seem to work correctly, and it should never
// be necessary to touch any of them.
Square deltas[][4] = { { DELTA_N, DELTA_E, DELTA_S, DELTA_W }, void init_masks() {
{ DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW } }; SetMaskBB[SQ_NONE] = 0ULL;
Bitboard attacks = 0; ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE];
Square* delta = (pt == ROOK ? deltas[0] : deltas[1]); for(Square s = SQ_A1; s <= SQ_H8; s++) {
SetMaskBB[s] = (1ULL << s);
for (int i = 0; i < 4; i++) ClearMaskBB[s] = ~SetMaskBB[s];
{
Square s = sq + delta[i];
while (square_is_ok(s) && square_distance(s, s - delta[i]) == 1)
{
set_bit(&attacks, s);
if (bit_is_set(occupied, s))
break;
s += delta[i];
} }
for(Color c = WHITE; c <= BLACK; c++)
for(Square s = SQ_A1; s <= SQ_H8; s++) {
PassedPawnMask[c][s] =
in_front_bb(c, s) & this_and_neighboring_files_bb(s);
OutpostMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
} }
return attacks;
for (Bitboard b = 0ULL; b < 256ULL; b++)
BitCount8Bit[b] = (uint8_t)count_1s(b);
} }
Bitboard pick_random(Bitboard mask, RKISS& rk, int booster) { void init_ray_bitboards() {
int d[8] = {1, -1, 16, -16, 17, -17, 15, -15};
for(int i = 0; i < 128; i = (i + 9) & ~8) {
for(int j = 0; j < 8; j++) {
RayBB[(i&7)|((i>>4)<<3)][j] = EmptyBoardBB;
for(int k = i + d[j]; (k & 0x88) == 0; k += d[j])
set_bit(&(RayBB[(i&7)|((i>>4)<<3)][j]), Square((k&7)|((k>>4)<<3)));
}
}
}
Bitboard magic;
// Values s1 and s2 are used to rotate the candidate magic of a void init_attacks() {
// quantity known to be the optimal to quickly find the magics. int i, j, k, l;
int s1 = booster & 63, s2 = (booster >> 6) & 63; 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}
};
while (true) for(i = 0; i < 64; i++) {
{ for(j = 0; j <= int(BK); j++) {
magic = rk.rand<Bitboard>(); StepAttackBB[j][i] = EmptyBoardBB;
magic = (magic >> s1) | (magic << (64 - s1)); for(k = 0; k < 8 && step[j][k] != 0; k++) {
magic &= rk.rand<Bitboard>(); l = i + step[j][k];
magic = (magic >> s2) | (magic << (64 - s2)); if(l >= 0 && l < 64 && abs((i&7) - (l&7)) < 3)
magic &= rk.rand<Bitboard>(); StepAttackBB[j][i] |= (1ULL << l);
}
if (BitCount8Bit[(mask * magic) >> 56] >= 6) }
return magic;
} }
} }
// init_magic_bitboards() computes all rook and bishop magics at startup. Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
// Magic bitboards are used to look up attacks of sliding pieces. As reference int fmin=0, int fmax=7, int rmin=0, int rmax=7) {
// see chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we Bitboard result = 0ULL;
// use the so called "fancy" approach. int rk = sq / 8, fl = sq % 8, r, f, i;
for(i = 0; i < dirs; i++) {
void init_magic_bitboards(PieceType pt, Bitboard* attacks[], Bitboard magics[], int dx = deltas[i][0], dy = deltas[i][1];
Bitboard masks[], int shifts[]) { for(f = fl+dx, r = rk+dy;
(dx==0 || (f>=fmin && f<=fmax)) && (dy==0 || (r>=rmin && r<=rmax));
int MagicBoosters[][8] = { { 3191, 2184, 1310, 3618, 2091, 1308, 2452, 3996 }, f += dx, r += dy) {
{ 1059, 3608, 605, 3234, 3326, 38, 2029, 3043 } }; result |= (1ULL << (f + r*8));
RKISS rk; if(block & (1ULL << (f + r*8))) break;
Bitboard occupancy[4096], reference[4096], edges, b;
int i, size, index, booster;
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
attacks[SQ_A1] = (pt == ROOK ? RookTable : BishopTable);
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_attacks(pt, 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 attacks bitboard in reference[].
b = size = 0;
do {
occupancy[size] = b;
reference[size++] = sliding_attacks(pt, s, b);
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;
booster = MagicBoosters[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 {
magics[s] = pick_random(masks[s], rk, booster);
memset(attacks[s], 0, size * sizeof(Bitboard));
// A good magic must map every possible occupancy to an index that
// looks up the correct sliding attack in the attacks[s] database.
// Note that we build up the database for square 's' as a side
// effect of verifying the magic.
for (i = 0; i < size; i++)
{
index = (pt == ROOK ? rook_index(s, occupancy[i])
: bishop_index(s, occupancy[i]));
if (!attacks[s][index])
attacks[s][index] = reference[i];
else if (attacks[s][index] != reference[i])
break;
}
} while (i != size);
} }
} }
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);
}
}
} }
+164 -85
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,36 +18,76 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(BITBOARD_H_INCLUDED) #if !defined(BITBOARD_H_INCLUDED)
#define BITBOARD_H_INCLUDED #define BITBOARD_H_INCLUDED
////
//// Includes
////
#include "direction.h"
#include "piece.h"
#include "square.h"
#include "types.h" #include "types.h"
extern Bitboard FileBB[8];
extern Bitboard NeighboringFilesBB[8]; ////
extern Bitboard ThisAndNeighboringFilesBB[8]; //// Constants and variables
extern Bitboard RankBB[8]; ////
extern Bitboard InFrontBB[2][8];
const Bitboard EmptyBoardBB = 0ULL;
const Bitboard WhiteSquaresBB = 0x55AA55AA55AA55AAULL;
const Bitboard BlackSquaresBB = 0xAA55AA55AA55AA55ULL;
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = 0x0202020202020202ULL;
const Bitboard FileCBB = 0x0404040404040404ULL;
const Bitboard FileDBB = 0x0808080808080808ULL;
const Bitboard FileEBB = 0x1010101010101010ULL;
const Bitboard FileFBB = 0x2020202020202020ULL;
const Bitboard FileGBB = 0x4040404040404040ULL;
const Bitboard FileHBB = 0x8080808080808080ULL;
const Bitboard Rank1BB = 0xFFULL;
const Bitboard Rank2BB = 0xFF00ULL;
const Bitboard Rank3BB = 0xFF0000ULL;
const Bitboard Rank4BB = 0xFF000000ULL;
const Bitboard Rank5BB = 0xFF00000000ULL;
const Bitboard Rank6BB = 0xFF0000000000ULL;
const Bitboard Rank7BB = 0xFF000000000000ULL;
const Bitboard Rank8BB = 0xFF00000000000000ULL;
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 SetMaskBB[65]; extern Bitboard SetMaskBB[65];
extern Bitboard ClearMaskBB[65]; extern Bitboard ClearMaskBB[65];
extern Bitboard StepAttacksBB[16][64]; extern Bitboard StepAttackBB[16][64];
extern Bitboard RayBB[64][8];
extern Bitboard BetweenBB[64][64]; extern Bitboard BetweenBB[64][64];
extern Bitboard SquaresInFrontMask[2][64];
extern Bitboard PassedPawnMask[2][64]; extern Bitboard PassedPawnMask[2][64];
extern Bitboard AttackSpanMask[2][64]; extern Bitboard OutpostMask[2][64];
extern uint64_t RMagics[64]; extern const uint64_t RMult[64];
extern int RShifts[64]; extern const int RShift[64];
extern Bitboard RMasks[64]; extern Bitboard RMask[64];
extern Bitboard* RAttacks[64]; extern int RAttackIndex[64];
extern Bitboard RAttacks[0x19000];
extern uint64_t BMagics[64]; extern const uint64_t BMult[64];
extern int BShifts[64]; extern const int BShift[64];
extern Bitboard BMasks[64]; extern Bitboard BMask[64];
extern Bitboard* BAttacks[64]; extern int BAttackIndex[64];
extern Bitboard BAttacks[0x1480];
extern Bitboard BishopPseudoAttacks[64]; extern Bitboard BishopPseudoAttacks[64];
extern Bitboard RookPseudoAttacks[64]; extern Bitboard RookPseudoAttacks[64];
@@ -56,6 +96,10 @@ extern Bitboard QueenPseudoAttacks[64];
extern uint8_t BitCount8Bit[256]; extern uint8_t BitCount8Bit[256];
////
//// Inline functions
////
/// Functions for testing whether a given bit is set in a bitboard, and for /// Functions for testing whether a given bit is set in a bitboard, and for
/// setting and clearing bits. /// setting and clearing bits.
@@ -83,16 +127,16 @@ inline void do_move_bb(Bitboard* b, Bitboard move_bb) {
*b ^= move_bb; *b ^= move_bb;
} }
/// rank_bb() and file_bb() gives a bitboard containing all squares on a given
/// rank_bb() and file_bb() take a file or a square as input and return /// file or rank. It is also possible to pass a square as input to these
/// a bitboard representing all squares on the given file or rank. /// 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) {
@@ -100,25 +144,45 @@ 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));
} }
/// neighboring_files_bb takes a file as input and returns a bitboard representing /// neighboring_files_bb takes a file or a square as input, and returns a
/// all squares on the neighboring files. /// bitboard representing all squares on the neighboring files.
inline Bitboard neighboring_files_bb(File f) { inline Bitboard neighboring_files_bb(File f) {
return NeighboringFilesBB[f]; return NeighboringFilesBB[f];
} }
inline Bitboard neighboring_files_bb(Square s) {
return neighboring_files_bb(square_file(s));
}
/// this_and_neighboring_files_bb takes a file as input and returns a bitboard
/// representing all squares on the given and neighboring files. /// this_and_neighboring_files_bb takes a file or a square as input, and
/// returns a bitboard representing all squares on the given and neighboring
/// files.
inline Bitboard this_and_neighboring_files_bb(File f) { inline Bitboard this_and_neighboring_files_bb(File f) {
return ThisAndNeighboringFilesBB[f]; return ThisAndNeighboringFilesBB[f];
} }
inline Bitboard this_and_neighboring_files_bb(Square s) {
return this_and_neighboring_files_bb(square_file(s));
}
/// relative_rank_bb() takes a color and a rank as input, and returns a bitboard
/// representing all squares on the given rank from the given color's point of
/// view. For instance, relative_rank_bb(WHITE, 7) gives all squares on the
/// 7th rank, while relative_rank_bb(BLACK, 7) gives all squares on the 2nd
/// rank.
inline Bitboard relative_rank_bb(Color c, Rank r) {
return RelativeRankBB[c][r];
}
/// in_front_bb() takes a color and a rank or square as input, and returns a /// in_front_bb() takes a color and a rank or square as input, and returns a
/// bitboard representing all the squares on all ranks in front of the rank /// bitboard representing all the squares on all ranks in front of the rank
@@ -131,7 +195,28 @@ inline Bitboard in_front_bb(Color c, Rank r) {
} }
inline Bitboard in_front_bb(Color c, Square s) { inline Bitboard in_front_bb(Color c, Square s) {
return InFrontBB[c][rank_of(s)]; return in_front_bb(c, square_rank(s));
}
/// behind_bb() takes a color and a rank or square as input, and returns a
/// bitboard representing all the squares on all ranks behind of the rank
/// (or square), from the given color's point of view.
inline Bitboard behind_bb(Color c, Rank r) {
return InFrontBB[opposite_color(c)][r];
}
inline Bitboard behind_bb(Color c, Square s) {
return in_front_bb(opposite_color(c), square_rank(s));
}
/// ray_bb() gives a bitboard representing all squares along the ray in a
/// given direction from a given square.
inline Bitboard ray_bb(Square s, SignedDirection d) {
return RayBB[s][d];
} }
@@ -142,35 +227,36 @@ inline Bitboard in_front_bb(Color c, Square s) {
#if defined(IS_64BIT) #if defined(IS_64BIT)
FORCE_INLINE unsigned rook_index(Square s, Bitboard occ) { inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
return unsigned(((occ & RMasks[s]) * RMagics[s]) >> RShifts[s]); Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] + ((b * RMult[s]) >> RShift[s])];
} }
FORCE_INLINE unsigned bishop_index(Square s, Bitboard occ) { inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
return unsigned(((occ & BMasks[s]) * BMagics[s]) >> BShifts[s]); Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] + ((b * BMult[s]) >> BShift[s])];
} }
#else // if !defined(IS_64BIT) #else // if !defined(IS_64BIT)
FORCE_INLINE unsigned rook_index(Square s, Bitboard occ) { inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = occ & RMasks[s]; Bitboard b = blockers & RMask[s];
return unsigned(int(b) * int(RMagics[s]) ^ int(b >> 32) * int(RMagics[s] >> 32)) >> RShifts[s]; return RAttacks[RAttackIndex[s] +
(unsigned(int(b) * int(RMult[s]) ^
int(b >> 32) * int(RMult[s] >> 32))
>> RShift[s])];
} }
FORCE_INLINE unsigned bishop_index(Square s, Bitboard occ) { inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = occ & BMasks[s]; Bitboard b = blockers & BMask[s];
return unsigned(int(b) * int(BMagics[s]) ^ int(b >> 32) * int(BMagics[s] >> 32)) >> BShifts[s]; return BAttacks[BAttackIndex[s] +
(unsigned(int(b) * int(BMult[s]) ^
int(b >> 32) * int(BMult[s] >> 32))
>> BShift[s])];
} }
#endif #endif
inline Bitboard rook_attacks_bb(Square s, Bitboard occ) {
return RAttacks[s][rook_index(s, occ)];
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard occ) {
return BAttacks[s][bishop_index(s, occ)];
}
inline Bitboard queen_attacks_bb(Square s, Bitboard blockers) { inline Bitboard queen_attacks_bb(Square s, Bitboard blockers) {
return rook_attacks_bb(s, blockers) | bishop_attacks_bb(s, blockers); return rook_attacks_bb(s, blockers) | bishop_attacks_bb(s, blockers);
} }
@@ -188,49 +274,46 @@ inline Bitboard squares_between(Square s1, Square s2) {
/// squares_in_front_of takes a color and a square as input, and returns a /// 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, /// bitboard representing all squares along the line in front of the square,
/// from the point of view of the given color. Definition of the table is: /// from the point of view of the given color. For instance,
/// SquaresInFrontOf[c][s] = in_front_bb(c, s) & file_bb(s) /// 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) { inline Bitboard squares_in_front_of(Color c, Square s) {
return SquaresInFrontMask[c][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 /// 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 /// bitboard mask which can be used to test if a pawn of the given color on
/// the given square is a passed pawn. Definition of the table is: /// the given square is a passed pawn.
/// PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(s)
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];
} }
/// attack_span_mask takes a color and a square as input, and returns a bitboard /// outpost_mask takes a color and a square as input, and returns a bitboard
/// representing all squares that can be attacked by a pawn of the given color /// mask which can be used to test whether a piece on the square can possibly
/// when it moves along its file starting from the given square. Definition is: /// be driven away by an enemy pawn.
/// AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
inline Bitboard attack_span_mask(Color c, Square s) { inline Bitboard outpost_mask(Color c, Square s) {
return AttackSpanMask[c][s]; return OutpostMask[c][s];
} }
/// squares_aligned returns true if the squares s1, s2 and s3 are aligned /// isolated_pawn_mask takes a square as input, and returns a bitboard mask
/// either on a straight or on a diagonal line. /// which can be used to test whether a pawn on the given square is isolated.
inline bool squares_aligned(Square s1, Square s2, Square s3) { inline Bitboard isolated_pawn_mask(Square s) {
return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3]) return neighboring_files_bb(s);
& ( SetMaskBB[s1] | SetMaskBB[s2] | SetMaskBB[s3]);
}
/// same_color_squares() returns a bitboard representing all squares with
/// the same color of the given square.
inline Bitboard same_color_squares(Square s) {
return bit_is_set(0xAA55AA55AA55AA55ULL, s) ? 0xAA55AA55AA55AA55ULL
: ~0xAA55AA55AA55AA55ULL;
} }
@@ -238,25 +321,15 @@ inline Bitboard same_color_squares(Square s) {
/// pop_1st_bit() finds and clears the least significant nonzero bit in a /// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard. /// nonzero bitboard.
#if defined(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) {
FORCE_INLINE Square first_1(Bitboard b) {
unsigned long index;
_BitScanForward64(&index, b);
return (Square) index;
}
#else
FORCE_INLINE Square first_1(Bitboard b) { // Assembly code by Heinz van Saanen
Bitboard dummy; Bitboard dummy;
__asm__("bsfq %1, %0": "=r"(dummy): "rm"(b) ); __asm__("bsfq %1, %0": "=r"(dummy): "rm"(b) );
return (Square) dummy; return (Square)(dummy);
} }
#endif
FORCE_INLINE Square pop_1st_bit(Bitboard* b) { inline Square pop_1st_bit(Bitboard* b) {
const Square s = first_1(*b); const Square s = first_1(*b);
*b &= ~(1ULL<<s); *b &= ~(1ULL<<s);
return s; return s;
@@ -270,7 +343,13 @@ extern Square pop_1st_bit(Bitboard* b);
#endif #endif
////
//// Prototypes
////
extern void print_bitboard(Bitboard b); extern void print_bitboard(Bitboard b);
extern void bitboards_init(); extern void init_bitboards();
extern int bitScanReverse32(uint32_t b);
#endif // !defined(BITBOARD_H_INCLUDED) #endif // !defined(BITBOARD_H_INCLUDED)
+98 -43
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,32 +18,79 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(BITCOUNT_H_INCLUDED) #if !defined(BITCOUNT_H_INCLUDED)
#define BITCOUNT_H_INCLUDED #define BITCOUNT_H_INCLUDED
#include <cassert>
#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 if #if defined(__INTEL_COMPILER) && defined(USE_POPCNT) // Intel compiler
/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count or
/// use hardware popcnt instruction when available. #include <nmmintrin.h>
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; inline bool cpu_has_popcnt() {
int CPUInfo[4] = {-1};
__cpuid(CPUInfo, 0x00000001);
return (CPUInfo[2] >> 23) & 1;
}
#define POPCNT_INTRINSIC(x) _mm_popcnt_u64(x)
#elif defined(_MSC_VER) && defined(USE_POPCNT) // Microsoft compiler
#include <intrin.h>
inline bool cpu_has_popcnt() {
int CPUInfo[4] = {-1};
__cpuid(CPUInfo, 0x00000001);
return (CPUInfo[2] >> 23) & 1;
}
#define POPCNT_INTRINSIC(x) __popcnt64(x)
#elif defined(__GNUC__) && defined(USE_POPCNT) // Gcc compiler
inline void __cpuid(unsigned int op,
unsigned int *eax, unsigned int *ebx,
unsigned int *ecx, unsigned int *edx)
{
*eax = op;
*ecx = 0;
__asm__("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
: "0" (*eax), "2" (*ecx));
}
inline bool cpu_has_popcnt() {
unsigned int eax, ebx, ecx, edx;
__cpuid(1, &eax, &ebx, &ecx, &edx);
return (ecx >> 23) & 1;
}
#define POPCNT_INTRINSIC(x) ({ \
unsigned long __ret; \
__asm__("popcnt %1, %0" : "=r" (__ret) : "r" (x)); \
__ret; })
#else // Safe fallback for unsupported compilers or when USE_POPCNT is disabled
inline bool cpu_has_popcnt() { return false; }
#define POPCNT_INTRINSIC(x) 0
#endif // cpu_has_popcnt() and POPCNT_INTRINSIC() definitions
/// popcount() counts the number of nonzero bits in a bitboard /// Software implementation of bit count functions
template<BitCountType> inline int popcount(Bitboard);
template<> #if defined(IS_64BIT)
inline int popcount<CNT_64>(Bitboard b) {
inline int count_1s(Bitboard b) {
b -= ((b>>1) & 0x5555555555555555ULL); b -= ((b>>1) & 0x5555555555555555ULL);
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL; b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL;
@@ -51,16 +98,16 @@ inline int popcount<CNT_64>(Bitboard b) {
return int(b >> 56); return int(b >> 56);
} }
template<> inline int count_1s_max_15(Bitboard b) {
inline int popcount<CNT_64_MAX15>(Bitboard b) {
b -= (b>>1) & 0x5555555555555555ULL; b -= (b>>1) & 0x5555555555555555ULL;
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b *= 0x1111111111111111ULL; b *= 0x1111111111111111ULL;
return int(b >> 60); return int(b >> 60);
} }
template<> #else // if !defined(IS_64BIT)
inline int popcount<CNT_32>(Bitboard b) {
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;
@@ -72,8 +119,7 @@ inline int popcount<CNT_32>(Bitboard b) {
return int(v >> 24); 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;
@@ -84,29 +130,38 @@ inline int popcount<CNT_32_MAX15>(Bitboard b) {
return int(v >> 28); return int(v >> 28);
} }
template<> #endif // BITCOUNT
inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#if !defined(USE_POPCNT)
assert(false); /// count_1s() counts the number of nonzero bits in a bitboard.
return int(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)
return (int)__popcnt64(b);
#else
unsigned long ret;
__asm__("popcnt %1, %0" : "=r" (ret) : "r" (b));
return ret;
#endif
} }
template<bool UseIntrinsic>
inline int count_1s_max_15(Bitboard b) {
return UseIntrinsic ? POPCNT_INTRINSIC(b) : count_1s_max_15(b);
}
// Global constant initialized at startup that is set to true if
// CPU on which application runs supports POPCNT intrinsic. Unless
// USE_POPCNT is not defined.
const bool CpuHasPOPCNT = cpu_has_popcnt();
// Global constant used to print info about the use of 64 optimized
// functions to verify that a 64 bit compile has been correctly built.
#if defined(IS_64BIT)
const bool CpuHas64BitPath = true;
#else
const bool CpuHas64BitPath = false;
#endif
#endif // !defined(BITCOUNT_H_INCLUDED) #endif // !defined(BITCOUNT_H_INCLUDED)
+256 -160
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,26 +17,46 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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 The code in this file is based on the opening book code in PolyGlot
by Fabien Letouzey. PolyGlot is available under the GNU General by Fabien Letouzey. PolyGlot is available under the GNU General
Public License, and can be downloaded from http://wbec-ridderkerk.nl Public License, and can be downloaded from http://wbec-ridderkerk.nl
*/ */
#include <algorithm>
////
//// Includes
////
#include <cassert> #include <cassert>
#include <iostream>
#include "book.h" #include "book.h"
#include "misc.h" #include "mersenne.h"
#include "movegen.h" #include "movegen.h"
using namespace std; using namespace std;
////
//// Global variables
////
Book OpeningBook;
////
//// Local definitions
////
namespace { namespace {
// Random numbers from PolyGlot, used to compute book hash keys /// Book entry size in bytes
const Key PolyGlotRandoms[781] = { const int EntrySize = 16;
/// Random numbers from PolyGlot, used to compute book hash keys
const uint64_t Random64[781] = {
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL, 0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL, 0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL, 0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
@@ -300,188 +320,264 @@ namespace {
0xF8D626AAAF278509ULL 0xF8D626AAAF278509ULL
}; };
// Offsets to the PolyGlotRandoms[] array of zobrist keys
const Key* ZobPiece = PolyGlotRandoms + 0;
const Key* ZobCastle = PolyGlotRandoms + 768;
const Key* ZobEnPassant = PolyGlotRandoms + 772;
const Key* ZobTurn = PolyGlotRandoms + 780;
// PieceOffset is calculated as 64 * (PolyPiece ^ 1) where PolyPiece /// Indices to the Random64[] array
// is: BP = 0, WP = 1, BN = 2, WN = 3 ... BK = 10, WK = 11
const int PieceOffset[] = { 0, 64, 192, 320, 448, 576, 704, 0, const int RandomPiece = 0;
0, 0, 128, 256, 384, 512, 640 }; 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 {
// book_key() returns the PolyGlot hash key of the given position
uint64_t book_key(const Position& pos) { uint64_t book_key(const Position& pos) {
uint64_t key = 0; uint64_t result = 0ULL;
Bitboard b = pos.occupied_squares();
for (Color c = WHITE; c <= BLACK; c++)
{
Bitboard b = pos.pieces_of_color(c);
while (b) while (b)
{ {
Square s = pop_1st_bit(&b); Square s = pop_1st_bit(&b);
key ^= ZobPiece[PieceOffset[pos.piece_on(s)] + s]; Piece p = pos.piece_on(s);
assert(piece_is_ok(p));
assert(color_of_piece(p) == c);
result ^= book_piece_key(p, s);
} }
b = (pos.can_castle(WHITE_OO) << 0) | (pos.can_castle(WHITE_OOO) << 1)
| (pos.can_castle(BLACK_OO) << 2) | (pos.can_castle(BLACK_OOO) << 3);
while (b)
key ^= ZobCastle[pop_1st_bit(&b)];
if (pos.ep_square() != SQ_NONE)
key ^= ZobEnPassant[file_of(pos.ep_square())];
if (pos.side_to_move() == WHITE)
key ^= ZobTurn[0];
return key;
} }
result ^= book_castle_key(pos);
} // namespace result ^= book_ep_key(pos);
result ^= book_color_key(pos);
Book::Book() : size(0) { return result;
for (int i = abs(system_time() % 10000); i > 0; i--)
RKiss.rand<unsigned>(); // Make random number generation less deterministic
}
Book::~Book() { if (is_open()) close(); }
/// Book::operator>>() reads sizeof(T) chars from the file's binary byte stream
/// and converts them in a number of type T. A Polyglot book stores numbers in
/// big-endian format.
template<typename T> Book& Book::operator>>(T& n) {
n = 0;
for (size_t i = 0; i < sizeof(T); i++)
n = T((n << 8) + ifstream::get());
return *this;
}
template<> Book& Book::operator>>(BookEntry& e) {
return *this >> e.key >> e.move >> e.count >> e.learn;
} }
/// Book::open() tries to open a book file with the given name after closing uint64_t book_piece_key(Piece p, Square s) {
/// any exsisting one.
bool Book::open(const char* fName) { /// 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 };
fileName = ""; return Random64[RandomPiece + (PieceTo12[int(p)]^1) * 64 + int(s)];
if (is_open()) // Cannot close an already closed file
close();
ifstream::open(fName, ifstream::in | ifstream::binary | ios::ate);
if (!is_open())
return false; // Silently fail if the file is not found
// Get the book size in number of entries, we are already at the end of file
size = tellg() / sizeof(BookEntry);
if (!good())
{
cerr << "Failed to open book file " << fName << endl;
exit(EXIT_FAILURE);
}
fileName = fName; // Set only if successful
return true;
} }
/// Book::probe() tries to find a book move for the given position. If no move uint64_t book_castle_key(const Position& pos) {
/// is found returns MOVE_NONE. If pickBest is true returns always the highest
/// rated move, otherwise randomly chooses one, based on the move score.
Move Book::probe(const Position& pos, const string& fName, bool pickBest) { uint64_t result = 0ULL;
BookEntry e; if (pos.can_castle_kingside(WHITE))
uint16_t best = 0; result ^= Random64[RandomCastle+0];
unsigned sum = 0;
Move move = MOVE_NONE;
uint64_t key = book_key(pos);
if (fileName != fName && !open(fName.c_str())) if (pos.can_castle_queenside(WHITE))
return MOVE_NONE; result ^= Random64[RandomCastle+1];
binary_search(key); if (pos.can_castle_kingside(BLACK))
result ^= Random64[RandomCastle+2];
while (*this >> e, e.key == key && good()) if (pos.can_castle_queenside(BLACK))
{ result ^= Random64[RandomCastle+3];
best = max(best, e.count);
sum += e.count;
// Choose book move according to its score. If a move has a very return result;
// high score it has higher probability to be choosen than a move
// with lower score. Note that first entry is always chosen.
if ( (RKiss.rand<unsigned>() % sum < e.count)
|| (pickBest && e.count == best))
move = Move(e.move);
}
if (!move)
return MOVE_NONE;
// A PolyGlot book move is encoded as follows:
//
// bit 0- 5: destination square (from 0 to 63)
// bit 6-11: origin square (from 0 to 63)
// bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
//
// Castling moves follow "king captures rook" representation. So in case book
// move is a promotion we have to convert to our representation, in all the
// other cases we can directly compare with a Move after having masked out
// the special Move's flags (bit 14-15) that are not supported by PolyGlot.
int pt = (move >> 12) & 7;
if (pt)
move = make_promotion(from_sq(move), to_sq(move), PieceType(pt + 1));
// Add 'special move' flags and verify it is legal
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
if (move == (ml.move() & 0x3FFF))
return ml.move();
return MOVE_NONE;
} }
/// Book::binary_search() takes a book key as input, and does a binary search uint64_t book_ep_key(const Position& pos) {
/// through the book file for the given key. File stream current position is set return (pos.ep_square() == SQ_NONE ? 0ULL : Random64[RandomEnPassant + square_file(pos.ep_square())]);
/// to the leftmost book entry with the same key as the input.
void Book::binary_search(uint64_t key) {
size_t low, high, mid;
BookEntry e;
low = 0;
high = size - 1;
assert(low <= high);
while (low < high && good())
{
mid = (low + high) / 2;
assert(mid >= low && mid < high);
seekg(mid * sizeof(BookEntry), ios_base::beg);
*this >> e;
if (key <= e.key)
high = mid;
else
low = mid + 1;
} }
assert(low == high);
seekg(low * sizeof(BookEntry), ios_base::beg); uint64_t book_color_key(const Position& pos) {
return (pos.side_to_move() == WHITE ? Random64[RandomTurn] : 0ULL);
}
} }
+40 -14
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,42 +17,68 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. 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) #if !defined(BOOK_H_INCLUDED)
#define BOOK_H_INCLUDED #define BOOK_H_INCLUDED
////
//// Includes
////
#include <fstream> #include <fstream>
#include <string> #include <string>
#include "move.h"
#include "position.h" #include "position.h"
#include "rkiss.h"
/// A Polyglot book is a series of "entries" of 16 bytes. All integers are ////
/// stored highest byte first (regardless of size). The entries are ordered //// Types
/// according to key. Lowest key first. ////
struct BookEntry { struct BookEntry {
uint64_t key; uint64_t key;
uint16_t move; uint16_t move;
uint16_t count; uint16_t count;
uint32_t learn; uint16_t n;
uint16_t sum;
}; };
class Book : private std::ifstream { class Book : private std::ifstream {
public: public:
Book();
~Book(); ~Book();
Move probe(const Position& pos, const std::string& fName, bool pickBest); void open(const std::string& fName);
void close();
const std::string file_name() const;
Move get_move(const Position& pos);
private: private:
template<typename T> Book& operator>>(T& n); 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; }
bool open(const char* fName); uint64_t read_integer(int size);
void binary_search(uint64_t key); void read_entry(BookEntry& e, int n);
int find_key(uint64_t key);
RKISS RKiss;
std::string fileName; std::string fileName;
size_t size; int bookSize;
}; };
////
//// Global variables
////
extern Book OpeningBook;
#endif // !defined(BOOK_H_INCLUDED) #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)
+312 -292
View File
File diff suppressed because it is too large Load Diff
+40 -54
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,22 +17,26 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(ENDGAME_H_INCLUDED) #if !defined(ENDGAME_H_INCLUDED)
#define ENDGAME_H_INCLUDED #define ENDGAME_H_INCLUDED
#include <map> ////
#include <string> //// Includes
////
#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 KXK, // Generic "mate lone king" eval
KBNK, // KBN vs K KBNK, // KBN vs K
KPK, // KP vs K KPK, // KP vs K
@@ -44,10 +48,7 @@ enum EndgameType {
KNNK, // KNN vs K KNNK, // KNN vs K
KmmKm, // K and two minors vs K and one or two minors KmmKm, // K and two minors vs K and one or two minors
// Scaling functions // Scaling functions
SCALE_FUNS,
KBPsK, // KB+pawns vs K KBPsK, // KB+pawns vs K
KQKRPs, // KQ vs KR+pawns KQKRPs, // KQ vs KR+pawns
KRPKR, // KRP vs KR KRPKR, // KRP vs KR
@@ -60,61 +61,46 @@ enum EndgameType {
KPKP // KP vs KP KPKP // KP vs KP
}; };
/// Template abstract base class for all special endgame functions
/// Some magic to detect family type of endgame from its enum value
template<bool> struct bool_to_type { typedef Value type; };
template<> struct bool_to_type<true> { typedef ScaleFactor type; };
template<EndgameType E> struct eg_family : public bool_to_type<(E > SCALE_FUNS)> {};
/// Base and derived templates for endgame evaluation and scaling functions
template<typename T> template<typename T>
struct EndgameBase { class EndgameFunctionBase {
public:
virtual ~EndgameBase() {} EndgameFunctionBase(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {}
virtual Color color() const = 0; virtual ~EndgameFunctionBase() {}
virtual T operator()(const Position&) const = 0; virtual T apply(const Position&) = 0;
};
template<EndgameType E, typename T = typename eg_family<E>::type>
struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : strongerSide(c), weakerSide(flip(c)) {}
Color color() const { return strongerSide; } Color color() const { return strongerSide; }
T operator()(const Position&) const;
private: protected:
Color strongerSide, weakerSide; Color strongerSide, weakerSide;
}; };
typedef EndgameFunctionBase<Value> EndgameEvaluationFunctionBase;
typedef EndgameFunctionBase<ScaleFactor> EndgameScalingFunctionBase;
/// Endgames class stores in two std::map the pointers to endgame evaluation
/// and scaling base objects. Then we use polymorphism to invoke the actual
/// endgame function calling its operator() method that is virtual.
class Endgames { /// Templates subclass for various concrete endgames
typedef std::map<Key, EndgameBase<Value>*> M1; template<EndgameType>
typedef std::map<Key, EndgameBase<ScaleFactor>*> M2; struct EvaluationFunction : public EndgameEvaluationFunctionBase {
typedef EndgameEvaluationFunctionBase Base;
M1 m1; explicit EvaluationFunction(Color c): EndgameEvaluationFunctionBase(c) {}
M2 m2; Value apply(const Position&);
M1& map(Value*) { return m1; }
M2& map(ScaleFactor*) { return m2; }
template<EndgameType E> void add(const std::string& code);
public:
Endgames();
~Endgames();
template<typename T> EndgameBase<T>* get(Key key) {
return map((T*)0).count(key) ? map((T*)0)[key] : NULL;
}
}; };
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) #endif // !defined(ENDGAME_H_INCLUDED)
+770 -719
View File
File diff suppressed because it is too large Load Diff
+87 -5
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,15 +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/>.
*/ */
#if !defined(EVALUATE_H_INCLUDED) #if !defined(EVALUATE_H_INCLUDED)
#define EVALUATE_H_INCLUDED #define EVALUATE_H_INCLUDED
#include "types.h" ////
//// Includes
////
#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;
extern Value evaluate(const Position& pos, Value& margin); struct EvalInfo {
extern std::string trace_evaluate(const Position& pos);
extern void read_evaluation_uci_options(Color sideToMove); // Middle game and endgame evaluations
Score value;
// Pointers to material and pawn hash table entries
MaterialInfo* mi;
PawnInfo* pi;
// attackedBy[color][piece type] is a bitboard representing all squares
// attacked by a given color and piece type, attackedBy[color][0] contains
// all squares attacked by the given color.
Bitboard attackedBy[2][8];
Bitboard attacked_by(Color c) const { return attackedBy[c][0]; }
Bitboard attacked_by(Color c, PieceType pt) const { return attackedBy[c][pt]; }
// 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) #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];
}
+30 -28
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,12 +17,22 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(HISTORY_H_INCLUDED) #if !defined(HISTORY_H_INCLUDED)
#define HISTORY_H_INCLUDED #define HISTORY_H_INCLUDED
#include "types.h" ////
#include <cstring> //// Includes
#include <algorithm> ////
#include "depth.h"
#include "move.h"
#include "piece.h"
////
//// Types
////
/// The History class stores statistics about how often different moves /// The History class stores statistics about how often different moves
/// have been successful or unsuccessful during the current search. These /// have been successful or unsuccessful during the current search. These
@@ -34,38 +44,30 @@
class History { class History {
public: public:
History();
void clear(); void clear();
Value value(Piece p, Square to) const; void success(Piece p, Square to, Depth d);
void add(Piece p, Square to, Value bonus); void failure(Piece p, Square to, Depth d);
Value gain(Piece p, Square to) const; int move_ordering_score(Piece p, Square to) const;
void update_gain(Piece p, Square to, Value g);
static const Value MaxValue = Value(2000);
private: private:
Value history[16][64]; // [piece][to_square] int history[16][64]; // [piece][square]
Value maxGains[16][64]; // [piece][to_square]
}; };
inline void History::clear() {
memset(history, 0, 16 * 64 * sizeof(Value));
memset(maxGains, 0, 16 * 64 * sizeof(Value));
}
inline Value History::value(Piece p, Square to) const { ////
return history[p][to]; //// Constants and variables
} ////
inline void History::add(Piece p, Square to, Value bonus) { /// HistoryMax controls how often the history counters will be scaled down:
if (abs(history[p][to] + bonus) < MaxValue) history[p][to] += bonus; /// 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.
inline Value History::gain(Piece p, Square to) const { const int HistoryMax = 25000 * OnePly;
return maxGains[p][to];
}
inline void History::update_gain(Piece p, Square to, Value g) {
maxGains[p][to] = std::max(g, maxGains[p][to] - 1);
}
#endif // !defined(HISTORY_H_INCLUDED) #endif // !defined(HISTORY_H_INCLUDED)
+58 -40
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,69 +17,87 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(LOCK_H_INCLUDED) #if !defined(LOCK_H_INCLUDED)
#define LOCK_H_INCLUDED #define LOCK_H_INCLUDED
#if !defined(_MSC_VER)
// 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> # include <pthread.h>
typedef pthread_mutex_t Lock; typedef pthread_mutex_t Lock;
typedef pthread_cond_t WaitCondition;
# define lock_init(x) pthread_mutex_init(x, NULL) # define lock_init(x, y) pthread_mutex_init(x, y)
# define lock_grab(x) pthread_mutex_lock(x) # define lock_grab(x) pthread_mutex_lock(x)
# define lock_release(x) pthread_mutex_unlock(x) # define lock_release(x) pthread_mutex_unlock(x)
# define lock_destroy(x) pthread_mutex_destroy(x) # define lock_destroy(x) pthread_mutex_destroy(x)
# define cond_destroy(x) pthread_cond_destroy(x)
# define cond_init(x) pthread_cond_init(x, NULL)
# define cond_signal(x) pthread_cond_signal(x)
# define cond_wait(x,y) pthread_cond_wait(x,y)
# define cond_timedwait(x,y,z) pthread_cond_timedwait(x,y,z)
#else #else
#define NOMINMAX // disable macros min() and max()
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#undef WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
// Default fast and race free locks and condition variables
#if !defined(OLD_LOCKS)
typedef SRWLOCK Lock;
typedef CONDITION_VARIABLE WaitCondition;
# define lock_init(x) InitializeSRWLock(x)
# define lock_grab(x) AcquireSRWLockExclusive(x)
# define lock_release(x) ReleaseSRWLockExclusive(x)
# define lock_destroy(x) (x)
# define cond_destroy(x) (x)
# define cond_init(x) InitializeConditionVariable(x)
# define cond_signal(x) WakeConditionVariable(x)
# define cond_wait(x,y) SleepConditionVariableSRW(x,y,INFINITE,0)
# define cond_timedwait(x,y,z) SleepConditionVariableSRW(x,y,z,0)
// Fallback solution to build for Windows XP and older versions, note that
// cond_wait() is racy between lock_release() and WaitForSingleObject().
#else
typedef CRITICAL_SECTION Lock; typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition; # define lock_init(x, y) InitializeCriticalSection(x)
# define lock_init(x) InitializeCriticalSection(x)
# define lock_grab(x) EnterCriticalSection(x) # define lock_grab(x) EnterCriticalSection(x)
# define lock_release(x) LeaveCriticalSection(x) # define lock_release(x) LeaveCriticalSection(x)
# define lock_destroy(x) DeleteCriticalSection(x) # define lock_destroy(x) DeleteCriticalSection(x)
# define cond_init(x) { *x = CreateEvent(0, FALSE, FALSE, 0); }
# define cond_destroy(x) CloseHandle(*x)
# define cond_signal(x) SetEvent(*x)
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(*x, INFINITE); lock_grab(y); }
# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(*x,z); lock_grab(y); }
#endif #endif
#endif
#endif // !defined(LOCK_H_INCLUDED) #endif // !defined(LOCK_H_INCLUDED)
+56 -24
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,41 +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 <string>
#include "bitboard.h" #include "benchmark.h"
#include "bitcount.h"
#include "misc.h" #include "misc.h"
#include "position.h" #include "uci.h"
#include "search.h"
#include "thread.h" #ifdef USE_CALLGRIND
#include <valgrind/callgrind.h>
#endif
using namespace std; using namespace std;
extern void uci_loop();
extern void benchmark(int argc, char* argv[]); ////
extern void kpk_bitbase_init(); //// Functions
////
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
bitboards_init(); // Disable IO buffering
Position::init(); cout.rdbuf()->pubsetbuf(NULL, 0);
kpk_bitbase_init(); cin.rdbuf()->pubsetbuf(NULL, 0);
Search::init();
Threads.init();
cout << engine_info() << endl; // Initialization through global resources manager
Application::initialize();
if (argc == 1) #ifdef USE_CALLGRIND
uci_loop(); CALLGRIND_START_INSTRUMENTATION;
#endif
else if (string(argv[1]) == "bench")
benchmark(argc, argv);
// 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 else
cerr << "\nUsage: stockfish bench [hash size = 128] [threads = 1] " {
<< "[limit = 12] [fen positions file = default] " string time = argc > 4 ? argv[4] : "60";
<< "[limited by depth, time, nodes or perft = depth]" << endl; string fen = argc > 5 ? argv[5] : "default";
string lim = argc > 6 ? argv[6] : "time";
Threads.exit(); 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;
} }
+360 -216
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,258 +17,120 @@
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 <cstring> #include <sstream>
#include <algorithm> #include <map>
#include "material.h" #include "material.h"
using namespace std; using namespace std;
////
//// Local definitions
////
namespace { namespace {
// Values modified by Joona Kiiski // Values modified by Joona Kiiski
const Value MidgameLimit = Value(15581); const Value MidgameLimit = Value(15581);
const Value EndgameLimit = Value(3998); const Value EndgameLimit = Value(3998);
// Scale factors used when one side has no more pawns
const int NoPawnsSF[4] = { 6, 12, 32 };
// Polynomial material balance parameters // Polynomial material balance parameters
const Value RedundantQueenPenalty = Value(320); const Value RedundantQueenPenalty = Value(320);
const Value RedundantRookPenalty = Value(554); const Value RedundantRookPenalty = Value(554);
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 }; const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
const int QuadraticCoefficientsSameColor[][8] = { const int QuadraticCoefficientsSameColor[][6] = {
{ 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 }, { 7, 7, 7, 7, 7, 7 }, { 39, 2, 7, 7, 7, 7 }, { 35, 271, -4, 7, 7, 7 },
{ 7, 25, 4, 7, 7, 7 }, { -27, -2, 46, 100, 56, 7 }, { 58, 29, 83, 148, -3, -25 } }; { 7, 25, 4, 7, 7, 7 }, { -27, -2, 46, 100, 56, 7 }, { 58, 29, 83, 148, -3, -25 } };
const int QuadraticCoefficientsOppositeColor[][8] = { const int QuadraticCoefficientsOppositeColor[][6] = {
{ 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 }, { 41, 41, 41, 41, 41, 41 }, { 37, 41, 41, 41, 41, 41 }, { 10, 62, 41, 41, 41, 41 },
{ 57, 64, 39, 41, 41, 41 }, { 50, 40, 23, -22, 41, 41 }, { 106, 101, 3, 151, 171, 41 } }; { 57, 64, 39, 41, 41, 41 }, { 50, 40, 23, -22, 41, 41 }, { 106, 101, 3, 151, 171, 41 } };
// Endgame evaluation and scaling functions accessed direcly and not through // Named endgame evaluation and scaling functions, these
// the function maps because correspond to more then one material hash key. // are accessed direcly and not through the function maps.
Endgame<KmmKm> EvaluateKmmKm[] = { Endgame<KmmKm>(WHITE), Endgame<KmmKm>(BLACK) }; EvaluationFunction<KmmKm> EvaluateKmmKm(WHITE);
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(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);
Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) }; typedef EndgameEvaluationFunctionBase EF;
Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) }; typedef EndgameScalingFunctionBase SF;
Endgame<KPsK> ScaleKPsK[] = { Endgame<KPsK>(WHITE), Endgame<KPsK>(BLACK) };
Endgame<KPKP> ScaleKPKP[] = { Endgame<KPKP>(WHITE), Endgame<KPKP>(BLACK) };
// Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.non_pawn_material(Them) == VALUE_ZERO
&& pos.piece_count(Them, PAWN) == 0
&& pos.non_pawn_material(Us) >= RookValueMidgame;
} }
template<Color Us> bool is_KBPsKs(const Position& pos) {
return pos.non_pawn_material(Us) == BishopValueMidgame
&& pos.piece_count(Us, BISHOP) == 1
&& pos.piece_count(Us, PAWN) >= 1;
}
template<Color Us> bool is_KQKRPs(const Position& pos) { ////
const Color Them = (Us == WHITE ? BLACK : WHITE); //// Classes
return pos.piece_count(Us, PAWN) == 0 ////
&& pos.non_pawn_material(Us) == QueenValueMidgame
&& pos.piece_count(Us, QUEEN) == 1
&& pos.piece_count(Them, ROOK) == 1
&& pos.piece_count(Them, PAWN) >= 1;
}
} // namespace /// EndgameFunctions class stores endgame evaluation and scaling functions
/// in two std::map. Because STL library is not guaranteed to be thread
/// safe even for read access, the maps, although with identical content,
/// are replicated for each thread. This is faster then using locks.
class EndgameFunctions {
public:
EndgameFunctions();
~EndgameFunctions();
template<class T> T* get(Key key) const;
private:
template<class T> void add(const string& keyCode);
static Key buildKey(const string& keyCode);
static const string swapColors(const string& keyCode);
// Here we store two maps, for evaluate and scaling functions
pair<map<Key, EF*>, map<Key, SF*> > maps;
// Maps accessing functions returning const and non-const references
template<typename T> const map<Key, T*>& get() const { return maps.first; }
template<typename T> map<Key, T*>& get() { return maps.first; }
};
// 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; }
template<> map<Key, SF*>&
EndgameFunctions::get<SF>() { return maps.second; }
/// MaterialInfoTable c'tor and d'tor allocate and free the space for Endgames ////
//// Functions
////
void MaterialInfoTable::init() { Base::init(); if (!funcs) funcs = new Endgames(); } /// MaterialInfoTable c'tor and d'tor, called once by each thread
MaterialInfoTable::~MaterialInfoTable() { delete funcs; }
MaterialInfoTable::MaterialInfoTable(unsigned int numOfEntries) {
/// MaterialInfoTable::material_info() takes a position object as input, size = numOfEntries;
/// computes or looks up a MaterialInfo object, and returns a pointer to it. entries = new MaterialInfo[size];
/// If the material configuration is not already present in the table, it funcs = new EndgameFunctions();
/// is stored there, so we don't have to recompute everything when the
/// same material configuration occurs again.
MaterialInfo* MaterialInfoTable::material_info(const Position& pos) const { if (!entries || !funcs)
Key key = pos.material_key();
MaterialInfo* mi = probe(key);
// 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;
// Initialize MaterialInfo entry
memset(mi, 0, sizeof(MaterialInfo));
mi->key = key;
mi->factor[WHITE] = mi->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
// 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<Value>(key)) != NULL)
return mi;
if (is_KXK<WHITE>(pos))
{ {
mi->evaluationFunction = &EvaluateKXK[WHITE]; cerr << "Failed to allocate " << numOfEntries * sizeof(MaterialInfo)
return mi; << " bytes for material hash table." << endl;
} Application::exit_with_failure();
if (is_KXK<BLACK>(pos))
{
mi->evaluationFunction = &EvaluateKXK[BLACK];
return mi;
}
if (!pos.pieces(PAWN) && !pos.pieces(ROOK) && !pos.pieces(QUEEN))
{
// 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[pos.side_to_move()];
return mi;
} }
} }
// OK, we didn't find any special evaluation function for the current MaterialInfoTable::~MaterialInfoTable() {
// material configuration. Is there a suitable scaling function?
//
// We face problems when there are several conflicting applicable
// scaling functions and we need to decide which one to use.
EndgameBase<ScaleFactor>* sf;
if ((sf = funcs->get<ScaleFactor>(key)) != NULL) delete funcs;
{ delete [] entries;
mi->scalingFunction[sf->color()] = sf;
return mi;
}
// Generic scaling functions that refer to more then one material
// distribution. Should be probed after the specialized ones.
// Note that these ones don't return after setting the function.
if (is_KBPsKs<WHITE>(pos))
mi->scalingFunction[WHITE] = &ScaleKBPsK[WHITE];
if (is_KBPsKs<BLACK>(pos))
mi->scalingFunction[BLACK] = &ScaleKBPsK[BLACK];
if (is_KQKRPs<WHITE>(pos))
mi->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE];
else if (is_KQKRPs<BLACK>(pos))
mi->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK];
Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK);
if (npm_w + npm_b == VALUE_ZERO)
{
if (pos.piece_count(BLACK, PAWN) == 0)
{
assert(pos.piece_count(WHITE, PAWN) >= 2);
mi->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
}
else if (pos.piece_count(WHITE, PAWN) == 0)
{
assert(pos.piece_count(BLACK, PAWN) >= 2);
mi->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
}
else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1)
{
// This is a special case because we set scaling functions
// for both colors instead of only one.
mi->scalingFunction[WHITE] = &ScaleKPKP[WHITE];
mi->scalingFunction[BLACK] = &ScaleKPKP[BLACK];
}
}
// No pawns makes it difficult to win, even with a material advantage
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMidgame)
{
mi->factor[WHITE] = uint8_t
(npm_w == npm_b || npm_w < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]);
}
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMidgame)
{
mi->factor[BLACK] = uint8_t
(npm_w == npm_b || npm_b < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]);
}
// Compute the space weight
if (npm_w + npm_b >= 2 * QueenValueMidgame + 4 * RookValueMidgame + 2 * KnightValueMidgame)
{
int minorPieceCount = pos.piece_count(WHITE, KNIGHT) + pos.piece_count(WHITE, BISHOP)
+ pos.piece_count(BLACK, KNIGHT) + pos.piece_count(BLACK, BISHOP);
mi->spaceWeight = minorPieceCount * minorPieceCount;
}
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
// for the bishop pair "extended piece", this allow us to be more flexible
// in defining bishop pair bonuses.
const int pieceCount[2][8] = {
{ 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) } };
mi->value = int16_t((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
return mi;
}
/// MaterialInfoTable::imbalance() calculates imbalance comparing piece count of each
/// piece type for both colors.
template<Color Us>
int MaterialInfoTable::imbalance(const int pieceCount[][8]) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
int pt1, pt2, pc, v;
int value = 0;
// Redundancy of major pieces, formula based on Kaufman's paper
// "The Evaluation of Material Imbalances in Chess"
if (pieceCount[Us][ROOK] > 0)
value -= RedundantRookPenalty * (pieceCount[Us][ROOK] - 1)
+ RedundantQueenPenalty * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
{
pc = pieceCount[Us][pt1];
if (!pc)
continue;
v = LinearCoefficients[pt1];
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
value += pc * v;
}
return value;
} }
@@ -280,7 +142,289 @@ Phase MaterialInfoTable::game_phase(const Position& pos) {
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK); Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
return npm >= MidgameLimit ? PHASE_MIDGAME if (npm >= MidgameLimit)
: npm <= EndgameLimit ? PHASE_ENDGAME return PHASE_MIDGAME;
: Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit)); 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;
}
}
// OK, we didn't find any special evaluation function for the current
// material configuration. Is there a suitable scaling function?
//
// The code below is rather messy, and it could easily get worse later,
// if we decide to add more special cases. We face problems when there
// are several conflicting applicable scaling functions and we need to
// decide which one to use.
SF* sf;
if ((sf = funcs->get<SF>(key)) != NULL)
{
mi->scalingFunction[sf->color()] = sf;
return mi;
}
// Generic scaling functions that refer to more then one material
// distribution. Should be probed after the specialized ones.
// Note that these ones don't return after setting the function.
if ( pos.non_pawn_material(WHITE) == BishopValueMidgame
&& pos.piece_count(WHITE, BISHOP) == 1
&& pos.piece_count(WHITE, PAWN) >= 1)
mi->scalingFunction[WHITE] = &ScaleKBPsK;
if ( pos.non_pawn_material(BLACK) == BishopValueMidgame
&& pos.piece_count(BLACK, BISHOP) == 1
&& pos.piece_count(BLACK, PAWN) >= 1)
mi->scalingFunction[BLACK] = &ScaleKKBPs;
if ( pos.piece_count(WHITE, PAWN) == 0
&& pos.non_pawn_material(WHITE) == QueenValueMidgame
&& pos.piece_count(WHITE, QUEEN) == 1
&& pos.piece_count(BLACK, ROOK) == 1
&& pos.piece_count(BLACK, PAWN) >= 1)
mi->scalingFunction[WHITE] = &ScaleKQKRPs;
else if ( pos.piece_count(BLACK, PAWN) == 0
&& pos.non_pawn_material(BLACK) == QueenValueMidgame
&& pos.piece_count(BLACK, QUEEN) == 1
&& pos.piece_count(WHITE, ROOK) == 1
&& pos.piece_count(WHITE, PAWN) >= 1)
mi->scalingFunction[BLACK] = &ScaleKRPsKQ;
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == Value(0))
{
if (pos.piece_count(BLACK, PAWN) == 0)
{
assert(pos.piece_count(WHITE, PAWN) >= 2);
mi->scalingFunction[WHITE] = &ScaleKPsK;
}
else if (pos.piece_count(WHITE, PAWN) == 0)
{
assert(pos.piece_count(BLACK, PAWN) >= 2);
mi->scalingFunction[BLACK] = &ScaleKKPs;
}
else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1)
{
// This is a special case because we set scaling functions
// for both colors instead of only one.
mi->scalingFunction[WHITE] = &ScaleKPKPw;
mi->scalingFunction[BLACK] = &ScaleKPKPb;
}
}
// Compute the space weight
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >=
2*QueenValueMidgame + 4*RookValueMidgame + 2*KnightValueMidgame)
{
int minorPieceCount = pos.piece_count(WHITE, KNIGHT)
+ pos.piece_count(BLACK, KNIGHT)
+ pos.piece_count(WHITE, BISHOP)
+ pos.piece_count(BLACK, BISHOP);
mi->spaceWeight = minorPieceCount * minorPieceCount;
}
// Evaluate the material balance
const int pieceCount[2][6] = { { pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
pos.piece_count(WHITE, BISHOP), pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) },
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
pos.piece_count(BLACK, BISHOP), pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
Color c, them;
int sign, pt1, pt2, pc;
int v, vv, matValue = 0;
for (c = WHITE, sign = 1; c <= BLACK; c++, sign = -sign)
{
// No pawns makes it difficult to win, even with a material advantage
if ( pos.piece_count(c, PAWN) == 0
&& pos.non_pawn_material(c) - pos.non_pawn_material(opposite_color(c)) <= BishopValueMidgame)
{
if ( pos.non_pawn_material(c) == pos.non_pawn_material(opposite_color(c))
|| pos.non_pawn_material(c) < RookValueMidgame)
mi->factor[c] = 0;
else
{
switch (pos.piece_count(c, BISHOP)) {
case 2:
mi->factor[c] = 32;
break;
case 1:
mi->factor[c] = 12;
break;
case 0:
mi->factor[c] = 6;
break;
}
}
}
// Redundancy of major pieces, formula based on Kaufman's paper
// "The Evaluation of Material Imbalances in Chess"
// http://mywebpages.comcast.net/danheisman/Articles/evaluation_of_material_imbalance.htm
if (pieceCount[c][ROOK] >= 1)
matValue -= sign * ((pieceCount[c][ROOK] - 1) * RedundantRookPenalty + pieceCount[c][QUEEN] * RedundantQueenPenalty);
them = opposite_color(c);
v = 0;
// Second-degree polynomial material imbalance by Tord Romstad
//
// We use NO_PIECE_TYPE as a place holder for the bishop pair "extended piece",
// this allow us to be more flexible in defining bishop pair bonuses.
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
{
pc = pieceCount[c][pt1];
if (!pc)
continue;
vv = LinearCoefficients[pt1];
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
vv += pieceCount[c][pt2] * QuadraticCoefficientsSameColor[pt1][pt2]
+ pieceCount[them][pt2] * QuadraticCoefficientsOppositeColor[pt1][pt2];
v += pc * vv;
}
matValue += sign * v;
}
mi->value = int16_t(matValue / 16);
return mi;
}
/// 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);
} }
+84 -31
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,22 +17,22 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(MATERIAL_H_INCLUDED) #if !defined(MATERIAL_H_INCLUDED)
#define MATERIAL_H_INCLUDED #define MATERIAL_H_INCLUDED
////
//// Includes
////
#include "endgame.h" #include "endgame.h"
#include "position.h" #include "position.h"
#include "tt.h" #include "scale.h"
#include "types.h"
const int MaterialTableSize = 8192;
/// Game phase
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
};
////
//// Types
////
/// MaterialInfo is a class which contains various information about a /// MaterialInfo is a class which contains various information about a
/// material configuration. It contains a material balance evaluation, /// material configuration. It contains a material balance evaluation,
@@ -49,6 +49,8 @@ class MaterialInfo {
friend class MaterialInfoTable; friend class MaterialInfoTable;
public: public:
MaterialInfo() : key(0) { clear(); }
Score material_value() const; Score material_value() const;
ScaleFactor scale_factor(const Position& pos, Color c) const; ScaleFactor scale_factor(const Position& pos, Color c) const;
int space_weight() const; int space_weight() const;
@@ -57,34 +59,66 @@ public:
Value evaluate(const Position& pos) const; Value evaluate(const Position& pos) const;
private: private:
inline void clear();
Key key; Key key;
int16_t value; int16_t value;
uint8_t factor[2]; uint8_t factor[2];
EndgameBase<Value>* evaluationFunction; EndgameEvaluationFunctionBase* evaluationFunction;
EndgameBase<ScaleFactor>* scalingFunction[2]; EndgameScalingFunctionBase* scalingFunction[2];
int spaceWeight; int spaceWeight;
Phase gamePhase; Phase gamePhase;
}; };
/// 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;
/// The MaterialInfoTable class represents a pawn hash table. The most important class MaterialInfoTable {
/// method is material_info(), which returns a pointer to a MaterialInfo object.
class MaterialInfoTable : public SimpleHash<MaterialInfo, MaterialTableSize> {
public: public:
MaterialInfoTable(unsigned numOfEntries);
~MaterialInfoTable(); ~MaterialInfoTable();
void init(); MaterialInfo* get_material_info(const Position& pos);
MaterialInfo* material_info(const Position& pos) const;
static Phase game_phase(const Position& pos); static Phase game_phase(const Position& pos);
private: private:
template<Color Us> unsigned size;
static int imbalance(const int pieceCount[][8]); MaterialInfo* entries;
EndgameFunctions* funcs;
Endgames* 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 /// 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 /// 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 /// position in addition to the color, because the scale factor need not
@@ -94,31 +128,50 @@ private:
inline ScaleFactor MaterialInfo::scale_factor(const Position& pos, Color c) const { inline ScaleFactor MaterialInfo::scale_factor(const Position& pos, Color c) const {
if (!scalingFunction[c]) if (scalingFunction[c] != NULL)
{
ScaleFactor sf = scalingFunction[c]->apply(pos);
if (sf != SCALE_FACTOR_NONE)
return sf;
}
return ScaleFactor(factor[c]); return ScaleFactor(factor[c]);
ScaleFactor sf = (*scalingFunction[c])(pos);
return sf == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) : sf;
} }
inline Value MaterialInfo::evaluate(const Position& pos) const {
return (*evaluationFunction)(pos);
}
inline Score MaterialInfo::material_value() const { /// MaterialInfo::space_weight() simply returns the weight for the space
return make_score(value, value); /// evaluation for this material configuration.
}
inline int MaterialInfo::space_weight() const { inline int MaterialInfo::space_weight() const {
return spaceWeight; return spaceWeight;
} }
/// MaterialInfo::game_phase() returns the game phase according
/// to this material configuration.
inline Phase MaterialInfo::game_phase() const { inline Phase MaterialInfo::game_phase() const {
return gamePhase; 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 { inline bool MaterialInfo::specialized_eval_exists() const {
return evaluationFunction != NULL; 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) #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)
+191 -117
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,29 +17,25 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if defined(_MSC_VER)
#define _CRT_SECURE_NO_DEPRECATE ////
#define NOMINMAX // disable macros min() and max() //// Includes
#include <windows.h> ////
#include <sys/timeb.h>
#else #if !defined(_MSC_VER)
# include <sys/time.h> # include <sys/time.h>
# include <sys/types.h> # include <sys/types.h>
# include <unistd.h> # include <unistd.h>
# if defined(__hpux)
# include <sys/pstat.h> #else
# endif
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <sys/timeb.h>
#endif #endif
#if !defined(NO_PREFETCH)
# include <xmmintrin.h>
#endif
#include <algorithm>
#include <cassert> #include <cassert>
#include <cstdio> #include <cstdio>
#include <iomanip> #include <iomanip>
@@ -48,73 +44,128 @@
#include "bitcount.h" #include "bitcount.h"
#include "misc.h" #include "misc.h"
#include "thread.h"
using namespace std; using namespace std;
/// Version number. If Version is left empty, then Tag plus current /// Version number. If this is left empty, the current date (in the format
/// date (in the format YYMMDD) is used as a version number. /// YYMMDD) is used as a version number.
static const string Version = "2.2.1"; static const string EngineVersion = "1.6.3";
static const string Tag = ""; static const string AppName = "Stockfish";
static const string AppTag = "";
/// engine_info() returns the full name of the current Stockfish version. ////
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when //// Variables
/// the program was compiled) or "Stockfish <version number>", depending ////
/// on whether Version is empty.
const string engine_info(bool to_uci) { bool Chess960;
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); uint64_t dbg_cnt0 = 0;
const string cpu64(Is64Bit ? " 64bit" : ""); uint64_t dbg_cnt1 = 0;
const string popcnt(HasPopCnt ? " SSE4.2" : "");
string month, day, year; bool dbg_show_mean = false;
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008" bool dbg_show_hit_rate = false;
if (Version.empty())
{
date >> month >> day >> year;
s << "Stockfish " << Tag ////
<< setfill('0') << " " << year.substr(2) //// Functions
<< setw(2) << (1 + months.find(month) / 4) ////
<< setw(2) << day << cpu64 << popcnt;
void dbg_hit_on(bool b) {
assert(!dbg_show_mean);
dbg_show_hit_rate = true;
dbg_cnt0++;
if (b)
dbg_cnt1++;
} }
else
s << "Stockfish " << Version << cpu64 << popcnt;
s << (to_uci ? "\nid author ": " by ") void dbg_hit_on_c(bool c, bool b) {
<< "Tord Romstad, Marco Costalba and Joona Kiiski";
if (c)
dbg_hit_on(b);
}
void dbg_before() {
assert(!dbg_show_mean);
dbg_show_hit_rate = true;
dbg_cnt0++;
}
void dbg_after() {
assert(!dbg_show_mean);
dbg_show_hit_rate = true;
dbg_cnt1++;
}
void dbg_mean_of(int v) {
assert(!dbg_show_hit_rate);
dbg_show_mean = true;
dbg_cnt0++;
dbg_cnt1 += v;
}
void dbg_print_hit_rate() {
cout << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
<< " hit rate (%) " << (dbg_cnt1*100)/(dbg_cnt0 ? dbg_cnt0 : 1) << endl;
}
void dbg_print_mean() {
cout << "Total " << dbg_cnt0 << " Mean "
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl;
}
void dbg_print_hit_rate(ofstream& logFile) {
logFile << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
<< " hit rate (%) " << (dbg_cnt1*100)/(dbg_cnt0 ? dbg_cnt0 : 1) << endl;
}
void dbg_print_mean(ofstream& logFile) {
logFile << "Total " << dbg_cnt0 << " Mean "
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl;
}
/// engine_name() returns the full name of the current Stockfish version.
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when the
/// program was compiled) or "Stockfish <version number>", depending on whether
/// the constant EngineVersion (defined in misc.h) is empty.
const string engine_name() {
const string cpu64(CpuHas64BitPath ? " 64bit" : "");
if (!EngineVersion.empty())
return AppName+ " " + EngineVersion + cpu64;
string date(__DATE__); // From compiler, format is "Sep 21 2008"
string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
size_t mon = 1 + months.find(date.substr(0, 3)) / 4;
stringstream s;
string day = (date[4] == ' ' ? date.substr(5, 1) : date.substr(4, 2));
string name = AppName + " " + AppTag + " ";
s << name << date.substr(date.length() - 2) << setfill('0')
<< setw(2) << mon << setw(2) << day << cpu64;
return s.str(); return s.str();
} }
/// Debug functions used mainly to collect run-time statistics /// get_system_time() returns the current system time, measured in
/// milliseconds.
static uint64_t hits[2], means[2]; int get_system_time() {
void dbg_hit_on(bool b) { hits[0]++; if (b) hits[1]++; }
void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
void dbg_mean_of(int v) { means[0]++; means[1] += v; }
void dbg_print() {
if (hits[0])
cerr << "Total " << hits[0] << " Hits " << hits[1]
<< " hit rate (%) " << 100 * hits[1] / hits[0] << endl;
if (means[0])
cerr << "Total " << means[0] << " Mean "
<< (float)means[1] / means[0] << endl;
}
/// system_time() returns the current system time, measured in milliseconds
int system_time() {
#if defined(_MSC_VER) #if defined(_MSC_VER)
struct _timeb t; struct _timeb t;
@@ -128,77 +179,100 @@ int system_time() {
} }
/// cpu_count() tries to detect the number of CPU cores /// cpu_count() tries to detect the number of CPU cores.
int cpu_count() { #if !defined(_MSC_VER)
#if defined(_MSC_VER)
SYSTEM_INFO s;
GetSystemInfo(&s);
return std::min(int(s.dwNumberOfProcessors), MAX_THREADS);
#else
# if defined(_SC_NPROCESSORS_ONLN) # if defined(_SC_NPROCESSORS_ONLN)
return std::min((int)sysconf(_SC_NPROCESSORS_ONLN), MAX_THREADS); int cpu_count() {
# elif defined(__hpux) return Min(sysconf(_SC_NPROCESSORS_ONLN), 8);
struct pst_dynamic psd; }
if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1)
return 1;
return std::min((int)psd.psd_proc_cnt, MAX_THREADS);
# else # else
int cpu_count() {
return 1; return 1;
}
# endif # endif
#endif #else
int cpu_count() {
SYSTEM_INFO s;
GetSystemInfo(&s);
return Min(s.dwNumberOfProcessors, 8);
} }
#endif
/// timed_wait() waits for msec milliseconds. It is mainly an helper to wrap
/// conversion from milliseconds to struct timespec, as used by pthreads.
void timed_wait(WaitCondition* sleepCond, Lock* sleepLock, int msec) { /*
From Beowulf, from Olithink
#if defined(_MSC_VER) */
int tm = msec; #ifndef _WIN32
#else /* Non-windows version */
struct timeval t; int Bioskey()
struct timespec abstime, *tm = &abstime;
gettimeofday(&t, NULL);
abstime.tv_sec = t.tv_sec + (msec / 1000);
abstime.tv_nsec = (t.tv_usec + (msec % 1000) * 1000) * 1000;
if (abstime.tv_nsec > 1000000000LL)
{ {
abstime.tv_sec += 1; fd_set readfds;
abstime.tv_nsec -= 1000000000LL; 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));
} }
#endif
cond_timedwait(sleepCond, sleepLock, tm);
}
/// prefetch() preloads the given address in L1/L2 cache. This is a non
/// blocking function and do not stalls the CPU waiting for data to be
/// loaded from memory, that can be quite slow.
#if defined(NO_PREFETCH)
void prefetch(char*) {}
#else #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 */
void prefetch(char* addr) { #if defined(FILE_CNT)
if (stdin->_cnt > 0)
# if defined(__INTEL_COMPILER) || defined(__ICL) return stdin->_cnt;
// This hack prevents prefetches to be optimized away by
// Intel compiler. Both MSVC and gcc seems not affected.
__asm__ ("");
#endif #endif
if (!init) {
_mm_prefetch(addr, _MM_HINT_T2); init = 1;
_mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead 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 #endif
+47 -17
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,34 +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/>.
*/ */
#if !defined(MISC_H_INCLUDED) #if !defined(MISC_H_INCLUDED)
#define MISC_H_INCLUDED #define MISC_H_INCLUDED
////
//// Includes
////
#include <fstream> #include <fstream>
#include <string> #include <string>
#include "lock.h" #include "application.h"
#include "types.h" #include "types.h"
extern const std::string engine_info(bool to_uci = false); ////
extern int system_time(); //// Macros
////
#define Min(x, y) (((x) < (y))? (x) : (y))
#define Max(x, y) (((x) < (y))? (y) : (x))
////
//// Variables
////
extern bool Chess960;
////
//// Prototypes
////
extern const std::string engine_name();
extern int get_system_time();
extern int cpu_count(); extern int cpu_count();
extern void timed_wait(WaitCondition*, Lock*, int); extern int Bioskey();
extern void prefetch(char* addr);
////
//// Debug
////
extern bool dbg_show_mean;
extern bool dbg_show_hit_rate;
extern uint64_t dbg_cnt0;
extern uint64_t dbg_cnt1;
extern void dbg_hit_on(bool b); extern void dbg_hit_on(bool b);
extern void dbg_hit_on_c(bool c, 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_mean_of(int v);
extern void dbg_print(); extern void dbg_print_hit_rate();
extern void dbg_print_mean();
class Position; extern void dbg_print_hit_rate(std::ofstream& logFile);
extern Move move_from_uci(const Position& pos, const std::string& str); extern void dbg_print_mean(std::ofstream& logFile);
extern const std::string move_to_uci(Move m, bool chess960);
extern const std::string move_to_san(Position& pos, Move m);
struct Log : public std::ofstream {
Log(const std::string& f = "log.txt") : std::ofstream(f.c_str(), std::ios::out | std::ios::app) {}
~Log() { if (is_open()) close(); }
};
#endif // !defined(MISC_H_INCLUDED) #endif // !defined(MISC_H_INCLUDED)
+115 -122
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,143 +17,136 @@
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 <cassert>
#include <cstring>
#include <string>
#include "movegen.h" ////
//// Includes
////
#include <cassert>
#include "move.h"
#include "piece.h"
#include "position.h" #include "position.h"
using std::string;
/// move_to_uci() converts a move to a string in coordinate notation ////
//// 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 /// (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 /// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
/// Chess960 mode. Instead internally Move is coded as "king captures rook". /// Chess960 mode.
const string move_to_uci(Move m, bool chess960) { const std::string move_to_string(Move move) {
Square from = from_sq(m); std::string str;
Square to = to_sq(m); Square from = move_from(move);
string promotion; Square to = move_to(move);
if (m == MOVE_NONE) if (move == MOVE_NONE)
return "(none)"; str = "(none)";
else if (move == MOVE_NULL)
if (m == MOVE_NULL) str = "0000";
return "0000";
if (is_castle(m) && !chess960)
to = from + (file_of(to) == FILE_H ? Square(2) : -Square(2));
if (is_promotion(m))
promotion = char(tolower(piece_type_to_char(promotion_piece_type(m))));
return square_to_string(from) + square_to_string(to) + promotion;
}
/// move_from_uci() takes a position and a string representing a move in
/// simple coordinate notation and returns an equivalent Move if any.
/// Moves are guaranteed to be legal.
Move move_from_uci(const Position& pos, const string& str) {
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
if (str == move_to_uci(ml.move(), pos.is_chess960()))
return ml.move();
return MOVE_NONE;
}
/// move_to_san() takes a position and a move as input, where it is assumed
/// that the move is a legal move for the position. The return value is
/// a string containing the move in short algebraic notation.
const string move_to_san(Position& pos, Move m) {
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "(null)";
assert(is_ok(m));
Bitboard attackers;
bool ambiguousMove, ambiguousFile, ambiguousRank;
Square sq, from = from_sq(m);
Square to = to_sq(m);
PieceType pt = type_of(pos.piece_on(from));
string san;
if (is_castle(m))
san = (to_sq(m) < from_sq(m) ? "O-O-O" : "O-O");
else else
{ {
if (pt != PAWN) if (!Chess960)
{ {
san = piece_type_to_char(pt); if (move_is_short_castle(move))
return (from == SQ_E1 ? "e1g1" : "e8g8");
// Disambiguation if we have more then one piece with destination 'to' if (move_is_long_castle(move))
// note that for pawns is not needed because starting file is explicit. return (from == SQ_E1 ? "e1c1" : "e8c8");
attackers = pos.attackers_to(to) & pos.pieces(pt, pos.side_to_move()); }
clear_bit(&attackers, from); str = square_to_string(from) + square_to_string(to);
ambiguousMove = ambiguousFile = ambiguousRank = false; if (move_is_promotion(move))
str += piece_type_to_char(move_promotion_piece(move), false);
while (attackers) }
{ return str;
sq = pop_1st_bit(&attackers);
// Pinned pieces are not included in the possible sub-set
if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces()))
continue;
if (file_of(sq) == file_of(from))
ambiguousFile = true;
if (rank_of(sq) == rank_of(from))
ambiguousRank = true;
ambiguousMove = true;
} }
if (ambiguousMove)
{ /// Overload the << operator, to make it easier to print moves.
if (!ambiguousFile)
san += file_to_char(file_of(from)); std::ostream &operator << (std::ostream& os, Move m) {
else if (!ambiguousRank)
san += rank_to_char(rank_of(from)); return os << move_to_string(m);
else
san += square_to_string(from);
}
} }
if (pos.is_capture(m))
{
if (pt == PAWN)
san += file_to_char(file_of(from));
san += 'x'; /// move_is_ok(), for debugging.
}
bool move_is_ok(Move m) {
san += square_to_string(to);
return square_is_ok(move_from(m)) && square_is_ok(move_to(m));
if (is_promotion(m))
{
san += '=';
san += piece_type_to_char(promotion_piece_type(m));
}
}
// The move gives check? We don't use pos.move_gives_check() here
// because we need to test for a mate after the move is done.
StateInfo st;
pos.do_move(m, st);
if (pos.in_check())
san += pos.is_mate() ? "#" : "+";
pos.undo_move(m);
return san;
} }
+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)
+434 -278
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,12 +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/>.
*/ */
////
//// Includes
////
#include <cassert> #include <cassert>
#include <algorithm>
#include "bitcount.h" #include "bitcount.h"
#include "movegen.h" #include "movegen.h"
#include "position.h"
// Simple macro to wrap a very common while loop, no facny, no flexibility, // Simple macro to wrap a very common while loop, no facny, no flexibility,
// hardcoded list name 'mlist' and from square 'from'. // hardcoded list name 'mlist' and from square 'from'.
@@ -31,6 +34,10 @@
// Version used for pawns, where the 'from' square is given as a delta from the 'to' square // Version used for pawns, where the 'from' square is given as a delta from the 'to' square
#define SERIALIZE_MOVES_D(b, d) while (b) { to = pop_1st_bit(&b); (*mlist++).move = make_move(to + (d), to); } #define SERIALIZE_MOVES_D(b, d) while (b) { to = pop_1st_bit(&b); (*mlist++).move = make_move(to + (d), to); }
////
//// Local definitions
////
namespace { namespace {
enum CastlingSide { enum CastlingSide {
@@ -38,178 +45,123 @@ namespace {
QUEEN_SIDE QUEEN_SIDE
}; };
template<CastlingSide> enum MoveType {
MoveStack* generate_castle_moves(const Position&, MoveStack*, Color us); CAPTURE,
NON_CAPTURE,
CHECK,
EVASION
};
template<Color, MoveType> // Helper templates
template<CastlingSide Side>
MoveStack* generate_castle_moves(const Position&, MoveStack*);
template<Color Us, MoveType Type>
MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square); MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square);
template<PieceType Pt> // Template generate_piece_moves (captures and non-captures) with specializations and overloads
inline MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) { template<PieceType>
MoveStack* generate_piece_moves(const Position&, MoveStack*, Color, Bitboard);
assert(Pt != QUEEN && Pt != PAWN);
Bitboard b = pos.attacks_from<Pt>(from) & pos.empty_squares();
if (Pt == KING)
b &= ~QueenPseudoAttacks[pos.king_square(flip(pos.side_to_move()))];
SERIALIZE_MOVES(b);
return mlist;
}
template<PieceType Pt>
inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
Bitboard dc, Square ksq) {
assert(Pt != KING && Pt != PAWN);
Bitboard checkSqs, b;
Square from;
const Square* pl = pos.piece_list(us, Pt);
if ((from = *pl++) == SQ_NONE)
return mlist;
checkSqs = pos.attacks_from<Pt>(ksq) & pos.empty_squares();
do
{
if ( (Pt == QUEEN && !(QueenPseudoAttacks[from] & checkSqs))
|| (Pt == ROOK && !(RookPseudoAttacks[from] & checkSqs))
|| (Pt == BISHOP && !(BishopPseudoAttacks[from] & checkSqs)))
continue;
if (dc && bit_is_set(dc, from))
continue;
b = pos.attacks_from<Pt>(from) & checkSqs;
SERIALIZE_MOVES(b);
} while ((from = *pl++) != SQ_NONE);
return mlist;
}
template<> template<>
FORCE_INLINE MoveStack* generate_direct_checks<PAWN>(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) { MoveStack* generate_piece_moves<KING>(const Position&, MoveStack*, Color, Bitboard);
return (us == WHITE ? generate_pawn_moves<WHITE, MV_CHECK>(p, m, dc, ksq) template<PieceType Piece, MoveType Type>
: generate_pawn_moves<BLACK, MV_CHECK>(p, m, dc, ksq)); inline MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) {
}
template<PieceType Pt, MoveType Type> assert(Piece == PAWN);
FORCE_INLINE MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) { assert(Type == CAPTURE || Type == NON_CAPTURE || Type == EVASION);
assert(Pt == PAWN);
return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m, t, SQ_NONE) return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m, t, SQ_NONE)
: generate_pawn_moves<BLACK, Type>(p, m, t, SQ_NONE)); : generate_pawn_moves<BLACK, Type>(p, m, t, SQ_NONE));
} }
template<PieceType Pt> // Templates for non-capture checks generation
FORCE_INLINE MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
Bitboard b; template<PieceType Piece>
Square from; MoveStack* generate_discovered_checks(const Position&, MoveStack*, Square);
const Square* pl = pos.piece_list(us, Pt);
if (*pl != SQ_NONE) template<PieceType>
{ MoveStack* generate_direct_checks(const Position&, MoveStack*, Color, Bitboard, Square);
do {
from = *pl;
b = pos.attacks_from<Pt>(from) & target;
SERIALIZE_MOVES(b);
} while (*++pl != SQ_NONE);
}
return mlist;
}
template<> template<>
FORCE_INLINE MoveStack* generate_piece_moves<KING>(const Position& pos, MoveStack* mlist, Color us, Bitboard target) { inline MoveStack* generate_direct_checks<PAWN>(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) {
Bitboard b; return (us == WHITE ? generate_pawn_moves<WHITE, CHECK>(p, m, dc, ksq)
Square from = pos.king_square(us); : generate_pawn_moves<BLACK, CHECK>(p, m, dc, ksq));
b = pos.attacks_from<KING>(from) & target;
SERIALIZE_MOVES(b);
return mlist;
} }
} }
/// generate<MV_CAPTURE> generates all pseudo-legal captures and queen ////
//// Functions
////
/// generate_captures() generates all pseudo-legal captures and queen
/// promotions. Returns a pointer to the end of the move list. /// promotions. Returns a pointer to the end of the move list.
///
/// generate<MV_NON_CAPTURE> generates all pseudo-legal non-captures and
/// underpromotions. Returns a pointer to the end of the move list.
///
/// generate<MV_NON_EVASION> generates all pseudo-legal captures and
/// non-captures. Returns a pointer to the end of the move list.
template<MoveType Type> MoveStack* generate_captures(const Position& pos, MoveStack* mlist) {
MoveStack* generate(const Position& pos, MoveStack* mlist) {
assert(Type == MV_CAPTURE || Type == MV_NON_CAPTURE || Type == MV_NON_EVASION); assert(pos.is_ok());
assert(!pos.in_check()); assert(!pos.is_check());
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Bitboard target; Bitboard target = pos.pieces_of_color(opposite_color(us));
if (Type == MV_CAPTURE) mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
target = pos.pieces(flip(us)); mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<PAWN, CAPTURE>(pos, mlist, us, target);
return generate_piece_moves<KING>(pos, mlist, us, target);
}
else if (Type == MV_NON_CAPTURE)
target = pos.empty_squares();
else if (Type == MV_NON_EVASION) /// generate_noncaptures() generates all pseudo-legal non-captures and
target = pos.pieces(flip(us)) | pos.empty_squares(); /// underpromotions. Returns a pointer to the end of the move list.
mlist = generate_piece_moves<PAWN, Type>(pos, mlist, us, target); MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.is_check());
Color us = pos.side_to_move();
Bitboard target = pos.empty_squares();
mlist = generate_piece_moves<PAWN, NON_CAPTURE>(pos, mlist, us, target);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target); mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target); mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target); mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target); mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
mlist = generate_piece_moves<KING>(pos, mlist, us, target); mlist = generate_piece_moves<KING>(pos, mlist, us, target);
mlist = generate_castle_moves<KING_SIDE>(pos, mlist);
if (Type != MV_CAPTURE && pos.can_castle(us)) return generate_castle_moves<QUEEN_SIDE>(pos, mlist);
{
if (pos.can_castle(us == WHITE ? WHITE_OO : BLACK_OO))
mlist = generate_castle_moves<KING_SIDE>(pos, mlist, us);
if (pos.can_castle(us == WHITE ? WHITE_OOO : BLACK_OOO))
mlist = generate_castle_moves<QUEEN_SIDE>(pos, mlist, us);
} }
return mlist;
}
// Explicit template instantiations /// generate_non_capture_checks() generates all pseudo-legal non-captures and knight
template MoveStack* generate<MV_CAPTURE>(const Position& pos, MoveStack* mlist);
template MoveStack* generate<MV_NON_CAPTURE>(const Position& pos, MoveStack* mlist);
template MoveStack* generate<MV_NON_EVASION>(const Position& pos, MoveStack* mlist);
/// generate<MV_NON_CAPTURE_CHECK> generates all pseudo-legal non-captures and knight
/// underpromotions that give check. Returns a pointer to the end of the move list. /// underpromotions that give check. Returns a pointer to the end of the move list.
template<>
MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist) {
assert(!pos.in_check()); MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.is_check());
Bitboard b, dc; Bitboard b, dc;
Square from; Square from;
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Square ksq = pos.king_square(flip(us)); Square ksq = pos.king_square(opposite_color(us));
assert(pos.piece_on(ksq) == make_piece(flip(us), KING)); assert(pos.piece_on(ksq) == piece_of_color_and_type(opposite_color(us), KING));
// Discovered non-capture checks // Discovered non-capture checks
b = dc = pos.discovered_check_candidates(); b = dc = pos.discovered_check_candidates(us);
while (b) while (b)
{ {
from = pop_1st_bit(&b); from = pop_1st_bit(&b);
switch (type_of(pos.piece_on(from))) switch (pos.type_of_piece_on(from))
{ {
case PAWN: /* Will be generated togheter with pawns direct checks */ break; case PAWN: /* Will be generated togheter with pawns direct checks */ break;
case KNIGHT: mlist = generate_discovered_checks<KNIGHT>(pos, mlist, from); break; case KNIGHT: mlist = generate_discovered_checks<KNIGHT>(pos, mlist, from); break;
@@ -229,12 +181,13 @@ MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist)
} }
/// generate<MV_EVASION> generates all pseudo-legal check evasions when the side /// generate_evasions() generates all pseudo-legal check evasions when
/// to move is in check. Returns a pointer to the end of the move list. /// the side to move is in check. Returns a pointer to the end of the move list.
template<>
MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
assert(pos.in_check()); MoveStack* generate_evasions(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(pos.is_check());
Bitboard b, target; Bitboard b, target;
Square from, checksq; Square from, checksq;
@@ -242,9 +195,9 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Square ksq = pos.king_square(us); Square ksq = pos.king_square(us);
Bitboard checkers = pos.checkers(); Bitboard checkers = pos.checkers();
Bitboard sliderAttacks = 0; Bitboard sliderAttacks = EmptyBoardBB;
assert(pos.piece_on(ksq) == make_piece(us, KING)); assert(pos.piece_on(ksq) == piece_of_color_and_type(us, KING));
assert(checkers); assert(checkers);
// Find squares attacked by slider checkers, we will remove // Find squares attacked by slider checkers, we will remove
@@ -256,31 +209,26 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
checkersCnt++; checkersCnt++;
checksq = pop_1st_bit(&b); checksq = pop_1st_bit(&b);
assert(color_of(pos.piece_on(checksq)) == flip(us)); assert(pos.color_of_piece_on(checksq) == opposite_color(us));
switch (type_of(pos.piece_on(checksq))) switch (pos.type_of_piece_on(checksq))
{ {
case BISHOP: sliderAttacks |= BishopPseudoAttacks[checksq]; break; case BISHOP: sliderAttacks |= BishopPseudoAttacks[checksq]; break;
case ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break; case ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break;
case QUEEN: case QUEEN:
// If queen and king are far we can safely remove all the squares attacked // In case of a queen remove also squares attacked in the other direction to
// in the other direction becuase are not reachable by the king anyway. // avoid possible illegal moves when queen and king are on adjacent squares.
if (squares_between(ksq, checksq) || (RookPseudoAttacks[checksq] & (1ULL << ksq))) if (direction_is_straight(checksq, ksq))
sliderAttacks |= QueenPseudoAttacks[checksq]; sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq);
// Otherwise, if king and queen are adjacent and on a diagonal line, we need to
// use real rook attacks to check if king is safe to move in the other direction.
// For example: king in B2, queen in A1 a knight in B1, and we can safely move to C1.
else else
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq); sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq);
default: default:
break; break;
} }
} while (b); } while (b);
// Generate evasions for king, capture and non capture moves // Generate evasions for king, capture and non capture moves
b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks; b = pos.attacks_from<KING>(ksq) & ~pos.pieces_of_color(us) & ~sliderAttacks;
from = ksq; from = ksq;
SERIALIZE_MOVES(b); SERIALIZE_MOVES(b);
@@ -292,7 +240,7 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
// checker piece is possible. // checker piece is possible.
target = squares_between(checksq, ksq) | checkers; target = squares_between(checksq, ksq) | checkers;
mlist = generate_piece_moves<PAWN, MV_EVASION>(pos, mlist, us, target); mlist = generate_piece_moves<PAWN, EVASION>(pos, mlist, us, target);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target); mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target); mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target); mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
@@ -300,238 +248,446 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
} }
/// generate<MV_LEGAL> computes a complete list of legal moves in the current position /// generate_moves() computes a complete list of legal or pseudo-legal moves in
/// the current position. This function is not very fast, and should be used
/// only in non time-critical paths.
template<> MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLegal) {
MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
MoveStack *last, *cur = mlist; MoveStack *last, *cur = mlist;
Bitboard pinned = pos.pinned_pieces(); Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
last = pos.in_check() ? generate<MV_EVASION>(pos, mlist) // Generate pseudo-legal moves
: generate<MV_NON_EVASION>(pos, mlist); if (pos.is_check())
last = generate_evasions(pos, mlist);
else
last = generate_noncaptures(pos, generate_captures(pos, mlist));
if (pseudoLegal)
return last;
// Remove illegal moves from the list // Remove illegal moves from the list
while (cur != last) while (cur != last)
if (!pos.pl_move_is_legal(cur->move, pinned)) if (pos.pl_move_is_legal(cur->move, pinned))
cur->move = (--last)->move;
else
cur++; cur++;
else
cur->move = (--last)->move;
return last; return last;
} }
namespace { /// move_is_legal() takes a position and a (not necessarily pseudo-legal)
/// move and tests whether the move is legal. This version is not very fast
/// and should be used only in non time-critical paths.
template<Square Delta> bool move_is_legal(const Position& pos, const Move m) {
inline Bitboard move_pawns(Bitboard p) {
return Delta == DELTA_N ? p << 8 : Delta == DELTA_S ? p >> 8 : MoveStack mlist[256];
Delta == DELTA_NE ? p << 9 : Delta == DELTA_SE ? p >> 7 : MoveStack *cur, *last = generate_moves(pos, mlist, true);
Delta == DELTA_NW ? p << 7 : Delta == DELTA_SW ? p >> 9 : p;
for (cur = mlist; cur != last; cur++)
if (cur->move == m)
return pos.pl_move_is_legal(m, pos.pinned_pieces(pos.side_to_move()));
return false;
} }
template<Square Delta>
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB); /// Fast version of move_is_legal() that takes a position a move and a
/// bitboard of pinned pieces as input, and tests whether the move is legal.
bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
assert(pos.is_ok());
assert(move_is_ok(m));
assert(pinned == pos.pinned_pieces(pos.side_to_move()));
Color us = pos.side_to_move();
Color them = opposite_color(us);
Square from = move_from(m);
Square to = move_to(m);
Piece pc = pos.piece_on(from);
// Use a slower but simpler function for uncommon cases
if (move_is_ep(m) || move_is_castle(m))
return move_is_legal(pos, m);
// If the from square is not occupied by a piece belonging to the side to
// move, the move is obviously not legal.
if (color_of_piece(pc) != us)
return false;
// The destination square cannot be occupied by a friendly piece
if (pos.color_of_piece_on(to) == us)
return false;
// Handle the special case of a pawn move
if (type_of_piece(pc) == PAWN)
{
// Move direction must be compatible with pawn color
int direction = to - from;
if ((us == WHITE) != (direction > 0))
return false;
// A pawn move is a promotion iff the destination square is
// on the 8/1th rank.
if (( (square_rank(to) == RANK_8 && us == WHITE)
||(square_rank(to) == RANK_1 && us != WHITE)) != bool(move_is_promotion(m)))
return false;
// Proceed according to the square delta between the origin and
// destination squares.
switch (direction)
{
case DELTA_NW:
case DELTA_NE:
case DELTA_SW:
case DELTA_SE:
// Capture. The destination square must be occupied by an enemy
// piece (en passant captures was handled earlier).
if (pos.color_of_piece_on(to) != them)
return false;
break;
case DELTA_N:
case DELTA_S:
// Pawn push. The destination square must be empty.
if (!pos.square_is_empty(to))
return false;
break;
case DELTA_NN:
// Double white pawn push. The destination square must be on the fourth
// rank, and both the destination square and the square between the
// source and destination squares must be empty.
if ( square_rank(to) != RANK_4
|| !pos.square_is_empty(to)
|| !pos.square_is_empty(from + DELTA_N))
return false;
break;
case DELTA_SS:
// Double black pawn push. The destination square must be on the fifth
// rank, and both the destination square and the square between the
// source and destination squares must be empty.
if ( square_rank(to) != RANK_5
|| !pos.square_is_empty(to)
|| !pos.square_is_empty(from + DELTA_S))
return false;
break;
default:
return false;
}
// The move is pseudo-legal, check if it is also legal
return pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned);
}
// Luckly we can handle all the other pieces in one go
return bit_is_set(pos.attacks_from(pc, from), to)
&& (pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned))
&& !move_is_promotion(m);
}
namespace {
template<PieceType Piece>
MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
Bitboard b; Bitboard b;
Square to; Square from;
const Square* ptr = pos.piece_list_begin(us, Piece);
// Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black) while ((from = *ptr++) != SQ_NONE)
b = move_pawns<Delta>(pawns) & target & ~TFileABB; {
SERIALIZE_MOVES_D(b, -Delta); b = pos.attacks_from<Piece>(from) & target;
SERIALIZE_MOVES(b);
}
return mlist; return mlist;
} }
template<MoveType Type, Square Delta> template<>
inline MoveStack* generate_promotions(const Position& pos, MoveStack* mlist, Bitboard pawnsOn7, Bitboard target) { MoveStack* generate_piece_moves<KING>(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
Bitboard b; Bitboard b;
Square from = pos.king_square(us);
b = pos.attacks_from<KING>(from) & target;
SERIALIZE_MOVES(b);
return mlist;
}
template<Color Us, SquareDelta Direction>
inline Bitboard move_pawns(Bitboard p) {
if (Direction == DELTA_N)
return Us == WHITE ? p << 8 : p >> 8;
else if (Direction == DELTA_NE)
return Us == WHITE ? p << 9 : p >> 7;
else if (Direction == DELTA_NW)
return Us == WHITE ? p << 7 : p >> 9;
else
return p;
}
template<Color Us, MoveType Type, SquareDelta Diagonal>
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces) {
// Calculate our parametrized parameters at compile time
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
const Bitboard TFileABB = (Diagonal == DELTA_NE ? FileABB : FileHBB);
const SquareDelta TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE);
const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
const SquareDelta TTDELTA_NE = (Diagonal == DELTA_NE ? TDELTA_NE : TDELTA_NW);
Bitboard b1, b2;
Square to; Square to;
// Promotions and under-promotions, both captures and non-captures // Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black)
b = move_pawns<Delta>(pawnsOn7) & target; b1 = move_pawns<Us, Diagonal>(pawns) & ~TFileABB & enemyPieces;
if (Delta != DELTA_N && Delta != DELTA_S) // Capturing promotions and under-promotions
b &= ~TFileABB; if (b1 & TRank8BB)
while (b)
{ {
to = pop_1st_bit(&b); b2 = b1 & TRank8BB;
b1 &= ~TRank8BB;
if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION) while (b2)
(*mlist++).move = make_promotion(to - Delta, to, QUEEN);
if (Type == MV_NON_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
{ {
(*mlist++).move = make_promotion(to - Delta, to, ROOK); to = pop_1st_bit(&b2);
(*mlist++).move = make_promotion(to - Delta, to, BISHOP);
(*mlist++).move = make_promotion(to - Delta, to, KNIGHT); if (Type == CAPTURE || Type == EVASION)
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, QUEEN);
if (Type == NON_CAPTURE || Type == EVASION)
{
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, ROOK);
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, BISHOP);
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT);
} }
// This is the only possible under promotion that can give a check // This is the only possible under promotion that can give a check
// not already included in the queen-promotion. // not already included in the queen-promotion. It is not sure that
if ( Type == MV_CHECK // the promoted knight will give check, but it doesn't worth to verify.
&& bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(Delta > 0 ? BLACK : WHITE))) if (Type == CHECK)
(*mlist++).move = make_promotion(to - Delta, to, KNIGHT); (*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT);
else (void)pos; // Silence a warning under MSVC
} }
}
// Serialize standard captures
if (Type == CAPTURE || Type == EVASION)
SERIALIZE_MOVES_D(b1, -TTDELTA_NE);
return mlist; return mlist;
} }
template<Color Us, MoveType Type> template<Color Us, MoveType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) { MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) {
// Calculate our parametrized parameters at compile time, named // Calculate our parametrized parameters at compile time
// according to the point of view of white side.
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square UP = (Us == WHITE ? DELTA_N : DELTA_S); const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S);
const Square RIGHT_UP = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square LEFT_UP = (Us == WHITE ? DELTA_NW : DELTA_SE);
Square to; Square to;
Bitboard b1, b2, dc1, dc2, pawnPushes, emptySquares; Bitboard b1, b2, enemyPieces, emptySquares;
Bitboard pawns = pos.pieces(PAWN, Us); Bitboard pawns = pos.pieces(PAWN, Us);
Bitboard pawnsOn7 = pawns & TRank7BB;
Bitboard enemyPieces = (Type == MV_CAPTURE ? target : pos.pieces(Them));
// Pre-calculate pawn pushes before changing emptySquares definition // Standard captures and capturing promotions and underpromotions
if (Type != MV_CAPTURE) if (Type == CAPTURE || Type == EVASION || (pawns & TRank7BB))
{ {
emptySquares = (Type == MV_NON_CAPTURE ? target : pos.empty_squares()); enemyPieces = (Type == CAPTURE ? target : pos.pieces_of_color(opposite_color(Us)));
pawnPushes = move_pawns<UP>(pawns & ~TRank7BB) & emptySquares;
}
if (Type == MV_EVASION) if (Type == EVASION)
{
emptySquares &= target; // Only blocking squares
enemyPieces &= target; // Capture only the checker piece enemyPieces &= target; // Capture only the checker piece
mlist = generate_pawn_captures<Us, Type, DELTA_NE>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<Us, Type, DELTA_NW>(mlist, pawns, enemyPieces);
} }
// Promotions and underpromotions // Non-capturing promotions and underpromotions
if (pawnsOn7) if (pawns & TRank7BB)
{ {
if (Type == MV_CAPTURE) b1 = move_pawns<Us, DELTA_N>(pawns) & TRank8BB & pos.empty_squares();
emptySquares = pos.empty_squares();
pawns &= ~TRank7BB; if (Type == EVASION)
mlist = generate_promotions<Type, RIGHT_UP>(pos, mlist, pawnsOn7, enemyPieces); b1 &= target; // Only blocking promotion pushes
mlist = generate_promotions<Type, LEFT_UP>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Type, UP>(pos, mlist, pawnsOn7, emptySquares);
}
// Standard captures while (b1)
if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
{ {
mlist = generate_pawn_captures<RIGHT_UP>(mlist, pawns, enemyPieces); to = pop_1st_bit(&b1);
mlist = generate_pawn_captures<LEFT_UP>(mlist, pawns, enemyPieces);
if (Type == CAPTURE || Type == EVASION)
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, QUEEN);
if (Type == NON_CAPTURE || Type == EVASION)
{
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, ROOK);
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, BISHOP);
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT);
} }
// This is the only possible under promotion that can give a check
// not already included in the queen-promotion.
if (Type == CHECK && bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(Them)))
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT);
}
}
// Standard pawn pushes and double pushes
if (Type != CAPTURE)
{
emptySquares = (Type == NON_CAPTURE ? target : pos.empty_squares());
// Single and double pawn pushes // Single and double pawn pushes
if (Type != MV_CAPTURE) b1 = move_pawns<Us, DELTA_N>(pawns) & emptySquares & ~TRank8BB;
{ b2 = move_pawns<Us, DELTA_N>(b1 & TRank3BB) & emptySquares;
b1 = (Type != MV_EVASION ? pawnPushes : pawnPushes & emptySquares);
b2 = move_pawns<UP>(pawnPushes & TRank3BB) & emptySquares;
if (Type == MV_CHECK) // Filter out unwanted pushes according to the move type
if (Type == EVASION)
{ {
// Consider only pawn moves which give direct checks b1 &= target;
b2 &= target;
}
else if (Type == CHECK)
{
// Pawn moves which give direct cheks
b1 &= pos.attacks_from<PAWN>(ksq, Them); b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(ksq, Them); b2 &= pos.attacks_from<PAWN>(ksq, Them);
// Add pawn moves which gives discovered check. This is possible only // Pawn moves which gives discovered check. This is possible only if
// if the pawn is not on the same file as the enemy king, because we // the pawn is not on the same file as the enemy king, because we
// don't generate captures. // don't generate captures.
if (pawns & target) // For CHECK type target is dc bitboard if (pawns & target) // For CHECK type target is dc bitboard
{ {
dc1 = move_pawns<UP>(pawns & target & ~file_bb(ksq)) & emptySquares; Bitboard dc1 = move_pawns<Us, DELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares & ~TRank8BB;
dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares; Bitboard dc2 = move_pawns<Us, DELTA_N>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1; b1 |= dc1;
b2 |= dc2; b2 |= dc2;
} }
} }
SERIALIZE_MOVES_D(b1, -UP); SERIALIZE_MOVES_D(b1, -TDELTA_N);
SERIALIZE_MOVES_D(b2, -UP -UP); SERIALIZE_MOVES_D(b2, -TDELTA_N -TDELTA_N);
} }
// En passant captures // En passant captures
if ( (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION) if ((Type == CAPTURE || Type == EVASION) && pos.ep_square() != SQ_NONE)
&& pos.ep_square() != SQ_NONE)
{ {
assert(Us != WHITE || rank_of(pos.ep_square()) == RANK_6); assert(Us != WHITE || square_rank(pos.ep_square()) == RANK_6);
assert(Us != BLACK || rank_of(pos.ep_square()) == RANK_3); assert(Us != BLACK || square_rank(pos.ep_square()) == RANK_3);
// An en passant capture can be an evasion only if the checking piece // An en passant capture can be an evasion only if the checking piece
// is the double pushed pawn and so is in the target. Otherwise this // is the double pushed pawn and so is in the target. Otherwise this
// is a discovery check and we are forced to do otherwise. // is a discovery check and we are forced to do otherwise.
if (Type == MV_EVASION && !bit_is_set(target, pos.ep_square() - UP)) if (Type == EVASION && !bit_is_set(target, pos.ep_square() - TDELTA_N))
return mlist; return mlist;
b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them); b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them);
assert(b1); assert(b1 != EmptyBoardBB);
while (b1) while (b1)
{ {
to = pop_1st_bit(&b1); to = pop_1st_bit(&b1);
(*mlist++).move = make_enpassant(to, pos.ep_square()); (*mlist++).move = make_ep_move(to, pos.ep_square());
} }
} }
return mlist; return mlist;
} }
template<PieceType Piece>
MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) {
assert(Piece != QUEEN);
Bitboard b = pos.attacks_from<Piece>(from) & pos.empty_squares();
if (Piece == KING)
{
Square ksq = pos.king_square(opposite_color(pos.side_to_move()));
b &= ~QueenPseudoAttacks[ksq];
}
SERIALIZE_MOVES(b);
return mlist;
}
template<PieceType Piece>
MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
Bitboard dc, Square ksq) {
assert(Piece != KING);
Bitboard checkSqs, b;
Square from;
const Square* ptr = pos.piece_list_begin(us, Piece);
if ((from = *ptr++) == SQ_NONE)
return mlist;
checkSqs = pos.attacks_from<Piece>(ksq) & pos.empty_squares();
do
{
if ( (Piece == QUEEN && !(QueenPseudoAttacks[from] & checkSqs))
|| (Piece == ROOK && !(RookPseudoAttacks[from] & checkSqs))
|| (Piece == BISHOP && !(BishopPseudoAttacks[from] & checkSqs)))
continue;
if (dc && bit_is_set(dc, from))
continue;
b = pos.attacks_from<Piece>(from) & checkSqs;
SERIALIZE_MOVES(b);
} while ((from = *ptr++) != SQ_NONE);
return mlist;
}
template<CastlingSide Side> template<CastlingSide Side>
MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist, Color us) { MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist) {
CastleRight f = CastleRight((Side == KING_SIDE ? WHITE_OO : WHITE_OOO) << us); Color us = pos.side_to_move();
Color them = flip(us);
// After castling, the rook and king's final positions are exactly the same if ( (Side == KING_SIDE && pos.can_castle_kingside(us))
// in Chess960 as they would be in standard chess. ||(Side == QUEEN_SIDE && pos.can_castle_queenside(us)))
Square kfrom = pos.king_square(us);
Square rfrom = pos.castle_rook_square(f);
Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1);
assert(!pos.in_check());
assert(pos.piece_on(kfrom) == make_piece(us, KING));
assert(pos.piece_on(rfrom) == make_piece(us, ROOK));
// Unimpeded rule: All the squares between the king's initial and final squares
// (including the final square), and all the squares between the rook's initial
// and final squares (including the final square), must be vacant except for
// the king and castling rook.
for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); s++)
if ( (s != kfrom && s != rfrom && !pos.square_is_empty(s))
||(pos.attackers_to(s) & pos.pieces(them)))
return mlist;
for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); s++)
if (s != kfrom && s != rfrom && !pos.square_is_empty(s))
return mlist;
// Because we generate only legal castling moves we need to verify that
// when moving the castling rook we do not discover some hidden checker.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
if (pos.is_chess960())
{ {
Bitboard occ = pos.occupied_squares(); Color them = opposite_color(us);
clear_bit(&occ, rfrom); Square ksq = pos.king_square(us);
if (pos.attackers_to(kto, occ) & pos.pieces(them))
assert(pos.piece_on(ksq) == piece_of_color_and_type(us, KING));
Square rsq = (Side == KING_SIDE ? pos.initial_kr_square(us) : pos.initial_qr_square(us));
Square s1 = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Square s2 = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1);
Square s;
bool illegal = false;
assert(pos.piece_on(rsq) == piece_of_color_and_type(us, ROOK));
// It is a bit complicated to correctly handle Chess960
for (s = Min(ksq, s1); s <= Max(ksq, s1); s++)
if ( (s != ksq && s != rsq && pos.square_is_occupied(s))
||(pos.attackers_to(s) & pos.pieces_of_color(them)))
illegal = true;
for (s = Min(rsq, s2); s <= Max(rsq, s2); s++)
if (s != ksq && s != rsq && pos.square_is_occupied(s))
illegal = true;
if ( Side == QUEEN_SIDE
&& square_file(rsq) == FILE_B
&& ( pos.piece_on(relative_square(us, SQ_A1)) == piece_of_color_and_type(them, ROOK)
|| pos.piece_on(relative_square(us, SQ_A1)) == piece_of_color_and_type(them, QUEEN)))
illegal = true;
if (!illegal)
(*mlist++).move = make_castle_move(ksq, rsq);
}
return mlist; return mlist;
} }
(*mlist++).move = make_castle(kfrom, rfrom);
return mlist;
} }
} // namespace
+16 -28
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,40 +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/>.
*/ */
#if !defined(MOVEGEN_H_INCLUDED) #if !defined(MOVEGEN_H_INCLUDED)
#define MOVEGEN_H_INCLUDED #define MOVEGEN_H_INCLUDED
#include "types.h" ////
//// Includes
////
enum MoveType { #include "position.h"
MV_CAPTURE,
MV_NON_CAPTURE,
MV_CHECK,
MV_NON_CAPTURE_CHECK,
MV_EVASION,
MV_NON_EVASION,
MV_LEGAL
};
class Position;
template<MoveType> ////
MoveStack* generate(const Position& pos, MoveStack* mlist); //// Prototypes
////
/// The MoveList struct is a simple wrapper around generate(), sometimes comes extern MoveStack* generate_captures(const Position& pos, MoveStack* mlist);
/// handy to use this class instead of the low level generate() function. extern MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist);
template<MoveType T> extern MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist);
struct MoveList { 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);
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) {}
void operator++() { cur++; }
bool end() const { return cur == last; }
Move move() const { return cur->move; }
int size() const { return int(last - mlist); }
private:
MoveStack mlist[MAX_MOVES];
MoveStack *cur, *last;
};
#endif // !defined(MOVEGEN_H_INCLUDED) #endif // !defined(MOVEGEN_H_INCLUDED)
+153 -192
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,138 +18,86 @@
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 <cassert> #include <cassert>
#include "history.h"
#include "movegen.h" #include "movegen.h"
#include "movepick.h" #include "movepick.h"
#include "search.h" #include "search.h"
#include "types.h" #include "value.h"
////
//// Local definitions
////
namespace { namespace {
enum MovegenPhase { enum MovegenPhase {
PH_TT_MOVE, // Transposition table move PH_TT_MOVES, // Transposition table move and mate killer
PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= captureThreshold (captureThreshold <= 0) PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= 0
PH_GOOD_PROBCUT, // Queen promotions and captures with SEE values > captureThreshold (captureThreshold >= 0)
PH_KILLERS, // Killer moves from the current ply PH_KILLERS, // Killer moves from the current ply
PH_NONCAPTURES_1, // Non-captures and underpromotions with positive score PH_NONCAPTURES, // Non-captures and underpromotions
PH_NONCAPTURES_2, // Non-captures and underpromotions with non-positive score PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < 0
PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < captureThreshold (captureThreshold <= 0)
PH_EVASIONS, // Check evasions PH_EVASIONS, // Check evasions
PH_QCAPTURES, // Captures in quiescence search PH_QCAPTURES, // Captures in quiescence search
PH_QRECAPTURES, // Recaptures in quiescence search
PH_QCHECKS, // Non-capture checks in quiescence search PH_QCHECKS, // Non-capture checks in quiescence search
PH_STOP PH_STOP
}; };
CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
const uint8_t MainSearchTable[] = { PH_TT_MOVE, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES_1, PH_NONCAPTURES_2, PH_BAD_CAPTURES, PH_STOP }; const uint8_t MainSearchPhaseTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
const uint8_t EvasionTable[] = { PH_TT_MOVE, PH_EVASIONS, PH_STOP }; const uint8_t EvasionsPhaseTable[] = { PH_TT_MOVES, PH_EVASIONS, PH_STOP};
const uint8_t QsearchWithChecksTable[] = { PH_TT_MOVE, PH_QCAPTURES, PH_QCHECKS, PH_STOP }; const uint8_t QsearchWithChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP};
const uint8_t QsearchWithoutChecksTable[] = { PH_TT_MOVE, PH_QCAPTURES, PH_STOP }; const uint8_t QsearchWithoutChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP};
const uint8_t QsearchRecapturesTable[] = { PH_TT_MOVE, PH_QRECAPTURES, PH_STOP };
const uint8_t ProbCutTable[] = { PH_TT_MOVE, PH_GOOD_PROBCUT, PH_STOP };
// Unary predicate used by std::partition to split positive scores from remaining
// ones so to sort separately the two sets, and with the second sort delayed.
inline bool has_positive_score(const MoveStack& move) { return move.score > 0; }
// Picks and pushes to the front the best move in range [firstMove, lastMove),
// it is faster than sorting all the moves in advance when moves are few, as
// normally are the possible captures.
inline MoveStack* pick_best(MoveStack* firstMove, MoveStack* lastMove)
{
std::swap(*firstMove, *std::max_element(firstMove, lastMove));
return firstMove;
}
} }
/// Constructors for the MovePicker class. As arguments we pass information
////
//// 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 /// 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 /// moves to return (in the quiescence search, for instance, we only want to
/// search captures, promotions and some checks) and about how important good /// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node. /// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
Search::Stack* ss, Value beta) : pos(p), H(h), depth(d) { const History& h, SearchStack* ss) : pos(p), H(h) {
captureThreshold = 0; int searchTT = ttm;
badCaptures = moves + MAX_MOVES; ttMoves[0].move = ttm;
finished = false;
lastBadCapture = badCaptures;
assert(d > DEPTH_ZERO); pinned = p.pinned_pieces(pos.side_to_move());
if (p.in_check()) if (ss && !p.is_check())
{
killers[0].move = killers[1].move = MOVE_NONE;
phasePtr = EvasionTable;
}
else
{ {
ttMoves[1].move = (ss->mateKiller == ttm)? MOVE_NONE : ss->mateKiller;
searchTT |= ttMoves[1].move;
killers[0].move = ss->killers[0]; killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1]; killers[1].move = ss->killers[1];
} else
ttMoves[1].move = killers[0].move = killers[1].move = MOVE_NONE;
// Consider sligtly negative captures as good if at low depth and far from beta if (p.is_check())
if (ss && ss->eval < beta - PawnValueMidgame && d < 3 * ONE_PLY) phasePtr = EvasionsPhaseTable;
captureThreshold = -PawnValueMidgame; else if (d > Depth(0))
phasePtr = MainSearchPhaseTable;
// Consider negative captures as good if still enough to reach beta else if (d == Depth(0))
else if (ss && ss->eval > beta) phasePtr = QsearchWithChecksPhaseTable;
captureThreshold = beta - ss->eval;
phasePtr = MainSearchTable;
}
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
phasePtr += int(ttMove == MOVE_NONE) - 1;
go_next_phase();
}
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, Square recaptureSq)
: pos(p), H(h) {
assert(d <= DEPTH_ZERO);
if (p.in_check())
phasePtr = EvasionTable;
else if (d >= DEPTH_QS_CHECKS)
phasePtr = QsearchWithChecksTable;
else if (d >= DEPTH_QS_RECAPTURES)
{
phasePtr = QsearchWithoutChecksTable;
// Skip TT move if is not a capture or a promotion, this avoids
// qsearch tree explosion due to a possible perpetual check or
// similar rare cases when TT table is full.
if (ttm != MOVE_NONE && !pos.is_capture_or_promotion(ttm))
ttm = MOVE_NONE;
}
else else
{ phasePtr = QsearchWithoutChecksPhaseTable;
phasePtr = QsearchRecapturesTable;
recaptureSquare = recaptureSq;
ttm = MOVE_NONE;
}
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); phasePtr += !searchTT - 1;
phasePtr += int(ttMove == MOVE_NONE) - 1;
go_next_phase();
}
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType parentCapture)
: pos(p), H(h) {
assert (!pos.in_check());
// In ProbCut we consider only captures better than parent's move
captureThreshold = PieceValueMidgame[Piece(parentCapture)];
phasePtr = ProbCutTable;
if ( ttm != MOVE_NONE
&& (!pos.is_capture(ttm) || pos.see(ttm) <= captureThreshold))
ttm = MOVE_NONE;
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
phasePtr += int(ttMove == MOVE_NONE) - 1;
go_next_phase(); go_next_phase();
} }
@@ -163,13 +111,13 @@ void MovePicker::go_next_phase() {
phase = *(++phasePtr); phase = *(++phasePtr);
switch (phase) { switch (phase) {
case PH_TT_MOVE: case PH_TT_MOVES:
lastMove = curMove + 1; curMove = ttMoves;
lastMove = curMove + 2;
return; return;
case PH_GOOD_CAPTURES: case PH_GOOD_CAPTURES:
case PH_GOOD_PROBCUT: lastMove = generate_captures(pos, moves);
lastMove = generate<MV_CAPTURE>(pos, moves);
score_captures(); score_captures();
return; return;
@@ -178,48 +126,37 @@ void MovePicker::go_next_phase() {
lastMove = curMove + 2; lastMove = curMove + 2;
return; return;
case PH_NONCAPTURES_1: case PH_NONCAPTURES:
lastNonCapture = lastMove = generate<MV_NON_CAPTURE>(pos, moves); lastMove = generate_noncaptures(pos, moves);
score_noncaptures(); score_noncaptures();
lastMove = std::partition(curMove, lastMove, has_positive_score); sort_moves(moves, lastMove);
sort<MoveStack>(curMove, lastMove);
return;
case PH_NONCAPTURES_2:
curMove = lastMove;
lastMove = lastNonCapture;
if (depth >= 3 * ONE_PLY)
sort<MoveStack>(curMove, lastMove);
return; return;
case PH_BAD_CAPTURES: case PH_BAD_CAPTURES:
// Bad captures SEE value is already calculated so just pick // Bad captures SEE value is already calculated so just sort them
// them in order to get SEE move ordering. // to get SEE move ordering.
curMove = badCaptures; curMove = badCaptures;
lastMove = moves + MAX_MOVES; lastMove = lastBadCapture;
return; return;
case PH_EVASIONS: case PH_EVASIONS:
assert(pos.in_check()); assert(pos.is_check());
lastMove = generate<MV_EVASION>(pos, moves); lastMove = generate_evasions(pos, moves);
score_evasions(); score_evasions();
return; return;
case PH_QCAPTURES: case PH_QCAPTURES:
lastMove = generate<MV_CAPTURE>(pos, moves); lastMove = generate_captures(pos, moves);
score_captures(); score_captures();
return; return;
case PH_QRECAPTURES:
lastMove = generate<MV_CAPTURE>(pos, moves);
return;
case PH_QCHECKS: case PH_QCHECKS:
lastMove = generate<MV_NON_CAPTURE_CHECK>(pos, moves); // Perhaps we should order moves move here? FIXME
lastMove = generate_non_capture_checks(pos, moves);
return; return;
case PH_STOP: case PH_STOP:
lastMove = curMove + 1; // Avoid another go_next_phase() call lastMove = curMove + 1; // hack to be friendly for get_next_move()
return; return;
default: default:
@@ -232,7 +169,7 @@ void MovePicker::go_next_phase() {
/// MovePicker::score_captures(), MovePicker::score_noncaptures() and /// MovePicker::score_captures(), MovePicker::score_noncaptures() and
/// MovePicker::score_evasions() assign a numerical move ordering score /// MovePicker::score_evasions() assign a numerical move ordering score
/// to each move in a move list. The moves with highest scores will be /// to each move in a move list. The moves with highest scores will be
/// picked first by next_move(). /// picked first by get_next_move().
void MovePicker::score_captures() { void MovePicker::score_captures() {
// Winning and equal captures in the main search are ordered by MVV/LVA. // Winning and equal captures in the main search are ordered by MVV/LVA.
@@ -254,24 +191,37 @@ void MovePicker::score_captures() {
for (MoveStack* cur = moves; cur != lastMove; cur++) for (MoveStack* cur = moves; cur != lastMove; cur++)
{ {
m = cur->move; m = cur->move;
cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))] if (move_is_promotion(m))
- type_of(pos.piece_on(from_sq(m))); cur->score = QueenValueMidgame;
else
if (is_promotion(m)) cur->score = pos.midgame_value_of_piece_on(move_to(m))
cur->score += PieceValueMidgame[Piece(promotion_piece_type(m))]; - pos.type_of_piece_on(move_from(m));
} }
} }
void MovePicker::score_noncaptures() { 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; Move m;
Square from; Piece piece;
Square from, to;
int hs;
for (MoveStack* cur = moves; cur != lastMove; cur++) for (MoveStack* cur = moves; cur != lastMove; cur++)
{ {
m = cur->move; m = cur->move;
from = from_sq(m); from = move_from(m);
cur->score = H.value(pos.piece_on(from), to_sq(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));
} }
} }
@@ -283,108 +233,98 @@ void MovePicker::score_evasions() {
Move m; Move m;
int seeScore; int seeScore;
// Skip if we don't have at least two moves to order
if (lastMove < moves + 2)
return;
for (MoveStack* cur = moves; cur != lastMove; cur++) for (MoveStack* cur = moves; cur != lastMove; cur++)
{ {
m = cur->move; m = cur->move;
if ((seeScore = pos.see_sign(m)) < 0) if ((seeScore = pos.see_sign(m)) < 0)
cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom cur->score = seeScore;
else if (pos.is_capture(m)) else if (pos.move_is_capture(m))
cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))] cur->score = pos.midgame_value_of_piece_on(move_to(m))
- type_of(pos.piece_on(from_sq(m))) + History::MaxValue; - pos.type_of_piece_on(move_from(m)) + HistoryMax;
else else
cur->score = H.value(pos.piece_on(from_sq(m)), to_sq(m)); cur->score = H.move_ordering_score(pos.piece_on(move_from(m)), move_to(m));
} }
} }
/// MovePicker::next_move() is the most important method of the MovePicker class. /// MovePicker::get_next_move() is the most important method of the MovePicker
/// It returns a new pseudo legal move every time it is called, until there /// 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 /// are no more moves left.
/// of generated moves taking care not to return the tt move if has already been /// It picks the move with the biggest score from a list of generated moves taking
/// searched previously. Note that this function is not thread safe so should be /// care not to return the tt move if has already been searched previously.
/// lock protected by caller when accessed through a shared MovePicker object.
Move MovePicker::next_move() { Move MovePicker::get_next_move() {
Move move; Move move;
while (true) while (true)
{ {
while (curMove == lastMove) while (curMove != lastMove)
go_next_phase(); {
switch (phase) { switch (phase) {
case PH_TT_MOVE: case PH_TT_MOVES:
curMove++; move = (curMove++)->move;
return ttMove; if ( move != MOVE_NONE
&& move_is_legal(pos, move, pinned))
return move;
break; break;
case PH_GOOD_CAPTURES: case PH_GOOD_CAPTURES:
move = pick_best(curMove++, lastMove)->move; move = pick_best(curMove++, lastMove).move;
if (move != ttMove) if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& pos.pl_move_is_legal(move, pinned))
{ {
assert(captureThreshold <= 0); // Otherwise we must use see instead of see_sign
// Check for a non negative SEE now // Check for a non negative SEE now
int seeValue = pos.see_sign(move); int seeValue = pos.see_sign(move);
if (seeValue >= captureThreshold) if (seeValue >= 0)
return move; return move;
// Losing capture, move it to the tail of the array // Losing capture, move it to the badCaptures[] array, note
(--badCaptures)->move = move; // that move has now been already checked for legality.
badCaptures->score = seeValue; assert(int(lastBadCapture - badCaptures) < 63);
lastBadCapture->move = move;
lastBadCapture->score = seeValue;
lastBadCapture++;
} }
break; break;
case PH_GOOD_PROBCUT:
move = pick_best(curMove++, lastMove)->move;
if ( move != ttMove
&& pos.see(move) > captureThreshold)
return move;
break;
case PH_KILLERS: case PH_KILLERS:
move = (curMove++)->move; move = (curMove++)->move;
if ( move != MOVE_NONE if ( move != MOVE_NONE
&& pos.is_pseudo_legal(move) && move != ttMoves[0].move
&& move != ttMove && move != ttMoves[1].move
&& !pos.is_capture(move)) && move_is_legal(pos, move, pinned)
&& !pos.move_is_capture(move))
return move; return move;
break; break;
case PH_NONCAPTURES_1: case PH_NONCAPTURES:
case PH_NONCAPTURES_2:
move = (curMove++)->move; move = (curMove++)->move;
if ( move != ttMove if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& move != killers[0].move && move != killers[0].move
&& move != killers[1].move) && move != killers[1].move
&& pos.pl_move_is_legal(move, pinned))
return move; return move;
break; break;
case PH_BAD_CAPTURES: case PH_BAD_CAPTURES:
move = pick_best(curMove++, lastMove)->move; move = pick_best(curMove++, lastMove).move;
return move; return move;
case PH_EVASIONS: case PH_EVASIONS:
case PH_QCAPTURES: case PH_QCAPTURES:
move = pick_best(curMove++, lastMove)->move; move = pick_best(curMove++, lastMove).move;
if (move != ttMove) if ( move != ttMoves[0].move
return move; && pos.pl_move_is_legal(move, pinned))
break;
case PH_QRECAPTURES:
move = (curMove++)->move;
if (to_sq(move) == recaptureSquare)
return move; return move;
break; break;
case PH_QCHECKS: case PH_QCHECKS:
move = (curMove++)->move; move = (curMove++)->move;
if (move != ttMove) if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
return move; return move;
break; break;
@@ -396,4 +336,25 @@ Move MovePicker::next_move() {
break; break;
} }
} }
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;
} }
+48 -22
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,31 +17,43 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined MOVEPICK_H_INCLUDED #if !defined MOVEPICK_H_INCLUDED
#define MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED
#include "history.h" ////
#include "position.h" //// Includes
#include "search.h" ////
#include "types.h"
/// MovePicker is a class which is used to pick one pseudo legal move at a time #include "depth.h"
/// from the current position. It is initialized with a Position object and a few #include "history.h"
#include "lock.h"
#include "position.h"
////
//// Types
////
struct SearchStack;
/// MovePicker is a class which is used to pick one legal move at a time from
/// the current position. It is initialized with a Position object and a few
/// moves we have reason to believe are good. The most important method is /// moves we have reason to believe are good. The most important method is
/// MovePicker::next_move(), which returns a new pseudo legal move each time /// MovePicker::pick_next_move(), which returns a new legal move each time it
/// it is called, until there are no moves left, when MOVE_NONE is returned. /// 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 /// 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. /// attempts to return the moves which are most likely to be strongest first.
class MovePicker { class MovePicker {
MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC MovePicker& operator=(const MovePicker&); // silence a warning under MSVC
public: public:
MovePicker(const Position&, Move, Depth, const History&, Search::Stack*, Value); MovePicker(const Position& p, Move ttm, Depth d, const History& h, SearchStack* ss = NULL);
MovePicker(const Position&, Move, Depth, const History&, Square recaptureSq); Move get_next_move();
MovePicker(const Position&, Move, const History&, PieceType parentCapture); Move get_next_move(Lock& lock);
Move next_move(); int number_of_evasions() const;
private: private:
void score_captures(); void score_captures();
@@ -51,14 +63,28 @@ private:
const Position& pos; const Position& pos;
const History& H; const History& H;
Depth depth; MoveStack ttMoves[2], killers[2];
Move ttMove; bool finished;
MoveStack killers[2]; int phase;
Square recaptureSquare;
int captureThreshold, phase;
const uint8_t* phasePtr; const uint8_t* phasePtr;
MoveStack *curMove, *lastMove, *lastNonCapture, *badCaptures; MoveStack *curMove, *lastMove, *lastBadCapture;
MoveStack moves[MAX_MOVES]; Bitboard pinned;
MoveStack moves[256], badCaptures[64];
}; };
////
//// 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) #endif // !defined(MOVEPICK_H_INCLUDED)
+250 -122
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,37 +17,46 @@
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 <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"
////
//// Local definitions
////
namespace { namespace {
/// Constants and variables
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by opposed flag and file // Doubled pawn penalty by file
const Score DoubledPawnPenalty[2][8] = { 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)
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48), };
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
// Isolated pawn penalty by opposed flag and file // Isolated pawn penalty by file
const Score IsolatedPawnPenalty[2][8] = { const Score IsolatedPawnPenalty[8] = {
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52), S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(60, 52), S(60, 52), S(54, 52), S(37, 45) }, S(40, 35), S(40, 35), S(36, 35), S(25, 30)
{ 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 and file // Backward pawn penalty by file
const Score BackwardPawnPenalty[2][8] = { const Score BackwardPawnPenalty[8] = {
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46), S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(49, 46), S(49, 46), S(43, 46), S(30, 42) }, S(33, 31), S(33, 31), S(29, 31), S(20, 28)
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31), };
S(33, 31), S(33, 31), S(29, 31), S(20, 28) }};
// Pawn chain membership bonus by file // Pawn chain membership bonus by file
const Score ChainBonus[8] = { const Score ChainBonus[8] = {
@@ -61,26 +70,87 @@ namespace {
S(34,68), S(83,166), S(0, 0), S( 0, 0) S(34,68), S(83,166), S(0, 0), S( 0, 0)
}; };
const Score PawnStructureWeight = S(233, 201); // Pawn storm tables for positions with opposite castling
const int QStormTable[64] = {
0, 0, 0, 0, 0, 0, 0, 0,
-22,-22,-22,-14,-6, 0, 0, 0,
-6,-10,-10,-10,-6, 0, 0, 0,
4, 12, 16, 12, 4, 0, 0, 0,
16, 23, 23, 16, 0, 0, 0, 0,
23, 31, 31, 23, 0, 0, 0, 0,
23, 31, 31, 23, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
#undef S const int KStormTable[64] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,-10,-19,-28,-33,-33,
0, 0, 0,-10,-15,-19,-24,-24,
0, 0, 0, 0, 1, 1, 1, 1,
0, 0, 0, 0, 1, 10, 19, 19,
0, 0, 0, 0, 1, 19, 31, 27,
0, 0, 0, 0, 0, 22, 31, 22,
0, 0, 0, 0, 0, 0, 0, 0
};
inline Score apply_weight(Score v, Score w) { // Pawn storm open file bonuses by file
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100, const int16_t KStormOpenFileBonus[8] = { 31, 31, 18, 0, 0, 0, 0, 0 };
(int(eg_value(v)) * eg_value(w)) / 0x100); const int16_t QStormOpenFileBonus[8] = { 0, 0, 0, 0, 0, 26, 42, 26 };
// Pawn storm lever bonuses by file
const int StormLeverBonus[8] = { -8, -8, -13, 0, 0, -13, -8, -8 };
}
////
//// Functions
////
/// Constructor
PawnInfoTable::PawnInfoTable(unsigned numOfEntries) {
size = numOfEntries;
entries = new PawnInfo[size];
if (!entries)
{
std::cerr << "Failed to allocate " << (numOfEntries * sizeof(PawnInfo))
<< " bytes for pawn hash table." << std::endl;
Application::exit_with_failure();
} }
} }
/// PawnInfoTable::pawn_info() takes a position object as input, computes /// Destructor
/// a PawnInfo object, and returns a pointer to it. The result is also stored
/// in an hash table, so we don't have to recompute everything when the same
/// pawn structure occurs again.
PawnInfo* PawnInfoTable::pawn_info(const Position& pos) const { PawnInfoTable::~PawnInfoTable() {
delete [] entries;
}
Key key = pos.pawn_key();
PawnInfo* pi = probe(key); /// PawnInfo::clear() resets to zero the PawnInfo entry. Note that
/// kingSquares[] is initialized to SQ_NONE instead.
void PawnInfo::clear() {
memset(this, 0, sizeof(PawnInfo));
kingSquares[WHITE] = kingSquares[BLACK] = SQ_NONE;
}
/// PawnInfoTable::get_pawn_info() takes a position object as input, computes
/// a PawnInfo object, and returns a pointer to it. The result is also
/// stored in a hash table, so we don't have to recompute everything when
/// the same pawn structure occurs again.
PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
assert(pos.is_ok());
Key key = pos.get_pawn_key();
int index = int(key & (size - 1));
PawnInfo* pi = entries + index;
// If pi->key matches the position's pawn hash key, it means that we // 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 // have analysed this pawn structure before, and we can simply return
@@ -88,24 +158,19 @@ PawnInfo* PawnInfoTable::pawn_info(const Position& pos) const {
if (pi->key == key) if (pi->key == key)
return pi; return pi;
// Initialize PawnInfo entry // Clear the PawnInfo object, and set the key
pi->clear();
pi->key = key; pi->key = key;
pi->passedPawns[WHITE] = pi->passedPawns[BLACK] = 0;
pi->kingSquares[WHITE] = pi->kingSquares[BLACK] = SQ_NONE;
pi->halfOpenFiles[WHITE] = pi->halfOpenFiles[BLACK] = 0xFF;
// Calculate pawn attacks // Calculate pawn attacks
Bitboard wPawns = pos.pieces(PAWN, WHITE); Bitboard whitePawns = pos.pieces(PAWN, WHITE);
Bitboard bPawns = pos.pieces(PAWN, BLACK); Bitboard blackPawns = pos.pieces(PAWN, BLACK);
pi->pawnAttacks[WHITE] = ((wPawns << 9) & ~FileABB) | ((wPawns << 7) & ~FileHBB); pi->pawnAttacks[WHITE] = ((whitePawns << 9) & ~FileABB) | ((whitePawns << 7) & ~FileHBB);
pi->pawnAttacks[BLACK] = ((bPawns >> 7) & ~FileABB) | ((bPawns >> 9) & ~FileHBB); pi->pawnAttacks[BLACK] = ((blackPawns >> 7) & ~FileABB) | ((blackPawns >> 9) & ~FileHBB);
// Evaluate pawns for both colors and weight the result
pi->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, pi)
- evaluate_pawns<BLACK>(pos, bPawns, wPawns, pi);
pi->value = apply_weight(pi->value, PawnStructureWeight);
// Evaluate pawns for both colors
pi->value = evaluate_pawns<WHITE>(pos, whitePawns, blackPawns, pi)
- evaluate_pawns<BLACK>(pos, blackPawns, whitePawns, pi);
return pi; return pi;
} }
@@ -115,126 +180,189 @@ PawnInfo* PawnInfoTable::pawn_info(const Position& pos) const {
template<Color Us> template<Color Us>
Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns, Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnInfo* pi) { Bitboard theirPawns, PawnInfo* pi) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b;
Square s; Square s;
File f; File f;
Rank r; Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate; bool passed, isolated, doubled, chain, backward, candidate;
Score value = SCORE_ZERO; int bonus;
const Square* pl = pos.piece_list(Us, PAWN); Score value = make_score(0, 0);
const Square* ptr = pos.piece_list_begin(Us, PAWN);
// 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);
}
// Loop through all pawns of the current color and score each pawn // Loop through all pawns of the current color and score each pawn
while ((s = *pl++) != SQ_NONE) while ((s = *ptr++) != SQ_NONE)
{ {
assert(pos.piece_on(s) == make_piece(Us, PAWN)); f = square_file(s);
r = square_rank(s);
f = file_of(s); assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN));
r = rank_of(s);
// This file cannot be half open // Passed, isolated or doubled pawn?
pi->halfOpenFiles[Us] &= ~(1 << f); passed = Position::pawn_is_passed(theirPawns, Us, s);
isolated = Position::pawn_is_isolated(ourPawns, s);
doubled = Position::pawn_is_doubled(ourPawns, Us, s);
// Our rank plus previous one. Used for chain detection // We calculate kingside and queenside pawn storm
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1)); // 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.
// Flag the pawn as passed, isolated, doubled or member of a pawn // Kingside pawn storms
// chain (but not the backward one). bonus = KStormTable[relative_square(Us, s)];
passed = !(theirPawns & passed_pawn_mask(Us, s)); if (f >= FILE_F)
doubled = ourPawns & squares_in_front_of(Us, s); {
opposed = theirPawns & squares_in_front_of(Us, s); Bitboard b = outpost_mask(Us, s) & theirPawns & (FileFBB | FileGBB | FileHBB);
isolated = !(ourPawns & neighboring_files_bb(f)); while (b)
chain = ourPawns & neighboring_files_bb(f) & 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 // 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; backward = false;
else
// If the pawn is passed, isolated, or member of a pawn chain it cannot
// be backward. If there are friendly pawns behind on neighboring files
// or if can capture an enemy pawn it cannot be backward either.
if ( !(passed | isolated | chain)
&& !(ourPawns & attack_span_mask(Them, s))
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
{ {
// We now know that there are no friendly pawns beside or behind this // We now know that there are no friendly pawns beside or behind this
// pawn on neighboring files. We now check whether the pawn is // pawn on neighboring files. We now check whether the pawn is
// backward by looking in the forward direction on the neighboring // backward by looking in the forward direction on the neighboring
// files, and seeing whether we meet a friendly or an enemy pawn first. // files, and seeing whether we meet a friendly or an enemy pawn first.
b = pos.attacks_from<PAWN>(s, Us); Bitboard b = pos.attacks_from<PAWN>(s, Us);
// Note that we are sure to find something because pawn is not passed // Note that we are sure to find something because pawn is not passed
// nor isolated, so loop is potentially infinite, but it isn't. // nor isolated, so loop is potentially infinite, but it isn't.
while (!(b & (ourPawns | theirPawns))) while (!(b & (ourPawns | theirPawns)))
Us == WHITE ? b <<= 8 : b >>= 8; Us == WHITE ? b <<= 8 : b >>= 8;
// The friendly pawn needs to be at least two ranks closer than the // The friendly pawn needs to be at least two ranks closer than the enemy
// enemy pawn in order to help the potentially backward pawn advance. // pawn in order to help the potentially backward pawn advance.
backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns; backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns;
} }
assert(opposed | passed | (attack_span_mask(Us, s) & 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);
// A not passed pawn is a candidate to become passed if it is free to // In order to prevent doubled passed pawns from receiving a too big
// advance and if the number of friendly pawns beside or behind this // bonus, only the frontmost passed pawn on each file is considered as
// pawn on neighboring files is higher or equal than the number of // a true passed pawn.
// enemy pawns in the forward direction on the neighboring files. if (passed && (ourPawns & squares_in_front_of(Us, s)))
candidate = !(opposed | passed | backward | isolated) passed = false;
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != 0
&& popcount<Max15>(b) >= popcount<Max15>(attack_span_mask(Us, s) & theirPawns);
// Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate passed pawns. Only the frontmost passed
// pawn on each file is considered a true passed pawn.
if (passed && !doubled)
set_bit(&(pi->passedPawns[Us]), s);
// Score this pawn // Score this pawn
if (isolated) if (passed)
value -= IsolatedPawnPenalty[opposed][f]; set_bit(&(pi->passedPawns), s);
if (isolated)
{
value -= IsolatedPawnPenalty[f];
if (!(theirPawns & file_bb(f)))
value -= IsolatedPawnPenalty[f] / 2;
}
if (doubled) if (doubled)
value -= DoubledPawnPenalty[opposed][f]; value -= DoubledPawnPenalty[f];
if (backward) if (backward)
value -= BackwardPawnPenalty[opposed][f]; {
value -= BackwardPawnPenalty[f];
if (!(theirPawns & file_bb(f)))
value -= BackwardPawnPenalty[f] / 2;
}
if (chain) if (chain)
value += ChainBonus[f]; value += ChainBonus[f];
if (candidate) if (candidate)
value += CandidateBonus[relative_rank(Us, s)]; value += CandidateBonus[relative_rank(Us, s)];
} }
return value; return value;
} }
/// PawnInfo::updateShelter() calculates and caches king shelter. It is called /// PawnInfo::updateShelter calculates and caches king shelter. It is called
/// only when king square changes, about 20% of total king_shelter() calls. /// only when king square changes, about 20% of total get_king_shelter() calls.
template<Color Us> int PawnInfo::updateShelter(const Position& pos, Color c, Square ksq) {
Score PawnInfo::updateShelter(const Position& pos, Square ksq) {
const int Shift = (Us == WHITE ? 8 : -8); unsigned shelter = 0;
Bitboard pawns = pos.pieces(PAWN, c) & this_and_neighboring_files_bb(ksq);
Bitboard pawns; unsigned r = ksq & (7 << 3);
int r, shelter = 0; for (int i = 1, k = (c ? -8 : 8); i < 4; i++)
if (relative_rank(Us, ksq) <= RANK_4)
{ {
pawns = pos.pieces(PAWN, Us) & this_and_neighboring_files_bb(file_of(ksq)); r += k;
r = ksq & (7 << 3); shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (128 >> i);
for (int i = 0; i < 3; i++)
{
r += Shift;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] << (6 - i);
} }
kingSquares[c] = ksq;
kingShelters[c] = shelter;
return shelter;
} }
kingSquares[Us] = ksq;
kingShelters[Us] = make_score(shelter, 0);
return kingShelters[Us];
}
// Explicit template instantiation
template Score PawnInfo::updateShelter<WHITE>(const Position& pos, Square ksq);
template Score PawnInfo::updateShelter<BLACK>(const Position& pos, Square ksq);
+57 -29
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,78 +17,107 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(PAWNS_H_INCLUDED) #if !defined(PAWNS_H_INCLUDED)
#define PAWNS_H_INCLUDED #define PAWNS_H_INCLUDED
#include "position.h" ////
#include "tt.h" //// Includes
#include "types.h" ////
const int PawnTableSize = 16384; #include "bitboard.h"
#include "value.h"
////
//// Types
////
/// PawnInfo is a class which contains various information about a pawn /// PawnInfo is a class which contains various information about a pawn
/// structure. Currently, it only includes a middle game and an end game /// structure. Currently, it only includes a middle game and an end game
/// pawn structure evaluation, and a bitboard of passed pawns. We may want /// 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 /// to add further information in the future. A lookup to the pawn hash table
/// table (performed by calling the pawn_info method in a PawnInfoTable /// (performed by calling the get_pawn_info method in a PawnInfoTable object)
/// object) returns a pointer to a PawnInfo object. /// returns a pointer to a PawnInfo object.
class Position;
class PawnInfo { class PawnInfo {
friend class PawnInfoTable; friend class PawnInfoTable;
public: public:
PawnInfo() { clear(); }
Score pawns_value() const; Score pawns_value() const;
Value kingside_storm_value(Color c) const;
Value queenside_storm_value(Color c) const;
Bitboard pawn_attacks(Color c) const; Bitboard pawn_attacks(Color c) const;
Bitboard passed_pawns(Color c) const; Bitboard passed_pawns() const;
int file_is_half_open(Color c, File f) 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_left(Color c, File f) const;
int has_open_file_to_right(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>
Score king_shelter(const Position& pos, Square ksq);
private: private:
template<Color Us> void clear();
Score updateShelter(const Position& pos, Square ksq); int updateShelter(const Position& pos, Color c, Square ksq);
Key key; Key key;
Bitboard passedPawns[2]; Bitboard passedPawns;
Bitboard pawnAttacks[2]; Bitboard pawnAttacks[2];
Square kingSquares[2];
Score value; Score value;
int halfOpenFiles[2]; int16_t ksStormValue[2], qsStormValue[2];
Score kingShelters[2]; uint8_t halfOpenFiles[2];
Square kingSquares[2];
uint8_t kingShelters[2];
}; };
/// 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.
/// The PawnInfoTable class represents a pawn hash table. The most important class PawnInfoTable {
/// method is pawn_info, which returns a pointer to a PawnInfo object.
class PawnInfoTable : public SimpleHash<PawnInfo, PawnTableSize> {
public: public:
PawnInfo* pawn_info(const Position& pos) const; PawnInfoTable(unsigned numOfEntries);
~PawnInfoTable();
PawnInfo* get_pawn_info(const Position& pos);
private: private:
template<Color Us> template<Color Us>
static Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi); Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi);
unsigned size;
PawnInfo* entries;
}; };
////
//// Inline functions
////
inline Score PawnInfo::pawns_value() const { inline Score PawnInfo::pawns_value() const {
return value; return value;
} }
inline Bitboard PawnInfo::passed_pawns() const {
return passedPawns;
}
inline Bitboard PawnInfo::pawn_attacks(Color c) const { inline Bitboard PawnInfo::pawn_attacks(Color c) const {
return pawnAttacks[c]; return pawnAttacks[c];
} }
inline Bitboard PawnInfo::passed_pawns(Color c) const { inline Value PawnInfo::kingside_storm_value(Color c) const {
return passedPawns[c]; 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 { inline int PawnInfo::file_is_half_open(Color c, File f) const {
return halfOpenFiles[c] & (1 << int(f)); return (halfOpenFiles[c] & (1 << int(f)));
} }
inline int PawnInfo::has_open_file_to_left(Color c, File f) const { inline int PawnInfo::has_open_file_to_left(Color c, File f) const {
@@ -99,9 +128,8 @@ inline int PawnInfo::has_open_file_to_right(Color c, File f) const {
return halfOpenFiles[c] & ~((1 << int(f+1)) - 1); return halfOpenFiles[c] & ~((1 << int(f+1)) - 1);
} }
template<Color Us> inline int PawnInfo::get_king_shelter(const Position& pos, Color c, Square ksq) {
inline Score PawnInfo::king_shelter(const Position& pos, Square ksq) { return (kingSquares[c] == ksq ? kingShelters[c] : updateShelter(pos, c, ksq));
return kingSquares[Us] == ksq ? kingShelters[Us] : updateShelter<Us>(pos, ksq);
} }
#endif // !defined(PAWNS_H_INCLUDED) #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)
+1183 -955
View File
File diff suppressed because it is too large Load Diff
+244 -149
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,28 +17,81 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(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
#if defined(_MSC_VER)
// Forcing value to bool 'true' or 'false' (performance warning)
#pragma warning(disable: 4800)
// Conditional expression is constant
#pragma warning(disable: 4127)
#endif
////
//// Includes
////
#include "bitboard.h" #include "bitboard.h"
#include "types.h" #include "color.h"
#include "direction.h"
#include "move.h"
#include "piece.h"
#include "square.h"
#include "value.h"
/// The checkInfo struct is initialized at c'tor time and keeps info used ////
/// to detect if a move gives check. //// Constants
class Position; ////
/// FEN string for the initial position
const std::string StartPosition = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
/// Maximum number of plies per game (220 should be enough, because the
/// maximum search depth is 100, and during position setup we reset the
/// move counter for every non-reversible move).
const int MaxGameLength = 220;
////
//// Types
////
/// struct checkInfo is initialized at c'tor time and keeps
/// info used to detect if a move gives check.
struct CheckInfo { struct CheckInfo {
explicit CheckInfo(const Position&); CheckInfo(const Position&);
Square ksq;
Bitboard dcCandidates; Bitboard dcCandidates;
Bitboard pinned;
Bitboard checkSq[8]; Bitboard checkSq[8];
}; };
/// Castle rights, encoded as bit fields
enum CastleRights {
NO_CASTLES = 0,
WHITE_OO = 1,
BLACK_OO = 2,
WHITE_OOO = 4,
BLACK_OOO = 8,
ALL_CASTLES = 15
};
/// Game phase
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
};
/// The StateInfo struct stores information we need to restore a Position /// The StateInfo struct stores information we need to restore a Position
/// object to its previous state when we retract a move. Whenever a move /// object to its previous state when we retract a move. Whenever a move
@@ -47,14 +100,14 @@ struct CheckInfo {
struct StateInfo { struct StateInfo {
Key pawnKey, materialKey; Key pawnKey, materialKey;
Value npMaterial[2];
int castleRights, rule50, pliesFromNull; int castleRights, rule50, pliesFromNull;
Score value;
Square epSquare; Square epSquare;
Score value;
Value npMaterial[2];
Key key; Key key;
PieceType capture;
Bitboard checkersBB; Bitboard checkersBB;
PieceType capturedType;
StateInfo* previous; StateInfo* previous;
}; };
@@ -83,24 +136,37 @@ struct StateInfo {
class Position { class Position {
// No copy c'tor or assignment operator allowed friend class MaterialInfo;
Position(const Position&); friend class EndgameFunctions;
Position& operator=(const Position&);
public: public:
enum GamePhase {
MidGame,
EndGame
};
// Constructors
Position() {} Position() {}
Position(const Position& pos, int th) { copy(pos, th); } Position(const Position& pos);
Position(const std::string& fen, bool isChess960, int th); Position(const std::string& fen);
// Text input/output // Text input/output
void copy(const Position& pos, int th); void from_fen(const std::string& fen);
void from_fen(const std::string& fen, bool isChess960);
const std::string to_fen() const; const std::string to_fen() const;
void print(Move m = MOVE_NONE) const; void print(Move m = MOVE_NONE) const;
// Copying
void copy(const Position& pos);
void flipped_copy(const Position& pos);
// The piece on a given square // The piece on a given square
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_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 // Side to move
Color side_to_move() const; Color side_to_move() const;
@@ -108,7 +174,7 @@ public:
// Bitboard representation of the position // Bitboard representation of the position
Bitboard empty_squares() const; Bitboard empty_squares() const;
Bitboard occupied_squares() const; Bitboard occupied_squares() const;
Bitboard pieces(Color c) const; Bitboard pieces_of_color(Color c) const;
Bitboard pieces(PieceType pt) const; Bitboard pieces(PieceType pt) const;
Bitboard pieces(PieceType pt, Color c) const; Bitboard pieces(PieceType pt, Color c) const;
Bitboard pieces(PieceType pt1, PieceType pt2) const; Bitboard pieces(PieceType pt1, PieceType pt2) const;
@@ -124,59 +190,68 @@ public:
Square king_square(Color c) const; Square king_square(Color c) const;
// Castling rights // Castling rights
bool can_castle(CastleRight f) const; bool can_castle_kingside(Color c) const;
bool can_castle_queenside(Color c) const;
bool can_castle(Color c) const; bool can_castle(Color c) const;
Square castle_rook_square(CastleRight f) const; Square initial_kr_square(Color c) const;
Square initial_qr_square(Color c) const;
// Bitboards for pinned pieces and discovered check candidates // Bitboards for pinned pieces and discovered check candidates
Bitboard discovered_check_candidates() const; Bitboard discovered_check_candidates(Color c) const;
Bitboard pinned_pieces() const; Bitboard pinned_pieces(Color c) const;
// Checking pieces and under check information // Checking pieces and under check information
Bitboard checkers() const; Bitboard checkers() const;
bool in_check() const; bool is_check() const;
// Piece lists // Piece lists
const Square* piece_list(Color c, PieceType pt) const; 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 // 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 occ) const;
Bitboard attacks_from(Piece p, Square s) const; Bitboard attacks_from(Piece p, Square s) const;
static Bitboard attacks_from(Piece p, Square s, Bitboard occ);
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 move_gives_check(Move m, const CheckInfo& ci) const;
bool move_attacks_square(Move m, Square s) const;
bool pl_move_is_legal(Move m, Bitboard pinned) const; bool pl_move_is_legal(Move m, Bitboard pinned) const;
bool is_pseudo_legal(const Move m) const; bool pl_move_is_evasion(Move m, Bitboard pinned) const;
bool is_capture(Move m) const; bool move_is_check(Move m) const;
bool is_capture_or_promotion(Move m) const; bool move_is_check(Move m, const CheckInfo& ci) const;
bool is_passed_pawn_push(Move m) const; bool move_is_capture(Move m) const;
bool move_is_capture_or_promotion(Move m) const;
// Piece captured with previous moves bool move_is_passed_pawn_push(Move m) const;
PieceType captured_piece_type() const; bool move_attacks_square(Move m, Square s) const;
// Information about pawns // Information about pawns
bool pawn_is_passed(Color c, Square s) const; bool pawn_is_passed(Color c, Square s) const;
static bool pawn_is_passed(Bitboard theirPawns, Color c, Square s);
static bool pawn_is_isolated(Bitboard ourPawns, Square s);
static bool pawn_is_doubled(Bitboard ourPawns, Color c, Square s);
// Weak squares
bool square_is_weak(Square s, Color c) const;
// Doing and undoing moves // Doing and undoing moves
void saveState();
void do_move(Move m, StateInfo& st); void do_move(Move m, StateInfo& st);
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck); void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
void undo_move(Move m); void undo_move(Move m);
template<bool Do> void do_null_move(StateInfo& st); void do_null_move(StateInfo& st);
void undo_null_move();
// Static exchange evaluation // Static exchange evaluation
int see(Square from, Square to) const;
int see(Move m) const; int see(Move m) const;
int see(Square to) const;
int see_sign(Move m) const; int see_sign(Move m) const;
// Accessing hash keys // Accessing hash keys
Key key() const; Key get_key() const;
Key exclusion_key() const; Key get_exclusion_key() const;
Key pawn_key() const; Key get_pawn_key() const;
Key material_key() const; Key get_material_key() const;
// Incremental evaluation // Incremental evaluation
Score value() const; Score value() const;
@@ -185,40 +260,44 @@ public:
// Game termination checks // Game termination checks
bool is_mate() const; bool is_mate() const;
template<bool SkipRepetition> bool is_draw() const; bool is_draw() const;
// Plies from start position to the beginning of search // Check if one side threatens a mate in one
int startpos_ply_counter() const; 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
bool opposite_colored_bishops() const; bool opposite_colored_bishops() const;
bool has_pawn_on_7th(Color c) const; bool has_pawn_on_7th(Color c) const;
bool is_chess960() const;
// Current thread ID searching on the position // Reset the gamePly variable to 0
int thread() const; void reset_game_ply();
int64_t nodes_searched() const;
void set_nodes_searched(int64_t n);
// Position consistency check, for debugging // Position consistency check, for debugging
bool pos_is_ok(int* failedStep = NULL) const; bool is_ok(int* failedStep = NULL) const;
void flip_me();
// Global initialization // Static member functions
static void init(); static void init_zobrist();
static void init_piece_square_tables();
private: private:
// Initialization helper functions (used while setting up a position) // Initialization helper functions (used while setting up a position)
void clear(); void clear();
void put_piece(Piece p, Square s); void put_piece(Piece p, Square s);
void set_castle_right(Square ksq, Square rsq); void allow_oo(Color c);
bool move_is_legal(const Move m) const; void allow_ooo(Color c);
// Helper template functions // Helper functions for doing and undoing moves
template<bool Do> void do_castle_move(Move m); void do_capture_move(Bitboard& key, PieceType capture, Color them, Square to, bool ep);
template<bool FindPinned> Bitboard hidden_checkers() const; void do_castle_move(Move m);
void undo_castle_move(Move m);
void find_checkers();
template<bool FindPinned>
Bitboard hidden_checkers(Color c) const;
// Computing hash keys from scratch (for initialization and debugging) // Computing hash keys from scratch (for initialization and debugging)
Key compute_key() const; Key compute_key() const;
@@ -226,17 +305,15 @@ private:
Key compute_material_key() const; Key compute_material_key() const;
// Computing incremental evaluation scores and material counts // Computing incremental evaluation scores and material counts
Score pst(Piece p, Square s) const; Score pst(Color c, PieceType pt, Square s) const;
Score compute_value() const; Score compute_value() const;
Value compute_non_pawn_material(Color c) const; Value compute_non_pawn_material(Color c) const;
// Board // Board
Piece board[64]; // [square] Piece board[64];
// Bitboards // Bitboards
Bitboard byTypeBB[8]; // [pieceType] Bitboard byTypeBB[8], byColorBB[2];
Bitboard byColorBB[2]; // [color]
Bitboard occupied;
// Piece counts // Piece counts
int pieceCount[2][8]; // [color][pieceType] int pieceCount[2][8]; // [color][pieceType]
@@ -246,39 +323,55 @@ private:
int index[64]; // [square] int index[64]; // [square]
// Other info // Other info
int castleRightsMask[64]; // [square]
Square castleRookSquare[16]; // [castleRight]
StateInfo startState;
int64_t nodes;
int startPosPly;
Color sideToMove; Color sideToMove;
int threadID; int gamePly;
Key history[MaxGameLength];
File initialKFile, initialKRFile, initialQRFile;
StateInfo startState;
StateInfo* st; StateInfo* st;
int chess960;
// Static variables // Static variables
static Score pieceSquareTable[16][64]; // [piece][square] static int castleRightsMask[64];
static Key zobrist[2][8][64]; // [color][pieceType][square]/[piece count] static Key zobrist[2][8][64];
static Key zobEp[64]; // [square] static Key zobEp[64];
static Key zobCastle[16]; // [castleRight] static Key zobCastle[16];
static Key zobMaterial[2][8][16];
static Key zobSideToMove; static Key zobSideToMove;
static Score PieceSquareTable[16][64];
static Key zobExclusion; static Key zobExclusion;
}; };
inline int64_t Position::nodes_searched() const {
return nodes;
}
inline void Position::set_nodes_searched(int64_t n) { ////
nodes = n; //// Inline functions
} ////
inline Piece Position::piece_on(Square s) const { inline Piece Position::piece_on(Square s) const {
return board[s]; return board[s];
} }
inline Color Position::color_of_piece_on(Square s) const {
return color_of_piece(piece_on(s));
}
inline PieceType Position::type_of_piece_on(Square s) const {
return type_of_piece(piece_on(s));
}
inline bool Position::square_is_empty(Square s) const { inline bool Position::square_is_empty(Square s) const {
return board[s] == NO_PIECE; 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 { inline Color Position::side_to_move() const {
@@ -286,14 +379,14 @@ inline Color Position::side_to_move() const {
} }
inline Bitboard Position::occupied_squares() const { inline Bitboard Position::occupied_squares() const {
return occupied; return byTypeBB[0];
} }
inline Bitboard Position::empty_squares() const { inline Bitboard Position::empty_squares() const {
return ~occupied; return ~(occupied_squares());
} }
inline Bitboard Position::pieces(Color c) const { inline Bitboard Position::pieces_of_color(Color c) const {
return byColorBB[c]; return byColorBB[c];
} }
@@ -317,7 +410,11 @@ inline int Position::piece_count(Color c, PieceType pt) const {
return pieceCount[c][pt]; return pieceCount[c][pt];
} }
inline const Square* Position::piece_list(Color c, PieceType pt) const { inline Square Position::piece_list(Color c, PieceType pt, int index) const {
return pieceList[c][pt][index];
}
inline const Square* Position::piece_list_begin(Color c, PieceType pt) const {
return pieceList[c][pt]; return pieceList[c][pt];
} }
@@ -329,26 +426,34 @@ inline Square Position::king_square(Color c) const {
return pieceList[c][KING][0]; return pieceList[c][KING][0];
} }
inline bool Position::can_castle(CastleRight f) const { inline bool Position::can_castle_kingside(Color side) const {
return st->castleRights & f; return st->castleRights & (1+int(side));
} }
inline bool Position::can_castle(Color c) const { inline bool Position::can_castle_queenside(Color side) const {
return st->castleRights & ((WHITE_OO | WHITE_OOO) << c); return st->castleRights & (4+4*int(side));
} }
inline Square Position::castle_rook_square(CastleRight f) const { inline bool Position::can_castle(Color side) const {
return castleRookSquare[f]; return can_castle_kingside(side) || can_castle_queenside(side);
}
inline Square Position::initial_kr_square(Color c) const {
return relative_square(c, make_square(initialKRFile, RANK_1));
}
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];
} }
template<PieceType Piece> // Knight and King and white pawns template<PieceType Piece> // Knight and King and white pawns
inline Bitboard Position::attacks_from(Square s) const { inline Bitboard Position::attacks_from(Square s) const {
return StepAttacksBB[Piece][s]; return StepAttackBB[Piece][s];
} }
template<> template<>
@@ -366,56 +471,56 @@ inline Bitboard Position::attacks_from<QUEEN>(Square s) const {
return attacks_from<ROOK>(s) | attacks_from<BISHOP>(s); return attacks_from<ROOK>(s) | attacks_from<BISHOP>(s);
} }
inline Bitboard Position::attacks_from(Piece p, Square s) const {
return attacks_from(p, s, occupied_squares());
}
inline Bitboard Position::attackers_to(Square s) const {
return attackers_to(s, occupied_squares());
}
inline Bitboard Position::checkers() const { inline Bitboard Position::checkers() const {
return st->checkersBB; return st->checkersBB;
} }
inline bool Position::in_check() const { inline bool Position::is_check() const {
return st->checkersBB != 0; return st->checkersBB != EmptyBoardBB;
}
inline Bitboard Position::discovered_check_candidates() const {
return hidden_checkers<false>();
}
inline Bitboard Position::pinned_pieces() const {
return hidden_checkers<true>();
} }
inline bool Position::pawn_is_passed(Color c, Square s) const { inline bool Position::pawn_is_passed(Color c, Square s) const {
return !(pieces(PAWN, flip(c)) & passed_pawn_mask(c, s)); return !(pieces(PAWN, opposite_color(c)) & passed_pawn_mask(c, s));
} }
inline Key Position::key() const { inline bool Position::pawn_is_passed(Bitboard theirPawns, Color c, Square s) {
return !(theirPawns & passed_pawn_mask(c, s));
}
inline bool Position::pawn_is_isolated(Bitboard ourPawns, Square s) {
return !(ourPawns & neighboring_files_bb(s));
}
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::exclusion_key() const { inline Key Position::get_exclusion_key() const {
return st->key ^ zobExclusion; return st->key ^ zobExclusion;
} }
inline Key Position::pawn_key() const { 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::pst(Piece p, Square s) const { inline Score Position::pst(Color c, PieceType pt, Square s) const {
return pieceSquareTable[p][s]; return PieceSquareTable[piece_of_color_and_type(c, pt)][s];
} }
inline Score Position::pst_delta(Piece piece, Square from, Square to) const { inline Score Position::pst_delta(Piece piece, Square from, Square to) const {
return pieceSquareTable[piece][to] - pieceSquareTable[piece][from]; return PieceSquareTable[piece][to] - PieceSquareTable[piece][from];
} }
inline Score Position::value() const { inline Score Position::value() const {
@@ -426,50 +531,40 @@ inline Value Position::non_pawn_material(Color c) const {
return st->npMaterial[c]; return st->npMaterial[c];
} }
inline bool Position::is_passed_pawn_push(Move m) const { inline bool Position::move_is_passed_pawn_push(Move m) const {
return board[from_sq(m)] == make_piece(sideToMove, PAWN) Color c = side_to_move();
&& pawn_is_passed(sideToMove, to_sq(m)); return piece_on(move_from(m)) == piece_of_color_and_type(c, PAWN)
&& pawn_is_passed(c, move_to(m));
} }
inline int Position::startpos_ply_counter() const { inline int Position::rule_50_counter() const {
return startPosPly + st->pliesFromNull; // HACK
return st->rule50;
} }
inline bool Position::opposite_colored_bishops() const { inline bool Position::opposite_colored_bishops() const {
return pieceCount[WHITE][BISHOP] == 1 return piece_count(WHITE, BISHOP) == 1
&& pieceCount[BLACK][BISHOP] == 1 && piece_count(BLACK, BISHOP) == 1
&& opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]); && square_color(piece_list(WHITE, BISHOP, 0)) != square_color(piece_list(BLACK, BISHOP, 0));
} }
inline bool Position::has_pawn_on_7th(Color c) const { inline bool Position::has_pawn_on_7th(Color c) const {
return pieces(PAWN, c) & rank_bb(relative_rank(c, RANK_7));
return pieces(PAWN, c) & relative_rank_bb(c, RANK_7);
} }
inline bool Position::is_chess960() const { inline bool Position::move_is_capture(Move m) const {
return chess960;
// Move must not be MOVE_NONE !
return (m & (3 << 15)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
} }
inline bool Position::is_capture_or_promotion(Move m) const { inline bool Position::move_is_capture_or_promotion(Move m) const {
assert(is_ok(m)); // Move must not be MOVE_NONE !
return is_special(m) ? !is_castle(m) : !square_is_empty(to_sq(m)); return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
}
inline bool Position::is_capture(Move m) const {
// Note that castle is coded as "king captures the rook"
assert(is_ok(m));
return (!square_is_empty(to_sq(m)) && !is_castle(m)) || is_enpassant(m);
}
inline PieceType Position::captured_piece_type() const {
return st->capturedType;
}
inline int Position::thread() const {
return threadID;
} }
#endif // !defined(POSITION_H_INCLUDED) #endif // !defined(POSITION_H_INCLUDED)
+146 -56
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,82 +17,172 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(PSQTAB_H_INCLUDED) #if !defined(PSQTAB_H_INCLUDED)
#define PSQTAB_H_INCLUDED #define PSQTAB_H_INCLUDED
#include "types.h" ////
//// Includes
////
#define S(mg, eg) make_score(mg, eg) #include "value.h"
/// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on ////
/// a given square a (midgame, endgame) score pair is assigned. PSQT is defined //// Constants modified by Joona Kiiski
/// for white side, for black side the tables are symmetric. ////
static const Score PSQT[][64] = { 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 {// Pawn
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0), // A B C D E F G H
S(-28,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-28,-8), 0, 0, 0, 0, 0, 0, 0, 0,
S(-28,-8), S(-6,-8), S( 9,-8), S(36,-8), S(36,-8), S( 9,-8), S(-6,-8), S(-28,-8), MP-28, MP-6, MP+ 4, MP+14, MP+14, MP+ 4, MP-6, MP-28,
S(-28,-8), S(-6,-8), S(17,-8), S(58,-8), S(58,-8), S(17,-8), S(-6,-8), S(-28,-8), MP-28, MP-6, MP+ 9, MP+36, MP+36, MP+ 9, MP-6, MP-28,
S(-28,-8), S(-6,-8), S(17,-8), S(36,-8), S(36,-8), S(17,-8), S(-6,-8), S(-28,-8), MP-28, MP-6, MP+17, MP+58, MP+58, MP+17, MP-6, MP-28,
S(-28,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-28,-8), MP-28, MP-6, MP+17, MP+36, MP+36, MP+17, MP-6, MP-28,
S(-28,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-28,-8), MP-28, MP-6, MP+ 9, MP+14, MP+14, MP+ 9, MP-6, MP-28,
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0) 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 {// Knight
S(-135,-104), S(-107,-79), S(-80,-55), S(-67,-42), S(-67,-42), S(-80,-55), S(-107,-79), S(-135,-104), // A B C D E F G H
S( -93, -79), S( -67,-55), S(-39,-30), S(-25,-17), S(-25,-17), S(-39,-30), S( -67,-55), S( -93, -79), MK-135, MK-107, MK-80, MK-67, MK-67, MK-80, MK-107, MK-135,
S( -53, -55), S( -25,-30), S( 1, -6), S( 13, 5), S( 13, 5), S( 1, -6), S( -25,-30), S( -53, -55), MK- 93, MK- 67, MK-39, MK-25, MK-25, MK-39, MK- 67, MK- 93,
S( -25, -42), S( 1,-17), S( 27, 5), S( 41, 18), S( 41, 18), S( 27, 5), S( 1,-17), S( -25, -42), MK- 53, MK- 25, MK+ 1, MK+13, MK+13, MK+ 1, MK- 25, MK- 53,
S( -11, -42), S( 13,-17), S( 41, 5), S( 55, 18), S( 55, 18), S( 41, 5), S( 13,-17), S( -11, -42), MK- 25, MK+ 1, MK+27, MK+41, MK+41, MK+27, MK+ 1, MK- 25,
S( -11, -55), S( 13,-30), S( 41, -6), S( 55, 5), S( 55, 5), S( 41, -6), S( 13,-30), S( -11, -55), MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11,
S( -53, -79), S( -25,-55), S( 1,-30), S( 13,-17), S( 13,-17), S( 1,-30), S( -25,-55), S( -53, -79), MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11,
S(-193,-104), S( -67,-79), S(-39,-55), S(-25,-42), S(-25,-42), S(-39,-55), S( -67,-79), S(-193,-104) 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 {// Bishop
S(-40,-59), S(-40,-42), S(-35,-35), S(-30,-26), S(-30,-26), S(-35,-35), S(-40,-42), S(-40,-59), // A B C D E F G H
S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42), MB-40, MB-40, MB-35, MB-30, MB-30, MB-35, MB-40, MB-40,
S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35), MB-17, MB+ 0, MB- 4, MB+ 0, MB+ 0, MB- 4, MB+ 0, MB-17,
S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26), MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13,
S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26), MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8,
S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35), MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8,
S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42), MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13,
S(-17,-59), S(-17,-42), S(-13,-35), S( -8,-26), S( -8,-26), S(-13,-35), S(-17,-42), S(-17,-59) 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 {// Rook
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), // A B C D E F G H
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3), MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12,
S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3) 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 {// Queen
S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80), // A B C D E F G H
S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54), MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42), MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30), MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30), MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42), MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54), MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8,
S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80) 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 {// King
S(287, 18), S(311, 77), S(262,105), S(214,135), S(214,135), S(262,105), S(311, 77), S(287, 18), //A B C D E F G H
S(262, 77), S(287,135), S(238,165), S(190,193), S(190,193), S(238,165), S(287,135), S(262, 77), 287, 311, 262, 214, 214, 262, 311, 287,
S(214,105), S(238,165), S(190,193), S(142,222), S(142,222), S(190,193), S(238,165), S(214,105), 262, 287, 238, 190, 190, 238, 287, 262,
S(190,135), S(214,193), S(167,222), S(119,251), S(119,251), S(167,222), S(214,193), S(190,135), 214, 238, 190, 142, 142, 190, 238, 214,
S(167,135), S(190,193), S(142,222), S( 94,251), S( 94,251), S(142,222), S(190,193), S(167,135), 190, 214, 167, 119, 119, 167, 214, 190,
S(142,105), S(167,165), S(119,193), S( 69,222), S( 69,222), S(119,193), S(167,165), S(142,105), 167, 190, 142, 94, 94, 142, 190, 167,
S(119, 77), S(142,135), S( 94,165), S( 46,193), S( 46,193), S( 94,165), S(142,135), S(119, 77), 142, 167, 119, 69, 69, 119, 167, 142,
S(94, 18), S(119, 77), S( 69,105), S( 21,135), S( 21,135), S( 69,105), S(119, 77), S( 94, 18) 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
} }
}; };
#undef S
#endif // !defined(PSQTAB_H_INCLUDED) #endif // !defined(PSQTAB_H_INCLUDED)
-78
View File
@@ -1,78 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, 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/>.
This file is based on original code by Heinz van Saanen and is
available under the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
#if !defined(RKISS_H_INCLUDED)
#define RKISS_H_INCLUDED
#include "types.h"
/// RKISS is our pseudo random number generator (PRNG) used to compute hash keys.
/// George Marsaglia invented the RNG-Kiss-family in the early 90's. This is a
/// specific version that Heinz van Saanen derived from some public domain code
/// by Bob Jenkins. Following the feature list, as tested by Heinz.
///
/// - Quite platform independent
/// - Passes ALL dieharder tests! Here *nix sys-rand() e.g. fails miserably:-)
/// - ~12 times faster than my *nix sys-rand()
/// - ~4 times faster than SSE2-version of Mersenne twister
/// - Average cycle length: ~2^126
/// - 64 bit seed
/// - Return doubles with a full 53 bit mantissa
/// - Thread safe
class RKISS {
// Keep variables always together
struct S { uint64_t a, b, c, d; } s;
uint64_t rotate(uint64_t x, uint64_t k) const {
return (x << k) | (x >> (64 - k));
}
// Return 64 bit unsigned integer in between [0, 2^64 - 1]
uint64_t rand64() {
const uint64_t
e = s.a - rotate(s.b, 7);
s.a = s.b ^ rotate(s.c, 13);
s.b = s.c + rotate(s.d, 37);
s.c = s.d + e;
return s.d = e + s.a;
}
// Init seed and scramble a few rounds
void raninit() {
s.a = 0xf1ea5eed;
s.b = s.c = s.d = 0xd4e12c77;
for (int i = 0; i < 73; i++)
rand64();
}
public:
RKISS() { raninit(); }
template<typename T> T rand() { return T(rand64()); }
};
#endif // !defined(RKISS_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)
+2600 -1510
View File
File diff suppressed because it is too large Load Diff
+41 -47
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,66 +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/>.
*/ */
#if !defined(SEARCH_H_INCLUDED) #if !defined(SEARCH_H_INCLUDED)
#define SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED
#include <cstring> ////
#include <vector> //// Includes
////
#include "types.h" #include "depth.h"
#include "move.h"
class Position;
struct SplitPoint;
namespace Search { ////
//// Constants
////
/// The Stack struct keeps track of the information we need to remember from const int PLY_MAX = 100;
/// nodes shallower and deeper in the tree during the search. Each search thread const int PLY_MAX_PLUS_2 = 102;
/// has its own array of Stack objects, indexed by the current ply. const int KILLER_MAX = 2;
struct Stack {
SplitPoint* sp; ////
int ply; //// 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 bestMove; Move threatMove;
Move killers[2]; Move killers[KILLER_MAX];
Depth reduction; Depth reduction;
Value eval;
Value evalMargin; void init(int ply);
int skipNullMove; void initKillers();
}; };
/// The LimitsType struct stores information sent by GUI about available time ////
/// to search the current move, maximum depth/time, if we are in analysis mode //// Prototypes
/// or if we have to ponder while is our opponent's side to move. ////
struct LimitsType { extern void init_threads();
extern void stop_threads();
extern bool think(const Position &pos, bool infinite, bool ponder, int side_to_move,
int time[], int increment[], int movesToGo, int maxDepth,
int maxNodes, int maxTime, Move searchMoves[]);
extern int perft(Position &pos, Depth depth);
extern int64_t nodes_searched();
LimitsType() { memset(this, 0, sizeof(LimitsType)); }
bool use_time_management() const { return !(maxTime | maxDepth | maxNodes | infinite); }
int time, increment, movesToGo, maxTime, maxDepth, maxNodes, infinite, ponder;
};
/// The SignalsType struct stores volatile flags updated during the search
/// typically in an async fashion, for instance to stop the search by the GUI.
struct SignalsType {
bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot;
};
extern volatile SignalsType Signals;
extern LimitsType Limits;
extern std::vector<Move> SearchMoves;
extern Position RootPosition;
extern void init();
extern int64_t perft(Position& pos, Depth depth);
extern void think();
} // namespace
#endif // !defined(SEARCH_H_INCLUDED) #endif // !defined(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)
-502
View File
@@ -1,502 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, 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 <iostream>
#include "search.h"
#include "thread.h"
#include "ucioption.h"
using namespace Search;
ThreadsManager Threads; // Global object
namespace { extern "C" {
// start_routine() is the C function which is called when a new thread
// is launched. It simply calls idle_loop() of the supplied thread. The first
// and last thread are special. First one is the main search thread while the
// last one mimics a timer, they run in main_loop() and timer_loop().
#if defined(_MSC_VER)
DWORD WINAPI start_routine(LPVOID thread) {
#else
void* start_routine(void* thread) {
#endif
Thread* th = (Thread*)thread;
if (th->threadID == 0)
th->main_loop();
else if (th->threadID == MAX_THREADS)
th->timer_loop();
else
th->idle_loop(NULL);
return 0;
}
} }
// wake_up() wakes up the thread, normally at the beginning of the search or,
// if "sleeping threads" is used, when there is some work to do.
void Thread::wake_up() {
lock_grab(&sleepLock);
cond_signal(&sleepCond);
lock_release(&sleepLock);
}
// cutoff_occurred() checks whether a beta cutoff has occurred in the current
// active split point, or in some ancestor of the split point.
bool Thread::cutoff_occurred() const {
for (SplitPoint* sp = splitPoint; sp; sp = sp->parent)
if (sp->is_betaCutoff)
return true;
return false;
}
// is_available_to() checks whether the thread is available to help the thread with
// threadID "master" at a split point. An obvious requirement is that thread must be
// idle. With more than two threads, this is not by itself sufficient: If the thread
// is the master of some active split point, it is only available as a slave to the
// threads which are busy searching the split point at the top of "slave"'s split
// point stack (the "helpful master concept" in YBWC terminology).
bool Thread::is_available_to(int master) const {
if (is_searching)
return false;
// Make a local copy to be sure doesn't become zero under our feet while
// testing next condition and so leading to an out of bound access.
int localActiveSplitPoints = activeSplitPoints;
// No active split points means that the thread is available as a slave for any
// other thread otherwise apply the "helpful master" concept if possible.
if ( !localActiveSplitPoints
|| splitPoints[localActiveSplitPoints - 1].is_slave[master])
return true;
return false;
}
// read_uci_options() updates number of active threads and other parameters
// according to the UCI options values. It is called before to start a new search.
void ThreadsManager::read_uci_options() {
maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
useSleepingThreads = Options["Use Sleeping Threads"];
set_size(Options["Threads"]);
}
// set_size() changes the number of active threads and raises do_sleep flag for
// all the unused threads that will go immediately to sleep.
void ThreadsManager::set_size(int cnt) {
assert(cnt > 0 && cnt <= MAX_THREADS);
activeThreads = cnt;
for (int i = 1; i < MAX_THREADS; i++) // Ignore main thread
if (i < activeThreads)
{
// Dynamically allocate pawn and material hash tables according to the
// number of active threads. This avoids preallocating memory for all
// possible threads if only few are used.
threads[i].pawnTable.init();
threads[i].materialTable.init();
threads[i].do_sleep = false;
}
else
threads[i].do_sleep = true;
}
// init() is called during startup. Initializes locks and condition variables
// and launches all threads sending them immediately to sleep.
void ThreadsManager::init() {
// Initialize sleep condition and lock used by thread manager
cond_init(&sleepCond);
lock_init(&threadsLock);
// Initialize thread's sleep conditions and split point locks
for (int i = 0; i <= MAX_THREADS; i++)
{
lock_init(&threads[i].sleepLock);
cond_init(&threads[i].sleepCond);
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
lock_init(&(threads[i].splitPoints[j].lock));
}
// Allocate main thread tables to call evaluate() also when not searching
threads[0].pawnTable.init();
threads[0].materialTable.init();
// Create and launch all the threads, threads will go immediately to sleep
for (int i = 0; i <= MAX_THREADS; i++)
{
threads[i].is_searching = false;
threads[i].do_sleep = (i != 0); // Avoid a race with start_thinking()
threads[i].threadID = i;
#if defined(_MSC_VER)
threads[i].handle = CreateThread(NULL, 0, start_routine, &threads[i], 0, NULL);
bool ok = (threads[i].handle != NULL);
#else
bool ok = !pthread_create(&threads[i].handle, NULL, start_routine, &threads[i]);
#endif
if (!ok)
{
std::cerr << "Failed to create thread number " << i << std::endl;
::exit(EXIT_FAILURE);
}
}
}
// exit() is called to cleanly terminate the threads when the program finishes
void ThreadsManager::exit() {
for (int i = 0; i <= MAX_THREADS; i++)
{
threads[i].do_terminate = true; // Search must be already finished
threads[i].wake_up();
// Wait for thread termination
#if defined(_MSC_VER)
WaitForSingleObject(threads[i].handle, INFINITE);
CloseHandle(threads[i].handle);
#else
pthread_join(threads[i].handle, NULL);
#endif
// Now we can safely destroy associated locks and wait conditions
lock_destroy(&threads[i].sleepLock);
cond_destroy(&threads[i].sleepCond);
for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
lock_destroy(&(threads[i].splitPoints[j].lock));
}
lock_destroy(&threadsLock);
cond_destroy(&sleepCond);
}
// available_slave_exists() tries to find an idle thread which is available as
// a slave for the thread with threadID 'master'.
bool ThreadsManager::available_slave_exists(int master) const {
assert(master >= 0 && master < activeThreads);
for (int i = 0; i < activeThreads; i++)
if (threads[i].is_available_to(master))
return true;
return false;
}
// split_point_finished() checks if all the slave threads of a given split
// point have finished searching.
bool ThreadsManager::split_point_finished(SplitPoint* sp) const {
for (int i = 0; i < activeThreads; i++)
if (sp->is_slave[i])
return false;
return true;
}
// split() does the actual work of distributing the work at a node between
// several available threads. If it does not succeed in splitting the node
// (because no idle threads are available, or because we have no unused split
// point objects), the function immediately returns. If splitting is possible, a
// SplitPoint object is initialized with all the data that must be copied to the
// helper threads and then helper threads are told that they have been assigned
// work. This will cause them to instantly leave their idle loops and call
// search(). When all threads have returned from search() then split() returns.
template <bool Fake>
Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta,
Value bestValue, Depth depth, Move threatMove,
int moveCount, MovePicker* mp, int nodeType) {
assert(pos.pos_is_ok());
assert(bestValue > -VALUE_INFINITE);
assert(bestValue <= alpha);
assert(alpha < beta);
assert(beta <= VALUE_INFINITE);
assert(depth > DEPTH_ZERO);
assert(pos.thread() >= 0 && pos.thread() < activeThreads);
assert(activeThreads > 1);
int i, master = pos.thread();
Thread& masterThread = threads[master];
// If we already have too many active split points, don't split
if (masterThread.activeSplitPoints >= MAX_ACTIVE_SPLIT_POINTS)
return bestValue;
// Pick the next available split point from the split point stack
SplitPoint* sp = &masterThread.splitPoints[masterThread.activeSplitPoints];
// Initialize the split point
sp->parent = masterThread.splitPoint;
sp->master = master;
sp->is_betaCutoff = false;
sp->depth = depth;
sp->threatMove = threatMove;
sp->alpha = alpha;
sp->beta = beta;
sp->nodeType = nodeType;
sp->bestValue = bestValue;
sp->mp = mp;
sp->moveCount = moveCount;
sp->pos = &pos;
sp->nodes = 0;
sp->ss = ss;
for (i = 0; i < activeThreads; i++)
sp->is_slave[i] = false;
// If we are here it means we are not available
assert(masterThread.is_searching);
int workersCnt = 1; // At least the master is included
// Try to allocate available threads and ask them to start searching setting
// is_searching flag. This must be done under lock protection to avoid concurrent
// allocation of the same slave by another master.
lock_grab(&threadsLock);
for (i = 0; !Fake && i < activeThreads && workersCnt < maxThreadsPerSplitPoint; i++)
if (threads[i].is_available_to(master))
{
workersCnt++;
sp->is_slave[i] = true;
threads[i].splitPoint = sp;
// This makes the slave to exit from idle_loop()
threads[i].is_searching = true;
if (useSleepingThreads)
threads[i].wake_up();
}
lock_release(&threadsLock);
// We failed to allocate even one slave, return
if (!Fake && workersCnt == 1)
return bestValue;
masterThread.splitPoint = sp;
masterThread.activeSplitPoints++;
// Everything is set up. The master thread enters the idle loop, from which
// it will instantly launch a search, because its is_searching flag is set.
// We pass the split point as a parameter to the idle loop, which means that
// the thread will return from the idle loop when all slaves have finished
// their work at this split point.
masterThread.idle_loop(sp);
// In helpful master concept a master can help only a sub-tree of its split
// point, and because here is all finished is not possible master is booked.
assert(!masterThread.is_searching);
// We have returned from the idle loop, which means that all threads are
// finished. Note that changing state and decreasing activeSplitPoints is done
// under lock protection to avoid a race with Thread::is_available_to().
lock_grab(&threadsLock);
masterThread.is_searching = true;
masterThread.activeSplitPoints--;
lock_release(&threadsLock);
masterThread.splitPoint = sp->parent;
pos.set_nodes_searched(pos.nodes_searched() + sp->nodes);
return sp->bestValue;
}
// Explicit template instantiations
template Value ThreadsManager::split<false>(Position&, Stack*, Value, Value, Value, Depth, Move, int, MovePicker*, int);
template Value ThreadsManager::split<true>(Position&, Stack*, Value, Value, Value, Depth, Move, int, MovePicker*, int);
// Thread::timer_loop() is where the timer thread waits maxPly milliseconds and
// then calls do_timer_event(). If maxPly is 0 thread sleeps until is woken up.
extern void check_time();
void Thread::timer_loop() {
while (!do_terminate)
{
lock_grab(&sleepLock);
timed_wait(&sleepCond, &sleepLock, maxPly ? maxPly : INT_MAX);
lock_release(&sleepLock);
check_time();
}
}
// ThreadsManager::set_timer() is used to set the timer to trigger after msec
// milliseconds. If msec is 0 then timer is stopped.
void ThreadsManager::set_timer(int msec) {
Thread& timer = threads[MAX_THREADS];
lock_grab(&timer.sleepLock);
timer.maxPly = msec;
cond_signal(&timer.sleepCond); // Wake up and restart the timer
lock_release(&timer.sleepLock);
}
// Thread::main_loop() is where the main thread is parked waiting to be started
// when there is a new search. Main thread will launch all the slave threads.
void Thread::main_loop() {
while (true)
{
lock_grab(&sleepLock);
do_sleep = true; // Always return to sleep after a search
is_searching = false;
while (do_sleep && !do_terminate)
{
cond_signal(&Threads.sleepCond); // Wake up UI thread if needed
cond_wait(&sleepCond, &sleepLock);
}
is_searching = true;
lock_release(&sleepLock);
if (do_terminate)
return;
think(); // This is the search entry point
}
}
// ThreadsManager::start_thinking() is used by UI thread to wake up the main
// thread parked in main_loop() and starting a new search. If asyncMode is true
// then function returns immediately, otherwise caller is blocked waiting for
// the search to finish.
void ThreadsManager::start_thinking(const Position& pos, const LimitsType& limits,
const std::vector<Move>& searchMoves, bool asyncMode) {
Thread& main = threads[0];
lock_grab(&main.sleepLock);
// Wait main thread has finished before to launch a new search
while (!main.do_sleep)
cond_wait(&sleepCond, &main.sleepLock);
// Copy input arguments to initialize the search
RootPosition.copy(pos, 0);
Limits = limits;
SearchMoves = searchMoves;
// Reset signals before to start the new search
memset((void*)&Signals, 0, sizeof(Signals));
main.do_sleep = false;
cond_signal(&main.sleepCond); // Wake up main thread and start searching
if (!asyncMode)
while (!main.do_sleep)
cond_wait(&sleepCond, &main.sleepLock);
lock_release(&main.sleepLock);
}
// ThreadsManager::stop_thinking() is used by UI thread to raise a stop request
// and to wait for the main thread finishing the search. Needed to wait exiting
// and terminate the threads after a 'quit' command.
void ThreadsManager::stop_thinking() {
Thread& main = threads[0];
Search::Signals.stop = true;
lock_grab(&main.sleepLock);
cond_signal(&main.sleepCond); // In case is waiting for stop or ponderhit
while (!main.do_sleep)
cond_wait(&sleepCond, &main.sleepLock);
lock_release(&main.sleepLock);
}
// ThreadsManager::wait_for_stop_or_ponderhit() is called when the maximum depth
// is reached while the program is pondering. The point is to work around a wrinkle
// in the UCI protocol: When pondering, the engine is not allowed to give a
// "bestmove" before the GUI sends it a "stop" or "ponderhit" command. We simply
// wait here until one of these commands (that raise StopRequest) is sent and
// then return, after which the bestmove and pondermove will be printed.
void ThreadsManager::wait_for_stop_or_ponderhit() {
Signals.stopOnPonderhit = true;
Thread& main = threads[0];
lock_grab(&main.sleepLock);
while (!Signals.stop)
cond_wait(&main.sleepCond, &main.sleepLock);
lock_release(&main.sleepLock);
}
+39 -101
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,126 +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/>.
*/ */
#if !defined(THREAD_H_INCLUDED) #if !defined(THREAD_H_INCLUDED)
#define THREAD_H_INCLUDED #define THREAD_H_INCLUDED
#include <cstring>
////
//// Includes
////
#include "lock.h" #include "lock.h"
#include "material.h"
#include "movepick.h" #include "movepick.h"
#include "pawns.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
const int MAX_THREADS = 32;
const int MAX_ACTIVE_SPLIT_POINTS = 8; ////
//// Constants and variables
////
const int THREAD_MAX = 8;
////
//// Types
////
struct SplitPoint { struct SplitPoint {
// Const data after splitPoint has been setup
SplitPoint *parent; SplitPoint *parent;
const Position* pos; Position pos;
Depth depth; SearchStack sstack[THREAD_MAX][PLY_MAX_PLUS_2];
Value beta; SearchStack *parentSstack;
int nodeType;
int ply; int ply;
int master; Depth depth;
Move threatMove; volatile Value alpha, beta, bestValue, futilityValue;
Value approximateEval;
// Const pointers to shared data bool pvNode;
MovePicker* mp; int master, slaves[THREAD_MAX];
Search::Stack* ss;
// Shared data
Lock lock; Lock lock;
volatile int64_t nodes; MovePicker *mp;
volatile Value alpha; volatile int moves;
volatile Value bestValue; volatile int cpus;
volatile int moveCount; bool finished;
volatile bool is_betaCutoff;
volatile bool is_slave[MAX_THREADS];
}; };
/// Thread struct keeps together all the thread related stuff like locks, state
/// and especially split points. We also use per-thread pawn and material hash
/// tables so that once we get a pointer to an entry its life time is unlimited
/// and we don't have to care about someone changing the entry under our feet.
struct Thread { struct Thread {
SplitPoint *splitPoint;
void wake_up();
bool cutoff_occurred() const;
bool is_available_to(int master) const;
void idle_loop(SplitPoint* sp);
void main_loop();
void timer_loop();
SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS];
MaterialInfoTable materialTable;
PawnInfoTable pawnTable;
int threadID;
int maxPly;
Lock sleepLock;
WaitCondition sleepCond;
SplitPoint* volatile splitPoint;
volatile int activeSplitPoints; volatile int activeSplitPoints;
volatile bool is_searching; uint64_t nodes;
volatile bool do_sleep; uint64_t betaCutOffs[2];
volatile bool do_terminate; bool failHighPly1;
volatile bool stop;
#if defined(_MSC_VER) volatile bool running;
HANDLE handle; volatile bool idle;
#else volatile bool workIsWaiting;
pthread_t handle; volatile bool printCurrentLine;
#endif unsigned char pad[64]; // set some distance among local data for each thread
}; };
/// ThreadsManager class handles all the threads related stuff like init, starting,
/// parking and, the most important, launching a slave thread at a split point.
/// All the access to shared thread data is done through this class.
class ThreadsManager {
/* As long as the single ThreadsManager object is defined as a global we don't
need to explicitly initialize to zero its data members because variables with
static storage duration are automatically set to zero before enter main()
*/
public:
Thread& operator[](int threadID) { return threads[threadID]; }
void init();
void exit();
bool use_sleeping_threads() const { return useSleepingThreads; }
int min_split_depth() const { return minimumSplitDepth; }
int size() const { return activeThreads; }
void set_size(int cnt);
void read_uci_options();
bool available_slave_exists(int master) const;
bool split_point_finished(SplitPoint* sp) const;
void set_timer(int msec);
void wait_for_stop_or_ponderhit();
void stop_thinking();
void start_thinking(const Position& pos, const Search::LimitsType& limits,
const std::vector<Move>& searchMoves, bool asyncMode);
template <bool Fake>
Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue,
Depth depth, Move threatMove, int moveCount, MovePicker* mp, int nodeType);
private:
friend struct Thread;
Thread threads[MAX_THREADS + 1]; // Last one is used as a timer
Lock threadsLock;
Depth minimumSplitDepth;
int maxThreadsPerSplitPoint;
int activeThreads;
bool useSleepingThreads;
WaitCondition sleepCond;
};
extern ThreadsManager Threads;
#endif // !defined(THREAD_H_INCLUDED) #endif // !defined(THREAD_H_INCLUDED)
-162
View File
@@ -1,162 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, 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 <cmath>
#include <algorithm>
#include "misc.h"
#include "search.h"
#include "timeman.h"
#include "ucioption.h"
namespace {
/// Constants
const int MoveHorizon = 50; // Plan time management at most this many moves ahead
const float MaxRatio = 3.0f; // When in trouble, we can step over reserved time with this ratio
const float StealRatio = 0.33f; // However we must not steal time from remaining moves over this ratio
// MoveImportance[] is 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.
const int MoveImportance[512] = {
7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780, 7780,
7780, 7780, 7780, 7780, 7778, 7778, 7776, 7776, 7776, 7773, 7770, 7768, 7766, 7763, 7757, 7751,
7743, 7735, 7724, 7713, 7696, 7689, 7670, 7656, 7627, 7605, 7571, 7549, 7522, 7493, 7462, 7425,
7385, 7350, 7308, 7272, 7230, 7180, 7139, 7094, 7055, 7010, 6959, 6902, 6841, 6778, 6705, 6651,
6569, 6508, 6435, 6378, 6323, 6253, 6152, 6085, 5995, 5931, 5859, 5794, 5717, 5646, 5544, 5462,
5364, 5282, 5172, 5078, 4988, 4901, 4831, 4764, 4688, 4609, 4536, 4443, 4365, 4293, 4225, 4155,
4085, 4005, 3927, 3844, 3765, 3693, 3634, 3560, 3479, 3404, 3331, 3268, 3207, 3146, 3077, 3011,
2947, 2894, 2828, 2776, 2727, 2676, 2626, 2589, 2538, 2490, 2442, 2394, 2345, 2302, 2243, 2192,
2156, 2115, 2078, 2043, 2004, 1967, 1922, 1893, 1845, 1809, 1772, 1736, 1702, 1674, 1640, 1605,
1566, 1536, 1509, 1479, 1452, 1423, 1388, 1362, 1332, 1304, 1289, 1266, 1250, 1228, 1206, 1180,
1160, 1134, 1118, 1100, 1080, 1068, 1051, 1034, 1012, 1001, 980, 960, 945, 934, 916, 900, 888,
878, 865, 852, 828, 807, 787, 770, 753, 744, 731, 722, 706, 700, 683, 676, 671, 664, 652, 641,
634, 627, 613, 604, 591, 582, 568, 560, 552, 540, 534, 529, 519, 509, 495, 484, 474, 467, 460,
450, 438, 427, 419, 410, 406, 399, 394, 387, 382, 377, 372, 366, 359, 353, 348, 343, 337, 333,
328, 321, 315, 309, 303, 298, 293, 287, 284, 281, 277, 273, 265, 261, 255, 251, 247, 241, 240,
235, 229, 218, 217, 213, 212, 208, 206, 197, 193, 191, 189, 185, 184, 180, 177, 172, 170, 170,
170, 166, 163, 159, 158, 156, 155, 151, 146, 141, 138, 136, 132, 130, 128, 125, 123, 122, 118,
118, 118, 117, 115, 114, 108, 107, 105, 105, 105, 102, 97, 97, 95, 94, 93, 91, 88, 86, 83, 80,
80, 79, 79, 79, 78, 76, 75, 72, 72, 71, 70, 68, 65, 63, 61, 61, 59, 59, 59, 58, 56, 55, 54, 54,
52, 49, 48, 48, 48, 48, 45, 45, 45, 44, 43, 41, 41, 41, 41, 40, 40, 38, 37, 36, 34, 34, 34, 33,
31, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 24, 24, 23, 23, 22, 21, 20, 20,
19, 19, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, 17, 17, 16, 16, 15, 15, 14, 14, 14, 12, 12, 11,
9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1 };
int move_importance(int ply) { return MoveImportance[std::min(ply, 511)]; }
/// Function Prototypes
enum TimeType { OptimumTime, MaxTime };
template<TimeType>
int remaining(int myTime, int movesToGo, int fullMoveNumber);
}
void TimeManager::pv_instability(int curChanges, int prevChanges) {
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
+ prevChanges * (optimumSearchTime / 3);
}
void TimeManager::init(const Search::LimitsType& limits, int currentPly)
{
/* We support four different kind of time controls:
increment == 0 && movesToGo == 0 means: x basetime [sudden death!]
increment == 0 && movesToGo != 0 means: x moves in y minutes
increment > 0 && movesToGo == 0 means: x basetime + z increment
increment > 0 && movesToGo != 0 means: x moves in y minutes + z increment
Time management is adjusted by following UCI parameters:
emergencyMoveHorizon: Be prepared to always play at least this many moves
emergencyBaseTime : Always attempt to keep at least this much time (in ms) at clock
emergencyMoveTime : Plus attempt to keep at least this much time for each remaining emergency move
minThinkingTime : No matter what, use at least this much thinking before doing the move
*/
int hypMTG, hypMyTime, t1, t2;
// Read uci parameters
int emergencyMoveHorizon = Options["Emergency Move Horizon"];
int emergencyBaseTime = Options["Emergency Base Time"];
int emergencyMoveTime = Options["Emergency Move Time"];
int minThinkingTime = Options["Minimum Thinking Time"];
// Initialize to maximum values but unstablePVExtraTime that is reset
unstablePVExtraTime = 0;
optimumSearchTime = maximumSearchTime = limits.time;
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
for (hypMTG = 1; hypMTG <= (limits.movesToGo ? std::min(limits.movesToGo, MoveHorizon) : MoveHorizon); hypMTG++)
{
// Calculate thinking time for hypothetic "moves to go"-value
hypMyTime = limits.time
+ limits.increment * (hypMTG - 1)
- emergencyBaseTime
- emergencyMoveTime * std::min(hypMTG, emergencyMoveHorizon);
hypMyTime = std::max(hypMyTime, 0);
t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly);
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly);
optimumSearchTime = std::min(optimumSearchTime, t1);
maximumSearchTime = std::min(maximumSearchTime, t2);
}
if (Options["Ponder"])
optimumSearchTime += optimumSearchTime / 4;
// Make sure that maxSearchTime is not over absoluteMaxSearchTime
optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime);
}
namespace {
template<TimeType T>
int remaining(int myTime, int movesToGo, int currentPly)
{
const float TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
const float TStealRatio = (T == OptimumTime ? 0 : StealRatio);
int thisMoveImportance = move_importance(currentPly);
int otherMovesImportance = 0;
for (int i = 1; i < movesToGo; i++)
otherMovesImportance += move_importance(currentPly + 2 * i);
float ratio1 = (TMaxRatio * thisMoveImportance) / float(TMaxRatio * thisMoveImportance + otherMovesImportance);
float ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / float(thisMoveImportance + otherMovesImportance);
return int(floor(myTime * std::min(ratio1, ratio2)));
}
}
+156 -41
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,17 +17,31 @@
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>
#include <iostream>
////
//// 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::TranspositionTable() { TranspositionTable::TranspositionTable() {
size = generation = 0; size = writes = 0;
entries = NULL; entries = 0;
generation = 0;
} }
TranspositionTable::~TranspositionTable() { TranspositionTable::~TranspositionTable() {
@@ -36,39 +50,40 @@ TranspositionTable::~TranspositionTable() {
} }
/// TranspositionTable::set_size() sets the size of the transposition table, /// TranspositionTable::set_size sets the size of the transposition table,
/// measured in megabytes. /// measured in megabytes.
void TranspositionTable::set_size(size_t mbSize) { void TranspositionTable::set_size(size_t mbSize) {
assert(mbSize >= 4 && mbSize <= 8192);
size_t newSize = 1024; size_t newSize = 1024;
// Transposition table consists of clusters and each cluster consists // We store a cluster of ClusterSize number of TTEntry for each position
// of ClusterSize number of TTEntries. Each non-empty entry contains // and newSize is the maximum number of storable positions.
// information of exactly one position and newSize is the number of while ((2 * newSize) * sizeof(TTCluster) <= (mbSize << 20))
// clusters we are going to allocate.
while (2ULL * newSize * sizeof(TTCluster) <= (mbSize << 20))
newSize *= 2; newSize *= 2;
if (newSize == size) if (newSize != size)
return; {
size = newSize; size = newSize;
delete [] entries; delete [] entries;
entries = new (std::nothrow) TTCluster[size]; entries = new TTCluster[size];
if (!entries) if (!entries)
{ {
std::cerr << "Failed to allocate " << mbSize std::cerr << "Failed to allocate " << mbSize
<< " MB for transposition table." << std::endl; << " MB for transposition table." << std::endl;
exit(EXIT_FAILURE); Application::exit_with_failure();
} }
clear(); clear();
} }
}
/// TranspositionTable::clear() overwrites the entire transposition table /// TranspositionTable::clear overwrites the entire transposition table
/// with zeroes. It is called whenever the table is resized, or when the /// 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() {
@@ -76,51 +91,66 @@ void TranspositionTable::clear() {
} }
/// TranspositionTable::store() writes a new entry containing position key and /// TranspositionTable::first_entry returns a pointer to the first
/// valuable information of current position. The lowest order bits of position /// entry of a cluster given a position. The low 32 bits of the key
/// key are used to decide on which cluster the position will be placed. /// are used to get the index in the table.
/// When a new entry is written and there are no empty entries available in cluster,
/// it replaces the least valuable of entries. 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.
void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m, Value statV, Value kingD) { inline TTEntry* TranspositionTable::first_entry(const Key posKey) const {
return entries[uint32_t(posKey) & (size - 1)].data;
}
/// TranspositionTable::store writes a new entry containing a position,
/// a value, a value type, a search depth, and a best move to the
/// transposition table. Transposition table is organized in clusters of
/// four TTEntry objects, and when a new entry is written, it replaces
/// the least valuable of the four entries in a cluster. A TTEntry t1 is
/// considered to be more valuable than a TTEntry t2 if t1 is from the
/// current search and t2 is from a previous search, or if the depth of t1
/// is bigger than the depth of t2. A TTEntry of type VALUE_TYPE_EVAL
/// never replaces another entry for the same position.
void TranspositionTable::store(const Key posKey, Value v, ValueType t, Depth d, Move m) {
int c1, c2, c3;
TTEntry *tte, *replace; TTEntry *tte, *replace;
uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key inside the cluster uint32_t posKey32 = posKey >> 32; // Use the high 32 bits as key
tte = replace = first_entry(posKey); tte = replace = first_entry(posKey);
for (int i = 0; i < ClusterSize; i++, tte++) for (int i = 0; i < ClusterSize; i++, tte++)
{ {
if (!tte->key() || tte->key() == posKey32) // Empty or overwrite old if (!tte->key() || tte->key() == posKey32) // empty or overwrite old
{ {
// Preserve any existing ttMove // Do not overwrite when new type is VALUE_TYPE_EV_LO
if (tte->key() && t == VALUE_TYPE_EV_LO)
return;
if (m == MOVE_NONE) if (m == MOVE_NONE)
m = tte->move(); m = tte->move();
tte->save(posKey32, v, t, d, m, generation, statV, kingD); *tte = TTEntry(posKey32, v, t, d, m, generation);
return; return;
} }
else if (i == 0) // replace would be a no-op in this common case
continue;
// Implement replace strategy int c1 = (replace->generation() == generation ? 2 : 0);
c1 = (replace->generation() == generation ? 2 : 0); int c2 = (tte->generation() == generation ? -2 : 0);
c2 = (tte->generation() == generation || tte->type() == VALUE_TYPE_EXACT ? -2 : 0); int c3 = (tte->depth() < replace->depth() ? 1 : 0);
c3 = (tte->depth() < replace->depth() ? 1 : 0);
if (c1 + c2 + c3 > 0) if (c1 + c2 + c3 > 0)
replace = tte; replace = tte;
} }
replace->save(posKey32, v, t, d, m, generation, statV, kingD); *replace = TTEntry(posKey32, v, t, d, m, generation);
writes++;
} }
/// TranspositionTable::probe() looks up the current position in the /// TranspositionTable::retrieve looks up the current position in the
/// transposition table. Returns a pointer to the TTEntry or NULL if /// transposition table. Returns a pointer to the TTEntry or NULL
/// position is not found. /// if position is not found.
TTEntry* TranspositionTable::probe(const Key posKey) const { TTEntry* TranspositionTable::retrieve(const Key posKey) const {
uint32_t posKey32 = posKey >> 32; uint32_t posKey32 = posKey >> 32;
TTEntry* tte = first_entry(posKey); TTEntry* tte = first_entry(posKey);
@@ -133,11 +163,96 @@ TTEntry* TranspositionTable::probe(const Key posKey) const {
} }
/// 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 /// TranspositionTable::new_search() is called at the beginning of every new
/// search. It increments the "generation" variable, which is used to /// search. It increments the "generation" variable, which is used to
/// distinguish transposition table entries from previous searches from /// distinguish transposition table entries from previous searches from
/// entries from the current search. /// entries from the current search.
void TranspositionTable::new_search() { void TranspositionTable::new_search() {
generation++; 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))));
} }
+55 -104
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,157 +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/>.
*/ */
#if !defined(TT_H_INCLUDED) #if !defined(TT_H_INCLUDED)
#define TT_H_INCLUDED #define TT_H_INCLUDED
#include <iostream> ////
//// Includes
////
#include "misc.h" #include "depth.h"
#include "types.h" #include "position.h"
#include "value.h"
/// The TTEntry is the class of transposition table entries ////
//// Types
////
/// The TTEntry class is the class of transposition table entries
/// ///
/// A TTEntry needs 128 bits to be stored /// A TTEntry needs 96 bits to be stored
/// ///
/// bit 0-31: key /// bit 0-31: key
/// bit 32-63: data /// bit 32-63: data
/// bit 64-79: value /// bit 64-79: value
/// bit 80-95: depth /// bit 80-95: depth
/// bit 96-111: static value
/// bit 112-127: margin of static value
/// ///
/// the 32 bits of the data field are so defined /// the 32 bits of the data field are so defined
/// ///
/// bit 0-15: move /// bit 0-16: move
/// bit 16-20: not used /// bit 17-19: not used
/// bit 21-22: value type /// bit 20-22: value type
/// bit 23-31: generation /// bit 23-31: generation
class TTEntry { class TTEntry {
public: public:
void save(uint32_t k, Value v, ValueType t, Depth d, Move m, int g, Value statV, Value statM) { TTEntry() {}
TTEntry(uint32_t k, Value v, ValueType t, Depth d, Move m, int generation)
: key_ (k), data((m & 0x1FFFF) | (t << 20) | (generation << 23)),
value_(int16_t(v)), depth_(int16_t(d)) {}
key32 = (uint32_t)k; uint32_t key() const { return key_; }
move16 = (uint16_t)m; Depth depth() const { return Depth(depth_); }
valueType = (uint8_t)t; Move move() const { return Move(data & 0x1FFFF); }
generation8 = (uint8_t)g; Value value() const { return Value(value_); }
value16 = (int16_t)v; ValueType type() const { return ValueType((data >> 20) & 7); }
depth16 = (int16_t)d; int generation() const { return (data >> 23); }
staticValue = (int16_t)statV;
staticMargin = (int16_t)statM;
}
void set_generation(int g) { generation8 = (uint8_t)g; }
uint32_t key() const { return key32; }
Depth depth() const { return (Depth)depth16; }
Move move() const { return (Move)move16; }
Value value() const { return (Value)value16; }
ValueType type() const { return (ValueType)valueType; }
int generation() const { return (int)generation8; }
Value static_value() const { return (Value)staticValue; }
Value static_value_margin() const { return (Value)staticMargin; }
private: private:
uint32_t key32; uint32_t key_;
uint16_t move16; uint32_t data;
uint8_t valueType, generation8; int16_t value_;
int16_t value16, depth16, staticValue, staticMargin; int16_t depth_;
}; };
/// This is the number of TTEntry slots for each cluster /// This is the number of TTEntry slots for each position
const int ClusterSize = 4; const int ClusterSize = 5;
/// Each group of ClusterSize number of TTEntry form a TTCluster
/// TTCluster consists of ClusterSize number of TTEntries. Size of TTCluster /// that is indexed by a single position key. Cluster is padded
/// must not be bigger than a cache line size. In case it is less, it should /// to a cache line size so to guarantee always aligned accesses.
/// be padded to guarantee always aligned accesses.
struct TTCluster { struct TTCluster {
TTEntry data[ClusterSize]; TTEntry data[ClusterSize];
char cache_line_padding[64 - sizeof(TTEntry[ClusterSize])];
}; };
/// The transposition table class. This is basically just a huge array containing /// The transposition table class. This is basically just a huge array
/// TTCluster objects, and a few methods for writing and reading entries. /// containing TTEntry objects, and a few methods for writing new entries
/// and reading new ones.
class TranspositionTable { class TranspositionTable {
TranspositionTable(const TranspositionTable&);
TranspositionTable& operator=(const TranspositionTable&);
public: public:
TranspositionTable(); TranspositionTable();
~TranspositionTable(); ~TranspositionTable();
void set_size(size_t mbSize); void set_size(size_t mbSize);
void clear(); void clear();
void store(const Key posKey, Value v, ValueType type, Depth d, Move m, Value statV, Value kingD); void store(const Key posKey, Value v, ValueType type, Depth d, Move m);
TTEntry* probe(const Key posKey) const; TTEntry* retrieve(const Key posKey) const;
void prefetch(const Key posKey) const;
void new_search(); void new_search();
TTEntry* first_entry(const Key posKey) const; void insert_pv(const Position& pos, Move pv[]);
void refresh(const TTEntry* tte) const; void extract_pv(const Position& pos, Move pv[], const int PLY_MAX);
int full() const;
private: private:
inline TTEntry* first_entry(const Key posKey) const;
// Be sure 'writes' is at least one cache line away
// from read only variables.
unsigned char pad_before[64 - sizeof(unsigned)];
unsigned writes; // heavy SMP read/write access here
unsigned char pad_after[64];
size_t size; size_t size;
TTCluster* entries; TTCluster* entries;
uint8_t generation; // Size must be not bigger then TTEntry::generation8 uint8_t generation;
}; };
extern TranspositionTable TT; extern TranspositionTable TT;
/// TranspositionTable::first_entry() returns a pointer to the first entry of
/// a cluster given a position. The lowest order bits of the key are used to
/// get the index of the cluster.
inline TTEntry* TranspositionTable::first_entry(const Key posKey) const {
return entries[((uint32_t)posKey) & (size - 1)].data;
}
/// TranspositionTable::refresh() updates the 'generation' value of the TTEntry
/// to avoid aging. Normally called after a TT hit.
inline void TranspositionTable::refresh(const TTEntry* tte) const {
const_cast<TTEntry*>(tte)->set_generation(generation);
}
/// A simple fixed size hash table used to store pawns and material
/// configurations. It is basically just an array of Entry objects.
/// Without cluster concept or overwrite policy.
template<class Entry, int HashSize>
struct SimpleHash {
typedef SimpleHash<Entry, HashSize> Base;
void init() {
if (entries)
return;
entries = new (std::nothrow) Entry[HashSize];
if (!entries)
{
std::cerr << "Failed to allocate " << HashSize * sizeof(Entry)
<< " bytes for hash table." << std::endl;
exit(EXIT_FAILURE);
}
memset(entries, 0, HashSize * sizeof(Entry));
}
virtual ~SimpleHash() { delete [] entries; }
Entry* probe(Key key) const { return entries + ((uint32_t)key & (HashSize - 1)); }
void prefetch(Key key) const { ::prefetch((char*)probe(key)); }
protected:
Entry* entries;
};
#endif // !defined(TT_H_INCLUDED) #endif // !defined(TT_H_INCLUDED)
+39 -475
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,496 +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/>.
*/ */
#if !defined(TYPES_H_INCLUDED) #if !defined(TYPES_H_INCLUDED)
#define TYPES_H_INCLUDED #define TYPES_H_INCLUDED
/// For Linux and OSX configuration is done automatically using Makefile. To get #if !defined(_MSC_VER)
/// started type 'make help'.
///
/// For Windows, part of the configuration is detected automatically, but some
/// switches need to be set manually:
///
/// -DNDEBUG | Disable debugging mode. Use always.
///
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want
/// | the executable to run on some very old machines.
///
/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
/// | only in 64-bit mode. For compiling requires hardware with
/// | popcnt support.
///
/// -DOLD_LOCKS | Under Windows are used the fast Slim Reader/Writer (SRW)
/// | Locks and Condition Variables: these are not supported by
/// | Windows XP and older, to compile for those platforms you
/// | should enable OLD_LOCKS.
#include <climits> #include <inttypes.h>
#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'
// MSVC does not support <inttypes.h>
typedef signed __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef signed __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else #else
# include <inttypes.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef __int16 int16_t;
typedef __int64 int64_t;
#endif // !defined(_MSC_VER)
// Hash keys
typedef uint64_t Key;
// Bitboard type
typedef uint64_t Bitboard;
////
//// Compiler specific defines
////
// Quiet a warning on Intel compiler
#if !defined(__SIZEOF_INT__ )
#define __SIZEOF_INT__ 0
#endif #endif
#if defined(_WIN64) // Check for 64 bits for different compilers: Intel, MSVC and gcc
# include <intrin.h> // MSVC popcnt and bsfq instrinsics #if defined(__x86_64) || defined(_M_X64) || defined(_WIN64) || (__SIZEOF_INT__ > 4)
#define IS_64BIT #define IS_64BIT
#endif
#if defined(IS_64BIT) && (defined(__GNUC__) || defined(__INTEL_COMPILER))
#define USE_BSFQ #define USE_BSFQ
#endif #endif
#if defined(USE_POPCNT) && defined(_MSC_VER) && defined(__INTEL_COMPILER) // Cache line alignment specification
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
#endif
#if defined(_MSC_VER) || defined(__INTEL_COMPILER) #if defined(_MSC_VER) || defined(__INTEL_COMPILER)
#define CACHE_LINE_ALIGNMENT __declspec(align(64)) #define CACHE_LINE_ALIGNMENT __declspec(align(64))
#else #else
#define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64))) #define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
#endif #endif
#if defined(_MSC_VER)
# define FORCE_INLINE __forceinline
#elif defined(__GNUC__)
# define FORCE_INLINE inline __attribute__((always_inline))
#else
# define FORCE_INLINE inline
#endif
#if defined(USE_POPCNT)
const bool HasPopCnt = true;
#else
const bool HasPopCnt = false;
#endif
#if defined(IS_64BIT)
const bool Is64Bit = true;
#else
const bool Is64Bit = false;
#endif
typedef uint64_t Key;
typedef uint64_t Bitboard;
const int MAX_MOVES = 256;
const int MAX_PLY = 100;
const int MAX_PLY_PLUS_2 = MAX_PLY + 2;
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1;
const Bitboard FileCBB = FileABB << 2;
const Bitboard FileDBB = FileABB << 3;
const Bitboard FileEBB = FileABB << 4;
const Bitboard FileFBB = FileABB << 5;
const Bitboard FileGBB = FileABB << 6;
const Bitboard FileHBB = FileABB << 7;
const Bitboard Rank1BB = 0xFF;
const Bitboard Rank2BB = Rank1BB << (8 * 1);
const Bitboard Rank3BB = Rank1BB << (8 * 2);
const Bitboard Rank4BB = Rank1BB << (8 * 3);
const Bitboard Rank5BB = Rank1BB << (8 * 4);
const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
/// A move needs 16 bits to be stored
///
/// bit 0- 5: destination square (from 0 to 63)
/// bit 6-11: origin square (from 0 to 63)
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
/// bit 14-15: special move flag: promotion (1), en passant (2), castle (3)
///
/// 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 {
MOVE_NONE = 0,
MOVE_NULL = 65
};
struct MoveStack {
Move move;
int score;
};
inline bool operator<(const MoveStack& f, const MoveStack& s) {
return f.score < s.score;
}
enum CastleRight {
CASTLES_NONE = 0,
WHITE_OO = 1,
BLACK_OO = 2,
WHITE_OOO = 4,
BLACK_OOO = 8,
ALL_CASTLES = 15
};
enum ScaleFactor {
SCALE_FACTOR_DRAW = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
enum ValueType {
VALUE_TYPE_NONE = 0,
VALUE_TYPE_UPPER = 1,
VALUE_TYPE_LOWER = 2,
VALUE_TYPE_EXACT = VALUE_TYPE_UPPER | VALUE_TYPE_LOWER
};
enum Value {
VALUE_ZERO = 0,
VALUE_DRAW = 0,
VALUE_KNOWN_WIN = 15000,
VALUE_MATE = 30000,
VALUE_INFINITE = 30001,
VALUE_NONE = 30002,
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY,
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
enum PieceType {
NO_PIECE_TYPE = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
};
enum Piece {
NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR
W_PAWN = 1, W_KNIGHT = 2, W_BISHOP = 3, W_ROOK = 4, W_QUEEN = 5, W_KING = 6,
B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14
};
enum Color {
WHITE, BLACK, NO_COLOR
};
enum Depth {
ONE_PLY = 2,
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_QS_RECAPTURES = -4 * ONE_PLY,
DEPTH_NONE = -127 * ONE_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,
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
};
enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8
};
/// 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 {
SCORE_ZERO = 0,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
/// 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(((s + 32768) & ~0xffff) / 0x10000); }
/// On Intel 64 bit we have a small speed regression with the standard conforming
/// version, so use a faster code in this case that, 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
#define ENABLE_SAFE_OPERATORS_ON(T) \
inline T operator+(const T d1, const T d2) { return T(int(d1) + int(d2)); } \
inline T operator-(const T d1, const T d2) { return T(int(d1) - int(d2)); } \
inline T operator*(int i, const T d) { return T(i * int(d)); } \
inline T operator*(const T d, int i) { return T(int(d) * i); } \
inline T operator-(const T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, const T d2) { d1 = d1 + d2; return d1; } \
inline T& operator-=(T& d1, const T d2) { d1 = d1 - d2; return d1; } \
inline T& operator*=(T& d, int i) { d = T(int(d) * i); return d; }
#define ENABLE_OPERATORS_ON(T) ENABLE_SAFE_OPERATORS_ON(T) \
inline T operator++(T& d, int) { d = T(int(d) + 1); return d; } \
inline T operator--(T& d, int) { d = T(int(d) - 1); return d; } \
inline T operator/(const T d, int i) { return T(int(d) / i); } \
inline T& operator/=(T& d, int i) { d = T(int(d) / i); return d; }
ENABLE_OPERATORS_ON(Value)
ENABLE_OPERATORS_ON(PieceType)
ENABLE_OPERATORS_ON(Piece)
ENABLE_OPERATORS_ON(Color)
ENABLE_OPERATORS_ON(Depth)
ENABLE_OPERATORS_ON(Square)
ENABLE_OPERATORS_ON(File)
ENABLE_OPERATORS_ON(Rank)
/// Added operators for adding 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); }
ENABLE_SAFE_OPERATORS_ON(Score)
/// 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);
}
#undef ENABLE_OPERATORS_ON
#undef ENABLE_SAFE_OPERATORS_ON
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);
extern const Value PieceValueMidgame[17];
extern const Value PieceValueEndgame[17];
extern int SquareDistance[64][64];
inline Value mate_in(int ply) {
return VALUE_MATE - ply;
}
inline Value mated_in(int ply) {
return -VALUE_MATE + ply;
}
inline Piece make_piece(Color c, PieceType pt) {
return Piece((c << 3) | pt);
}
inline PieceType type_of(Piece p) {
return PieceType(p & 7);
}
inline Color color_of(Piece p) {
return Color(p >> 3);
}
inline Color flip(Color c) {
return Color(c ^ 1);
}
inline Square make_square(File f, Rank r) {
return Square((r << 3) | f);
}
inline bool square_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 flip(Square s) {
return Square(s ^ 56);
}
inline Square mirror(Square s) {
return Square(s ^ 7);
}
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 = s1 ^ s2;
return ((s >> 3) ^ s) & 1;
}
inline int file_distance(Square s1, Square s2) {
return abs(file_of(s1) - file_of(s2));
}
inline int rank_distance(Square s1, Square s2) {
return abs(rank_of(s1) - rank_of(s2));
}
inline int square_distance(Square s1, Square s2) {
return SquareDistance[s1][s2];
}
inline char piece_type_to_char(PieceType pt) {
return " PNBRQK"[pt];
}
inline char file_to_char(File f) {
return char(f - FILE_A + int('a'));
}
inline char rank_to_char(Rank r) {
return char(r - RANK_1 + int('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 bool is_special(Move m) {
return m & (3 << 14);
}
inline bool is_promotion(Move m) {
return (m & (3 << 14)) == (1 << 14);
}
inline int is_enpassant(Move m) {
return (m & (3 << 14)) == (2 << 14);
}
inline int is_castle(Move m) {
return (m & (3 << 14)) == (3 << 14);
}
inline PieceType promotion_piece_type(Move m) {
return PieceType(((m >> 12) & 3) + 2);
}
inline Move make_move(Square from, Square to) {
return Move(to | (from << 6));
}
inline Move make_promotion(Square from, Square to, PieceType pt) {
return Move(to | (from << 6) | (1 << 14) | ((pt - 2) << 12)) ;
}
inline Move make_enpassant(Square from, Square to) {
return Move(to | (from << 6) | (2 << 14));
}
inline Move make_castle(Square from, Square to) {
return Move(to | (from << 6) | (3 << 14));
}
inline bool is_ok(Move m) {
return from_sq(m) != to_sq(m); // Catches also MOVE_NULL and MOVE_NONE
}
#include <string>
inline const std::string square_to_string(Square s) {
char ch[] = { file_to_char(file_of(s)), rank_to_char(rank_of(s)), 0 };
return ch;
}
/// Our insertion sort implementation, works with pointers and iterators and is
/// guaranteed to be stable, as is needed.
template<typename T, typename K>
void sort(K firstMove, K lastMove)
{
T value;
K cur, p, d;
if (firstMove != lastMove)
for (cur = firstMove + 1; cur != lastMove; cur++)
{
p = d = cur;
value = *p--;
if (*p < value)
{
do *d = *p;
while (--d != firstMove && *--p < value);
*d = value;
}
}
}
#endif // !defined(TYPES_H_INCLUDED) #endif // !defined(TYPES_H_INCLUDED)
+241 -178
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,246 +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 <vector>
#include "book.h"
#include "evaluate.h" #include "evaluate.h"
#include "misc.h" #include "misc.h"
#include "move.h"
#include "movegen.h"
#include "position.h" #include "position.h"
#include "san.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "uci.h"
#include "ucioption.h" #include "ucioption.h"
using namespace std; using namespace std;
////
//// 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.
// Keep track of position keys along the setup moves (from start position to the typedef istringstream UCIInputParser;
// position just before to start searching). This is needed by draw detection
// where, due to 50 moves rule, we need to check at most 100 plies back.
StateInfo StateRingBuf[102], *SetupState = StateRingBuf;
void set_option(istringstream& up); // The root position. This is set up when the user (or in practice, the GUI)
void set_position(Position& pos, istringstream& up); // sends the "position" UCI command. The root position is sent to the think()
void go(Position& pos, istringstream& up); // function when the program receives the "go" command.
void perft(Position& pos, istringstream& up); 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);
} }
/// Wait for a command from the user, parse this text string as an UCI command, ////
/// and call the appropriate functions. Also intercepts EOF from stdin to ensure //// Functions
/// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI ////
/// commands, the function also supports a few debug commands.
void uci_loop() { /// 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.
Position pos(StartFEN, false, 0); // The root position void uci_main_loop() {
string cmd, token;
while (token != "quit") RootPosition.from_fen(StartPosition);
{ string command;
if (!getline(cin, cmd)) // Block here waiting for input
cmd = "quit";
istringstream is(cmd); do {
// Wait for a command from stdin
if (!getline(cin, command))
command = "quit";
is >> skipws >> token; } while (handle_command(command));
if (token == "quit" || token == "stop")
Threads.stop_thinking();
else if (token == "ponderhit")
{
// The opponent has played the expected move. GUI sends "ponderhit" if
// we were told to ponder on the same move the opponent has played. We
// should continue searching but switching from pondering to normal search.
Search::Limits.ponder = false;
if (Search::Signals.stopOnPonderhit)
Threads.stop_thinking();
} }
else if (token == "go")
go(pos, is);
////
//// 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")
{
cout << "id name " << engine_name()
<< "\nid author Tord Romstad, Marco Costalba, Joona Kiiski\n";
print_uci_options();
cout << "uciok" << endl;
}
else if (token == "ucinewgame") else if (token == "ucinewgame")
pos.from_fen(StartFEN, false); {
push_button("New Game");
Position::init_piece_square_tables();
RootPosition.from_fen(StartPosition);
}
else if (token == "isready") else if (token == "isready")
cout << "readyok" << endl; cout << "readyok" << endl;
else if (token == "position") else if (token == "position")
set_position(pos, is); set_position(uip);
else if (token == "setoption") else if (token == "setoption")
set_option(is); set_option(uip);
else if (token == "perft")
perft(pos, is);
// 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") else if (token == "d")
pos.print(); RootPosition.print();
else if (token == "flip") else if (token == "flip")
pos.flip_me(); {
Position p(RootPosition);
RootPosition.flipped_copy(p);
}
else if (token == "eval") else if (token == "eval")
{ {
read_evaluation_uci_options(pos.side_to_move()); EvalInfo ei;
cout << trace_evaluate(pos) << endl; cout << "Incremental mg: " << mg_value(RootPosition.value())
<< "\nIncremental eg: " << eg_value(RootPosition.value())
<< "\nFull eval: " << evaluate(RootPosition, ei, 0) << endl;
} }
else if (token == "key") else if (token == "key")
cout << "key: " << hex << pos.key() cout << "key: " << hex << RootPosition.get_key()
<< "\nmaterial key: " << pos.material_key() << "\nmaterial key: " << RootPosition.get_material_key()
<< "\npawn key: " << pos.pawn_key() << endl; << "\npawn key: " << RootPosition.get_pawn_key() << endl;
else if (token == "perft")
else if (token == "uci") perft(uip);
cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << endl;
else else
cout << "Unknown command: " << cmd << endl; cout << "Unknown command: " << command << endl;
}
return true;
} }
namespace { // 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).
// set_position() is called when engine receives the "position" UCI void set_position(UCIInputParser& uip) {
// command. The function sets up the position described in the given
// fen string ("fen") or the starting position ("startpos") and then
// makes the moves given in the following move list ("moves").
void set_position(Position& pos, istringstream& is) { string token;
Move m; if (!(uip >> token)) // operator>>() skips any whitespace
string token, fen;
is >> token;
if (token == "startpos")
{
fen = StartFEN;
is >> token; // Consume "moves" token if any
}
else if (token == "fen")
while (is >> token && token != "moves")
fen += token + " ";
else
return; return;
pos.from_fen(fen, Options["UCI_Chess960"]); if (token == "startpos")
RootPosition.from_fen(StartPosition);
// Parse move list (if any) else if (token == "fen")
while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE)
{ {
pos.do_move(m, *SetupState); string fen;
while (uip >> token && token != "moves")
{
fen += token;
fen += ' ';
}
RootPosition.from_fen(fen);
}
// Increment pointer to StateRingBuf circular buffer if (uip.good())
if (++SetupState - StateRingBuf >= 102) {
SetupState = StateRingBuf; 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();
}
} }
} }
// set_option() 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 set_option(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
// Read option name (can contain spaces)
while (is >> token && token != "value")
name += string(" ", !name.empty()) + token;
// Read option value (can contain spaces)
while (is >> token)
value += string(" ", !value.empty()) + token;
if (!Options.count(name))
cout << "No such option: " << name << endl;
else if (value.empty()) // UCI buttons don't have a value
Options[name] = true;
else
Options[name] = value;
}
// go() is called when engine receives the "go" UCI command. The function sets
// the thinking time and other parameters from the input string, and then starts
// the main searching thread.
void go(Position& pos, istringstream& is) {
string token;
Search::LimitsType limits;
std::vector<Move> searchMoves;
int time[] = { 0, 0 }, inc[] = { 0, 0 };
while (is >> token)
{
if (token == "infinite")
limits.infinite = true;
else if (token == "ponder")
limits.ponder = true;
else if (token == "wtime")
is >> time[WHITE];
else if (token == "btime")
is >> time[BLACK];
else if (token == "winc")
is >> inc[WHITE];
else if (token == "binc")
is >> inc[BLACK];
else if (token == "movestogo")
is >> limits.movesToGo;
else if (token == "depth")
is >> limits.maxDepth;
else if (token == "nodes")
is >> limits.maxNodes;
else if (token == "movetime")
is >> limits.maxTime;
else if (token == "searchmoves")
while (is >> token)
searchMoves.push_back(move_from_uci(pos, token));
}
limits.time = time[pos.side_to_move()];
limits.increment = inc[pos.side_to_move()];
Threads.start_thinking(pos, limits, searchMoves, true);
}
// perft() is called when engine receives the "perft" command. The function
// calls perft() with the required search depth then prints counted leaf nodes
// and elapsed time.
void perft(Position& pos, istringstream& is) {
int depth, time;
if (!(is >> depth))
return; return;
time = system_time(); if (token == "name" && uip >> name)
{
while (uip >> token && token != "value")
name += (" " + token);
int64_t n = Search::perft(pos, depth * ONE_PLY); if (token == "value" && uip >> value)
{
while (uip >> token)
value += (" " + token);
time = system_time() - time; set_option_value(name, value);
} else
push_button(name);
}
}
// go() is called when Stockfish receives the "go" UCI command. The
// input parameter is a UCIInputParser. It is assumed that this
// parser has consumed the first token of the UCI command ("go"),
// and is ready to read the second token. The function sets the
// thinking time and other parameters from the input string, and
// calls think() (defined in search.cpp) with the appropriate
// parameters. Returns false if a quit command is received while
// thinking, returns true otherwise.
bool go(UCIInputParser& uip) {
string token;
int time[2] = {0, 0}, inc[2] = {0, 0};
int movesToGo = 0, depth = 0, nodes = 0, moveTime = 0;
bool infinite = false, ponder = false;
Move searchMoves[500];
searchMoves[0] = MOVE_NONE;
while (uip >> token)
{
if (token == "infinite")
infinite = true;
else if (token == "ponder")
ponder = true;
else if (token == "wtime")
uip >> time[0];
else if (token == "btime")
uip >> time[1];
else if (token == "winc")
uip >> inc[0];
else if (token == "binc")
uip >> inc[1];
else if (token == "movestogo")
uip >> movesToGo;
else if (token == "depth")
uip >> depth;
else if (token == "nodes")
uip >> nodes;
else if (token == "movetime")
uip >> moveTime;
else if (token == "searchmoves")
{
int numOfMoves = 0;
while (uip >> token)
searchMoves[numOfMoves++] = move_from_string(RootPosition, token);
searchMoves[numOfMoves] = MOVE_NONE;
}
}
assert(RootPosition.is_ok());
return think(RootPosition, infinite, ponder, RootPosition.side_to_move(),
time, inc, movesToGo, depth, nodes, moveTime, searchMoves);
}
void perft(UCIInputParser& uip) {
string token;
int depth, tm, n;
Position pos = RootPosition;
if (!(uip >> depth))
return;
tm = get_system_time();
n = perft(pos, depth * OnePly);
tm = get_system_time() - tm;
std::cout << "\nNodes " << n std::cout << "\nNodes " << n
<< "\nTime (ms) " << time << "\nTime (ms) " << tm
<< "\nNodes/second " << int(n / (time / 1000.0)) << std::endl; << "\nNodes/second " << (int)(n/(tm/1000.0)) << std::endl;
} }
} }
+31
View File
@@ -0,0 +1,31 @@
/*
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(UCI_H_INCLUDED)
#define UCI_H_INCLUDED
////
//// Prototypes
////
extern void uci_main_loop();
#endif // !defined(UCI_H_INCLUDED)
+310 -76
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,8 +17,17 @@
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 <map>
#include <string>
#include <sstream> #include <sstream>
#include <vector>
#include "misc.h" #include "misc.h"
#include "thread.h" #include "thread.h"
@@ -26,102 +35,327 @@
using std::string; using std::string;
OptionsMap Options; // Global object ////
//// Local definitions
////
namespace {
/// Our case insensitive less() function as required by UCI protocol ///
static bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); } /// Types
///
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { enum OptionType { SPIN, COMBO, CHECK, STRING, BUTTON };
return lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less);
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();
} }
/// OptionsMap c'tor initializes the UCI options to their hard coded default // get_option_value implements the various get_option_value_<type>
/// values and initializes the default value of "Threads" and "Min Split Depth" // functions defined later, because only the option value
/// parameters according to the number of CPU cores detected. // type changes a template seems a proper solution.
OptionsMap::OptionsMap() { template<typename T>
T get_option_value(const string& optionName) {
int cpus = std::min(cpu_count(), MAX_THREADS); T ret = T();
int msd = cpus < 8 ? 4 : 7; if (options.find(optionName) == options.end())
OptionsMap& o = *this; return ret;
o["Use Search Log"] = UCIOption(false); std::istringstream ss(options[optionName].currentValue);
o["Search Log Filename"] = UCIOption("SearchLog.txt"); ss >> ret;
o["Book File"] = UCIOption("book.bin"); return ret;
o["Best Book Move"] = UCIOption(false); }
o["Mobility (Middle Game)"] = UCIOption(100, 0, 200);
o["Mobility (Endgame)"] = UCIOption(100, 0, 200); // Specialization for std::string where instruction 'ss >> ret;'
o["Passed Pawns (Middle Game)"] = UCIOption(100, 0, 200); // would erroneusly tokenize a string with spaces.
o["Passed Pawns (Endgame)"] = UCIOption(100, 0, 200);
o["Space"] = UCIOption(100, 0, 200); template<>
o["Aggressiveness"] = UCIOption(100, 0, 200); string get_option_value<string>(const string& optionName) {
o["Cowardice"] = UCIOption(100, 0, 200);
o["Min Split Depth"] = UCIOption(msd, 4, 7); if (options.find(optionName) == options.end())
o["Max Threads per Split Point"] = UCIOption(5, 4, 8); return string();
o["Threads"] = UCIOption(cpus, 1, MAX_THREADS);
o["Use Sleeping Threads"] = UCIOption(false); return options[optionName].currentValue;
o["Hash"] = UCIOption(32, 4, 8192); }
o["Clear Hash"] = UCIOption(false, "button");
o["Ponder"] = UCIOption(true); }
o["OwnBook"] = UCIOption(true);
o["MultiPV"] = UCIOption(1, 1, 500); ////
o["Skill Level"] = UCIOption(20, 0, 20); //// Functions
o["Emergency Move Horizon"] = UCIOption(40, 0, 50); ////
o["Emergency Base Time"] = UCIOption(200, 0, 30000);
o["Emergency Move Time"] = UCIOption(70, 0, 5000); /// init_uci_options() initializes the UCI options. Currently, the only
o["Minimum Thinking Time"] = UCIOption(20, 0, 5000); /// thing this function does is to initialize the default value of the
o["UCI_Chess960"] = UCIOption(false); /// "Threads" parameter to the number of available CPU cores.
o["UCI_AnalyseMode"] = UCIOption(false);
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);
} }
/// operator<<() is used to output all the UCI options in chronological insertion /// print_uci_options() prints all the UCI options to the standard output,
/// order (the idx field) and in the format defined by the UCI protocol. /// in the format defined by the UCI protocol.
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
for (size_t idx = 0; idx < om.size(); idx++) void print_uci_options() {
for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it)
if (it->second.idx == idx) static const char optionTypeName[][16] = {
"spin", "combo", "check", "string", "button"
};
// Build up a vector out of the options map and sort it according to idx
// field, that is the chronological insertion order in options map.
std::vector<Option> vec;
for (Options::const_iterator it = options.begin(); it != options.end(); ++it)
vec.push_back(it->second);
std::sort(vec.begin(), vec.end());
for (std::vector<Option>::const_iterator it = vec.begin(); it != vec.end(); ++it)
{ {
const UCIOption& o = it->second; std::cout << "\noption name " << it->name
os << "\noption name " << it->first << " type " << o.type; << " type " << optionTypeName[it->type];
if (o.type != "button") if (it->type == BUTTON)
os << " default " << o.defaultValue; continue;
if (o.type == "spin") if (it->type == CHECK)
os << " min " << o.min << " max " << o.max; std::cout << " default " << (it->defaultValue == "1" ? "true" : "false");
else
std::cout << " default " << it->defaultValue;
break; 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;
} }
return os; std::cout << std::endl;
} }
/// UCIOption class c'tors /// get_option_value_bool() returns the current value of a UCI parameter of
/// type "check".
UCIOption::UCIOption(const char* v) : type("string"), min(0), max(0), idx(Options.size()) bool get_option_value_bool(const string& optionName) {
{ defaultValue = currentValue = v; }
return get_option_value<bool>(optionName);
}
/// get_option_value_int() returns the value of a UCI parameter as an integer.
/// Normally, this function will be used for a parameter of type "spin", but
/// it could also be used with a "combo" parameter, where all the available
/// values are integers.
int get_option_value_int(const string& optionName) {
return get_option_value<int>(optionName);
}
/// get_option_value_string() returns the current value of a UCI parameter as
/// a string. It is used with parameters of type "combo" and "string".
string get_option_value_string(const string& optionName) {
return get_option_value<string>(optionName);
}
/// set_option_value() inserts a new value for a UCI parameter. Note that
/// the function does not check that the new value is legal for the given
/// parameter: This is assumed to be the responsibility of the GUI.
void set_option_value(const string& name, const string& value) {
// UCI protocol uses "true" and "false" instead of "1" and "0", so convert
// value according to standard C++ convention before to store it.
string v(value);
if (v == "true")
v = "1";
else if (v == "false")
v = "0";
if (options.find(name) == options.end())
{
std::cout << "No such option: " << name << std::endl;
return;
}
// Normally it's up to the GUI to check for option's limits,
// but we could receive the new value directly from the user
// by teminal window. So let's check the bounds anyway.
Option& opt = options[name];
if (opt.type == CHECK && v != "0" && v != "1")
return;
else if (opt.type == SPIN)
{
int val = atoi(v.c_str());
if (val < opt.minValue || val > opt.maxValue)
return;
}
opt.currentValue = v;
}
/// push_button() is used to tell the engine that a UCI parameter of type
/// "button" has been selected:
void push_button(const string& buttonName) {
set_option_value(buttonName, "true");
}
/// button_was_pressed() tests whether a UCI parameter of type "button" has
/// been selected since the last time the function was called, in this case
/// it also resets the button.
bool button_was_pressed(const string& buttonName) {
if (!get_option_value<bool>(buttonName))
return false;
set_option_value(buttonName, "false");
return true;
}
namespace {
// Define constructors of Option class.
Option::Option() {} // To allow insertion in a std::map
Option::Option(const char* def, OptionType t)
: defaultValue(def), currentValue(def), type(t), idx(options.size()), minValue(0), maxValue(0) {}
Option::Option(bool def, OptionType t)
: defaultValue(stringify(def)), currentValue(stringify(def)), type(t), idx(options.size()), minValue(0), maxValue(0) {}
Option::Option(int def, int minv, int maxv)
: defaultValue(stringify(def)), currentValue(stringify(def)), type(SPIN), idx(options.size()), minValue(minv), maxValue(maxv) {}
UCIOption::UCIOption(bool v, string t) : type(t), min(0), max(0), idx(Options.size())
{ defaultValue = currentValue = (v ? "true" : "false"); }
UCIOption::UCIOption(int v, int minv, int maxv) : type("spin"), min(minv), max(maxv), idx(Options.size())
{ std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); }
/// UCIOption::operator=() updates currentValue. 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.
void UCIOption::operator=(const string& v) {
assert(!type.empty());
if ( !v.empty()
&& (type == "check" || type == "button") == (v == "true" || v == "false")
&& (type != "spin" || (atoi(v.c_str()) >= min && atoi(v.c_str()) <= max)))
currentValue = v;
} }
+17 -47
View File
@@ -1,7 +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-2012 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2009 Marco Costalba
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,58 +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/>.
*/ */
#if !defined(UCIOPTION_H_INCLUDED) #if !defined(UCIOPTION_H_INCLUDED)
#define UCIOPTION_H_INCLUDED #define UCIOPTION_H_INCLUDED
#include <cassert> ////
#include <cstdlib> //// Includes
#include <map> ////
#include <string> #include <string>
struct OptionsMap; ////
//// Prototypes
////
/// UCIOption class implements an option as defined by UCI protocol extern void init_uci_options();
class UCIOption { extern void print_uci_options();
public: extern bool get_option_value_bool(const std::string& optionName);
UCIOption() {} // Required by std::map::operator[] extern int get_option_value_int(const std::string& optionName);
UCIOption(const char* v); extern std::string get_option_value_string(const std::string& optionName);
UCIOption(bool v, std::string type = "check"); extern bool button_was_pressed(const std::string& buttonName);
UCIOption(int v, int min, int max); extern void set_option_value(const std::string& optionName,const std::string& newValue);
extern void push_button(const std::string& buttonName);
void operator=(const std::string& v);
void operator=(bool v) { *this = std::string(v ? "true" : "false"); }
operator int() const {
assert(type == "check" || type == "button" || type == "spin");
return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true");
}
operator std::string() const {
assert(type == "string");
return currentValue;
}
private:
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
std::string defaultValue, currentValue, type;
int min, max;
size_t idx;
};
/// 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 map with a customized c'tor
struct OptionsMap : public std::map<std::string, UCIOption, CaseInsensitiveLess> {
OptionsMap();
};
extern std::ostream& operator<<(std::ostream&, const OptionsMap&);
extern OptionsMap Options;
#endif // !defined(UCIOPTION_H_INCLUDED) #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)