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
67 changed files with 13907 additions and 9003 deletions
+39 -30
View File
@@ -2,21 +2,21 @@
--------------- ---------------
Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
not a complete chess program and requires some UCI-compatible GUI not a complete chess program, but requires some UCI compatible GUI
(e.g. XBoard with PolyGlot, eboard, Arena, Sigma Chess, Shredder, Chess (like XBoard with PolyGlot, eboard, Jos, Arena, Sigma Chess, Shredder,
Partner or Fritz) in order to be used comfortably. Read the Chess Partner, or Fritz) in order to be used comfortably. Read the
documentation for your GUI of choice for information about how to use documentation for your GUI of choice for information about how to use
Stockfish with it. Stockfish with your GUI.
This version of Stockfish supports up to 32 CPUs, but has not been This version of Stockfish supports up to 8 CPUs, but has not been
tested thoroughly with more than 4. The program tries to detect the tested thoroughly with more than 2. The program tries to detect the
number of CPUs on your computer and sets the number of search threads number of CPUs on your computer and set the number of search threads
accordingly, but please be aware that the detection is not always accordingly, but please be aware that the detection is not always
correct. It is therefore recommended to inspect the value of the correct. It is therefore recommended to inspect the value of the
"Threads" UCI parameter, and to make sure it equals the number of CPU "Threads" UCI parameter, and to make sure it equals the number of CPU
cores on your computer. If you are using more than eight threads, it is cores on your computer. If you are using more than four threads, it
recommended to raise the value of the "Min Split Depth" UCI parameter to is recommended to raise the value of "Minimum Split Depth" UCI parameter
7. to 6.
2. Files 2. Files
@@ -26,12 +26,13 @@ This distribution of Stockfish consists of the following files:
* Readme.txt, the file you are currently reading. * Readme.txt, the file you are currently reading.
* Copying.txt, a text file containing the GNU General Public License. * Copying.txt, a text file containing the GNU General Public
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 read systems. For further information about how to compile Stockfish
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.
@@ -40,33 +41,35 @@ 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. For This version of Stockfish has experimental support for PolyGlot opening
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 "Book File" PolyGlot documentation. The book file can be selected by setting the
UCI parameter. UCI parameter "Book File".
4. Compiling it yourself 4. Compiling it yourself
------------------------ ------------------------
On Unix-like systems, it should 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-bit CPUs, the hardware POPCNT For big-endian machines like Power PC you need to enable the proper
instruction, big-endian machines such as Power PC, and other platforms. flag changing from -DNBIGENDIAN to -DBIGENDIAN in the Makefile.
In general it is recommended to run 'make help' to see a list of make Stockfish has POPCNT instruction runtime detection and support. This can
targets with corresponding descriptions. When not using Makefile to give an extra speed on Core i7 or similar systems. To enable this feature
compile (for instance with Microsoft MSVC) you need to manually compile with 'make icc-profile-popcnt'
set/unset some switches in the compiler command line; 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
--------------- ---------------
Stockfish is free, and distributed under the GNU General Public License Stockfish is free, and distributed under the GNU General Public License
(GPL). Essentially, this means that you are free to do almost exactly (GPL). Essentially, this means that you are free to do almost exactly
what you want with the program, including distributing it among your what you want with the program, including distributing it among your
friends, making it available for download from your web site, selling friends, making it available for download from your web site, selling
it (either by itself or as part of some bigger software package), or it (either by itself or as part of some bigger software package), or
@@ -74,8 +77,14 @@ using it as the starting point for a software project of your own.
The only real limitation is that whenever you distribute Stockfish in The only real limitation is that whenever you distribute Stockfish in
some way, you must always include the full source code, or a pointer some way, you must always include the full source code, or a pointer
to where the source code can be found. If you make any changes to the to where the source code can be found. If you make any changes to the
source code, these changes must also be made available under the GPL. 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
+63 -43
View File
@@ -1,43 +1,63 @@
[PolyGlot] [PolyGlot]
EngineDir = . EngineDir = .
EngineCommand = ./stockfish 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
ResignScore = 600 ResignScore = 600
[Engine] [Engine]
Use Search Log = false Hash = 128
Search Log Filename = SearchLog.txt Threads = 1
Book File = book.bin OwnBook = false
Best Book Move = false Book File = book.bin
Mobility (Middle Game) = 100 Use Search Log = false
Mobility (Endgame) = 100 Mobility (Middle Game) = 100
Passed Pawns (Middle Game) = 100 Mobility (Endgame) = 100
Passed Pawns (Endgame) = 100 Pawn Structure (Middle Game) = 100
Space = 100 Pawn Structure (Endgame) = 100
Aggressiveness = 100 Passed Pawns (Middle Game) = 100
Cowardice = 100 Passed Pawns (Endgame) = 100
Min Split Depth = 4 Aggressiveness = 100
Max Threads per Split Point = 5 Cowardice = 100
Threads = 1 King Safety Curve = Quadratic
Use Sleeping Threads = true Quadratic = Linear
Hash = 128 King Safety Coefficient = 40
Ponder = true King Safety X Intercept = 0
OwnBook = false King Safety Max Slope = 30
MultiPV = 1 King Safety Max Value = 500
Skill Level = 20 Queen Contact Check Bonus = 3
Emergency Move Horizon = 40 Queen Check Bonus = 2
Emergency Base Time = 200 Rook Check Bonus = 1
Emergency Move Time = 70 Bishop Check Bonus = 1
Minimum Thinking Time = 20 Knight Check Bonus = 1
UCI_Chess960 = false Discovered Check Bonus = 3
UCI_AnalyseMode = false Mate Threat Bonus = 3
Check Extension (PV nodes) = 2
Check Extension (non-PV nodes) = 1
Single Reply Extension (PV nodes) = 2
Single Reply Extension (non-PV nodes) = 2
Mate Threat Extension (PV nodes) = 0
Mate Threat Extension (non-PV nodes) = 0
Pawn Push to 7th Extension (PV nodes) = 1
Pawn Push to 7th Extension (non-PV nodes) = 1
Passed Pawn Extension (PV nodes) = 1
Passed Pawn Extension (non-PV nodes) = 0
Pawn Endgame Extension (PV nodes) = 2
Pawn Endgame Extension (non-PV nodes) = 2
Full Depth Moves (PV nodes) = 14
Full Depth Moves (non-PV nodes) = 3
Threat Depth = 5
Futility Pruning (Main Search) = true
Futility Pruning (Quiescence Search) = true
Randomness = 0
Minimum Split Depth = 4
Maximum Number of Threads per Split Point = 5
+674
View File
@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+264 -464
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,499 +18,297 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
### ========================================================================== ### Executable name. Do not change
### Section 1. General Configuration
### ==========================================================================
### Establish the operating system name
UNAME = $(shell uname)
### Executable name
EXE = stockfish EXE = stockfish
### Installation dir definitions
PREFIX = /usr/local
# Haiku has a non-standard filesystem layout
ifeq ($(UNAME),Haiku)
PREFIX=/boot/common
endif
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 movegen.o movepick.o notation.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
# 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
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),general-32)
arch = any
os = any
bits = 32
prefetch = no
bsfq = no
popcnt = no
endif
# x86-section
ifeq ($(ARCH),x86-64)
arch = x86_64
os = any
bits = 64
prefetch = yes
bsfq = yes
popcnt = no
endif
ifeq ($(ARCH),x86-64-modern)
arch = x86_64
os = any
bits = 64
prefetch = yes
bsfq = yes
popcnt = yes
endif
ifeq ($(ARCH),x86-32)
arch = i386
os = any
bits = 32
prefetch = yes
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),x86-32-old)
arch = i386
os = any
bits = 32
prefetch = no
bsfq = no
popcnt = no
endif
# osx-section
ifeq ($(ARCH),osx-ppc-64)
arch = ppc64
os = osx
bits = 64
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),osx-ppc-32)
arch = ppc
os = osx
bits = 32
prefetch = no
bsfq = no
popcnt = no
endif
ifeq ($(ARCH),osx-x86-64)
arch = x86_64
os = osx
bits = 64
prefetch = yes
bsfq = yes
popcnt = no
endif
ifeq ($(ARCH),osx-x86-32)
arch = i386
os = osx
bits = 32
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
ifeq ($(COMP),clang)
comp=clang
CXX=clang++
profile_prepare = gcc-profile-prepare
profile_make = gcc-profile-make
profile_use = gcc-profile-use
profile_clean = gcc-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 ($(comp),clang)
CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra -Wshadow
endif
ifeq ($(os),osx)
CXXFLAGS += -arch $(arch)
endif
### 3.3 General linker settings
LDFLAGS = $(EXTRALDFLAGS)
### On mingw use Windows threads, otherwise POSIX
ifneq ($(comp),mingw)
# Haiku has pthreads in its libroot, so only link it in on other platforms
ifneq ($(UNAME),Haiku)
LDFLAGS += -lpthread
endif
endif
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
ifeq ($(comp),clang)
### -O4 requires a linker that supports LLVM's LTO
CXXFLAGS += -O3
ifeq ($(os),osx)
ifeq ($(arch),i386)
CXXFLAGS += -mdynamic-no-pic
endif
ifeq ($(arch),x86_64)
CXXFLAGS += -mdynamic-no-pic
endif
endif
endif
endif
### 3.6. Bits
ifeq ($(bits),64)
CXXFLAGS += -DIS_64BIT
endif
### 3.7 prefetch
ifeq ($(prefetch),yes)
CXXFLAGS += -msse
DEPENDFLAGS += -msse
else
CXXFLAGS += -DNO_PREFETCH
endif
### 3.8 bsfq
ifeq ($(bsfq),yes)
CXXFLAGS += -DUSE_BSFQ
endif
### 3.9 popcnt
ifeq ($(popcnt),yes)
CXXFLAGS += -msse3 -DUSE_POPCNT
endif
### 3.10 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)
ifeq ($(optimize),yes)
GCC_MAJOR := `$(CXX) -dumpversion | cut -f1 -d.`
GCC_MINOR := `$(CXX) -dumpversion | cut -f2 -d.`
ifeq (1,$(shell expr \( $(GCC_MAJOR) \> 4 \) \| \( $(GCC_MAJOR) \= 4 \& $(GCC_MINOR) \>= 5 \)))
CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS)
endif
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 "strip > Strip executable" @echo "make osx-ppc64 > PPC-Mac OS X 64 bit. Compiler = g++"
@echo "install > Install executable" @echo "make osx-x86 > x86-Mac OS X 32 bit. Compiler = g++"
@echo "clean > Clean up" @echo "make osx-x86_64 > x86-Mac OS X 64 bit. Compiler = g++"
@echo "testrun > Make sample run" @echo "make osx-icc32 > x86-Mac OS X 32 bit. Compiler = icpc"
@echo "" @echo "make osx-icc64 > x86-Mac OS X 64 bit. Compiler = icpc"
@echo "Supported archs:" @echo "make osx-icc32-profile > OSX 32 bit. Compiler = icpc + automatic pgo-build"
@echo "" @echo "make osx-icc64-profile > OSX 64 bit. Compiler = icpc + automatic pgo-build"
@echo "x86-64 > x86 64-bit" @echo "make strip > Strip executable"
@echo "x86-64-modern > x86 64-bit with runtime support for popcnt instruction" @echo "make clean > Clean up"
@echo "x86-32 > x86 32-bit excluding 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 ""
@echo "Supported comps:"
@echo ""
@echo "gcc > Gnu compiler (default)"
@echo "icc > Intel compiler"
@echo "mingw > Gnu compiler with MinGW under Windows"
@echo "clang > LLVM Clang compiler"
@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) ### Possible targets. You may add your own ones here
@echo "" gcc:
@echo "Step 1/4. Building executable for benchmark ..." $(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS)" \
all
gcc-popcnt:
$(MAKE) \
CXX='g++' \
CXXFLAGS="$(GCCFLAGS) -DUSE_POPCNT" \
all
icc:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS)" \
all
icc-profile-make:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS)" \
CXXFLAGS+='-prof-gen=srcpos -prof_dir ./profdir' \
all
icc-profile-use:
$(MAKE) \
CXX='icpc' \
CXXFLAGS="$(ICCFLAGS)" \
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
all
icc-profile:
@rm -rf profdir
@mkdir profdir
@touch *.cpp *.h @touch *.cpp *.h
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make) $(MAKE) icc-profile-make
@echo "" @echo ""
@echo "Step 2/4. Running benchmark for pgo-build ..." @echo "Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null @$(PGOBENCH) > /dev/null
@echo "Benchmark finished. Build final executable now ..."
@echo "" @echo ""
@echo "Step 3/4. Building final executable ..." @touch *.cpp *.h
@touch *.cpp $(MAKE) icc-profile-use
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(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/4. Deleting profile data ..." @echo "Running benchmark for pgo-build (popcnt disabled)..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean) @$(PGOBENCH) > /dev/null
@touch *.cpp *.h
$(MAKE) icc-profile-make-with-popcnt
@echo ""
@echo "Running benchmark for pgo-build (popcnt enabled)..."
@$(PGOBENCH) > /dev/null
@echo "Benchmarks finished. Build final executable now ..."
@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 "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 "$(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" || test "$(comp)" = "clang"
### Compilation. Do not change
$(EXE): $(OBJS) $(EXE): $(OBJS)
$(CXX) -o $@ $(OBJS) $(LDFLAGS) $(CXX) $(LDFLAGS) -o $@ $(OBJS)
gcc-profile-prepare:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) gcc-profile-clean
gcc-profile-make:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-generate' \
EXTRALDFLAGS='-lgcov' \
all
gcc-profile-use:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-use' \
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 -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, Color us); 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)
+114 -75
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,24 +17,29 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
////
//// Includes
////
#include <fstream> #include <fstream>
#include <iostream> #include <sstream>
#include <istream>
#include <vector> #include <vector>
#include "misc.h" #include "benchmark.h"
#include "position.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
#include "tt.h"
#include "ucioption.h" #include "ucioption.h"
using namespace std; using namespace std;
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",
@@ -46,97 +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(const Position& current, istream& is) { void benchmark(const string& commandLine) {
string token; istringstream csVal(commandLine);
Search::LimitsType limits; istringstream csStr(commandLine);
vector<string> fens; string ttSize, threads, fileName, limitType, timFile;
int val, secsPerPos, maxDepth, maxNodes;
// Assign default values to missing arguments csStr >> ttSize;
string ttSize = (is >> token) ? token : "128"; csVal >> val;
string threads = (is >> token) ? token : "1"; if (val < 4 || val > 1024)
string limit = (is >> token) ? token : "12"; {
string fenFile = (is >> token) ? token : "default"; cerr << "The hash table size must be between 4 and 1024" << endl;
string limitType = (is >> token) ? token : "depth"; Application::exit_with_failure();
}
csStr >> threads;
csVal >> val;
if (val < 1 || val > THREAD_MAX)
{
cerr << "The number of threads must be between 1 and " << THREAD_MAX << endl;
Application::exit_with_failure();
}
set_option_value("Hash", ttSize);
set_option_value("Threads", threads);
set_option_value("OwnBook", "false");
set_option_value("Use Search Log", "true");
set_option_value("Search Log Filename", "bench.txt");
Options["Hash"] = ttSize; csVal >> val;
Options["Threads"] = threads; csVal >> fileName;
TT.clear(); csVal >> limitType;
csVal >> timFile;
secsPerPos = maxDepth = maxNodes = 0;
if (limitType == "time") if (limitType == "time")
limits.movetime = 1000 * atoi(limit.c_str()); // movetime is in ms secsPerPos = val * 1000;
else if (limitType == "depth" || limitType == "perft")
else if (limitType == "nodes") maxDepth = val;
limits.nodes = atoi(limit.c_str());
else else
limits.depth = atoi(limit.c_str()); maxNodes = val;
if (fenFile == "default") vector<string> positions;
fens.assign(Defaults, Defaults + 16);
else if (fenFile == "current") if (fileName != "default")
fens.push_back(current.to_fen());
else
{ {
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 (fenFile.good())
{
getline(fenFile, pos);
if (!pos.empty())
positions.push_back(pos);
}
fenFile.close();
} else
for (int i = 0; i < 16; i++)
positions.push_back(string(BenchmarkPositions[i]));
while (getline(file, fen)) ofstream timingFile;
if (!fen.empty()) if (!timFile.empty())
fens.push_back(fen); {
timingFile.open(timFile.c_str(), ios::out | ios::app);
file.close(); if (!timingFile.is_open())
{
cerr << "Unable to open timing file " << timFile << endl;
Application::exit_with_failure();
}
} }
int64_t nodes = 0; vector<string>::iterator it;
Search::StateStackPtr st; int cnt = 1;
Time::point elapsed = Time::now(); int64_t totalNodes = 0;
int startTime = get_system_time();
for (size_t i = 0; i < fens.size(); i++) for (it = positions.begin(); it != positions.end(); ++it, ++cnt)
{ {
Position pos(fens[i], Options["UCI_Chess960"], Threads.main_thread()); Move moves[1] = {MOVE_NONE};
int dummy[2] = {0, 0};
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; Position pos(*it);
cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl;
if (limitType == "perft") if (limitType == "perft")
{ totalNodes += perft(pos, maxDepth * OnePly);
size_t cnt = Search::perft(pos, limits.depth * ONE_PLY); else if (!think(pos, false, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl; break;
nodes += cnt; totalNodes += nodes_searched();
}
else
{
Threads.start_searching(pos, limits, vector<Move>(), st);
Threads.wait_for_search_finished();
nodes += Search::RootPosition.nodes_searched();
}
} }
elapsed = Time::now() - elapsed + 1; // Assure positive to avoid a 'divide by zero' cnt = get_system_time() - startTime;
cerr << "==============================="
<< "\nTotal time (ms) : " << cnt
<< "\nNodes searched : " << totalNodes
<< "\nNodes/second : " << (int)(totalNodes/(cnt/1000.0)) << endl << endl;
cerr << "\n===========================" if (!timFile.empty())
<< "\nTotal time (ms) : " << elapsed {
<< "\nNodes searched : " << nodes timingFile << cnt << endl << endl;
<< "\nNodes/second : " << 1000 * nodes / elapsed << 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)
+287 -167
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,214 +17,334 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
////
//// 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 {
enum Result { enum Result {
INVALID = 0, RESULT_UNKNOWN,
UNKNOWN = 1, RESULT_INVALID,
DRAW = 2, RESULT_WIN,
WIN = 4 RESULT_LOSS,
RESULT_DRAW
}; };
inline Result& operator|=(Result& r, Result v) { return r = Result(r | v); }
struct KPKPosition { struct KPKPosition {
void from_index(int index);
int to_index() const;
bool is_legal() const;
bool is_immediate_draw() const;
bool is_immediate_win() const;
Bitboard wk_attacks() const;
Bitboard bk_attacks() const;
Bitboard pawn_attacks() const;
Result classify_leaf(int idx); Square whiteKingSquare, blackKingSquare, pawnSquare;
Result classify(int idx, Result db[]); Color sideToMove;
private:
template<Color Us> Result classify(const Result db[]) const;
template<Color Us> Bitboard k_attacks() const {
return Us == WHITE ? StepAttacksBB[W_KING][wksq] : StepAttacksBB[B_KING][bksq];
}
Bitboard p_attacks() const { return StepAttacksBB[W_PAWN][psq]; }
void decode_index(int idx);
Square wksq, bksq, psq;
Color stm;
}; };
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7
const int IndexMax = 2 * 24 * 64 * 64; // stm * 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 index(Square wksq, Square bksq, Square psq, Color stm);
} }
uint32_t Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm) { ////
//// Functions
////
int idx = index(wksq, bksq, wpsq, stm); void generate_kpk_bitbase(uint8_t bitbase[]) {
return KPKBitbase[idx / 32] & (1 << (idx & 31)); // Allocate array and initialize:
} Bitbase = new Result[IndexMax];
initialize();
// Iterate until all positions are classified:
while(next_iteration());
void Bitbases::init_kpk() { // 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;
}
Result db[IndexMax]; // Release allocated memory:
KPKPosition pos; delete [] Bitbase;
int idx, bit, repeat = 1;
// Initialize table with known win / draw positions
for (idx = 0; idx < IndexMax; idx++)
db[idx] = pos.classify_leaf(idx);
// Iterate until all positions are classified (30 cycles needed)
while (repeat)
for (repeat = idx = 0; idx < IndexMax; idx++)
if (db[idx] == UNKNOWN && (db[idx] = pos.classify(idx, db)) != UNKNOWN)
repeat = 1;
// Map 32 position results into one KPKBitbase[] entry
for (idx = 0; idx < IndexMax / 32; idx++)
for (bit = 0; bit < 32; bit++)
if (db[32 * idx + bit] == WIN)
KPKBitbase[idx] |= 1 << bit;
} }
namespace { namespace {
// A KPK bitbase index is an integer in [0, IndexMax] range void KPKPosition::from_index(int index) {
// int s;
// Information is mapped in this way sideToMove = Color(index % 2);
// blackKingSquare = Square((index / 2) % 64);
// bit 0: side to move (WHITE or BLACK) whiteKingSquare = Square((index / 128) % 64);
// bit 1- 6: black king square (from SQ_A1 to SQ_H8) s = (index / 8192) % 24;
// bit 7-12: white king square (from SQ_A1 to SQ_H8) pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1));
// 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 index(Square w, Square b, Square p, Color c) {
assert(file_of(p) <= FILE_D);
return c + (b << 1) + (w << 7) + (file_of(p) << 13) + ((rank_of(p) - 1) << 15);
} }
void KPKPosition::decode_index(int idx) {
stm = Color(idx & 1); int KPKPosition::to_index() const {
bksq = Square((idx >> 1) & 63); return compute_index(whiteKingSquare, blackKingSquare, pawnSquare,
wksq = Square((idx >> 7) & 63); sideToMove);
psq = File((idx >> 13) & 3) | Rank((idx >> 15) + 1);
} }
Result KPKPosition::classify_leaf(int idx) {
decode_index(idx); bool KPKPosition::is_legal() const {
if(whiteKingSquare == pawnSquare || whiteKingSquare == blackKingSquare ||
pawnSquare == blackKingSquare)
return false;
if(sideToMove == WHITE) {
if(bit_is_set(this->wk_attacks(), blackKingSquare))
return false;
if(bit_is_set(this->pawn_attacks(), blackKingSquare))
return false;
}
else {
if(bit_is_set(this->bk_attacks(), whiteKingSquare))
return false;
}
return true;
}
// Check if two pieces are on the same square or if a king can be captured
if ( wksq == psq || wksq == bksq || bksq == psq
|| (k_attacks<WHITE>() & bksq)
|| (stm == WHITE && (p_attacks() & bksq)))
return INVALID;
bool KPKPosition::is_immediate_draw() const {
if(sideToMove == BLACK) {
Bitboard wka = this->wk_attacks();
Bitboard bka = this->bk_attacks();
// Case 1: Stalemate
if((bka & ~(wka | this->pawn_attacks())) == EmptyBoardBB)
return true;
// Case 2: King can capture pawn
if(bit_is_set(bka, pawnSquare) && !bit_is_set(wka, pawnSquare))
return true;
}
else {
// Case 1: Stalemate
if(whiteKingSquare == SQ_A8 && pawnSquare == SQ_A7 &&
(blackKingSquare == SQ_C7 || blackKingSquare == SQ_C8))
return true;
}
return false;
}
bool KPKPosition::is_immediate_win() const {
// The position is an immediate win if it is white to move and the white // The position is an immediate win if it is white to move and the white
// pawn can be promoted without getting captured. // pawn can be promoted without getting captured:
if ( rank_of(psq) == RANK_7 return
&& stm == WHITE sideToMove == WHITE &&
&& wksq != psq + DELTA_N square_rank(pawnSquare) == RANK_7 &&
&& ( square_distance(bksq, psq + DELTA_N) > 1 (square_distance(blackKingSquare, pawnSquare+DELTA_N) > 1 ||
||(k_attacks<WHITE>() & (psq + DELTA_N)))) bit_is_set(this->wk_attacks(), pawnSquare+DELTA_N));
return WIN;
// Check for known draw positions
//
// Case 1: Stalemate
if ( stm == BLACK
&& !(k_attacks<BLACK>() & ~(k_attacks<WHITE>() | p_attacks())))
return DRAW;
// Case 2: King can capture undefended pawn
if ( stm == BLACK
&& (k_attacks<BLACK>() & psq & ~k_attacks<WHITE>()))
return DRAW;
// Case 3: Black king in front of white pawn
if ( bksq == psq + DELTA_N
&& rank_of(psq) < RANK_7)
return DRAW;
// Case 4: White king in front of pawn and black has opposition
if ( stm == WHITE
&& wksq == psq + DELTA_N
&& bksq == wksq + DELTA_N + DELTA_N
&& rank_of(psq) < RANK_5)
return DRAW;
// Case 5: Stalemate with rook pawn
if ( bksq == SQ_A8
&& file_of(psq) == FILE_A)
return DRAW;
// Case 6: White king trapped on the rook file
if ( file_of(wksq) == FILE_A
&& file_of(psq) == FILE_A
&& rank_of(wksq) > rank_of(psq)
&& bksq == wksq + 2)
return DRAW;
return UNKNOWN;
} }
template<Color Us>
Result KPKPosition::classify(const Result db[]) const {
// White to Move: If one move leads to a position classified as RESULT_WIN, Bitboard KPKPosition::wk_attacks() const {
// the result of the current position is RESULT_WIN. If all moves lead to return StepAttackBB[WK][whiteKingSquare];
// positions classified as RESULT_DRAW, the current position is classified
// RESULT_DRAW otherwise the current position is classified as RESULT_UNKNOWN.
//
// Black to Move: If one move leads to a position classified as RESULT_DRAW,
// the result of the current position is RESULT_DRAW. If all moves lead to
// positions classified as RESULT_WIN, the position is classified RESULT_WIN.
// Otherwise, the current position is classified as RESULT_UNKNOWN.
Result r = INVALID;
Bitboard b = k_attacks<Us>();
while (b)
{
r |= Us == WHITE ? db[index(pop_lsb(&b), bksq, psq, BLACK)]
: db[index(wksq, pop_lsb(&b), psq, WHITE)];
if (Us == WHITE && (r & WIN))
return WIN;
if (Us == BLACK && (r & DRAW))
return DRAW;
}
if (Us == WHITE && rank_of(psq) < RANK_7)
{
Square s = psq + DELTA_N;
r |= db[index(wksq, bksq, s, BLACK)]; // Single push
if (rank_of(s) == RANK_3 && s != wksq && s != bksq)
r |= db[index(wksq, bksq, s + DELTA_N, BLACK)]; // Double push
if (r & WIN)
return WIN;
}
return r & UNKNOWN ? UNKNOWN : Us == WHITE ? DRAW : WIN;
} }
Result KPKPosition::classify(int idx, Result db[]) {
decode_index(idx); Bitboard KPKPosition::bk_attacks() const {
return stm == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); return StepAttackBB[BK][blackKingSquare];
}
Bitboard KPKPosition::pawn_attacks() const {
return StepAttackBB[WP][pawnSquare];
}
void initialize() {
KPKPosition p;
for(int i = 0; i < IndexMax; i++) {
p.from_index(i);
if(!p.is_legal())
Bitbase[i] = RESULT_INVALID;
else if(p.is_immediate_draw())
Bitbase[i] = RESULT_DRAW;
else if(p.is_immediate_win())
Bitbase[i] = RESULT_WIN;
else {
Bitbase[i] = RESULT_UNKNOWN;
UnknownCount++;
}
}
}
bool next_iteration() {
KPKPosition p;
int previousUnknownCount = UnknownCount;
for(int i = 0; i < IndexMax; i++)
if(Bitbase[i] == RESULT_UNKNOWN) {
p.from_index(i);
Bitbase[i] = (p.sideToMove == WHITE)? classify_wtm(p) : classify_btm(p);
if(Bitbase[i] == RESULT_WIN || Bitbase[i] == RESULT_LOSS ||
Bitbase[i] == RESULT_DRAW)
UnknownCount--;
}
return UnknownCount != previousUnknownCount;
}
Result classify_wtm(const KPKPosition &p) {
// If one move leads to a position classified as RESULT_LOSS, the result
// of the current position is RESULT_WIN. If all moves lead to positions
// classified as RESULT_DRAW, the current position is classified as
// RESULT_DRAW. Otherwise, the current position is classified as
// RESULT_UNKNOWN.
bool unknownFound = false;
Bitboard b;
Square s;
// King moves
b = p.wk_attacks();
while(b) {
s = pop_1st_bit(&b);
switch(Bitbase[compute_index(s, p.blackKingSquare, p.pawnSquare,
BLACK)]) {
case RESULT_LOSS:
return RESULT_WIN;
case RESULT_UNKNOWN:
unknownFound = true;
break;
case RESULT_DRAW: case RESULT_INVALID:
break;
default:
assert(false);
}
}
// Pawn moves
if(square_rank(p.pawnSquare) < RANK_7) {
s = p.pawnSquare + DELTA_N;
switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s,
BLACK)]) {
case RESULT_LOSS:
return RESULT_WIN;
case RESULT_UNKNOWN:
unknownFound = true;
break;
case RESULT_DRAW: case RESULT_INVALID:
break;
default:
assert(false);
}
if(square_rank(s) == RANK_3 &&
s != p.whiteKingSquare && s != p.blackKingSquare) {
s += DELTA_N;
switch(Bitbase[compute_index(p.whiteKingSquare, p.blackKingSquare, s,
BLACK)]) {
case RESULT_LOSS:
return RESULT_WIN;
case RESULT_UNKNOWN:
unknownFound = true;
break;
case RESULT_DRAW: case RESULT_INVALID:
break;
default:
assert(false);
}
}
}
return unknownFound? RESULT_UNKNOWN : RESULT_DRAW;
}
Result classify_btm(const KPKPosition &p) {
// If one move leads to a position classified as RESULT_DRAW, the result
// of the current position is RESULT_DRAW. If all moves lead to positions
// classified as RESULT_WIN, the current position is classified as
// RESULT_LOSS. Otherwise, the current position is classified as
// RESULT_UNKNOWN.
bool unknownFound = false;
Bitboard b;
Square s;
// King moves
b = p.bk_attacks();
while(b) {
s = pop_1st_bit(&b);
switch(Bitbase[compute_index(p.whiteKingSquare, s, p.pawnSquare,
WHITE)]) {
case RESULT_DRAW:
return RESULT_DRAW;
case RESULT_UNKNOWN:
unknownFound = true;
break;
case RESULT_WIN: case RESULT_INVALID:
break;
default:
assert(false);
}
}
return unknownFound? RESULT_UNKNOWN : RESULT_LOSS;
}
int compute_index(Square wksq, Square bksq, Square psq, Color stm) {
int p = int(square_file(psq)) + (int(square_rank(psq)) - 1) * 4;
int result = int(stm) + 2*int(bksq) + 128*int(wksq) + 8192*p;
assert(result >= 0 && result < IndexMax);
return result;
}
int compress_result(Result r) {
return (r == RESULT_WIN || r == RESULT_LOSS)? 1 : 0;
} }
} }
+14 -11
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,19 +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(NOTATION_H_INCLUDED)
#define NOTATION_H_INCLUDED
#include <string> #if !defined(BITBASE_H_INCLUDED)
#define BITBASE_H_INCLUDED
////
//// Includes
////
#include "types.h" #include "types.h"
class Position;
std::string score_to_uci(Value v, Value alpha = -VALUE_INFINITE, Value beta = VALUE_INFINITE); ////
Move move_from_uci(const Position& pos, std::string& str); //// Prototypes
const std::string move_to_uci(Move m, bool chess960); ////
const std::string move_to_san(Position& pos, Move m);
std::string pretty_pv(Position& pos, int depth, Value score, int64_t msecs, Move pv[]);
#endif // !defined(NOTATION_H_INCLUDED) extern void generate_kpk_bitbase(uint8_t bitbase[]);
#endif // !defined(BITBASE_H_INCLUDED)
+495 -302
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,335 +17,528 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cstring> ////
//// Includes
////
#include <iostream> #include <iostream>
#include "bitboard.h" #include "bitboard.h"
#include "bitcount.h" #include "bitcount.h"
#include "misc.h" #include "direction.h"
#include "rkiss.h"
CACHE_LINE_ALIGNMENT
Bitboard RMasks[64]; #if defined(IS_64BIT)
Bitboard RMagics[64];
Bitboard* RAttacks[64];
unsigned RShifts[64];
Bitboard BMasks[64]; const uint64_t BMult[64] = {
Bitboard BMagics[64]; 0x440049104032280ULL, 0x1021023c82008040ULL, 0x404040082000048ULL,
Bitboard* BAttacks[64]; 0x48c4440084048090ULL, 0x2801104026490000ULL, 0x4100880442040800ULL,
unsigned BShifts[64]; 0x181011002e06040ULL, 0x9101004104200e00ULL, 0x1240848848310401ULL,
0x2000142828050024ULL, 0x1004024d5000ULL, 0x102044400800200ULL,
0x8108108820112000ULL, 0xa880818210c00046ULL, 0x4008008801082000ULL,
0x60882404049400ULL, 0x104402004240810ULL, 0xa002084250200ULL,
0x100b0880801100ULL, 0x4080201220101ULL, 0x44008080a00000ULL,
0x202200842000ULL, 0x5006004882d00808ULL, 0x200045080802ULL,
0x86100020200601ULL, 0xa802080a20112c02ULL, 0x80411218080900ULL,
0x200a0880080a0ULL, 0x9a01010000104000ULL, 0x28008003100080ULL,
0x211021004480417ULL, 0x401004188220806ULL, 0x825051400c2006ULL,
0x140c0210943000ULL, 0x242800300080ULL, 0xc2208120080200ULL,
0x2430008200002200ULL, 0x1010100112008040ULL, 0x8141050100020842ULL,
0x822081014405ULL, 0x800c049e40400804ULL, 0x4a0404028a000820ULL,
0x22060201041200ULL, 0x360904200840801ULL, 0x881a08208800400ULL,
0x60202c00400420ULL, 0x1204440086061400ULL, 0x8184042804040ULL,
0x64040315300400ULL, 0xc01008801090a00ULL, 0x808010401140c00ULL,
0x4004830c2020040ULL, 0x80005002020054ULL, 0x40000c14481a0490ULL,
0x10500101042048ULL, 0x1010100200424000ULL, 0x640901901040ULL,
0xa0201014840ULL, 0x840082aa011002ULL, 0x10010840084240aULL,
0x420400810420608ULL, 0x8d40230408102100ULL, 0x4a00200612222409ULL,
0xa08520292120600ULL
};
Bitboard SquareBB[64]; const uint64_t RMult[64] = {
Bitboard FileBB[8]; 0xa8002c000108020ULL, 0x4440200140003000ULL, 0x8080200010011880ULL,
Bitboard RankBB[8]; 0x380180080141000ULL, 0x1a00060008211044ULL, 0x410001000a0c0008ULL,
Bitboard AdjacentFilesBB[8]; 0x9500060004008100ULL, 0x100024284a20700ULL, 0x802140008000ULL,
Bitboard ThisAndAdjacentFilesBB[8]; 0x80c01002a00840ULL, 0x402004282011020ULL, 0x9862000820420050ULL,
Bitboard InFrontBB[2][8]; 0x1001448011100ULL, 0x6432800200800400ULL, 0x40100010002000cULL,
Bitboard StepAttacksBB[16][64]; 0x2800d0010c080ULL, 0x90c0008000803042ULL, 0x4010004000200041ULL,
0x3010010200040ULL, 0xa40828028001000ULL, 0x123010008000430ULL,
0x24008004020080ULL, 0x60040001104802ULL, 0x582200028400d1ULL,
0x4000802080044000ULL, 0x408208200420308ULL, 0x610038080102000ULL,
0x3601000900100020ULL, 0x80080040180ULL, 0xc2020080040080ULL,
0x80084400100102ULL, 0x4022408200014401ULL, 0x40052040800082ULL,
0xb08200280804000ULL, 0x8a80a008801000ULL, 0x4000480080801000ULL,
0x911808800801401ULL, 0x822a003002001894ULL, 0x401068091400108aULL,
0x4a10a00004cULL, 0x2000800640008024ULL, 0x1486408102020020ULL,
0x100a000d50041ULL, 0x810050020b0020ULL, 0x204000800808004ULL,
0x20048100a000cULL, 0x112000831020004ULL, 0x9000040810002ULL,
0x440490200208200ULL, 0x8910401000200040ULL, 0x6404200050008480ULL,
0x4b824a2010010100ULL, 0x4080801810c0080ULL, 0x400802a0080ULL,
0x8224080110026400ULL, 0x40002c4104088200ULL, 0x1002100104a0282ULL,
0x1208400811048021ULL, 0x3201014a40d02001ULL, 0x5100019200501ULL,
0x101000208001005ULL, 0x2008450080702ULL, 0x1002080301d00cULL,
0x410201ce5c030092ULL
};
const int BShift[64] = {
58, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 59, 59,
59, 59, 57, 57, 57, 57, 59, 59, 59, 59, 57, 55, 55, 57, 59, 59,
59, 59, 57, 55, 55, 57, 59, 59, 59, 59, 57, 57, 57, 57, 59, 59,
59, 59, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 58
};
const int RShift[64] = {
52, 53, 53, 53, 53, 53, 53, 52, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 52, 53, 53, 53, 53, 53, 53, 52
};
#else // if !defined(IS_64BIT)
const uint64_t BMult[64] = {
0x54142844c6a22981ULL, 0x710358a6ea25c19eULL, 0x704f746d63a4a8dcULL,
0xbfed1a0b80f838c5ULL, 0x90561d5631e62110ULL, 0x2804260376e60944ULL,
0x84a656409aa76871ULL, 0xf0267f64c28b6197ULL, 0x70764ebb762f0585ULL,
0x92aa09e0cfe161deULL, 0x41ee1f6bb266f60eULL, 0xddcbf04f6039c444ULL,
0x5a3fab7bac0d988aULL, 0xd3727877fa4eaa03ULL, 0xd988402d868ddaaeULL,
0x812b291afa075c7cULL, 0x94faf987b685a932ULL, 0x3ed867d8470d08dbULL,
0x92517660b8901de8ULL, 0x2d97e43e058814b4ULL, 0x880a10c220b25582ULL,
0xc7c6520d1f1a0477ULL, 0xdbfc7fbcd7656aa6ULL, 0x78b1b9bfb1a2b84fULL,
0x2f20037f112a0bc1ULL, 0x657171ea2269a916ULL, 0xc08302b07142210eULL,
0x880a4403064080bULL, 0x3602420842208c00ULL, 0x852800dc7e0b6602ULL,
0x595a3fbbaa0f03b2ULL, 0x9f01411558159d5eULL, 0x2b4a4a5f88b394f2ULL,
0x4afcbffc292dd03aULL, 0x4a4094a3b3f10522ULL, 0xb06f00b491f30048ULL,
0xd5b3820280d77004ULL, 0x8b2e01e7c8e57a75ULL, 0x2d342794e886c2e6ULL,
0xc302c410cde21461ULL, 0x111f426f1379c274ULL, 0xe0569220abb31588ULL,
0x5026d3064d453324ULL, 0xe2076040c343cd8aULL, 0x93efd1e1738021eeULL,
0xb680804bed143132ULL, 0x44e361b21986944cULL, 0x44c60170ef5c598cULL,
0xf4da475c195c9c94ULL, 0xa3afbb5f72060b1dULL, 0xbc75f410e41c4ffcULL,
0xb51c099390520922ULL, 0x902c011f8f8ec368ULL, 0x950b56b3d6f5490aULL,
0x3909e0635bf202d0ULL, 0x5744f90206ec10ccULL, 0xdc59fd76317abbc1ULL,
0x881c7c67fcbfc4f6ULL, 0x47ca41e7e440d423ULL, 0xeb0c88112048d004ULL,
0x51c60e04359aef1aULL, 0x1aa1fe0e957a5554ULL, 0xdd9448db4f5e3104ULL,
0xdc01f6dca4bebbdcULL,
};
const uint64_t RMult[64] = {
0xd7445cdec88002c0ULL, 0xd0a505c1f2001722ULL, 0xe065d1c896002182ULL,
0x9a8c41e75a000892ULL, 0x8900b10c89002aa8ULL, 0x9b28d1c1d60005a2ULL,
0x15d6c88de002d9aULL, 0xb1dbfc802e8016a9ULL, 0x149a1042d9d60029ULL,
0xb9c08050599e002fULL, 0x132208c3af300403ULL, 0xc1000ce2e9c50070ULL,
0x9d9aa13c99020012ULL, 0xb6b078daf71e0046ULL, 0x9d880182fb6e002eULL,
0x52889f467e850037ULL, 0xda6dc008d19a8480ULL, 0x468286034f902420ULL,
0x7140ac09dc54c020ULL, 0xd76ffffa39548808ULL, 0xea901c4141500808ULL,
0xc91004093f953a02ULL, 0x2882afa8f6bb402ULL, 0xaebe335692442c01ULL,
0xe904a22079fb91eULL, 0x13a514851055f606ULL, 0x76c782018c8fe632ULL,
0x1dc012a9d116da06ULL, 0x3c9e0037264fffa6ULL, 0x2036002853c6e4a2ULL,
0xe3fe08500afb47d4ULL, 0xf38af25c86b025c2ULL, 0xc0800e2182cf9a40ULL,
0x72002480d1f60673ULL, 0x2500200bae6e9b53ULL, 0xc60018c1eefca252ULL,
0x600590473e3608aULL, 0x46002c4ab3fe51b2ULL, 0xa200011486bcc8d2ULL,
0xb680078095784c63ULL, 0x2742002639bf11aeULL, 0xc7d60021a5bdb142ULL,
0xc8c04016bb83d820ULL, 0xbd520028123b4842ULL, 0x9d1600344ac2a832ULL,
0x6a808005631c8a05ULL, 0x604600a148d5389aULL, 0xe2e40103d40dea65ULL,
0x945b5a0087c62a81ULL, 0x12dc200cd82d28eULL, 0x2431c600b5f9ef76ULL,
0xfb142a006a9b314aULL, 0x6870e00a1c97d62ULL, 0x2a9db2004a2689a2ULL,
0xd3594600caf5d1a2ULL, 0xee0e4900439344a7ULL, 0x89c4d266ca25007aULL,
0x3e0013a2743f97e3ULL, 0x180e31a0431378aULL, 0x3a9e465a4d42a512ULL,
0x98d0a11a0c0d9cc2ULL, 0x8e711c1aba19b01eULL, 0x8dcdc836dd201142ULL,
0x5ac08a4735370479ULL,
};
const int BShift[64] = {
26, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 25, 25, 25, 25, 27, 27, 27, 27, 25, 23, 23, 25, 27, 27,
27, 27, 25, 23, 23, 25, 27, 27, 27, 27, 25, 25, 25, 25, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 26
};
const int RShift[64] = {
20, 21, 21, 21, 21, 21, 21, 20, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 20, 21, 21, 21, 21, 21, 21, 20
};
#endif // defined(IS_64BIT)
const Bitboard SquaresByColorBB[2] = { BlackSquaresBB, WhiteSquaresBB };
const Bitboard FileBB[8] = {
FileABB, FileBBB, FileCBB, FileDBB, FileEBB, FileFBB, FileGBB, FileHBB
};
const Bitboard NeighboringFilesBB[8] = {
FileBBB, FileABB|FileCBB, FileBBB|FileDBB, FileCBB|FileEBB,
FileDBB|FileFBB, FileEBB|FileGBB, FileFBB|FileHBB, FileGBB
};
const Bitboard ThisAndNeighboringFilesBB[8] = {
FileABB|FileBBB, FileABB|FileBBB|FileCBB,
FileBBB|FileCBB|FileDBB, FileCBB|FileDBB|FileEBB,
FileDBB|FileEBB|FileFBB, FileEBB|FileFBB|FileGBB,
FileFBB|FileGBB|FileHBB, FileGBB|FileHBB
};
const Bitboard RankBB[8] = {
Rank1BB, Rank2BB, Rank3BB, Rank4BB, Rank5BB, Rank6BB, Rank7BB, Rank8BB
};
const Bitboard RelativeRankBB[2][8] = {
{ Rank1BB, Rank2BB, Rank3BB, Rank4BB, Rank5BB, Rank6BB, Rank7BB, Rank8BB },
{ Rank8BB, Rank7BB, Rank6BB, Rank5BB, Rank4BB, Rank3BB, Rank2BB, Rank1BB }
};
const Bitboard InFrontBB[2][8] = {
{ Rank2BB | Rank3BB | Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank3BB | Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank5BB | Rank6BB | Rank7BB | Rank8BB,
Rank6BB | Rank7BB | Rank8BB,
Rank7BB | Rank8BB,
Rank8BB,
EmptyBoardBB
},
{ EmptyBoardBB,
Rank1BB,
Rank2BB | Rank1BB,
Rank3BB | Rank2BB | Rank1BB,
Rank4BB | Rank3BB | Rank2BB | Rank1BB,
Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB,
Rank6BB | Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB,
Rank7BB | Rank6BB | Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB
}
};
Bitboard RMask[64];
int RAttackIndex[64];
Bitboard RAttacks[0x19000];
Bitboard BMask[64];
int BAttackIndex[64];
Bitboard BAttacks[0x1480];
Bitboard SetMaskBB[65];
Bitboard ClearMaskBB[65];
Bitboard StepAttackBB[16][64];
Bitboard RayBB[64][8];
Bitboard BetweenBB[64][64]; Bitboard BetweenBB[64][64];
Bitboard DistanceRingsBB[64][8];
Bitboard ForwardBB[2][64];
Bitboard PassedPawnMask[2][64];
Bitboard AttackSpanMask[2][64];
Bitboard PseudoAttacks[6][64];
int SquareDistance[64][64]; Bitboard PassedPawnMask[2][64];
Bitboard OutpostMask[2][64];
Bitboard BishopPseudoAttacks[64];
Bitboard RookPseudoAttacks[64];
Bitboard QueenPseudoAttacks[64];
uint8_t BitCount8Bit[256];
////
//// Local definitions
////
namespace { namespace {
// De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan void init_masks();
const uint64_t DeBruijn_64 = 0x218A392CD3D5DBFULL; void init_ray_bitboards();
const uint32_t DeBruijn_32 = 0x783A9B23; void init_attacks();
void init_between_bitboards();
CACHE_LINE_ALIGNMENT Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
int fmin, int fmax, int rmin, int rmax);
int MS1BTable[256]; Bitboard index_to_bitboard(int index, Bitboard mask);
Square BSFTable[64]; void init_sliding_attacks(Bitboard attacks[],
Bitboard RTable[0x19000]; // Storage space for rook attacks int attackIndex[], Bitboard mask[],
Bitboard BTable[0x1480]; // Storage space for bishop attacks const int shift[2], const Bitboard mult[],
uint8_t BitCount8Bit[256]; int deltas[][2]);
void init_pseudo_attacks();
typedef unsigned (Fn)(Square, Bitboard);
void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index);
FORCE_INLINE unsigned bsf_index(Bitboard b) {
if (Is64Bit)
return ((b & -b) * DeBruijn_64) >> 58;
// Use Matt Taylor's folding trick for 32 bit systems
b ^= (b - 1);
return ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26;
}
}
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
#if !defined(USE_BSFQ)
Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; }
Square pop_lsb(Bitboard* b) {
Bitboard bb = *b;
*b = bb & (bb - 1);
return BSFTable[bsf_index(bb)];
}
Square msb(Bitboard b) {
unsigned b32;
int result = 0;
if (b > 0xFFFFFFFF)
{
b >>= 32;
result = 32;
}
b32 = unsigned(b);
if (b32 > 0xFFFF)
{
b32 >>= 16;
result += 16;
}
if (b32 > 0xFF)
{
b32 >>= 8;
result += 8;
}
return (Square)(result + MS1BTable[b32]);
}
#endif // !defined(USE_BSFQ)
/// Bitboards::print() prints a bitboard in an easily readable format to the
/// standard output. This is sometimes useful for debugging.
void Bitboards::print(Bitboard b) {
sync_cout;
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
{
std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
for (File file = FILE_A; file <= FILE_H; file++)
std::cout << "| " << (b & (file | rank) ? "X " : " ");
std::cout << "|\n";
}
std::cout << "+---+---+---+---+---+---+---+---+" << sync_endl;
} }
/// Bitboards::init() initializes various bitboard arrays. It is called during ////
//// Functions
////
/// print_bitboard() prints a bitboard in an easily readable format to the
/// standard output. This is sometimes useful for debugging.
void print_bitboard(Bitboard b) {
for(Rank r = RANK_8; r >= RANK_1; r--) {
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
for(File f = FILE_A; f <= FILE_H; f++)
std::cout << "| " << (bit_is_set(b, make_square(f, r))? 'X' : ' ') << ' ';
std::cout << "|" << std::endl;
}
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
}
/// init_bitboards() initializes various bitboard arrays. It is called during
/// program initialization. /// program initialization.
void Bitboards::init() { void init_bitboards() {
int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
for (int k = 0, i = 0; i < 8; i++) int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
while (k < (2 << i)) init_masks();
MS1BTable[k++] = i; init_ray_bitboards();
init_attacks();
for (int i = 0; i < 64; i++) init_between_bitboards();
BSFTable[bsf_index(1ULL << i)] = Square(i); init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas);
init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift, BMult, bishopDeltas);
for (Bitboard b = 0; b < 256; b++) init_pseudo_attacks();
BitCount8Bit[b] = (uint8_t)popcount<Max15>(b);
for (Square s = SQ_A1; s <= SQ_H8; s++)
SquareBB[s] = 1ULL << s;
FileBB[FILE_A] = FileABB;
RankBB[RANK_1] = Rank1BB;
for (int i = 1; i < 8; i++)
{
FileBB[i] = FileBB[i - 1] << 1;
RankBB[i] = RankBB[i - 1] << 8;
}
for (File f = FILE_A; f <= FILE_H; f++)
{
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndAdjacentFilesBB[f] = FileBB[f] | AdjacentFilesBB[f];
}
for (Rank r = RANK_1; r < RANK_8; r++)
InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]);
for (Color c = WHITE; c <= BLACK; c++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)];
PassedPawnMask[c][s] = InFrontBB[c][rank_of(s)] & ThisAndAdjacentFilesBB[file_of(s)];
AttackSpanMask[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[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 (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (int d = 1; d < 8; d++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (SquareDistance[s1][s2] == d)
DistanceRingsBB[s1][d - 1] |= s2;
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
for (int k = 0; steps[pt][k]; k++)
{
Square to = s + Square(c == WHITE ? steps[pt][k] : -steps[pt][k]);
if (is_ok(to) && square_distance(s, to) < 3)
StepAttacksBB[make_piece(c, pt)][s] |= to;
}
Square RDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W };
Square BDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW };
init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index<ROOK>);
init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index<BISHOP>);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
PseudoAttacks[QUEEN][s] = PseudoAttacks[BISHOP][s] = attacks_bb<BISHOP>(s, 0);
PseudoAttacks[QUEEN][s] |= PseudoAttacks[ ROOK][s] = attacks_bb< ROOK>(s, 0);
}
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (PseudoAttacks[QUEEN][s1] & s2)
{
Square delta = (s2 - s1) / square_distance(s1, s2);
for (Square s = s1 + delta; s != s2; s += delta)
BetweenBB[s1][s2] |= s;
}
} }
/// first_1() finds the least significant nonzero bit in a nonzero bitboard.
/// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard.
#if defined(IS_64BIT) && !defined(USE_BSFQ)
CACHE_LINE_ALIGNMENT
static const int BitTable[64] = {
0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34, 20, 40, 5, 17, 26, 38, 15,
46, 29, 48, 10, 31, 35, 54, 21, 50, 41, 57, 63, 6, 12, 18, 24, 27, 33, 39,
16, 37, 45, 47, 30, 53, 49, 56, 62, 11, 23, 32, 36, 44, 52, 55, 61, 22, 43,
51, 60, 42, 59, 58
};
Square first_1(Bitboard b) {
return Square(BitTable[((b & -b) * 0x218a392cd3d5dbfULL) >> 58]);
}
Square pop_1st_bit(Bitboard* b) {
Bitboard bb = *b;
*b &= (*b - 1);
return Square(BitTable[((bb & -bb) * 0x218a392cd3d5dbfULL) >> 58]);
}
#elif !defined(USE_BSFQ)
static CACHE_LINE_ALIGNMENT
const int BitTable[64] = {
63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2,
51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57, 0, 35, 62, 31, 40, 4, 49, 5, 52,
26, 60, 6, 23, 44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38, 28,
58, 20, 37, 17, 36, 8
};
Square first_1(Bitboard b) {
b ^= (b - 1);
uint32_t fold = int(b) ^ int(b >> 32);
return Square(BitTable[(fold * 0x783a9b23) >> 26]);
}
// Use type-punning
union b_union {
Bitboard b;
struct {
#if defined (BIGENDIAN)
uint32_t h;
uint32_t l;
#else
uint32_t l;
uint32_t h;
#endif
} dw;
};
Square pop_1st_bit(Bitboard* bb) {
b_union u;
Square ret;
u.b = *bb;
if (u.dw.l)
{
ret = Square(BitTable[((u.dw.l ^ (u.dw.l - 1)) * 0x783a9b23) >> 26]);
u.dw.l &= (u.dw.l - 1);
*bb = u.b;
return ret;
}
ret = Square(BitTable[((~(u.dw.h ^ (u.dw.h - 1))) * 0x783a9b23) >> 26]);
u.dw.h &= (u.dw.h - 1);
*bb = u.b;
return ret;
}
#endif
int bitScanReverse32(uint32_t b)
{
int result = 0;
if (b > 0xFFFF) {
b >>= 16;
result += 16;
}
if (b > 0xFF) {
b >>= 8;
result += 8;
}
if (b > 0xF) {
b >>= 4;
result += 4;
}
if (b > 0x3) {
b >>= 2;
result += 2;
}
return result + (b > 0) + (b > 1);
}
namespace { namespace {
Bitboard sliding_attack(Square deltas[], Square sq, Bitboard occupied) { // All functions below are used to precompute various bitboards during
// program initialization. Some of the functions may be difficult to
// understand, but they all seem to work correctly, and it should never
// be necessary to touch any of them.
Bitboard attack = 0; void init_masks() {
SetMaskBB[SQ_NONE] = 0ULL;
ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE];
for(Square s = SQ_A1; s <= SQ_H8; s++) {
SetMaskBB[s] = (1ULL << s);
ClearMaskBB[s] = ~SetMaskBB[s];
}
for(Color c = WHITE; c <= BLACK; c++)
for(Square s = SQ_A1; s <= SQ_H8; s++) {
PassedPawnMask[c][s] =
in_front_bb(c, s) & this_and_neighboring_files_bb(s);
OutpostMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
}
for (int i = 0; i < 4; i++) for (Bitboard b = 0ULL; b < 256ULL; b++)
for (Square s = sq + deltas[i]; BitCount8Bit[b] = (uint8_t)count_1s(b);
is_ok(s) && square_distance(s, s - deltas[i]) == 1;
s += deltas[i])
{
attack |= s;
if (occupied & s)
break;
}
return attack;
} }
Bitboard pick_random(RKISS& rk, int booster) { void init_ray_bitboards() {
int d[8] = {1, -1, 16, -16, 17, -17, 15, -15};
// Values s1 and s2 are used to rotate the candidate magic of a for(int i = 0; i < 128; i = (i + 9) & ~8) {
// quantity known to be the optimal to quickly find the magics. for(int j = 0; j < 8; j++) {
int s1 = booster & 63, s2 = (booster >> 6) & 63; RayBB[(i&7)|((i>>4)<<3)][j] = EmptyBoardBB;
for(int k = i + d[j]; (k & 0x88) == 0; k += d[j])
Bitboard m = rk.rand<Bitboard>(); set_bit(&(RayBB[(i&7)|((i>>4)<<3)][j]), Square((k&7)|((k>>4)<<3)));
m = (m >> s1) | (m << (64 - s1)); }
m &= rk.rand<Bitboard>();
m = (m >> s2) | (m << (64 - s2));
return m & rk.rand<Bitboard>();
}
// init_magics() computes all rook and bishop attacks at startup. Magic
// bitboards are used to look up attacks of sliding pieces. As a reference see
// chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
// use the so called "fancy" approach.
void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) {
int MagicBoosters[][8] = { { 3191, 2184, 1310, 3618, 2091, 1308, 2452, 3996 },
{ 1059, 3608, 605, 3234, 3326, 38, 2029, 3043 } };
RKISS rk;
Bitboard occupancy[4096], reference[4096], edges, b;
int i, size, booster;
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
attacks[SQ_A1] = table;
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
// Board edges are not considered in the relevant occupancies
edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
// Given a square 's', the mask is the bitboard of sliding attacks from
// 's' computed on an empty board. The index must be big enough to contain
// all the attacks for each possible subset of the mask and so is 2 power
// the number of 1s of the mask. Hence we deduce the size of the shift to
// apply to the 64 or 32 bits word to get the index.
masks[s] = sliding_attack(deltas, s, 0) & ~edges;
shifts[s] = (Is64Bit ? 64 : 32) - popcount<Max15>(masks[s]);
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
// store the corresponding sliding attack bitboard in reference[].
b = size = 0;
do {
occupancy[size] = b;
reference[size++] = sliding_attack(deltas, s, b);
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 {
do magics[s] = pick_random(rk, booster);
while (BitCount8Bit[(magics[s] * masks[s]) >> 56] < 6);
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++)
{
Bitboard& attack = attacks[s][index(s, occupancy[i])];
if (attack && attack != reference[i])
break;
assert(reference[i] != 0);
attack = reference[i];
}
} while (i != size);
} }
} }
void init_attacks() {
int i, j, k, l;
int step[16][8] = {
{0},
{7,9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0},
{9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}, {0}, {0},
{-7,-9,0}, {17,15,10,6,-6,-10,-15,-17}, {9,7,-7,-9,0}, {8,1,-1,-8,0},
{9,7,-7,-9,8,1,-1,-8}, {9,7,-7,-9,8,1,-1,-8}
};
for(i = 0; i < 64; i++) {
for(j = 0; j <= int(BK); j++) {
StepAttackBB[j][i] = EmptyBoardBB;
for(k = 0; k < 8 && step[j][k] != 0; k++) {
l = i + step[j][k];
if(l >= 0 && l < 64 && abs((i&7) - (l&7)) < 3)
StepAttackBB[j][i] |= (1ULL << l);
}
}
}
}
Bitboard sliding_attacks(int sq, Bitboard block, int dirs, int deltas[][2],
int fmin=0, int fmax=7, int rmin=0, int rmax=7) {
Bitboard result = 0ULL;
int rk = sq / 8, fl = sq % 8, r, f, i;
for(i = 0; i < dirs; i++) {
int dx = deltas[i][0], dy = deltas[i][1];
for(f = fl+dx, r = rk+dy;
(dx==0 || (f>=fmin && f<=fmax)) && (dy==0 || (r>=rmin && r<=rmax));
f += dx, r += dy) {
result |= (1ULL << (f + r*8));
if(block & (1ULL << (f + r*8))) break;
}
}
return result;
}
void init_between_bitboards() {
SquareDelta step[8] = {
DELTA_E, DELTA_W, DELTA_N, DELTA_S, DELTA_NE, DELTA_SW, DELTA_NW, DELTA_SE
};
SignedDirection d;
for(Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for(Square s2 = SQ_A1; s2 <= SQ_H8; s2++) {
BetweenBB[s1][s2] = EmptyBoardBB;
d = signed_direction_between_squares(s1, s2);
if(d != SIGNED_DIR_NONE)
for(Square s3 = s1 + step[d]; s3 != s2; s3 += step[d])
set_bit(&(BetweenBB[s1][s2]), s3);
}
}
Bitboard index_to_bitboard(int index, Bitboard mask) {
int i, j, bits = count_1s(mask);
Bitboard result = 0ULL;
for(i = 0; i < bits; i++) {
j = pop_1st_bit(&mask);
if(index & (1 << i)) result |= (1ULL << j);
}
return result;
}
void init_sliding_attacks(Bitboard attacks[],
int attackIndex[], Bitboard mask[],
const int shift[2], const Bitboard mult[],
int deltas[][2]) {
int i, j, k, index = 0;
Bitboard b;
for(i = 0; i < 64; i++) {
attackIndex[i] = index;
mask[i] = sliding_attacks(i, 0ULL, 4, deltas, 1, 6, 1, 6);
#if defined(IS_64BIT)
j = (1 << (64 - shift[i]));
#else
j = (1 << (32 - shift[i]));
#endif
for(k = 0; k < j; k++) {
#if defined(IS_64BIT)
b = index_to_bitboard(k, mask[i]);
attacks[index + ((b * mult[i]) >> shift[i])] =
sliding_attacks(i, b, 4, deltas);
#else
b = index_to_bitboard(k, mask[i]);
attacks[index +
(unsigned(int(b) * int(mult[i]) ^
int(b >> 32) * int(mult[i] >> 32))
>> shift[i])] =
sliding_attacks(i, b, 4, deltas);
#endif
}
index += j;
}
}
void init_pseudo_attacks() {
Square s;
for(s = SQ_A1; s <= SQ_H8; s++) {
BishopPseudoAttacks[s] = bishop_attacks_bb(s, EmptyBoardBB);
RookPseudoAttacks[s] = rook_attacks_bb(s, EmptyBoardBB);
QueenPseudoAttacks[s] = queen_attacks_bb(s, EmptyBoardBB);
}
}
} }
+232 -157
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,92 +18,125 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#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"
namespace Bitboards {
void init(); ////
void print(Bitboard b); //// Constants and variables
////
} const Bitboard EmptyBoardBB = 0ULL;
namespace Bitbases { const Bitboard WhiteSquaresBB = 0x55AA55AA55AA55AAULL;
const Bitboard BlackSquaresBB = 0xAA55AA55AA55AA55ULL;
void init_kpk(); const Bitboard FileABB = 0x0101010101010101ULL;
uint32_t probe_kpk(Square wksq, Square wpsq, Square bksq, Color stm); 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;
CACHE_LINE_ALIGNMENT 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 RMasks[64]; extern Bitboard SetMaskBB[65];
extern Bitboard RMagics[64]; extern Bitboard ClearMaskBB[65];
extern Bitboard* RAttacks[64];
extern unsigned RShifts[64];
extern Bitboard BMasks[64]; extern Bitboard StepAttackBB[16][64];
extern Bitboard BMagics[64]; extern Bitboard RayBB[64][8];
extern Bitboard* BAttacks[64];
extern unsigned BShifts[64];
extern Bitboard SquareBB[64];
extern Bitboard FileBB[8];
extern Bitboard RankBB[8];
extern Bitboard AdjacentFilesBB[8];
extern Bitboard ThisAndAdjacentFilesBB[8];
extern Bitboard InFrontBB[2][8];
extern Bitboard StepAttacksBB[16][64];
extern Bitboard BetweenBB[64][64]; extern Bitboard BetweenBB[64][64];
extern Bitboard DistanceRingsBB[64][8];
extern Bitboard ForwardBB[2][64];
extern Bitboard PassedPawnMask[2][64]; extern Bitboard PassedPawnMask[2][64];
extern Bitboard AttackSpanMask[2][64]; extern Bitboard OutpostMask[2][64];
extern Bitboard PseudoAttacks[6][64];
extern const uint64_t RMult[64];
extern const int RShift[64];
extern Bitboard RMask[64];
extern int RAttackIndex[64];
extern Bitboard RAttacks[0x19000];
extern const uint64_t BMult[64];
extern const int BShift[64];
extern Bitboard BMask[64];
extern int BAttackIndex[64];
extern Bitboard BAttacks[0x1480];
extern Bitboard BishopPseudoAttacks[64];
extern Bitboard RookPseudoAttacks[64];
extern Bitboard QueenPseudoAttacks[64];
extern uint8_t BitCount8Bit[256];
/// Overloads of bitwise operators between a Bitboard and a Square for testing ////
/// whether a given bit is set in a bitboard, and for setting and clearing bits. //// Inline functions
////
inline Bitboard operator&(Bitboard b, Square s) { /// Functions for testing whether a given bit is set in a bitboard, and for
return b & SquareBB[s]; /// setting and clearing bits.
inline Bitboard bit_is_set(Bitboard b, Square s) {
return b & SetMaskBB[s];
} }
inline Bitboard& operator|=(Bitboard& b, Square s) { inline void set_bit(Bitboard *b, Square s) {
return b |= SquareBB[s]; *b |= SetMaskBB[s];
} }
inline Bitboard& operator^=(Bitboard& b, Square s) { inline void clear_bit(Bitboard *b, Square s) {
return b ^= SquareBB[s]; *b &= ClearMaskBB[s];
}
inline Bitboard operator|(Bitboard b, Square s) {
return b | SquareBB[s];
}
inline Bitboard operator^(Bitboard b, Square s) {
return b ^ SquareBB[s];
} }
/// more_than_one() returns true if in 'b' there is more than one bit set /// Functions used to update a bitboard after a move. This is faster
/// then calling a sequence of clear_bit() + set_bit()
inline bool more_than_one(Bitboard b) { inline Bitboard make_move_bb(Square from, Square to) {
return b & (b - 1); return SetMaskBB[from] | SetMaskBB[to];
} }
inline void do_move_bb(Bitboard *b, Bitboard move_bb) {
*b ^= move_bb;
}
/// rank_bb() and file_bb() take a file or a square as input and return /// rank_bb() and file_bb() gives a bitboard containing all squares on a given
/// a bitboard representing all squares on the given file or rank. /// file or rank. It is also possible to pass a square as input to these
/// functions.
inline Bitboard rank_bb(Rank r) { inline Bitboard rank_bb(Rank r) {
return RankBB[r]; return RankBB[r];
} }
inline Bitboard rank_bb(Square s) { inline Bitboard rank_bb(Square s) {
return RankBB[rank_of(s)]; return rank_bb(square_rank(s));
} }
inline Bitboard file_bb(File f) { inline Bitboard file_bb(File f) {
@@ -111,23 +144,43 @@ 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));
} }
/// adjacent_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 adjacent files. /// bitboard representing all squares on the neighboring files.
inline Bitboard adjacent_files_bb(File f) { inline Bitboard neighboring_files_bb(File f) {
return AdjacentFilesBB[f]; return NeighboringFilesBB[f];
}
inline Bitboard neighboring_files_bb(Square s) {
return neighboring_files_bb(square_file(s));
} }
/// this_and_adjacent_files_bb takes a file as input and returns a bitboard /// this_and_neighboring_files_bb takes a file or a square as input, and
/// representing all squares on the given and adjacent files. /// returns a bitboard representing all squares on the given and neighboring
/// files.
inline Bitboard this_and_adjacent_files_bb(File f) { inline Bitboard this_and_neighboring_files_bb(File f) {
return ThisAndAdjacentFilesBB[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];
} }
@@ -142,139 +195,161 @@ 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));
} }
/// between_bb returns a bitboard representing all squares between two squares. /// behind_bb() takes a color and a rank or square as input, and returns a
/// For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with the bits for /// bitboard representing all the squares on all ranks behind of the rank
/// square d5 and e6 set. If s1 and s2 are not on the same line, file or diagonal, /// (or square), from the given color's point of view.
/// 0 is returned.
inline Bitboard between_bb(Square s1, Square s2) { 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];
}
/// Functions for computing sliding attack bitboards. rook_attacks_bb(),
/// bishop_attacks_bb() and queen_attacks_bb() all take a square and a
/// bitboard of occupied squares as input, and return a bitboard representing
/// all squares attacked by a rook, bishop or queen on the given square.
#if defined(IS_64BIT)
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] + ((b * RMult[s]) >> RShift[s])];
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] + ((b * BMult[s]) >> BShift[s])];
}
#else // if !defined(IS_64BIT)
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] +
(unsigned(int(b) * int(RMult[s]) ^
int(b >> 32) * int(RMult[s] >> 32))
>> RShift[s])];
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] +
(unsigned(int(b) * int(BMult[s]) ^
int(b >> 32) * int(BMult[s] >> 32))
>> BShift[s])];
}
#endif
inline Bitboard queen_attacks_bb(Square s, Bitboard blockers) {
return rook_attacks_bb(s, blockers) | bishop_attacks_bb(s, blockers);
}
/// squares_between returns a bitboard representing all squares between
/// two squares. For instance, squares_between(SQ_C4, SQ_F7) returns a
/// bitboard with the bits for square d5 and e6 set. If s1 and s2 are not
/// on the same line, file or diagonal, EmptyBoardBB is returned.
inline Bitboard squares_between(Square s1, Square s2) {
return BetweenBB[s1][s2]; return BetweenBB[s1][s2];
} }
/// forward_bb takes a color and a square as input, and returns a bitboard /// squares_in_front_of takes a color and a square as input, and returns a
/// representing all squares along the line in front of the square, from the /// bitboard representing all squares along the line in front of the square,
/// point of view of the given color. Definition of the table is: /// from the point of view of the given color. For instance,
/// ForwardBB[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 forward_bb(Color c, Square s) { inline Bitboard squares_in_front_of(Color c, Square s) {
return ForwardBB[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_adjacent_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) & adjacent_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);
& ( SquareBB[s1] | SquareBB[s2] | SquareBB[s3]);
} }
/// same_color_squares() returns a bitboard representing all squares with /// first_1() finds the least significant nonzero bit in a nonzero bitboard.
/// the same color of the given square. /// pop_1st_bit() finds and clears the least significant nonzero bit in a
/// nonzero bitboard.
inline Bitboard same_color_squares(Square s) { #if defined(USE_BSFQ) // Assembly code by Heinz van Saanen
return Bitboard(0xAA55AA55AA55AA55ULL) & s ? 0xAA55AA55AA55AA55ULL
: ~0xAA55AA55AA55AA55ULL; inline Square first_1(Bitboard b) {
Bitboard dummy;
__asm__("bsfq %1, %0": "=r"(dummy): "rm"(b) );
return (Square)(dummy);
} }
inline Square pop_1st_bit(Bitboard* b) {
/// Functions for computing sliding attack bitboards. Function attacks_bb() takes const Square s = first_1(*b);
/// a square and a bitboard of occupied squares as input, and returns a bitboard *b &= ~(1ULL<<s);
/// representing all squares attacked by Pt (bishop or rook) on the given square.
template<PieceType Pt>
FORCE_INLINE unsigned magic_index(Square s, Bitboard occ) {
Bitboard* const Masks = Pt == ROOK ? RMasks : BMasks;
Bitboard* const Magics = Pt == ROOK ? RMagics : BMagics;
unsigned* const Shifts = Pt == ROOK ? RShifts : BShifts;
if (Is64Bit)
return unsigned(((occ & Masks[s]) * Magics[s]) >> Shifts[s]);
unsigned lo = unsigned(occ) & unsigned(Masks[s]);
unsigned hi = unsigned(occ >> 32) & unsigned(Masks[s] >> 32);
return (lo * unsigned(Magics[s]) ^ hi * unsigned(Magics[s] >> 32)) >> Shifts[s];
}
template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Bitboard occ) {
return (Pt == ROOK ? RAttacks : BAttacks)[s][magic_index<Pt>(s, occ)];
}
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
#if defined(USE_BSFQ)
# if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
FORCE_INLINE Square lsb(Bitboard b) {
unsigned long index;
_BitScanForward64(&index, b);
return (Square) index;
}
FORCE_INLINE Square msb(Bitboard b) {
unsigned long index;
_BitScanReverse64(&index, b);
return (Square) index;
}
# else
FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen
Bitboard index;
__asm__("bsfq %1, %0": "=r"(index): "rm"(b) );
return (Square) index;
}
FORCE_INLINE Square msb(Bitboard b) {
Bitboard index;
__asm__("bsrq %1, %0": "=r"(index): "rm"(b) );
return (Square) index;
}
# endif
FORCE_INLINE Square pop_lsb(Bitboard* b) {
const Square s = lsb(*b);
*b &= ~(1ULL << s);
return s; return s;
} }
#else // if !defined(USE_BSFQ) #else // if !defined(USE_BSFQ)
extern Square msb(Bitboard b); extern Square first_1(Bitboard b);
extern Square lsb(Bitboard b); extern Square pop_1st_bit(Bitboard* b);
extern Square pop_lsb(Bitboard* b);
#endif #endif
////
//// Prototypes
////
extern void print_bitboard(Bitboard b);
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 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)
+240 -138
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,36 +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 {
// A Polyglot book is a series of "entries" of 16 bytes. All integers are /// Book entry size in bytes
// stored in big-endian format, with highest byte first (regardless of size). const int EntrySize = 16;
// The entries are ordered according to the key in ascending order.
struct BookEntry {
uint64_t key;
uint16_t move;
uint16_t count;
uint32_t learn;
};
// Random numbers from PolyGlot, used to compute book hash keys
const Key PolyGlotRandoms[781] = { /// 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,
@@ -310,172 +320,264 @@ namespace {
0xF8D626AAAF278509ULL 0xF8D626AAAF278509ULL
}; };
// Offsets to the PolyGlotRandoms[] array of zobrist keys
const Key* ZobPiece = PolyGlotRandoms;
const Key* ZobCastle = ZobPiece + 12 * 64; // Pieces * squares
const Key* ZobEnPassant = ZobCastle + 4; // Castle flags
const Key* ZobTurn = ZobEnPassant + 8; // Number of files
// book_key() returns the PolyGlot hash key of the given position /// Indices to the Random64[] array
uint64_t book_key(const Position& pos) {
uint64_t key = 0; const int RandomPiece = 0;
Bitboard b = pos.pieces(); const int RandomCastle = 768;
const int RandomEnPassant = 772;
const int RandomTurn = 780;
while (b)
{
// In PolyGlotRandoms[] pieces are stored in the following sequence:
// BP = 0, WP = 1, BN = 2, WN = 3, ... BK = 10, WK = 11
Square s = pop_lsb(&b);
Piece p = pos.piece_on(s);
int pieceOfs = 2 * (type_of(p) - 1) + (color_of(p) == WHITE);
key ^= ZobPiece[64 * pieceOfs + s];
}
b = pos.can_castle(ALL_CASTLES); /// Prototypes
while (b) uint64_t book_key(const Position& pos);
key ^= ZobCastle[pop_lsb(&b)]; 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);
}
if (pos.ep_square() != SQ_NONE)
key ^= ZobEnPassant[file_of(pos.ep_square())];
if (pos.side_to_move() == WHITE) ////
key ^= ZobTurn[0]; //// Functions
////
return key;
/// 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();
} }
} // namespace
PolyglotBook::PolyglotBook() {
for (int i = Time::now() % 10000; i > 0; i--)
RKiss.rand<unsigned>(); // Make random number generation less deterministic
}
PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
/// 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> PolyglotBook& PolyglotBook::operator>>(T& n) {
n = 0;
for (size_t i = 0; i < sizeof(T); i++)
n = T((n << 8) + ifstream::get());
return *this;
}
template<> PolyglotBook& PolyglotBook::operator>>(BookEntry& e) {
return *this >> e.key >> e.move >> e.count >> e.learn;
} }
/// open() tries to open a book file with the given name after closing any /// Book::close() closes the file only if it is open, otherwise
/// exsisting one. /// we can end up in a little mess due to how std::ifstream works.
bool PolyglotBook::open(const char* fName) { void Book::close() {
if (is_open()) // Cannot close an already closed file if (is_open())
close(); ifstream::close();
ifstream::open(fName, ifstream::in | ifstream::binary);
fileName = is_open() ? fName : "";
ifstream::clear(); // Reset any error flag to allow retry ifstream::open()
return !fileName.empty();
} }
/// probe() tries to find a book move for the given position. If no move is /// Book::file_name() returns the file name of the currently active book,
/// found returns MOVE_NONE. If pickBest is true returns always the highest /// or the empty string if no book is open.
/// rated move, otherwise randomly chooses one, based on the move score.
Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest) { const string Book::file_name() const {
if (fileName != fName && !open(fName.c_str())) 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; return MOVE_NONE;
BookEntry e; int bookMove = 0, scoresSum = 0;
uint16_t best = 0;
unsigned sum = 0;
Move move = MOVE_NONE;
uint64_t key = book_key(pos); uint64_t key = book_key(pos);
BookEntry entry;
seekg(find_first(key) * sizeof(BookEntry), ios_base::beg); // Choose a book move among the possible moves for the given position
for (int idx = find_key(key); idx < bookSize; idx++)
while (*this >> e, e.key == key && good())
{ {
best = max(best, e.count); read_entry(entry, idx);
sum += e.count; 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 // Choose book move according to its score. If a move has a very
// high score it has higher probability to be choosen than a move // high score it has more probability to be choosen then a one with
// with lower score. Note that first entry is always chosen. // lower score. Note that first entry is always chosen.
if ( (sum && RKiss.rand<unsigned>() % sum < e.count) scoresSum += score;
|| (pickBest && e.count == best)) if (int(genrand_int32() % scoresSum) < score)
move = Move(e.move); bookMove = entry.move;
} }
if (!bookMove)
if (!move)
return MOVE_NONE; return MOVE_NONE;
// A PolyGlot book move is encoded as follows: MoveStack mlist[256];
// MoveStack* last = generate_moves(pos, mlist);
// bit 0- 5: destination square (from 0 to 63) for (MoveStack* cur = mlist; cur != last; cur++)
// bit 6-11: origin square (from 0 to 63) if ((int(cur->move) & 07777) == bookMove)
// bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4) return cur->move;
//
// 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<LEGAL> ml(pos); !ml.end(); ++ml)
if (move == (ml.move() & 0x3FFF))
return ml.move();
return MOVE_NONE; return MOVE_NONE;
} }
/// find_first() takes a book key as input, and does a binary search through /// Book::find_key() takes a book key as input, and does a binary search
/// the book file for the given key. Returns the index of the leftmost book /// through the book file for the given key. The index to the first book
/// entry with the same key as the input. /// entry with the same key as the input is returned. When the key is not
/// found in the book file, bookSize is returned.
size_t PolyglotBook::find_first(uint64_t key) { int Book::find_key(uint64_t key) {
seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size int left, right, mid;
BookEntry entry;
size_t low = 0, mid, high = (size_t)tellg() / sizeof(BookEntry) - 1; // Binary search (finds the leftmost entry)
BookEntry e; left = 0;
right = bookSize - 1;
assert(low <= high); assert(left <= right);
while (low < high && good()) while (left < right)
{ {
mid = (low + high) / 2; mid = (left + right) / 2;
assert(mid >= low && mid < high); assert(mid >= left && mid < right);
seekg(mid * sizeof(BookEntry), ios_base::beg); read_entry(entry, mid);
*this >> e; if (key <= entry.key)
right = mid;
if (key <= e.key)
high = mid;
else else
low = mid + 1; left = mid + 1;
} }
assert(low == high); assert(left == right);
return low; read_entry(entry, left);
return (entry.key == key)? left : bookSize;
}
/// Book::read_entry() takes a BookEntry reference and an integer index as
/// input, and looks up the opening book entry at the given index in the book
/// file. The book entry is copied to the first input parameter.
void Book::read_entry(BookEntry& entry, int idx) {
assert(idx >= 0 && idx < bookSize);
assert(is_open());
seekg(idx * EntrySize, ios_base::beg);
*this >> entry;
if (!good())
{
cerr << "Failed to read book entry at index " << idx << endl;
Application::exit_with_failure();
}
}
/// Book::read_integer() reads size chars from the file stream
/// and converts them in an integer number.
uint64_t Book::read_integer(int size) {
char buf[8];
read(buf, size);
// Numbers are stored on disk as a binary byte stream
uint64_t n = 0ULL;
for (int i = 0; i < size; i++)
n = (n << 8) + (unsigned char)buf[i];
return n;
}
////
//// Local definitions
////
namespace {
uint64_t book_key(const Position& pos) {
uint64_t result = 0ULL;
for (Color c = WHITE; c <= BLACK; c++)
{
Bitboard b = pos.pieces_of_color(c);
while (b)
{
Square s = pop_1st_bit(&b);
Piece p = pos.piece_on(s);
assert(piece_is_ok(p));
assert(color_of_piece(p) == c);
result ^= book_piece_key(p, s);
}
}
result ^= book_castle_key(pos);
result ^= book_ep_key(pos);
result ^= book_color_key(pos);
return result;
}
uint64_t book_piece_key(Piece p, Square s) {
/// Convert pieces to the range 0..11
static const int PieceTo12[] = { 0, 0, 2, 4, 6, 8, 10, 0, 0, 1, 3, 5, 7, 9, 11 };
return Random64[RandomPiece + (PieceTo12[int(p)]^1) * 64 + int(s)];
}
uint64_t book_castle_key(const Position& pos) {
uint64_t result = 0ULL;
if (pos.can_castle_kingside(WHITE))
result ^= Random64[RandomCastle+0];
if (pos.can_castle_queenside(WHITE))
result ^= Random64[RandomCastle+1];
if (pos.can_castle_kingside(BLACK))
result ^= Random64[RandomCastle+2];
if (pos.can_castle_queenside(BLACK))
result ^= Random64[RandomCastle+3];
return result;
}
uint64_t book_ep_key(const Position& pos) {
return (pos.ep_square() == SQ_NONE ? 0ULL : Random64[RandomEnPassant + square_file(pos.ep_square())]);
}
uint64_t book_color_key(const Position& pos) {
return (pos.side_to_move() == WHITE ? Random64[RandomTurn] : 0ULL);
}
} }
+53 -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,29 +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"
class PolyglotBook : private std::ifstream {
public:
PolyglotBook();
~PolyglotBook();
Move probe(const Position& pos, const std::string& fName, bool pickBest);
private: ////
template<typename T> PolyglotBook& operator>>(T& n); //// Types
////
bool open(const char* fName); struct BookEntry {
size_t find_first(uint64_t key); uint64_t key;
uint16_t move;
RKISS RKiss; uint16_t count;
std::string fileName; uint16_t n;
uint16_t sum;
}; };
class Book : private std::ifstream {
public:
~Book();
void open(const std::string& fName);
void close();
const std::string file_name() const;
Move get_move(const Position& pos);
private:
Book& operator>>(uint64_t& n) { n = read_integer(8); return *this; }
Book& operator>>(uint16_t& n) { n = (uint16_t)read_integer(2); return *this; }
void operator>>(BookEntry& e) { *this >> e.key >> e.move >> e.count >> e.n >> e.sum; }
uint64_t read_integer(int size);
void read_entry(BookEntry& e, int n);
int find_key(uint64_t key);
std::string fileName;
int bookSize;
};
////
//// Global variables
////
extern Book OpeningBook;
#endif // !defined(BOOK_H_INCLUDED) #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)
+361 -343
View File
File diff suppressed because it is too large Load Diff
+62 -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,104 +17,90 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#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
KBNK, // KBN vs K
KPK, // KP vs K
KRKP, // KR vs KP
KRKB, // KR vs KB
KRKN, // KR vs KN
KQKR, // KQ vs KR
KBBKN, // KBB vs KN
KNNK, // KNN vs K
KmmKm, // K and two minors vs K and one or two minors
KXK, // Generic "mate lone king" eval // Scaling functions
KBNK, // KBN vs K KBPsK, // KB+pawns vs K
KPK, // KP vs K KQKRPs, // KQ vs KR+pawns
KRKP, // KR vs KP KRPKR, // KRP vs KR
KRKB, // KR vs KB KRPPKRP, // KRPP vs KRP
KRKN, // KR vs KN KPsK, // King and pawns vs king
KQKR, // KQ vs KR KBPKB, // KBP vs KB
KBBKN, // KBB vs KN KBPPKB, // KBPP vs KB
KNNK, // KNN vs K KBPKN, // KBP vs KN
KmmKm, // K and two minors vs K and one or two minors KNPK, // KNP vs K
KPKP // KP vs KP
// Scaling functions
SCALE_FUNS,
KBPsK, // KB+pawns vs K
KQKRPs, // KQ vs KR+pawns
KRPKR, // KRP vs KR
KRPPKRP, // KRPP vs KRP
KPsK, // King and pawns vs king
KBPKB, // KBP vs KB
KBPPKB, // KBPP vs KB
KBPKN, // KBP vs KN
KNPK, // KNP vs K
KPKP // KP vs KP
}; };
/// Template abstract base class for all special endgame functions
/// Endgame functions can be of two types according if return a Value or a
/// ScaleFactor. Type eg_fun<int>::type equals to either ScaleFactor or Value
/// depending if the template parameter is 0 or 1.
template<int> struct eg_fun { typedef Value type; };
template<> struct eg_fun<1> { typedef ScaleFactor type; };
/// 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_fun<(E > SCALE_FUNS)>::type>
struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : strongerSide(c), weakerSide(~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() that is virtual.
class Endgames { /// Templates subclass for various concrete endgames
typedef std::map<Key, EndgameBase<eg_fun<0>::type>*> M1; template<EndgameType>
typedef std::map<Key, EndgameBase<eg_fun<1>::type>*> M2; struct EvaluationFunction : public EndgameEvaluationFunctionBase {
typedef EndgameEvaluationFunctionBase Base;
M1 m1; explicit EvaluationFunction(Color c): EndgameEvaluationFunctionBase(c) {}
M2 m2; Value apply(const Position&);
M1& map(M1::mapped_type) { return m1; }
M2& map(M2::mapped_type) { return m2; }
template<EndgameType E> void add(const std::string& code);
public:
Endgames();
~Endgames();
template<typename T> T probe(Key key, T& eg)
{ return eg = map(eg).count(key) ? map(eg)[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)
+833 -796
View File
File diff suppressed because it is too large Load Diff
+84 -8
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,21 +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;
namespace Eval { struct EvalInfo {
extern Color RootColor; // Middle game and endgame evaluations
Score value;
extern void init(); // Pointers to material and pawn hash table entries
extern Value evaluate(const Position& pos, Value& margin); MaterialInfo* mi;
extern std::string trace(const Position& pos); 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];
}
+29 -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,13 +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 <algorithm> ////
#include <cstring> //// Includes
////
#include "types.h" #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
@@ -35,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)
+103
View File
@@ -0,0 +1,103 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(LOCK_H_INCLUDED)
#define LOCK_H_INCLUDED
// x86 assembly language locks or OS spin locks may perform faster than
// mutex locks on some platforms. On my machine, mutexes seem to be the
// best.
//#define ASM_LOCK
//#define OS_SPIN_LOCK
#if defined(ASM_LOCK)
typedef volatile int Lock;
static inline void LockX86(Lock *lock) {
int dummy;
asm __volatile__("1: movl $1, %0" "\n\t"
" xchgl (%1), %0" "\n\t" " testl %0, %0" "\n\t"
" jz 3f" "\n\t" "2: pause" "\n\t"
" movl (%1), %0" "\n\t" " testl %0, %0" "\n\t"
" jnz 2b" "\n\t" " jmp 1b" "\n\t" "3:"
"\n\t":"=&q"(dummy)
:"q"(lock)
:"cc");
}
static inline void UnlockX86(Lock *lock) {
int dummy;
asm __volatile__("movl $0, (%1)":"=&q"(dummy)
:"q"(lock));
}
# define lock_init(x, y) (*(x) = 0)
# define lock_grab(x) LockX86(x)
# define lock_release(x) UnlockX86(x)
# define lock_destroy(x)
#elif defined(OS_SPIN_LOCK)
# include <libkern/OSAtomic.h>
typedef OSSpinLock Lock;
# define lock_init(x, y) (*(x) = 0)
# define lock_grab(x) OSSpinLockLock(x)
# define lock_release(x) OSSpinLockUnlock(x)
# define lock_destroy(x)
#elif !defined(_MSC_VER)
# include <pthread.h>
typedef pthread_mutex_t Lock;
# define lock_init(x, y) pthread_mutex_init(x, y)
# define lock_grab(x) pthread_mutex_lock(x)
# define lock_release(x) pthread_mutex_unlock(x)
# define lock_destroy(x) pthread_mutex_destroy(x)
#else
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
typedef CRITICAL_SECTION Lock;
# define lock_init(x, y) InitializeCriticalSection(x)
# define lock_grab(x) EnterCriticalSection(x)
# define lock_release(x) LeaveCriticalSection(x)
# define lock_destroy(x) DeleteCriticalSection(x)
#endif
#endif // !defined(LOCK_H_INCLUDED)
+60 -23
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,36 +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 "evaluate.h" #include "bitcount.h"
#include "position.h" #include "misc.h"
#include "search.h" #include "uci.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h"
int main(int argc, char* argv[]) { #ifdef USE_CALLGRIND
#include <valgrind/callgrind.h>
#endif
std::cout << engine_info() << std::endl; using namespace std;
UCI::init(Options);
Bitboards::init();
Zobrist::init();
Bitbases::init_kpk();
Search::init();
Eval::init();
Threads.init();
TT.set_size(Options["Hash"]);
std::string args; ////
//// Functions
////
for (int i = 1; i < argc; i++) int main(int argc, char *argv[]) {
args += std::string(argv[i]) + " ";
UCI::loop(args); // Disable IO buffering
cout.rdbuf()->pubsetbuf(NULL, 0);
cin.rdbuf()->pubsetbuf(NULL, 0);
Threads.exit(); // Initialization through global resources manager
Application::initialize();
#ifdef USE_CALLGRIND
CALLGRIND_START_INSTRUMENTATION;
#endif
// Process command line arguments if any
if (argc > 1)
{
if (string(argv[1]) != "bench" || argc < 4 || argc > 8)
cout << "Usage: stockfish bench <hash size> <threads> "
<< "[time = 60s] [fen positions file = default] "
<< "[time, depth, perft or node limited = time] "
<< "[timing file name = none]" << endl;
else
{
string time = argc > 4 ? argv[4] : "60";
string fen = argc > 5 ? argv[5] : "default";
string lim = argc > 6 ? argv[6] : "time";
string tim = argc > 7 ? argv[7] : "";
benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen + " " + lim + " " + tim);
}
return 0;
}
// Print copyright notice
cout << engine_name()
<< ". By Tord Romstad, Marco Costalba, Joona Kiiski." << endl;
if (CpuHasPOPCNT)
cout << "Good! CPU has hardware POPCNT. We will use it." << endl;
// Enter UCI mode
uci_main_loop();
return 0;
} }
+308 -154
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,260 +17,414 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
////
//// Includes
////
#include <cassert> #include <cassert>
#include <cstring> #include <sstream>
#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); //// Classes
return pos.non_pawn_material(Them) == VALUE_ZERO ////
&& pos.piece_count(Them, PAWN) == 0
&& pos.non_pawn_material(Us) >= RookValueMg; /// 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; }
////
//// Functions
////
/// MaterialInfoTable c'tor and d'tor, called once by each thread
MaterialInfoTable::MaterialInfoTable(unsigned int numOfEntries) {
size = numOfEntries;
entries = new MaterialInfo[size];
funcs = new EndgameFunctions();
if (!entries || !funcs)
{
cerr << "Failed to allocate " << numOfEntries * sizeof(MaterialInfo)
<< " bytes for material hash table." << endl;
Application::exit_with_failure();
} }
}
template<Color Us> bool is_KBPsKs(const Position& pos) { MaterialInfoTable::~MaterialInfoTable() {
return pos.non_pawn_material(Us) == BishopValueMg
&& pos.piece_count(Us, BISHOP) == 1
&& pos.piece_count(Us, PAWN) >= 1;
}
template<Color Us> bool is_KQKRPs(const Position& pos) { delete funcs;
const Color Them = (Us == WHITE ? BLACK : WHITE); delete [] entries;
return pos.piece_count(Us, PAWN) == 0 }
&& pos.non_pawn_material(Us) == QueenValueMg
&& pos.piece_count(Us, QUEEN) == 1
&& pos.piece_count(Them, ROOK) == 1
&& pos.piece_count(Them, PAWN) >= 1;
}
} // namespace
/// MaterialTable::probe() takes a position object as input, looks up a MaterialEntry /// MaterialInfoTable::game_phase() calculates the phase given the current
/// object, and returns a pointer to it. If the material configuration is not /// position. Because the phase is strictly a function of the material, it
/// already present in the table, it is computed and stored there, so we don't /// is stored in MaterialInfo.
/// have to recompute everything when the same material configuration occurs again.
MaterialEntry* MaterialTable::probe(const Position& pos) { Phase MaterialInfoTable::game_phase(const Position& pos) {
Key key = pos.material_key(); Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
MaterialEntry* e = entries[key];
// If e->key matches the position's material hash key, it means that we if (npm >= MidgameLimit)
return PHASE_MIDGAME;
else if (npm <= EndgameLimit)
return PHASE_ENDGAME;
return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
}
/// MaterialInfoTable::get_material_info() takes a position object as input,
/// computes or looks up a MaterialInfo object, and returns a pointer to it.
/// If the material configuration is not already present in the table, it
/// is stored there, so we don't have to recompute everything when the
/// same material configuration occurs again.
MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
Key key = pos.get_material_key();
int index = key & (size - 1);
MaterialInfo* mi = entries + index;
// If mi->key matches the position's material hash key, it means that we
// have analysed this material configuration before, and we can simply // have analysed this material configuration before, and we can simply
// return the information we found the last time instead of recomputing it. // return the information we found the last time instead of recomputing it.
if (e->key == key) if (mi->key == key)
return e; return mi;
memset(e, 0, sizeof(MaterialEntry)); // Clear the MaterialInfo object, and set its key
e->key = key; mi->clear();
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL; mi->key = key;
e->gamePhase = MaterialTable::game_phase(pos);
// Store game phase
mi->gamePhase = MaterialInfoTable::game_phase(pos);
// Let's look if we have a specialized evaluation function for this // Let's look if we have a specialized evaluation function for this
// particular material configuration. First we look for a fixed // particular material configuration. First we look for a fixed
// configuration one, then a generic one if previous search failed. // configuration one, then a generic one if previous search failed.
if (endgames.probe(key, e->evaluationFunction)) if ((mi->evaluationFunction = funcs->get<EF>(key)) != NULL)
return e; return mi;
if (is_KXK<WHITE>(pos)) else if ( pos.non_pawn_material(BLACK) == Value(0)
&& pos.piece_count(BLACK, PAWN) == 0
&& pos.non_pawn_material(WHITE) >= RookValueMidgame)
{ {
e->evaluationFunction = &EvaluateKXK[WHITE]; mi->evaluationFunction = &EvaluateKXK;
return e; return mi;
} }
else if ( pos.non_pawn_material(WHITE) == Value(0)
if (is_KXK<BLACK>(pos)) && pos.piece_count(WHITE, PAWN) == 0
&& pos.non_pawn_material(BLACK) >= RookValueMidgame)
{ {
e->evaluationFunction = &EvaluateKXK[BLACK]; mi->evaluationFunction = &EvaluateKKX;
return e; return mi;
} }
else if ( pos.pieces(PAWN) == EmptyBoardBB
if (!pos.pieces(PAWN) && !pos.pieces(ROOK) && !pos.pieces(QUEEN)) && pos.pieces(ROOK) == EmptyBoardBB
&& pos.pieces(QUEEN) == EmptyBoardBB)
{ {
// Minor piece endgame with at least one minor piece per side and // Minor piece endgame with at least one minor piece per side and
// no pawns. Note that the case KmmK is already handled by KXK. // no pawns. Note that the case KmmK is already handled by KXK.
assert((pos.pieces(WHITE, KNIGHT) | pos.pieces(WHITE, BISHOP))); assert((pos.pieces(KNIGHT, WHITE) | pos.pieces(BISHOP, WHITE)));
assert((pos.pieces(BLACK, KNIGHT) | pos.pieces(BLACK, BISHOP))); assert((pos.pieces(KNIGHT, BLACK) | pos.pieces(BISHOP, BLACK)));
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2 if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2) && pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
{ {
e->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()]; mi->evaluationFunction = &EvaluateKmmKm;
return e; return mi;
} }
} }
// OK, we didn't find any special evaluation function for the current // OK, we didn't find any special evaluation function for the current
// material configuration. Is there a suitable scaling function? // material configuration. Is there a suitable scaling function?
// //
// We face problems when there are several conflicting applicable // The code below is rather messy, and it could easily get worse later,
// scaling functions and we need to decide which one to use. // if we decide to add more special cases. We face problems when there
EndgameBase<ScaleFactor>* sf; // are several conflicting applicable scaling functions and we need to
// decide which one to use.
SF* sf;
if (endgames.probe(key, sf)) if ((sf = funcs->get<SF>(key)) != NULL)
{ {
e->scalingFunction[sf->color()] = sf; mi->scalingFunction[sf->color()] = sf;
return e; return mi;
} }
// Generic scaling functions that refer to more then one material // Generic scaling functions that refer to more then one material
// distribution. Should be probed after the specialized ones. // distribution. Should be probed after the specialized ones.
// Note that these ones don't return after setting the function. // Note that these ones don't return after setting the function.
if (is_KBPsKs<WHITE>(pos)) if ( pos.non_pawn_material(WHITE) == BishopValueMidgame
e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE]; && pos.piece_count(WHITE, BISHOP) == 1
&& pos.piece_count(WHITE, PAWN) >= 1)
mi->scalingFunction[WHITE] = &ScaleKBPsK;
if (is_KBPsKs<BLACK>(pos)) if ( pos.non_pawn_material(BLACK) == BishopValueMidgame
e->scalingFunction[BLACK] = &ScaleKBPsK[BLACK]; && pos.piece_count(BLACK, BISHOP) == 1
&& pos.piece_count(BLACK, PAWN) >= 1)
mi->scalingFunction[BLACK] = &ScaleKKBPs;
if (is_KQKRPs<WHITE>(pos)) if ( pos.piece_count(WHITE, PAWN) == 0
e->scalingFunction[WHITE] = &ScaleKQKRPs[WHITE]; && 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 (is_KQKRPs<BLACK>(pos)) else if ( pos.piece_count(BLACK, PAWN) == 0
e->scalingFunction[BLACK] = &ScaleKQKRPs[BLACK]; && 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;
Value npm_w = pos.non_pawn_material(WHITE); if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) == Value(0))
Value npm_b = pos.non_pawn_material(BLACK);
if (npm_w + npm_b == VALUE_ZERO)
{ {
if (pos.piece_count(BLACK, PAWN) == 0) if (pos.piece_count(BLACK, PAWN) == 0)
{ {
assert(pos.piece_count(WHITE, PAWN) >= 2); assert(pos.piece_count(WHITE, PAWN) >= 2);
e->scalingFunction[WHITE] = &ScaleKPsK[WHITE]; mi->scalingFunction[WHITE] = &ScaleKPsK;
} }
else if (pos.piece_count(WHITE, PAWN) == 0) else if (pos.piece_count(WHITE, PAWN) == 0)
{ {
assert(pos.piece_count(BLACK, PAWN) >= 2); assert(pos.piece_count(BLACK, PAWN) >= 2);
e->scalingFunction[BLACK] = &ScaleKPsK[BLACK]; mi->scalingFunction[BLACK] = &ScaleKKPs;
} }
else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1) else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1)
{ {
// This is a special case because we set scaling functions // This is a special case because we set scaling functions
// for both colors instead of only one. // for both colors instead of only one.
e->scalingFunction[WHITE] = &ScaleKPKP[WHITE]; mi->scalingFunction[WHITE] = &ScaleKPKPw;
e->scalingFunction[BLACK] = &ScaleKPKP[BLACK]; mi->scalingFunction[BLACK] = &ScaleKPKPb;
} }
} }
// No pawns makes it difficult to win, even with a material advantage
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMg)
{
e->factor[WHITE] = (uint8_t)
(npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]);
}
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMg)
{
e->factor[BLACK] = (uint8_t)
(npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]);
}
// Compute the space weight // Compute the space weight
if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg) 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(WHITE, BISHOP) int minorPieceCount = pos.piece_count(WHITE, KNIGHT)
+ pos.piece_count(BLACK, KNIGHT) + pos.piece_count(BLACK, BISHOP); + pos.piece_count(BLACK, KNIGHT)
+ pos.piece_count(WHITE, BISHOP)
+ pos.piece_count(BLACK, BISHOP);
e->spaceWeight = minorPieceCount * minorPieceCount; mi->spaceWeight = minorPieceCount * minorPieceCount;
} }
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // Evaluate the material balance
// for the bishop pair "extended piece", this allow us to be more flexible const int pieceCount[2][6] = { { pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT),
// in defining bishop pair bonuses. pos.piece_count(WHITE, BISHOP), pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) },
const int pieceCount[2][8] = { { pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
{ pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT), pos.piece_count(BLACK, BISHOP), pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
pos.piece_count(WHITE, BISHOP) , pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) }, Color c, them;
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT), int sign, pt1, pt2, pc;
pos.piece_count(BLACK, BISHOP) , pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } }; int v, vv, matValue = 0;
e->value = (int16_t)((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16); for (c = WHITE, sign = 1; c <= BLACK; c++, sign = -sign)
return e;
}
/// MaterialTable::imbalance() calculates imbalance comparing piece count of each
/// piece type for both colors.
template<Color Us>
int MaterialTable::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]; // No pawns makes it difficult to win, even with a material advantage
if (!pc) if ( pos.piece_count(c, PAWN) == 0
continue; && 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;
}
}
}
v = LinearCoefficients[pt1]; // 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);
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++) them = opposite_color(c);
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2] v = 0;
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];
value += pc * v; // 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;
} }
return value; mi->value = int16_t(matValue / 16);
return mi;
} }
/// MaterialTable::game_phase() calculates the phase given the current /// EndgameFunctions member definitions.
/// position. Because the phase is strictly a function of the material, it
/// is stored in MaterialEntry.
Phase MaterialTable::game_phase(const Position& pos) { EndgameFunctions::EndgameFunctions() {
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK); 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");
return npm >= MidgameLimit ? PHASE_MIDGAME add<ScalingFunction<KNPK> >("KNPK");
: npm <= EndgameLimit ? PHASE_ENDGAME add<ScalingFunction<KRPKR> >("KRPKR");
: Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit)); 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);
} }
+96 -39
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,24 +17,24 @@
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 "misc.h"
#include "position.h" #include "position.h"
#include "types.h" #include "scale.h"
const int MaterialTableSize = 8192;
/// Game phase
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
};
/// MaterialEntry is a class which contains various information about a ////
//// Types
////
/// 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,
/// a function pointer to a special endgame evaluation function (which in /// a function pointer to a special endgame evaluation function (which in
/// most cases is NULL, meaning that the standard evaluation function will /// most cases is NULL, meaning that the standard evaluation function will
@@ -44,11 +44,13 @@ enum Phase {
/// For instance, in KRB vs KR endgames, the score is scaled down by a factor /// For instance, in KRB vs KR endgames, the score is scaled down by a factor
/// of 4, which will result in scores of absolute value less than one pawn. /// of 4, which will result in scores of absolute value less than one pawn.
class MaterialEntry { class MaterialInfo {
friend struct MaterialTable; 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,64 +59,119 @@ 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 MaterialTable class represents a material hash table. The most important class MaterialInfoTable {
/// method is probe(), which returns a pointer to a MaterialEntry object.
struct MaterialTable { public:
MaterialInfoTable(unsigned numOfEntries);
~MaterialInfoTable();
MaterialInfo* get_material_info(const Position& pos);
MaterialEntry* probe(const Position& pos);
static Phase game_phase(const Position& pos); static Phase game_phase(const Position& pos);
template<Color Us> static int imbalance(const int pieceCount[][8]);
HashTable<MaterialEntry, MaterialTableSize> entries; private:
Endgames endgames; unsigned size;
MaterialInfo* entries;
EndgameFunctions* funcs;
}; };
/// MaterialEntry::scale_factor takes a position and a color as input, and ////
//// Inline functions
////
/// MaterialInfo::material_value simply returns the material balance
/// evaluation that is independent from game phase.
inline Score MaterialInfo::material_value() const {
return make_score(value, value);
}
/// MaterialInfo::clear() resets a MaterialInfo object to an empty state,
/// with all slots at their default values but the key.
inline void MaterialInfo::clear() {
value = 0;
factor[WHITE] = factor[BLACK] = uint8_t(SCALE_FACTOR_NORMAL);
evaluationFunction = NULL;
scalingFunction[WHITE] = scalingFunction[BLACK] = NULL;
spaceWeight = 0;
}
/// MaterialInfo::scale_factor takes a position and a color as input, and
/// returns a scale factor for the given color. We have to provide the /// 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
/// to be a constant: It can also be a function which should be applied to /// to be a constant: It can also be a function which should be applied to
/// the position. For instance, in KBP vs K endgames, a scaling function /// the position. For instance, in KBP vs K endgames, a scaling function
/// which checks for draws with rook pawns and wrong-colored bishops. /// which checks for draws with rook pawns and wrong-colored bishops.
inline ScaleFactor MaterialEntry::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)
return ScaleFactor(factor[c]); {
ScaleFactor sf = scalingFunction[c]->apply(pos);
ScaleFactor sf = (*scalingFunction[c])(pos); if (sf != SCALE_FACTOR_NONE)
return sf == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) : sf; return sf;
}
return ScaleFactor(factor[c]);
} }
inline Value MaterialEntry::evaluate(const Position& pos) const {
return (*evaluationFunction)(pos);
}
inline Score MaterialEntry::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 MaterialEntry::space_weight() const {
return spaceWeight; return spaceWeight;
} }
inline Phase MaterialEntry::game_phase() const { /// MaterialInfo::game_phase() returns the game phase according
/// to this material configuration.
inline Phase MaterialInfo::game_phase() const {
return gamePhase; return gamePhase;
} }
inline bool MaterialEntry::specialized_eval_exists() const {
/// MaterialInfo::specialized_eval_exists decides whether there is a
/// specialized evaluation function for the current material configuration,
/// or if the normal evaluation function should be used.
inline bool MaterialInfo::specialized_eval_exists() const {
return evaluationFunction != NULL; 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)
+223 -189
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,228 +17,262 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
////
//// Includes
////
#if !defined(_MSC_VER)
# include <sys/time.h>
# include <sys/types.h>
# include <unistd.h>
#else
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <sys/timeb.h>
#endif
#include <cassert>
#include <cstdio>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include "bitcount.h"
#include "misc.h" #include "misc.h"
#include "thread.h"
#if defined(__hpux)
# include <sys/pstat.h>
#endif
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 = ""; 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;
s << "Stockfish " << Version;
if (Version.empty()) ////
{ //// Functions
date >> month >> day >> year; ////
s << Tag << setfill('0') << " " << year.substr(2) void dbg_hit_on(bool b) {
<< setw(2) << (1 + months.find(month) / 4) << setw(2) << day;
}
s << cpu64 << popcnt << (to_uci ? "\nid author ": " by ") assert(!dbg_show_mean);
<< "Tord Romstad, Marco Costalba and Joona Kiiski"; dbg_show_hit_rate = true;
dbg_cnt0++;
if (b)
dbg_cnt1++;
}
void dbg_hit_on_c(bool c, bool b) {
if (c)
dbg_hit_on(b);
}
void dbg_before() {
assert(!dbg_show_mean);
dbg_show_hit_rate = true;
dbg_cnt0++;
}
void dbg_after() {
assert(!dbg_show_mean);
dbg_show_hit_rate = true;
dbg_cnt1++;
}
void dbg_mean_of(int v) {
assert(!dbg_show_hit_rate);
dbg_show_mean = true;
dbg_cnt0++;
dbg_cnt1 += v;
}
void dbg_print_hit_rate() {
cout << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
<< " hit rate (%) " << (dbg_cnt1*100)/(dbg_cnt0 ? dbg_cnt0 : 1) << endl;
}
void dbg_print_mean() {
cout << "Total " << dbg_cnt0 << " Mean "
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl;
}
void dbg_print_hit_rate(ofstream& logFile) {
logFile << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
<< " hit rate (%) " << (dbg_cnt1*100)/(dbg_cnt0 ? dbg_cnt0 : 1) << endl;
}
void dbg_print_mean(ofstream& logFile) {
logFile << "Total " << dbg_cnt0 << " Mean "
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << endl;
}
/// engine_name() returns the full name of the current Stockfish version.
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when the
/// program was compiled) or "Stockfish <version number>", depending on whether
/// the constant EngineVersion (defined in misc.h) is empty.
const string engine_name() {
const string cpu64(CpuHas64BitPath ? " 64bit" : "");
if (!EngineVersion.empty())
return AppName+ " " + EngineVersion + cpu64;
string date(__DATE__); // From compiler, format is "Sep 21 2008"
string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
size_t mon = 1 + months.find(date.substr(0, 3)) / 4;
stringstream s;
string day = (date[4] == ' ' ? date.substr(5, 1) : date.substr(4, 2));
string name = AppName + " " + AppTag + " ";
s << name << date.substr(date.length() - 2) << setfill('0')
<< setw(2) << mon << setw(2) << day << cpu64;
return s.str(); return s.str();
} }
/// Convert system time to milliseconds. That's all we need. /// get_system_time() returns the current system time, measured in
/// milliseconds.
Time::point Time::now() { int get_system_time() {
sys_time_t t; system_time(&t); return time_to_msec(t);
}
#if defined(_MSC_VER)
/// Debug functions used mainly to collect run-time statistics struct _timeb t;
_ftime(&t);
static uint64_t hits[2], means[2]; return int(t.time*1000 + t.millitm);
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;
}
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
/// can toggle the logging of std::cout and std:cin at runtime while preserving
/// usual i/o functionality and without changing a single line of code!
/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
struct Tie: public streambuf { // MSVC requires splitted streambuf for cin and cout
Tie(streambuf* b, ofstream* f) : buf(b), file(f) {}
int sync() { return file->rdbuf()->pubsync(), buf->pubsync(); }
int overflow(int c) { return log(buf->sputc((char)c), "<< "); }
int underflow() { return buf->sgetc(); }
int uflow() { return log(buf->sbumpc(), ">> "); }
streambuf* buf;
ofstream* file;
int log(int c, const char* prefix) {
static int last = '\n';
if (last == '\n')
file->rdbuf()->sputn(prefix, 3);
return last = file->rdbuf()->sputc((char)c);
}
};
class Logger {
Logger() : in(cin.rdbuf(), &file), out(cout.rdbuf(), &file) {}
~Logger() { start(false); }
ofstream file;
Tie in, out;
public:
static void start(bool b) {
static Logger l;
if (b && !l.file.is_open())
{
l.file.open("io_log.txt", ifstream::out | ifstream::app);
cin.rdbuf(&l.in);
cout.rdbuf(&l.out);
}
else if (!b && l.file.is_open())
{
cout.rdbuf(l.out.buf);
cin.rdbuf(l.in.buf);
l.file.close();
}
}
};
/// Used to serialize access to std::cout to avoid multiple threads to write at
/// the same time.
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
static Mutex m;
if (sc == io_lock)
m.lock();
if (sc == io_unlock)
m.unlock();
return os;
}
/// Trampoline helper to avoid moving Logger to misc.h
void start_logger(bool b) { Logger::start(b); }
/// cpu_count() tries to detect the number of CPU cores
int cpu_count() {
#if defined(_WIN32) || defined(_WIN64)
SYSTEM_INFO s;
GetSystemInfo(&s);
return s.dwNumberOfProcessors;
#else #else
struct timeval t;
gettimeofday(&t, NULL);
return t.tv_sec*1000 + t.tv_usec/1000;
#endif
}
/// cpu_count() tries to detect the number of CPU cores.
#if !defined(_MSC_VER)
# if defined(_SC_NPROCESSORS_ONLN) # if defined(_SC_NPROCESSORS_ONLN)
return sysconf(_SC_NPROCESSORS_ONLN); 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 psd.psd_proc_cnt;
# else # else
int cpu_count() {
return 1; return 1;
}
# endif # endif
#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) {
#if defined(_WIN32) || defined(_WIN64)
int tm = msec;
#else
timespec ts, *tm = &ts;
uint64_t ms = Time::now() + msec;
ts.tv_sec = ms / 1000;
ts.tv_nsec = (ms % 1000) * 1000000LL;
#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
# include <xmmintrin.h> int cpu_count() {
SYSTEM_INFO s;
void prefetch(char* addr) { GetSystemInfo(&s);
return Min(s.dwNumberOfProcessors, 8);
# if defined(__INTEL_COMPILER) || defined(__ICL)
// This hack prevents prefetches to be optimized away by
// Intel compiler. Both MSVC and gcc seems not affected.
__asm__ ("");
# endif
_mm_prefetch(addr, _MM_HINT_T2);
_mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead
} }
#endif #endif
/*
From Beowulf, from Olithink
*/
#ifndef _WIN32
/* Non-windows version */
int Bioskey()
{
fd_set readfds;
struct timeval timeout;
FD_ZERO(&readfds);
FD_SET(fileno(stdin), &readfds);
/* Set to timeout immediately */
timeout.tv_sec = 0;
timeout.tv_usec = 0;
select(16, &readfds, 0, 0, &timeout);
return (FD_ISSET(fileno(stdin), &readfds));
}
#else
/* Windows-version */
#include <windows.h>
#include <conio.h>
int Bioskey()
{
static int init = 0,
pipe;
static HANDLE inh;
DWORD dw;
/* If we're running under XBoard then we can't use _kbhit() as the input
* commands are sent to us directly over the internal pipe */
#if defined(FILE_CNT)
if (stdin->_cnt > 0)
return stdin->_cnt;
#endif
if (!init) {
init = 1;
inh = GetStdHandle(STD_INPUT_HANDLE);
pipe = !GetConsoleMode(inh, &dw);
if (!pipe) {
SetConsoleMode(inh, dw & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
FlushConsoleInputBuffer(inh);
}
}
if (pipe) {
if (!PeekNamedPipe(inh, NULL, 0, NULL, &dw, NULL))
return 1;
return dw;
} else {
// Count the number of unread input records, including keyboard,
// mouse, and window-resizing input records.
GetNumberOfConsoleInputEvents(inh, &dw);
if (dw <= 0)
return 0;
// Read data from console without removing it from the buffer
INPUT_RECORD rec[256];
DWORD recCnt;
if (!PeekConsoleInput(inh, rec, Min(dw, 256), &recCnt))
return 0;
// Search for at least one keyboard event
for (DWORD i = 0; i < recCnt; i++)
if (rec[i].EventType == KEY_EVENT)
return 1;
return 0;
}
}
#endif
+47 -36
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,53 +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 <vector>
#include "application.h"
#include "types.h" #include "types.h"
extern const std::string engine_info(bool to_uci = false); ////
//// 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);
extern void start_logger(bool b);
////
//// 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();
extern void dbg_print_hit_rate(std::ofstream& logFile);
struct Log : public std::ofstream { extern void dbg_print_mean(std::ofstream& logFile);
Log(const std::string& f = "log.txt") : std::ofstream(f.c_str(), std::ios::out | std::ios::app) {}
~Log() { if (is_open()) close(); }
};
namespace Time {
typedef int64_t point;
point now();
}
template<class Entry, int Size>
struct HashTable {
HashTable() : e(Size, Entry()) {}
Entry* operator[](Key k) { return &e[(uint32_t)k & (Size - 1)]; }
private:
std::vector<Entry> e;
};
enum SyncCout { io_lock, io_unlock };
std::ostream& operator<<(std::ostream&, SyncCout);
#define sync_cout std::cout << io_lock
#define sync_endl std::endl << io_unlock
#endif // !defined(MISC_H_INCLUDED) #endif // !defined(MISC_H_INCLUDED)
+152
View File
@@ -0,0 +1,152 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include "move.h"
#include "piece.h"
#include "position.h"
////
//// Functions
////
/// move_from_string() takes a position and a string as input, and attempts to
/// convert the string to a move, using simple coordinate notation (g1f3,
/// a7a8q, etc.). In order to correctly parse en passant captures and castling
/// moves, we need the position. This function is not robust, and expects that
/// the input move is legal and correctly formatted.
Move move_from_string(const Position& pos, const std::string& str) {
Square from, to;
Piece piece;
Color us = pos.side_to_move();
if (str.length() < 4)
return MOVE_NONE;
// Read the from and to squares
from = square_from_string(str.substr(0, 2));
to = square_from_string(str.substr(2, 4));
// Find the moving piece
piece = pos.piece_on(from);
// If the string has more than 4 characters, try to interpret the 5th
// character as a promotion
if (type_of_piece(piece) == PAWN && str.length() > 4)
{
switch (tolower(str[4])) {
case 'n':
return make_promotion_move(from, to, KNIGHT);
case 'b':
return make_promotion_move(from, to, BISHOP);
case 'r':
return make_promotion_move(from, to, ROOK);
case 'q':
return make_promotion_move(from, to, QUEEN);
}
}
if (piece == piece_of_color_and_type(us, KING))
{
// Is this a castling move? A king move is assumed to be a castling
// move if the destination square is occupied by a friendly rook, or
// if the distance between the source and destination squares is more
// than 1.
if (pos.piece_on(to) == piece_of_color_and_type(us, ROOK))
return make_castle_move(from, to);
else if (square_distance(from, to) > 1)
{
// This is a castling move, but we have to translate it to the
// internal "king captures rook" representation.
SquareDelta delta = (to > from ? DELTA_E : DELTA_W);
Square s = from + delta;
while (relative_rank(us, s) == RANK_1 && pos.piece_on(s) != piece_of_color_and_type(us, ROOK))
s += delta;
return (relative_rank(us, s) == RANK_1 ? make_castle_move(from, s) : MOVE_NONE);
}
}
else if (piece == piece_of_color_and_type(us, PAWN))
{
// En passant move? We assume that a pawn move is an en passant move
// without further testing if the destination square is epSquare.
if (to == pos.ep_square())
return make_ep_move(from, to);
}
return make_move(from, to);
}
/// move_to_string() converts a move to a string in coordinate notation
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we
/// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
/// Chess960 mode.
const std::string move_to_string(Move move) {
std::string str;
Square from = move_from(move);
Square to = move_to(move);
if (move == MOVE_NONE)
str = "(none)";
else if (move == MOVE_NULL)
str = "0000";
else
{
if (!Chess960)
{
if (move_is_short_castle(move))
return (from == SQ_E1 ? "e1g1" : "e8g8");
if (move_is_long_castle(move))
return (from == SQ_E1 ? "e1c1" : "e8c8");
}
str = square_to_string(from) + square_to_string(to);
if (move_is_promotion(move))
str += piece_type_to_char(move_promotion_piece(move), false);
}
return str;
}
/// Overload the << operator, to make it easier to print moves.
std::ostream &operator << (std::ostream& os, Move m) {
return os << move_to_string(m);
}
/// move_is_ok(), for debugging.
bool move_is_ok(Move m) {
return square_is_ok(move_from(m)) && square_is_ok(move_to(m));
}
+230
View File
@@ -0,0 +1,230 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2009 Marco Costalba
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MOVE_H_INCLUDED)
#define MOVE_H_INCLUDED
////
//// Includes
////
#include <iostream>
#include "misc.h"
#include "piece.h"
#include "square.h"
////
//// Types
////
class Position;
/// A move needs 17 bits to be stored
///
/// bit 0- 5: destination square (from 0 to 63)
/// bit 6-11: origin square (from 0 to 63)
/// bit 12-14: promotion piece type
/// bit 15: en passant flag
/// bit 16: castle flag
///
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in
/// because in any normal move destination square is always different
/// from origin square while MOVE_NONE and MOVE_NULL have the same
/// origin and destination square, 0 and 1 respectively.
enum Move {
MOVE_NONE = 0,
MOVE_NULL = 65
};
struct MoveStack {
Move move;
int score;
};
// Note that operator< is set up such that sorting will be in descending order
inline bool operator<(const MoveStack& f, const MoveStack& s) { return s.score < f.score; }
// An helper insertion sort implementation
template<typename T>
inline void insertion_sort(T* firstMove, T* lastMove)
{
T value;
T *cur, *p, *d;
if (firstMove != lastMove)
for (cur = firstMove + 1; cur != lastMove; cur++)
{
p = d = cur;
value = *p--;
if (value < *p)
{
do *d = *p;
while (--d != firstMove && value < *--p);
*d = value;
}
}
}
// Our dedicated sort in range [firstMove, lastMove), it is well
// tuned for non-captures where we have a lot of zero scored moves.
template<typename T>
inline void sort_moves(T* firstMove, T* lastMove)
{
T tmp;
T *p, *d;
d = lastMove;
p = firstMove - 1;
d->score = -1; // right guard
// Split positives vs non-positives
do {
while ((++p)->score > 0);
if (p != d)
{
while (--d != p && d->score <= 0);
tmp = *p;
*p = *d;
*d = tmp;
}
} while (p != d);
// Sort positives
insertion_sort<T>(firstMove, p);
d = lastMove;
p--;
// Split zero vs negatives
do {
while ((++p)->score == 0);
if (p != d)
{
while (--d != p && d->score < 0);
tmp = *p;
*p = *d;
*d = tmp;
}
} while (p != d);
// Sort negatives
insertion_sort<T>(p, lastMove);
}
// Picks up the best move in range [curMove, lastMove), one per cycle.
// It is faster then sorting all the moves in advance when moves are few,
// as normally are the possible captures. Note that is not a stable alghoritm.
template<typename T>
inline T pick_best(T* curMove, T* lastMove)
{
T bestMove, tmp;
bestMove = *curMove;
while (++curMove != lastMove)
{
if (*curMove < bestMove)
{
tmp = *curMove;
*curMove = bestMove;
bestMove = tmp;
}
}
return bestMove;
}
////
//// Inline functions
////
inline Square move_from(Move m) {
return Square((int(m) >> 6) & 0x3F);
}
inline Square move_to(Move m) {
return Square(m & 0x3F);
}
inline PieceType move_promotion_piece(Move m) {
return PieceType((int(m) >> 12) & 7);
}
inline int move_is_special(Move m) {
return m & (0x1F << 12);
}
inline int move_is_promotion(Move m) {
return m & (7 << 12);
}
inline int move_is_ep(Move m) {
return m & (1 << 15);
}
inline int move_is_castle(Move m) {
return m & (1 << 16);
}
inline bool move_is_short_castle(Move m) {
return move_is_castle(m) && (move_to(m) > move_from(m));
}
inline bool move_is_long_castle(Move m) {
return move_is_castle(m) && (move_to(m) < move_from(m));
}
inline Move make_promotion_move(Square from, Square to, PieceType promotion) {
return Move(int(to) | (int(from) << 6) | (int(promotion) << 12));
}
inline Move make_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6));
}
inline Move make_castle_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6) | (1 << 16));
}
inline Move make_ep_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6) | (1 << 15));
}
////
//// Prototypes
////
extern std::ostream& operator<<(std::ostream &os, Move m);
extern Move move_from_string(const Position &pos, const std::string &str);
extern const std::string move_to_string(Move m);
extern bool move_is_ok(Move m);
#endif // !defined(MOVE_H_INCLUDED)
+595 -347
View File
File diff suppressed because it is too large Load Diff
+16 -27
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,39 +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 GenType { #include "position.h"
CAPTURES,
QUIETS,
QUIET_CHECKS,
EVASIONS,
NON_EVASIONS,
LEGAL
};
class Position;
template<GenType> ////
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<GenType 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; }
size_t size() const { return last - mlist; }
private:
MoveStack mlist[MAX_MOVES];
MoveStack *cur, *last;
};
#endif // !defined(MOVEGEN_H_INCLUDED) #endif // !defined(MOVEGEN_H_INCLUDED)
+250 -254
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,133 +18,158 @@
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 "thread.h" #include "search.h"
#include "value.h"
////
//// Local definitions
////
namespace { namespace {
enum Sequencer { enum MovegenPhase {
MAIN_SEARCH, CAPTURES_S1, KILLERS_S1, QUIETS_1_S1, QUIETS_2_S1, BAD_CAPTURES_S1, PH_TT_MOVES, // Transposition table move and mate killer
EVASION, EVASIONS_S2, PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= 0
QSEARCH_0, CAPTURES_S3, QUIET_CHECKS_S3, PH_KILLERS, // Killer moves from the current ply
QSEARCH_1, CAPTURES_S4, PH_NONCAPTURES, // Non-captures and underpromotions
PROBCUT, CAPTURES_S5, PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < 0
RECAPTURE, CAPTURES_S6, PH_EVASIONS, // Check evasions
STOP PH_QCAPTURES, // Captures in quiescence search
PH_QCHECKS, // Non-capture checks in quiescence search
PH_STOP
}; };
// Unary predicate used by std::partition to split positive scores from remaining CACHE_LINE_ALIGNMENT
// ones so to sort separately the two sets, and with the second sort delayed. const uint8_t MainSearchPhaseTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
inline bool has_positive_score(const MoveStack& ms) { return ms.score > 0; } const uint8_t EvasionsPhaseTable[] = { PH_TT_MOVES, PH_EVASIONS, PH_STOP};
const uint8_t QsearchWithChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP};
// Picks and moves to the front the best move in the range [begin, end), const uint8_t QsearchWithoutChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP};
// 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* begin, MoveStack* end)
{
std::swap(*begin, *std::max_element(begin, end));
return begin;
}
} }
/// Constructors of 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* s, Value beta) : pos(p), H(h), depth(d) { const History& h, SearchStack* ss) : pos(p), H(h) {
int searchTT = ttm;
ttMoves[0].move = ttm;
finished = false;
lastBadCapture = badCaptures;
assert(d > DEPTH_ZERO); pinned = p.pinned_pieces(pos.side_to_move());
captureThreshold = 0; if (ss && !p.is_check())
cur = end = moves;
endBadCaptures = moves + MAX_MOVES - 1;
ss = s;
if (p.in_check())
phase = EVASION;
else
{ {
phase = MAIN_SEARCH; 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 - PawnValueMg && d < 3 * ONE_PLY) phasePtr = EvasionsPhaseTable;
captureThreshold = -PawnValueMg; 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;
}
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
end += (ttMove != MOVE_NONE);
}
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
Square sq) : pos(p), H(h), cur(moves), end(moves) {
assert(d <= DEPTH_ZERO);
if (p.in_check())
phase = EVASION;
else if (d > DEPTH_QS_NO_CHECKS)
phase = QSEARCH_0;
else if (d > DEPTH_QS_RECAPTURES)
{
phase = QSEARCH_1;
// 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 && !pos.is_capture_or_promotion(ttm))
ttm = MOVE_NONE;
}
else else
{ phasePtr = QsearchWithoutChecksPhaseTable;
phase = RECAPTURE;
recaptureSquare = sq;
ttm = MOVE_NONE;
}
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); phasePtr += !searchTT - 1;
end += (ttMove != MOVE_NONE); go_next_phase();
} }
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt)
: pos(p), H(h), cur(moves), end(moves) {
assert(!pos.in_check()); /// MovePicker::go_next_phase() generates, scores and sorts the next bunch
/// of moves when there are no more moves to try for the current phase.
phase = PROBCUT; void MovePicker::go_next_phase() {
// In ProbCut we generate only captures better than parent's captured piece curMove = moves;
captureThreshold = PieceValue[Mg][pt]; phase = *(++phasePtr);
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); switch (phase) {
if (ttMove && (!pos.is_capture(ttMove) || pos.see(ttMove) <= captureThreshold)) case PH_TT_MOVES:
ttMove = MOVE_NONE; curMove = ttMoves;
lastMove = curMove + 2;
return;
end += (ttMove != MOVE_NONE); case PH_GOOD_CAPTURES:
lastMove = generate_captures(pos, moves);
score_captures();
return;
case PH_KILLERS:
curMove = killers;
lastMove = curMove + 2;
return;
case PH_NONCAPTURES:
lastMove = generate_noncaptures(pos, moves);
score_noncaptures();
sort_moves(moves, lastMove);
return;
case PH_BAD_CAPTURES:
// Bad captures SEE value is already calculated so just sort them
// to get SEE move ordering.
curMove = badCaptures;
lastMove = lastBadCapture;
return;
case PH_EVASIONS:
assert(pos.is_check());
lastMove = generate_evasions(pos, moves);
score_evasions();
return;
case PH_QCAPTURES:
lastMove = generate_captures(pos, moves);
score_captures();
return;
case PH_QCHECKS:
// Perhaps we should order moves move here? FIXME
lastMove = generate_non_capture_checks(pos, moves);
return;
case PH_STOP:
lastMove = curMove + 1; // hack to be friendly for get_next_move()
return;
default:
assert(false);
return;
}
} }
/// MovePicker::score_captures(), MovePicker::score_noncaptures() and /// MovePicker::score_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.
@@ -162,203 +187,174 @@ void MovePicker::score_captures() {
// some SEE calls in case we get a cutoff (idea from Pablo Vazquez). // some SEE calls in case we get a cutoff (idea from Pablo Vazquez).
Move m; Move m;
for (MoveStack* it = moves; it != end; ++it) // Use MVV/LVA ordering
for (MoveStack* cur = moves; cur != lastMove; cur++)
{ {
m = it->move; m = cur->move;
it->score = PieceValue[Mg][pos.piece_on(to_sq(m))] if (move_is_promotion(m))
- type_of(pos.piece_moved(m)); cur->score = QueenValueMidgame;
else
if (type_of(m) == PROMOTION) cur->score = pos.midgame_value_of_piece_on(move_to(m))
it->score += PieceValue[Mg][promotion_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;
Piece piece;
Square from, to;
int hs;
for (MoveStack* it = moves; it != end; ++it) for (MoveStack* cur = moves; cur != lastMove; cur++)
{ {
m = it->move; m = cur->move;
it->score = H.value(pos.piece_moved(m), to_sq(m)); from = move_from(m);
to = move_to(m);
piece = pos.piece_on(from);
hs = H.move_ordering_score(piece, to);
// Ensure history is always preferred to pst
if (hs > 0)
hs += 1000;
// pst based scoring
cur->score = hs + mg_value(pos.pst_delta(piece, from, to));
} }
} }
void MovePicker::score_evasions() { void MovePicker::score_evasions() {
// Try good captures ordered by MVV/LVA, then non-captures if destination square // Try good captures ordered by MVV/LVA, then non-captures if
// is not under attack, ordered by history value, then bad-captures and quiet // destination square is not under attack, ordered by history
// moves with a negative SEE. This last group is ordered by the SEE score. // value, and at the end bad-captures and non-captures with a
// negative SEE. This last group is ordered by the SEE score.
Move m; Move m;
int seeScore; int seeScore;
if (end < moves + 2) for (MoveStack* cur = moves; cur != lastMove; cur++)
return;
for (MoveStack* it = moves; it != end; ++it)
{ {
m = it->move; m = cur->move;
if ((seeScore = pos.see_sign(m)) < 0) if ((seeScore = pos.see_sign(m)) < 0)
it->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))
it->score = PieceValue[Mg][pos.piece_on(to_sq(m))] cur->score = pos.midgame_value_of_piece_on(move_to(m))
- type_of(pos.piece_moved(m)) + History::MaxValue; - pos.type_of_piece_on(move_from(m)) + HistoryMax;
else else
it->score = H.value(pos.piece_moved(m), to_sq(m)); cur->score = H.move_ordering_score(pos.piece_on(move_from(m)), move_to(m));
} }
} }
/// MovePicker::get_next_move() is the most important method of the MovePicker
/// class. It returns a new legal move every time it is called, until there
/// are no more moves left.
/// It picks the move with the biggest score from a list of generated moves taking
/// care not to return the tt move if has already been searched previously.
/// MovePicker::generate_next() generates, scores and sorts the next bunch of moves, Move MovePicker::get_next_move() {
/// when there are no more moves to try for the current phase.
void MovePicker::generate_next() {
cur = moves;
switch (++phase) {
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
end = generate<CAPTURES>(pos, moves);
score_captures();
return;
case KILLERS_S1:
cur = killers;
end = cur + 2;
return;
case QUIETS_1_S1:
endQuiets = end = generate<QUIETS>(pos, moves);
score_noncaptures();
end = std::partition(cur, end, has_positive_score);
sort<MoveStack>(cur, end);
return;
case QUIETS_2_S1:
cur = end;
end = endQuiets;
if (depth >= 3 * ONE_PLY)
sort<MoveStack>(cur, end);
return;
case BAD_CAPTURES_S1:
// Just pick them in reverse order to get MVV/LVA ordering
cur = moves + MAX_MOVES - 1;
end = endBadCaptures;
return;
case EVASIONS_S2:
end = generate<EVASIONS>(pos, moves);
score_evasions();
return;
case QUIET_CHECKS_S3:
end = generate<QUIET_CHECKS>(pos, moves);
return;
case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE:
phase = STOP;
case STOP:
end = cur + 1; // Avoid another next_phase() call
return;
default:
assert(false);
}
}
/// MovePicker::next_move() is the most important method of the MovePicker class.
/// It returns a new pseudo legal move every time it is called, until there
/// are no more moves left. It picks the move with the biggest score from a list
/// of generated moves taking care not to return the tt move if has already been
/// searched previously.
template<>
Move MovePicker::next_move<false>() {
Move move; Move move;
while (true) while (true)
{ {
while (cur == end) while (curMove != lastMove)
generate_next(); {
switch (phase) {
switch (phase) { case PH_TT_MOVES:
move = (curMove++)->move;
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: if ( move != MOVE_NONE
cur++; && move_is_legal(pos, move, pinned))
return ttMove;
case CAPTURES_S1:
move = pick_best(cur++, end)->move;
if (move != ttMove)
{
assert(captureThreshold <= 0); // Otherwise we cannot use see_sign()
if (pos.see_sign(move) >= captureThreshold)
return move; return move;
break;
// Losing capture, move it to the tail of the array case PH_GOOD_CAPTURES:
(endBadCaptures--)->move = move; move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& pos.pl_move_is_legal(move, pinned))
{
// Check for a non negative SEE now
int seeValue = pos.see_sign(move);
if (seeValue >= 0)
return move;
// Losing capture, move it to the badCaptures[] array, note
// that move has now been already checked for legality.
assert(int(lastBadCapture - badCaptures) < 63);
lastBadCapture->move = move;
lastBadCapture->score = seeValue;
lastBadCapture++;
}
break;
case PH_KILLERS:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& move != ttMoves[0].move
&& move != ttMoves[1].move
&& move_is_legal(pos, move, pinned)
&& !pos.move_is_capture(move))
return move;
break;
case PH_NONCAPTURES:
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& move != killers[0].move
&& move != killers[1].move
&& pos.pl_move_is_legal(move, pinned))
return move;
break;
case PH_BAD_CAPTURES:
move = pick_best(curMove++, lastMove).move;
return move;
case PH_EVASIONS:
case PH_QCAPTURES:
move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
return move;
break;
case PH_QCHECKS:
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
return move;
break;
case PH_STOP:
return MOVE_NONE;
default:
assert(false);
break;
} }
break;
case KILLERS_S1:
move = (cur++)->move;
if ( move != MOVE_NONE
&& pos.is_pseudo_legal(move)
&& move != ttMove
&& !pos.is_capture(move))
return move;
break;
case QUIETS_1_S1: case QUIETS_2_S1:
move = (cur++)->move;
if ( move != ttMove
&& move != killers[0].move
&& move != killers[1].move)
return move;
break;
case BAD_CAPTURES_S1:
return (cur--)->move;
case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4:
move = pick_best(cur++, end)->move;
if (move != ttMove)
return move;
break;
case CAPTURES_S5:
move = pick_best(cur++, end)->move;
if (move != ttMove && pos.see(move) > captureThreshold)
return move;
break;
case CAPTURES_S6:
move = pick_best(cur++, end)->move;
if (to_sq(move) == recaptureSquare)
return move;
break;
case QUIET_CHECKS_S3:
move = (cur++)->move;
if (move != ttMove)
return move;
break;
case STOP:
return MOVE_NONE;
default:
assert(false);
} }
go_next_phase();
} }
} }
/// A variant of get_next_move() which takes a lock as a parameter, used to
/// prevent multiple threads from picking the same move at a split point.
/// Version of next_move() to use at split point nodes where the move is grabbed Move MovePicker::get_next_move(Lock &lock) {
/// from the split point's shared MovePicker object. This function is not thread
/// safe so should be lock protected by the caller. lock_grab(&lock);
template<> if (finished)
Move MovePicker::next_move<true>() { return ss->sp->mp->next_move<false>(); } {
lock_release(&lock);
return MOVE_NONE;
}
Move m = get_next_move();
if (m == MOVE_NONE)
finished = true;
lock_release(&lock);
return m;
}
+49 -23
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,48 +17,74 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined MOVEPICK_H_INCLUDED #if !defined MOVEPICK_H_INCLUDED
#define MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED
////
//// Includes
////
#include "depth.h"
#include "history.h" #include "history.h"
#include "lock.h"
#include "position.h" #include "position.h"
#include "search.h"
#include "types.h"
/// MovePicker class is used to pick one pseudo legal move at a time from the ////
/// current position. The most important method is next_move(), which returns a //// Types
/// new pseudo legal move each time it is called, until there are no moves left, ////
/// when MOVE_NONE is returned. In order to improve the efficiency of the alpha
/// beta algorithm, MovePicker attempts to return the moves which are most likely struct SearchStack;
/// to get a cut-off first.
/// MovePicker is a class which is used to pick one legal move at a time from
/// the current position. It is initialized with a Position object and a few
/// moves we have reason to believe are good. The most important method is
/// MovePicker::pick_next_move(), which returns a new legal move each time it
/// is called, until there are no legal moves left, when MOVE_NONE is returned.
/// In order to improve the efficiency of the alpha beta algorithm, MovePicker
/// attempts to return the moves which are most likely to be strongest first.
class MovePicker { 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); Move get_next_move();
MovePicker(const Position&, Move, const History&, PieceType); Move get_next_move(Lock& lock);
template<bool SpNode> Move next_move(); int number_of_evasions() const;
private: private:
void score_captures(); void score_captures();
void score_noncaptures(); void score_noncaptures();
void score_evasions(); void score_evasions();
void generate_next(); void go_next_phase();
const Position& pos; const Position& pos;
const History& H; const History& H;
Search::Stack* ss; MoveStack ttMoves[2], killers[2];
Depth depth; bool finished;
Move ttMove; int phase;
MoveStack killers[2]; const uint8_t* phasePtr;
Square recaptureSquare; MoveStack *curMove, *lastMove, *lastBadCapture;
int captureThreshold, phase; Bitboard pinned;
MoveStack *cur, *end, *endQuiets, *endBadCaptures; MoveStack moves[256], badCaptures[64];
MoveStack moves[MAX_MOVES];
}; };
////
//// 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)
-271
View File
@@ -1,271 +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 <cassert>
#include <iomanip>
#include <sstream>
#include <string>
#include "movegen.h"
#include "notation.h"
#include "position.h"
using namespace std;
static const char* PieceToChar = " PNBRQK pnbrqk";
/// score_to_uci() converts a value to a string suitable for use with the UCI
/// protocol specifications:
///
/// cp <x> The score from the engine's point of view in centipawns.
/// mate <y> Mate in y moves, not plies. If the engine is getting mated
/// use negative values for y.
string score_to_uci(Value v, Value alpha, Value beta) {
stringstream s;
if (abs(v) < VALUE_MATE_IN_MAX_PLY)
s << "cp " << v * 100 / int(PawnValueMg);
else
s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
s << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
return s.str();
}
/// move_to_uci() converts a move to a string in coordinate notation
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print
/// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960
/// mode. Internally castle moves are always coded as "king captures rook".
const string move_to_uci(Move m, bool chess960) {
Square from = from_sq(m);
Square to = to_sq(m);
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "0000";
if (type_of(m) == CASTLE && !chess960)
to = (to > from ? FILE_G : FILE_C) | rank_of(from);
string move = square_to_string(from) + square_to_string(to);
if (type_of(m) == PROMOTION)
move += PieceToChar[make_piece(BLACK, promotion_type(m))]; // Lower case
return move;
}
/// move_from_uci() takes a position and a string representing a move in
/// simple coordinate notation and returns an equivalent legal Move if any.
Move move_from_uci(const Position& pos, string& str) {
if (str.length() == 5) // Junior could send promotion piece in uppercase
str[4] = char(tolower(str[4]));
for (MoveList<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 legal Move as input and returns its
/// short algebraic notation representation.
const string move_to_san(Position& pos, Move m) {
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "(null)";
assert(pos.move_is_legal(m));
Bitboard attackers;
bool ambiguousMove, ambiguousFile, ambiguousRank;
string san;
Color us = pos.side_to_move();
Square from = from_sq(m);
Square to = to_sq(m);
Piece pc = pos.piece_on(from);
PieceType pt = type_of(pc);
if (type_of(m) == CASTLE)
san = to > from ? "O-O" : "O-O-O";
else
{
if (pt != PAWN)
{
san = PieceToChar[pt]; // Upper case
// Disambiguation if we have more then one piece with destination 'to'
// note that for pawns is not needed because starting file is explicit.
ambiguousMove = ambiguousFile = ambiguousRank = false;
attackers = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from;
while (attackers)
{
Square sq = pop_lsb(&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;
ambiguousFile |= file_of(sq) == file_of(from);
ambiguousRank |= rank_of(sq) == rank_of(from);
ambiguousMove = true;
}
if (ambiguousMove)
{
if (!ambiguousFile)
san += file_to_char(file_of(from));
else if (!ambiguousRank)
san += rank_to_char(rank_of(from));
else
san += square_to_string(from);
}
}
else if (pos.is_capture(m))
san = file_to_char(file_of(from));
if (pos.is_capture(m))
san += 'x';
san += square_to_string(to);
if (type_of(m) == PROMOTION)
san += string("=") + PieceToChar[promotion_type(m)];
}
if (pos.move_gives_check(m, CheckInfo(pos)))
{
StateInfo st;
pos.do_move(m, st);
san += MoveList<LEGAL>(pos).size() ? "+" : "#";
pos.undo_move(m);
}
return san;
}
/// pretty_pv() formats human-readable search information, typically to be
/// appended to the search log file. It uses the two helpers below to pretty
/// format time and score respectively.
static string time_to_string(int64_t msecs) {
const int MSecMinute = 1000 * 60;
const int MSecHour = 1000 * 60 * 60;
int64_t hours = msecs / MSecHour;
int64_t minutes = (msecs % MSecHour) / MSecMinute;
int64_t seconds = ((msecs % MSecHour) % MSecMinute) / 1000;
stringstream s;
if (hours)
s << hours << ':';
s << setfill('0') << setw(2) << minutes << ':' << setw(2) << seconds;
return s.str();
}
static string score_to_string(Value v) {
stringstream s;
if (v >= VALUE_MATE_IN_MAX_PLY)
s << "#" << (VALUE_MATE - v + 1) / 2;
else if (v <= VALUE_MATED_IN_MAX_PLY)
s << "-#" << (VALUE_MATE + v) / 2;
else
s << setprecision(2) << fixed << showpos << float(v) / PawnValueMg;
return s.str();
}
string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]) {
const int64_t K = 1000;
const int64_t M = 1000000;
StateInfo state[MAX_PLY_PLUS_2], *st = state;
Move* m = pv;
string san, padding;
size_t length;
stringstream s;
s << setw(2) << depth
<< setw(8) << score_to_string(value)
<< setw(8) << time_to_string(msecs);
if (pos.nodes_searched() < M)
s << setw(8) << pos.nodes_searched() / 1 << " ";
else if (pos.nodes_searched() < K * M)
s << setw(7) << pos.nodes_searched() / K << "K ";
else
s << setw(7) << pos.nodes_searched() / M << "M ";
padding = string(s.str().length(), ' ');
length = padding.length();
while (*m != MOVE_NONE)
{
san = move_to_san(pos, *m);
if (length + san.length() > 80)
{
s << "\n" + padding;
length = padding.length();
}
s << san << ' ';
length += san.length() + 1;
pos.do_move(*m++, *st++);
}
while (m != pv)
pos.undo_move(*--m);
return s.str();
}
+260 -179
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,38 +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 {
#define V Value /// 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] = {
@@ -62,152 +70,275 @@ 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
};
// Weakness of our pawn shelter in front of the king indexed by [king pawn][rank] const int KStormTable[64] = {
const Value ShelterWeakness[2][8] = 0, 0, 0, 0, 0, 0, 0, 0,
{ { V(141), V(0), V(38), V(102), V(128), V(141), V(141) }, 0, 0, 0,-10,-19,-28,-33,-33,
{ V( 61), V(0), V(16), V( 44), V( 56), V( 61), V( 61) } }; 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
};
// Danger of enemy pawns moving toward our king indexed by [pawn blocked][rank] // Pawn storm open file bonuses by file
const Value StormDanger[2][8] = const int16_t KStormOpenFileBonus[8] = { 31, 31, 18, 0, 0, 0, 0, 0 };
{ { V(26), V(0), V(128), V(51), V(26) }, const int16_t QStormOpenFileBonus[8] = { 0, 0, 0, 0, 0, 26, 42, 26 };
{ V(13), V(0), V( 64), V(25), V(13) } };
// Max bonus for king safety. Corresponds to start position with all the pawns // Pawn storm lever bonuses by file
// in front of the king and no enemy pawn on the horizont. const int StormLeverBonus[8] = { -8, -8, -13, 0, 0, -13, -8, -8 };
const Value MaxSafetyBonus = V(263);
#undef S
#undef V
} }
/// PawnTable::probe() takes a position object as input, computes a PawnEntry ////
/// object, and returns a pointer to it. The result is also stored in a hash //// Functions
/// table, so we don't have to recompute everything when the same pawn structure ////
/// occurs again.
PawnEntry* PawnTable::probe(const Position& pos) { /// Constructor
Key key = pos.pawn_key(); PawnInfoTable::PawnInfoTable(unsigned numOfEntries) {
PawnEntry* e = entries[key];
// If e->key matches the position's pawn hash key, it means that we 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();
}
}
/// Destructor
PawnInfoTable::~PawnInfoTable() {
delete [] entries;
}
/// PawnInfo::clear() resets to zero the PawnInfo entry. Note that
/// kingSquares[] is initialized to SQ_NONE instead.
void PawnInfo::clear() {
memset(this, 0, sizeof(PawnInfo));
kingSquares[WHITE] = kingSquares[BLACK] = SQ_NONE;
}
/// PawnInfoTable::get_pawn_info() takes a position object as input, computes
/// a PawnInfo object, and returns a pointer to it. The result is also
/// stored in a hash table, so we don't have to recompute everything when
/// the same pawn structure occurs again.
PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
assert(pos.is_ok());
Key key = pos.get_pawn_key();
int index = int(key & (size - 1));
PawnInfo* pi = entries + index;
// If pi->key matches the position's pawn hash key, it means that we
// have analysed this pawn structure before, and we can simply return // have analysed this pawn structure before, and we can simply return
// the information we found the last time instead of recomputing it. // the information we found the last time instead of recomputing it.
if (e->key == key) if (pi->key == key)
return e; return pi;
e->key = key; // Clear the PawnInfo object, and set the key
e->passedPawns[WHITE] = e->passedPawns[BLACK] = 0; pi->clear();
e->kingSquares[WHITE] = e->kingSquares[BLACK] = SQ_NONE; pi->key = key;
e->halfOpenFiles[WHITE] = e->halfOpenFiles[BLACK] = 0xFF;
Bitboard wPawns = pos.pieces(WHITE, PAWN); // Calculate pawn attacks
Bitboard bPawns = pos.pieces(BLACK, PAWN); Bitboard whitePawns = pos.pieces(PAWN, WHITE);
e->pawnAttacks[WHITE] = ((wPawns & ~FileHBB) << 9) | ((wPawns & ~FileABB) << 7); Bitboard blackPawns = pos.pieces(PAWN, BLACK);
e->pawnAttacks[BLACK] = ((bPawns & ~FileHBB) >> 7) | ((bPawns & ~FileABB) >> 9); pi->pawnAttacks[WHITE] = ((whitePawns << 9) & ~FileABB) | ((whitePawns << 7) & ~FileHBB);
pi->pawnAttacks[BLACK] = ((blackPawns >> 7) & ~FileABB) | ((blackPawns >> 9) & ~FileHBB);
e->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, e) // Evaluate pawns for both colors
- evaluate_pawns<BLACK>(pos, bPawns, wPawns, e); pi->value = evaluate_pawns<WHITE>(pos, whitePawns, blackPawns, pi)
- evaluate_pawns<BLACK>(pos, blackPawns, whitePawns, pi);
e->value = apply_weight(e->value, PawnStructureWeight); return pi;
return e;
} }
/// PawnTable::evaluate_pawns() evaluates each pawn of the given color /// PawnInfoTable::evaluate_pawns() evaluates each pawn of the given color
template<Color Us> template<Color Us>
Score PawnTable::evaluate_pawns(const Position& pos, Bitboard ourPawns, Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnEntry* e) { 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?
e->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)];
chain = ourPawns & adjacent_files_bb(f) & b; if (f >= FILE_F)
isolated = !(ourPawns & adjacent_files_bb(f)); {
doubled = ourPawns & forward_bb(Us, s); Bitboard b = outpost_mask(Us, s) & theirPawns & (FileFBB | FileGBB | FileHBB);
opposed = theirPawns & forward_bb(Us, s); while (b)
passed = !(theirPawns & passed_pawn_mask(Us, s)); {
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
backward = false; //
// If the pawn is passed, isolated, or member of a pawn chain
// If the pawn is passed, isolated, or member of a pawn chain it cannot // it cannot be backward. If can capture an enemy pawn or if
// be backward. If there are friendly pawns behind on adjacent files // there are friendly pawns behind on neighboring files it cannot
// or if can capture an enemy pawn it cannot be backward either. // be backward either.
if ( !(passed | isolated | chain) if ( (passed | isolated | chain)
&& !(ourPawns & attack_span_mask(Them, s)) || (ourPawns & behind_bb(Us, r) & neighboring_files_bb(f))
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns)) || (pos.attacks_from<PAWN>(s, Us) & theirPawns))
backward = false;
else
{ {
// 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 adjacent 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 adjacent // 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 adjacent files is higher or equal than the number of // a true passed pawn.
// enemy pawns in the forward direction on the adjacent 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)
e->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];
@@ -219,69 +350,19 @@ Score PawnTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
} }
/// PawnEntry::shelter_storm() calculates shelter and storm penalties for the file /// PawnInfo::updateShelter calculates and caches king shelter. It is called
/// the king is on, as well as the two adjacent files. /// only when king square changes, about 20% of total get_king_shelter() calls.
int PawnInfo::updateShelter(const Position& pos, Color c, Square ksq) {
template<Color Us> unsigned shelter = 0;
Value PawnEntry::shelter_storm(const Position& pos, Square ksq) { Bitboard pawns = pos.pieces(PAWN, c) & this_and_neighboring_files_bb(ksq);
unsigned r = ksq & (7 << 3);
const Color Them = (Us == WHITE ? BLACK : WHITE); for (int i = 1, k = (c ? -8 : 8); i < 4; i++)
Value safety = MaxSafetyBonus;
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, ksq) | rank_bb(ksq));
Bitboard ourPawns = b & pos.pieces(Us) & ~rank_bb(ksq);
Bitboard theirPawns = b & pos.pieces(Them);
Rank rkUs, rkThem;
File kf = file_of(ksq);
kf = (kf == FILE_A) ? kf++ : (kf == FILE_H) ? kf-- : kf;
for (int f = kf - 1; f <= kf + 1; f++)
{ {
// Shelter penalty is higher for the pawn in front of the king r += k;
b = ourPawns & FileBB[f]; shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (128 >> i);
rkUs = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1;
safety -= ShelterWeakness[f != kf][rkUs];
// Storm danger is smaller if enemy pawn is blocked
b = theirPawns & FileBB[f];
rkThem = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1;
safety -= StormDanger[rkThem == rkUs + 1][rkThem];
} }
kingSquares[c] = ksq;
return safety; kingShelters[c] = shelter;
return shelter;
} }
/// PawnEntry::update_safety() calculates and caches a bonus for king safety. It is
/// called only when king square changes, about 20% of total king_safety() calls.
template<Color Us>
Score PawnEntry::update_safety(const Position& pos, Square ksq) {
kingSquares[Us] = ksq;
castleRights[Us] = pos.can_castle(Us);
minKPdistance[Us] = 0;
Bitboard pawns = pos.pieces(Us, PAWN);
if (pawns)
while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {}
if (relative_rank(Us, ksq) > RANK_4)
return kingSafety[Us] = make_score(0, -16 * minKPdistance[Us]);
Value bonus = shelter_storm<Us>(pos, ksq);
// If we can castle use the bonus after the castle if is bigger
if (pos.can_castle(make_castle_right(Us, KING_SIDE)))
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1)));
if (pos.can_castle(make_castle_right(Us, QUEEN_SIDE)))
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1)));
return kingSafety[Us] = make_score(bonus, -16 * minKPdistance[Us]);
}
// Explicit template instantiation
template Score PawnEntry::update_safety<WHITE>(const Position& pos, Square ksq);
template Score PawnEntry::update_safety<BLACK>(const Position& pos, Square ksq);
+73 -53
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,99 +17,119 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(PAWNS_H_INCLUDED) #if !defined(PAWNS_H_INCLUDED)
#define PAWNS_H_INCLUDED #define PAWNS_H_INCLUDED
#include "misc.h" ////
#include "position.h" //// Includes
#include "types.h" ////
const int PawnTableSize = 16384; #include "bitboard.h"
#include "value.h"
/// PawnEntry is a class which contains various information about a pawn ////
//// Types
////
/// 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 probe method in a PawnTable object) /// (performed by calling the get_pawn_info method in a PawnInfoTable object)
/// returns a pointer to a PawnEntry object. /// returns a pointer to a PawnInfo object.
class Position;
class PawnEntry { class PawnInfo {
friend struct PawnTable; 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> private:
Score king_safety(const Position& pos, Square ksq); void clear();
int updateShelter(const Position& pos, Color c, Square ksq);
Key key;
Bitboard passedPawns;
Bitboard pawnAttacks[2];
Score value;
int16_t ksStormValue[2], qsStormValue[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.
class PawnInfoTable {
public:
PawnInfoTable(unsigned numOfEntries);
~PawnInfoTable();
PawnInfo* get_pawn_info(const Position& pos);
private: private:
template<Color Us> template<Color Us>
Score update_safety(const Position& pos, Square ksq); Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi);
template<Color Us> unsigned size;
Value shelter_storm(const Position& pos, Square ksq); PawnInfo* entries;
Key key;
Bitboard passedPawns[2];
Bitboard pawnAttacks[2];
Square kingSquares[2];
int minKPdistance[2];
int castleRights[2];
Score value;
int halfOpenFiles[2];
Score kingSafety[2];
}; };
/// The PawnTable class represents a pawn hash table. The most important ////
/// method is probe, which returns a pointer to a PawnEntry object. //// Inline functions
////
struct PawnTable { inline Score PawnInfo::pawns_value() const {
PawnEntry* probe(const Position& pos);
template<Color Us>
static Score evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnEntry* e);
HashTable<PawnEntry, PawnTableSize> entries;
};
inline Score PawnEntry::pawns_value() const {
return value; return value;
} }
inline Bitboard PawnEntry::pawn_attacks(Color c) const { inline Bitboard PawnInfo::passed_pawns() const {
return passedPawns;
}
inline Bitboard PawnInfo::pawn_attacks(Color c) const {
return pawnAttacks[c]; return pawnAttacks[c];
} }
inline Bitboard PawnEntry::passed_pawns(Color c) const { inline Value PawnInfo::kingside_storm_value(Color c) const {
return passedPawns[c]; return Value(ksStormValue[c]);
} }
inline int PawnEntry::file_is_half_open(Color c, File f) const { inline Value PawnInfo::queenside_storm_value(Color c) const {
return halfOpenFiles[c] & (1 << int(f)); return Value(qsStormValue[c]);
} }
inline int PawnEntry::has_open_file_to_left(Color c, File f) const { inline int PawnInfo::file_is_half_open(Color c, File f) const {
return (halfOpenFiles[c] & (1 << int(f)));
}
inline int PawnInfo::has_open_file_to_left(Color c, File f) const {
return halfOpenFiles[c] & ((1 << int(f)) - 1); return halfOpenFiles[c] & ((1 << int(f)) - 1);
} }
inline int PawnEntry::has_open_file_to_right(Color c, File f) const { 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 PawnEntry::king_safety(const Position& pos, Square ksq) { return (kingSquares[c] == ksq ? kingShelters[c] : updateShelter(pos, c, ksq));
return kingSquares[Us] == ksq && castleRights[Us] == pos.can_castle(Us)
? kingSafety[Us] : update_safety<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)
-109
View File
@@ -1,109 +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/>.
*/
#if !defined(PLATFORM_H_INCLUDED)
#define PLATFORM_H_INCLUDED
#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'
#pragma warning(disable: 4996) // Function _ftime() may be unsafe
// 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
# include <inttypes.h>
#endif
#if !defined(_WIN32) && !defined(_WIN64) // Linux - Unix
# include <sys/time.h>
typedef timeval sys_time_t;
inline void system_time(sys_time_t* t) { gettimeofday(t, NULL); }
inline int64_t time_to_msec(const sys_time_t& t) { return t.tv_sec * 1000LL + t.tv_usec / 1000; }
# include <pthread.h>
typedef pthread_mutex_t Lock;
typedef pthread_cond_t WaitCondition;
typedef pthread_t NativeHandle;
typedef void*(*pt_start_fn)(void*);
# define lock_init(x) pthread_mutex_init(&(x), NULL)
# define lock_grab(x) pthread_mutex_lock(&(x))
# define lock_release(x) pthread_mutex_unlock(&(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)
# define thread_create(x,f,t) !pthread_create(&(x),NULL,(pt_start_fn)f,t)
# define thread_join(x) pthread_join(x, NULL)
#else // Windows and MinGW
# include <sys/timeb.h>
typedef _timeb sys_time_t;
inline void system_time(sys_time_t* t) { _ftime(t); }
inline int64_t time_to_msec(const sys_time_t& t) { return t.time * 1000LL + t.millitm; }
#if !defined(NOMINMAX)
# define NOMINMAX // disable macros min() and max()
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
// We use critical sections on Windows to support Windows XP and older versions,
// unfortunatly cond_wait() is racy between lock_release() and WaitForSingleObject()
// but apart from this they have the same speed performance of SRW locks.
typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition;
typedef HANDLE NativeHandle;
# define lock_init(x) InitializeCriticalSection(&(x))
# define lock_grab(x) EnterCriticalSection(&(x))
# define lock_release(x) LeaveCriticalSection(&(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); }
# define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,NULL), x != NULL)
# define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); }
#endif
#endif // !defined(PLATFORM_H_INCLUDED)
+1462 -1129
View File
File diff suppressed because it is too large Load Diff
+336 -199
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,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(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; ////
class Thread;
/// 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&);
Bitboard dcCandidates; Square ksq;
Bitboard pinned; Bitboard dcCandidates;
Bitboard checkSq[8]; Bitboard checkSq[8];
Square ksq; };
/// Castle rights, encoded as bit fields
enum CastleRights {
NO_CASTLES = 0,
WHITE_OO = 1,
BLACK_OO = 2,
WHITE_OOO = 4,
BLACK_OOO = 8,
ALL_CASTLES = 15
};
/// Game phase
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
}; };
@@ -49,25 +100,17 @@ struct CheckInfo {
struct StateInfo { struct StateInfo {
Key pawnKey, materialKey; Key pawnKey, materialKey;
Value npMaterial[2];
int castleRights, rule50, pliesFromNull; int castleRights, rule50, pliesFromNull;
Score psqScore;
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;
}; };
struct ReducedStateInfo {
Key pawnKey, materialKey;
Value npMaterial[2];
int castleRights, rule50, pliesFromNull;
Score psqScore;
Square epSquare;
};
/// The position data structure. A position consists of the following data: /// The position data structure. A position consists of the following data:
/// ///
@@ -92,112 +135,169 @@ struct ReducedStateInfo {
/// * A counter for detecting 50 move rule draws. /// * A counter for detecting 50 move rule draws.
class Position { class Position {
friend class MaterialInfo;
friend class EndgameFunctions;
public: public:
enum GamePhase {
MidGame,
EndGame
};
// Constructors
Position() {} Position() {}
Position(const Position& p, Thread* t) { *this = p; thisThread = t; } Position(const Position& pos);
Position(const std::string& f, bool c960, Thread* t) { from_fen(f, c960, t); } Position(const std::string& fen);
Position& operator=(const Position&);
// Text input/output // Text input/output
void from_fen(const std::string& fen, bool isChess960, Thread* th); void from_fen(const std::string& fen);
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;
// Position representation // Copying
Bitboard pieces() const; void copy(const Position& pos);
Bitboard pieces(PieceType pt) const; void flipped_copy(const Position& pos);
Bitboard pieces(PieceType pt1, PieceType pt2) const;
Bitboard pieces(Color c) const; // The piece on a given square
Bitboard pieces(Color c, PieceType pt) const;
Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const;
Piece piece_on(Square s) const; Piece piece_on(Square s) const;
Square king_square(Color c) const; PieceType type_of_piece_on(Square s) const;
Square ep_square() const; Color color_of_piece_on(Square s) const;
bool is_empty(Square s) const; bool square_is_empty(Square s) const;
const Square* piece_list(Color c, PieceType pt) const; bool square_is_occupied(Square s) const;
Value midgame_value_of_piece_on(Square s) const;
Value endgame_value_of_piece_on(Square s) const;
// Side to move
Color side_to_move() const;
// Bitboard representation of the position
Bitboard empty_squares() const;
Bitboard occupied_squares() const;
Bitboard pieces_of_color(Color c) const;
Bitboard pieces(PieceType pt) const;
Bitboard pieces(PieceType pt, Color c) const;
Bitboard pieces(PieceType pt1, PieceType pt2) const;
Bitboard pieces(PieceType pt1, PieceType pt2, Color c) const;
// Number of pieces of each color and type
int piece_count(Color c, PieceType pt) const; int piece_count(Color c, PieceType pt) const;
// Castling // The en passant square
int can_castle(CastleRight f) const; Square ep_square() const;
int can_castle(Color c) const;
bool castle_impeded(Color c, CastlingSide s) const;
Square castle_rook_square(Color c, CastlingSide s) const;
// Checking // Current king position for each color
bool in_check() const; Square king_square(Color c) const;
// Castling rights
bool can_castle_kingside(Color c) const;
bool can_castle_queenside(Color c) const;
bool can_castle(Color c) const;
Square initial_kr_square(Color c) const;
Square initial_qr_square(Color c) const;
// Bitboards for pinned pieces and discovered check candidates
Bitboard discovered_check_candidates(Color c) const;
Bitboard pinned_pieces(Color c) const;
// Checking pieces and under check information
Bitboard checkers() const; Bitboard checkers() const;
Bitboard discovered_check_candidates() const; bool is_check() const;
Bitboard pinned_pieces() const;
// Attacks to/from a given square // Piece lists
Square piece_list(Color c, PieceType pt, int index) const;
const Square* piece_list_begin(Color c, PieceType pt) const;
// Information about attacks to or from a given square
Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s) 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 move_is_legal(const Move m) 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;
Piece piece_moved(Move m) const; bool move_is_capture_or_promotion(Move m) const;
PieceType captured_piece_type() const; bool move_is_passed_pawn_push(Move m) const;
bool move_attacks_square(Move m, Square s) const;
// Piece specific // Information about pawns
bool pawn_is_passed(Color c, Square s) const; bool pawn_is_passed(Color c, Square s) const;
bool pawn_on_7th(Color c) const; static bool pawn_is_passed(Bitboard theirPawns, Color c, Square s);
bool opposite_bishops() const; static bool pawn_is_isolated(Bitboard ourPawns, Square s);
bool bishop_pair(Color c) const; 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 piece-square evaluation // Incremental evaluation
Score psq_score() const; Score value() const;
Score psq_delta(Piece p, Square from, Square to) const;
Value non_pawn_material(Color c) const; Value non_pawn_material(Color c) const;
Score pst_delta(Piece piece, Square from, Square to) const;
// Game termination checks
bool is_mate() const;
bool is_draw() const;
// Check if one side threatens a mate in one
bool has_mate_threat(Color c);
// Number of plies since the last non-reversible move
int rule_50_counter() const;
// Other properties of the position // Other properties of the position
Color side_to_move() const; bool opposite_colored_bishops() const;
int startpos_ply_counter() const; bool has_pawn_on_7th(Color c) const;
bool is_chess960() const;
Thread* this_thread() const; // Reset the gamePly variable to 0
int64_t nodes_searched() const; void reset_game_ply();
void set_nodes_searched(int64_t n);
template<bool SkipRepetition> bool is_draw() const;
// 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();
// Static member functions
static void init_zobrist();
static void init_piece_square_tables();
private: private:
// Initialization helpers (used while setting up a position)
// Initialization helper functions (used while setting up a position)
void clear(); void clear();
void put_piece(Piece p, Square s); void put_piece(Piece p, Square s);
void set_castle_right(Color c, Square rfrom); void allow_oo(Color c);
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;
@@ -205,83 +305,116 @@ 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 compute_psq_score() const; Score pst(Color c, PieceType pt, Square s) const;
Score compute_value() const;
Value compute_non_pawn_material(Color c) const; Value compute_non_pawn_material(Color c) const;
// Board and pieces // Board
Piece board[64]; // [square] Piece board[64];
Bitboard byTypeBB[8]; // [pieceType]
Bitboard byColorBB[2]; // [color] // Bitboards
int pieceCount[2][8]; // [color][pieceType] Bitboard byTypeBB[8], byColorBB[2];
Square pieceList[2][8][16]; // [color][pieceType][index]
int index[64]; // [square] // Piece counts
int pieceCount[2][8]; // [color][pieceType]
// Piece lists
Square pieceList[2][8][16]; // [color][pieceType][index]
int index[64]; // [square]
// Other info // Other info
int castleRightsMask[64]; // [square]
Square castleRookSquare[2][2]; // [color][side]
Bitboard castlePath[2][2]; // [color][side]
StateInfo startState;
int64_t nodes;
int startPosPly;
Color sideToMove; Color sideToMove;
Thread* thisThread; int gamePly;
Key history[MaxGameLength];
File initialKFile, initialKRFile, initialQRFile;
StateInfo startState;
StateInfo* st; StateInfo* st;
int chess960;
// Static variables
static int castleRightsMask[64];
static Key zobrist[2][8][64];
static Key zobEp[64];
static Key zobCastle[16];
static Key zobMaterial[2][8][16];
static Key zobSideToMove;
static Score PieceSquareTable[16][64];
static Key zobExclusion;
}; };
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 Piece Position::piece_moved(Move m) const { inline Color Position::color_of_piece_on(Square s) const {
return board[from_sq(m)]; return color_of_piece(piece_on(s));
} }
inline bool Position::is_empty(Square s) const { inline PieceType Position::type_of_piece_on(Square s) const {
return board[s] == NO_PIECE; return type_of_piece(piece_on(s));
}
inline bool Position::square_is_empty(Square s) const {
return piece_on(s) == EMPTY;
}
inline bool Position::square_is_occupied(Square s) const {
return !square_is_empty(s);
}
inline Value Position::midgame_value_of_piece_on(Square s) const {
return piece_value_midgame(piece_on(s));
}
inline Value Position::endgame_value_of_piece_on(Square s) const {
return piece_value_endgame(piece_on(s));
} }
inline Color Position::side_to_move() const { inline Color Position::side_to_move() const {
return sideToMove; return sideToMove;
} }
inline Bitboard Position::pieces() const { inline Bitboard Position::occupied_squares() const {
return byTypeBB[ALL_PIECES]; return byTypeBB[0];
}
inline Bitboard Position::empty_squares() const {
return ~(occupied_squares());
}
inline Bitboard Position::pieces_of_color(Color c) const {
return byColorBB[c];
} }
inline Bitboard Position::pieces(PieceType pt) const { inline Bitboard Position::pieces(PieceType pt) const {
return byTypeBB[pt]; return byTypeBB[pt];
} }
inline Bitboard Position::pieces(PieceType pt, Color c) const {
return byTypeBB[pt] & byColorBB[c];
}
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const { inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const {
return byTypeBB[pt1] | byTypeBB[pt2]; return byTypeBB[pt1] | byTypeBB[pt2];
} }
inline Bitboard Position::pieces(Color c) const { inline Bitboard Position::pieces(PieceType pt1, PieceType pt2, Color c) const {
return byColorBB[c]; return (byTypeBB[pt1] | byTypeBB[pt2]) & byColorBB[c];
}
inline Bitboard Position::pieces(Color c, PieceType pt) const {
return byColorBB[c] & byTypeBB[pt];
}
inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const {
return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]);
} }
inline int Position::piece_count(Color c, PieceType pt) const { 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];
} }
@@ -293,141 +426,145 @@ inline Square Position::king_square(Color c) const {
return pieceList[c][KING][0]; return pieceList[c][KING][0];
} }
inline int 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 int Position::can_castle(Color c) const { inline bool Position::can_castle_queenside(Color side) const {
return st->castleRights & ((WHITE_OO | WHITE_OOO) << (2 * c)); return st->castleRights & (4+4*int(side));
} }
inline bool Position::castle_impeded(Color c, CastlingSide s) const { inline bool Position::can_castle(Color side) const {
return byTypeBB[ALL_PIECES] & castlePath[c][s]; return can_castle_kingside(side) || can_castle_queenside(side);
} }
inline Square Position::castle_rook_square(Color c, CastlingSide s) const { inline Square Position::initial_kr_square(Color c) const {
return castleRookSquare[c][s]; return relative_square(c, make_square(initialKRFile, RANK_1));
} }
template<PieceType Pt> inline Square Position::initial_qr_square(Color c) const {
inline Bitboard Position::attacks_from(Square s) const { return relative_square(c, make_square(initialQRFile, RANK_1));
return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, pieces())
: Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
: StepAttacksBB[Pt][s];
} }
template<> template<>
inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const { inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
return StepAttacksBB[make_piece(c, PAWN)][s]; return StepAttackBB[piece_of_color_and_type(c, PAWN)][s];
} }
inline Bitboard Position::attacks_from(Piece p, Square s) const { template<PieceType Piece> // Knight and King and white pawns
return attacks_from(p, s, byTypeBB[ALL_PIECES]); inline Bitboard Position::attacks_from(Square s) const {
return StepAttackBB[Piece][s];
} }
inline Bitboard Position::attackers_to(Square s) const { template<>
return attackers_to(s, byTypeBB[ALL_PIECES]); inline Bitboard Position::attacks_from<BISHOP>(Square s) const {
return bishop_attacks_bb(s, occupied_squares());
}
template<>
inline Bitboard Position::attacks_from<ROOK>(Square s) const {
return rook_attacks_bb(s, occupied_squares());
}
template<>
inline Bitboard Position::attacks_from<QUEEN>(Square s) const {
return attacks_from<ROOK>(s) | attacks_from<BISHOP>(s);
} }
inline Bitboard Position::checkers() const { inline Bitboard Position::checkers() const {
return st->checkersBB; return st->checkersBB;
} }
inline 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(~c, PAWN) & 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 ^ Zobrist::exclusion; 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::psq_delta(Piece p, Square from, Square to) const { inline Score Position::pst(Color c, PieceType pt, Square s) const {
return pieceSquareTable[p][to] - pieceSquareTable[p][from]; return PieceSquareTable[piece_of_color_and_type(c, pt)][s];
} }
inline Score Position::psq_score() const { inline Score Position::pst_delta(Piece piece, Square from, Square to) const {
return st->psqScore; return PieceSquareTable[piece][to] - PieceSquareTable[piece][from];
}
inline Score Position::value() const {
return st->value;
} }
inline Value Position::non_pawn_material(Color c) const { inline Value Position::non_pawn_material(Color c) const {
return st->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 type_of(piece_moved(m)) == 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_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::bishop_pair(Color c) const { inline bool Position::has_pawn_on_7th(Color c) const {
return pieceCount[c][BISHOP] >= 2 return pieces(PAWN, c) & relative_rank_bb(c, RANK_7);
&& opposite_colors(pieceList[c][BISHOP][0], pieceList[c][BISHOP][1]);
} }
inline bool Position::pawn_on_7th(Color c) const { inline bool Position::move_is_capture(Move m) const {
return pieces(c, PAWN) & rank_bb(relative_rank(c, RANK_7));
// Move must not be MOVE_NONE !
return (m & (3 << 15)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
} }
inline bool Position::is_chess960() const { inline bool Position::move_is_capture_or_promotion(Move m) const {
return chess960;
}
inline bool Position::is_capture_or_promotion(Move m) const { // Move must not be MOVE_NONE !
return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
assert(is_ok(m));
return type_of(m) ? type_of(m) != CASTLE : !is_empty(to_sq(m));
}
inline bool Position::is_capture(Move m) const {
// Note that castle is coded as "king captures the rook"
assert(is_ok(m));
return (!is_empty(to_sq(m)) && type_of(m) != CASTLE) || type_of(m) == ENPASSANT;
}
inline PieceType Position::captured_piece_type() const {
return st->capturedType;
}
inline Thread* Position::this_thread() const {
return thisThread;
} }
#endif // !defined(POSITION_H_INCLUDED) #endif // !defined(POSITION_H_INCLUDED)
+152 -62
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)
+2728 -1426
View File
File diff suppressed because it is too large Load Diff
+41 -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,95 +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 <memory> //// Includes
#include <stack> ////
#include <vector>
#include "misc.h" #include "depth.h"
#include "position.h" #include "move.h"
#include "types.h"
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 killers[2]; Move threatMove;
Move killers[KILLER_MAX];
Depth reduction; Depth reduction;
Value eval;
Value evalMargin; void init(int ply);
int skipNullMove; void initKillers();
}; };
/// RootMove struct is used for moves at the root of the tree. For each root ////
/// move we store a score, a node count, and a PV (really a refutation in the //// Prototypes
/// case of moves which fail low). Score is normally set at -VALUE_INFINITE for ////
/// all non-pv moves.
struct RootMove {
RootMove(){} // Needed by sort() extern void init_threads();
RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) { extern void stop_threads();
pv.push_back(m); pv.push_back(MOVE_NONE); extern bool think(const Position &pos, bool infinite, bool ponder, int side_to_move,
} int time[], int increment[], int movesToGo, int maxDepth,
int maxNodes, int maxTime, Move searchMoves[]);
extern int perft(Position &pos, Depth depth);
extern int64_t nodes_searched();
bool operator<(const RootMove& m) const { return score < m.score; }
bool operator==(const Move& m) const { return pv[0] == m; }
void extract_pv_from_tt(Position& pos);
void insert_pv_in_tt(Position& pos);
Value score;
Value prevScore;
std::vector<Move> pv;
};
/// 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
/// or if we have to ponder while is our opponent's side to move.
struct LimitsType {
LimitsType() { memset(this, 0, sizeof(LimitsType)); }
bool use_time_management() const { return !(movetime | depth | nodes | infinite); }
int time[2], inc[2], movestogo, depth, nodes, movetime, 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;
};
typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr;
extern volatile SignalsType Signals;
extern LimitsType Limits;
extern std::vector<RootMove> RootMoves;
extern Position RootPosition;
extern Time::point SearchTime;
extern StateStackPtr SetupStates;
extern void init();
extern size_t perft(Position& pos, Depth depth);
extern void think();
} // namespace Search
#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)
-436
View File
@@ -1,436 +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 <cassert>
#include <iostream>
#include "movegen.h"
#include "search.h"
#include "thread.h"
#include "ucioption.h"
using namespace Search;
ThreadPool Threads; // Global object
namespace { extern "C" {
// start_routine() is the C function which is called when a new thread
// is launched. It is a wrapper to member function pointed by start_fn.
long start_routine(Thread* th) { (th->*(th->start_fn))(); return 0; }
} }
// Thread c'tor starts a newly-created thread of execution that will call
// the idle loop function pointed by start_fn going immediately to sleep.
Thread::Thread(Fn fn) {
is_searching = do_exit = false;
maxPly = splitPointsCnt = 0;
curSplitPoint = NULL;
start_fn = fn;
idx = Threads.size();
do_sleep = (fn != &Thread::main_loop); // Avoid a race with start_searching()
if (!thread_create(handle, start_routine, this))
{
std::cerr << "Failed to create thread number " << idx << std::endl;
::exit(EXIT_FAILURE);
}
}
// Thread d'tor waits for thread termination before to return.
Thread::~Thread() {
assert(do_sleep);
do_exit = true; // Search must be already finished
wake_up();
thread_join(handle); // Wait for thread termination
}
// Thread::timer_loop() is where the timer thread waits maxPly milliseconds and
// then calls check_time(). If maxPly is 0 thread sleeps until is woken up.
extern void check_time();
void Thread::timer_loop() {
while (!do_exit)
{
mutex.lock();
sleepCondition.wait_for(mutex, maxPly ? maxPly : INT_MAX);
mutex.unlock();
check_time();
}
}
// 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)
{
mutex.lock();
do_sleep = true; // Always return to sleep after a search
is_searching = false;
while (do_sleep && !do_exit)
{
Threads.sleepCondition.notify_one(); // Wake up UI thread if needed
sleepCondition.wait(mutex);
}
mutex.unlock();
if (do_exit)
return;
is_searching = true;
Search::think();
assert(is_searching);
}
}
// Thread::wake_up() wakes up the thread, normally at the beginning of the search
// or, if "sleeping threads" is used at split time.
void Thread::wake_up() {
mutex.lock();
sleepCondition.notify_one();
mutex.unlock();
}
// Thread::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 Thread::wait_for_stop_or_ponderhit() {
Signals.stopOnPonderhit = true;
mutex.lock();
while (!Signals.stop) sleepCondition.wait(mutex);;
mutex.unlock();
}
// Thread::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 = curSplitPoint; sp; sp = sp->parent)
if (sp->cutoff)
return true;
return false;
}
// Thread::is_available_to() checks whether the thread is available to help the
// thread 'master' at a split point. An obvious requirement is that thread must
// be idle. With more than two threads, this is not sufficient: If the thread is
// the master of some active split point, it is only available as a slave to the
// slaves which are busy searching the split point at the top of slaves split
// point stack (the "helpful master concept" in YBWC terminology).
bool Thread::is_available_to(Thread* 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 spCnt = splitPointsCnt;
// 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.
return !spCnt || (splitPoints[spCnt - 1].slavesMask & (1ULL << master->idx));
}
// init() is called at startup. Initializes lock and condition variable and
// launches requested threads sending them immediately to sleep. We cannot use
// a c'tor becuase Threads is a static object and we need a fully initialized
// engine at this point due to allocation of endgames in Thread c'tor.
void ThreadPool::init() {
timer = new Thread(&Thread::timer_loop);
threads.push_back(new Thread(&Thread::main_loop));
read_uci_options();
}
// exit() cleanly terminates the threads before the program exits.
void ThreadPool::exit() {
for (size_t i = 0; i < threads.size(); i++)
delete threads[i];
delete timer;
}
// read_uci_options() updates internal threads parameters from the corresponding
// UCI options and creates/destroys threads to match the requested number. Thread
// objects are dynamically allocated to avoid creating in advance all possible
// threads, with included pawns and material tables, if only few are used.
void ThreadPool::read_uci_options() {
maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
useSleepingThreads = Options["Use Sleeping Threads"];
size_t requested = Options["Threads"];
assert(requested > 0);
while (threads.size() < requested)
threads.push_back(new Thread(&Thread::idle_loop));
while (threads.size() > requested)
{
delete threads.back();
threads.pop_back();
}
}
// wake_up() is called before a new search to start the threads that are waiting
// on the sleep condition and to reset maxPly. When useSleepingThreads is set
// threads will be woken up at split time.
void ThreadPool::wake_up() const {
for (size_t i = 0; i < threads.size(); i++)
{
threads[i]->maxPly = 0;
threads[i]->do_sleep = false;
if (!useSleepingThreads)
threads[i]->wake_up();
}
}
// sleep() is called after the search finishes to ask all the threads but the
// main one to go waiting on a sleep condition.
void ThreadPool::sleep() const {
// Main thread will go to sleep by itself to avoid a race with start_searching()
for (size_t i = 1; i < threads.size(); i++)
threads[i]->do_sleep = true;
}
// available_slave_exists() tries to find an idle thread which is available as
// a slave for the thread 'master'.
bool ThreadPool::available_slave_exists(Thread* master) const {
for (size_t i = 0; i < threads.size(); i++)
if (threads[i]->is_available_to(master))
return true;
return false;
}
// 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 ThreadPool::split(Position& pos, Stack* ss, Value alpha, Value beta,
Value bestValue, Move* bestMove, 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);
Thread* master = pos.this_thread();
if (master->splitPointsCnt >= MAX_SPLITPOINTS_PER_THREAD)
return bestValue;
// Pick the next available split point from the split point stack
SplitPoint& sp = master->splitPoints[master->splitPointsCnt];
sp.parent = master->curSplitPoint;
sp.master = master;
sp.cutoff = false;
sp.slavesMask = 1ULL << master->idx;
sp.depth = depth;
sp.bestMove = *bestMove;
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;
assert(master->is_searching);
master->curSplitPoint = &sp;
int slavesCnt = 0;
// 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.
sp.mutex.lock();
mutex.lock();
for (size_t i = 0; i < threads.size() && !Fake; ++i)
if (threads[i]->is_available_to(master))
{
sp.slavesMask |= 1ULL << i;
threads[i]->curSplitPoint = &sp;
threads[i]->is_searching = true; // Slave leaves idle_loop()
if (useSleepingThreads)
threads[i]->wake_up();
if (++slavesCnt + 1 >= maxThreadsPerSplitPoint) // Master is always included
break;
}
master->splitPointsCnt++;
mutex.unlock();
sp.mutex.unlock();
// 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.
// The thread will return from the idle loop when all slaves have finished
// their work at this split point.
if (slavesCnt || Fake)
{
master->idle_loop();
// 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(!master->is_searching);
}
// We have returned from the idle loop, which means that all threads are
// finished. Note that setting is_searching and decreasing splitPointsCnt is
// done under lock protection to avoid a race with Thread::is_available_to().
sp.mutex.lock(); // To protect sp.nodes
mutex.lock();
master->is_searching = true;
master->splitPointsCnt--;
master->curSplitPoint = sp.parent;
pos.set_nodes_searched(pos.nodes_searched() + sp.nodes);
*bestMove = sp.bestMove;
mutex.unlock();
sp.mutex.unlock();
return sp.bestValue;
}
// Explicit template instantiations
template Value ThreadPool::split<false>(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int);
template Value ThreadPool::split<true>(Position&, Stack*, Value, Value, Value, Move*, Depth, Move, int, MovePicker*, int);
// set_timer() is used to set the timer to trigger after msec milliseconds.
// If msec is 0 then timer is stopped.
void ThreadPool::set_timer(int msec) {
timer->mutex.lock();
timer->maxPly = msec;
timer->sleepCondition.notify_one(); // Wake up and restart the timer
timer->mutex.unlock();
}
// wait_for_search_finished() waits for main thread to go to sleep, this means
// search is finished. Then returns.
void ThreadPool::wait_for_search_finished() {
Thread* t = main_thread();
t->mutex.lock();
t->sleepCondition.notify_one(); // In case is waiting for stop or ponderhit
while (!t->do_sleep) sleepCondition.wait(t->mutex);
t->mutex.unlock();
}
// start_searching() wakes up the main thread sleeping in main_loop() so to start
// a new search, then returns immediately.
void ThreadPool::start_searching(const Position& pos, const LimitsType& limits,
const std::vector<Move>& searchMoves, StateStackPtr& states) {
wait_for_search_finished();
SearchTime = Time::now(); // As early as possible
Signals.stopOnPonderhit = Signals.firstRootMove = false;
Signals.stop = Signals.failedLowAtRoot = false;
RootPosition = pos;
Limits = limits;
SetupStates = states; // Ownership transfer here
RootMoves.clear();
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if (searchMoves.empty() || count(searchMoves.begin(), searchMoves.end(), ml.move()))
RootMoves.push_back(RootMove(ml.move()));
main_thread()->do_sleep = false;
main_thread()->wake_up();
}
+40 -128
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,152 +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 <vector>
#include "material.h" ////
//// Includes
////
#include "lock.h"
#include "movepick.h" #include "movepick.h"
#include "pawns.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
const int MAX_THREADS = 32;
const int MAX_SPLITPOINTS_PER_THREAD = 8;
struct Mutex { ////
Mutex() { lock_init(l); } //// Constants and variables
~Mutex() { lock_destroy(l); } ////
void lock() { lock_grab(l); } const int THREAD_MAX = 8;
void unlock() { lock_release(l); }
private:
friend struct ConditionVariable;
Lock l; ////
}; //// Types
////
struct ConditionVariable {
ConditionVariable() { cond_init(c); }
~ConditionVariable() { cond_destroy(c); }
void wait(Mutex& m) { cond_wait(c, m.l); }
void wait_for(Mutex& m, int ms) { timed_wait(c, m.l, ms); }
void notify_one() { cond_signal(c); }
private:
WaitCondition c;
};
class Thread;
struct SplitPoint { struct SplitPoint {
SplitPoint *parent;
// Const data after split point has been setup Position pos;
const Position* pos; SearchStack sstack[THREAD_MAX][PLY_MAX_PLUS_2];
const Search::Stack* ss; SearchStack *parentSstack;
int ply;
Depth depth; Depth depth;
Value beta; volatile Value alpha, beta, bestValue, futilityValue;
int nodeType; Value approximateEval;
Thread* master; bool pvNode;
Move threatMove; int master, slaves[THREAD_MAX];
Lock lock;
// Const pointers to shared data MovePicker *mp;
MovePicker* mp; volatile int moves;
SplitPoint* parent; volatile int cpus;
bool finished;
// Shared data
Mutex mutex;
volatile uint64_t slavesMask;
volatile int64_t nodes;
volatile Value alpha;
volatile Value bestValue;
volatile Move bestMove;
volatile int moveCount;
volatile bool cutoff;
}; };
/// Thread struct keeps together all the thread related stuff like locks, state struct Thread {
/// and especially split points. We also use per-thread pawn and material hash SplitPoint *splitPoint;
/// tables so that once we get a pointer to an entry its life time is unlimited volatile int activeSplitPoints;
/// and we don't have to care about someone changing the entry under our feet. uint64_t nodes;
uint64_t betaCutOffs[2];
class Thread { bool failHighPly1;
volatile bool stop;
typedef void (Thread::* Fn) (); // Pointer to member function volatile bool running;
volatile bool idle;
public: volatile bool workIsWaiting;
Thread(Fn fn); volatile bool printCurrentLine;
~Thread(); unsigned char pad[64]; // set some distance among local data for each thread
void wake_up();
bool cutoff_occurred() const;
bool is_available_to(Thread* master) const;
void idle_loop();
void main_loop();
void timer_loop();
void wait_for_stop_or_ponderhit();
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
MaterialTable materialTable;
PawnTable pawnTable;
size_t idx;
int maxPly;
Mutex mutex;
ConditionVariable sleepCondition;
NativeHandle handle;
Fn start_fn;
SplitPoint* volatile curSplitPoint;
volatile int splitPointsCnt;
volatile bool is_searching;
volatile bool do_sleep;
volatile bool do_exit;
}; };
/// ThreadPool 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 ThreadPool {
public:
void init(); // No c'tor and d'tor, threads rely on globals that should
void exit(); // be initialized and valid during the whole thread lifetime.
Thread& operator[](size_t id) { return *threads[id]; }
bool use_sleeping_threads() const { return useSleepingThreads; }
int min_split_depth() const { return minimumSplitDepth; }
size_t size() const { return threads.size(); }
Thread* main_thread() { return threads[0]; }
void wake_up() const;
void sleep() const;
void read_uci_options();
bool available_slave_exists(Thread* master) const;
void set_timer(int msec);
void wait_for_search_finished();
void start_searching(const Position&, const Search::LimitsType&,
const std::vector<Move>&, Search::StateStackPtr&);
template <bool Fake>
Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Move* bestMove,
Depth depth, Move threatMove, int moveCount, MovePicker* mp, int nodeType);
private:
friend class Thread;
std::vector<Thread*> threads;
Thread* timer;
Mutex mutex;
ConditionVariable sleepCondition;
Depth minimumSplitDepth;
int maxThreadsPerSplitPoint;
bool useSleepingThreads;
};
extern ThreadPool 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 "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, int slowMover);
}
void TimeManager::pv_instability(int curChanges, int prevChanges) {
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
+ prevChanges * (optimumSearchTime / 3);
}
void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color us)
{
/* 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"];
int slowMover = Options["Slow Mover"];
// Initialize to maximum values but unstablePVExtraTime that is reset
unstablePVExtraTime = 0;
optimumSearchTime = maximumSearchTime = limits.time[us];
// 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[us]
+ limits.inc[us] * (hypMTG - 1)
- emergencyBaseTime
- emergencyMoveTime * std::min(hypMTG, emergencyMoveHorizon);
hypMyTime = std::max(hypMyTime, 0);
t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly, slowMover);
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly, slowMover);
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, int slowMover)
{
const float TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
const float TStealRatio = (T == OptimumTime ? 0 : StealRatio);
int thisMoveImportance = move_importance(currentPly) * slowMover / 100;
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)));
}
}
+164 -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,18 +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>
#include "bitboard.h" ////
//// Includes
////
#include <cassert>
#include <cmath>
#include <cstring>
#include <xmmintrin.h>
#include "movegen.h"
#include "tt.h" #include "tt.h"
TranspositionTable TT; // Our global transposition table // The main transposition table
TranspositionTable TT;
////
//// Functions
////
TranspositionTable::TranspositionTable() { TranspositionTable::TranspositionTable() {
size = generation = 0; size = writes = 0;
entries = NULL; entries = 0;
generation = 0;
} }
TranspositionTable::~TranspositionTable() { TranspositionTable::~TranspositionTable() {
@@ -37,36 +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. Transposition table consists of a power of 2 number of /// measured in megabytes.
/// TTCluster and each cluster consists of ClusterSize number of TTEntries. Each
/// non-empty entry contains information of exactly one position.
void TranspositionTable::set_size(size_t mbSize) { void TranspositionTable::set_size(size_t mbSize) {
size_t newSize = 1ULL << msb((mbSize << 20) / sizeof(TTCluster)); assert(mbSize >= 4 && mbSize <= 8192);
if (newSize == size) size_t newSize = 1024;
return;
size = newSize; // We store a cluster of ClusterSize number of TTEntry for each position
delete [] entries; // and newSize is the maximum number of storable positions.
entries = new (std::nothrow) TTCluster[size]; while ((2 * newSize) * sizeof(TTCluster) <= (mbSize << 20))
newSize *= 2;
if (!entries) if (newSize != size)
{ {
std::cerr << "Failed to allocate " << mbSize size = newSize;
<< "MB for transposition table." << std::endl; delete [] entries;
exit(EXIT_FAILURE); entries = new TTCluster[size];
if (!entries)
{
std::cerr << "Failed to allocate " << mbSize
<< " MB for transposition table." << std::endl;
Application::exit_with_failure();
}
clear();
} }
clear(); // Operator new is not guaranteed to initialize memory to zero
} }
/// 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() {
@@ -74,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, Bound 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() == BOUND_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);
@@ -131,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))));
} }
+57 -69
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,120 +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 "misc.h" ////
#include "types.h" //// Includes
////
/// The TTEntry is the class of transposition table entries #include "depth.h"
#include "position.h"
#include "value.h"
////
//// 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, Bound b, 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_); }
bound = (uint8_t)b; 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; }
Bound type() const { return (Bound)bound; }
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 bound, 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, Bound 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);
}
#endif // !defined(TT_H_INCLUDED) #endif // !defined(TT_H_INCLUDED)
+37 -454
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,477 +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.
#include <cctype> #include <inttypes.h>
#include <climits>
#include <cstdlib>
#include "platform.h"
#if defined(_WIN64)
# include <intrin.h> // MSVC popcnt and bsfq instrinsics
# define IS_64BIT
# define USE_BSFQ
#endif
#if defined(USE_POPCNT) && defined(_MSC_VER) && defined(__INTEL_COMPILER)
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
#endif
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
# define CACHE_LINE_ALIGNMENT __declspec(align(64))
#else #else
# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
#endif
#if defined(_MSC_VER) typedef __int8 int8_t;
# define FORCE_INLINE __forceinline typedef unsigned __int8 uint8_t;
#elif defined(__GNUC__) typedef __int16 int16;
# define FORCE_INLINE inline __attribute__((always_inline)) typedef unsigned __int16 uint16_t;
#else typedef __int32 int32_t;
# define FORCE_INLINE inline typedef unsigned __int32 uint32_t;
#endif typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#if defined(USE_POPCNT) typedef __int16 int16_t;
const bool HasPopCnt = true; typedef __int64 int64_t;
#else
const bool HasPopCnt = false;
#endif
#if defined(IS_64BIT) #endif // !defined(_MSC_VER)
const bool Is64Bit = true;
#else
const bool Is64Bit = false;
#endif
// Hash keys
typedef uint64_t Key; typedef uint64_t Key;
// Bitboard type
typedef uint64_t Bitboard; typedef uint64_t Bitboard;
const int MAX_MOVES = 192;
const int MAX_PLY = 100;
const int MAX_PLY_PLUS_2 = MAX_PLY + 2;
const Bitboard FileABB = 0x0101010101010101ULL; ////
const Bitboard FileBBB = FileABB << 1; //// Compiler specific defines
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
};
enum MoveType {
NORMAL = 0,
PROMOTION = 1 << 14,
ENPASSANT = 2 << 14,
CASTLE = 3 << 14
};
enum CastleRight { // Defined as in PolyGlot book hash key
CASTLES_NONE = 0,
WHITE_OO = 1,
WHITE_OOO = 2,
BLACK_OO = 4,
BLACK_OOO = 8,
ALL_CASTLES = 15
};
enum CastlingSide {
KING_SIDE,
QUEEN_SIDE
};
enum ScaleFactor {
SCALE_FACTOR_DRAW = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
enum Bound {
BOUND_NONE = 0,
BOUND_UPPER = 1,
BOUND_LOWER = 2,
BOUND_EXACT = BOUND_UPPER | BOUND_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,
Mg = 0, Eg = 1,
PawnValueMg = 198, PawnValueEg = 258,
KnightValueMg = 817, KnightValueEg = 846,
BishopValueMg = 836, BishopValueEg = 857,
RookValueMg = 1270, RookValueEg = 1278,
QueenValueMg = 2521, QueenValueEg = 2558
};
enum PieceType {
NO_PIECE_TYPE = 0, ALL_PIECES = 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 = -5 * 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));
}
// Quiet a warning on Intel compiler
#if !defined(__SIZEOF_INT__ )
#define __SIZEOF_INT__ 0
#endif #endif
#define ENABLE_SAFE_OPERATORS_ON(T) \ // Check for 64 bits for different compilers: Intel, MSVC and gcc
inline T operator+(const T d1, const T d2) { return T(int(d1) + int(d2)); } \ #if defined(__x86_64) || defined(_M_X64) || defined(_WIN64) || (__SIZEOF_INT__ > 4)
inline T operator-(const T d1, const T d2) { return T(int(d1) - int(d2)); } \ #define IS_64BIT
inline T operator*(int i, const T d) { return T(i * int(d)); } \ #endif
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) \ #if defined(IS_64BIT) && (defined(__GNUC__) || defined(__INTEL_COMPILER))
inline T operator++(T& d, int) { d = T(int(d) + 1); return d; } \ #define USE_BSFQ
inline T operator--(T& d, int) { d = T(int(d) - 1); return d; } \ #endif
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) // Cache line alignment specification
ENABLE_OPERATORS_ON(PieceType) #if defined(_MSC_VER) || defined(__INTEL_COMPILER)
ENABLE_OPERATORS_ON(Piece) #define CACHE_LINE_ALIGNMENT __declspec(align(64))
ENABLE_OPERATORS_ON(Color) #else
ENABLE_OPERATORS_ON(Depth) #define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
ENABLE_OPERATORS_ON(Square) #endif
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);
}
/// Weight score v by score w trying to prevent overflow
inline Score apply_weight(Score v, Score w) {
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
(int(eg_value(v)) * eg_value(w)) / 0x100);
}
#undef ENABLE_OPERATORS_ON
#undef ENABLE_SAFE_OPERATORS_ON
namespace Zobrist {
extern Key psq[2][8][64]; // [color][pieceType][square / piece count]
extern Key enpassant[8]; // [file]
extern Key castle[16]; // [castleRight]
extern Key side;
extern Key exclusion;
void init();
}
CACHE_LINE_ALIGNMENT
extern Score pieceSquareTable[16][64]; // [piece][square]
extern Value PieceValue[2][18]; // [Mg / Eg][piece / pieceType]
extern int SquareDistance[64][64]; // [square][square]
struct MoveStack {
Move move;
int score;
};
inline bool operator<(const MoveStack& f, const MoveStack& s) {
return f.score < s.score;
}
inline Color operator~(Color c) {
return Color(c ^ 1);
}
inline Square operator~(Square s) {
return Square(s ^ 56); // Vertical flip SQ_A1 -> SQ_A8
}
inline Square operator|(File f, Rank r) {
return Square((r << 3) | f);
}
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 CastleRight make_castle_right(Color c, CastlingSide s) {
return CastleRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c));
}
inline PieceType type_of(Piece p) {
return PieceType(p & 7);
}
inline Color color_of(Piece p) {
return Color(p >> 3);
}
inline bool is_ok(Square s) {
return s >= SQ_A1 && s <= SQ_H8;
}
inline File file_of(Square s) {
return File(s & 7);
}
inline Rank rank_of(Square s) {
return Rank(s >> 3);
}
inline Square mirror(Square s) {
return Square(s ^ 7); // Horizontal flip SQ_A1 -> SQ_H1
}
inline Square relative_square(Color c, Square s) {
return Square(s ^ (c * 56));
}
inline Rank relative_rank(Color c, Rank r) {
return Rank(r ^ (c * 7));
}
inline Rank relative_rank(Color c, Square s) {
return relative_rank(c, rank_of(s));
}
inline bool opposite_colors(Square s1, Square s2) {
int s = int(s1) ^ int(s2);
return ((s >> 3) ^ s) & 1;
}
inline 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 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 MoveType type_of(Move m) {
return MoveType(m & (3 << 14));
}
inline PieceType promotion_type(Move m) {
return PieceType(((m >> 12) & 3) + 2);
}
inline Move make_move(Square from, Square to) {
return Move(to | (from << 6));
}
template<MoveType T>
inline Move make(Square from, Square to, PieceType pt = KNIGHT) {
return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12)) ;
}
inline bool is_ok(Move m) {
return from_sq(m) != to_sq(m); // 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 first, K last)
{
T tmp;
K p, q;
for (p = first + 1; p < last; p++)
{
tmp = *p;
for (q = p; q != first && *(q-1) < tmp; --q)
*q = *(q-1);
*q = tmp;
}
}
#endif // !defined(TYPES_H_INCLUDED) #endif // !defined(TYPES_H_INCLUDED)
+243 -170
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,236 +17,309 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
////
//// Includes
////
#include <cassert>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include "book.h"
#include "evaluate.h" #include "evaluate.h"
#include "notation.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 "tt.h"
#include "ucioption.h" #include "ucioption.h"
using namespace std; using namespace std;
extern void benchmark(const Position& pos, istream& is); ////
//// Local definitions:
////
namespace { namespace {
// FEN string of the initial position, normal chess // UCIInputParser is a class for parsing UCI input. The class
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; // is actually a string stream built on a given input string.
// Keep track of position keys along the setup moves (from start position to the typedef istringstream UCIInputParser;
// position just before to start searching). Needed by repetition draw detection.
Search::StateStackPtr SetupStates;
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.
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(const string& args) { /// 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, Threads.main_thread()); // The root position void uci_main_loop() {
string cmd, token;
while (token != "quit") RootPosition.from_fen(StartPosition);
{ string command;
if (!args.empty())
cmd = args;
else if (!getline(cin, cmd)) // Block here waiting for input do {
cmd = "quit"; // Wait for a command from stdin
if (!getline(cin, command))
command = "quit";
istringstream is(cmd); } while (handle_command(command));
is >> skipws >> token;
if (token == "quit" || token == "stop")
{
Search::Signals.stop = true;
Threads.wait_for_search_finished(); // Cannot quit while threads are running
}
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)
{
Search::Signals.stop = true;
Threads.main_thread()->wake_up(); // Could be sleeping
}
}
else if (token == "go")
go(pos, is);
else if (token == "ucinewgame")
TT.clear();
else if (token == "isready")
sync_cout << "readyok" << sync_endl;
else if (token == "position")
set_position(pos, is);
else if (token == "setoption")
set_option(is);
else if (token == "d")
pos.print();
else if (token == "flip")
pos.flip();
else if (token == "eval")
sync_cout << Eval::trace(pos) << sync_endl;
else if (token == "bench")
benchmark(pos, is);
else if (token == "key")
sync_cout << "key: " << hex << pos.key()
<< "\nmaterial key: " << pos.material_key()
<< "\npawn key: " << pos.pawn_key() << sync_endl;
else if (token == "uci")
sync_cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << sync_endl;
else if (token == "perft" && (is >> token)) // Read depth
{
stringstream ss;
ss << Options["Hash"] << " "
<< Options["Threads"] << " " << token << " current perft";
benchmark(pos, ss);
}
else
sync_cout << "Unknown command: " << cmd << sync_endl;
if (!args.empty()) // Command line arguments have one-shot behaviour
{
Threads.wait_for_search_finished();
break;
}
}
} }
////
//// Local functions
////
namespace { namespace {
// set_position() is called when engine receives the "position" UCI command. // handle_command() takes a text string as input, uses a
// The function sets up the position described in the given fen string ("fen") // UCIInputParser object to parse this text string as a UCI command,
// or the starting position ("startpos") and then makes the moves given in the // and calls the appropriate functions. In addition to the UCI
// following move list ("moves"). // commands, the function also supports a few debug commands.
void set_position(Position& pos, istringstream& is) { bool handle_command(const string& command) {
Move m; UCIInputParser uip(command);
string token, fen; string token;
is >> token; if (!(uip >> token)) // operator>>() skips any whitespace
return true;
if (token == "startpos") if (token == "quit")
return false;
if (token == "go")
return go(uip);
if (token == "uci")
{ {
fen = StartFEN; cout << "id name " << engine_name()
is >> token; // Consume "moves" token if any << "\nid author Tord Romstad, Marco Costalba, Joona Kiiski\n";
print_uci_options();
cout << "uciok" << endl;
} }
else if (token == "fen") else if (token == "ucinewgame")
while (is >> token && token != "moves") {
fen += token + " "; push_button("New Game");
Position::init_piece_square_tables();
RootPosition.from_fen(StartPosition);
}
else if (token == "isready")
cout << "readyok" << endl;
else if (token == "position")
set_position(uip);
else if (token == "setoption")
set_option(uip);
// The remaining commands are for debugging purposes only.
// Perhaps they should be removed later in order to reduce the
// size of the program binary.
else if (token == "d")
RootPosition.print();
else if (token == "flip")
{
Position p(RootPosition);
RootPosition.flipped_copy(p);
}
else if (token == "eval")
{
EvalInfo ei;
cout << "Incremental mg: " << mg_value(RootPosition.value())
<< "\nIncremental eg: " << eg_value(RootPosition.value())
<< "\nFull eval: " << evaluate(RootPosition, ei, 0) << endl;
}
else if (token == "key")
cout << "key: " << hex << RootPosition.get_key()
<< "\nmaterial key: " << RootPosition.get_material_key()
<< "\npawn key: " << RootPosition.get_pawn_key() << endl;
else if (token == "perft")
perft(uip);
else else
cout << "Unknown command: " << command << endl;
return true;
}
// set_position() is called when Stockfish receives the "position" UCI
// command. The input parameter is a UCIInputParser. It is assumed
// that this parser has consumed the first token of the UCI command
// ("position"), and is ready to read the second token ("startpos"
// or "fen", if the input is well-formed).
void set_position(UCIInputParser& uip) {
string token;
if (!(uip >> token)) // operator>>() skips any whitespace
return; return;
pos.from_fen(fen, Options["UCI_Chess960"], Threads.main_thread()); if (token == "startpos")
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>()); RootPosition.from_fen(StartPosition);
else if (token == "fen")
// Parse move list (if any)
while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE)
{ {
SetupStates->push(StateInfo()); string fen;
pos.do_move(m, SetupStates->top()); while (uip >> token && token != "moves")
{
fen += token;
fen += ' ';
}
RootPosition.from_fen(fen);
}
if (uip.good())
{
if (token != "moves")
uip >> token;
if (token == "moves")
{
Move move;
StateInfo st;
while (uip >> token)
{
move = move_from_string(RootPosition, token);
RootPosition.do_move(move, st);
if (RootPosition.rule_50_counter() == 0)
RootPosition.reset_game_ply();
}
// Our StateInfo st is about going out of scope so copy
// its content inside RootPosition before they disappear.
RootPosition.saveState();
}
} }
} }
// 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
return;
// Read option name (can contain spaces) if (token == "name" && uip >> name)
while (is >> token && token != "value") {
name += string(" ", !name.empty()) + token; while (uip >> token && token != "value")
name += (" " + token);
// Read option value (can contain spaces) if (token == "value" && uip >> value)
while (is >> token) {
value += string(" ", !value.empty()) + token; while (uip >> token)
value += (" " + token);
if (Options.count(name)) set_option_value(name, value);
Options[name] = value; } else
else push_button(name);
sync_cout << "No such option: " << name << sync_endl; }
} }
// go() is called when engine receives the "go" UCI command. The function sets // go() is called when Stockfish receives the "go" UCI command. The
// the thinking time and other parameters from the input string, and then starts // input parameter is a UCIInputParser. It is assumed that this
// the search. // parser has consumed the first token of the UCI command ("go"),
// and is ready to read the second token. The function sets the
// thinking time and other parameters from the input string, and
// calls think() (defined in search.cpp) with the appropriate
// parameters. Returns false if a quit command is received while
// thinking, returns true otherwise.
void go(Position& pos, istringstream& is) { bool go(UCIInputParser& uip) {
Search::LimitsType limits;
vector<Move> searchMoves;
string token; string token;
while (is >> 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 == "wtime") if (token == "infinite")
is >> limits.time[WHITE]; infinite = true;
else if (token == "btime")
is >> limits.time[BLACK];
else if (token == "winc")
is >> limits.inc[WHITE];
else if (token == "binc")
is >> limits.inc[BLACK];
else if (token == "movestogo")
is >> limits.movestogo;
else if (token == "depth")
is >> limits.depth;
else if (token == "nodes")
is >> limits.nodes;
else if (token == "movetime")
is >> limits.movetime;
else if (token == "infinite")
limits.infinite = true;
else if (token == "ponder") else if (token == "ponder")
limits.ponder = true; 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") else if (token == "searchmoves")
while (is >> token) {
searchMoves.push_back(move_from_uci(pos, token)); int numOfMoves = 0;
while (uip >> token)
searchMoves[numOfMoves++] = move_from_string(RootPosition, token);
searchMoves[numOfMoves] = MOVE_NONE;
}
} }
Threads.start_searching(pos, limits, searchMoves, SetupStates); 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
<< "\nTime (ms) " << tm
<< "\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)
+305 -110
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,150 +17,345 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
////
//// Includes
////
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cstdlib> #include <map>
#include <string>
#include <sstream> #include <sstream>
#include <vector>
#include "evaluate.h"
#include "misc.h" #include "misc.h"
#include "thread.h" #include "thread.h"
#include "tt.h"
#include "ucioption.h" #include "ucioption.h"
using std::string; using std::string;
UCI::OptionsMap Options; // Global object ////
//// Local definitions
////
namespace UCI { namespace {
/// 'On change' actions, triggered by an option's value change ///
void on_logger(const Option& o) { start_logger(o); } /// Types
void on_eval(const Option&) { Eval::init(); } ///
void on_threads(const Option&) { Threads.read_uci_options(); }
void on_hash_size(const Option& o) { TT.set_size(o); } enum OptionType { SPIN, COMBO, CHECK, STRING, BUTTON };
void on_clear_hash(const Option&) { TT.clear(); }
typedef std::vector<string> ComboValues;
struct Option {
string name, defaultValue, currentValue;
OptionType type;
size_t idx;
int minValue, maxValue;
ComboValues comboValues;
Option();
Option(const char* defaultValue, OptionType = STRING);
Option(bool defaultValue, OptionType = CHECK);
Option(int defaultValue, int minValue, int maxValue);
bool operator<(const Option& o) const { return this->idx < o.idx; }
};
typedef std::map<string, Option> Options;
///
/// Constants
///
// load_defaults populates the options map with the hard
// coded names and default values.
void load_defaults(Options& o) {
o["Use Search Log"] = Option(false);
o["Search Log Filename"] = Option("SearchLog.txt");
o["Book File"] = Option("book.bin");
o["Mobility (Middle Game)"] = Option(100, 0, 200);
o["Mobility (Endgame)"] = Option(100, 0, 200);
o["Pawn Structure (Middle Game)"] = Option(100, 0, 200);
o["Pawn Structure (Endgame)"] = Option(100, 0, 200);
o["Passed Pawns (Middle Game)"] = Option(100, 0, 200);
o["Passed Pawns (Endgame)"] = Option(100, 0, 200);
o["Space"] = Option(100, 0, 200);
o["Aggressiveness"] = Option(100, 0, 200);
o["Cowardice"] = Option(100, 0, 200);
o["King Safety Curve"] = Option("Quadratic", COMBO);
o["King Safety Curve"].comboValues.push_back("Quadratic");
o["King Safety Curve"].comboValues.push_back("Linear"); /*, "From File"*/
o["King Safety Coefficient"] = Option(40, 1, 100);
o["King Safety X Intercept"] = Option(0, 0, 20);
o["King Safety Max Slope"] = Option(30, 10, 100);
o["King Safety Max Value"] = Option(500, 100, 1000);
o["Queen Contact Check Bonus"] = Option(3, 0, 8);
o["Queen Check Bonus"] = Option(2, 0, 4);
o["Rook Check Bonus"] = Option(1, 0, 4);
o["Bishop Check Bonus"] = Option(1, 0, 4);
o["Knight Check Bonus"] = Option(1, 0, 4);
o["Discovered Check Bonus"] = Option(3, 0, 8);
o["Mate Threat Bonus"] = Option(3, 0, 8);
o["Check Extension (PV nodes)"] = Option(2, 0, 2);
o["Check Extension (non-PV nodes)"] = Option(1, 0, 2);
o["Single Reply Extension (PV nodes)"] = Option(2, 0, 2);
o["Single Reply Extension (non-PV nodes)"] = Option(2, 0, 2);
o["Mate Threat Extension (PV nodes)"] = Option(0, 0, 2);
o["Mate Threat Extension (non-PV nodes)"] = Option(0, 0, 2);
o["Pawn Push to 7th Extension (PV nodes)"] = Option(1, 0, 2);
o["Pawn Push to 7th Extension (non-PV nodes)"] = Option(1, 0, 2);
o["Passed Pawn Extension (PV nodes)"] = Option(1, 0, 2);
o["Passed Pawn Extension (non-PV nodes)"] = Option(0, 0, 2);
o["Pawn Endgame Extension (PV nodes)"] = Option(2, 0, 2);
o["Pawn Endgame Extension (non-PV nodes)"] = Option(2, 0, 2);
o["Full Depth Moves (PV nodes)"] = Option(10, 1, 100);
o["Full Depth Moves (non-PV nodes)"] = Option(3, 1, 100);
o["Threat Depth"] = Option(5, 0, 100);
o["Randomness"] = Option(0, 0, 10);
o["Minimum Split Depth"] = Option(4, 4, 7);
o["Maximum Number of Threads per Split Point"] = Option(5, 4, 8);
o["Threads"] = Option(1, 1, THREAD_MAX);
o["Hash"] = Option(32, 4, 8192);
o["Clear Hash"] = Option(false, BUTTON);
o["New Game"] = Option(false, BUTTON);
o["Ponder"] = Option(true);
o["OwnBook"] = Option(true);
o["MultiPV"] = Option(1, 1, 500);
o["UCI_ShowCurrLine"] = Option(false);
o["UCI_Chess960"] = Option(false);
o["UCI_AnalyseMode"] = Option(false);
// Any option should know its name so to be easily printed
for (Options::iterator it = o.begin(); it != o.end(); ++it)
it->second.name = it->first;
}
///
/// Variables
///
Options options;
// stringify converts a value of type T to a std::string
template<typename T>
string stringify(const T& v) {
std::ostringstream ss;
ss << v;
return ss.str();
}
/// Our case insensitive less() function as required by UCI protocol // get_option_value implements the various get_option_value_<type>
bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); } // functions defined later, because only the option value
// type changes a template seems a proper solution.
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { template<typename T>
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less); T get_option_value(const string& optionName) {
T ret = T();
if (options.find(optionName) == options.end())
return ret;
std::istringstream ss(options[optionName].currentValue);
ss >> ret;
return ret;
}
// Specialization for std::string where instruction 'ss >> ret;'
// would erroneusly tokenize a string with spaces.
template<>
string get_option_value<string>(const string& optionName) {
if (options.find(optionName) == options.end())
return string();
return options[optionName].currentValue;
}
}
////
//// Functions
////
/// init_uci_options() initializes the UCI options. Currently, the only
/// thing this function does is to initialize the default value of the
/// "Threads" parameter to the number of available CPU cores.
void init_uci_options() {
load_defaults(options);
// Set optimal value for parameter "Minimum Split Depth"
// according to number of available cores.
assert(options.find("Threads") != options.end());
assert(options.find("Minimum Split Depth") != options.end());
Option& thr = options["Threads"];
Option& msd = options["Minimum Split Depth"];
thr.defaultValue = thr.currentValue = stringify(cpu_count());
if (cpu_count() >= 8)
msd.defaultValue = msd.currentValue = stringify(7);
} }
/// init() initializes the UCI options to their hard coded default values /// print_uci_options() prints all the UCI options to the standard output,
/// and initializes the default value of "Threads" and "Min Split Depth" /// in the format defined by the UCI protocol.
/// parameters according to the number of CPU cores detected.
void init(OptionsMap& o) { void print_uci_options() {
int cpus = std::min(cpu_count(), MAX_THREADS); static const char optionTypeName[][16] = {
int msd = cpus < 8 ? 4 : 7; "spin", "combo", "check", "string", "button"
};
o["Use Debug Log"] = Option(false, on_logger); // Build up a vector out of the options map and sort it according to idx
o["Use Search Log"] = Option(false); // field, that is the chronological insertion order in options map.
o["Search Log Filename"] = Option("SearchLog.txt"); std::vector<Option> vec;
o["Book File"] = Option("book.bin"); for (Options::const_iterator it = options.begin(); it != options.end(); ++it)
o["Best Book Move"] = Option(false); vec.push_back(it->second);
o["Mobility (Middle Game)"] = Option(100, 0, 200, on_eval);
o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval); std::sort(vec.begin(), vec.end());
o["Passed Pawns (Middle Game)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval); for (std::vector<Option>::const_iterator it = vec.begin(); it != vec.end(); ++it)
o["Space"] = Option(100, 0, 200, on_eval); {
o["Aggressiveness"] = Option(100, 0, 200, on_eval); std::cout << "\noption name " << it->name
o["Cowardice"] = Option(100, 0, 200, on_eval); << " type " << optionTypeName[it->type];
o["Min Split Depth"] = Option(msd, 4, 7, on_threads);
o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads); if (it->type == BUTTON)
o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads); continue;
o["Use Sleeping Threads"] = Option(true, on_threads);
o["Hash"] = Option(32, 4, 8192, on_hash_size); if (it->type == CHECK)
o["Clear Hash"] = Option(on_clear_hash); std::cout << " default " << (it->defaultValue == "1" ? "true" : "false");
o["Ponder"] = Option(true); else
o["OwnBook"] = Option(false); std::cout << " default " << it->defaultValue;
o["MultiPV"] = Option(1, 1, 500);
o["Skill Level"] = Option(20, 0, 20); if (it->type == SPIN)
o["Emergency Move Horizon"] = Option(40, 0, 50); std::cout << " min " << it->minValue << " max " << it->maxValue;
o["Emergency Base Time"] = Option(200, 0, 30000); else if (it->type == COMBO)
o["Emergency Move Time"] = Option(70, 0, 5000); for (ComboValues::const_iterator itc = it->comboValues.begin();
o["Minimum Thinking Time"] = Option(20, 0, 5000); itc != it->comboValues.end(); ++itc)
o["Slow Mover"] = Option(100, 10, 1000); std::cout << " var " << *itc;
o["UCI_Chess960"] = Option(false); }
o["UCI_AnalyseMode"] = Option(false, on_eval); std::cout << std::endl;
} }
/// operator<<() is used to print all the options default values in chronological /// get_option_value_bool() returns the current value of a UCI parameter of
/// insertion order (the idx field) and in the format defined by the UCI protocol. /// type "check".
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { bool get_option_value_bool(const string& optionName) {
for (size_t idx = 0; idx < om.size(); idx++) return get_option_value<bool>(optionName);
for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it)
if (it->second.idx == idx)
{
const Option& o = it->second;
os << "\noption name " << it->first << " type " << o.type;
if (o.type != "button")
os << " default " << o.defaultValue;
if (o.type == "spin")
os << " min " << o.min << " max " << o.max;
break;
}
return os;
} }
/// Option c'tors and conversion operators /// get_option_value_int() returns the value of a UCI parameter as an integer.
/// Normally, this function will be used for a parameter of type "spin", but
/// it could also be used with a "combo" parameter, where all the available
/// values are integers.
Option::Option(const char* v, Fn* f) : type("string"), min(0), max(0), idx(Options.size()), on_change(f) int get_option_value_int(const string& optionName) {
{ defaultValue = currentValue = v; }
Option::Option(bool v, Fn* f) : type("check"), min(0), max(0), idx(Options.size()), on_change(f) return get_option_value<int>(optionName);
{ defaultValue = currentValue = (v ? "true" : "false"); }
Option::Option(Fn* f) : type("button"), min(0), max(0), idx(Options.size()), on_change(f)
{}
Option::Option(int v, int minv, int maxv, Fn* f) : type("spin"), min(minv), max(maxv), idx(Options.size()), on_change(f)
{ std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); }
Option::operator int() const {
assert(type == "check" || type == "spin");
return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true");
}
Option::operator std::string() const {
assert(type == "string");
return currentValue;
} }
/// operator=() updates currentValue and triggers on_change() action. It's up to /// get_option_value_string() returns the current value of a UCI parameter as
/// the GUI to check for option's limits, but we could receive the new value from /// a string. It is used with parameters of type "combo" and "string".
/// the user by console window, so let's check the bounds anyway.
Option& Option::operator=(const string& v) { string get_option_value_string(const string& optionName) {
assert(!type.empty()); return get_option_value<string>(optionName);
if ( (type != "button" && v.empty())
|| (type == "check" && v != "true" && v != "false")
|| (type == "spin" && (atoi(v.c_str()) < min || atoi(v.c_str()) > max)))
return *this;
if (type != "button")
currentValue = v;
if (on_change)
(*on_change)(*this);
return *this;
} }
} // namespace UCI
/// 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) {}
}
+17 -42
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,53 +17,28 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(UCIOPTION_H_INCLUDED) #if !defined(UCIOPTION_H_INCLUDED)
#define UCIOPTION_H_INCLUDED #define UCIOPTION_H_INCLUDED
#include <map> ////
//// Includes
////
#include <string> #include <string>
namespace UCI { ////
//// Prototypes
////
class Option; extern void init_uci_options();
extern void print_uci_options();
extern bool get_option_value_bool(const std::string& optionName);
extern int get_option_value_int(const std::string& optionName);
extern std::string get_option_value_string(const std::string& optionName);
extern bool button_was_pressed(const std::string& buttonName);
extern void set_option_value(const std::string& optionName,const std::string& newValue);
extern void push_button(const std::string& buttonName);
/// Custom comparator because UCI options should be case insensitive
struct CaseInsensitiveLess {
bool operator() (const std::string&, const std::string&) const;
};
/// Our options container is actually a std::map
typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap;
/// Option class implements an option as defined by UCI protocol
class Option {
typedef void (Fn)(const Option&);
public:
Option(Fn* = NULL);
Option(bool v, Fn* = NULL);
Option(const char* v, Fn* = NULL);
Option(int v, int min, int max, Fn* = NULL);
Option& operator=(const std::string& v);
operator int() const;
operator std::string() const;
private:
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
std::string defaultValue, currentValue, type;
int min, max;
size_t idx;
Fn* on_change;
};
void init(OptionsMap&);
void loop(const std::string&);
} // namespace UCI
extern UCI::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)