Compare commits

...

236 Commits

Author SHA1 Message Date
Joona Kiiski 5b555525d2 Stockfish 6
Stockfish bench signature is: 8918745
2015-01-27 20:27:38 +00:00
Joona Kiiski c4518e395e Stockfish 6 Release Candidate 3
- Fix a skill level problem: Don't allow move pruning at root node
- Revert "Fix profile build for gcc on Mac OSX". Results for a faster binary in x86-64.
- Fix a MSVC warning

Bench: 8918745
2015-01-25 22:03:57 +00:00
Stefan Geschwentner d8b3ad2208 Fix a skill level problem: Don't allow move pruning at root node
Bench: 8918745

Resolves #231
2015-01-25 21:57:13 +00:00
Joona Kiiski ec36b8dea9 Revert "Fix profile build for gcc on Mac OSX"
Seems to be a performance regression for standard build.

For SF6 people compiling on Mac OSX using profile-build option
just need to make necessary adjustments manually...

No functional change

Resolves #223
2015-01-25 21:51:09 +00:00
Marco Costalba 8ebf30d44a Fix a MSVC warning at W4
Warning is C4512 (assignment operator could not be generated)

Now, apart the foreign syzygy code, everything compiles
without warnings at warning level 4.

Backported from C++11 branch.

No functional change.
2015-01-25 09:36:05 +01:00
Joona Kiiski 5154ac9cff Stockfish 6 Release Candidate 2
- Fix a compilation issue related to BMI2 PEXT instruction
- Retrieve a ponder move from TT if PV is only one move long

Bench: 8080602

No functional change
2015-01-24 19:42:49 +00:00
Marco Costalba 54b5b528d9 Don't use _pext_u64() directly
This intrinsic to call BMI2 PEXT instruction is
defined in immintrin.h. This header should be
included only when USE_PEXT is defined, otherwise
we define _pext_u64 as 0 forcing a nop.

But under some mingw platforms, even if we don't
include the header, immintrin.h gets included
anyhow through an include chain that starts with
STL <algorithm> header. So we end up both defining
_pext_u64 function and at the same time defining
_pext_u64 as 0 leading to a compile error.

The correct solution is of not using _pext_u64 directly.

This patch fixes a compile error with some mingw64
package when compiling with x86-64.

No functional change.

Resolves #222
2015-01-24 19:38:06 +00:00
Marco Costalba 44643c2770 Try hard to retrieve a ponder move
In case we stop the search during a fail-high
it is possible we return to GUI without a ponder
move. This patch try harder to find a ponder move
retrieving it from TT. This is important in games
played with 'ponder on'.

bench: 8080602

Resolves #221
2015-01-24 19:35:31 +00:00
Joona Kiiski 97a034ad3e Stockfish 6 Release Candidate 1
Bench: 8080602

No functional change

Resolves #218
2015-01-18 14:53:11 +00:00
Joona Kiiski 1b62b47b62 Fix profile build for gcc on Mac OSX
Switch back to using -fprofile-generate and
-fprofile-use flags

No functional change

Resolves #219
Resolves #210
2015-01-18 14:42:30 +00:00
Joona Kiiski 7f51610103 Don't print fail-high or fail-lows in MultiPV mode
Supposed to give a better user experience when using MultiPV mode

No functional change

Resolves #217
2015-01-18 14:38:46 +00:00
Marco Costalba c73f33f37e Fix syzygy warnings with Intel compiler
Quick hack, a better job would require to
first move all syzygy code up to SF standard.

No functional change.
2015-01-18 07:29:51 +01:00
Marco Costalba 05cb58f4fc Fix some missing rename from previous patch
No functional change.
2015-01-17 22:15:15 +01:00
Marco Costalba 595fc342cf Fix a possible overflow in TT resize
On platforms where size_t is 32 bit, we
can have an overflow in this expression:

(mbSize * 1024 * 1024)

Fix it setting max hash size of 2GB on platforms
where size_t is 32 bit.

A small rename while there: now struct Cluster
is definied inside class TranspositionTable so
we should drop the redundant TT prefix.

No functional change.
2015-01-17 21:42:47 +01:00
mstembera 58fdb84b0d Simplify and optimize value extractors
Speed up results by Joona:

gcc-4.7 (1.5%)
gcc-4.8 (0.5%)
gcc-4.9 (1.0%)

Speed up results by mstembera:

gcc 474
p-value: 0.719

gcc 482
p-value: 1

gcc 492
p-value: 0.859

No functional change

Resolves #211
2015-01-16 19:03:49 +00:00
lucasart ab276357d7 Enable futility pruning for PV nodes in qsearch
STC:

LLR: 4.20 (-2.94,2.94) [-3.00,1.00]
Total: 85573 W: 17195 L: 17125 D: 51253

LTC:

LLR: 2.94 (-2.94,2.94) [-3.00,1.00]
Total: 43385 W: 7298 L: 7214 D: 28873

Bench: 8080602

Resolves #206
2015-01-14 20:21:54 +00:00
Stefan Geschwentner 4abe333e1f Enable Futility pruning in PV nodes
STC:

LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 21553 W: 4342 L: 4221 D: 12990

LTC:

LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 7675 W: 1351 L: 1209 D: 5115

Bench: 8668014

Resolves #205
2015-01-14 20:13:48 +00:00
hxim 7b20bb6e1a Use VALUE_ZERO instead of 0 when comparing with Value
This is the correct practice.

No functional change

Resolves #208
2015-01-13 21:07:18 +00:00
Marco Costalba 4eb2d8ce09 Assorted headers cleanup
Mostly comments fixing and other small things.

No functional change.
2015-01-11 22:56:35 +01:00
Marco Costalba b97df4c236 Fix a crash of syzygy on Android
On Android-ARM current TB code crashes at
random times even in single thread mode.

Reported, debugged, fixed and verified
by Peter Osterlund.

No functional change.

Resolves #201
2015-01-11 20:10:31 +00:00
Stefano80 17bcdb9212 Higher movetime resolution for benchmarks
Use a millisecond movetime precision for benchmarks

No functional change

Resolves #198
2015-01-11 20:07:11 +00:00
Marco Costalba 202a78e8e4 Fix compile for Android 5
Android 5 can only run position independent executables.

Note that this breaks Android 4.0 and earlier.

See here for more info:
http://stackoverflow.com/questions/24818902/running-a-native-library-on-android-l-error-only-position-independent-executab

Thanks to Peter Osterlund for the support.

No functional change
2015-01-11 10:52:42 +01:00
Marco Costalba 42b48b08e8 Update copyright year
No functional change.
2015-01-10 11:46:28 +01:00
Marco Costalba aea2fde611 Assorted formatting and comment tweaks in position.h
No functional change.
2015-01-07 09:09:41 +01:00
lucasart b73ae56ee1 Tune null search reduction
STC

LLR: 2.96 (-2.94,2.94) [-0.50,4.50]
Total: 107289 W: 21851 L: 21325 D: 64113

LTC

LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 83837 W: 14378 L: 13916 D: 55543

Bench: 7604776

Resolves #194
2015-01-05 22:09:12 +00:00
Marco Costalba 3fda064a66 Retire one implementation of pop_lsb()
We have two implementations that are equivalent,
so retire one.

Plus usual tidy up of comments and code reshuffle.

No functional change.
2015-01-03 22:16:30 +01:00
lucasart a6e292034a Remove a useless optimization
This optimization is aimed at old hardware only (withouth popcount), and even on
non popcount compile (ARCH=x86-64), it provides no mesurable speedup:

stat        test     master     diff
mean   2,341,779  2,354,699  -12,920
stdev     12,910     14,770   18,150

speedup      -0.55%
P(speedup>0)  23.8%

No functional change.

Resolves #187
2015-01-03 20:40:57 +00:00
Joona Kiiski c7332d5610 Smoother king safety
STC:

LLR: 4.03 (-2.94,2.94) [-1.50,4.50]
Total: 35707 W: 7352 L: 7106 D: 21249

LTC:

LLR: 2.95 (-2.94,2.94) [0.00,4.00]
Total: 24563 W: 4330 L: 4095 D: 16138

Bench: 8411409

Resolves #190
2015-01-03 20:34:20 +00:00
Marco Costalba 9d1e2c0e76 Assorted work in uci.cpp
- Change UCI::value() signature

This function should only return the value,
lowerbound and upperbound info is up to the
caller because it requires external knowledge,
out of the scope of this little helper.

- Retire 'key' command

It is not an UCI command and is absolutely
useless: never used.

- Comments fixing and other trivia

No functional change.
2015-01-03 18:46:59 +01:00
Marco Costalba c150f07291 Fix a silly warning with Intel compiler
Intel compiler cries for an explicit cast

warning #2259: non-pointer conversion from "double" to "int"
may lose significant bits

No functional change.
2015-01-03 10:17:04 +01:00
Marco Costalba 62f531254e Fix comments in thread.cpp
And reshuffle a bit the functions to place
them in a consistent order.

To be on the safe side, patch has been
validated for no regression/crashes with
a small 8K games test with 3 threads:

ELO: 3.98 +-4.4 (95%) LOS: 96.3%
Total: 8388 W: 1500 L: 1404 D: 5484

No functional change.
2015-01-03 09:34:58 +01:00
Marco Costalba 91cc82aa25 Let material probing to access per-thread table
It is up to material (and pawn) table look up
code to know where the per-thread tables are,
so change API to reflect this.

Also some comment fixing while there

No functional change.
2015-01-02 21:31:02 +01:00
Matthew Lai 19b8249ff4 Disable the default copy constructor for Position class
No functional change

Resolved #183
2015-01-02 20:19:11 +00:00
Stefan Geschwentner 25b492ab58 Big King Safety tuning
All king safety related terms (shelterweakness, stormdanger,
attackunits, ..) was tuned together. Additionally for attack units a
finer granularity (factor 4) is used.

STC
ELO: 9.22 +-3.1 (95%) LOS: 100.0%
Total: 19514 W: 4340 L: 3822 D: 11352

LTC
LLR: 2.96 (-2.94,2.94) [0.00,6.00]
Total: 6399 W: 1192 L: 1056 D: 4151

Bench: 8224782

Resolves #184
2015-01-03 03:33:02 +08:00
Marco Costalba 4c9b423161 Retire Material::space_weight()
Move all in evaluation.

Simplify the code and concentrate in a single place
all the logic behind space evaluation, making it much
more clear.

Verified also at STC it does not regress due to a possible
slow down:

LLR: 3.91 (-2.94,2.94) [-3.00,1.00]
Total: 65744 W: 13285 L: 13194 D: 39265

No functional change.
2015-01-02 10:02:28 +01:00
Marco Costalba 2416242c96 Additional tidy up in timeman.cpp
Fixed some comments and moved/renamed some
variables.

No functional change.
2014-12-30 09:49:54 +01:00
Marco Costalba 6933f05f4b Use score and value consistently
And other assorted small fixing, code style
tweaks and reshuffles in evaluate.cpp

No functional change.
2014-12-28 19:06:56 +01:00
Arjun Temurnikar f9571e8d57 Remove some comments in timeman.cpp
Commenst are obsolete now, an updated description
would be quite obscure, so better let the code
to talk and remove them all together.

No functional change.
2014-12-28 11:58:29 +01:00
Marco Costalba 2bfacf136c Reformat promotion generation
Use the same template of other pawns moves generation,
make the code more uniform, simplify generate_promotions
that has now been renamed.

No functional change (verified also with perft).
2014-12-25 11:09:07 +01:00
Marco Costalba 296534f234 Prefer names to numbers in storm code
Also replaces some tabs with spaces and
change StormDanger order to reflect
ShelterWeakness one.

No functional change.
2014-12-22 08:33:07 +01:00
Joona Kiiski e5c7b44f7a Use "rm -f" instead of "rm" for gcc profiling hack in Makefile
In some UNIX systems "rm" prompts user for confirmation.
However "rm -f" is always a guaranteed forced deletion.

Also move gcc profiling hack under the correct target

No Functional change

Resolves #168
2014-12-20 17:55:18 +00:00
Stefan Geschwentner 3231038262 Big King Safety tuning
ShelterWeakness and Stormdanger array are now indexed additionally by
file pair (a/h,b/g,c/f,d/e). The special case of king blocking a pawn
is incorporated in the StormDanger array.  Finally the 93 parameters
are tuned by SPSA on LTC.

STC
ELO: 3.46 +-2.2 (95%) LOS: 99.9%
Total: 40000 W: 8275 L: 7877 D: 23848

LTC
LLR: 2.96 (-2.94,2.94) [0.00,6.00]
Total: 10311 W: 1876 L: 1721 D: 6714

Bench: 9498821

Resolves #163
2014-12-21 01:53:44 +08:00
Marco Costalba 9cae6e66ce Don't account for Tempo in specialized endgames
The evaluation is already done by the specialized
function, don't need to add something elese later.

With this patch following positions are evaluated
correctly as draws:

8/6p1/1Pkp1p1p/2nNn2P/2P1K1P1/8/8/3B4 w - - 7
8/1k4p1/1P1p1p1p/3NnK1P/2P3P1/1n6/4B3/8 w - -

Verified it not regress with an STC test:
LLR: 3.15 (-2.94,2.94) [-3.00,1.00]
Total: 49812 W: 10095 L: 10016 D: 29701

Reported by Arjun Temurnikar.

bench: 8289983
2014-12-19 11:06:40 +01:00
mstembera 46d5fff01f Change profile-build options to produce 1% to 2% faster executables.
The "@rm ucioption.gc*" line is necessary to avoid a gcc 4.7.x bug.
Confirmed for gcc 4.7.4, 4.8.1, and 4.9.1
Suggested by Kiran Panditrao on fishcooking forum.
https://groups.google.com/forum/?fromgroups=#!topic/fishcooking/AY8gN53nG18

No functional change.

Resolves #160
2014-12-19 03:57:04 +08:00
Marco Costalba b8fd1a78dc Improve comments in UCI
And simplify naming while there.

No functional change.

Resolves #159
2014-12-14 23:50:33 +00:00
Marco Costalba 413b243809 Coding style in TT code
In particular seems more natural to return
bool and TTEntry on the same line, actually
we should pass and return them as a pair,
but due to limitations of C++ and not wanting
to use std::pair this can be an acceptable
compromise.

No functional change.

Resolves #157
2014-12-14 23:49:00 +00:00
Gary Linscott 0edb6348d2 Fix compile for some versions of mingw
The bswap intrinsics are specific to the compiler, not the
host platform.

No functional change.

Resolves #155
2014-12-14 14:45:43 -05:00
mstembera 14cf27e6f6 Avoid searching TT twice for the same key/position during probe() and store().
Just keep the pointer and remove code from tt.cpp

STC
LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 13620 W: 2810 L: 2665 D: 8145

LTC
LLR: 2.97 (-2.94,2.94) [0.00,6.00]
Total: 13021 W: 2238 L: 2073 D: 8710STC http://tests.stockfishchess.org/tests/view/548436860ebc59331739b90c

STC 4MB
ELO: 2.41 +-2.2 (95%) LOS: 98.6%
Total: 40000 W: 8175 L: 7897 D: 23928

LTC 16MB
ELO: 1.78 +-2.0 (95%) LOS: 96.1%
Total: 39683 W: 6763 L: 6560 D: 26360

Resolves #151

Bench: 8116521
2014-12-13 07:22:37 +00:00
Gary Linscott 7b4828b68c Only use _ReadWriteBarrier on MSVC
It was causing compile errors when cross-compiling using mingw.

No functional change.
2014-12-11 14:56:24 -05:00
joergoster f6d220ab14 Halve StormDanger bonus for blocked pawn on A/H file
STC
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 3410 W: 758 L: 641 D: 2011

LTC
LLR: 2.95 (-2.94,2.94) [0.00,6.00]
Total: 33225 W: 5708 L: 5445 D: 22072

Bench: 8465811

Resolves #153
2014-12-11 13:08:29 -05:00
Gary Linscott afafdf7b73 MSVC compiling fixes
No functional change.

Resolves #150
2014-12-11 13:03:44 -05:00
Joona Kiiski b15dcd9774 Fix profile build for syzygy
Touch source files under syzygy directory to force recompilation
after collecting profile data.

No functional change

Resolves #149
2014-12-10 17:59:41 +00:00
Joona Kiiski 94dd204c3b Retire 'os' flag from Makefile
Appears to be unused

No functional change

Resolves #147
2014-12-10 17:57:55 +00:00
Marco Costalba 5943600a89 Assorted nitpicking code-style
No functional change.
2014-12-10 12:38:13 +01:00
Marco Costalba 589c711449 Clarify when forcing the moves loop
In some cases we want to go direcly to the moves loop
without checking for early return. The patch make this
logic more clear and consistent.

Tested for no regression, passed STC
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 25282 W: 5136 L: 5022 D: 15124

and LTC
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 72007 W: 12133 L: 12095 D: 47779

bench: 9316798
2014-12-10 12:35:21 +01:00
Ernesto Gatti 158864270a Simpler PRNG and faster magics search
This patch replaces RKISS by a simpler and faster PRNG, xorshift64* proposed
by S. Vigna (2014). It is extremely simple, has a large enough period for
Stockfish's needs (2^64), requires no warming-up (allowing such code to be
removed), and offers slightly better randomness than MT19937.

Paper: http://xorshift.di.unimi.it/
Reference source code (public domain):
http://xorshift.di.unimi.it/xorshift64star.c

The patch also simplifies how init_magics() searches for magics:

- Old logic: seed the PRNG always with the same seed,
  then use optimized bit rotations to tailor the RNG sequence per rank.

- New logic: seed the PRNG with an optimized seed per rank.

This has two advantages:
1. Less code and less computation to perform during magics search (not ROTL).
2. More choices for random sequence tuning. The old logic only let us choose
from 4096 bit rotation pairs. With the new one, we can look for the best seeds
among 2^64 values. Indeed, the set of seeds[][] provided in the patch reduces
the effort needed to find the magics:

64-bit SF:
Old logic -> 5,783,789 rand64() calls needed to find the magics
New logic -> 4,420,086 calls

32-bit SF:
Old logic -> 2,175,518 calls
New logic -> 1,895,955 calls

In the 64-bit case, init_magics() take 25 ms less to complete (Intel Core i5).

Finally, when playing with strength handicap, non-determinism is achieved
by setting the seed of the static RNG only once. Afterwards, there is no need
to skip output values.

The bench only changes because the Zobrist keys are now different (since they
are random numbers straight out of the PRNG).

The RNG seed has been carefully chosen so that the
resulting Zobrist keys are particularly well-behaved:

1. All triplets of XORed keys are unique, implying that it
   would take at least 7 keys to find a 64-bit collision
   (test suggested by ceebo)

2. All pairs of XORed keys are unique modulo 2^32

3. The cardinality of { (key1 ^ key2) >> 48 } is as close
   as possible to the maximum (65536)

Point 2 aims at ensuring a good distribution among the bits
that determine an TT entry's cluster, likewise point 3
among the bits that form the TT entry's key16 inside a
cluster.

Details:

     Bitset   card(key1^key2)
     ------   ---------------
RKISS
     key16     64894   = 99.020% of theoretical maximum
     low18    180117   = 99.293%
     low32    305362   = 99.997%

Xorshift64*, old seed
     key16     64918   = 99.057%
     low18    179994   = 99.225%
     low32    305350   = 99.993%

Xorshift64*, new seed
     key16     65027   = 99.223%
     low18    181118   = 99.845%
     low32    305371   = 100.000%

Bench: 9324905

Resolves #148
2014-12-08 08:18:26 +08:00
Gary Linscott a87da2c4b3 Add some tablebase positions to bench
This makes it easier to check for regressions in the tablebase code.

Bench: 9489202
5-man bench: 8943906

Resolves #145
2014-12-08 07:58:05 +08:00
hxim fbb53524ef Rename some variables for more clarity.
No functional change.

Resolves #131
2014-12-08 07:53:33 +08:00
Marco Costalba ba1464751d Explicitly pass RootMoves to TB probes
Currently Search::RootMoves is accessed and even
modified by TB probing functions in a hidden
and sneaky way.

This is bad practice and makes the code tricky.
Instead explicily pass the vector as function
argument so to clarify that the vector is modified
inside the functions.

No functional change.
2014-12-06 15:08:21 +00:00
Marco Costalba eeb6d923fa Move TB stuff under Tablebases namespace
Simplified also some logic while there.

TBLargest needs renaming too, but itis for
a future patch because touches also syzygy
directory stuff.

No functional change.
2014-12-06 14:58:00 +00:00
Marco Costalba c30eb4c9c9 Refactor syzygy code in search
Move to SF coding style.

Also skip calculating piece count in search()
when TB are not found (!TBCardinality)

No functional change.
2014-12-06 14:35:50 +00:00
Joona Kiiski 35c1ccef39 Retire support for Haiku installation directory from Makefile
- It is out of the scope of the project.
- It is the responsibility of Haiku package maintainer to
  configure this.

No functional change

Resolves #143
2014-12-06 14:23:08 +00:00
Joona Kiiski 0935dca9a6 Retire hackish support for aCC and HP-UX from Makefile
- It is out of scope of the project.
- We have no way to verify that it even works anymore

No functional change

Resolves #142
2014-12-06 14:19:39 +00:00
Marco Costalba 314d446518 Retire total_piece_count()
We really don't need to uglify in this way
our nice count() API with this ad-hoc hack.

So remove the hack and use the already
existing infrastructure.

No functional change.

Resolves #134
2014-11-30 20:37:24 +00:00
hxim c014444f09 Remove CONNECTED_KINGS from Syzygy code
No functional change

Resolves #140
2014-11-30 20:24:32 +00:00
Rodrigo Exterckötter Tjäder 9b4e123fbe Cleaning Syzygy profiling data
Updating the makefile so that the clean and gcc-profile-clean targets also
remove the profiling data files in the syzygy directory.

No functional change.

Resolves #136
2014-11-30 19:53:04 +00:00
Marco Costalba a43f633c19 Rewrite TBScore in uci_pv()
Streamline the code and make
it understandable.

No functional change.

Resolves #135
2014-11-30 19:35:35 +00:00
Marco Costalba 66f5cd3f9d Retire #ifdef SYZYGY macro
It just clutters the code for no
real reason.

No functional change.

Resolves #139
2014-11-30 19:23:17 +00:00
mstembera fe07ae4cb4 Bitbase index() from ADD to OR.
No functional change.

Resolves #132
2014-11-26 07:56:48 +08:00
lucasart 2c52147dbf Introduce ratio operation
Just like in Physics, the ratio of 2 things in the same unit, should be
without unit.

Example use case:
- Ratio of a Depth by a Depth (eg. ONE_PLY) should be an int.
- Ratio of a Value by a Value (eg. PawnValueEg) should be an int.

Remove a bunch of useless const while there.

No functional change.

Resolves #128
2014-11-26 07:55:57 +08:00
Ronald de Man 7caa6cd338 Syzygy tablebases
Adds support for Syzygy tablebases to Stockfish.  See
the Readme for information on using the tablebases.

Tablebase support can be enabled/disabled at the Makefile
level as well, by setting syzygy=yes or syzygy=no.

Big/little endian are both supported.

No functional change (if Tablebases are not used).

Resolves #6
2014-11-26 07:49:58 +08:00
Gary Linscott 4509eb1342 Fix out-of-bound array access printing ponder move
It is possible that we won't have a ponder move if our PV
is too short.  In that case, just don't print a ponder move.

No functional change

Resolves #130
2014-11-24 08:53:00 +08:00
Gary Linscott 7ad59d9ac9 Fix pondering
The UCI specification states that an engine can never exit the search
while pondering.

No functional change.

Resolves #118
2014-11-24 08:50:36 +08:00
Jonathan Calovski 48127fe5d3 Amend defended
Amend defended to remove now redundant condition.

No functional change.

Resolves #125
2014-11-22 05:46:59 +08:00
Marco Costalba 84408e5cd6 Fix doubled pawns asymmetry
When evaluating double pawns we use always
lsb() to extract the frontmost square.

This breaks evaluation color symmetry as is
possible to verify with an instrumented evaluate()

  Value evaluate(const Position& pos) {

    Value v = do_evaluate<false>(pos);
    Position p = pos;
    p.flip();
    assert(v == do_evaluate<false>(p));
    return v;
  }

Passed no regression test:

STC
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 21035 W: 4244 L: 4122 D: 12669

LTC
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 39839 W: 6662 L: 6572 D: 26605

bench: 8255966
2014-11-21 20:40:25 +01:00
Marco Costalba 79232be02a Further tweak accurate pv
It is a non functional change, but because
we now skip copying of pv[] in SpNode, patch
has been tested for regression with 3 threads:

STC
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 54668 W: 9873 L: 9809 D: 34986

No functional change.
2014-11-21 20:37:45 +01:00
hxim 0a1f54975f Fix some comments
No functional change.

Resolves #123
2014-11-19 06:39:17 +08:00
Gary Linscott bffe32f4fe Fix fen output for castling rights
This is a regression from 428962a

We have to cast to char here, otherwise the compiler
interprets it as an integer, and writes a number.

No functional change

Resolves #122
2014-11-19 06:37:59 +08:00
Marco Costalba 4aca11ae2a Codying style in accurate PV
This is the first of a patch series to
rearrange and simplify accurate PV.

In this patch there is simple coding
style and reformatting stuff.

Verified with fishtest it does not crash
with MAX_PLY = 8

No functional change.
2014-11-18 11:57:57 +01:00
Marco Costalba 1a939cd8c8 Fix a warning on Intel C++
warning #2259: non-pointer conversion from "int" to
"uint8_t={unsigned char}" may lose significant bits

No functional change.
2014-11-17 12:56:48 +01:00
sf-x d65c9a3262 Use PHASE_MIDGAME in game_phase()
No functional change

Resolves #117
2014-11-17 07:50:33 +08:00
Rodrigo Exterckötter Tjäder 99f2c1a2a6 Clear token before reading from input
Previously token would keep its value from the previous line when an empty
line was input, leading to unexpected behaviour.

No functional change

Resolves #119
2014-11-17 07:48:30 +08:00
lucasart 3b1f552b08 Half History Max
STC
LLR: 3.35 (-2.94,2.94) [-0.50,3.50]
Total: 17993 W: 3740 L: 3508 D: 10745

LTC
LLR: 3.25 (-2.94,2.94) [0.00,4.00]
Total: 21346 W: 3691 L: 3453 D: 14202

Bench: 7694316

Resolves #120
2014-11-17 07:04:58 +08:00
Marco Costalba 4840643fed Use DEPTH_MAX instead of MAX_PLY
When comparing to a Depth it is more
consistent to use DEPTH_MAX instead
of a int.

This is a subtle difference because we use
ply and depth almost interchangably in SF,
but they are different. FOr counting plies
makes ense to continue using ints, while
for Depth we have our specific enum.

This cleanly fixes a new Clang 3.5 warning:

No functional change.
2014-11-15 05:36:49 +01:00
Gary Linscott 4739037f96 100% accurate PV display
This gives SF accurate PVs, such that the evaluation of the leaf node in
the PV matches the score backed up to the root (99% of the time.
q-search will use the value stored in the hash table instead of the eval
value sometimes).

One drawback is that fail-high/low only get a minimal 2 move PV.

It doesn't add any additional overhead to the non-PV codepath except an
extra eight bytes to the SearchStack structure in multi-threaded
searches.

A core part of this is not pruning based on TT score in PV nodes. This
was measured as not being a regression at multiple TCs, except for one
exception, fast TC with huge hash, which is not realistic for longer
searches.

STC - 1 thread, 128 mb hash
ELO: 1.42 +-3.1 (95%) LOS: 81.9%
Total: 20000 W: 4078 L: 3996 D: 11926

STC - 3 thread, 128 mb hash
ELO: -3.60 +-2.9 (95%) LOS: 0.8%
Total: 20000 W: 3575 L: 3782 D: 12643

STC - 3 thread, 8 mb hash
ELO: 0.12 +-2.9 (95%) LOS: 53.3%
Total: 20000 W: 3654 L: 3647 D: 12699

LTC - 3 thread, 32mb hash
ELO: 2.29 +-2.0 (95%) LOS: 98.8%
Total: 35740 W: 5618 L: 5382 D: 24740

Bench: 6984058

Resolves #102
2014-11-12 16:16:33 -05:00
lucasart 234344500f Use quiet ttMove in qsearch() (7962287)
Daniel Jose reported that it was an elo gain in his engine:
http://www.talkchess.com/forum/viewtopic.php?t=54290

STC: Hash=16
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 33067 W: 6670 L: 6571 D: 19826

LTC: Hash=64
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 41181 W: 7008 L: 6920 D: 27253

And another one to verify no regression with hash pressure:

STC: Hash=4
LLR: 2.96 (-2.94,2.94) [-4.00,0.00]
Total: 25085 W: 5059 L: 4991 D: 15035

Verified that qsearch does not explode after this patch (recapture threshold).

Bench 7962287

Resolves #112
2014-11-12 21:06:14 +00:00
Marco Costalba b777b17f6f Use if/else instead of goto
Real men jump/branch by hand...but
we prefer the humble way.

Moved also some uci info code where it
belongs, while there.

No functional change.

Resolves #110
2014-11-12 21:02:20 +00:00
Marco Costalba db4b8ee000 Use Depth instead of int in search
And make it more ONE_PLY value independent,
although we are not there yet.

No functional change.

Resolves #111
2014-11-12 21:00:16 +00:00
lucasart c6d45c60b5 Profile Build with Hash=16
16MB for 1" searches is more comensurate with the average use case.

And 16 is the default used by stockfish bench, so it makes sense to be
consistent, if only to have the same minimum memory requirement for using
SF and compiling it with PGO.

No functional change.

Resolves #109
2014-11-10 23:06:12 +00:00
lucasart d709a5f1c5 Fix bounds of FutilityMoveCounts
This is a left-over from ONE_PLY == 2.

No functional change.

Resolves #107
2014-11-09 20:13:56 +00:00
Marco Costalba 1b0df1ae14 Retire pvMove in search()
Now we can directly replace it with
the definition resulting in simpler
and possibly faster code because
PvNode is evaluated at compile time.

No functional change.
2014-11-09 20:36:28 +01:00
Marco Costalba 57fdfdedcf Assorted code-style triviality
No functional change.
2014-11-09 20:17:29 +01:00
Marco Costalba 6fb0a1bc40 Introduce distance() and unify some API
Original work by Lucas.

No functional change.
2014-11-09 10:27:04 +01:00
lucasart 8631b08d97 Codestyle massage Search::init()
* remove some erroneous comments, that were based on the ONE_PLY == 2.
* rename hd to d, because there's no more half-depth in SF.
* rescope variables into the for loops.
* reindent the for loops correctly.
* add a comment to explain the eval improving part (not so obvious to read
this code as array has 4 dimensions).

No functional change.
2014-11-08 10:56:51 -05:00
lucasart 3d2aab11d8 Be more optimistic in aspiration window
Be more optimistic wrt search instability, and set the unviolated bound
half window.

STC
LLR: 2.96 (-2.94,2.94) [-1.00,4.00]
Total: 16362 W: 3371 L: 3197 D: 9794

LTC
LLR: 2.94 (-2.94,2.94) [0.00,5.00]
Total: 21666 W: 3780 L: 3569 D: 14317

Bench: 6807896

Resolves #105
2014-11-08 10:47:56 -05:00
lucasart 7ebb872409 Prune ttMove like any other
This should reduce search inconsistencies, and doesn't seem to have a measurable ELO Impact:

STC with Hash=16
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 49264 W: 10076 L: 10007 D: 29181

LTC with Hash=64
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 82149 W: 14044 L: 14023 D: 54082

Plus an extra test, to make sure it doesn't regress with strong hash pressure:

STC with Hash=4
LLR: 2.95 (-2.94,2.94) [-4.00,0.00]
Total: 21498 W: 4327 L: 4246 D: 12925

Bench: 7302735

Resolves #100
2014-11-07 21:40:24 +00:00
lucasart 375797d51c Retire CACHE_LINE_ALIGNMENT
Speed tests showed no benefit.

No functional change.

Resolves #97
2014-11-07 14:27:04 -05:00
lucasart 8e98bd616e Apply King Safety later in the endgame
Idea is to apply king safety later in the endgame. Previously, we didn't
apply KS in a RR vs. Q ending for example, which causes poor play.
Now we calculate king attacks when the attacking side has a queen or more.

STC with 8moves_v3
LLR: 3.06 (-2.94,2.94) [0.00,4.00]
Total: 38481 W: 6228 L: 5952 D: 26301

LTC with 2moves_v1
LLR: 2.95 (-2.94,2.94) [0.00,4.00]
Total: 51053 W: 8670 L: 8353 D: 34030

Bench: 7514010

Resolves #98
2014-11-06 13:01:47 -05:00
mstembera bc83515c9e Removing some superfluous extern declarations
No functional change.

Resolves #93
2014-11-05 21:17:19 +00:00
Marco Costalba bcbab19376 Assume UCI 'nodes' is int64_t instead of int
UCI specification is not clear on the size of
integers that are exchanged in the protocol, so
instead of a simple int, assume 'nodes' is a
int64_t because we need a bigger size to store
this value in many real cases, especialy with
very long searches.

No functional change.

Resolves #75
2014-11-05 21:11:05 +00:00
Marco Costalba d29a68f585 Rearrange check_time()
Remove an ugly workaround for a gcc
warning while there.

No functional change.
2014-11-05 21:09:21 +00:00
Ajith 0608d6aaec Add bonuses for each threat instead of max threat value.
Use SPSA tuned values for all threat bonuses

STC
LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 6074 W: 1284 L: 1160 D: 3630

LTC
LLR: 2.97 (-2.94,2.94) [0.00,6.00]
Total: 13563 W: 2402 L: 2232 D: 8929

Bench: 6981908

Resolves #94
2014-11-04 23:50:54 +08:00
mstembera 2fd075d1ea Optimize TranspositionTable::store() and TranspositionTable::probe() for speed.
No functional change.

Resolves #85
2014-11-03 18:40:49 +00:00
lucasart d12378497c Do not assume that enum are signed
Clang 3.5 issues warning on constructs like: abs(f1 - f2). The thing is that
f1 and f2 are enum types, and the range given (all positive) allows the
compiler to choose an unsigned type (efficiency being one reason to prefer
unsigned arithmetic). If f1 < f2 are unsigned, then f1 - f2 wraps around zero
and the abs() becomes a no-op. It's the reinterpretation of the unsigned
result (large value) as a signed int that happens to give the correct result,
thanks to 2's complement. This is all tricky and dangerous!

In the spirit of the standard, we assume nothing on the signedness of enums,
and simply calculate the rank and file distances as:
- rank_dist(r1, r2) = r1 < r2 ? r2 - r1 : r1 - r2
- file_dist(f1, f2) = f1 < f2 ? f2 - f1 : f1 - f2
this logic can in fact be applied to any enum we may use, so for better
generality and to avoid code duplication, we use a template function diff()
here.

No functional change.

Resolves #95
2014-11-04 00:35:02 +08:00
lucasart 8ab9c2511a Cleanup MAX_PLY
This area has become obscure and tricky over the course of incremental
changes that did not respect the original's consistency and clarity. Now,
it's not clear why we use MAX_PLY = 120, or why we use MAX_PLY+6, among
other things.

This patch does the following:

* ID loop: depth ranges from 1 to MAX_PLY-1, and due to TT constraint (depth
must fit into an int8_t), MAX_PLY should be 128.

* stack[]: plies now range from 0 to MAX_PLY-1, hence stack[MAX_PLY+4],
because of the extra 2+2 padding elements (for ss-2 and ss+2). Document this
better, while we're at it.

* Enforce 0 <= ply < MAX_PLY:
  - stop condition is now ss->ply >= MAX_PLY and not ss->ply > MAX_PLY.
  - assert(ss->ply < MAX_PLY), before using ss+1 and ss+2.
  - as a result, we don't need the artificial MAX_PLY+6 range. Instead we
  can use MAX_PLY+4 and it's clear why (for ss-2 and ss+2).

* fix: extract_pv_from_tt() and insert_pv_in_tt() had no reason to use
MAX_PLY_PLUS_6, because the array is indexed by plies, so the range of
available plies applies (0..MAX_PLY before, and now 0..MAX_PLY-1).

Tested with debug compile, using MAX_PLY=16, and running bench at depth 17,
using 1 and 7 threads. No assert() fired. Feel free to submit to more severe
crash-tests, if you can think of any.

No functional change.
2014-11-03 23:36:24 +08:00
Marco Costalba fc0733087a Restore std::dec after std::hex
Code is leaking a std::hex, and causes subsequent
sync_cout output to be in hexadecimal.

Spotted by Lucas

No functional change.
2014-11-02 08:03:52 +01:00
Marco Costalba 42a20920e5 Retire ScalePawnSpan[]
Obscure, undocmented and misnamed array. Replace with
the direct formula: it is much more clear what the
code does.

No functional change.

Resolves #90
2014-11-01 22:10:25 +00:00
Marco Costalba 79fa72f392 Merge pull request #89 from official-stockfish/pull_no_pretty
Prefer operator<<() to pretty()

No functional change.
2014-11-01 22:24:33 +01:00
Marco Costalba d3091971b7 Retire PawnsFileSpan
It is useless. Tested as no regression:

STC
LLR: 4.06 (-2.94,2.94) [-3.00,1.00]
Total: 140718 W: 28527 L: 28568 D: 83623

LTC
LLR: 2.94 (-2.94,2.94) [-3.00,1.00]
Total: 60034 W: 10359 L: 10303 D: 39372

bench: 6564212

Resolves #88
2014-11-01 20:50:52 +00:00
lucasart d9caede324 Correctly describe POPCNT compile
SSE4.2 has nothing to do with POPCNT. We must dispell this myth, because
Stockfish is a reference and many will copy this mistake if they see it in Stockfish:
* SSE is an SIMD instruction set, relative to vectorization (using special 128-bit registers).
* POPCNT/LZCNT work on normal registers (eg. AL, AX, EAX, RAX).

The confusion comes from the fact that, in the Intel product line, it just
so happens that SSE4.2 and POPCNT/LZCNT came out at the same time. But this
is not true for AMD. For example, all AMD Pheniom II have SSE3 but no
POPCNT/LZCNT, and that is why the modern compile uses  -msse3 -popcnt and not -msse4.2.

No functional change.

Resolves #86
2014-11-01 20:43:57 +00:00
lucasart 8a7876d48d Consistent use of anonymous namespace
Objects that are only accessible at file-scope should be put in the anonymous namespace.
This is what the  C++ standard recommends, rather than using static, which is really C-style and results in static linkage.

Stockfish already does this throughout the code. So let's weed out the few exceptions,
because... they have no reason to be exceptional.

No functional change.

Resolves #84
2014-11-01 20:35:10 +00:00
lucasart 2ee1250294 Remove dead code
No functional change.

Closes #87
2014-11-01 20:16:29 +00:00
Marco Costalba 5644e14d0e Prefer operator<<() to pretty()
It is more idiomatic, we didn't used it
in the past because Position::pretty(Move)
had a calling argument, but now we can.

As an added benefit, we avoid a lot of string
copies in the process because now we avoid
std::ostringstream ss.

No functional change.
2014-11-01 19:02:35 +01:00
Marco Costalba d07a875398 Merge pull request #82 from official-stockfish/clean_up_bishop_code
Code style clean-up

No functional change.
2014-11-01 18:05:03 +01:00
Marco Costalba 460892382a Code style clean-up
This piece of code needs some love.

No functional change.
2014-10-30 12:32:43 +01:00
mstembera 5605cc7684 max_piece_type cleanup, and slight speed increase.
No functional change.

Resolves #81
2014-10-28 22:23:01 +08:00
Joona Kiiski 7d42752158 A small clean up of previous patch suggested by Marco
No functional change
2014-10-27 20:26:12 +00:00
Joona Kiiski fefb27bab4 Speed up max_piece_type()
Write code in the way that allows compiler to perform loop unrolling.

My measurement (32 cycles each):

Orig:

Time (Mean: 2466.59375, Trimmed mean: 2464.25, Std: 12.6869487803348)
Nodes (Mean: 4294458, Trimmed mean: 4294458, Std: 0)
Speed (Mean: 1741.09247987678, Trimmed mean: 1742.72879715475, Std: 8.93612608292678)

Time (Mean: 2470.15625, Trimmed mean: 2468.75, Std: 12.7484581610433)
Nodes (Mean: 4294458, Trimmed mean: 4294458, Std: 0)
Speed (Mean: 1738.58176151341, Trimmed mean: 1739.54618465403, Std: 8.95585822316946)

Mod:

Time (Mean: 2449.90625, Trimmed mean: 2445.9375, Std: 12.1000116635508)
Nodes (Mean: 4294458, Trimmed mean: 4294458, Std: 0)
Speed (Mean: 1752.94829372932, Trimmed mean: 1755.75934908231, Std: 8.61478453124504)

Time (Mean: 2442.78125, Trimmed mean: 2441.1875, Std: 8.17839157228837)
Nodes (Mean: 4294458, Trimmed mean: 4294458, Std: 0)
Speed (Mean: 1758.03872783803, Trimmed mean: 1759.16825356261, Std: 5.81131316346191)

No functional change
2014-10-27 20:25:57 +00:00
snicolet 7de40076ac Tune PawnsFileSpan
Passed the following SPRT tests:

STC:
LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 24428 W: 5056 L: 4880 D: 14492

LTC:
LLR: 2.96 (-2.94,2.94) [0.00,4.00]
Total: 26590 W: 4715 L: 4472 D: 17403

Bench: 6615949

Resolves #78
2014-10-27 11:48:44 +00:00
Pascal Romaret f1359845de Improve compatibility with Shredder Classic GUI
This commit fixes two issues:

1) Don't print PVs after the search has been interrupted

    This solves the "mate 0 upperbound" scores that sometimes
    creep up when a multi-PV analysis gets interrupted with
    the `stop` command.

2) Print multipv before score

    Shredder Classic fails to identify the main PV
    (the one with multipv 1) if `score` comes first.
    This leads to an eval graph that doesn't reflect
    the scores actually reported by Stockfish when
    doing a multiPV analysis.

No functional change

Closes #76
2014-10-27 11:07:35 +00:00
Marco Costalba 5ab55827b8 Fix an obscure gcc warning
warning: narrowing conversion from ‘int’ to ‘char’ inside { }
is ill-formed in C++11 [-Wnarrowing]

When pedantic meets esoteric!

No functional change.
2014-10-26 19:40:20 +00:00
Marco Costalba 9ba391c5cb Retire notation.cpp
Now we can finally retire notation.cpp
and move UCI helpers under uci.cpp

No functional change.
2014-10-26 19:40:13 +00:00
Marco Costalba 2469daebb1 Final UCI helpers renaming
To reflect new changes, specifically that
now are all under UCI namespace.

No functional change.
2014-10-26 19:40:04 +00:00
Marco Costalba aa60c80ade Retire notation.h
And move the few remaining content
under UCI namespace where they belong.

No functional change.
2014-10-26 19:39:56 +00:00
Marco Costalba 5cbcff55cc Rename ucioption.h to uci.h
We are going to add all UCI related
functions here, so first rename it
to a more proper name.

No functional change.
2014-10-26 19:39:46 +00:00
Marco Costalba 428962a2e8 Retire to_char() helpers
Remove some useless wrappers and make
the conversion explicit and starightforward.

No functional change.
2014-10-26 19:39:37 +00:00
Marco Costalba 4f6b1bf3be Reformat max_threat()
Helper function should just know how to find the
biggest piece type in a bitboard. All the threat
logic and data shoud be in evaluate_threats().

This nicely separates the scope of the two functions
in a more consistent way and simplifies the code.

No functional change.
2014-10-26 09:12:56 +00:00
snicolet 8d62ece945 Calculate maximum threat for hanging pieces
Use the max_threat() helper function to estimate more precisely the
best hanging piece threat.  Also retunes the Threat array using SPSA.

STC
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 7598 W: 1596 L: 1468 D: 4534

LTC
LLR: 2.97 (-2.94,2.94) [0.00,6.00]
Total: 7896 W: 1495 L: 1350 D: 5051

Bench: 6816504

Resolves #73
2014-10-24 01:10:11 +08:00
Marco Costalba 480682b670 Document why initing eval tables
Instead of hard-code the weights in a big table,
we prefer to calculate them out of few parameters
at startup. This allows to keep low the number of
independent parameters and hence is good for tuning
and for a better insight in the meaning of the numbers.

No functional change.
2014-10-15 19:36:22 +01:00
Marco Costalba 89d9db2979 Rename Tracing methods
Easier to understand and more in line with
standard Trace classes naming like:

http://msdn.microsoft.com/en-us/library/system.diagnostics.trace.aspx

No functional change.
2014-10-12 20:35:19 +01:00
Marco Costalba 907f912463 Account for Tempo in do_evaluate()
This is more correct because we let evaluate()
to be a pure dispatcher and also now evaluate
and tracing outputs are consistent.

No functional change.
2014-10-12 20:35:07 +01:00
Marco Costalba c97b702f4d Fix some warnings with Intel C++ compiler
No functional change.
2014-10-12 20:34:54 +01:00
lucasart 91de6b0f37 Further streamline connected pawn evaluation
Make even more clear what are the terms that
contribute to evaluate connected pawns, and
completely separate them from the weights
that are now fully looked up in a table.

For future tuning makes sense to init the table with
a formula instead of hard-code it. This allows to
reduce problem space cardinality and makes tuning
easier.

And fix a MSVC warning while there:
warning C4804: '>>' : unsafe use of type 'bool' in operation

No functional change.
2014-10-12 20:03:49 +01:00
lucasart 069073ea68 Merge Connected and Candidate
These two notions are very correlated. Since connected has the most
generality, it makes sense to generalize it to encompass what is
covered by candidate.

STC:
LLR: 4.03 (-2.94,2.94) [-3.00,1.00]
Total: 11970 W: 2577 L: 2379 D: 7014

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 13194 W: 2389 L: 2255 D: 8550

bench 7328585
2014-10-07 07:42:17 +01:00
joergoster 15e2191111 Remove the now redundant TT prefetch call from Position::do_move.
Tested for no regression and passed both
STC
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 48250 W: 9757 L: 9686 D: 28807

LTC
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 51412 W: 8887 L: 8816 D: 33709

No functional change.

Resolves #66
2014-10-06 23:59:56 +08:00
Luca Brivio 8a9b9ec96a Simplify futility move count array formula
No functional change
2014-10-05 12:49:18 +01:00
Marco Costalba 74829342ef Reformat and rename hash_after_move()
Align to standard coding style and properly use
enum types. Rename while there.

No functional change.
2014-10-04 10:36:29 +01:00
Ajith 4b926f227d Extend King Threats to all pieces (other than pawns).
STC
LLR: 2.99 (-2.94,2.94) [-1.50,4.50]
Total: 20559 W: 4261 L: 4095 D: 12203

LTC
LLR: 2.96 (-2.94,2.94) [0.00,4.00]
Total: 75232 W: 13097 L: 12696 D: 49439

Bench: 7543790

Resolves #63
2014-10-04 03:54:12 +08:00
joergoster 82d065b011 Speculative prefetch
Idea by Peter Oesterlund.
Implemented and tested by Joerg Oester

STC 3 threads
ELO: 3.19 +-2.1 (95%) LOS: 99.9%
Total: 40000 W: 7576 L: 7209 D: 25215

LTC
LLR: 2.96 (-2.94,2.94) [0.00,6.00]
Total: 22026 W: 3829 L: 3619 D: 14578

STC
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 7291 W: 1531 L: 1404 D: 4356

No functional change

Resolves #61
2014-10-02 22:19:14 +01:00
lucasart e60cdca9b0 Convert TT depth to int8_t
Now that half plies have been removed from the engine, we can encode
TT depth into an int8_t.

Range is -128 to +127, so it goes still further than the previous
limit of 121 plies (with ONE_PLY == 2 where depth - DEPTH_NONE was
encoded as an uint8_t).

No functional change.

Resolved #60
2014-10-01 20:51:32 +01:00
Marco Costalba a1b62d68ec Trivial code style fixes
Mainly to sync mine and official repo.

No functional change.
2014-09-30 09:05:20 +02:00
Marco Costalba 222f59b9c1 Move ONE_PLY to be 1 instead of 2: search()
Now that half-plies are no more used we can simplify
the code assuming that ONE_PLY is 1 and no more 2.

Verified with a SMP test:
LLR: 2.95 (-2.94,2.94) [-4.50,0.00]
Total: 8926 W: 1712 L: 1607 D: 5607

No functional change.
2014-09-29 15:17:12 +02:00
lucasart 27a1877299 Clean up VALUE_KNOWN_WIN conditions
A patch (+ some extra changes) passed with:

STC
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 14575 W: 3101 L: 2967 D: 8507

LTC
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 42579 W: 7580 L: 7496 D: 27503

Bench: 6545733

Resolves #52
2014-09-28 17:45:49 +01:00
Uri Blass ea9c424bba Remove use of half-ply reductions from LMR, Null-move, IID and
Singular extensions.

STC:
ELO: 3.80 +-3.1 (95%) LOS: 99.2%
Total: 19727 W: 4190 L: 3974 D: 11563

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 7647 W: 1356 L: 1214 D: 5077

Bench: 6545733

Resolves #55
2014-09-28 04:33:28 +08:00
Joona Kiiski 7ed15af371 Cap evaluation based null move extra reduction to three plies
It's a zero elo patch, and a reasonable safeguard against uncontrolled extreme reductions.

STC:
ELO: -0.08 +-2.0 (95%) LOS: 46.9%
Total: 40000 W: 6728 L: 6737 D: 26535

LTC:
ELO: -0.14 +-1.8 (95%) LOS: 44.0%
Total: 40000 W: 5557 L: 5573 D: 28870

Bench: 7201830
2014-09-25 20:42:25 +01:00
Uri Blass d6613b7589 Change history reduction in LMR to be a full ply.
STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 9829 W: 2142 L: 1998 D: 5689

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 27162 W: 4802 L: 4692 D: 17668

Bench: 7284120

Resolves #53
2014-09-26 02:22:39 +08:00
uriblass fed3e752ae Evaluate king safety when no queen is present.
LLR: 2.97 (-2.94,2.94) [-1.50,4.50]
Total: 16657 W: 3547 L: 3391 D: 9719

LLR: 2.95 (-2.94,2.94) [0.00,6.00]
Total: 31258 W: 5664 L: 5403 D: 20191

Bench: 8331165

Resolves #51
2014-09-22 01:32:47 +08:00
Gary Linscott 766fb9c67d Fix spacing.
No functional change.
2014-09-22 01:27:34 +08:00
Marco Costalba b652720903 Rearrange evaluation constants definitions
Make them more uniform and consistent.

No functional change.

Conflicts:
	src/evaluate.cpp
2014-09-21 08:26:25 +08:00
Marco Costalba 4d0a6c5a6f Rename time variable to reflect UCI parameters
On top of previous patch, rename time variables to
reflect the simplification of UCI parameters.

It is more correct to use as varibales directly the
corresponding UCI option, without intorducing redundant
intermediate variables.

This allows also to simplify the code.

No functional change.
2014-09-21 08:23:12 +08:00
mbootsector 2eec710318 King-pawn threat bonus for endgames.
STC:
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 10224 W: 1765 L: 1638 D: 6821

LTC:
LLR: 2.96 (-2.94,2.94) [0.00,6.00]
Total: 6923 W: 1027 L: 899 D: 4997

bench 7818100

Resolves #49
2014-09-19 22:36:05 +08:00
Marco Costalba aa10d0aaa6 Rename "Contempt Factor" to "Contempt"
Suggested by Ronald and Lucas on talkchess.

No functional change.

Conflicts:
	src/ucioption.cpp
2014-09-16 21:14:00 +01:00
Marco Costalba ff480bdb83 Retire struct Log
No more used now that we have removed
"Write Search Log" UCI option.

No functional change.
2014-09-16 21:13:50 +01:00
lucasart b66552fc27 Simplify Time Management UCI options
No functional change
2014-09-15 20:24:51 +01:00
Marco Costalba cd065dd584 Small tweak to idle_loop()
In case of a succesful late join we set again
'searching' flag, so we can restart search
immediately without an useless lock/unlock
cycle.

No functional change.
2014-09-04 20:19:03 +01:00
Joona Kiiski 95b24083fb Simplify idle_loop()
No functional change
2014-08-30 21:03:41 +01:00
lucasart 877313a413 Retire Search Log
No functional change

Bench: 7461881
2014-08-24 16:28:51 +01:00
Marco Costalba 8b8885ab07 Fix perft 1
Compute correct number of moves for this corner case.

A smal bug crept in after recent perft rework.

No functional change.
2014-08-10 07:36:22 +08:00
lucasart a903ed07e0 Retire move_to_san()
Now "Write Search Log" will pring moves in UCI format, consistent with all the rest. This functionality is
not aimed at end-users anyway. It's hardly useful at all, in fact. Also, pretty-printing SAN moves is
something that better belongs in the GUI than in the engine.

No functional change.
2014-08-10 07:28:00 +08:00
lucasart 880e3cd7c8 Move to_char() and to_string() to notation
Where they better belong.

Also, this removes '#include <string>' from types.h, which reduces the amount of code to compile (every
translation unit includes types.h).

No functional change.
2014-08-09 13:25:05 +08:00
Marco Costalba a67c22611a Rework perft implementation
Unify various perft functions and move all the code
to search.cpp.

Avoid perft implementation to be splitted between
benchmark.cpp (where it has no reason to be) and
search.cpp

No functional and no speed change (tested).
2014-08-09 13:00:59 +08:00
lucasart 2efeded6e3 Write perft(N-1) into cout
So that one can redirect cout to /dev/null and only print print cerr in the terminal (for more accurate speed
tests).

Suggested by Marco.

No functional change.
2014-08-07 21:15:05 +08:00
lucasart 6044f25d71 Fix Hash in bench
The compiler tries to cast Options["Hash"] into a string, using:

Option::operator std::string() const {
  assert(type == "string");
  return currentValue;
}

And, as expected, the assert() fails.

std::to_string() would be the right solution, but it's C++11. And using a stringstream is too much code to
achieve so little. Let's keep it the way it was: hardcoded (ie. default hash defined in two places).

No functional change.
2014-08-07 18:51:07 +08:00
joergoster 9da015517c Remove insufficient material rule
The eval already returns zero in KK, KBK, KNK (see material.cpp). The difference is:
- we lose the "TB pruning" benefit of the draw rule (ie. search goes on even if eval is zero)
- we gain some speed by removing a useless test from the hot path

STC:
LLR: 0.05 (-2.94,2.94) [-3.00,1.00]
Total: 128000 W: 21357 L: 21560 D: 85083

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 33023 W: 4613 L: 4509 D: 23901

bench 7461881
2014-08-06 18:42:10 +08:00
lucasart 888a1d3445 Remove useless code in Position::pretty()
First, remove some dead code (function never called with a Move argument).

Then, remove printing of legal moves, which does not belong here. Let's keep commands orthogonal and minimal:
- the "d" command should display the board, nothing more, or less.
- "perft 1" will display the list of legal moves.

No functional change.
2014-08-06 07:04:27 +08:00
lucasart 8b88ca9017 Reduce minimum memory requirement by 16MB
Stockfish allocates the default hash (32MB) in main(), before entering UCI::loop(). If there is not enough
memory, the program will crash even before UCI::loop() is entered and the GUI is given a change to specify a
lower Hash value.

This defective design could be resolved by doing a lazy allocation upon "isready" command, as the UCI protocol
guarantees that "isready" will be sent at least once before any search. But it's a bit cumbersome when using
Stockfish "manually" to have to remember to type "isready" everytime.

So leave the current design, but reduce the default hash to 16MB instread of 32MB. In order to perform such
quick searches (depth=13), there is no reason to use so much Hash anyway. Another benefit is to introduce a
bit of hash pressure in bench, which increases chances to detect rare bugs related to TT replacement, for
example.

This is not a functional change, although it obviously changes the bench.

bench 7461879
2014-08-05 11:42:48 +08:00
lucasart 94fe366779 Default Hash defined in a single place
Instead of defining it both in ucioption.cpp and benchmark.cpp. Obviously changing the default Hash will
change the bench as a result.

No functional change.
2014-08-05 11:40:25 +08:00
lucasart 535f70088e Retire divide command
The main purpose of perft is to help debugging. But without the breakdown in sum of perft(N-1), it is a
completely useless debugging tool.

So perft now displays the breakdown, and divide is therefore removed.

No functional change.
2014-08-04 13:54:09 +08:00
Marco Costalba 1b69910865 Ensure printing UCI info in multi-pv case
After commit 94b1bbb68b, in case available root moves are less than multiPV, we
could never reach condition:

PVIdx + 1 == multiPV

and as a consequence UCI output is not printed.

Fixed suggested by Joerg Oster.

No functional change.
2014-08-02 22:16:45 +08:00
Oskar Werkelin Ahlin 29451de874 Correct bench timing
No functional change.
2014-08-01 19:32:53 +08:00
lucasart 5adc678628 Document Threat[] indices
From Marco's repo.

No functional change.
2014-07-30 07:06:48 +08:00
Marco Costalba f2053ba19f Fix a warning with MSVC 2010
Warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data

No functional change.
2014-07-29 06:53:09 +08:00
David Zar 68171ecaca Small code style reformatting
No functional change.
2014-07-29 06:52:32 +08:00
lucasart 6b82a234d0 Aspiration: widen slower
STC:
LLR: -2.95 (-2.94,2.94) [0.00,4.00]
Total: 182323 W: 30664 L: 30234 D: 121425

LTC:
LLR: 3.51 (-2.94,2.94) [0.00,4.00]
Total: 59841 W: 8345 L: 8006 D: 43490

bench 7962536
2014-07-27 09:18:04 +08:00
David Zar 4758fd31b1 Outpost tuning
double mg bonus and half eg bonus.

STC:
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 18142 W: 3094 L: 2948 D: 12100

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,4.00]
Total: 83561 W: 11706 L: 11329 D: 60526

bench 7831429
2014-07-23 07:05:10 +08:00
lucasart 7f68fc611c Revert Contempt = 20
Despite being neutral at STC, it turned out to be regressive at LTC:

40k games at LTC with Hash=8
ELO: -2.06 +-1.9 (95%) LOS: 1.4%
Total: 39720 W: 5740 L: 5976 D: 28004

40k games at LTC with Hash=128
ELO: -2.69 +-1.9 (95%) LOS: 0.2%
Total: 39149 W: 5702 L: 6005 D: 27442

bench 7477963
2014-07-20 08:50:54 +08:00
Marco Costalba 896bd917f8 Simplify evaluate_passed_pawns
From a suggestion by David Zar.

No functional change.
2014-07-15 20:59:32 +08:00
lucasart 67a5e1ecf9 Contempt = 20
Also raise the admissible bounds to (-100,100), as there is no reason to prevent users from using high
values if they want to.

Does not regress in self play:
ELO: 0.10 +-2.0 (95%) LOS: 53.7%
Total: 40000 W: 7084 L: 7073 D: 25843

master vs SF 3
ELO: 182.86 +-2.7 (95%) LOS: 100.0%
Total: 40000 W: 21843 L: 2541 D: 15616

Contempt = 20 vs SF 3
ELO: 189.25 +-2.8 (95%) LOS: 100.0%
Total: 40000 W: 22721 L: 2859 D: 14420

Diff is therefore 6.4 +/- 3.9 elo against a 180-190 elo weaker engine, which is significantly positive,
as expected. This elo difference is likely understated, because of FishTest aggressive draw adjudication
though.

We could push Contempt further, but after 20cp, it would get in the way of FishTest draw adjudication
rule, and is likely to reduce the testing throughput as a result.

bench 8198667
2014-07-15 07:14:58 +08:00
David Zar a9b8e8b931 Bonus passed pawn blocked by our pieces
passed STC:
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 63965 W: 10950 L: 10692 D: 42323

and LTC:
LLR: 2.95 (-2.94,2.94) [0.00,6.00]
Total: 19237 W: 2740 L: 2562 D: 13935

bench: 7477963
2014-07-14 21:19:07 +08:00
Marco Costalba 94b1bbb68b Small reformat to Skill class
Steamline a bit the implementation of
skill levels. As a side effect we can
retire MultiPV global and use a local
variable instead.

No functional change.
2014-07-11 18:08:27 +08:00
lucasart f133f61e3f Remove useless condition
Small simplification, suggested by Uri Blass.

passed STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 25839 W: 4464 L: 4351 D: 17024

and LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 220039 W: 29981 L: 30131 D: 159927

No functional change.
2014-07-10 18:25:20 +08:00
Joona Kiiski eb50793cff Retire FakeSplit
- Currently broken
    - Never been really useful
    - Does not work well with new splitting model

Verified for no regression at STC with 3 threads:
LLR: 2.96 (-2.94,2.94) [-6.00,0.00]
Total: 81905 W: 12122 L: 12381 D: 57402

No functional change
2014-07-09 07:19:06 +08:00
Marco Costalba 9b30913996 Avoid 'double assigments' tricks
Bitboard init code is already noteasy to follow,
so don't make it even harder using 'smart' code.

Also reindent a while loop in standard way.

No functional change.
2014-07-07 21:53:25 +08:00
joergoster ac7780bd35 Tune trapped rook penalty
Passed STC
LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 15687 W: 3352 L: 3199 D: 9136

and LTC (parameter tweaks)
LLR: 2.95 (-2.94,2.94) [0.00,4.00]
Total: 27983 W: 5046 L: 4797 D: 18140

bench: 8330705
2014-07-03 19:38:54 +08:00
lucasart c192b692cf size_t cast in TranspositionTable::first_entry()
32-bit truncation would make this function bogus when clusterCount >= 2^33 (ie. Hash >= 256 GB).

No function change.
2014-07-03 18:23:56 +08:00
lucasart 85b08ce3ad Use compiler intrinsic instead of assembly for popcnt
This time, do not break compatibility with some AMD machines that have SSE3 and popcnt, but do
not have SSE4.2.

No functional change.
2014-07-03 18:22:53 +08:00
Gary Linscott b9a88da4ab Revert "Use compiler intrinsic instead of assembly for popcnt"
This reverts commit a69f1d7c20.
2014-07-01 17:01:54 -04:00
lucasart a69f1d7c20 Use compiler intrinsic instead of assembly for popcnt
No functional change.
2014-07-01 20:50:33 +08:00
lucasart 24ba204931 Raise max Hash to 1TB
And use size_t where appropriate, as suggested on FishCooking.

No functional change.
2014-07-01 18:37:18 +08:00
Ajith 6b354305e1 Add bonuses for Minors attacking enemy pieces(except pawns) even when they are protected by enemy pawns.
Patch passed STC
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 8206 W: 1426 L: 1304 D: 5476

and LTC
LLR: 2.97 (-2.94,2.94) [0.00,6.00]
Total: 19534 W: 2821 L: 2640 D: 14073

Bench: 9942172
2014-06-30 10:55:10 -04:00
joergoster ffedfa3354 Fix Singular extension condition to handle mate scores
With Eelco's patch "Don't special case for abs(beta) >= VALUE_MATE_IN_MAX_PLY" condition "abs(ttValue) < VALUE_KNOWN_WIN" has been removed from singular extension search, and condition "abs(beta) < VALUE_KNOWN_WIN" was added to the SingularExtensionNode definition.
This might lead to problems, especially in positions, where a mate is due.
For example, this position 5rk1/4K1pp/8/5PPP/8/8/8/1R6 w - - 12 1 triggers an assert.
stockfish: search.cpp:434: Value {anonymous}::search(Position&, Search::Stack*, Value, Value, Depth, bool) [with {anonymous}::NodeType NT = (<unnamed>::NodeType)2u; bool SpNode = false]: Assertion `-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE' failed.

So let's re-insert the removed condition.
First spotted by Uri Blass, fix by me.

Bench: 8759675
2014-06-29 20:17:40 +01:00
Joseph R. Prostko e4e423bb05 Change the install prefix for Haiku
* /boot/common was removed from Haiku
* The equivalent path now that package management has been implemented is /boot/system/non-packaged

No functional change

Bench: 8759681
2014-06-29 15:15:10 +01:00
Ron Britvich ccd823a4ff Pack 3 TT entries in 32 bytes cluster
Idea from Ron Britvich

Code reworked by Marco Costalba and Joona Kiiski

Bench: 8095369

Resolves #3
Resolves #10
2014-06-28 14:06:32 -04:00
Gary Linscott 7ff865b924 Merge pull request #9 from glinscott/pawnspan
Scale down endgames with pawns on one or two adjacent files

Passed STC
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 16081 W: 2745 L: 2604 D: 10732

Passed LTC
LLR: 2.95 (-2.94,2.94) [1.00,6.00]
Total: 123832 W: 17292 L: 16584 D: 89956

128k games to measure ELO at 15+0.05:
ELO: 2.07 +-1.1 (95%) LOS: 100.0%
Total: 128000 W: 21632 L: 20869 D: 85499

New bench: 8028792
2014-06-26 12:21:06 -07:00
Gary Linscott ec627911f0 A bit more cleanup 2014-06-26 15:20:30 -04:00
Gary Linscott 22ffb588e5 Merge pull request #8 from glinscott/revert_ce1c260
Revert "Check for an available slave early on"
2014-06-26 08:20:45 -07:00
Gary Linscott e6a8d03dd8 Revert "Check for an available slave early on"
This reverts commit ce1c260ea9.
2014-06-26 11:16:36 -04:00
Gary Linscott 747e035c7d Original version of shane's patch 2014-06-25 16:45:14 -04:00
shane31 6c9f4cf36f Scale down endgames with pawns on one or two adjacent files 2014-06-25 16:01:00 -04:00
Gary Linscott ab580106fd Merge pull request #5 from glinscott/authors
Add AUTHORS
2014-06-23 09:14:03 -07:00
Marco Costalba aa23eb4440 Some reformatting in pawns.cpp
No functional change.
2014-06-21 15:08:31 +02:00
Marco Costalba e8baf2b772 Ensure ttValue != VALUE_NONE in singular extension search
The assert:

  assert(ttValue != VALUE_NONE);

Could fire for multiple reasons (although is very rare),
for instance after an IID we can have ttMove != MOVE_NONE
while ttValue is still set at VALUE_NONE.

But not only this, actually SMP is a source of corrupted
ttValue and anyhow we can detect the condition:

 ttMove != MOVE_NONE && ttValue == VALUE_NONE

even north of IID.

Reported by Ronald de Man.

It is so rare that bench didn't change.

bench: 7710548
2014-06-21 13:07:29 +02:00
Gary Linscott 58bb23d0c3 Remove some duplicates 2014-06-21 00:49:49 -04:00
Gary Linscott bbacff5e01 Add AUTHORS 2014-06-20 21:35:09 -04:00
Marco Costalba 43efd7fad7 Better value clipping in game_phase()
No functional change.
2014-06-21 00:05:14 +02:00
Marco Costalba 3b315c9ada Move game_phase() to Position
It seems a more natural to place this
function there.

No functional change.
2014-06-20 23:44:46 +02:00
Marco Costalba f7926ea41e Small renaming in material weights
Also dropped some temporary variable: compiler
is more than able to push on stack temp values
by itself (verified).

No functional change.
2014-06-20 23:15:31 +02:00
Marco Costalba 264c8637a3 Simplify a condition in is_KXK()
No functional change.
2014-06-19 15:44:42 +02:00
Eelco de Groot 55a3e0af8d Don't special case for abs(beta) >= VALUE_MATE_IN_MAX_PLY
Remove from the search this special case and apply
null search and razoring also in mate positions.

Tested in no-regression mode and passed both

STC
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 65431 W: 10860 L: 10810 D: 43761

and LTC
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 34928 W: 4814 L: 4713 D: 25401

This patch kicks in only in mate positions and in
these cases it seems beneficial in finding mates
faster as Yery Spark measured on the Chest mate suite:

Total number of positions 6425
Fixed nodes 200K per position

master: 1049
new:    1154

And also the 5446 'hard' positions again with 2000K nodes
(those not found by both engines in 200K nodes):

master: 1069
new:    1395

bench: 7710548
2014-06-16 21:50:14 +02:00
Leonid Pechenik 66c93245e0 Simplify unstoppable pawns
Tested in no-regression mode and passed both

STC
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 17919 W: 3103 L: 2978 D: 11838

and LTC
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 31460 W: 4414 L: 4308 D: 22738

bench: 7709279
2014-06-16 12:39:50 +02:00
Marco Costalba 73ca93f3c0 Remove -ansi flag for Clang
It seems this flag is only for gcc and
yields a warning under OSX Mavericks:

clang: warning: argument unused during compilation: '-ansi'

No functional change.
2014-06-15 10:31:57 +02:00
Marco Costalba c6fc51c5ea Fix a warning with MSVC in 'analyze' mode
Here MSVC is worried that

StepAttacksBB[PAWN][psq]

could overflow, so change psq initialization
to clarify psq is always less than 64.

No functional change.
2014-06-14 12:46:58 +02:00
Marco Costalba 2cb4c7052e Triviality in UCI::loop
Code style paranoid in action here :-)

No functional change.
2014-06-14 12:26:08 +02:00
Marco Costalba 3c1201c20c Factor out pawn attacks in 'lever'
Improves readibility and possibly speed.

No functional change.
2014-06-14 12:20:54 +02:00
Ajith 84dabe5982 Simplify pawn threats and merge into ThreatenedByPawn[]
Tested in no-regression mode,

passed STC
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 14477 W: 2493 L: 2362 D: 9622

and LTC
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 51964 W: 7091 L: 7013 D: 37860

bench: 7875814
2014-06-12 08:32:16 +09:00
Reuven Peleg e10276f45d Tweak outpost name
This name is more accurate, since that function evaluates only one
outpost in every call.
No functional change.
2014-06-11 23:42:47 +09:00
Reuven Peleg 2312c26722 Add bonus for advanced lever
Passed both STC
LLR: 2.95 (-2.94,2.94) [-1.50,4.50]
Total: 15413 W: 2670 L: 2530 D: 10213

And LTC:
LLR: 2.95 (-2.94,2.94) [1.00,6.00]
Total: 66908 W: 9398 L: 8960 D: 48550

Bench : 7859385
2014-06-11 23:38:25 +09:00
Marco Costalba b6cd89aeaf Small renaming in Tracing
No functional change.
2014-06-09 05:30:18 +09:00
Marco Costalba ce1c260ea9 Check for an available slave early on
Don't take the split lock if we don't have
available slaves (about 30-40% of times).

This new condition allows to retire the now
redundant one on number of threads.

No functional change.
2014-06-09 04:56:31 +09:00
Marco Costalba 4d30126e4b Use unsigned char as argument of std::isspace
Although signature allows an int:

int isspace( int ch );

The behavior is undefined if the value of ch is
not representable as unsigned char and is not
equal to EOF.

See

http://en.cppreference.com/w/cpp/string/byte/isspace
http://www.greenend.org.uk/rjk/tech/cfu.html

This is really a tricky corner case of C standard!

Spotted and reported by Ron Britvich.

No functional change.
2014-06-07 01:21:47 +02:00
Marco Costalba 7f56d2949d Avoid to use nullChild
Use instead:

(ss-1)->currentMove == MOVE_NULL

No functional change.
2014-06-06 11:12:05 +02:00
Marco Costalba d10ae90dea Remove 'update gains' hack
Use (move != MOVE_NONE) condition to
filtering out updating gains at root.

bench: 8454456
2014-06-06 11:10:40 +02:00
Marco Costalba 2f75639485 Temporary revert previous patch
Split previous patch in 2 steps: first remove
the MOVE_NULL hack, then retire nullChild.

The first step is a prerequisite
for second one and affects bench.

The second step (next patch) just removes nullChild
without affecting bench.

bench: 8205159
2014-06-06 11:08:35 +02:00
Marco Costalba ad1167c482 Avoid to use nullChild
Should be a non functional change, but
for some reason bench is changed.

bench: 8454456
2014-06-06 10:07:48 +02:00
Marco Costalba 08753771fc Move Tempo to evaluation
No functional change.
2014-06-06 09:40:01 +02:00
Marco Costalba 69ac45d903 Revert "Score extractors"
Are broken for big-endian case and
I have verified with MSVC 2013 Premium
bench is correct and there is no
miscompilation, so the main reason
to change the original code drops.

No functional change.
2014-06-05 23:55:18 +02:00
Marco Costalba 323a006666 Fix a warning with MSVC Premium 2013
Reported by Ron Britvich.

No functional change.
2014-06-05 23:46:58 +02:00
pellanda 16e170d105 Update Readme.md
Retire line about polyglot.ini and
book as they are not used anymore.

No functional change.
2014-06-04 22:31:59 +02:00
Lucas Braesch adeded29fb Symmetric King Safety: take 2
Another attempt at retiring current asymmetric
king evaluation and use a much simpler symmetric
one. As a good side effect we can avoid recalculating
eval after a null move.

Tested in no-regression mode and passed

STC
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 21580 W: 3752 L: 3632 D: 14196

LTC
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 18253 W: 2593 L: 2469 D: 13191

And a LTC regression test against SF DD to
verify we don't have regression against
weaker engines due to some kind of 'contempt'
effect:

ELO: 54.69 +-2.1 (95%) LOS: 100.0%
Total: 40000 W: 11072 L: 4827 D: 24101

bench: 8205159
2014-06-03 20:01:36 +02:00
Marco Costalba 187a9fe5e7 Use see() instead of see_sign() in previous patch
Before it was working by accident in case of
see_sign() and failing with see() due to how
castle moves are coded (king captures the rook).

Better to explicitly filter out castling moves
and use see() without any surprise/trick.

No functional case.
2014-06-03 12:38:58 +02:00
kinderchocolate 6f48367094 Add some const qualifier
No functional change.
2014-06-03 11:43:52 +02:00
Reuven Peleg 83a574ff27 Decrease reduction for moves that escape a capture
Passed both STC
LLR: 2.96 (-2.94,2.94) [-1.50,4.50]
Total: 10094 W: 1833 L: 1704 D: 6557

and LTC
LLR: 2.95 (-2.94,2.94) [0.00,6.00]
Total: 27738 W: 4147 L: 3928 D: 19663

bench : 8599236
2014-06-03 11:33:19 +02:00
Marco Costalba f4dcec0b94 Retire Polyglot Book management
Book handling belongs to GUI, we kept this code
for historical reasons, but nowdays there is
really no need of this old, (mostly) unused
and especially incorrect designed functionality.

It is up to the GUI to choose the book (far easier for
the user) and to select the book parameters. In no
place, including fishtest, TCEC, rating lists, etc.
the "own book" is used, moreover currently SF is
released without any book and even if in the future we
bundle a book in the release package, it will be the GUI
that will take care of it.

This corrects a wrong design decision that Galurung
and later Stockfish inherited from what was common
practice many yeas ago.

No functional change.
2014-06-01 15:29:34 +02:00
Marco Costalba e4fc9d84d7 Retire eval weights UCI options
There is really little that user can achieve (apart
from a weakened engine) tweaking these parameters
that are already tuned and have no immediate or visible
effect.

So better do not expose them to the user and avoid the
typical "What is the best setup for my machine?" kind of
question (by far the most common, by far the most useless).

No functional change.
2014-06-01 13:37:21 +02:00
Marco Costalba 07a525cdfd Retire polyglot.ini
All actively maintained GUI are able
to understand UCI protocol, so there
is no more need for this obsolete file.

No functional change.
2014-05-31 23:48:23 +02:00
Marco Costalba f5622cd5ec Restore development version
bench: 8732553
2014-05-31 23:34:36 +02:00
49 changed files with 4644 additions and 3028 deletions
+64
View File
@@ -0,0 +1,64 @@
# Generated with git shortlog -sn | cut -c8-', which sorts by commits (manually moved Tord to top)
Tord Romstad
Marco Costalba
Joona Kiiski
Gary Linscott
Lucas Braesch
Reuven Peleg
Chris Caino
homoSapiensSapiens
Jean-Francois Romang
jundery
Arjun Temurnikar
Uri Blass
Ralph Stößer
Stefan Geschwentner
Tom Vijlbrief
Daylen Yang
Henri Wiechers
Leonid Pechenik
mstembera
Eelco de Groot
Jerry Donald
Jörg Oster
Ryan Schmitt
mcostalba
Alexander Kure
Dan Schmidt
H. Felix Wittmann
Justin Blanchard
Linus Arver
RyanTaker
ceebo
Joseph H Ellis
Stephane Nicolet
Ajith
Andy Duplain
Auguste Pop
Balint Pfliegel
Chris Cain
Dariusz Orzechowski
Gregor Cramer
Hiraoka Takuya
Hongzhi Cheng
Jonathan Calovski
Joseph R. Prostko
Kelly Wilson
Kojirion
Matt Sullivan
Matthew Sullivan
Michel Van den Bergh
Mysseno
Pablo Vazquez
Raminder Singh
Richard Lloyd
Ron Britvich
Ronald de Man
Ryan Takker
Thanar2
kinderchocolate
pellanda
renouve
shane31
thaspel
+53 -11
View File
@@ -7,11 +7,13 @@ Partner or Fritz) in order to be used comfortably. Read the
documentation for your GUI of choice for information about how to use
Stockfish with it.
This version of Stockfish supports up to 128 CPUs. The engine defaults
This version of Stockfish supports up to 128 cores. The engine defaults
to one search thread, so it is therefore recommended to inspect the value of
the *Threads* UCI parameter, and to make sure it equals the number of CPU
cores on your computer.
This version of Stockfish has support for Syzygybases.
### Files
@@ -22,19 +24,59 @@ This distribution of Stockfish consists of the following files:
* Copying.txt, a text file containing the GNU General Public License.
* src, a subdirectory containing the full source code, including a Makefile
that can be used to compile Stockfish on Unix-like systems. For further
information about how to compile Stockfish yourself read section below.
* polyglot.ini, for using Stockfish with Fabien Letouzey's PolyGlot
adapter.
that can be used to compile Stockfish on Unix-like systems.
### Opening books
### Syzygybases
This version of Stockfish has support for PolyGlot opening books. For
information about how to create such books, consult the PolyGlot
documentation. The book file can be selected by setting the *Book File*
UCI parameter.
**Configuration**
Syzygybases are configured using the UCI options "SyzygyPath",
"SyzygyProbeDepth", "Syzygy50MoveRule" and "SyzygyProbeLimit".
The option "SyzygyPath" should be set to the directory or directories that
contain the .rtbw and .rtbz files. Multiple directories should be
separated by ";" on Windows and by ":" on Unix-based operating systems.
**Do not use spaces around the ";" or ":".**
Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6`
It is recommended to store .rtbw files on an SSD. There is no loss in
storing the .rtbz files on a regular HD.
Increasing the "SyzygyProbeDepth" option lets the engine probe less
aggressively. Set this option to a higher value if you experience too much
slowdown (in terms of nps) due to TB probing.
Set the "Syzygy50MoveRule" option to false if you want tablebase positions
that are drawn by the 50-move rule to count as win or loss. This may be useful
for correspondence games (because of tablebase adjudication).
The "SyzygyProbeLimit" option should normally be left at its default value.
**What to expect**
If the engine is searching a position that is not in the tablebases (e.g.
a position with 7 pieces), it will access the tablebases during the search.
If the engine reports a very large score (typically 123.xx), this means
that it has found a winning line into a tablebase position.
If the engine is given a position to search that is in the tablebases, it
will use the tablebases at the beginning of the search to preselect all
good moves, i.e. all moves that preserve the win or preserve the draw while
taking into account the 50-move rule.
It will then perform a search only on those moves. **The engine will not move
immediately**, unless there is only a single good move. **The engine likely
will not report a mate score even if the position is known to be won.**
It is therefore clear that behaviour is not identical to what one might
be used to with Nalimov tablebases. There are technical reasons for this
difference, the main technical reason being that Nalimov tablebases use the
DTM metric (distance-to-mate), while Syzygybases use a variation of the
DTZ metric (distance-to-zero, zero meaning any move that resets the 50-move
counter). This special metric is one of the reasons that Syzygybases are
more compact than Nalimov tablebases, while still storing all information
needed for optimal play and in addition being able to take into account
the 50-move rule.
### Compiling it yourself
-43
View File
@@ -1,43 +0,0 @@
[PolyGlot]
EngineDir = .
EngineCommand = ./stockfish
Book = false
BookFile = book.bin
Log = false
LogFile = stockfish.log
Resign = true
ResignScore = 600
[Engine]
Write Debug Log = false
Write Search Log = false
Search Log Filename = SearchLog.txt
Book File = book.bin
Best Book Move = false
Contempt Factor = 0
Mobility (Midgame) = 100
Mobility (Endgame) = 100
Pawn Structure (Midgame) = 100
Pawn Structure (Endgame) = 100
Passed Pawns (Midgame) = 100
Passed Pawns (Endgame) = 100
Space = 100
King Safety = 100
Min Split Depth = 0
Threads = 1
Hash = 128
Ponder = true
OwnBook = false
MultiPV = 1
Skill Level = 20
Emergency Move Horizon = 40
Emergency Base Time = 200
Emergency Move Time = 70
Minimum Thinking Time = 20
Slow Mover = 100
UCI_Chess960 = false
+28 -37
View File
@@ -1,6 +1,6 @@
# Stockfish, a UCI chess playing engine derived from Glaurung 2.1
# Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
# Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
# Copyright (C) 2008-2015 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
@@ -28,19 +28,15 @@ 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
PGOBENCH = ./$(EXE) bench 32 1 1 default time
PGOBENCH = ./$(EXE) bench 16 1 1000 default time
### 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
OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \
material.o misc.o movegen.o movepick.o pawns.o position.o \
search.o thread.o timeman.o tt.o uci.o ucioption.o syzygy/tbprobe.o
### ==========================================================================
### Section 2. High-level Configuration
@@ -52,7 +48,6 @@ OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \
# 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
@@ -68,7 +63,6 @@ OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \
### 2.1. General and architecture defaults
optimize = yes
debug = no
os = any
bits = 32
prefetch = no
bsfq = no
@@ -175,7 +169,7 @@ endif
ifeq ($(COMP),clang)
comp=clang
CXX=clang++
CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra -Wshadow
CXXFLAGS += -pedantic -Wno-long-long -Wextra -Wshadow
endif
ifeq ($(comp),icc)
@@ -285,7 +279,11 @@ endif
### 3.9 popcnt
ifeq ($(popcnt),yes)
CXXFLAGS += -msse3 -DUSE_POPCNT
ifeq ($(comp),icc)
CXXFLAGS += -msse3 -DUSE_POPCNT
else
CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT
endif
endif
### 3.10 pext
@@ -312,6 +310,14 @@ ifeq ($(comp),gcc)
endif
endif
### 3.12 Android 5 can only run position independent executables. Note that this
### breaks Android 4.0 and earlier.
ifeq ($(arch),armv7)
CXXFLAGS += -fPIE
LDFLAGS += -fPIE -pie
endif
### ==========================================================================
### Section 4. Public targets
### ==========================================================================
@@ -350,10 +356,6 @@ help:
@echo "clang > LLVM Clang compiler"
@echo "icc > Intel 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 build ARCH=x86-64 (This is for 64-bit systems)"
@@ -372,14 +374,14 @@ profile-build:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_prepare)
@echo ""
@echo "Step 1/4. Building executable for benchmark ..."
@touch *.cpp *.h
@touch *.cpp *.h syzygy/*.cpp syzygy/*.h
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make)
@echo ""
@echo "Step 2/4. Running benchmark for pgo-build ..."
@$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 3/4. Building final executable ..."
@touch *.cpp
@touch *.cpp *.h syzygy/*.cpp syzygy/*.h
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_use)
@echo ""
@echo "Step 4/4. Deleting profile data ..."
@@ -394,7 +396,7 @@ install:
-strip $(BINDIR)/$(EXE)
clean:
$(RM) $(EXE) $(EXE).exe *.o .depend *~ core bench.txt *.gcda
$(RM) $(EXE) $(EXE).exe *.o .depend *~ core bench.txt *.gcda ./syzygy/*.o ./syzygy/*.gcda
default:
help
@@ -411,7 +413,6 @@ config-sanity:
@echo "debug: '$(debug)'"
@echo "optimize: '$(optimize)'"
@echo "arch: '$(arch)'"
@echo "os: '$(os)'"
@echo "bits: '$(bits)'"
@echo "prefetch: '$(prefetch)'"
@echo "bsfq: '$(bsfq)'"
@@ -430,7 +431,6 @@ config-sanity:
@test "$(optimize)" = "yes" || test "$(optimize)" = "no"
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "armv7"
@test "$(os)" = "any"
@test "$(bits)" = "32" || test "$(bits)" = "64"
@test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
@test "$(bsfq)" = "yes" || test "$(bsfq)" = "no"
@@ -447,18 +447,21 @@ gcc-profile-prepare:
gcc-profile-make:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-generate' \
EXTRACXXFLAGS='-fprofile-arcs' \
EXTRALDFLAGS='-lgcov' \
all
gcc-profile-use:
# Deleting corrupt ucioption.gc* profile files is necessary to avoid an
# "internal compiler error" for gcc versions 4.7.x
@rm -f ucioption.gc*
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-use' \
EXTRACXXFLAGS='-fbranch-probabilities' \
EXTRALDFLAGS='-lgcov' \
all
gcc-profile-clean:
@rm -rf *.gcda *.gcno bench.txt
@rm -rf *.gcda *.gcno syzygy/*.gcda syzygy/*.gcno bench.txt
icc-profile-prepare:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) icc-profile-clean
@@ -482,15 +485,3 @@ icc-profile-clean:
-include .depend
### ==========================================================================
### Section 6. Non-standard targets
### ==========================================================================
hpux:
$(MAKE) \
CXX='/opt/aCC/bin/aCC -AA +hpxstd98 -mt +O3 -DNDEBUG -DNO_PREFETCH' \
CXXFLAGS="" \
LDFLAGS="" \
all
+29 -26
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -17,22 +17,24 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <fstream>
#include <iostream>
#include <istream>
#include <vector>
#include "misc.h"
#include "notation.h"
#include "position.h"
#include "search.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h"
#include "uci.h"
using namespace std;
static const char* Defaults[] = {
namespace {
const char* Defaults[] = {
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11",
@@ -62,9 +64,23 @@ static const char* Defaults[] = {
"6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1",
"1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
"6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1",
"8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1"
"8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1",
// 5-man positions
"8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
"8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate
"8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw
// 6-man positions
"8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate
"8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate
"8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw
// 7-man positions
"8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw
};
} // namespace
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set
/// of positions for a given limit each. There are five parameters: the
@@ -72,7 +88,7 @@ static const char* Defaults[] = {
/// be used, the limit value spent for each position (optional, default is
/// depth 13), an optional file name where to look for positions in FEN
/// format (defaults are the positions defined above) and the type of the
/// limit value: depth (default), time in secs or number of nodes.
/// limit value: depth (default), time in millisecs or number of nodes.
void benchmark(const Position& current, istream& is) {
@@ -81,7 +97,7 @@ void benchmark(const Position& current, istream& is) {
vector<string> fens;
// Assign default values to missing arguments
string ttSize = (is >> token) ? token : "32";
string ttSize = (is >> token) ? token : "16";
string threads = (is >> token) ? token : "1";
string limit = (is >> token) ? token : "13";
string fenFile = (is >> token) ? token : "default";
@@ -92,7 +108,7 @@ void benchmark(const Position& current, istream& is) {
TT.clear();
if (limitType == "time")
limits.movetime = 1000 * atoi(limit.c_str()); // movetime is in ms
limits.movetime = atoi(limit.c_str()); // movetime is in ms
else if (limitType == "nodes")
limits.nodes = atoi(limit.c_str());
@@ -104,7 +120,7 @@ void benchmark(const Position& current, istream& is) {
limits.depth = atoi(limit.c_str());
if (fenFile == "default")
fens.assign(Defaults, Defaults + 30);
fens.assign(Defaults, Defaults + 37);
else if (fenFile == "current")
fens.push_back(current.fen());
@@ -137,22 +153,9 @@ void benchmark(const Position& current, istream& is) {
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;
if (limitType == "divide")
for (MoveList<LEGAL> it(pos); *it; ++it)
{
StateInfo si;
pos.do_move(*it, si);
uint64_t cnt = limits.depth > 1 ? Search::perft(pos, (limits.depth - 1) * ONE_PLY) : 1;
pos.undo_move(*it);
cerr << move_to_uci(*it, pos.is_chess960()) << ": " << cnt << endl;
nodes += cnt;
}
else if (limitType == "perft")
{
uint64_t cnt = Search::perft(pos, limits.depth * ONE_PLY);
cerr << "\nPerft " << limits.depth << " leaf nodes: " << cnt << endl;
nodes += cnt;
}
if (limitType == "perft")
nodes += Search::perft<true>(pos, limits.depth * ONE_PLY);
else
{
Threads.start_thinking(pos, limits, st);
@@ -161,7 +164,7 @@ void benchmark(const Position& current, istream& is) {
}
}
elapsed = Time::now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
elapsed = std::max(Time::now() - elapsed, Time::point(1)); // Avoid a 'divide by zero'
dbg_print(); // Just before to exit
+11 -11
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -41,7 +41,7 @@ namespace {
// bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2)
unsigned index(Color us, Square bksq, Square wksq, Square psq) {
return wksq + (bksq << 6) + (us << 12) + (file_of(psq) << 13) + ((RANK_7 - rank_of(psq)) << 15);
return wksq | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15);
}
enum Result {
@@ -71,7 +71,7 @@ namespace {
} // namespace
bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) {
bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color us) {
assert(file_of(wpsq) <= FILE_D);
@@ -80,7 +80,7 @@ bool Bitbases::probe_kpk(Square wksq, Square wpsq, Square bksq, Color us) {
}
void Bitbases::init_kpk() {
void Bitbases::init() {
unsigned idx, repeat = 1;
std::vector<KPKPosition> db;
@@ -107,14 +107,14 @@ namespace {
KPKPosition::KPKPosition(unsigned idx) {
wksq = Square((idx >> 0) & 0x3F);
bksq = Square((idx >> 6) & 0x3F);
us = Color ((idx >> 12) & 0x01);
psq = make_square(File((idx >> 13) & 0x03), Rank(RANK_7 - (idx >> 15)));
result = UNKNOWN;
wksq = Square((idx >> 0) & 0x3F);
bksq = Square((idx >> 6) & 0x3F);
us = Color ((idx >> 12) & 0x01);
psq = make_square(File((idx >> 13) & 0x3), RANK_7 - Rank((idx >> 15) & 0x7));
result = UNKNOWN;
// Check if two pieces are on the same square or if a king can be captured
if ( square_distance(wksq, bksq) <= 1
if ( distance(wksq, bksq) <= 1
|| wksq == psq
|| bksq == psq
|| (us == WHITE && (StepAttacksBB[PAWN][psq] & bksq)))
@@ -125,7 +125,7 @@ namespace {
// Immediate win if a pawn can be promoted without getting captured
if ( rank_of(psq) == RANK_7
&& wksq != psq + DELTA_N
&& ( square_distance(bksq, psq + DELTA_N) > 1
&& ( distance(bksq, psq + DELTA_N) > 1
||(StepAttacksBB[KING][wksq] & (psq + DELTA_N))))
result = WIN;
}
+49 -55
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -18,23 +18,23 @@
*/
#include <algorithm>
#include <cstring> // For memset
#include <cstring> // For std::memset
#include "bitboard.h"
#include "bitcount.h"
#include "rkiss.h"
#include "misc.h"
CACHE_LINE_ALIGNMENT
int SquareDistance[SQUARE_NB][SQUARE_NB];
Bitboard RMasks[SQUARE_NB];
Bitboard RMagics[SQUARE_NB];
Bitboard* RAttacks[SQUARE_NB];
unsigned RShifts[SQUARE_NB];
Bitboard RookMasks [SQUARE_NB];
Bitboard RookMagics [SQUARE_NB];
Bitboard* RookAttacks[SQUARE_NB];
unsigned RookShifts [SQUARE_NB];
Bitboard BMasks[SQUARE_NB];
Bitboard BMagics[SQUARE_NB];
Bitboard* BAttacks[SQUARE_NB];
unsigned BShifts[SQUARE_NB];
Bitboard BishopMasks [SQUARE_NB];
Bitboard BishopMagics [SQUARE_NB];
Bitboard* BishopAttacks[SQUARE_NB];
unsigned BishopShifts [SQUARE_NB];
Bitboard SquareBB[SQUARE_NB];
Bitboard FileBB[FILE_NB];
@@ -44,53 +44,44 @@ Bitboard InFrontBB[COLOR_NB][RANK_NB];
Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard DistanceRingsBB[SQUARE_NB][8];
Bitboard DistanceRingBB[SQUARE_NB][8];
Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
int SquareDistance[SQUARE_NB][SQUARE_NB];
namespace {
// De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
const uint64_t DeBruijn_64 = 0x3F79D71B4CB0A89ULL;
const uint32_t DeBruijn_32 = 0x783A9B23;
const uint64_t DeBruijn64 = 0x3F79D71B4CB0A89ULL;
const uint32_t DeBruijn32 = 0x783A9B23;
CACHE_LINE_ALIGNMENT
int MS1BTable[256];
Square BSFTable[SQUARE_NB];
Bitboard RTable[0x19000]; // Storage space for rook attacks
Bitboard BTable[0x1480]; // Storage space for bishop attacks
int MS1BTable[256]; // To implement software msb()
Square BSFTable[SQUARE_NB]; // To implement software bitscan
Bitboard RookTable[0x19000]; // To store rook attacks
Bitboard BishopTable[0x1480]; // To store bishop 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) {
// bsf_index() returns the index into BSFTable[] to look up the bitscan. Uses
// Matt Taylor's folding for 32 bit case, extended to 64 bit by Kim Walisch.
// Matt Taylor's folding for 32 bit systems, extended to 64 bits by Kim Walisch
b ^= (b - 1);
return Is64Bit ? (b * DeBruijn_64) >> 58
: ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26;
FORCE_INLINE unsigned bsf_index(Bitboard b) {
b ^= b - 1;
return Is64Bit ? (b * DeBruijn64) >> 58
: ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn32) >> 26;
}
}
/// lsb()/msb() finds the least/most significant bit in a non-zero bitboard.
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard.
#ifndef USE_BSFQ
Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; }
/// Software fall-back of lsb() and msb() for CPU lacking hardware support
Square pop_lsb(Bitboard* b) {
Bitboard bb = *b;
*b = bb & (bb - 1);
return BSFTable[bsf_index(bb)];
Square lsb(Bitboard b) {
return BSFTable[bsf_index(b)];
}
Square msb(Bitboard b) {
@@ -124,8 +115,8 @@ Square msb(Bitboard b) {
#endif // ifndef USE_BSFQ
/// Bitboards::pretty() returns an ASCII representation of a bitboard to be
/// printed to standard output. This is sometimes useful for debugging.
/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable
/// to be printed to standard output. Useful for debugging.
const std::string Bitboards::pretty(Bitboard b) {
@@ -149,7 +140,10 @@ const std::string Bitboards::pretty(Bitboard b) {
void Bitboards::init() {
for (Square s = SQ_A1; s <= SQ_H8; ++s)
BSFTable[bsf_index(SquareBB[s] = 1ULL << s)] = s;
{
SquareBB[s] = 1ULL << s;
BSFTable[bsf_index(SquareBB[s])] = s;
}
for (Bitboard b = 1; b < 256; ++b)
MS1BTable[b] = more_than_one(b) ? MS1BTable[b - 1] : lsb(b);
@@ -178,8 +172,8 @@ void Bitboards::init() {
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
if (s1 != s2)
{
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
DistanceRingsBB[s1][SquareDistance[s1][s2] - 1] |= s2;
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
DistanceRingBB[s1][SquareDistance[s1][s2] - 1] |= s2;
}
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
@@ -192,15 +186,15 @@ void Bitboards::init() {
{
Square to = s + Square(c == WHITE ? steps[pt][i] : -steps[pt][i]);
if (is_ok(to) && square_distance(s, to) < 3)
if (is_ok(to) && 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 };
Square RookDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W };
Square BishopDeltas[] = { 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>);
init_magics(RookTable, RookAttacks, RookMagics, RookMasks, RookShifts, RookDeltas, magic_index<ROOK>);
init_magics(BishopTable, BishopAttacks, BishopMagics, BishopMasks, BishopShifts, BishopDeltas, magic_index<BISHOP>);
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
{
@@ -230,7 +224,7 @@ namespace {
for (int i = 0; i < 4; ++i)
for (Square s = sq + deltas[i];
is_ok(s) && square_distance(s, s - deltas[i]) == 1;
is_ok(s) && distance(s, s - deltas[i]) == 1;
s += deltas[i])
{
attack |= s;
@@ -251,12 +245,11 @@ namespace {
void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) {
int MagicBoosters[][8] = { { 969, 1976, 2850, 542, 2069, 2852, 1708, 164 },
{ 3101, 552, 3555, 926, 834, 26, 2131, 1117 } };
int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 },
{ 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } };
RKISS rk;
Bitboard occupancy[4096], reference[4096], edges, b;
int i, size, booster;
int i, size;
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
attacks[SQ_A1] = table;
@@ -282,7 +275,7 @@ namespace {
reference[size] = sliding_attack(deltas, s, b);
if (HasPext)
attacks[s][_pext_u64(b, masks[s])] = reference[size];
attacks[s][pext(b, masks[s])] = reference[size];
size++;
b = (b - masks[s]) & masks[s];
@@ -296,12 +289,13 @@ namespace {
if (HasPext)
continue;
booster = MagicBoosters[Is64Bit][rank_of(s)];
PRNG rng(seeds[Is64Bit][rank_of(s)]);
// Find a magic for square 's' picking up an (almost) random number
// until we find the one that passes the verification test.
do {
do magics[s] = rk.magic_rand<Bitboard>(booster);
do
magics[s] = rng.sparse_rand<Bitboard>();
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
std::memset(attacks[s], 0, size * sizeof(Bitboard));
+114 -115
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -21,8 +21,17 @@
#ifndef BITBOARD_H_INCLUDED
#define BITBOARD_H_INCLUDED
#include <string>
#include "types.h"
namespace Bitbases {
void init();
bool probe(Square wksq, Square wpsq, Square bksq, Color us);
}
namespace Bitboards {
void init();
@@ -30,12 +39,7 @@ const std::string pretty(Bitboard b);
}
namespace Bitbases {
void init_kpk();
bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us);
}
const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1;
@@ -55,17 +59,17 @@ const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
CACHE_LINE_ALIGNMENT
extern int SquareDistance[SQUARE_NB][SQUARE_NB];
extern Bitboard RMasks[SQUARE_NB];
extern Bitboard RMagics[SQUARE_NB];
extern Bitboard* RAttacks[SQUARE_NB];
extern unsigned RShifts[SQUARE_NB];
extern Bitboard RookMasks [SQUARE_NB];
extern Bitboard RookMagics [SQUARE_NB];
extern Bitboard* RookAttacks[SQUARE_NB];
extern unsigned RookShifts [SQUARE_NB];
extern Bitboard BMasks[SQUARE_NB];
extern Bitboard BMagics[SQUARE_NB];
extern Bitboard* BAttacks[SQUARE_NB];
extern unsigned BShifts[SQUARE_NB];
extern Bitboard BishopMasks [SQUARE_NB];
extern Bitboard BishopMagics [SQUARE_NB];
extern Bitboard* BishopAttacks[SQUARE_NB];
extern unsigned BishopShifts [SQUARE_NB];
extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard FileBB[FILE_NB];
@@ -75,15 +79,12 @@ extern Bitboard InFrontBB[COLOR_NB][RANK_NB];
extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
extern Bitboard DistanceRingsBB[SQUARE_NB][8];
extern Bitboard DistanceRingBB[SQUARE_NB][8];
extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
extern int SquareDistance[SQUARE_NB][SQUARE_NB];
const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
/// 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.
@@ -92,14 +93,6 @@ inline Bitboard operator&(Bitboard b, Square s) {
return b & SquareBB[s];
}
inline Bitboard& operator|=(Bitboard& b, Square s) {
return b |= SquareBB[s];
}
inline Bitboard& operator^=(Bitboard& b, Square s) {
return b ^= SquareBB[s];
}
inline Bitboard operator|(Bitboard b, Square s) {
return b | SquareBB[s];
}
@@ -108,37 +101,21 @@ inline Bitboard operator^(Bitboard b, Square s) {
return b ^ SquareBB[s];
}
inline Bitboard& operator|=(Bitboard& b, Square s) {
return b |= SquareBB[s];
}
inline Bitboard& operator^=(Bitboard& b, Square s) {
return b ^= SquareBB[s];
}
inline bool more_than_one(Bitboard b) {
return b & (b - 1);
}
inline int square_distance(Square s1, Square s2) {
return SquareDistance[s1][s2];
}
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));
}
/// shift_bb() moves bitboard one step along direction Delta. Mainly for pawns.
template<Square Delta>
inline Bitboard shift_bb(Bitboard b) {
return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8
: Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7
: Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9
: 0;
}
/// rank_bb() and file_bb() take a file or a square as input and return
/// a bitboard representing all squares on the given file or rank.
/// rank_bb() and file_bb() return a bitboard representing all the squares on
/// the given file or rank.
inline Bitboard rank_bb(Rank r) {
return RankBB[r];
@@ -157,119 +134,138 @@ inline Bitboard file_bb(Square s) {
}
/// adjacent_files_bb() takes a file as input and returns a bitboard representing
/// all squares on the adjacent files.
/// shift_bb() moves a bitboard one step along direction Delta. Mainly for pawns
template<Square Delta>
inline Bitboard shift_bb(Bitboard b) {
return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8
: Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7
: Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9
: 0;
}
/// adjacent_files_bb() returns a bitboard representing all the squares on the
/// adjacent files of the given one.
inline Bitboard adjacent_files_bb(File f) {
return AdjacentFilesBB[f];
}
/// in_front_bb() takes a color and a rank as input, and returns a bitboard
/// representing all the squares on all ranks in front of the rank, from the
/// given color's point of view. For instance, in_front_bb(BLACK, RANK_3) will
/// give all squares on ranks 1 and 2.
inline Bitboard in_front_bb(Color c, Rank r) {
return InFrontBB[c][r];
}
/// between_bb() returns a bitboard representing all squares between two squares.
/// For instance, between_bb(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 rank, file or diagonal,
/// 0 is returned.
/// between_bb() returns a bitboard representing all the squares between the two
/// given ones. For instance, between_bb(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 rank, file
/// or diagonal, 0 is returned.
inline Bitboard between_bb(Square s1, Square s2) {
return BetweenBB[s1][s2];
}
/// forward_bb() takes a color and a square as input, and returns a bitboard
/// representing all squares along the line in front of the square, from the
/// point of view of the given color. Definition of the table is:
/// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s)
/// in_front_bb() returns a bitboard representing all the squares on all the ranks
/// in front of the given one, from the point of view of the given color. For
/// instance, in_front_bb(BLACK, RANK_3) will return the squares on ranks 1 and 2.
inline Bitboard in_front_bb(Color c, Rank r) {
return InFrontBB[c][r];
}
/// forward_bb() returns a bitboard representing all the squares along the line
/// in front of the given one, from the point of view of the given color:
/// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s)
inline Bitboard forward_bb(Color c, Square s) {
return ForwardBB[c][s];
}
/// pawn_attack_span() 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
/// when it moves along its file starting from the given square. Definition is:
/// PawnAttackSpan[c][s] = in_front_bb(c, s) & adjacent_files_bb(s);
/// pawn_attack_span() returns a bitboard representing all the squares that can be
/// attacked by a pawn of the given color when it moves along its file, starting
/// from the given square:
/// PawnAttackSpan[c][s] = in_front_bb(c, s) & adjacent_files_bb(s);
inline Bitboard pawn_attack_span(Color c, Square s) {
return PawnAttackSpan[c][s];
}
/// passed_pawn_mask() takes a color and a square as input, and returns a
/// bitboard mask which can be used to test if a pawn of the given color on
/// the given square is a passed pawn. Definition of the table is:
/// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_bb(c, s)
/// passed_pawn_mask() returns a bitboard mask which can be used to test if a
/// pawn of the given color and on the given square is a passed pawn:
/// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_bb(c, s)
inline Bitboard passed_pawn_mask(Color c, Square s) {
return PassedPawnMask[c][s];
}
/// squares_of_color() returns a bitboard representing all squares with the same
/// color of the given square.
/// squares_of_color() returns a bitboard representing all the squares of the
/// same color of the given one.
inline Bitboard squares_of_color(Square s) {
return DarkSquares & s ? DarkSquares : ~DarkSquares;
}
/// aligned() returns true if the squares s1, s2 and s3 are aligned
/// either on a straight or on a diagonal line.
/// aligned() returns true if the squares s1, s2 and s3 are aligned either on a
/// straight or on a diagonal line.
inline bool aligned(Square s1, Square s2, Square s3) {
return LineBB[s1][s2] & s3;
}
/// Functions for computing sliding attack bitboards. Function attacks_bb() takes
/// a square and a bitboard of occupied squares as input, and returns a bitboard
/// 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) {
/// distance() functions return the distance between x and y, defined as the
/// number of steps for a king in x to reach y. Works with squares, ranks, files.
Bitboard* const Masks = Pt == ROOK ? RMasks : BMasks;
Bitboard* const Magics = Pt == ROOK ? RMagics : BMagics;
unsigned* const Shifts = Pt == ROOK ? RShifts : BShifts;
template<typename T> inline int distance(T x, T y) { return x < y ? y - x : x - y; }
template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; }
template<typename T1, typename T2> inline int distance(T2 x, T2 y);
template<> inline int distance<File>(Square x, Square y) { return distance(file_of(x), file_of(y)); }
template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_of(x), rank_of(y)); }
/// attacks_bb() returns a bitboard representing all the squares attacked by a
/// piece of type Pt (bishop or rook) placed on 's'. The helper magic_index()
/// looks up the index using the 'magic bitboards' approach.
template<PieceType Pt>
FORCE_INLINE unsigned magic_index(Square s, Bitboard occupied) {
Bitboard* const Masks = Pt == ROOK ? RookMasks : BishopMasks;
Bitboard* const Magics = Pt == ROOK ? RookMagics : BishopMagics;
unsigned* const Shifts = Pt == ROOK ? RookShifts : BishopShifts;
if (HasPext)
return unsigned(_pext_u64(occ, Masks[s]));
return unsigned(pext(occupied, Masks[s]));
if (Is64Bit)
return unsigned(((occ & Masks[s]) * Magics[s]) >> Shifts[s]);
return unsigned(((occupied & Masks[s]) * Magics[s]) >> Shifts[s]);
unsigned lo = unsigned(occ) & unsigned(Masks[s]);
unsigned hi = unsigned(occ >> 32) & unsigned(Masks[s] >> 32);
unsigned lo = unsigned(occupied) & unsigned(Masks[s]);
unsigned hi = unsigned(occupied >> 32) & unsigned(Masks[s] >> 32);
return (lo * unsigned(Magics[s]) ^ hi * unsigned(Magics[s] >> 32)) >> Shifts[s];
}
template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Bitboard occ) {
return (Pt == ROOK ? RAttacks : BAttacks)[s][magic_index<Pt>(s, occ)];
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
return (Pt == ROOK ? RookAttacks : BishopAttacks)[s][magic_index<Pt>(s, occupied)];
}
inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occ) {
inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) {
switch (type_of(pc))
{
case BISHOP: return attacks_bb<BISHOP>(s, occ);
case ROOK : return attacks_bb<ROOK>(s, occ);
case QUEEN : return attacks_bb<BISHOP>(s, occ) | attacks_bb<ROOK>(s, occ);
case BISHOP: return attacks_bb<BISHOP>(s, occupied);
case ROOK : return attacks_bb<ROOK>(s, occupied);
case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
default : return StepAttacksBB[pc][s];
}
}
/// lsb()/msb() finds the least/most significant bit in a non-zero bitboard.
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard.
/// lsb() and msb() return the least/most significant bit in a non-zero bitboard
#ifdef USE_BSFQ
@@ -302,7 +298,7 @@ FORCE_INLINE Square lsb(Bitboard b) {
return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32)));
}
# else
# else // Assumed gcc or compatible compiler
FORCE_INLINE Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen
Bitboard idx;
@@ -318,21 +314,24 @@ FORCE_INLINE Square msb(Bitboard b) {
# endif
#else // ifdef(USE_BSFQ)
Square lsb(Bitboard b);
Square msb(Bitboard b);
#endif
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard
FORCE_INLINE Square pop_lsb(Bitboard* b) {
const Square s = lsb(*b);
*b &= *b - 1;
return s;
}
#else // if defined(USE_BSFQ)
extern Square msb(Bitboard b);
extern Square lsb(Bitboard b);
extern Square pop_lsb(Bitboard* b);
#endif
/// frontmost_sq() and backmost_sq() find the square corresponding to the
/// frontmost_sq() and backmost_sq() return the square corresponding to the
/// most/least advanced bit relative to the given color.
inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); }
+5 -5
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -22,6 +22,7 @@
#define BITCOUNT_H_INCLUDED
#include <cassert>
#include "types.h"
enum BitCountType {
@@ -35,7 +36,7 @@ enum BitCountType {
/// Determine at compile time the best popcount<> specialization according to
/// whether the platform is 32 or 64 bit, the maximum number of non-zero
/// bits to count and if the hardware popcnt instruction is available.
const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
@@ -94,10 +95,9 @@ inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
return (int)__popcnt64(b);
#else
#else // Assumed gcc or compatible compiler
__asm__("popcnt %1, %0" : "=r" (b) : "r" (b));
return b;
return __builtin_popcountll(b);
#endif
}
-478
View File
@@ -1,478 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 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/>.
*/
/*
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
*/
#include <algorithm>
#include <cassert>
#include "book.h"
#include "misc.h"
#include "movegen.h"
using namespace std;
namespace {
// A Polyglot book is a series of "entries" of 16 bytes. All integers are
// stored in big-endian format, with the highest byte first (regardless of
// size). The entries are ordered according to the key in ascending order.
struct Entry {
uint64_t key;
uint16_t move;
uint16_t count;
uint32_t learn;
};
// Random numbers from PolyGlot, used to compute book hash keys
const union {
Key PolyGlotRandoms[781];
struct {
Key psq[12][64]; // [piece][square]
Key castling[4]; // [castling flag]
Key enpassant[8]; // [file]
Key turn;
} Zobrist;
} PG = {{
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
0x1A083822CEAFE02DULL, 0x9605D5F0E25EC3B0ULL, 0xD021FF5CD13A2ED5ULL,
0x40BDF15D4A672E32ULL, 0x011355146FD56395ULL, 0x5DB4832046F3D9E5ULL,
0x239F8B2D7FF719CCULL, 0x05D1A1AE85B49AA1ULL, 0x679F848F6E8FC971ULL,
0x7449BBFF801FED0BULL, 0x7D11CDB1C3B7ADF0ULL, 0x82C7709E781EB7CCULL,
0xF3218F1C9510786CULL, 0x331478F3AF51BBE6ULL, 0x4BB38DE5E7219443ULL,
0xAA649C6EBCFD50FCULL, 0x8DBD98A352AFD40BULL, 0x87D2074B81D79217ULL,
0x19F3C751D3E92AE1ULL, 0xB4AB30F062B19ABFULL, 0x7B0500AC42047AC4ULL,
0xC9452CA81A09D85DULL, 0x24AA6C514DA27500ULL, 0x4C9F34427501B447ULL,
0x14A68FD73C910841ULL, 0xA71B9B83461CBD93ULL, 0x03488B95B0F1850FULL,
0x637B2B34FF93C040ULL, 0x09D1BC9A3DD90A94ULL, 0x3575668334A1DD3BULL,
0x735E2B97A4C45A23ULL, 0x18727070F1BD400BULL, 0x1FCBACD259BF02E7ULL,
0xD310A7C2CE9B6555ULL, 0xBF983FE0FE5D8244ULL, 0x9F74D14F7454A824ULL,
0x51EBDC4AB9BA3035ULL, 0x5C82C505DB9AB0FAULL, 0xFCF7FE8A3430B241ULL,
0x3253A729B9BA3DDEULL, 0x8C74C368081B3075ULL, 0xB9BC6C87167C33E7ULL,
0x7EF48F2B83024E20ULL, 0x11D505D4C351BD7FULL, 0x6568FCA92C76A243ULL,
0x4DE0B0F40F32A7B8ULL, 0x96D693460CC37E5DULL, 0x42E240CB63689F2FULL,
0x6D2BDCDAE2919661ULL, 0x42880B0236E4D951ULL, 0x5F0F4A5898171BB6ULL,
0x39F890F579F92F88ULL, 0x93C5B5F47356388BULL, 0x63DC359D8D231B78ULL,
0xEC16CA8AEA98AD76ULL, 0x5355F900C2A82DC7ULL, 0x07FB9F855A997142ULL,
0x5093417AA8A7ED5EULL, 0x7BCBC38DA25A7F3CULL, 0x19FC8A768CF4B6D4ULL,
0x637A7780DECFC0D9ULL, 0x8249A47AEE0E41F7ULL, 0x79AD695501E7D1E8ULL,
0x14ACBAF4777D5776ULL, 0xF145B6BECCDEA195ULL, 0xDABF2AC8201752FCULL,
0x24C3C94DF9C8D3F6ULL, 0xBB6E2924F03912EAULL, 0x0CE26C0B95C980D9ULL,
0xA49CD132BFBF7CC4ULL, 0xE99D662AF4243939ULL, 0x27E6AD7891165C3FULL,
0x8535F040B9744FF1ULL, 0x54B3F4FA5F40D873ULL, 0x72B12C32127FED2BULL,
0xEE954D3C7B411F47ULL, 0x9A85AC909A24EAA1ULL, 0x70AC4CD9F04F21F5ULL,
0xF9B89D3E99A075C2ULL, 0x87B3E2B2B5C907B1ULL, 0xA366E5B8C54F48B8ULL,
0xAE4A9346CC3F7CF2ULL, 0x1920C04D47267BBDULL, 0x87BF02C6B49E2AE9ULL,
0x092237AC237F3859ULL, 0xFF07F64EF8ED14D0ULL, 0x8DE8DCA9F03CC54EULL,
0x9C1633264DB49C89ULL, 0xB3F22C3D0B0B38EDULL, 0x390E5FB44D01144BULL,
0x5BFEA5B4712768E9ULL, 0x1E1032911FA78984ULL, 0x9A74ACB964E78CB3ULL,
0x4F80F7A035DAFB04ULL, 0x6304D09A0B3738C4ULL, 0x2171E64683023A08ULL,
0x5B9B63EB9CEFF80CULL, 0x506AACF489889342ULL, 0x1881AFC9A3A701D6ULL,
0x6503080440750644ULL, 0xDFD395339CDBF4A7ULL, 0xEF927DBCF00C20F2ULL,
0x7B32F7D1E03680ECULL, 0xB9FD7620E7316243ULL, 0x05A7E8A57DB91B77ULL,
0xB5889C6E15630A75ULL, 0x4A750A09CE9573F7ULL, 0xCF464CEC899A2F8AULL,
0xF538639CE705B824ULL, 0x3C79A0FF5580EF7FULL, 0xEDE6C87F8477609DULL,
0x799E81F05BC93F31ULL, 0x86536B8CF3428A8CULL, 0x97D7374C60087B73ULL,
0xA246637CFF328532ULL, 0x043FCAE60CC0EBA0ULL, 0x920E449535DD359EULL,
0x70EB093B15B290CCULL, 0x73A1921916591CBDULL, 0x56436C9FE1A1AA8DULL,
0xEFAC4B70633B8F81ULL, 0xBB215798D45DF7AFULL, 0x45F20042F24F1768ULL,
0x930F80F4E8EB7462ULL, 0xFF6712FFCFD75EA1ULL, 0xAE623FD67468AA70ULL,
0xDD2C5BC84BC8D8FCULL, 0x7EED120D54CF2DD9ULL, 0x22FE545401165F1CULL,
0xC91800E98FB99929ULL, 0x808BD68E6AC10365ULL, 0xDEC468145B7605F6ULL,
0x1BEDE3A3AEF53302ULL, 0x43539603D6C55602ULL, 0xAA969B5C691CCB7AULL,
0xA87832D392EFEE56ULL, 0x65942C7B3C7E11AEULL, 0xDED2D633CAD004F6ULL,
0x21F08570F420E565ULL, 0xB415938D7DA94E3CULL, 0x91B859E59ECB6350ULL,
0x10CFF333E0ED804AULL, 0x28AED140BE0BB7DDULL, 0xC5CC1D89724FA456ULL,
0x5648F680F11A2741ULL, 0x2D255069F0B7DAB3ULL, 0x9BC5A38EF729ABD4ULL,
0xEF2F054308F6A2BCULL, 0xAF2042F5CC5C2858ULL, 0x480412BAB7F5BE2AULL,
0xAEF3AF4A563DFE43ULL, 0x19AFE59AE451497FULL, 0x52593803DFF1E840ULL,
0xF4F076E65F2CE6F0ULL, 0x11379625747D5AF3ULL, 0xBCE5D2248682C115ULL,
0x9DA4243DE836994FULL, 0x066F70B33FE09017ULL, 0x4DC4DE189B671A1CULL,
0x51039AB7712457C3ULL, 0xC07A3F80C31FB4B4ULL, 0xB46EE9C5E64A6E7CULL,
0xB3819A42ABE61C87ULL, 0x21A007933A522A20ULL, 0x2DF16F761598AA4FULL,
0x763C4A1371B368FDULL, 0xF793C46702E086A0ULL, 0xD7288E012AEB8D31ULL,
0xDE336A2A4BC1C44BULL, 0x0BF692B38D079F23ULL, 0x2C604A7A177326B3ULL,
0x4850E73E03EB6064ULL, 0xCFC447F1E53C8E1BULL, 0xB05CA3F564268D99ULL,
0x9AE182C8BC9474E8ULL, 0xA4FC4BD4FC5558CAULL, 0xE755178D58FC4E76ULL,
0x69B97DB1A4C03DFEULL, 0xF9B5B7C4ACC67C96ULL, 0xFC6A82D64B8655FBULL,
0x9C684CB6C4D24417ULL, 0x8EC97D2917456ED0ULL, 0x6703DF9D2924E97EULL,
0xC547F57E42A7444EULL, 0x78E37644E7CAD29EULL, 0xFE9A44E9362F05FAULL,
0x08BD35CC38336615ULL, 0x9315E5EB3A129ACEULL, 0x94061B871E04DF75ULL,
0xDF1D9F9D784BA010ULL, 0x3BBA57B68871B59DULL, 0xD2B7ADEEDED1F73FULL,
0xF7A255D83BC373F8ULL, 0xD7F4F2448C0CEB81ULL, 0xD95BE88CD210FFA7ULL,
0x336F52F8FF4728E7ULL, 0xA74049DAC312AC71ULL, 0xA2F61BB6E437FDB5ULL,
0x4F2A5CB07F6A35B3ULL, 0x87D380BDA5BF7859ULL, 0x16B9F7E06C453A21ULL,
0x7BA2484C8A0FD54EULL, 0xF3A678CAD9A2E38CULL, 0x39B0BF7DDE437BA2ULL,
0xFCAF55C1BF8A4424ULL, 0x18FCF680573FA594ULL, 0x4C0563B89F495AC3ULL,
0x40E087931A00930DULL, 0x8CFFA9412EB642C1ULL, 0x68CA39053261169FULL,
0x7A1EE967D27579E2ULL, 0x9D1D60E5076F5B6FULL, 0x3810E399B6F65BA2ULL,
0x32095B6D4AB5F9B1ULL, 0x35CAB62109DD038AULL, 0xA90B24499FCFAFB1ULL,
0x77A225A07CC2C6BDULL, 0x513E5E634C70E331ULL, 0x4361C0CA3F692F12ULL,
0xD941ACA44B20A45BULL, 0x528F7C8602C5807BULL, 0x52AB92BEB9613989ULL,
0x9D1DFA2EFC557F73ULL, 0x722FF175F572C348ULL, 0x1D1260A51107FE97ULL,
0x7A249A57EC0C9BA2ULL, 0x04208FE9E8F7F2D6ULL, 0x5A110C6058B920A0ULL,
0x0CD9A497658A5698ULL, 0x56FD23C8F9715A4CULL, 0x284C847B9D887AAEULL,
0x04FEABFBBDB619CBULL, 0x742E1E651C60BA83ULL, 0x9A9632E65904AD3CULL,
0x881B82A13B51B9E2ULL, 0x506E6744CD974924ULL, 0xB0183DB56FFC6A79ULL,
0x0ED9B915C66ED37EULL, 0x5E11E86D5873D484ULL, 0xF678647E3519AC6EULL,
0x1B85D488D0F20CC5ULL, 0xDAB9FE6525D89021ULL, 0x0D151D86ADB73615ULL,
0xA865A54EDCC0F019ULL, 0x93C42566AEF98FFBULL, 0x99E7AFEABE000731ULL,
0x48CBFF086DDF285AULL, 0x7F9B6AF1EBF78BAFULL, 0x58627E1A149BBA21ULL,
0x2CD16E2ABD791E33ULL, 0xD363EFF5F0977996ULL, 0x0CE2A38C344A6EEDULL,
0x1A804AADB9CFA741ULL, 0x907F30421D78C5DEULL, 0x501F65EDB3034D07ULL,
0x37624AE5A48FA6E9ULL, 0x957BAF61700CFF4EULL, 0x3A6C27934E31188AULL,
0xD49503536ABCA345ULL, 0x088E049589C432E0ULL, 0xF943AEE7FEBF21B8ULL,
0x6C3B8E3E336139D3ULL, 0x364F6FFA464EE52EULL, 0xD60F6DCEDC314222ULL,
0x56963B0DCA418FC0ULL, 0x16F50EDF91E513AFULL, 0xEF1955914B609F93ULL,
0x565601C0364E3228ULL, 0xECB53939887E8175ULL, 0xBAC7A9A18531294BULL,
0xB344C470397BBA52ULL, 0x65D34954DAF3CEBDULL, 0xB4B81B3FA97511E2ULL,
0xB422061193D6F6A7ULL, 0x071582401C38434DULL, 0x7A13F18BBEDC4FF5ULL,
0xBC4097B116C524D2ULL, 0x59B97885E2F2EA28ULL, 0x99170A5DC3115544ULL,
0x6F423357E7C6A9F9ULL, 0x325928EE6E6F8794ULL, 0xD0E4366228B03343ULL,
0x565C31F7DE89EA27ULL, 0x30F5611484119414ULL, 0xD873DB391292ED4FULL,
0x7BD94E1D8E17DEBCULL, 0xC7D9F16864A76E94ULL, 0x947AE053EE56E63CULL,
0xC8C93882F9475F5FULL, 0x3A9BF55BA91F81CAULL, 0xD9A11FBB3D9808E4ULL,
0x0FD22063EDC29FCAULL, 0xB3F256D8ACA0B0B9ULL, 0xB03031A8B4516E84ULL,
0x35DD37D5871448AFULL, 0xE9F6082B05542E4EULL, 0xEBFAFA33D7254B59ULL,
0x9255ABB50D532280ULL, 0xB9AB4CE57F2D34F3ULL, 0x693501D628297551ULL,
0xC62C58F97DD949BFULL, 0xCD454F8F19C5126AULL, 0xBBE83F4ECC2BDECBULL,
0xDC842B7E2819E230ULL, 0xBA89142E007503B8ULL, 0xA3BC941D0A5061CBULL,
0xE9F6760E32CD8021ULL, 0x09C7E552BC76492FULL, 0x852F54934DA55CC9ULL,
0x8107FCCF064FCF56ULL, 0x098954D51FFF6580ULL, 0x23B70EDB1955C4BFULL,
0xC330DE426430F69DULL, 0x4715ED43E8A45C0AULL, 0xA8D7E4DAB780A08DULL,
0x0572B974F03CE0BBULL, 0xB57D2E985E1419C7ULL, 0xE8D9ECBE2CF3D73FULL,
0x2FE4B17170E59750ULL, 0x11317BA87905E790ULL, 0x7FBF21EC8A1F45ECULL,
0x1725CABFCB045B00ULL, 0x964E915CD5E2B207ULL, 0x3E2B8BCBF016D66DULL,
0xBE7444E39328A0ACULL, 0xF85B2B4FBCDE44B7ULL, 0x49353FEA39BA63B1ULL,
0x1DD01AAFCD53486AULL, 0x1FCA8A92FD719F85ULL, 0xFC7C95D827357AFAULL,
0x18A6A990C8B35EBDULL, 0xCCCB7005C6B9C28DULL, 0x3BDBB92C43B17F26ULL,
0xAA70B5B4F89695A2ULL, 0xE94C39A54A98307FULL, 0xB7A0B174CFF6F36EULL,
0xD4DBA84729AF48ADULL, 0x2E18BC1AD9704A68ULL, 0x2DE0966DAF2F8B1CULL,
0xB9C11D5B1E43A07EULL, 0x64972D68DEE33360ULL, 0x94628D38D0C20584ULL,
0xDBC0D2B6AB90A559ULL, 0xD2733C4335C6A72FULL, 0x7E75D99D94A70F4DULL,
0x6CED1983376FA72BULL, 0x97FCAACBF030BC24ULL, 0x7B77497B32503B12ULL,
0x8547EDDFB81CCB94ULL, 0x79999CDFF70902CBULL, 0xCFFE1939438E9B24ULL,
0x829626E3892D95D7ULL, 0x92FAE24291F2B3F1ULL, 0x63E22C147B9C3403ULL,
0xC678B6D860284A1CULL, 0x5873888850659AE7ULL, 0x0981DCD296A8736DULL,
0x9F65789A6509A440ULL, 0x9FF38FED72E9052FULL, 0xE479EE5B9930578CULL,
0xE7F28ECD2D49EECDULL, 0x56C074A581EA17FEULL, 0x5544F7D774B14AEFULL,
0x7B3F0195FC6F290FULL, 0x12153635B2C0CF57ULL, 0x7F5126DBBA5E0CA7ULL,
0x7A76956C3EAFB413ULL, 0x3D5774A11D31AB39ULL, 0x8A1B083821F40CB4ULL,
0x7B4A38E32537DF62ULL, 0x950113646D1D6E03ULL, 0x4DA8979A0041E8A9ULL,
0x3BC36E078F7515D7ULL, 0x5D0A12F27AD310D1ULL, 0x7F9D1A2E1EBE1327ULL,
0xDA3A361B1C5157B1ULL, 0xDCDD7D20903D0C25ULL, 0x36833336D068F707ULL,
0xCE68341F79893389ULL, 0xAB9090168DD05F34ULL, 0x43954B3252DC25E5ULL,
0xB438C2B67F98E5E9ULL, 0x10DCD78E3851A492ULL, 0xDBC27AB5447822BFULL,
0x9B3CDB65F82CA382ULL, 0xB67B7896167B4C84ULL, 0xBFCED1B0048EAC50ULL,
0xA9119B60369FFEBDULL, 0x1FFF7AC80904BF45ULL, 0xAC12FB171817EEE7ULL,
0xAF08DA9177DDA93DULL, 0x1B0CAB936E65C744ULL, 0xB559EB1D04E5E932ULL,
0xC37B45B3F8D6F2BAULL, 0xC3A9DC228CAAC9E9ULL, 0xF3B8B6675A6507FFULL,
0x9FC477DE4ED681DAULL, 0x67378D8ECCEF96CBULL, 0x6DD856D94D259236ULL,
0xA319CE15B0B4DB31ULL, 0x073973751F12DD5EULL, 0x8A8E849EB32781A5ULL,
0xE1925C71285279F5ULL, 0x74C04BF1790C0EFEULL, 0x4DDA48153C94938AULL,
0x9D266D6A1CC0542CULL, 0x7440FB816508C4FEULL, 0x13328503DF48229FULL,
0xD6BF7BAEE43CAC40ULL, 0x4838D65F6EF6748FULL, 0x1E152328F3318DEAULL,
0x8F8419A348F296BFULL, 0x72C8834A5957B511ULL, 0xD7A023A73260B45CULL,
0x94EBC8ABCFB56DAEULL, 0x9FC10D0F989993E0ULL, 0xDE68A2355B93CAE6ULL,
0xA44CFE79AE538BBEULL, 0x9D1D84FCCE371425ULL, 0x51D2B1AB2DDFB636ULL,
0x2FD7E4B9E72CD38CULL, 0x65CA5B96B7552210ULL, 0xDD69A0D8AB3B546DULL,
0x604D51B25FBF70E2ULL, 0x73AA8A564FB7AC9EULL, 0x1A8C1E992B941148ULL,
0xAAC40A2703D9BEA0ULL, 0x764DBEAE7FA4F3A6ULL, 0x1E99B96E70A9BE8BULL,
0x2C5E9DEB57EF4743ULL, 0x3A938FEE32D29981ULL, 0x26E6DB8FFDF5ADFEULL,
0x469356C504EC9F9DULL, 0xC8763C5B08D1908CULL, 0x3F6C6AF859D80055ULL,
0x7F7CC39420A3A545ULL, 0x9BFB227EBDF4C5CEULL, 0x89039D79D6FC5C5CULL,
0x8FE88B57305E2AB6ULL, 0xA09E8C8C35AB96DEULL, 0xFA7E393983325753ULL,
0xD6B6D0ECC617C699ULL, 0xDFEA21EA9E7557E3ULL, 0xB67C1FA481680AF8ULL,
0xCA1E3785A9E724E5ULL, 0x1CFC8BED0D681639ULL, 0xD18D8549D140CAEAULL,
0x4ED0FE7E9DC91335ULL, 0xE4DBF0634473F5D2ULL, 0x1761F93A44D5AEFEULL,
0x53898E4C3910DA55ULL, 0x734DE8181F6EC39AULL, 0x2680B122BAA28D97ULL,
0x298AF231C85BAFABULL, 0x7983EED3740847D5ULL, 0x66C1A2A1A60CD889ULL,
0x9E17E49642A3E4C1ULL, 0xEDB454E7BADC0805ULL, 0x50B704CAB602C329ULL,
0x4CC317FB9CDDD023ULL, 0x66B4835D9EAFEA22ULL, 0x219B97E26FFC81BDULL,
0x261E4E4C0A333A9DULL, 0x1FE2CCA76517DB90ULL, 0xD7504DFA8816EDBBULL,
0xB9571FA04DC089C8ULL, 0x1DDC0325259B27DEULL, 0xCF3F4688801EB9AAULL,
0xF4F5D05C10CAB243ULL, 0x38B6525C21A42B0EULL, 0x36F60E2BA4FA6800ULL,
0xEB3593803173E0CEULL, 0x9C4CD6257C5A3603ULL, 0xAF0C317D32ADAA8AULL,
0x258E5A80C7204C4BULL, 0x8B889D624D44885DULL, 0xF4D14597E660F855ULL,
0xD4347F66EC8941C3ULL, 0xE699ED85B0DFB40DULL, 0x2472F6207C2D0484ULL,
0xC2A1E7B5B459AEB5ULL, 0xAB4F6451CC1D45ECULL, 0x63767572AE3D6174ULL,
0xA59E0BD101731A28ULL, 0x116D0016CB948F09ULL, 0x2CF9C8CA052F6E9FULL,
0x0B090A7560A968E3ULL, 0xABEEDDB2DDE06FF1ULL, 0x58EFC10B06A2068DULL,
0xC6E57A78FBD986E0ULL, 0x2EAB8CA63CE802D7ULL, 0x14A195640116F336ULL,
0x7C0828DD624EC390ULL, 0xD74BBE77E6116AC7ULL, 0x804456AF10F5FB53ULL,
0xEBE9EA2ADF4321C7ULL, 0x03219A39EE587A30ULL, 0x49787FEF17AF9924ULL,
0xA1E9300CD8520548ULL, 0x5B45E522E4B1B4EFULL, 0xB49C3B3995091A36ULL,
0xD4490AD526F14431ULL, 0x12A8F216AF9418C2ULL, 0x001F837CC7350524ULL,
0x1877B51E57A764D5ULL, 0xA2853B80F17F58EEULL, 0x993E1DE72D36D310ULL,
0xB3598080CE64A656ULL, 0x252F59CF0D9F04BBULL, 0xD23C8E176D113600ULL,
0x1BDA0492E7E4586EULL, 0x21E0BD5026C619BFULL, 0x3B097ADAF088F94EULL,
0x8D14DEDB30BE846EULL, 0xF95CFFA23AF5F6F4ULL, 0x3871700761B3F743ULL,
0xCA672B91E9E4FA16ULL, 0x64C8E531BFF53B55ULL, 0x241260ED4AD1E87DULL,
0x106C09B972D2E822ULL, 0x7FBA195410E5CA30ULL, 0x7884D9BC6CB569D8ULL,
0x0647DFEDCD894A29ULL, 0x63573FF03E224774ULL, 0x4FC8E9560F91B123ULL,
0x1DB956E450275779ULL, 0xB8D91274B9E9D4FBULL, 0xA2EBEE47E2FBFCE1ULL,
0xD9F1F30CCD97FB09ULL, 0xEFED53D75FD64E6BULL, 0x2E6D02C36017F67FULL,
0xA9AA4D20DB084E9BULL, 0xB64BE8D8B25396C1ULL, 0x70CB6AF7C2D5BCF0ULL,
0x98F076A4F7A2322EULL, 0xBF84470805E69B5FULL, 0x94C3251F06F90CF3ULL,
0x3E003E616A6591E9ULL, 0xB925A6CD0421AFF3ULL, 0x61BDD1307C66E300ULL,
0xBF8D5108E27E0D48ULL, 0x240AB57A8B888B20ULL, 0xFC87614BAF287E07ULL,
0xEF02CDD06FFDB432ULL, 0xA1082C0466DF6C0AULL, 0x8215E577001332C8ULL,
0xD39BB9C3A48DB6CFULL, 0x2738259634305C14ULL, 0x61CF4F94C97DF93DULL,
0x1B6BACA2AE4E125BULL, 0x758F450C88572E0BULL, 0x959F587D507A8359ULL,
0xB063E962E045F54DULL, 0x60E8ED72C0DFF5D1ULL, 0x7B64978555326F9FULL,
0xFD080D236DA814BAULL, 0x8C90FD9B083F4558ULL, 0x106F72FE81E2C590ULL,
0x7976033A39F7D952ULL, 0xA4EC0132764CA04BULL, 0x733EA705FAE4FA77ULL,
0xB4D8F77BC3E56167ULL, 0x9E21F4F903B33FD9ULL, 0x9D765E419FB69F6DULL,
0xD30C088BA61EA5EFULL, 0x5D94337FBFAF7F5BULL, 0x1A4E4822EB4D7A59ULL,
0x6FFE73E81B637FB3ULL, 0xDDF957BC36D8B9CAULL, 0x64D0E29EEA8838B3ULL,
0x08DD9BDFD96B9F63ULL, 0x087E79E5A57D1D13ULL, 0xE328E230E3E2B3FBULL,
0x1C2559E30F0946BEULL, 0x720BF5F26F4D2EAAULL, 0xB0774D261CC609DBULL,
0x443F64EC5A371195ULL, 0x4112CF68649A260EULL, 0xD813F2FAB7F5C5CAULL,
0x660D3257380841EEULL, 0x59AC2C7873F910A3ULL, 0xE846963877671A17ULL,
0x93B633ABFA3469F8ULL, 0xC0C0F5A60EF4CDCFULL, 0xCAF21ECD4377B28CULL,
0x57277707199B8175ULL, 0x506C11B9D90E8B1DULL, 0xD83CC2687A19255FULL,
0x4A29C6465A314CD1ULL, 0xED2DF21216235097ULL, 0xB5635C95FF7296E2ULL,
0x22AF003AB672E811ULL, 0x52E762596BF68235ULL, 0x9AEBA33AC6ECC6B0ULL,
0x944F6DE09134DFB6ULL, 0x6C47BEC883A7DE39ULL, 0x6AD047C430A12104ULL,
0xA5B1CFDBA0AB4067ULL, 0x7C45D833AFF07862ULL, 0x5092EF950A16DA0BULL,
0x9338E69C052B8E7BULL, 0x455A4B4CFE30E3F5ULL, 0x6B02E63195AD0CF8ULL,
0x6B17B224BAD6BF27ULL, 0xD1E0CCD25BB9C169ULL, 0xDE0C89A556B9AE70ULL,
0x50065E535A213CF6ULL, 0x9C1169FA2777B874ULL, 0x78EDEFD694AF1EEDULL,
0x6DC93D9526A50E68ULL, 0xEE97F453F06791EDULL, 0x32AB0EDB696703D3ULL,
0x3A6853C7E70757A7ULL, 0x31865CED6120F37DULL, 0x67FEF95D92607890ULL,
0x1F2B1D1F15F6DC9CULL, 0xB69E38A8965C6B65ULL, 0xAA9119FF184CCCF4ULL,
0xF43C732873F24C13ULL, 0xFB4A3D794A9A80D2ULL, 0x3550C2321FD6109CULL,
0x371F77E76BB8417EULL, 0x6BFA9AAE5EC05779ULL, 0xCD04F3FF001A4778ULL,
0xE3273522064480CAULL, 0x9F91508BFFCFC14AULL, 0x049A7F41061A9E60ULL,
0xFCB6BE43A9F2FE9BULL, 0x08DE8A1C7797DA9BULL, 0x8F9887E6078735A1ULL,
0xB5B4071DBFC73A66ULL, 0x230E343DFBA08D33ULL, 0x43ED7F5A0FAE657DULL,
0x3A88A0FBBCB05C63ULL, 0x21874B8B4D2DBC4FULL, 0x1BDEA12E35F6A8C9ULL,
0x53C065C6C8E63528ULL, 0xE34A1D250E7A8D6BULL, 0xD6B04D3B7651DD7EULL,
0x5E90277E7CB39E2DULL, 0x2C046F22062DC67DULL, 0xB10BB459132D0A26ULL,
0x3FA9DDFB67E2F199ULL, 0x0E09B88E1914F7AFULL, 0x10E8B35AF3EEAB37ULL,
0x9EEDECA8E272B933ULL, 0xD4C718BC4AE8AE5FULL, 0x81536D601170FC20ULL,
0x91B534F885818A06ULL, 0xEC8177F83F900978ULL, 0x190E714FADA5156EULL,
0xB592BF39B0364963ULL, 0x89C350C893AE7DC1ULL, 0xAC042E70F8B383F2ULL,
0xB49B52E587A1EE60ULL, 0xFB152FE3FF26DA89ULL, 0x3E666E6F69AE2C15ULL,
0x3B544EBE544C19F9ULL, 0xE805A1E290CF2456ULL, 0x24B33C9D7ED25117ULL,
0xE74733427B72F0C1ULL, 0x0A804D18B7097475ULL, 0x57E3306D881EDB4FULL,
0x4AE7D6A36EB5DBCBULL, 0x2D8D5432157064C8ULL, 0xD1E649DE1E7F268BULL,
0x8A328A1CEDFE552CULL, 0x07A3AEC79624C7DAULL, 0x84547DDC3E203C94ULL,
0x990A98FD5071D263ULL, 0x1A4FF12616EEFC89ULL, 0xF6F7FD1431714200ULL,
0x30C05B1BA332F41CULL, 0x8D2636B81555A786ULL, 0x46C9FEB55D120902ULL,
0xCCEC0A73B49C9921ULL, 0x4E9D2827355FC492ULL, 0x19EBB029435DCB0FULL,
0x4659D2B743848A2CULL, 0x963EF2C96B33BE31ULL, 0x74F85198B05A2E7DULL,
0x5A0F544DD2B1FB18ULL, 0x03727073C2E134B1ULL, 0xC7F6AA2DE59AEA61ULL,
0x352787BAA0D7C22FULL, 0x9853EAB63B5E0B35ULL, 0xABBDCDD7ED5C0860ULL,
0xCF05DAF5AC8D77B0ULL, 0x49CAD48CEBF4A71EULL, 0x7A4C10EC2158C4A6ULL,
0xD9E92AA246BF719EULL, 0x13AE978D09FE5557ULL, 0x730499AF921549FFULL,
0x4E4B705B92903BA4ULL, 0xFF577222C14F0A3AULL, 0x55B6344CF97AAFAEULL,
0xB862225B055B6960ULL, 0xCAC09AFBDDD2CDB4ULL, 0xDAF8E9829FE96B5FULL,
0xB5FDFC5D3132C498ULL, 0x310CB380DB6F7503ULL, 0xE87FBB46217A360EULL,
0x2102AE466EBB1148ULL, 0xF8549E1A3AA5E00DULL, 0x07A69AFDCC42261AULL,
0xC4C118BFE78FEAAEULL, 0xF9F4892ED96BD438ULL, 0x1AF3DBE25D8F45DAULL,
0xF5B4B0B0D2DEEEB4ULL, 0x962ACEEFA82E1C84ULL, 0x046E3ECAAF453CE9ULL,
0xF05D129681949A4CULL, 0x964781CE734B3C84ULL, 0x9C2ED44081CE5FBDULL,
0x522E23F3925E319EULL, 0x177E00F9FC32F791ULL, 0x2BC60A63A6F3B3F2ULL,
0x222BBFAE61725606ULL, 0x486289DDCC3D6780ULL, 0x7DC7785B8EFDFC80ULL,
0x8AF38731C02BA980ULL, 0x1FAB64EA29A2DDF7ULL, 0xE4D9429322CD065AULL,
0x9DA058C67844F20CULL, 0x24C0E332B70019B0ULL, 0x233003B5A6CFE6ADULL,
0xD586BD01C5C217F6ULL, 0x5E5637885F29BC2BULL, 0x7EBA726D8C94094BULL,
0x0A56A5F0BFE39272ULL, 0xD79476A84EE20D06ULL, 0x9E4C1269BAA4BF37ULL,
0x17EFEE45B0DEE640ULL, 0x1D95B0A5FCF90BC6ULL, 0x93CBE0B699C2585DULL,
0x65FA4F227A2B6D79ULL, 0xD5F9E858292504D5ULL, 0xC2B5A03F71471A6FULL,
0x59300222B4561E00ULL, 0xCE2F8642CA0712DCULL, 0x7CA9723FBB2E8988ULL,
0x2785338347F2BA08ULL, 0xC61BB3A141E50E8CULL, 0x150F361DAB9DEC26ULL,
0x9F6A419D382595F4ULL, 0x64A53DC924FE7AC9ULL, 0x142DE49FFF7A7C3DULL,
0x0C335248857FA9E7ULL, 0x0A9C32D5EAE45305ULL, 0xE6C42178C4BBB92EULL,
0x71F1CE2490D20B07ULL, 0xF1BCC3D275AFE51AULL, 0xE728E8C83C334074ULL,
0x96FBF83A12884624ULL, 0x81A1549FD6573DA5ULL, 0x5FA7867CAF35E149ULL,
0x56986E2EF3ED091BULL, 0x917F1DD5F8886C61ULL, 0xD20D8C88C8FFE65FULL,
0x31D71DCE64B2C310ULL, 0xF165B587DF898190ULL, 0xA57E6339DD2CF3A0ULL,
0x1EF6E6DBB1961EC9ULL, 0x70CC73D90BC26E24ULL, 0xE21A6B35DF0C3AD7ULL,
0x003A93D8B2806962ULL, 0x1C99DED33CB890A1ULL, 0xCF3145DE0ADD4289ULL,
0xD0E4427A5514FB72ULL, 0x77C621CC9FB3A483ULL, 0x67A34DAC4356550BULL,
0xF8D626AAAF278509ULL
}};
// polyglot_key() returns the PolyGlot hash key of the given position
Key polyglot_key(const Position& pos) {
Key key = 0;
Bitboard b = pos.pieces();
while (b)
{
Square s = pop_lsb(&b);
Piece pc = pos.piece_on(s);
// PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11
key ^= PG.Zobrist.psq[2 * (type_of(pc) - 1) + (color_of(pc) == WHITE)][s];
}
b = pos.can_castle(ANY_CASTLING);
while (b)
key ^= PG.Zobrist.castling[pop_lsb(&b)];
if (pos.ep_square() != SQ_NONE)
key ^= PG.Zobrist.enpassant[file_of(pos.ep_square())];
if (pos.side_to_move() == WHITE)
key ^= PG.Zobrist.turn;
return key;
}
} // namespace
PolyglotBook::PolyglotBook() : rkiss(Time::now() % 10000) {}
PolyglotBook::~PolyglotBook() { if (is_open()) close(); }
/// operator>>() reads sizeof(T) chars from the file's binary byte stream and
/// converts them into 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>>(Entry& 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
/// existing one.
bool PolyglotBook::open(const char* fName) {
if (is_open()) // Cannot close an already closed file
close();
ifstream::open(fName, ifstream::in | ifstream::binary);
fileName = is_open() ? fName : "";
ifstream::clear(); // Reset any error flag to allow a retry ifstream::open()
return !fileName.empty();
}
/// probe() tries to find a book move for the given position. If no move is
/// found, it returns MOVE_NONE. If pickBest is true, then it always returns
/// the highest-rated move, otherwise it randomly chooses one based on the
/// move score.
Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest) {
if (fileName != fName && !open(fName.c_str()))
return MOVE_NONE;
Entry e;
uint16_t best = 0;
unsigned sum = 0;
Move move = MOVE_NONE;
Key key = polyglot_key(pos);
seekg(find_first(key) * sizeof(Entry), ios_base::beg);
while (*this >> e, e.key == key && good())
{
best = max(best, e.count);
sum += e.count;
// Choose book move according to its score. If a move has a very high
// score it has a higher probability of being choosen than a move with
// a lower score. Note that first entry is always chosen.
if ( (!pickBest && sum && rkiss.rand<unsigned>() % sum < e.count)
|| (pickBest && e.count == best))
move = Move(e.move);
}
if (!move)
return MOVE_NONE;
// A PolyGlot book move is encoded as follows:
//
// bit 0- 5: destination square (from 0 to 63)
// bit 6-11: origin square (from 0 to 63)
// bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
//
// Castling moves follow the "king captures rook" representation. If a book
// move is a promotion, we have to convert it to our representation and in
// all other cases, we can directly compare with a Move after having masked
// out the special Move 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> it(pos); *it; ++it)
if (move == (*it ^ type_of(*it)))
return *it;
return MOVE_NONE;
}
/// find_first() takes a book key as input, and does a binary search through
/// the book file for the given key. Returns the index of the leftmost book
/// entry with the same key as the input.
size_t PolyglotBook::find_first(Key key) {
seekg(0, ios::end); // Move pointer to end, so tellg() gets file's size
size_t low = 0, mid, high = (size_t)tellg() / sizeof(Entry) - 1;
Entry e;
assert(low <= high);
while (low < high && good())
{
mid = (low + high) / 2;
assert(mid >= low && mid < high);
seekg(mid * sizeof(Entry), ios_base::beg);
*this >> e;
if (key <= e.key)
high = mid;
else
low = mid + 1;
}
assert(low == high);
return low;
}
-45
View File
@@ -1,45 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 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/>.
*/
#ifndef BOOK_H_INCLUDED
#define BOOK_H_INCLUDED
#include <fstream>
#include <string>
#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);
bool open(const char* fName);
size_t find_first(Key key);
RKISS rkiss;
std::string fileName;
};
#endif // #ifndef BOOK_H_INCLUDED
+56 -55
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -60,8 +60,8 @@ namespace {
const int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 };
#ifndef NDEBUG
bool verify_material(const Position& pos, Color c, Value npm, int num_pawns) {
return pos.non_pawn_material(c) == npm && pos.count<PAWN>(c) == num_pawns;
bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) {
return pos.non_pawn_material(c) == npm && pos.count<PAWN>(c) == pawnsCnt;
}
#endif
@@ -162,12 +162,13 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
Value result = pos.non_pawn_material(strongSide)
+ pos.count<PAWN>(strongSide) * PawnValueEg
+ PushToEdges[loserKSq]
+ PushClose[square_distance(winnerKSq, loserKSq)];
+ PushClose[distance(winnerKSq, loserKSq)];
if ( pos.count<QUEEN>(strongSide)
|| pos.count<ROOK>(strongSide)
||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide))
|| pos.bishop_pair(strongSide))
||(pos.count<BISHOP>(strongSide) > 1 && opposite_colors(pos.list<BISHOP>(strongSide)[0],
pos.list<BISHOP>(strongSide)[1])))
result += VALUE_KNOWN_WIN;
return strongSide == pos.side_to_move() ? result : -result;
@@ -196,7 +197,7 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
}
Value result = VALUE_KNOWN_WIN
+ PushClose[square_distance(winnerKSq, loserKSq)]
+ PushClose[distance(winnerKSq, loserKSq)]
+ PushToCorners[loserKSq];
return strongSide == pos.side_to_move() ? result : -result;
@@ -217,7 +218,7 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
if (!Bitbases::probe_kpk(wksq, psq, bksq, us))
if (!Bitbases::probe(wksq, psq, bksq, us))
return VALUE_DRAW;
Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq));
@@ -246,26 +247,26 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
// If the stronger side's king is in front of the pawn, it's a win
if (wksq < psq && file_of(wksq) == file_of(psq))
result = RookValueEg - square_distance(wksq, psq);
result = RookValueEg - distance(wksq, psq);
// If the weaker side's king is too far from the pawn and the rook,
// it's a win.
else if ( square_distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide)
&& square_distance(bksq, rsq) >= 3)
result = RookValueEg - square_distance(wksq, psq);
else if ( distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide)
&& distance(bksq, rsq) >= 3)
result = RookValueEg - distance(wksq, psq);
// If the pawn is far advanced and supported by the defending king,
// the position is drawish
else if ( rank_of(bksq) <= RANK_3
&& square_distance(bksq, psq) == 1
&& distance(bksq, psq) == 1
&& rank_of(wksq) >= RANK_4
&& square_distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide))
result = Value(80) - 8 * square_distance(wksq, psq);
&& distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide))
result = Value(80) - 8 * distance(wksq, psq);
else
result = Value(200) - 8 * ( square_distance(wksq, psq + DELTA_S)
- square_distance(bksq, psq + DELTA_S)
- square_distance(psq, queeningSq));
result = Value(200) - 8 * ( distance(wksq, psq + DELTA_S)
- distance(bksq, psq + DELTA_S)
- distance(psq, queeningSq));
return strongSide == pos.side_to_move() ? result : -result;
}
@@ -294,7 +295,7 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
Square bksq = pos.king_square(weakSide);
Square bnsq = pos.list<KNIGHT>(weakSide)[0];
Value result = Value(PushToEdges[bksq] + PushAway[square_distance(bksq, bnsq)]);
Value result = Value(PushToEdges[bksq] + PushAway[distance(bksq, bnsq)]);
return strongSide == pos.side_to_move() ? result : -result;
}
@@ -313,10 +314,10 @@ Value Endgame<KQKP>::operator()(const Position& pos) const {
Square loserKSq = pos.king_square(weakSide);
Square pawnSq = pos.list<PAWN>(weakSide)[0];
Value result = Value(PushClose[square_distance(winnerKSq, loserKSq)]);
Value result = Value(PushClose[distance(winnerKSq, loserKSq)]);
if ( relative_rank(weakSide, pawnSq) != RANK_7
|| square_distance(loserKSq, pawnSq) != 1
|| distance(loserKSq, pawnSq) != 1
|| !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq))
result += QueenValueEg - PawnValueEg;
@@ -340,7 +341,7 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
Value result = QueenValueEg
- RookValueEg
+ PushToEdges[loserKSq]
+ PushClose[square_distance(winnerKSq, loserKSq)];
+ PushClose[distance(winnerKSq, loserKSq)];
return strongSide == pos.side_to_move() ? result : -result;
}
@@ -375,7 +376,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
Square kingSq = pos.king_square(weakSide);
if ( opposite_colors(queeningSq, bishopSq)
&& square_distance(queeningSq, kingSq) <= 1)
&& distance(queeningSq, kingSq) <= 1)
return SCALE_FACTOR_DRAW;
}
@@ -398,8 +399,8 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
&& (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide)))
&& (opposite_colors(bishopSq, weakPawnSq) || pos.count<PAWN>(strongSide) == 1))
{
int strongKingDist = square_distance(weakPawnSq, strongKingSq);
int weakKingDist = square_distance(weakPawnSq, weakKingSq);
int strongKingDist = distance(weakPawnSq, strongKingSq);
int weakKingDist = distance(weakPawnSq, weakKingSq);
// It's a draw if the weak king is on its back two ranks, within 2
// squares of the blocking pawn and the strong king is not
@@ -469,7 +470,7 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
// If the pawn is not too far advanced and the defending king defends the
// queening square, use the third-rank defence.
if ( r <= RANK_5
&& square_distance(bksq, queeningSq) <= 1
&& distance(bksq, queeningSq) <= 1
&& wksq <= SQ_H5
&& (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6)))
return SCALE_FACTOR_DRAW;
@@ -477,15 +478,15 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
// The defending side saves a draw by checking from behind in case the pawn
// has advanced to the 6th rank with the king behind.
if ( r == RANK_6
&& square_distance(bksq, queeningSq) <= 1
&& distance(bksq, queeningSq) <= 1
&& rank_of(wksq) + tempo <= RANK_6
&& (rank_of(brsq) == RANK_1 || (!tempo && abs(file_of(brsq) - f) >= 3)))
&& (rank_of(brsq) == RANK_1 || (!tempo && distance(file_of(brsq), f) >= 3)))
return SCALE_FACTOR_DRAW;
if ( r >= RANK_6
&& bksq == queeningSq
&& rank_of(brsq) == RANK_1
&& (!tempo || square_distance(wksq, wpsq) >= 2))
&& (!tempo || distance(wksq, wpsq) >= 2))
return SCALE_FACTOR_DRAW;
// White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7
@@ -501,8 +502,8 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
// away, it's a draw.
if ( r <= RANK_5
&& bksq == wpsq + DELTA_N
&& square_distance(wksq, wpsq) - tempo >= 2
&& square_distance(wksq, brsq) - tempo >= 2)
&& distance(wksq, wpsq) - tempo >= 2
&& distance(wksq, brsq) - tempo >= 2)
return SCALE_FACTOR_DRAW;
// Pawn on the 7th rank supported by the rook from behind usually wins if the
@@ -512,22 +513,22 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
&& f != FILE_A
&& file_of(wrsq) == f
&& wrsq != queeningSq
&& (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo)
&& (square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo))
return ScaleFactor(SCALE_FACTOR_MAX - 2 * square_distance(wksq, queeningSq));
&& (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo)
&& (distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo))
return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(wksq, queeningSq));
// Similar to the above, but with the pawn further back
if ( f != FILE_A
&& file_of(wrsq) == f
&& wrsq < wpsq
&& (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo)
&& (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wpsq + DELTA_N) - 2 + tempo)
&& ( square_distance(bksq, wrsq) + tempo >= 3
|| ( square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo
&& (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wrsq) + tempo))))
&& (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo)
&& (distance(wksq, wpsq + DELTA_N) < distance(bksq, wpsq + DELTA_N) - 2 + tempo)
&& ( distance(bksq, wrsq) + tempo >= 3
|| ( distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo
&& (distance(wksq, wpsq + DELTA_N) < distance(bksq, wrsq) + tempo))))
return ScaleFactor( SCALE_FACTOR_MAX
- 8 * square_distance(wpsq, queeningSq)
- 2 * square_distance(wksq, queeningSq));
- 8 * distance(wpsq, queeningSq)
- 2 * distance(wksq, queeningSq));
// If the pawn is not far advanced and the defending king is somewhere in
// the pawn's path, it's probably a draw.
@@ -535,9 +536,9 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
{
if (file_of(bksq) == file_of(wpsq))
return ScaleFactor(10);
if ( abs(file_of(bksq) - file_of(wpsq)) == 1
&& square_distance(wksq, bksq) > 2)
return ScaleFactor(24 - 2 * square_distance(wksq, bksq));
if ( distance<File>(bksq, wpsq) == 1
&& distance(wksq, bksq) > 2)
return ScaleFactor(24 - 2 * distance(wksq, bksq));
}
return SCALE_FACTOR_NONE;
}
@@ -564,7 +565,7 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
// corner but not trapped there.
if (rk == RANK_5 && !opposite_colors(bsq, psq))
{
int d = square_distance(psq + 3 * push, ksq);
int d = distance(psq + 3 * push, ksq);
if (d <= 2 && !(d == 0 && ksq == pos.king_square(strongSide) + 2 * push))
return ScaleFactor(24);
@@ -577,9 +578,9 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
// pawn from a reasonable distance and the defending king is near
// the corner
if ( rk == RANK_6
&& square_distance(psq + 2 * push, ksq) <= 1
&& distance(psq + 2 * push, ksq) <= 1
&& (PseudoAttacks[BISHOP][bsq] & (psq + push))
&& file_distance(bsq, psq) >= 2)
&& distance<File>(bsq, psq) >= 2)
return ScaleFactor(8);
}
@@ -604,8 +605,8 @@ ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
Rank r = std::max(relative_rank(strongSide, wpsq1), relative_rank(strongSide, wpsq2));
if ( file_distance(bksq, wpsq1) <= 1
&& file_distance(bksq, wpsq2) <= 1
if ( distance<File>(bksq, wpsq1) <= 1
&& distance<File>(bksq, wpsq2) <= 1
&& relative_rank(strongSide, bksq) > r)
{
switch (r) {
@@ -638,7 +639,7 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
// the king is within one file of the pawns, it's a draw.
if ( !(pawns & ~in_front_bb(weakSide, rank_of(ksq)))
&& !((pawns & ~FileABB) && (pawns & ~FileHBB))
&& file_distance(ksq, psq) <= 1)
&& distance<File>(ksq, psq) <= 1)
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
@@ -690,7 +691,7 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
return SCALE_FACTOR_DRAW;
if ( (pos.attacks_from<BISHOP>(weakBishopSq) & path)
&& square_distance(weakBishopSq, pawnSq) >= 3)
&& distance(weakBishopSq, pawnSq) >= 3)
return SCALE_FACTOR_DRAW;
}
}
@@ -729,7 +730,7 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
blockSq2 = make_square(file_of(psq1), rank_of(psq2));
}
switch (file_distance(psq1, psq2))
switch (distance<File>(psq1, psq2))
{
case 0:
// Both pawns are on the same file. It's an easy draw if the defender firmly
@@ -749,7 +750,7 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
|| abs(r1 - r2) >= 2))
|| distance(r1, r2) >= 2))
return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2
@@ -802,7 +803,7 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
Square pawnSq = normalize(pos, strongSide, pos.list<PAWN>(strongSide)[0]);
Square weakKingSq = normalize(pos, strongSide, pos.king_square(weakSide));
if (pawnSq == SQ_A7 && square_distance(SQ_A8, weakKingSq) <= 1)
if (pawnSq == SQ_A7 && distance(SQ_A8, weakKingSq) <= 1)
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
@@ -821,7 +822,7 @@ ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
// King needs to get close to promoting pawn to prevent knight from blocking.
// Rules for this are very tricky, so just approximate.
if (forward_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq))
return ScaleFactor(square_distance(weakKingSq, pawnSq));
return ScaleFactor(distance(weakKingSq, pawnSq));
return SCALE_FACTOR_NONE;
}
@@ -852,5 +853,5 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
// it's probably at least a draw even with the pawn.
return Bitbases::probe_kpk(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
return Bitbases::probe(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
}
+11 -10
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -45,7 +45,7 @@ enum EndgameType {
// Scaling functions
SCALE_FUNS,
SCALING_FUNCTIONS,
KBPsK, // KB and pawns vs K
KQKRPs, // KQ vs KR and pawns
@@ -76,26 +76,26 @@ template<typename T>
struct EndgameBase {
virtual ~EndgameBase() {}
virtual Color color() const = 0;
virtual Color strong_side() const = 0;
virtual T operator()(const Position&) const = 0;
};
template<EndgameType E, typename T = typename eg_fun<(E > SCALE_FUNS)>::type>
template<EndgameType E, typename T = typename eg_fun<(E > SCALING_FUNCTIONS)>::type>
struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : strongSide(c), weakSide(~c) {}
Color color() const { return strongSide; }
Color strong_side() const { return strongSide; }
T operator()(const Position&) const;
private:
const Color strongSide, weakSide;
Color strongSide, weakSide;
};
/// The Endgames class stores the pointers to endgame evaluation and scaling
/// base objects in two std::map typedefs. We then use polymorphism to invoke
/// the actual endgame function by calling its virtual operator().
/// base objects in two std::map. We use polymorphism to invoke the actual
/// endgame function by calling its virtual operator().
class Endgames {
@@ -114,8 +114,9 @@ public:
Endgames();
~Endgames();
template<typename T> T probe(Key key, T& eg)
{ return eg = map(eg).count(key) ? map(eg)[key] : NULL; }
template<typename T> T probe(Key key, T& eg) {
return eg = map(eg).count(key) ? map(eg)[key] : NULL;
}
};
#endif // #ifndef ENDGAME_H_INCLUDED
+262 -277
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -19,6 +19,7 @@
#include <algorithm>
#include <cassert>
#include <cstring> // For std::memset
#include <iomanip>
#include <sstream>
@@ -26,8 +27,6 @@
#include "evaluate.h"
#include "material.h"
#include "pawns.h"
#include "thread.h"
#include "ucioption.h"
namespace {
@@ -58,9 +57,8 @@ namespace {
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
// given color which attack a square in the kingRing of the enemy king. The
// weights of the individual piece types are given by the variables
// QueenAttackWeight, RookAttackWeight, BishopAttackWeight and
// KnightAttackWeight in evaluate.cpp
// weights of the individual piece types are given by the elements in the
// KingAttackWeights array.
int kingAttackersWeight[COLOR_NB];
// kingAdjacentZoneAttacksCount[color] is the number of attacks to squares
@@ -76,36 +74,29 @@ namespace {
namespace Tracing {
enum Terms { // First 8 entries are for PieceType
PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB
MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERMS_NB
};
Score terms[COLOR_NB][TERMS_NB];
Score scores[COLOR_NB][TERMS_NB];
EvalInfo ei;
ScaleFactor sf;
double to_cp(Value v);
void add_term(int idx, Score term_w, Score term_b = SCORE_ZERO);
void format_row(std::stringstream& ss, const char* name, int idx);
void write(int idx, Color c, Score s);
void write(int idx, Score w, Score b = SCORE_ZERO);
void print(std::stringstream& ss, const char* name, int idx);
std::string do_trace(const Position& pos);
}
// Evaluation weights, initialized from UCI options
enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem };
struct Weight { int mg, eg; } Weights[6];
typedef Value V;
#define S(mg, eg) make_score(mg, eg)
// Internal evaluation weights. These are applied on top of the evaluation
// weights read from UCI parameters. The purpose is to be able to change
// the evaluation weights while keeping the default values of the UCI
// parameters at 100, which looks prettier.
//
// Values modified by Joona Kiiski
const Score WeightsInternal[] = {
S(289, 344), S(233, 201), S(221, 273), S(46, 0), S(271, 0), S(307, 0)
// Evaluation weights, indexed by evaluation term
enum { Mobility, PawnStructure, PassedPawns, Space, KingSafety };
const struct Weight { int mg, eg; } Weights[] = {
{289, 344}, {233, 201}, {221, 273}, {46, 0}, {321, 0}
};
#define V(v) Value(v)
#define S(mg, eg) make_score(mg, eg)
// MobilityBonus[PieceType][attacked] contains bonuses for middle and end
// game, indexed by piece type and number of attacked squares not occupied by
// friendly pieces.
@@ -145,37 +136,40 @@ namespace {
V(0), V(5), V(8), V(8), V(8), V(8), V(5), V(0) }
};
// Threat[attacking][attacked] contains bonuses according to which piece
// type attacks which one.
const Score Threat[][PIECE_TYPE_NB] = {
{ S(0, 0), S( 7, 39), S(24, 49), S(24, 49), S(41,100), S(41,100) }, // Minor
{ S(0, 0), S(15, 39), S(15, 45), S(15, 45), S(15, 45), S(24, 49) } // Major
// Threat[defended/weak][minor/major attacking][attacked PieceType] contains
// bonuses according to which piece type attacks which one.
const Score Threat[][2][PIECE_TYPE_NB] = {
{ { S(0, 0), S( 0, 0), S(19, 37), S(24, 37), S(44, 97), S(35,106) }, // Defended Minor
{ S(0, 0), S( 0, 0), S( 9, 14), S( 9, 14), S( 7, 14), S(24, 48) } }, // Defended Major
{ { S(0, 0), S( 0,32), S(33, 41), S(31, 50), S(41,100), S(35,104) }, // Weak Minor
{ S(0, 0), S( 0,27), S(26, 57), S(26, 57), S(0 , 43), S(23, 51) } } // Weak Major
};
// ThreatenedByPawn[PieceType] contains a penalty according to which piece
// type is attacked by an enemy pawn.
const Score ThreatenedByPawn[] = {
S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118)
S(0, 0), S(0, 0), S(87, 118), S(84, 122), S(114, 203), S(121, 217)
};
// Hanging[side to move] contains a bonus for each enemy hanging piece
const Score Hanging[2] = { S(23, 20) , S(35, 45) };
#undef S
const Score Tempo = make_score(24, 11);
const Score RookOnPawn = make_score(10, 28);
const Score RookOpenFile = make_score(43, 21);
const Score RookSemiopenFile = make_score(19, 10);
const Score BishopPawns = make_score( 8, 12);
const Score MinorBehindPawn = make_score(16, 0);
const Score TrappedRook = make_score(90, 0);
const Score Unstoppable = make_score( 0, 20);
// Assorted bonuses and penalties used by evaluation
const Score KingOnOne = S( 2, 58);
const Score KingOnMany = S( 6,125);
const Score RookOnPawn = S( 7, 27);
const Score RookOnOpenFile = S(43, 21);
const Score RookOnSemiOpenFile = S(19, 10);
const Score BishopPawns = S( 8, 12);
const Score MinorBehindPawn = S(16, 0);
const Score TrappedRook = S(92, 0);
const Score Unstoppable = S( 0, 20);
const Score Hanging = S(31, 26);
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by
// a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
// happen in Chess960 games.
const Score TrappedBishopA1H1 = make_score(50, 50);
const Score TrappedBishopA1H1 = S(50, 50);
#undef S
#undef V
// SpaceMask[Color] contains the area of the board which is considered
// by the space evaluation. In the middlegame, each side is given a bonus
@@ -186,41 +180,29 @@ namespace {
(FileCBB | FileDBB | FileEBB | FileFBB) & (Rank7BB | Rank6BB | Rank5BB)
};
// King danger constants and variables. The king danger scores are taken
// from KingDanger[]. Various little "meta-bonuses" measuring the strength
// King danger constants and variables. The king danger scores are looked-up
// in KingDanger[]. Various little "meta-bonuses" measuring the strength
// of the enemy attack are added up into an integer, which is used as an
// index to KingDanger[].
//
// KingAttackWeights[PieceType] contains king attack weights by piece type
const int KingAttackWeights[] = { 0, 0, 2, 2, 3, 5 };
const int KingAttackWeights[] = { 0, 0, 6, 2, 5, 5 };
// Bonuses for enemy's safe checks
const int QueenContactCheck = 24;
const int RookContactCheck = 16;
const int QueenCheck = 12;
const int RookCheck = 8;
const int BishopCheck = 2;
const int KnightCheck = 3;
const int QueenContactCheck = 92;
const int RookContactCheck = 68;
const int QueenCheck = 50;
const int RookCheck = 36;
const int BishopCheck = 7;
const int KnightCheck = 14;
// KingDanger[Color][attackUnits] contains the actual king danger weighted
// scores, indexed by color and by a calculated integer number.
Score KingDanger[COLOR_NB][128];
// KingDanger[attackUnits] contains the actual king danger weighted
// scores, indexed by a calculated integer number.
Score KingDanger[512];
// apply_weight() weighs score 'v' by weight 'w' trying to prevent overflow
Score apply_weight(Score v, const Weight& w) {
return make_score(mg_value(v) * w.mg / 256, eg_value(v) * w.eg / 256);
}
// weight_option() computes the value of an evaluation weight, by combining
// two UCI-configurable weights (midgame and endgame) with an internal weight.
Weight weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) {
Weight w = { Options[mgOpt] * mg_value(internalWeight) / 100,
Options[egOpt] * eg_value(internalWeight) / 100 };
return w;
// apply_weight() weighs score 's' by weight 'w' trying to prevent overflow
Score apply_weight(Score s, const Weight& w) {
return make_score(mg_value(s) * w.mg / 256, eg_value(s) * w.eg / 256);
}
@@ -239,7 +221,7 @@ namespace {
ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
// Init king safety tables only if we are going to use them
if (pos.count<QUEEN>(Us) && pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg)
if (pos.non_pawn_material(Us) >= QueenValueMg)
{
ei.kingRing[Them] = b | shift_bb<Down>(b);
b &= ei.attackedBy[Us][PAWN];
@@ -251,10 +233,10 @@ namespace {
}
// evaluate_outposts() evaluates bishop and knight outpost squares
// evaluate_outpost() evaluates bishop and knight outpost squares
template<PieceType Pt, Color Us>
Score evaluate_outposts(const Position& pos, EvalInfo& ei, Square s) {
Score evaluate_outpost(const Position& pos, const EvalInfo& ei, Square s) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -274,7 +256,7 @@ namespace {
bonus += bonus / 2;
}
return make_score(bonus, bonus);
return make_score(bonus * 2, bonus / 2);
}
@@ -331,65 +313,65 @@ namespace {
if (Pt == BISHOP || Pt == KNIGHT)
{
// Penalty for bishop with same colored pawns
if (Pt == BISHOP)
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
// Bishop and knight outposts squares
// Bonus for outpost square
if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s)))
score += evaluate_outposts<Pt, Us>(pos, ei, s);
score += evaluate_outpost<Pt, Us>(pos, ei, s);
// Bishop or knight behind a pawn
// Bonus when behind a pawn
if ( relative_rank(Us, s) < RANK_5
&& (pos.pieces(PAWN) & (s + pawn_push(Us))))
score += MinorBehindPawn;
// Penalty for pawns on same color square of bishop
if (Pt == BISHOP)
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
// An important Chess960 pattern: A cornered bishop blocked by a friendly
// pawn diagonally in front of it is a very serious problem, especially
// when that pawn is also blocked.
if ( Pt == BISHOP
&& pos.is_chess960()
&& (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
{
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
: pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? TrappedBishopA1H1 * 2
: TrappedBishopA1H1;
}
}
if (Pt == ROOK)
{
// Rook piece attacking enemy pawns on the same rank/file
// Bonus for aligning with enemy pawns on the same rank/file
if (relative_rank(Us, s) >= RANK_5)
{
Bitboard pawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s];
if (pawns)
score += popcount<Max15>(pawns) * RookOnPawn;
Bitboard alignedPawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s];
if (alignedPawns)
score += popcount<Max15>(alignedPawns) * RookOnPawn;
}
// Give a bonus for a rook on a open or semi-open file
// Bonus when on an open or semi-open file
if (ei.pi->semiopen_file(Us, file_of(s)))
score += ei.pi->semiopen_file(Them, file_of(s)) ? RookOpenFile : RookSemiopenFile;
score += ei.pi->semiopen_file(Them, file_of(s)) ? RookOnOpenFile : RookOnSemiOpenFile;
if (mob > 3 || ei.pi->semiopen_file(Us, file_of(s)))
continue;
// Penalize when trapped by the king, even more if king cannot castle
if (mob <= 3 && !ei.pi->semiopen_file(Us, file_of(s)))
{
Square ksq = pos.king_square(Us);
Square ksq = pos.king_square(Us);
// Penalize rooks which are trapped by a king. Penalize more if the
// king has lost its castling capability.
if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq)))
&& (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1)
&& !ei.pi->semiopen_side(Us, file_of(ksq), file_of(s) < file_of(ksq)))
score -= (TrappedRook - make_score(mob * 8, 0)) * (1 + !pos.can_castle(Us));
}
// An important Chess960 pattern: A cornered bishop blocked by a friendly
// pawn diagonally in front of it is a very serious problem, especially
// when that pawn is also blocked.
if ( Pt == BISHOP
&& pos.is_chess960()
&& (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
{
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
score -= !pos.empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
: pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? TrappedBishopA1H1 * 2
: TrappedBishopA1H1;
if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq)))
&& (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1)
&& !ei.pi->semiopen_side(Us, file_of(ksq), file_of(s) < file_of(ksq)))
score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us));
}
}
}
if (Trace)
Tracing::terms[Us][Pt] = score;
Tracing::write(Pt, Us, score);
// Recursively call evaluate_pieces() of next piece type until KING excluded
return score - evaluate_pieces<NextPt, Them, Trace>(pos, ei, mobility, mobilityArea);
}
@@ -425,34 +407,32 @@ namespace {
| ei.attackedBy[Us][QUEEN]);
// Initialize the 'attackUnits' variable, which is used later on as an
// index to the KingDanger[] array. The initial value is based on the
// index into the KingDanger[] array. The initial value is based on the
// number and types of the enemy's attacking pieces, the number of
// attacked and undefended squares around our king and the quality of
// the pawn shelter (current 'score' value).
attackUnits = std::min(20, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended))
+ 2 * (ei.pinnedPieces[Us] != 0)
- mg_value(score) / 32;
attackUnits = std::min(77, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them])
+ 10 * ei.kingAdjacentZoneAttacksCount[Them]
+ 19 * popcount<Max15>(undefended)
+ 9 * (ei.pinnedPieces[Us] != 0)
- mg_value(score) * 63 / 512
- !pos.count<QUEEN>(Them) * 60;
// Analyse the enemy's safe queen contact checks. Firstly, find the
// undefended squares around the king that are attacked by the enemy's
// queen...
// undefended squares around the king reachable by the enemy queen...
b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them);
if (b)
{
// ...and then remove squares not supported by another enemy piece
b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]);
b &= ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK];
if (b)
attackUnits += QueenContactCheck
* popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1);
attackUnits += QueenContactCheck * popcount<Max15>(b);
}
// Analyse the enemy's safe rook contact checks. Firstly, find the
// undefended squares around the king that are attacked by the enemy's
// rooks...
// undefended squares around the king reachable by the enemy rooks...
b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them);
// Consider only squares where the enemy's rook gives check
@@ -465,15 +445,13 @@ namespace {
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
if (b)
attackUnits += RookContactCheck
* popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1);
attackUnits += RookContactCheck * popcount<Max15>(b);
}
// Analyse the enemy's safe distance checks for sliders and knights
safe = ~(pos.pieces(Them) | ei.attackedBy[Us][ALL_PIECES]);
safe = ~(ei.attackedBy[Us][ALL_PIECES] | pos.pieces(Them));
b1 = pos.attacks_from<ROOK>(ksq) & safe;
b1 = pos.attacks_from<ROOK >(ksq) & safe;
b2 = pos.attacks_from<BISHOP>(ksq) & safe;
// Enemy queen safe checks
@@ -496,16 +474,13 @@ namespace {
if (b)
attackUnits += KnightCheck * popcount<Max15>(b);
// To index KingDanger[] attackUnits must be in [0, 99] range
attackUnits = std::min(99, std::max(0, attackUnits));
// Finally, extract the king danger score from the KingDanger[]
// array and subtract the score from evaluation.
score -= KingDanger[Us == Search::RootColor][attackUnits];
score -= KingDanger[std::max(std::min(attackUnits, 399), 0)];
}
if (Trace)
Tracing::terms[Us][KING] = score;
Tracing::write(KING, Us, score);
return score;
}
@@ -519,33 +494,55 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b, weakEnemies;
enum { Defended, Weak };
enum { Minor, Major };
Bitboard b, weak, defended;
Score score = SCORE_ZERO;
// Enemies not defended by a pawn and under our attack
weakEnemies = pos.pieces(Them)
& ~ei.attackedBy[Them][PAWN]
& ei.attackedBy[Us][ALL_PIECES];
// Non-pawn enemies defended by a pawn
defended = (pos.pieces(Them) ^ pos.pieces(Them, PAWN))
& ei.attackedBy[Them][PAWN];
// Add a bonus according if the attacking pieces are minor or major
if (weakEnemies)
// Add a bonus according to the kind of attacking pieces
if (defended)
{
b = weakEnemies & (ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]);
if (b)
score += Threat[0][type_of(pos.piece_on(lsb(b)))];
b = defended & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]);
while (b)
score += Threat[Defended][Minor][type_of(pos.piece_on(pop_lsb(&b)))];
b = weakEnemies & (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]);
if (b)
score += Threat[1][type_of(pos.piece_on(lsb(b)))];
b = defended & (ei.attackedBy[Us][ROOK]);
while (b)
score += Threat[Defended][Major][type_of(pos.piece_on(pop_lsb(&b)))];
}
b = weakEnemies & ~ei.attackedBy[Them][ALL_PIECES];
// Enemies not defended by a pawn and under our attack
weak = pos.pieces(Them)
& ~ei.attackedBy[Them][PAWN]
& ei.attackedBy[Us][ALL_PIECES];
// Add a bonus according to the kind of attacking pieces
if (weak)
{
b = weak & (ei.attackedBy[Us][KNIGHT] | ei.attackedBy[Us][BISHOP]);
while (b)
score += Threat[Weak][Minor][type_of(pos.piece_on(pop_lsb(&b)))];
b = weak & (ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][QUEEN]);
while (b)
score += Threat[Weak][Major][type_of(pos.piece_on(pop_lsb(&b)))];
b = weak & ~ei.attackedBy[Them][ALL_PIECES];
if (b)
score += more_than_one(b) ? Hanging[Us != pos.side_to_move()] * popcount<Max15>(b)
: Hanging[Us == pos.side_to_move()];
score += Hanging * popcount<Max15>(b);
b = weak & ei.attackedBy[Us][KING];
if (b)
score += more_than_one(b) ? KingOnMany : KingOnOne;
}
if (Trace)
Tracing::terms[Us][Tracing::THREAT] = score;
Tracing::write(Tracing::THREAT, Us, score);
return score;
}
@@ -580,32 +577,28 @@ namespace {
Square blockSq = s + pawn_push(Us);
// Adjust bonus based on the king's proximity
ebonus += square_distance(pos.king_square(Them), blockSq) * 5 * rr
- square_distance(pos.king_square(Us ), blockSq) * 2 * rr;
ebonus += distance(pos.king_square(Them), blockSq) * 5 * rr
- distance(pos.king_square(Us ), blockSq) * 2 * rr;
// If blockSq is not the queening square then consider also a second push
if (relative_rank(Us, blockSq) != RANK_8)
ebonus -= square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr;
ebonus -= distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr;
// If the pawn is free to advance, then increase the bonus
if (pos.empty(blockSq))
{
squaresToQueen = forward_bb(Us, s);
// If there is a rook or queen attacking/defending the pawn from behind,
// consider all the squaresToQueen. Otherwise consider only the squares
// in the pawn's path attacked or occupied by the enemy.
defendedSquares = unsafeSquares = squaresToQueen = forward_bb(Us, s);
// If there is an enemy rook or queen attacking the pawn from behind,
// add all X-ray attacks by the rook or queen. Otherwise consider only
// the squares in the pawn's path attacked or occupied by the enemy.
if ( unlikely(forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN))
&& (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from<ROOK>(s)))
unsafeSquares = squaresToQueen;
else
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
Bitboard bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s);
if ( unlikely(forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN))
&& (forward_bb(Them, s) & pos.pieces(Us, ROOK, QUEEN) & pos.attacks_from<ROOK>(s)))
defendedSquares = squaresToQueen;
else
defendedSquares = squaresToQueen & ei.attackedBy[Us][ALL_PIECES];
if (!(pos.pieces(Us) & bb))
defendedSquares &= ei.attackedBy[Us][ALL_PIECES];
if (!(pos.pieces(Them) & bb))
unsafeSquares &= ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
// If there aren't any enemy attacks, assign a big bonus. Otherwise
// assign a smaller bonus if the block square isn't attacked.
@@ -621,6 +614,8 @@ namespace {
mbonus += k * rr, ebonus += k * rr;
}
else if (pos.pieces(Us) & blockSq)
mbonus += rr * 3 + r * 2 + 3, ebonus += rr + r * 2;
} // rr != 0
if (pos.count<PAWN>(Us) < pos.count<PAWN>(Them))
@@ -630,36 +625,21 @@ namespace {
}
if (Trace)
Tracing::terms[Us][Tracing::PASSED] = apply_weight(score, Weights[PassedPawns]);
Tracing::write(Tracing::PASSED, Us, apply_weight(score, Weights[PassedPawns]));
// Add the scores to the middlegame and endgame eval
return apply_weight(score, Weights[PassedPawns]);
}
// evaluate_unstoppable_pawns() scores the most advanced among the passed and
// candidate pawns. In case opponent has no pieces but pawns, this is somewhat
// related to the possibility that pawns are unstoppable.
Score evaluate_unstoppable_pawns(const Position& pos, Color us, const EvalInfo& ei) {
Bitboard b = ei.pi->passed_pawns(us) | ei.pi->candidate_pawns(us);
if (!b || pos.non_pawn_material(~us))
return SCORE_ZERO;
return Unstoppable * int(relative_rank(us, frontmost_sq(us, b)));
}
// evaluate_space() computes the space evaluation for a given side. The
// space evaluation is a simple bonus based on the number of safe squares
// available for minor pieces on the central four files on ranks 2--4. Safe
// squares one, two or three squares behind a friendly pawn are counted
// twice. Finally, the space bonus is scaled by a weight taken from the
// material hash table. The aim is to improve play on game opening.
// twice. Finally, the space bonus is multiplied by a weight. The aim is to
// improve play on game opening.
template<Color Us>
int evaluate_space(const Position& pos, const EvalInfo& ei) {
Score evaluate_space(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -680,7 +660,11 @@ namespace {
assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0);
// Count safe + (behind & safe) with a single popcount
return popcount<Full>((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe));
int bonus = popcount<Full>((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe));
int weight = pos.count<KNIGHT>(Us) + pos.count<BISHOP>(Us)
+ pos.count<KNIGHT>(Them) + pos.count<BISHOP>(Them);
return make_score(bonus * weight * weight, 0);
}
@@ -693,16 +677,15 @@ namespace {
EvalInfo ei;
Score score, mobility[2] = { SCORE_ZERO, SCORE_ZERO };
Thread* thisThread = pos.this_thread();
// Initialize score by reading the incrementally updated scores included
// in the position object (material + piece square tables) and adding a
// Tempo bonus. Score is computed from the point of view of white.
score = pos.psq_score() + (pos.side_to_move() == WHITE ? Tempo : -Tempo);
// in the position object (material + piece square tables).
// Score is computed from the point of view of white.
score = pos.psq_score();
// Probe the material hash table
ei.mi = Material::probe(pos, thisThread->materialTable, thisThread->endgames);
score += ei.mi->material_value();
ei.mi = Material::probe(pos);
score += ei.mi->imbalance();
// If we have a specialized evaluation function for the current material
// configuration, call it and return.
@@ -710,8 +693,8 @@ namespace {
return ei.mi->evaluate(pos);
// Probe the pawn hash table
ei.pi = Pawns::probe(pos, thisThread->pawnsTable);
score += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]);
ei.pi = Pawns::probe(pos);
score += apply_weight(ei.pi->pawns_score(), Weights[PawnStructure]);
// Initialize attack and king safety bitboards
init_eval_info<WHITE>(pos, ei);
@@ -741,42 +724,52 @@ namespace {
score += evaluate_passed_pawns<WHITE, Trace>(pos, ei)
- evaluate_passed_pawns<BLACK, Trace>(pos, ei);
// If one side has only a king, score for potential unstoppable pawns
if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
score += evaluate_unstoppable_pawns(pos, WHITE, ei)
- evaluate_unstoppable_pawns(pos, BLACK, ei);
// Evaluate space for both sides, only in middlegame
if (ei.mi->space_weight())
// If both sides have only pawns, score for potential unstoppable pawns
if (!pos.non_pawn_material(WHITE) && !pos.non_pawn_material(BLACK))
{
int s = evaluate_space<WHITE>(pos, ei) - evaluate_space<BLACK>(pos, ei);
score += apply_weight(s * ei.mi->space_weight(), Weights[Space]);
Bitboard b;
if ((b = ei.pi->passed_pawns(WHITE)) != 0)
score += int(relative_rank(WHITE, frontmost_sq(WHITE, b))) * Unstoppable;
if ((b = ei.pi->passed_pawns(BLACK)) != 0)
score -= int(relative_rank(BLACK, frontmost_sq(BLACK, b))) * Unstoppable;
}
// Evaluate space for both sides, only during opening
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg)
{
Score s = evaluate_space<WHITE>(pos, ei) - evaluate_space<BLACK>(pos, ei);
score += apply_weight(s, Weights[Space]);
}
// Scale winning side if position is more drawish than it appears
ScaleFactor sf = eg_value(score) > VALUE_DRAW ? ei.mi->scale_factor(pos, WHITE)
: ei.mi->scale_factor(pos, BLACK);
Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK;
ScaleFactor sf = ei.mi->scale_factor(pos, strongSide);
// If we don't already have an unusual scale factor, check for opposite
// colored bishop endgames, and use a lower scale for those.
// If we don't already have an unusual scale factor, check for certain
// types of endgames, and use a lower scale for those.
if ( ei.mi->game_phase() < PHASE_MIDGAME
&& pos.opposite_bishops()
&& (sf == SCALE_FACTOR_NORMAL || sf == SCALE_FACTOR_ONEPAWN))
{
// Ignoring any pawns, do both sides only have a single bishop and no
// other pieces?
if ( pos.non_pawn_material(WHITE) == BishopValueMg
&& pos.non_pawn_material(BLACK) == BishopValueMg)
if (pos.opposite_bishops())
{
// Check for KBP vs KB with only a single pawn that is almost
// certainly a draw or at least two pawns.
bool one_pawn = (pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK) == 1);
sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32);
}
else
// Endgame with opposite-colored bishops and no other pieces (ignoring pawns)
// is almost a draw, in case of KBP vs KB is even more a draw.
if ( pos.non_pawn_material(WHITE) == BishopValueMg
&& pos.non_pawn_material(BLACK) == BishopValueMg)
sf = more_than_one(pos.pieces(PAWN)) ? ScaleFactor(32) : ScaleFactor(8);
// Endgame with opposite-colored bishops, but also other pieces. Still
// a bit drawish, but not as drawish as with only the two bishops.
sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL);
else
sf = ScaleFactor(50 * sf / SCALE_FACTOR_NORMAL);
}
// Endings where weaker side can place his king in front of the opponent's
// pawns are drawish.
else if ( abs(eg_value(score)) <= BishopValueEg
&& ei.pi->pawn_span(strongSide) <= 1
&& !pos.pawn_passed(~strongSide, pos.king_square(~strongSide)))
sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(56) : ScaleFactor(38);
}
// Interpolate between a middlegame and a (scaled by 'sf') endgame score
@@ -788,20 +781,19 @@ namespace {
// In case of tracing add all single evaluation contributions for both white and black
if (Trace)
{
Tracing::add_term(Tracing::PST, pos.psq_score());
Tracing::add_term(Tracing::IMBALANCE, ei.mi->material_value());
Tracing::add_term(PAWN, ei.pi->pawns_value());
Tracing::add_term(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility])
, apply_weight(mobility[BLACK], Weights[Mobility]));
Score w = ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei);
Score b = ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei);
Tracing::add_term(Tracing::SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
Tracing::add_term(Tracing::TOTAL, score);
Tracing::write(Tracing::MATERIAL, pos.psq_score());
Tracing::write(Tracing::IMBALANCE, ei.mi->imbalance());
Tracing::write(PAWN, ei.pi->pawns_score());
Tracing::write(Tracing::MOBILITY, apply_weight(mobility[WHITE], Weights[Mobility])
, apply_weight(mobility[BLACK], Weights[Mobility]));
Tracing::write(Tracing::SPACE, apply_weight(evaluate_space<WHITE>(pos, ei), Weights[Space])
, apply_weight(evaluate_space<BLACK>(pos, ei), Weights[Space]));
Tracing::write(Tracing::TOTAL, score);
Tracing::ei = ei;
Tracing::sf = sf;
}
return pos.side_to_move() == WHITE ? v : -v;
return (pos.side_to_move() == WHITE ? v : -v) + Eval::Tempo;
}
@@ -809,25 +801,27 @@ namespace {
double Tracing::to_cp(Value v) { return double(v) / PawnValueEg; }
void Tracing::add_term(int idx, Score wScore, Score bScore) {
void Tracing::write(int idx, Color c, Score s) { scores[c][idx] = s; }
terms[WHITE][idx] = wScore;
terms[BLACK][idx] = bScore;
void Tracing::write(int idx, Score w, Score b) {
write(idx, WHITE, w);
write(idx, BLACK, b);
}
void Tracing::format_row(std::stringstream& ss, const char* name, int idx) {
void Tracing::print(std::stringstream& ss, const char* name, int idx) {
Score wScore = terms[WHITE][idx];
Score bScore = terms[BLACK][idx];
Score wScore = scores[WHITE][idx];
Score bScore = scores[BLACK][idx];
switch (idx) {
case PST: case IMBALANCE: case PAWN: case TOTAL:
ss << std::setw(20) << name << " | --- --- | --- --- | "
case MATERIAL: case IMBALANCE: case PAWN: case TOTAL:
ss << std::setw(15) << name << " | --- --- | --- --- | "
<< std::setw(5) << to_cp(mg_value(wScore - bScore)) << " "
<< std::setw(5) << to_cp(eg_value(wScore - bScore)) << " \n";
break;
default:
ss << std::setw(20) << name << " | " << std::noshowpos
ss << std::setw(15) << name << " | " << std::noshowpos
<< std::setw(5) << to_cp(mg_value(wScore)) << " "
<< std::setw(5) << to_cp(eg_value(wScore)) << " | "
<< std::setw(5) << to_cp(mg_value(bScore)) << " "
@@ -839,32 +833,32 @@ namespace {
std::string Tracing::do_trace(const Position& pos) {
std::memset(terms, 0, sizeof(terms));
std::memset(scores, 0, sizeof(scores));
Value v = do_evaluate<true>(pos);
v = pos.side_to_move() == WHITE ? v : -v; // White's point of view
std::stringstream ss;
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
<< " Eval term | White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n"
<< "---------------------+-------------+-------------+-------------\n";
<< " Eval term | White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n"
<< "----------------+-------------+-------------+-------------\n";
format_row(ss, "Material, PST, Tempo", PST);
format_row(ss, "Material imbalance", IMBALANCE);
format_row(ss, "Pawns", PAWN);
format_row(ss, "Knights", KNIGHT);
format_row(ss, "Bishops", BISHOP);
format_row(ss, "Rooks", ROOK);
format_row(ss, "Queens", QUEEN);
format_row(ss, "Mobility", MOBILITY);
format_row(ss, "King safety", KING);
format_row(ss, "Threats", THREAT);
format_row(ss, "Passed pawns", PASSED);
format_row(ss, "Space", SPACE);
print(ss, "Material", MATERIAL);
print(ss, "Imbalance", IMBALANCE);
print(ss, "Pawns", PAWN);
print(ss, "Knights", KNIGHT);
print(ss, "Bishops", BISHOP);
print(ss, "Rooks", ROOK);
print(ss, "Queens", QUEEN);
print(ss, "Mobility", MOBILITY);
print(ss, "King safety", KING);
print(ss, "Threats", THREAT);
print(ss, "Passed pawns", PASSED);
print(ss, "Space", SPACE);
ss << "---------------------+-------------+-------------+-------------\n";
format_row(ss, "Total", TOTAL);
ss << "----------------+-------------+-------------+-------------\n";
print(ss, "Total", TOTAL);
ss << "\nTotal Evaluation: " << to_cp(v) << " (white side)\n";
@@ -893,27 +887,18 @@ namespace Eval {
}
/// init() computes evaluation weights from the corresponding UCI parameters
/// and setup king tables.
/// init() computes evaluation weights, usually at startup
void init() {
Weights[Mobility] = weight_option("Mobility (Midgame)", "Mobility (Endgame)", WeightsInternal[Mobility]);
Weights[PawnStructure] = weight_option("Pawn Structure (Midgame)", "Pawn Structure (Endgame)", WeightsInternal[PawnStructure]);
Weights[PassedPawns] = weight_option("Passed Pawns (Midgame)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]);
Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]);
Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]);
const double MaxSlope = 30;
const double MaxSlope = 7.5;
const double Peak = 1280;
double t = 0.0;
for (int t = 0, i = 1; i < 100; ++i)
for (int i = 1; i < 400; ++i)
{
t = int(std::min(Peak, std::min(0.4 * i * i, t + MaxSlope)));
KingDanger[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]);
KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]);
t = std::min(Peak, std::min(0.025 * i * i, t + MaxSlope));
KingDanger[i] = apply_weight(make_score(int(t), 0), Weights[KingSafety]);
}
}
+8 -4
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -20,15 +20,19 @@
#ifndef EVALUATE_H_INCLUDED
#define EVALUATE_H_INCLUDED
#include <string>
#include "types.h"
class Position;
namespace Eval {
extern void init();
extern Value evaluate(const Position& pos);
extern std::string trace(const Position& pos);
const Value Tempo = Value(17); // Must be visible to search
void init();
Value evaluate(const Position& pos);
std::string trace(const Position& pos);
}
+6 -4
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -25,7 +25,8 @@
#include "search.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h"
#include "uci.h"
#include "syzygy/tbprobe.h"
int main(int argc, char* argv[]) {
@@ -34,11 +35,12 @@ int main(int argc, char* argv[]) {
UCI::init(Options);
Bitboards::init();
Position::init();
Bitbases::init_kpk();
Bitbases::init();
Search::init();
Pawns::init();
Eval::init();
Pawns::init();
Threads.init();
Tablebases::init(Options["SyzygyPath"]);
TT.resize(Options["Hash"]);
UCI::loop(argc, argv);
+48 -75
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -17,33 +17,34 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm> // For std::min
#include <algorithm> // For std::min
#include <cassert>
#include <cstring>
#include <cstring> // For std::memset
#include "material.h"
#include "thread.h"
using namespace std;
namespace {
// Polynomial material balance parameters
// Polynomial material imbalance parameters
// pair pawn knight bishop rook queen
const int LinearCoefficients[6] = { 1852, -162, -1122, -183, 249, -154 };
// pair pawn knight bishop rook queen
const int Linear[6] = { 1852, -162, -1122, -183, 249, -154 };
const int QuadraticCoefficientsSameSide[][PIECE_TYPE_NB] = {
const int QuadraticOurs[][PIECE_TYPE_NB] = {
// OUR PIECES
// pair pawn knight bishop rook queen
{ 0 }, // Bishop pair
{ 39, 2 }, // Pawn
{ 35, 271, -4 }, // knight OUR PIECES
{ 35, 271, -4 }, // Knight OUR PIECES
{ 0, 105, 4, 0 }, // Bishop
{ -27, -2, 46, 100, -141 }, // Rook
{-177, 25, 129, 142, -137, 0 } // Queen
};
const int QuadraticCoefficientsOppositeSide[][PIECE_TYPE_NB] = {
const int QuadraticTheirs[][PIECE_TYPE_NB] = {
// THEIR PIECES
// pair pawn knight bishop rook queen
{ 0 }, // Bishop pair
@@ -56,7 +57,7 @@ namespace {
// Endgame evaluation and scaling functions are accessed directly and not through
// the function maps because they correspond to more than one material hash key.
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) };
Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) };
@@ -66,8 +67,7 @@ namespace {
// Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
return !pos.count<PAWN>(Them)
&& pos.non_pawn_material(Them) == VALUE_ZERO
return !more_than_one(pos.pieces(Them))
&& pos.non_pawn_material(Us) >= RookValueMg;
}
@@ -94,57 +94,52 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
int pt1, pt2, pc, v;
int value = 0;
int bonus = 0;
// Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
{
pc = pieceCount[Us][pt1];
if (!pc)
if (!pieceCount[Us][pt1])
continue;
v = LinearCoefficients[pt1];
int v = Linear[pt1];
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
v += QuadraticCoefficientsSameSide[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeSide[pt1][pt2] * pieceCount[Them][pt2];
for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2];
value += pc * v;
bonus += pieceCount[Us][pt1] * v;
}
return value;
return bonus;
}
} // namespace
namespace Material {
/// Material::probe() takes a position object as input, looks up a MaterialEntry
/// object, and returns a pointer to it. If the material configuration is not
/// already present in the table, it is computed and stored there, so we don't
/// have to recompute everything when the same material configuration occurs again.
/// Material::probe() looks up the current position's material configuration in
/// the material hash table. It returns a pointer to the Entry if the position
/// is found. Otherwise a new Entry is computed and stored there, so we don't
/// have to recompute all when the same material configuration occurs again.
Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
Entry* probe(const Position& pos) {
Key key = pos.material_key();
Entry* e = entries[key];
Entry* e = pos.this_thread()->materialTable[key];
// If e->key matches the position's material hash key, it means that we
// have analysed this material configuration before, and we can simply
// return the information we found the last time instead of recomputing it.
if (e->key == key)
return e;
std::memset(e, 0, sizeof(Entry));
e->key = key;
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
e->gamePhase = game_phase(pos);
e->gamePhase = pos.game_phase();
// Let's look if we have a specialized evaluation function for this particular
// material configuration. Firstly we look for a fixed configuration one, then
// for a generic one if the previous search failed.
if (endgames.probe(key, e->evaluationFunction))
if (pos.this_thread()->endgames.probe(key, e->evaluationFunction))
return e;
if (is_KXK<WHITE>(pos))
@@ -159,22 +154,19 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
return e;
}
// OK, we didn't find any special evaluation function for the current
// material configuration. Is there a suitable scaling function?
//
// We face problems when there are several conflicting applicable
// scaling functions and we need to decide which one to use.
// OK, we didn't find any special evaluation function for the current material
// configuration. Is there a suitable specialized scaling function?
EndgameBase<ScaleFactor>* sf;
if (endgames.probe(key, sf))
if (pos.this_thread()->endgames.probe(key, sf))
{
e->scalingFunction[sf->color()] = sf;
e->scalingFunction[sf->strong_side()] = sf; // Only strong color assigned
return e;
}
// Generic scaling functions that refer to more than one material
// distribution. They should be probed after the specialized ones.
// Note that these ones don't return after setting the function.
// We didn't find any specialized scaling function, so fall back on generic
// ones that refer to more than one material distribution. Note that in this
// case we don't return after setting the function.
if (is_KBPsKs<WHITE>(pos))
e->scalingFunction[WHITE] = &ScaleKBPsK[WHITE];
@@ -190,16 +182,18 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK);
if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN))
if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board
{
if (!pos.count<PAWN>(BLACK))
{
assert(pos.count<PAWN>(WHITE) >= 2);
e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
}
else if (!pos.count<PAWN>(WHITE))
{
assert(pos.count<PAWN>(BLACK) >= 2);
e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
}
else if (pos.count<PAWN>(WHITE) == 1 && pos.count<PAWN>(BLACK) == 1)
@@ -211,14 +205,16 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
}
}
// No pawns makes it difficult to win, even with a material advantage. This
// catches some trivial draws like KK, KBK and KNK and gives a very drawish
// scale factor for cases such as KRKBP and KmmKm (except for KBBKN).
// Zero or just one pawn makes it difficult to win, even with a small material
// advantage. This catches some trivial draws like KK, KBK and KNK and gives a
// drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN).
if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW : npm_b <= BishopValueMg ? 4 : 12);
e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW :
npm_b <= BishopValueMg ? 4 : 12);
if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg)
e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW : npm_w <= BishopValueMg ? 4 : 12);
e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW :
npm_w <= BishopValueMg ? 4 : 12);
if (pos.count<PAWN>(WHITE) == 1 && npm_w - npm_b <= BishopValueMg)
e->factor[WHITE] = (uint8_t) SCALE_FACTOR_ONEPAWN;
@@ -226,40 +222,17 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
if (pos.count<PAWN>(BLACK) == 1 && npm_b - npm_w <= BishopValueMg)
e->factor[BLACK] = (uint8_t) SCALE_FACTOR_ONEPAWN;
// Compute the space weight
if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg)
{
int minorPieceCount = pos.count<KNIGHT>(WHITE) + pos.count<BISHOP>(WHITE)
+ pos.count<KNIGHT>(BLACK) + pos.count<BISHOP>(BLACK);
e->spaceWeight = make_score(minorPieceCount * minorPieceCount, 0);
}
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
// for the bishop pair "extended piece", which allows us to be more flexible
// in defining bishop pair bonuses.
const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
const int PieceCount[COLOR_NB][PIECE_TYPE_NB] = {
{ pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),
pos.count<BISHOP>(WHITE) , pos.count<ROOK>(WHITE), pos.count<QUEEN >(WHITE) },
{ pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
e->value = (int16_t)((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
e->value = int16_t((imbalance<WHITE>(PieceCount) - imbalance<BLACK>(PieceCount)) / 16);
return e;
}
/// Material::game_phase() calculates the phase given the current
/// position. Because the phase is strictly a function of the material, it
/// is stored in MaterialEntry.
Phase game_phase(const Position& pos) {
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
return npm >= MidgameLimit ? PHASE_MIDGAME
: npm <= EndgameLimit ? PHASE_ENDGAME
: Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
}
} // namespace Material
+18 -21
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -28,47 +28,44 @@
namespace Material {
/// Material::Entry contains various information about a material configuration.
/// It contains a material balance evaluation, a function pointer to a special
/// It contains a material imbalance evaluation, a function pointer to a special
/// endgame evaluation function (which in most cases is NULL, meaning that the
/// standard evaluation function will be used), and "scale factors".
/// standard evaluation function will be used), and scale factors.
///
/// The scale factors are used to scale the evaluation score up or down.
/// For instance, in KRB vs KR endgames, the score is scaled down by a factor
/// of 4, which will result in scores of absolute value less than one pawn.
/// The scale factors are used to scale the evaluation score up or down. For
/// instance, in KRB vs KR endgames, the score is scaled down by a factor of 4,
/// which will result in scores of absolute value less than one pawn.
struct Entry {
Score material_value() const { return make_score(value, value); }
Score space_weight() const { return spaceWeight; }
Score imbalance() const { return make_score(value, value); }
Phase game_phase() const { return gamePhase; }
bool specialized_eval_exists() const { return evaluationFunction != NULL; }
Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); }
// scale_factor takes a position and a color as input, and returns a scale factor
// for the given color. We have to provide the position in addition to the color,
// because the scale factor need not 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 for draws with rook pawns and wrong-colored bishops.
// scale_factor takes a position and a color as input and returns a scale factor
// for the given color. We have to provide the position in addition to the color
// because the scale factor may also be a function which should be applied to
// the position. For instance, in KBP vs K endgames, the scaling function looks
// for rook pawns and wrong-colored bishops.
ScaleFactor scale_factor(const Position& pos, Color c) const {
return !scalingFunction[c] || (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE
? ScaleFactor(factor[c]) : (*scalingFunction[c])(pos);
return !scalingFunction[c]
|| (*scalingFunction[c])(pos) == SCALE_FACTOR_NONE ? ScaleFactor(factor[c])
: (*scalingFunction[c])(pos);
}
Key key;
int16_t value;
uint8_t factor[COLOR_NB];
EndgameBase<Value>* evaluationFunction;
EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB];
Score spaceWeight;
EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
// side (e.g. KPKP, KBPsKs)
Phase gamePhase;
};
typedef HashTable<Entry, 8192> Table;
Entry* probe(const Position& pos, Table& entries, Endgames& endgames);
Phase game_phase(const Position& pos);
Entry* probe(const Position& pos);
} // namespace Material
+54 -50
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
@@ -26,58 +27,14 @@
using namespace std;
namespace {
/// Version number. If Version is left empty, then compile date in the format
/// DD-MM-YY and show in engine_info.
static const string Version = "5";
/// engine_info() returns the full name of the current Stockfish version. This
/// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when
/// the program was compiled) or "Stockfish <Version>", depending on whether
/// Version is empty.
const string engine_info(bool to_uci) {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
string month, day, year;
stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008"
ss << "Stockfish " << Version << setfill('0');
if (Version.empty())
{
date >> month >> day >> year;
ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
}
ss << (Is64Bit ? " 64" : "")
<< (HasPext ? " BMI2" : (HasPopCnt ? " SSE4.2" : ""))
<< (to_uci ? "\nid author ": " by ")
<< "Tord Romstad, Marco Costalba and Joona Kiiski";
return ss.str();
}
/// Debug functions used mainly to collect run-time statistics
static int64_t hits[2], means[2];
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 "
<< (double)means[1] / means[0] << endl;
}
const string Version = "6";
/// Debug counters
int64_t hits[2], means[2];
/// 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
@@ -136,6 +93,53 @@ public:
}
};
} // namespace
/// engine_info() returns the full name of the current Stockfish version. This
/// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when
/// the program was compiled) or "Stockfish <Version>", depending on whether
/// Version is empty.
const string engine_info(bool to_uci) {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
string month, day, year;
stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008"
ss << "Stockfish " << Version << setfill('0');
if (Version.empty())
{
date >> month >> day >> year;
ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
}
ss << (Is64Bit ? " 64" : "")
<< (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : ""))
<< (to_uci ? "\nid author ": " by ")
<< "Tord Romstad, Marco Costalba and Joona Kiiski";
return ss.str();
}
/// Debug functions used mainly to collect run-time statistics
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 "
<< (double)means[1] / means[0] << endl;
}
/// Used to serialize access to std::cout to avoid multiple threads writing at
/// the same time.
+49 -17
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -20,27 +20,22 @@
#ifndef MISC_H_INCLUDED
#define MISC_H_INCLUDED
#include <fstream>
#include <cassert>
#include <ostream>
#include <string>
#include <vector>
#include "types.h"
extern const std::string engine_info(bool to_uci = false);
extern void timed_wait(WaitCondition&, Lock&, int);
extern void prefetch(char* addr);
extern void start_logger(bool b);
const std::string engine_info(bool to_uci = false);
void timed_wait(WaitCondition&, Lock&, int);
void prefetch(char* addr);
void start_logger(bool b);
extern void dbg_hit_on(bool b);
extern void dbg_hit_on_c(bool c, bool b);
extern void dbg_mean_of(int v);
extern void dbg_print();
struct Log : public std::ofstream {
Log(const std::string& f = "log.txt") : std::ofstream(f.c_str(), std::ios::out | std::ios::app) {}
~Log() { if (is_open()) close(); }
};
void dbg_hit_on(bool b);
void dbg_hit_on_c(bool c, bool b);
void dbg_mean_of(int v);
void dbg_print();
namespace Time {
@@ -52,7 +47,7 @@ namespace Time {
template<class Entry, int Size>
struct HashTable {
HashTable() : table(Size, Entry()) {}
Entry* operator[](Key k) { return &table[(uint32_t)k & (Size - 1)]; }
Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; }
private:
std::vector<Entry> table;
@@ -65,4 +60,41 @@ std::ostream& operator<<(std::ostream&, SyncCout);
#define sync_cout std::cout << IO_LOCK
#define sync_endl std::endl << IO_UNLOCK
/// xorshift64star Pseudo-Random Number Generator
/// This class is based on original code written and dedicated
/// to the public domain by Sebastiano Vigna (2014).
/// It has the following characteristics:
///
/// - Outputs 64-bit numbers
/// - Passes Dieharder and SmallCrush test batteries
/// - Does not require warm-up, no zeroland to escape
/// - Internal state is a single 64-bit integer
/// - Period is 2^64 - 1
/// - Speed: 1.60 ns/call (Core i7 @3.40GHz)
///
/// For further analysis see
/// <http://vigna.di.unimi.it/ftp/papers/xorshift.pdf>
class PRNG {
uint64_t s;
uint64_t rand64() {
s ^= s >> 12, s ^= s << 25, s ^= s >> 27;
return s * 2685821657736338717LL;
}
public:
PRNG(uint64_t seed) : s(seed) { assert(seed); }
template<typename T> T rand() { return T(rand64()); }
/// Special generator used to fast init magic numbers.
/// Output values only have 1/8th of their bits set on average.
template<typename T> T sparse_rand()
{ return T(rand64() & rand64() & rand64()); }
};
#endif // #ifndef MISC_H_INCLUDED
+88 -88
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -25,12 +25,12 @@
namespace {
template<CastlingRight Cr, bool Checks, bool Chess960>
ExtMove* generate_castling(const Position& pos, ExtMove* mlist, Color us, const CheckInfo* ci) {
ExtMove* generate_castling(const Position& pos, ExtMove* moveList, Color us, const CheckInfo* ci) {
static const bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO);
if (pos.castling_impeded(Cr) || !pos.can_castle(Cr))
return mlist;
return moveList;
// After castling, the rook and king final positions are the same in Chess960
// as they would be in standard chess.
@@ -46,59 +46,51 @@ namespace {
for (Square s = kto; s != kfrom; s += K)
if (pos.attackers_to(s) & enemies)
return mlist;
return moveList;
// Because we generate only legal castling moves we need to verify that
// when moving the castling rook we do not discover some hidden checker.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
if (Chess960 && (attacks_bb<ROOK>(kto, pos.pieces() ^ rfrom) & pos.pieces(~us, ROOK, QUEEN)))
return mlist;
return moveList;
Move m = make<CASTLING>(kfrom, rfrom);
if (Checks && !pos.gives_check(m, *ci))
return mlist;
return moveList;
(mlist++)->move = m;
(moveList++)->move = m;
return mlist;
return moveList;
}
template<GenType Type, Square Delta>
inline ExtMove* generate_promotions(ExtMove* mlist, Bitboard pawnsOn7,
Bitboard target, const CheckInfo* ci) {
inline ExtMove* make_promotions(ExtMove* moveList, Square to, const CheckInfo* ci) {
Bitboard b = shift_bb<Delta>(pawnsOn7) & target;
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
(moveList++)->move = make<PROMOTION>(to - Delta, to, QUEEN);
while (b)
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
{
Square to = pop_lsb(&b);
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
(mlist++)->move = make<PROMOTION>(to - Delta, to, QUEEN);
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
{
(mlist++)->move = make<PROMOTION>(to - Delta, to, ROOK);
(mlist++)->move = make<PROMOTION>(to - Delta, to, BISHOP);
(mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
}
// Knight promotion is the only promotion that can give a direct check
// that's not already included in the queen promotion.
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
(mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
else
(void)ci; // Silence a warning under MSVC
(moveList++)->move = make<PROMOTION>(to - Delta, to, ROOK);
(moveList++)->move = make<PROMOTION>(to - Delta, to, BISHOP);
(moveList++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
}
return mlist;
// Knight promotion is the only promotion that can give a direct check
// that's not already included in the queen promotion.
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
(moveList++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
else
(void)ci; // Silence a warning under MSVC
return moveList;
}
template<Color Us, GenType Type>
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* mlist,
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList,
Bitboard target, const CheckInfo* ci) {
// Compute our parametrized parameters at compile time, named according to
@@ -111,7 +103,7 @@ namespace {
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
Bitboard b1, b2, dc1, dc2, emptySquares;
Bitboard emptySquares;
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
@@ -124,8 +116,8 @@ namespace {
{
emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
b1 = shift_bb<Up>(pawnsNotOn7) & emptySquares;
b2 = shift_bb<Up>(b1 & TRank3BB) & emptySquares;
Bitboard b1 = shift_bb<Up>(pawnsNotOn7) & emptySquares;
Bitboard b2 = shift_bb<Up>(b1 & TRank3BB) & emptySquares;
if (Type == EVASIONS) // Consider only blocking squares
{
@@ -144,8 +136,8 @@ namespace {
// promotion has been already generated amongst the captures.
if (pawnsNotOn7 & ci->dcCandidates)
{
dc1 = shift_bb<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
dc2 = shift_bb<Up>(dc1 & TRank3BB) & emptySquares;
Bitboard dc1 = shift_bb<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
Bitboard dc2 = shift_bb<Up>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1;
b2 |= dc2;
@@ -155,13 +147,13 @@ namespace {
while (b1)
{
Square to = pop_lsb(&b1);
(mlist++)->move = make_move(to - Up, to);
(moveList++)->move = make_move(to - Up, to);
}
while (b2)
{
Square to = pop_lsb(&b2);
(mlist++)->move = make_move(to - Up - Up, to);
(moveList++)->move = make_move(to - Up - Up, to);
}
}
@@ -174,27 +166,36 @@ namespace {
if (Type == EVASIONS)
emptySquares &= target;
mlist = generate_promotions<Type, Right>(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, Left >(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, Up>(mlist, pawnsOn7, emptySquares, ci);
Bitboard b1 = shift_bb<Right>(pawnsOn7) & enemies;
Bitboard b2 = shift_bb<Left >(pawnsOn7) & enemies;
Bitboard b3 = shift_bb<Up >(pawnsOn7) & emptySquares;
while (b1)
moveList = make_promotions<Type, Right>(moveList, pop_lsb(&b1), ci);
while (b2)
moveList = make_promotions<Type, Left >(moveList, pop_lsb(&b2), ci);
while (b3)
moveList = make_promotions<Type, Up >(moveList, pop_lsb(&b3), ci);
}
// Standard and en-passant captures
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{
b1 = shift_bb<Right>(pawnsNotOn7) & enemies;
b2 = shift_bb<Left >(pawnsNotOn7) & enemies;
Bitboard b1 = shift_bb<Right>(pawnsNotOn7) & enemies;
Bitboard b2 = shift_bb<Left >(pawnsNotOn7) & enemies;
while (b1)
{
Square to = pop_lsb(&b1);
(mlist++)->move = make_move(to - Right, to);
(moveList++)->move = make_move(to - Right, to);
}
while (b2)
{
Square to = pop_lsb(&b2);
(mlist++)->move = make_move(to - Left, to);
(moveList++)->move = make_move(to - Left, to);
}
if (pos.ep_square() != SQ_NONE)
@@ -205,23 +206,23 @@ namespace {
// is the double pushed pawn and so is in the target. Otherwise this
// is a discovery check and we are forced to do otherwise.
if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
return mlist;
return moveList;
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
assert(b1);
while (b1)
(mlist++)->move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
(moveList++)->move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
}
}
return mlist;
return moveList;
}
template<PieceType Pt, bool Checks> FORCE_INLINE
ExtMove* generate_moves(const Position& pos, ExtMove* mlist, Color us,
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us,
Bitboard target, const CheckInfo* ci) {
assert(Pt != KING && Pt != PAWN);
@@ -236,7 +237,7 @@ namespace {
&& !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt]))
continue;
if (unlikely(ci->dcCandidates) && (ci->dcCandidates & from))
if (ci->dcCandidates && (ci->dcCandidates & from))
continue;
}
@@ -246,51 +247,50 @@ namespace {
b &= ci->checkSq[Pt];
while (b)
(mlist++)->move = make_move(from, pop_lsb(&b));
(moveList++)->move = make_move(from, pop_lsb(&b));
}
return mlist;
return moveList;
}
template<Color Us, GenType Type> FORCE_INLINE
ExtMove* generate_all(const Position& pos, ExtMove* mlist, Bitboard target,
ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target,
const CheckInfo* ci = NULL) {
const bool Checks = Type == QUIET_CHECKS;
mlist = generate_pawn_moves<Us, Type>(pos, mlist, target, ci);
mlist = generate_moves<KNIGHT, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves<BISHOP, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves< ROOK, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves< QUEEN, Checks>(pos, mlist, Us, target, ci);
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target, ci);
moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target, ci);
moveList = generate_moves<BISHOP, Checks>(pos, moveList, Us, target, ci);
moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target, ci);
moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target, ci);
if (Type != QUIET_CHECKS && Type != EVASIONS)
{
Square ksq = pos.king_square(Us);
Bitboard b = pos.attacks_from<KING>(ksq) & target;
while (b)
(mlist++)->move = make_move(ksq, pop_lsb(&b));
(moveList++)->move = make_move(ksq, pop_lsb(&b));
}
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
{
if (pos.is_chess960())
{
mlist = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, true>(pos, mlist, Us, ci);
mlist = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, true>(pos, mlist, Us, ci);
moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, true>(pos, moveList, Us, ci);
moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, true>(pos, moveList, Us, ci);
}
else
{
mlist = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, false>(pos, mlist, Us, ci);
mlist = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, false>(pos, mlist, Us, ci);
moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, false>(pos, moveList, Us, ci);
moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, false>(pos, moveList, Us, ci);
}
}
return mlist;
return moveList;
}
} // namespace
@@ -304,19 +304,19 @@ namespace {
/// non-captures. Returns a pointer to the end of the move list.
template<GenType Type>
ExtMove* generate(const Position& pos, ExtMove* mlist) {
ExtMove* generate(const Position& pos, ExtMove* moveList) {
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
assert(!pos.checkers());
Color us = pos.side_to_move();
Bitboard target = Type == CAPTURES ? pos.pieces(~us)
: Type == QUIETS ? ~pos.pieces()
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
Bitboard target = Type == CAPTURES ? pos.pieces(~us)
: Type == QUIETS ? ~pos.pieces()
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
return us == WHITE ? generate_all<WHITE, Type>(pos, mlist, target)
: generate_all<BLACK, Type>(pos, mlist, target);
return us == WHITE ? generate_all<WHITE, Type>(pos, moveList, target)
: generate_all<BLACK, Type>(pos, moveList, target);
}
// Explicit template instantiations
@@ -328,7 +328,7 @@ template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight
/// underpromotions that give check. Returns a pointer to the end of the move list.
template<>
ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* mlist) {
ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
assert(!pos.checkers());
@@ -350,18 +350,18 @@ ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* mlist) {
b &= ~PseudoAttacks[QUEEN][ci.ksq];
while (b)
(mlist++)->move = make_move(from, pop_lsb(&b));
(moveList++)->move = make_move(from, pop_lsb(&b));
}
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, mlist, ~pos.pieces(), &ci)
: generate_all<BLACK, QUIET_CHECKS>(pos, mlist, ~pos.pieces(), &ci);
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList, ~pos.pieces(), &ci)
: generate_all<BLACK, QUIET_CHECKS>(pos, moveList, ~pos.pieces(), &ci);
}
/// generate<EVASIONS> generates all pseudo-legal check evasions when the side
/// to move is in check. Returns a pointer to the end of the move list.
template<>
ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* mlist) {
ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* moveList) {
assert(pos.checkers());
@@ -382,37 +382,37 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* mlist) {
// Generate evasions for king, capture and non capture moves
Bitboard b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
while (b)
(mlist++)->move = make_move(ksq, pop_lsb(&b));
(moveList++)->move = make_move(ksq, pop_lsb(&b));
if (more_than_one(pos.checkers()))
return mlist; // Double check, only a king move can save the day
return moveList; // Double check, only a king move can save the day
// Generate blocking evasions or captures of the checking piece
Square checksq = lsb(pos.checkers());
Bitboard target = between_bb(checksq, ksq) | checksq;
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, mlist, target)
: generate_all<BLACK, EVASIONS>(pos, mlist, target);
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList, target)
: generate_all<BLACK, EVASIONS>(pos, moveList, target);
}
/// generate<LEGAL> generates all the legal moves in the given position
template<>
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* mlist) {
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
ExtMove *end, *cur = mlist;
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
Square ksq = pos.king_square(pos.side_to_move());
ExtMove* cur = moveList;
end = pos.checkers() ? generate<EVASIONS>(pos, mlist)
: generate<NON_EVASIONS>(pos, mlist);
while (cur != end)
moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList)
: generate<NON_EVASIONS>(pos, moveList);
while (cur != moveList)
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)
&& !pos.legal(cur->move, pinned))
cur->move = (--end)->move;
cur->move = (--moveList)->move;
else
++cur;
return end;
return moveList;
}
+16 -7
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -22,6 +22,8 @@
#include "types.h"
class Position;
enum GenType {
CAPTURES,
QUIETS,
@@ -31,27 +33,34 @@ enum GenType {
LEGAL
};
class Position;
struct ExtMove {
Move move;
Value value;
};
inline bool operator<(const ExtMove& f, const ExtMove& s) {
return f.value < s.value;
}
template<GenType>
ExtMove* generate(const Position& pos, ExtMove* mlist);
ExtMove* generate(const Position& pos, ExtMove* moveList);
/// The MoveList struct is a simple wrapper around generate(). It sometimes comes
/// in handy to use this class instead of the low level generate() function.
template<GenType T>
struct MoveList {
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) { last->move = MOVE_NONE; }
explicit MoveList(const Position& pos) : cur(moveList), last(generate<T>(pos, moveList)) { last->move = MOVE_NONE; }
void operator++() { ++cur; }
Move operator*() const { return cur->move; }
size_t size() const { return last - mlist; }
size_t size() const { return last - moveList; }
bool contains(Move m) const {
for (const ExtMove* it(mlist); it != last; ++it) if (it->move == m) return true;
for (const ExtMove* it(moveList); it != last; ++it) if (it->move == m) return true;
return false;
}
private:
ExtMove mlist[MAX_MOVES];
ExtMove moveList[MAX_MOVES];
ExtMove *cur, *last;
};
+3 -10
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -51,7 +51,7 @@ namespace {
// Unary predicate used by std::partition to split positive values from remaining
// ones so as to sort the two sets separately, with the second sort delayed.
inline bool has_positive_value(const ExtMove& ms) { return ms.value > 0; }
inline bool has_positive_value(const ExtMove& move) { return move.value > VALUE_ZERO; }
// Picks the best move in the range (begin, end) and moves it to the front.
// It's faster than sorting all the moves in advance when there are few
@@ -61,7 +61,7 @@ namespace {
std::swap(*begin, *std::max_element(begin, end));
return begin;
}
}
} // namespace
/// Constructors of the MovePicker class. As arguments we pass information
@@ -103,15 +103,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats&
stage = QSEARCH_0;
else if (d > DEPTH_QS_RECAPTURES)
{
stage = 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.capture_or_promotion(ttm))
ttm = MOVE_NONE;
}
else
{
stage = RECAPTURE;
+2 -2
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -40,7 +40,7 @@
template<bool Gain, typename T>
struct Stats {
static const Value Max = Value(2000);
static const Value Max = Value(250);
const T* operator[](Piece pc) const { return table[pc]; }
void clear() { std::memset(table, 0, sizeof(table)); }
-256
View File
@@ -1,256 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 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 <stack>
#include "movegen.h"
#include "notation.h"
#include "position.h"
using namespace std;
static const char* PieceToChar[COLOR_NB] = { " 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 ss;
if (abs(v) < VALUE_MATE_IN_MAX_PLY)
ss << "cp " << v * 100 / PawnValueEg;
else
ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
return ss.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 castling moves are always encoded 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) == CASTLING && !chess960)
to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
string move = to_string(from) + to_string(to);
if (type_of(m) == PROMOTION)
move += PieceToChar[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> it(pos); *it; ++it)
if (str == move_to_uci(*it, pos.is_chess960()))
return *it;
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(MoveList<LEGAL>(pos).contains(m));
Bitboard others, b;
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) == CASTLING)
san = to > from ? "O-O" : "O-O-O";
else
{
if (pt != PAWN)
{
san = PieceToChar[WHITE][pt]; // Upper case
// A disambiguation occurs if we have more then one piece of type 'pt'
// that can reach 'to' with a legal move.
others = b = (pos.attacks_from(pc, to) & pos.pieces(us, pt)) ^ from;
while (b)
{
Square s = pop_lsb(&b);
if (!pos.legal(make_move(s, to), pos.pinned_pieces(us)))
others ^= s;
}
if (!others)
{ /* Disambiguation is not needed */ }
else if (!(others & file_bb(from)))
san += to_char(file_of(from));
else if (!(others & rank_bb(from)))
san += to_char(rank_of(from));
else
san += to_string(from);
}
else if (pos.capture(m))
san = to_char(file_of(from));
if (pos.capture(m))
san += 'x';
san += to_string(to);
if (type_of(m) == PROMOTION)
san += string("=") + PieceToChar[WHITE][promotion_type(m)];
}
if (pos.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 the time and score respectively.
static string format(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 ss;
if (hours)
ss << hours << ':';
ss << setfill('0') << setw(2) << minutes << ':' << setw(2) << seconds;
return ss.str();
}
static string format(Value v) {
stringstream ss;
if (v >= VALUE_MATE_IN_MAX_PLY)
ss << "#" << (VALUE_MATE - v + 1) / 2;
else if (v <= VALUE_MATED_IN_MAX_PLY)
ss << "-#" << (VALUE_MATE + v) / 2;
else
ss << setprecision(2) << fixed << showpos << double(v) / PawnValueEg;
return ss.str();
}
string pretty_pv(Position& pos, int depth, Value value, int64_t msecs, Move pv[]) {
const uint64_t K = 1000;
const uint64_t M = 1000000;
std::stack<StateInfo> st;
Move* m = pv;
string san, str, padding;
stringstream ss;
ss << setw(2) << depth << setw(8) << format(value) << setw(8) << format(msecs);
if (pos.nodes_searched() < M)
ss << setw(8) << pos.nodes_searched() / 1 << " ";
else if (pos.nodes_searched() < K * M)
ss << setw(7) << pos.nodes_searched() / K << "K ";
else
ss << setw(7) << pos.nodes_searched() / M << "M ";
str = ss.str();
padding = string(str.length(), ' ');
while (*m != MOVE_NONE)
{
san = move_to_san(pos, *m) + ' ';
if ((str.length() + san.length()) % 80 <= san.length()) // Exceed 80 cols
str += "\n" + padding;
str += san;
st.push(StateInfo());
pos.do_move(*m++, st.top());
}
while (m != pv)
pos.undo_move(*--m);
return str;
}
-35
View File
@@ -1,35 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 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/>.
*/
#ifndef NOTATION_H_INCLUDED
#define NOTATION_H_INCLUDED
#include <string>
#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);
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 // #ifndef NOTATION_H_INCLUDED
+90 -94
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -24,6 +24,7 @@
#include "bitcount.h"
#include "pawns.h"
#include "position.h"
#include "thread.h"
namespace {
@@ -49,34 +50,46 @@ namespace {
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) } };
// Connected pawn bonus by file and rank (initialized by formula)
Score Connected[FILE_NB][RANK_NB];
// Connected pawn bonus by opposed, phalanx flags and rank
Score Connected[2][2][RANK_NB];
// Candidate passed pawn bonus by rank
const Score CandidatePassed[RANK_NB] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29),
S(34,68), S(83,166), S(0, 0), S( 0, 0) };
// Bonus for file distance of the two outermost pawns
const Score PawnsFileSpan = S(0, 15);
// Levers bonus by rank
const Score Lever[RANK_NB] = {
S( 0, 0), S( 0, 0), S(0, 0), S(0, 0),
S(20,20), S(40,40), S(0, 0), S(0, 0) };
// Unsupported pawn penalty
const Score UnsupportedPawnPenalty = S(20, 10);
// Weakness of our pawn shelter in front of the king indexed by [rank]
const Value ShelterWeakness[RANK_NB] =
{ V(100), V(0), V(27), V(73), V(92), V(101), V(101) };
// Weakness of our pawn shelter in front of the king by [distance from edge][rank]
const Value ShelterWeakness[][RANK_NB] = {
{ V(100), V(13), V(24), V(64), V(89), V( 93), V(104) },
{ V(110), V( 1), V(29), V(75), V(96), V(102), V(107) },
{ V(102), V( 0), V(39), V(74), V(88), V(101), V( 98) },
{ V( 88), V( 4), V(33), V(67), V(92), V( 94), V(107) } };
// Danger of enemy pawns moving toward our king indexed by
// [no friendly pawn | pawn unblocked | pawn blocked][rank of enemy pawn]
const Value StormDanger[3][RANK_NB] = {
{ V( 0), V(64), V(128), V(51), V(26) },
{ V(26), V(32), V( 96), V(38), V(20) },
{ V( 0), V( 0), V(160), V(25), V(13) } };
// Danger of enemy pawns moving toward our king by [type][distance from edge][rank]
const Value StormDanger[][4][RANK_NB] = {
{ { V( 0), V( 63), V( 128), V(43), V(27) },
{ V( 0), V( 62), V( 131), V(44), V(26) },
{ V( 0), V( 59), V( 121), V(50), V(28) },
{ V( 0), V( 62), V( 127), V(54), V(28) } },
{ { V(24), V( 40), V( 93), V(42), V(22) },
{ V(24), V( 28), V( 101), V(38), V(20) },
{ V(24), V( 32), V( 95), V(36), V(23) },
{ V(27), V( 24), V( 99), V(36), V(24) } },
{ { V( 0), V( 0), V( 81), V(16), V( 6) },
{ V( 0), V( 0), V( 165), V(29), V( 9) },
{ V( 0), V( 0), V( 163), V(23), V(12) },
{ V( 0), V( 0), V( 161), V(28), V(13) } },
{ { V( 0), V(-296), V(-299), V(55), V(25) },
{ V( 0), V( 67), V( 131), V(46), V(21) },
{ V( 0), V( 65), V( 135), V(50), V(31) },
{ V( 0), V( 62), V( 128), V(51), V(24) } } };
// Max bonus for king safety. Corresponds to start position with all the pawns
// in front of the king and no enemy pawn on the horizon.
const Value MaxSafetyBonus = V(263);
const Value MaxSafetyBonus = V(257);
#undef S
#undef V
@@ -89,17 +102,17 @@ namespace {
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
Bitboard b, p, doubled;
Bitboard b, p, doubled, connected;
Square s;
File f;
bool passed, isolated, opposed, connected, backward, candidate, unsupported;
Score value = SCORE_ZERO;
bool passed, isolated, opposed, phalanx, backward, unsupported, lever;
Score score = SCORE_ZERO;
const Square* pl = pos.list<PAWN>(Us);
const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)];
Bitboard ourPawns = pos.pieces(Us, PAWN);
Bitboard ourPawns = pos.pieces(Us , PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = e->candidatePawns[Us] = 0;
e->passedPawns[Us] = 0;
e->kingSquares[Us] = SQ_NONE;
e->semiopenFiles[Us] = 0xFF;
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns);
@@ -111,7 +124,7 @@ namespace {
{
assert(pos.piece_on(s) == make_piece(Us, PAWN));
f = file_of(s);
File f = file_of(s);
// This file cannot be semi-open
e->semiopenFiles[Us] &= ~(1 << f);
@@ -119,17 +132,16 @@ namespace {
// Previous rank
p = rank_bb(s - pawn_push(Us));
// Our rank plus previous one
b = rank_bb(s) | p;
// Flag the pawn as passed, isolated, doubled,
// unsupported or connected (but not the backward one).
connected = ourPawns & adjacent_files_bb(f) & b;
connected = ourPawns & adjacent_files_bb(f) & (rank_bb(s) | p);
phalanx = connected & rank_bb(s);
unsupported = !(ourPawns & adjacent_files_bb(f) & p);
isolated = !(ourPawns & adjacent_files_bb(f));
doubled = ourPawns & forward_bb(Us, s);
opposed = theirPawns & forward_bb(Us, s);
passed = !(theirPawns & passed_pawn_mask(Us, s));
lever = theirPawns & pawnAttacksBB[s];
// Test for backward pawn.
// If the pawn is passed, isolated, or connected it cannot be
@@ -155,14 +167,6 @@ namespace {
assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns));
// A not-passed pawn is a candidate to become passed, if it is free to
// advance and if the number of friendly pawns beside or behind this
// pawn on adjacent files is higher than or equal to the number of
// enemy pawns in the forward direction on the adjacent files.
candidate = !(opposed | passed | backward | isolated)
&& (b = pawn_attack_span(Them, s + pawn_push(Us)) & ourPawns) != 0
&& popcount<Max15>(b) >= popcount<Max15>(pawn_attack_span(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.
@@ -171,74 +175,67 @@ namespace {
// Score this pawn
if (isolated)
value -= Isolated[opposed][f];
score -= Isolated[opposed][f];
if (unsupported && !isolated)
value -= UnsupportedPawnPenalty;
score -= UnsupportedPawnPenalty;
if (doubled)
value -= Doubled[f] / rank_distance(s, lsb(doubled));
score -= Doubled[f] / distance<Rank>(s, frontmost_sq(Us, doubled));
if (backward)
value -= Backward[opposed][f];
score -= Backward[opposed][f];
if (connected)
value += Connected[f][relative_rank(Us, s)];
score += Connected[opposed][phalanx][relative_rank(Us, s)];
if (candidate)
{
value += CandidatePassed[relative_rank(Us, s)];
if (!doubled)
e->candidatePawns[Us] |= s;
}
if (lever)
score += Lever[relative_rank(Us, s)];
}
// In endgame it's better to have pawns on both wings. So give a bonus according
// to file distance between left and right outermost pawns.
if (pos.count<PAWN>(Us) > 1)
{
b = e->semiopenFiles[Us] ^ 0xFF;
value += PawnsFileSpan * int(msb(b) - lsb(b));
}
b = e->semiopenFiles[Us] ^ 0xFF;
e->pawnSpan[Us] = b ? int(msb(b) - lsb(b)) : 0;
return value;
return score;
}
} // namespace
namespace Pawns {
/// init() initializes some tables by formula instead of hard-coding their values
/// Pawns::init() initializes some tables needed by evaluation. Instead of using
/// hard-coded tables, when makes sense, we prefer to calculate them with a formula
/// to reduce independent parameters and to allow easier tuning and better insight.
void init() {
void init()
{
static const int Seed[RANK_NB] = { 0, 6, 15, 10, 57, 75, 135, 258 };
const int bonusesByFile[8] = { 1, 3, 3, 4, 4, 3, 3, 1 };
int bonus;
for (Rank r = RANK_1; r < RANK_8; ++r)
for (File f = FILE_A; f <= FILE_H; ++f)
{
bonus = r * (r-1) * (r-2) + bonusesByFile[f] * (r/2 + 1);
Connected[f][r] = make_score(bonus, bonus);
}
for (int opposed = 0; opposed <= 1; ++opposed)
for (int phalanx = 0; phalanx <= 1; ++phalanx)
for (Rank r = RANK_2; r < RANK_8; ++r)
{
int bonus = Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0);
Connected[opposed][phalanx][r] = make_score(bonus / 2, bonus >> opposed);
}
}
/// probe() takes a position object as input, computes a Entry 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.
/// Pawns::probe() looks up the current position's pawns configuration in
/// the pawns hash table. It returns a pointer to the Entry if the position
/// is found. Otherwise a new Entry is computed and stored there, so we don't
/// have to recompute all when the same pawns configuration occurs again.
Entry* probe(const Position& pos, Table& entries) {
Entry* probe(const Position& pos) {
Key key = pos.pawn_key();
Entry* e = entries[key];
Entry* e = pos.this_thread()->pawnsTable[key];
if (e->key == key)
return e;
e->key = key;
e->value = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e);
e->score = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e);
return e;
}
@@ -250,30 +247,29 @@ template<Color Us>
Value Entry::shelter_storm(const Position& pos, Square ksq) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
static const Bitboard MiddleEdges = (FileABB | FileHBB) & (Rank2BB | Rank3BB);
Value safety = MaxSafetyBonus;
enum { NoFriendlyPawn, Unblocked, BlockedByPawn, BlockedByKing };
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq));
Bitboard ourPawns = b & pos.pieces(Us);
Bitboard theirPawns = b & pos.pieces(Them);
Rank rkUs, rkThem;
File kf = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
Value safety = MaxSafetyBonus;
File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
for (File f = kf - File(1); f <= kf + File(1); ++f)
for (File f = center - File(1); f <= center + File(1); ++f)
{
b = ourPawns & file_bb(f);
rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
b = theirPawns & file_bb(f);
rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
if ( (MiddleEdges & make_square(f, rkThem))
&& file_of(ksq) == f
&& relative_rank(Us, ksq) == rkThem - 1)
safety += 200;
else
safety -= ShelterWeakness[rkUs]
+ StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
safety -= ShelterWeakness[std::min(f, FILE_H - f)][rkUs]
+ StormDanger
[f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing :
rkUs == RANK_1 ? NoFriendlyPawn :
rkThem == rkUs + 1 ? BlockedByPawn : Unblocked]
[std::min(f, FILE_H - f)][rkThem];
}
return safety;
@@ -288,14 +284,14 @@ Score Entry::do_king_safety(const Position& pos, Square ksq) {
kingSquares[Us] = ksq;
castlingRights[Us] = pos.can_castle(Us);
minKPdistance[Us] = 0;
minKingPawnDistance[Us] = 0;
Bitboard pawns = pos.pieces(Us, PAWN);
if (pawns)
while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] & pawns)) {}
while (!(DistanceRingBB[ksq][minKingPawnDistance[Us]++] & pawns)) {}
if (relative_rank(Us, ksq) > RANK_4)
return make_score(0, -16 * minKPdistance[Us]);
return make_score(0, -16 * minKingPawnDistance[Us]);
Value bonus = shelter_storm<Us>(pos, ksq);
@@ -306,7 +302,7 @@ Score Entry::do_king_safety(const Position& pos, Square ksq) {
if (pos.can_castle(MakeCastling<Us, QUEEN_SIDE>::right))
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1)));
return make_score(bonus, -16 * minKPdistance[Us]);
return make_score(bonus, -16 * minKingPawnDistance[Us]);
}
// Explicit template instantiation
+7 -7
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -32,10 +32,10 @@ namespace Pawns {
struct Entry {
Score pawns_value() const { return value; }
Score pawns_score() const { return score; }
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Bitboard candidate_pawns(Color c) const { return candidatePawns[c]; }
int pawn_span(Color c) const { return pawnSpan[c]; }
int semiopen_file(Color c, File f) const {
return semiopenFiles[c] & (1 << f);
@@ -62,22 +62,22 @@ struct Entry {
Value shelter_storm(const Position& pos, Square ksq);
Key key;
Score value;
Score score;
Bitboard passedPawns[COLOR_NB];
Bitboard candidatePawns[COLOR_NB];
Bitboard pawnAttacks[COLOR_NB];
Square kingSquares[COLOR_NB];
Score kingSafety[COLOR_NB];
int minKPdistance[COLOR_NB];
int minKingPawnDistance[COLOR_NB];
int castlingRights[COLOR_NB];
int semiopenFiles[COLOR_NB];
int pawnSpan[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
};
typedef HashTable<Entry, 16384> Table;
void init();
Entry* probe(const Position& pos, Table& entries);
Entry* probe(const Position& pos);
} // namespace Pawns
+1 -1
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
+92 -81
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -19,31 +19,25 @@
#include <algorithm>
#include <cassert>
#include <cstring>
#include <cstring> // For std::memset
#include <iomanip>
#include <sstream>
#include "bitcount.h"
#include "misc.h"
#include "movegen.h"
#include "notation.h"
#include "position.h"
#include "psqtab.h"
#include "rkiss.h"
#include "thread.h"
#include "tt.h"
#include "uci.h"
using std::string;
static const string PieceToChar(" PNBRQK pnbrqk");
CACHE_LINE_ALIGNMENT
Value PieceValue[PHASE_NB][PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
static Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
namespace Zobrist {
Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
@@ -57,6 +51,9 @@ Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion;}
namespace {
const string PieceToChar(" PNBRQK pnbrqk");
Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
// min_attacker() is a helper function used by see() to locate the least
// valuable attacker for the side to move, remove the attacker we just found
// from the bitboards and scan for new X-ray attacks behind it.
@@ -108,6 +105,30 @@ CheckInfo::CheckInfo(const Position& pos) {
}
/// operator<<(Position) returns an ASCII representation of the position
std::ostream& operator<<(std::ostream& os, const Position& pos) {
os << "\n +---+---+---+---+---+---+---+---+\n";
for (Rank r = RANK_8; r >= RANK_1; --r)
{
for (File f = FILE_A; f <= FILE_H; ++f)
os << " | " << PieceToChar[pos.piece_on(make_square(f, r))];
os << " |\n +---+---+---+---+---+---+---+---+\n";
}
os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase
<< std::setfill('0') << std::setw(16) << pos.st->key << std::dec << "\nCheckers: ";
for (Bitboard b = pos.checkers(); b; )
os << UCI::square(pop_lsb(&b)) << " ";
return os;
}
/// Position::init() initializes at startup the various arrays used to compute
/// hash keys and the piece square tables. The latter is a two-step operation:
/// Firstly, the white halves of the tables are copied from PSQT[] tables.
@@ -116,28 +137,28 @@ CheckInfo::CheckInfo(const Position& pos) {
void Position::init() {
RKISS rk;
PRNG rng(1070372);
for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= KING; ++pt)
for (Square s = SQ_A1; s <= SQ_H8; ++s)
Zobrist::psq[c][pt][s] = rk.rand<Key>();
Zobrist::psq[c][pt][s] = rng.rand<Key>();
for (File f = FILE_A; f <= FILE_H; ++f)
Zobrist::enpassant[f] = rk.rand<Key>();
Zobrist::enpassant[f] = rng.rand<Key>();
for (int cf = NO_CASTLING; cf <= ANY_CASTLING; ++cf)
for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr)
{
Bitboard b = cf;
Bitboard b = cr;
while (b)
{
Key k = Zobrist::castling[1ULL << pop_lsb(&b)];
Zobrist::castling[cf] ^= k ? k : rk.rand<Key>();
Zobrist::castling[cr] ^= k ? k : rng.rand<Key>();
}
}
Zobrist::side = rk.rand<Key>();
Zobrist::exclusion = rk.rand<Key>();
Zobrist::side = rng.rand<Key>();
Zobrist::exclusion = rng.rand<Key>();
for (PieceType pt = PAWN; pt <= KING; ++pt)
{
@@ -155,9 +176,8 @@ void Position::init() {
}
/// Position::operator=() creates a copy of 'pos'. We want the new born Position
/// object to not depend on any external data so we detach state pointer from
/// the source one.
/// Position::operator=() creates a copy of 'pos' but detaching the state pointer
/// from the source to be self-consistent and not depending on any external data.
Position& Position::operator=(const Position& pos) {
@@ -226,7 +246,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
incremented after Black's move.
*/
char col, row, token;
unsigned char col, row, token;
size_t idx;
Square sq = SQ_A8;
std::istringstream ss(fenStr);
@@ -342,7 +362,7 @@ void Position::set_castling_right(Color c, Square rfrom) {
void Position::set_state(StateInfo* si) const {
si->key = si->pawnKey = si->materialKey = 0;
si->npMaterial[WHITE] = si->npMaterial[BLACK] = VALUE_ZERO;
si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO;
si->psq = SCORE_ZERO;
si->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove);
@@ -376,7 +396,7 @@ void Position::set_state(StateInfo* si) const {
for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = KNIGHT; pt <= QUEEN; ++pt)
si->npMaterial[c] += pieceCount[c][pt] * PieceValue[MG][pt];
si->nonPawnMaterial[c] += pieceCount[c][pt] * PieceValue[MG][pt];
}
@@ -409,59 +429,37 @@ const string Position::fen() const {
ss << (sideToMove == WHITE ? " w " : " b ");
if (can_castle(WHITE_OO))
ss << (chess960 ? to_char(file_of(castling_rook_square(WHITE | KING_SIDE)), false) : 'K');
ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | KING_SIDE))) : 'K');
if (can_castle(WHITE_OOO))
ss << (chess960 ? to_char(file_of(castling_rook_square(WHITE | QUEEN_SIDE)), false) : 'Q');
ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | QUEEN_SIDE))) : 'Q');
if (can_castle(BLACK_OO))
ss << (chess960 ? to_char(file_of(castling_rook_square(BLACK | KING_SIDE)), true) : 'k');
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | KING_SIDE))) : 'k');
if (can_castle(BLACK_OOO))
ss << (chess960 ? to_char(file_of(castling_rook_square(BLACK | QUEEN_SIDE)), true) : 'q');
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q');
if (!can_castle(WHITE) && !can_castle(BLACK))
ss << '-';
ss << (ep_square() == SQ_NONE ? " - " : " " + to_string(ep_square()) + " ")
ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ")
<< st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2;
return ss.str();
}
/// Position::pretty() returns an ASCII representation of the position to be
/// printed to the standard output together with the move's san notation.
/// Position::game_phase() calculates the game phase interpolating total non-pawn
/// material between endgame and midgame limits.
const string Position::pretty(Move m) const {
Phase Position::game_phase() const {
std::ostringstream ss;
Value npm = st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK];
if (m)
ss << "\nMove: " << (sideToMove == BLACK ? ".." : "")
<< move_to_san(*const_cast<Position*>(this), m);
npm = std::max(EndgameLimit, std::min(npm, MidgameLimit));
ss << "\n +---+---+---+---+---+---+---+---+\n";
for (Rank r = RANK_8; r >= RANK_1; --r)
{
for (File f = FILE_A; f <= FILE_H; ++f)
ss << " | " << PieceToChar[piece_on(make_square(f, r))];
ss << " |\n +---+---+---+---+---+---+---+---+\n";
}
ss << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
<< std::setfill('0') << std::setw(16) << st->key << "\nCheckers: ";
for (Bitboard b = checkers(); b; )
ss << to_string(pop_lsb(&b)) << " ";
ss << "\nLegal moves: ";
for (MoveList<LEGAL> it(*this); *it; ++it)
ss << move_to_san(*const_cast<Position*>(this), *it) << " ";
return ss.str();
return Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
}
@@ -493,16 +491,16 @@ Bitboard Position::check_blockers(Color c, Color kingColor) const {
/// Position::attackers_to() computes a bitboard of all pieces which attack a
/// given square. Slider attacks use the occ bitboard to indicate occupancy.
/// given square. Slider attacks use the occupied bitboard to indicate occupancy.
Bitboard Position::attackers_to(Square s, Bitboard occ) const {
Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
return (attacks_from<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
| (attacks_from<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
| (attacks_from<KNIGHT>(s) & pieces(KNIGHT))
| (attacks_bb<ROOK>(s, occ) & pieces(ROOK, QUEEN))
| (attacks_bb<BISHOP>(s, occ) & pieces(BISHOP, QUEEN))
| (attacks_from<KING>(s) & pieces(KING));
return (attacks_from<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
| (attacks_from<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
| (attacks_from<KNIGHT>(s) & pieces(KNIGHT))
| (attacks_bb<ROOK>(s, occupied) & pieces(ROOK, QUEEN))
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
| (attacks_from<KING>(s) & pieces(KING));
}
@@ -527,15 +525,15 @@ bool Position::legal(Move m, Bitboard pinned) const {
Square ksq = king_square(us);
Square to = to_sq(m);
Square capsq = to - pawn_push(us);
Bitboard occ = (pieces() ^ from ^ capsq) | to;
Bitboard occupied = (pieces() ^ from ^ capsq) | to;
assert(to == ep_square());
assert(moved_piece(m) == make_piece(us, PAWN));
assert(piece_on(capsq) == make_piece(~us, PAWN));
assert(piece_on(to) == NO_PIECE);
return !(attacks_bb< ROOK>(ksq, occ) & pieces(~us, QUEEN, ROOK))
&& !(attacks_bb<BISHOP>(ksq, occ) & pieces(~us, QUEEN, BISHOP));
return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK))
&& !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
}
// If the moving piece is a king, check whether the destination
@@ -643,7 +641,7 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
return true;
// Is there a discovered check?
if ( unlikely(ci.dcCandidates)
if ( ci.dcCandidates
&& (ci.dcCandidates & from)
&& !aligned(from, to, ci.ksq))
return true;
@@ -768,7 +766,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
}
else
st->npMaterial[them] -= PieceValue[MG][captured];
st->nonPawnMaterial[them] -= PieceValue[MG][captured];
// Update board and piece lists
remove_piece(capsq, them, captured);
@@ -803,9 +801,6 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
st->castlingRights &= ~cr;
}
// Prefetch TT access as soon as we know the new hash key
prefetch((char*)TT.first_entry(k));
// Move the piece. The tricky Chess960 castling is handled earlier
if (type_of(m) != CASTLING)
move_piece(from, to, us, pt);
@@ -841,7 +836,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
st->psq += psq[us][promotion][to] - psq[us][PAWN][to];
// Update material
st->npMaterial[us] += PieceValue[MG][promotion];
st->nonPawnMaterial[us] += PieceValue[MG][promotion];
}
// Update pawn hash key and prefetch access to pawnsTable
@@ -875,7 +870,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
st->checkersBB |= to;
// Discovered checks
if (unlikely(ci.dcCandidates) && (ci.dcCandidates & from))
if (ci.dcCandidates && (ci.dcCandidates & from))
{
if (pt != ROOK)
st->checkersBB |= attacks_from<ROOK>(king_square(them)) & pieces(us, QUEEN, ROOK);
@@ -1012,6 +1007,26 @@ void Position::undo_null_move() {
}
/// Position::key_after() computes the new hash key after the given move. Needed
/// for speculative prefetch. It doesn't recognize special moves like castling,
/// en-passant and promotions.
Key Position::key_after(Move m) const {
Color us = sideToMove;
Square from = from_sq(m);
Square to = to_sq(m);
PieceType pt = type_of(piece_on(from));
PieceType captured = type_of(piece_on(to));
Key k = st->key ^ Zobrist::side;
if (captured)
k ^= Zobrist::psq[~us][captured][to];
return k ^ Zobrist::psq[us][pt][to] ^ Zobrist::psq[us][pt][from];
}
/// Position::see() is a static exchange evaluator: It tries to estimate the
/// material gain or loss resulting from a move.
@@ -1113,10 +1128,6 @@ Value Position::see(Move m) const {
bool Position::is_draw() const {
if ( !pieces(PAWN)
&& (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMg))
return true;
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
return true;
@@ -1222,8 +1233,8 @@ bool Position::pos_is_ok(int* step) const {
if ( st->key != si.key
|| st->pawnKey != si.pawnKey
|| st->materialKey != si.materialKey
|| st->npMaterial[WHITE] != si.npMaterial[WHITE]
|| st->npMaterial[BLACK] != si.npMaterial[BLACK]
|| st->nonPawnMaterial[WHITE] != si.nonPawnMaterial[WHITE]
|| st->nonPawnMaterial[BLACK] != si.nonPawnMaterial[BLACK]
|| st->psq != si.psq
|| st->checkersBB != si.checkersBB)
return false;
+75 -68
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -21,17 +21,18 @@
#define POSITION_H_INCLUDED
#include <cassert>
#include <cstddef>
#include <cstddef> // For offsetof()
#include <string>
#include "bitboard.h"
#include "types.h"
/// The checkInfo struct is initialized at c'tor time and keeps info used
/// to detect if a move gives check.
class Position;
struct Thread;
/// CheckInfo struct is initialized at c'tor time and keeps info used to detect
/// if a move gives check.
struct CheckInfo {
explicit CheckInfo(const Position&);
@@ -39,51 +40,61 @@ struct CheckInfo {
Bitboard dcCandidates;
Bitboard pinned;
Bitboard checkSq[PIECE_TYPE_NB];
Square ksq;
Square ksq;
};
/// The StateInfo struct stores information needed to restore a Position
/// object to its previous state when we retract a move. Whenever a move
/// is made on the board (by calling Position::do_move), a StateInfo
/// object must be passed as a parameter.
/// StateInfo struct stores information needed to restore a Position object to
/// its previous state when we retract a move. Whenever a move is made on the
/// board (by calling Position::do_move), a StateInfo object must be passed.
struct StateInfo {
Key pawnKey, materialKey;
Value npMaterial[COLOR_NB];
int castlingRights, rule50, pliesFromNull;
Score psq;
// Copied when making a move
Key pawnKey;
Key materialKey;
Value nonPawnMaterial[COLOR_NB];
int castlingRights;
int rule50;
int pliesFromNull;
Score psq;
Square epSquare;
Key key;
Bitboard checkersBB;
PieceType capturedType;
// Not copied when making a move
Key key;
Bitboard checkersBB;
PieceType capturedType;
StateInfo* previous;
};
/// When making a move the current StateInfo up to 'key' excluded is copied to
/// the new one. Here we calculate the quad words (64bits) needed to be copied.
/// the new one. Here we calculate the quad words (64 bit) needed to be copied.
const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1;
/// The Position class stores the information regarding the board representation
/// like pieces, side to move, hash keys, castling info, etc. The most important
/// methods are do_move() and undo_move(), used by the search to update node info
/// when traversing the search tree.
/// Position class stores information regarding the board representation as
/// pieces, side to move, hash keys, castling info, etc. Important methods are
/// do_move() and undo_move(), used by the search to update node info when
/// traversing the search tree.
class Position {
friend std::ostream& operator<<(std::ostream&, const Position&);
Position(const Position&); // Disable the default copy constructor
public:
Position() {}
Position(const Position& pos, Thread* t) { *this = pos; thisThread = t; }
Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); }
Position& operator=(const Position&);
static void init();
// Text input/output
Position() {} // To define the global object RootPos
Position(const Position& pos, Thread* th) { *this = pos; thisThread = th; }
Position(const std::string& f, bool c960, Thread* th) { set(f, c960, th); }
Position& operator=(const Position&); // To assign RootPos from UCI
// FEN string input/output
void set(const std::string& fenStr, bool isChess960, Thread* th);
const std::string fen() const;
const std::string pretty(Move m = MOVE_NONE) const;
// Position representation
Bitboard pieces() const;
@@ -112,7 +123,7 @@ public:
// Attacks to/from a given square
Bitboard attackers_to(Square s) const;
Bitboard attackers_to(Square s, Bitboard occ) const;
Bitboard attackers_to(Square s, Bitboard occupied) const;
Bitboard attacks_from(Piece pc, Square s) const;
template<PieceType> Bitboard attacks_from(Square s) const;
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
@@ -130,7 +141,6 @@ public:
// Piece specific
bool pawn_passed(Color c, Square s) const;
bool pawn_on_7th(Color c) const;
bool bishop_pair(Color c) const;
bool opposite_bishops() const;
// Doing and undoing moves
@@ -146,22 +156,23 @@ public:
// Accessing hash keys
Key key() const;
Key key_after(Move m) const;
Key exclusion_key() const;
Key pawn_key() const;
Key material_key() const;
// Incremental piece-square evaluation
Score psq_score() const;
Value non_pawn_material(Color c) const;
Key pawn_key() const;
// Other properties of the position
Color side_to_move() const;
Phase game_phase() const;
int game_ply() const;
bool is_chess960() const;
Thread* this_thread() const;
uint64_t nodes_searched() const;
void set_nodes_searched(uint64_t n);
bool is_draw() const;
int rule50_count() const;
Score psq_score() const;
Value non_pawn_material(Color c) const;
// Position consistency check, for debugging
bool pos_is_ok(int* step = NULL) const;
@@ -173,7 +184,7 @@ private:
void set_castling_right(Color c, Square rfrom);
void set_state(StateInfo* si) const;
// Helper functions
// Other helpers
Bitboard check_blockers(Color c, Color kingColor) const;
void put_piece(Square s, Color c, PieceType pt);
void remove_piece(Square s, Color c, PieceType pt);
@@ -181,15 +192,13 @@ private:
template<bool Do>
void do_castling(Square from, Square& to, Square& rfrom, Square& rto);
// Board and pieces
// Data members
Piece board[SQUARE_NB];
Bitboard byTypeBB[PIECE_TYPE_NB];
Bitboard byColorBB[COLOR_NB];
int pieceCount[COLOR_NB][PIECE_TYPE_NB];
Square pieceList[COLOR_NB][PIECE_TYPE_NB][16];
int index[SQUARE_NB];
// Other info
int castlingRightsMask[SQUARE_NB];
Square castlingRookSquare[CASTLING_RIGHT_NB];
Bitboard castlingPath[CASTLING_RIGHT_NB];
@@ -202,12 +211,12 @@ private:
bool chess960;
};
inline uint64_t Position::nodes_searched() const {
return nodes;
inline Color Position::side_to_move() const {
return sideToMove;
}
inline void Position::set_nodes_searched(uint64_t n) {
nodes = n;
inline bool Position::empty(Square s) const {
return board[s] == NO_PIECE;
}
inline Piece Position::piece_on(Square s) const {
@@ -218,14 +227,6 @@ inline Piece Position::moved_piece(Move m) const {
return board[from_sq(m)];
}
inline bool Position::empty(Square s) const {
return board[s] == NO_PIECE;
}
inline Color Position::side_to_move() const {
return sideToMove;
}
inline Bitboard Position::pieces() const {
return byTypeBB[ALL_PIECES];
}
@@ -258,14 +259,14 @@ template<PieceType Pt> inline const Square* Position::list(Color c) const {
return pieceList[c][Pt];
}
inline Square Position::ep_square() const {
return st->epSquare;
}
inline Square Position::king_square(Color c) const {
return pieceList[c][KING][0];
}
inline Square Position::ep_square() const {
return st->epSquare;
}
inline int Position::can_castle(CastlingRight cr) const {
return st->castlingRights & cr;
}
@@ -284,7 +285,6 @@ inline Square Position::castling_rook_square(CastlingRight cr) const {
template<PieceType Pt>
inline Bitboard Position::attacks_from(Square s) const {
return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES])
: Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
: StepAttacksBB[Pt][s];
@@ -341,26 +341,31 @@ inline Score Position::psq_score() const {
}
inline Value Position::non_pawn_material(Color c) const {
return st->npMaterial[c];
return st->nonPawnMaterial[c];
}
inline int Position::game_ply() const {
return gamePly;
}
inline bool Position::opposite_bishops() const {
inline int Position::rule50_count() const {
return st->rule50;
}
inline uint64_t Position::nodes_searched() const {
return nodes;
}
inline void Position::set_nodes_searched(uint64_t n) {
nodes = n;
}
inline bool Position::opposite_bishops() const {
return pieceCount[WHITE][BISHOP] == 1
&& pieceCount[BLACK][BISHOP] == 1
&& opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]);
}
inline bool Position::bishop_pair(Color c) const {
return pieceCount[c][BISHOP] >= 2
&& opposite_colors(pieceList[c][BISHOP][0], pieceList[c][BISHOP][1]);
}
inline bool Position::pawn_on_7th(Color c) const {
return pieces(c, PAWN) & rank_bb(relative_rank(c, RANK_7));
}
@@ -377,7 +382,7 @@ inline bool Position::capture_or_promotion(Move m) const {
inline bool Position::capture(Move m) const {
// Note that castling is encoded as "king captures the rook"
// Castling is encoded as "king captures the rook"
assert(is_ok(m));
return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT;
}
@@ -398,12 +403,13 @@ inline void Position::put_piece(Square s, Color c, PieceType pt) {
byColorBB[c] |= s;
index[s] = pieceCount[c][pt]++;
pieceList[c][pt][index[s]] = s;
pieceCount[c][ALL_PIECES]++;
}
inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) {
// index[from] is not updated and becomes stale. This works as long
// as index[] is accessed just by known occupied squares.
// index[from] is not updated and becomes stale. This works as long as index[]
// is accessed just by known occupied squares.
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
@@ -423,11 +429,12 @@ inline void Position::remove_piece(Square s, Color c, PieceType pt) {
byTypeBB[ALL_PIECES] ^= s;
byTypeBB[pt] ^= s;
byColorBB[c] ^= s;
/* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing
/* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */
Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]];
index[lastSquare] = index[s];
pieceList[c][pt][index[lastSquare]] = lastSquare;
pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE;
pieceCount[c][ALL_PIECES]--;
}
#endif // #ifndef POSITION_H_INCLUDED
+1 -1
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
-81
View File
@@ -1,81 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 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.
*/
#ifndef 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 {
uint64_t a, b, c, d;
uint64_t rotate_L(uint64_t x, unsigned k) const {
return (x << k) | (x >> (64 - k));
}
uint64_t rand64() {
const uint64_t e = a - rotate_L(b, 7);
a = b ^ rotate_L(c, 13);
b = c + rotate_L(d, 37);
c = d + e;
return d = e + a;
}
public:
RKISS(int seed = 73) {
a = 0xF1EA5EED, b = c = d = 0xD4E12C77;
for (int i = 0; i < seed; ++i) // Scramble a few rounds
rand64();
}
template<typename T> T rand() { return T(rand64()); }
/// Special generator used to fast init magic numbers. Here the
/// trick is to rotate the randoms of a given quantity 's' known
/// to be optimal to quickly find a good magic candidate.
template<typename T> T magic_rand(int s) {
return rotate_L(rotate_L(rand<T>(), (s >> 0) & 0x3F) & rand<T>()
, (s >> 6) & 0x3F) & rand<T>();
}
};
#endif // #ifndef RKISS_H_INCLUDED
+451 -410
View File
File diff suppressed because it is too large Load Diff
+30 -30
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -20,7 +20,7 @@
#ifndef SEARCH_H_INCLUDED
#define SEARCH_H_INCLUDED
#include <memory>
#include <memory> // For std::auto_ptr
#include <stack>
#include <vector>
@@ -32,12 +32,13 @@ struct SplitPoint;
namespace Search {
/// The Stack struct keeps track of the information we need to remember from
/// nodes shallower and deeper in the tree during the search. Each search thread
/// has its own array of Stack objects, indexed by the current ply.
/// Stack struct keeps track of the information we need to remember from nodes
/// shallower and deeper in the tree during the search. Each search thread has
/// its own array of Stack objects, indexed by the current ply.
struct Stack {
SplitPoint* splitPoint;
Move* pv;
int ply;
Move currentMove;
Move ttMove;
@@ -45,49 +46,49 @@ struct Stack {
Move killers[2];
Depth reduction;
Value staticEval;
int skipNullMove;
bool skipEarlyPruning;
};
/// RootMove struct is used for moves at the root of the tree. For each root move
/// we store a score and a PV (really a refutation in the case of moves which
/// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves.
/// 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
/// case of moves which fail low). Score is normally set at -VALUE_INFINITE for
/// all non-pv moves.
struct RootMove {
RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) {
pv.push_back(m); pv.push_back(MOVE_NONE);
}
RootMove(Move m) : score(-VALUE_INFINITE), previousScore(-VALUE_INFINITE), pv(1, m) {}
bool operator<(const RootMove& m) const { return score > m.score; } // Ascending sort
bool operator==(const Move& m) const { return pv[0] == m; }
void extract_pv_from_tt(Position& pos);
void insert_pv_in_tt(Position& pos);
Move extract_ponder_from_tt(Position& pos);
Value score;
Value prevScore;
Value previousScore;
std::vector<Move> pv;
};
typedef std::vector<RootMove> RootMoveVector;
/// 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 it's our opponent's turn to move.
/// LimitsType struct stores information sent by GUI about available time to
/// search the current move, maximum depth/time, if we are in analysis mode or
/// if we have to ponder while it's our opponent's turn to move.
struct LimitsType {
LimitsType() { // Using memset on a std::vector is undefined behavior
time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = movestogo =
depth = nodes = movetime = mate = infinite = ponder = 0;
LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC
nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = movestogo =
depth = movetime = mate = infinite = ponder = 0;
}
bool use_time_management() const {
return !(mate | movetime | depth | nodes | infinite);
}
bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); }
std::vector<Move> searchmoves;
int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder;
int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, movetime, mate, infinite, ponder;
int64_t nodes;
};
/// The SignalsType struct stores volatile flags updated during the search
/// typically in an async fashion e.g. to stop the search by the GUI.
@@ -99,15 +100,14 @@ typedef std::auto_ptr<std::stack<StateInfo> > StateStackPtr;
extern volatile SignalsType Signals;
extern LimitsType Limits;
extern std::vector<RootMove> RootMoves;
extern RootMoveVector RootMoves;
extern Position RootPos;
extern Color RootColor;
extern Time::point SearchTime;
extern StateStackPtr SetupStates;
extern void init();
extern uint64_t perft(Position& pos, Depth depth);
extern void think();
void init();
void think();
template<bool Root> uint64_t perft(Position& pos, Depth depth);
} // namespace Search
File diff suppressed because it is too large Load Diff
+169
View File
@@ -0,0 +1,169 @@
/*
Copyright (c) 2011-2013 Ronald de Man
*/
#ifndef TBCORE_H
#define TBCORE_H
#ifndef _WIN32
#include <pthread.h>
#define SEP_CHAR ':'
#define FD int
#define FD_ERR -1
#else
#include <windows.h>
#define SEP_CHAR ';'
#define FD HANDLE
#define FD_ERR INVALID_HANDLE_VALUE
#endif
#ifndef _WIN32
#define LOCK_T pthread_mutex_t
#define LOCK_INIT(x) pthread_mutex_init(&(x), NULL)
#define LOCK(x) pthread_mutex_lock(&(x))
#define UNLOCK(x) pthread_mutex_unlock(&(x))
#else
#define LOCK_T HANDLE
#define LOCK_INIT(x) do { x = CreateMutex(NULL, FALSE, NULL); } while (0)
#define LOCK(x) WaitForSingleObject(x, INFINITE)
#define UNLOCK(x) ReleaseMutex(x)
#endif
#ifndef _MSC_VER
#define BSWAP32(v) __builtin_bswap32(v)
#define BSWAP64(v) __builtin_bswap64(v)
#else
#define BSWAP32(v) _byteswap_ulong(v)
#define BSWAP64(v) _byteswap_uint64(v)
#endif
#define WDLSUFFIX ".rtbw"
#define DTZSUFFIX ".rtbz"
#define WDLDIR "RTBWDIR"
#define DTZDIR "RTBZDIR"
#define TBPIECES 6
typedef unsigned long long uint64;
typedef unsigned int uint32;
typedef unsigned char ubyte;
typedef unsigned short ushort;
const ubyte WDL_MAGIC[4] = { 0x71, 0xe8, 0x23, 0x5d };
const ubyte DTZ_MAGIC[4] = { 0xd7, 0x66, 0x0c, 0xa5 };
#define TBHASHBITS 10
struct TBHashEntry;
typedef uint64 base_t;
struct PairsData {
char *indextable;
ushort *sizetable;
ubyte *data;
ushort *offset;
ubyte *symlen;
ubyte *sympat;
int blocksize;
int idxbits;
int min_len;
base_t base[1]; // C++ complains about base[]...
};
struct TBEntry {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
}
#ifndef _WIN32
__attribute__((__may_alias__))
#endif
;
struct TBEntry_piece {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte enc_type;
struct PairsData *precomp[2];
int factor[2][TBPIECES];
ubyte pieces[2][TBPIECES];
ubyte norm[2][TBPIECES];
};
struct TBEntry_pawn {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte pawns[2];
struct {
struct PairsData *precomp[2];
int factor[2][TBPIECES];
ubyte pieces[2][TBPIECES];
ubyte norm[2][TBPIECES];
} file[4];
};
struct DTZEntry_piece {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte enc_type;
struct PairsData *precomp;
int factor[TBPIECES];
ubyte pieces[TBPIECES];
ubyte norm[TBPIECES];
ubyte flags; // accurate, mapped, side
ushort map_idx[4];
ubyte *map;
};
struct DTZEntry_pawn {
char *data;
uint64 key;
uint64 mapping;
ubyte ready;
ubyte num;
ubyte symmetric;
ubyte has_pawns;
ubyte pawns[2];
struct {
struct PairsData *precomp;
int factor[TBPIECES];
ubyte pieces[TBPIECES];
ubyte norm[TBPIECES];
} file[4];
ubyte flags[4];
ushort map_idx[4][4];
ubyte *map;
};
struct TBHashEntry {
uint64 key;
struct TBEntry *ptr;
};
struct DTZTableEntry {
uint64 key1;
uint64 key2;
struct TBEntry *entry;
};
#endif
+831
View File
@@ -0,0 +1,831 @@
/*
Copyright (c) 2013 Ronald de Man
This file may be redistributed and/or modified without restrictions.
tbprobe.cpp contains the Stockfish-specific routines of the
tablebase probing code. It should be relatively easy to adapt
this code to other chess engines.
*/
#include <algorithm>
#include "../position.h"
#include "../movegen.h"
#include "../bitboard.h"
#include "../search.h"
#include "../bitcount.h"
#include "tbprobe.h"
#include "tbcore.h"
#include "tbcore.cpp"
namespace Zobrist {
extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
}
int Tablebases::MaxCardinality = 0;
// Given a position with 6 or fewer pieces, produce a text string
// of the form KQPvKRP, where "KQP" represents the white pieces if
// mirror == 0 and the black pieces if mirror == 1.
static void prt_str(Position& pos, char *str, int mirror)
{
Color color;
PieceType pt;
int i;
color = !mirror ? WHITE : BLACK;
for (pt = KING; pt >= PAWN; --pt)
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
*str++ = pchr[6 - pt];
*str++ = 'v';
color = ~color;
for (pt = KING; pt >= PAWN; --pt)
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
*str++ = pchr[6 - pt];
*str++ = 0;
}
// Given a position, produce a 64-bit material signature key.
// If the engine supports such a key, it should equal the engine's key.
static uint64 calc_key(Position& pos, int mirror)
{
Color color;
PieceType pt;
int i;
uint64 key = 0;
color = !mirror ? WHITE : BLACK;
for (pt = PAWN; pt <= KING; ++pt)
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
key ^= Zobrist::psq[WHITE][pt][i - 1];
color = ~color;
for (pt = PAWN; pt <= KING; ++pt)
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
key ^= Zobrist::psq[BLACK][pt][i - 1];
return key;
}
// Produce a 64-bit material key corresponding to the material combination
// defined by pcs[16], where pcs[1], ..., pcs[6] is the number of white
// pawns, ..., kings and pcs[9], ..., pcs[14] is the number of black
// pawns, ..., kings.
static uint64 calc_key_from_pcs(int *pcs, int mirror)
{
int color;
PieceType pt;
int i;
uint64 key = 0;
color = !mirror ? 0 : 8;
for (pt = PAWN; pt <= KING; ++pt)
for (i = 0; i < pcs[color + pt]; i++)
key ^= Zobrist::psq[WHITE][pt][i];
color ^= 8;
for (pt = PAWN; pt <= KING; ++pt)
for (i = 0; i < pcs[color + pt]; i++)
key ^= Zobrist::psq[BLACK][pt][i];
return key;
}
bool is_little_endian() {
union {
int i;
char c[sizeof(int)];
} x;
x.i = 1;
return x.c[0] == 1;
}
static ubyte decompress_pairs(struct PairsData *d, uint64 idx)
{
static const bool isLittleEndian = is_little_endian();
return isLittleEndian ? decompress_pairs<true >(d, idx)
: decompress_pairs<false>(d, idx);
}
// probe_wdl_table and probe_dtz_table require similar adaptations.
static int probe_wdl_table(Position& pos, int *success)
{
struct TBEntry *ptr;
struct TBHashEntry *ptr2;
uint64 idx;
uint64 key;
int i;
ubyte res;
int p[TBPIECES];
// Obtain the position's material signature key.
key = pos.material_key();
// Test for KvK.
if (key == (Zobrist::psq[WHITE][KING][0] ^ Zobrist::psq[BLACK][KING][0]))
return 0;
ptr2 = TB_hash[key >> (64 - TBHASHBITS)];
for (i = 0; i < HSHMAX; i++)
if (ptr2[i].key == key) break;
if (i == HSHMAX) {
*success = 0;
return 0;
}
ptr = ptr2[i].ptr;
if (!ptr->ready) {
LOCK(TB_mutex);
if (!ptr->ready) {
char str[16];
prt_str(pos, str, ptr->key != key);
if (!init_table_wdl(ptr, str)) {
ptr2[i].key = 0ULL;
*success = 0;
UNLOCK(TB_mutex);
return 0;
}
// Memory barrier to ensure ptr->ready = 1 is not reordered.
#ifdef _MSC_VER
_ReadWriteBarrier();
#else
__asm__ __volatile__ ("" ::: "memory");
#endif
ptr->ready = 1;
}
UNLOCK(TB_mutex);
}
int bside, mirror, cmirror;
if (!ptr->symmetric) {
if (key != ptr->key) {
cmirror = 8;
mirror = 0x38;
bside = (pos.side_to_move() == WHITE);
} else {
cmirror = mirror = 0;
bside = !(pos.side_to_move() == WHITE);
}
} else {
cmirror = pos.side_to_move() == WHITE ? 0 : 8;
mirror = pos.side_to_move() == WHITE ? 0 : 0x38;
bside = 0;
}
// p[i] is to contain the square 0-63 (A1-H8) for a piece of type
// pc[i] ^ cmirror, where 1 = white pawn, ..., 14 = black king.
// Pieces of the same type are guaranteed to be consecutive.
if (!ptr->has_pawns) {
struct TBEntry_piece *entry = (struct TBEntry_piece *)ptr;
ubyte *pc = entry->pieces[bside];
for (i = 0; i < entry->num;) {
Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07));
do {
p[i++] = pop_lsb(&bb);
} while (bb);
}
idx = encode_piece(entry, entry->norm[bside], p, entry->factor[bside]);
res = decompress_pairs(entry->precomp[bside], idx);
} else {
struct TBEntry_pawn *entry = (struct TBEntry_pawn *)ptr;
int k = entry->file[0].pieces[0][0] ^ cmirror;
Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07));
i = 0;
do {
p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb);
int f = pawn_file(entry, p);
ubyte *pc = entry->file[f].pieces[bside];
for (; i < entry->num;) {
bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07));
do {
p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb);
}
idx = encode_pawn(entry, entry->file[f].norm[bside], p, entry->file[f].factor[bside]);
res = decompress_pairs(entry->file[f].precomp[bside], idx);
}
return ((int)res) - 2;
}
static int probe_dtz_table(Position& pos, int wdl, int *success)
{
struct TBEntry *ptr;
uint64 idx;
int i, res;
int p[TBPIECES];
// Obtain the position's material signature key.
uint64 key = pos.material_key();
if (DTZ_table[0].key1 != key && DTZ_table[0].key2 != key) {
for (i = 1; i < DTZ_ENTRIES; i++)
if (DTZ_table[i].key1 == key) break;
if (i < DTZ_ENTRIES) {
struct DTZTableEntry table_entry = DTZ_table[i];
for (; i > 0; i--)
DTZ_table[i] = DTZ_table[i - 1];
DTZ_table[0] = table_entry;
} else {
struct TBHashEntry *ptr2 = TB_hash[key >> (64 - TBHASHBITS)];
for (i = 0; i < HSHMAX; i++)
if (ptr2[i].key == key) break;
if (i == HSHMAX) {
*success = 0;
return 0;
}
ptr = ptr2[i].ptr;
char str[16];
int mirror = (ptr->key != key);
prt_str(pos, str, mirror);
if (DTZ_table[DTZ_ENTRIES - 1].entry)
free_dtz_entry(DTZ_table[DTZ_ENTRIES-1].entry);
for (i = DTZ_ENTRIES - 1; i > 0; i--)
DTZ_table[i] = DTZ_table[i - 1];
load_dtz_table(str, calc_key(pos, mirror), calc_key(pos, !mirror));
}
}
ptr = DTZ_table[0].entry;
if (!ptr) {
*success = 0;
return 0;
}
int bside, mirror, cmirror;
if (!ptr->symmetric) {
if (key != ptr->key) {
cmirror = 8;
mirror = 0x38;
bside = (pos.side_to_move() == WHITE);
} else {
cmirror = mirror = 0;
bside = !(pos.side_to_move() == WHITE);
}
} else {
cmirror = pos.side_to_move() == WHITE ? 0 : 8;
mirror = pos.side_to_move() == WHITE ? 0 : 0x38;
bside = 0;
}
if (!ptr->has_pawns) {
struct DTZEntry_piece *entry = (struct DTZEntry_piece *)ptr;
if ((entry->flags & 1) != bside && !entry->symmetric) {
*success = -1;
return 0;
}
ubyte *pc = entry->pieces;
for (i = 0; i < entry->num;) {
Bitboard bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07));
do {
p[i++] = pop_lsb(&bb);
} while (bb);
}
idx = encode_piece((struct TBEntry_piece *)entry, entry->norm, p, entry->factor);
res = decompress_pairs(entry->precomp, idx);
if (entry->flags & 2)
res = entry->map[entry->map_idx[wdl_to_map[wdl + 2]] + res];
if (!(entry->flags & pa_flags[wdl + 2]) || (wdl & 1))
res *= 2;
} else {
struct DTZEntry_pawn *entry = (struct DTZEntry_pawn *)ptr;
int k = entry->file[0].pieces[0] ^ cmirror;
Bitboard bb = pos.pieces((Color)(k >> 3), (PieceType)(k & 0x07));
i = 0;
do {
p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb);
int f = pawn_file((struct TBEntry_pawn *)entry, p);
if ((entry->flags[f] & 1) != bside) {
*success = -1;
return 0;
}
ubyte *pc = entry->file[f].pieces;
for (; i < entry->num;) {
bb = pos.pieces((Color)((pc[i] ^ cmirror) >> 3),
(PieceType)(pc[i] & 0x07));
do {
p[i++] = pop_lsb(&bb) ^ mirror;
} while (bb);
}
idx = encode_pawn((struct TBEntry_pawn *)entry, entry->file[f].norm, p, entry->file[f].factor);
res = decompress_pairs(entry->file[f].precomp, idx);
if (entry->flags[f] & 2)
res = entry->map[entry->map_idx[f][wdl_to_map[wdl + 2]] + res];
if (!(entry->flags[f] & pa_flags[wdl + 2]) || (wdl & 1))
res *= 2;
}
return res;
}
// Add underpromotion captures to list of captures.
static ExtMove *add_underprom_caps(Position& pos, ExtMove *stack, ExtMove *end)
{
ExtMove *moves, *extra = end;
for (moves = stack; moves < end; moves++) {
Move move = moves->move;
if (type_of(move) == PROMOTION && !pos.empty(to_sq(move))) {
(*extra++).move = (Move)(move - (1 << 12));
(*extra++).move = (Move)(move - (2 << 12));
(*extra++).move = (Move)(move - (3 << 12));
}
}
return extra;
}
static int probe_ab(Position& pos, int alpha, int beta, int *success)
{
int v;
ExtMove stack[64];
ExtMove *moves, *end;
StateInfo st;
// Generate (at least) all legal non-ep captures including (under)promotions.
// It is OK to generate more, as long as they are filtered out below.
if (!pos.checkers()) {
end = generate<CAPTURES>(pos, stack);
// Since underpromotion captures are not included, we need to add them.
end = add_underprom_caps(pos, stack, end);
} else
end = generate<EVASIONS>(pos, stack);
CheckInfo ci(pos);
for (moves = stack; moves < end; moves++) {
Move capture = moves->move;
if (!pos.capture(capture) || type_of(capture) == ENPASSANT
|| !pos.legal(capture, ci.pinned))
continue;
pos.do_move(capture, st, ci, pos.gives_check(capture, ci));
v = -probe_ab(pos, -beta, -alpha, success);
pos.undo_move(capture);
if (*success == 0) return 0;
if (v > alpha) {
if (v >= beta) {
*success = 2;
return v;
}
alpha = v;
}
}
v = probe_wdl_table(pos, success);
if (*success == 0) return 0;
if (alpha >= v) {
*success = 1 + (alpha > 0);
return alpha;
} else {
*success = 1;
return v;
}
}
// Probe the WDL table for a particular position.
// If *success != 0, the probe was successful.
// The return value is from the point of view of the side to move:
// -2 : loss
// -1 : loss, but draw under 50-move rule
// 0 : draw
// 1 : win, but draw under 50-move rule
// 2 : win
int Tablebases::probe_wdl(Position& pos, int *success)
{
int v;
*success = 1;
v = probe_ab(pos, -2, 2, success);
// If en passant is not possible, we are done.
if (pos.ep_square() == SQ_NONE)
return v;
if (!(*success)) return 0;
// Now handle en passant.
int v1 = -3;
// Generate (at least) all legal en passant captures.
ExtMove stack[192];
ExtMove *moves, *end;
StateInfo st;
if (!pos.checkers())
end = generate<CAPTURES>(pos, stack);
else
end = generate<EVASIONS>(pos, stack);
CheckInfo ci(pos);
for (moves = stack; moves < end; moves++) {
Move capture = moves->move;
if (type_of(capture) != ENPASSANT
|| !pos.legal(capture, ci.pinned))
continue;
pos.do_move(capture, st, ci, pos.gives_check(capture, ci));
int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture);
if (*success == 0) return 0;
if (v0 > v1) v1 = v0;
}
if (v1 > -3) {
if (v1 >= v) v = v1;
else if (v == 0) {
// Check whether there is at least one legal non-ep move.
for (moves = stack; moves < end; moves++) {
Move capture = moves->move;
if (type_of(capture) == ENPASSANT) continue;
if (pos.legal(capture, ci.pinned)) break;
}
if (moves == end && !pos.checkers()) {
end = generate<QUIETS>(pos, end);
for (; moves < end; moves++) {
Move move = moves->move;
if (pos.legal(move, ci.pinned))
break;
}
}
// If not, then we are forced to play the losing ep capture.
if (moves == end)
v = v1;
}
}
return v;
}
// This routine treats a position with en passant captures as one without.
static int probe_dtz_no_ep(Position& pos, int *success)
{
int wdl, dtz;
wdl = probe_ab(pos, -2, 2, success);
if (*success == 0) return 0;
if (wdl == 0) return 0;
if (*success == 2)
return wdl == 2 ? 1 : 101;
ExtMove stack[192];
ExtMove *moves, *end = NULL;
StateInfo st;
CheckInfo ci(pos);
if (wdl > 0) {
// Generate at least all legal non-capturing pawn moves
// including non-capturing promotions.
if (!pos.checkers())
end = generate<NON_EVASIONS>(pos, stack);
else
end = generate<EVASIONS>(pos, stack);
for (moves = stack; moves < end; moves++) {
Move move = moves->move;
if (type_of(pos.moved_piece(move)) != PAWN || pos.capture(move)
|| !pos.legal(move, ci.pinned))
continue;
pos.do_move(move, st, ci, pos.gives_check(move, ci));
int v = -probe_ab(pos, -2, -wdl + 1, success);
pos.undo_move(move);
if (*success == 0) return 0;
if (v == wdl)
return v == 2 ? 1 : 101;
}
}
dtz = 1 + probe_dtz_table(pos, wdl, success);
if (*success >= 0) {
if (wdl & 1) dtz += 100;
return wdl >= 0 ? dtz : -dtz;
}
if (wdl > 0) {
int best = 0xffff;
for (moves = stack; moves < end; moves++) {
Move move = moves->move;
if (pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN
|| !pos.legal(move, ci.pinned))
continue;
pos.do_move(move, st, ci, pos.gives_check(move, ci));
int v = -Tablebases::probe_dtz(pos, success);
pos.undo_move(move);
if (*success == 0) return 0;
if (v > 0 && v + 1 < best)
best = v + 1;
}
return best;
} else {
int best = -1;
if (!pos.checkers())
end = generate<NON_EVASIONS>(pos, stack);
else
end = generate<EVASIONS>(pos, stack);
for (moves = stack; moves < end; moves++) {
int v;
Move move = moves->move;
if (!pos.legal(move, ci.pinned))
continue;
pos.do_move(move, st, ci, pos.gives_check(move, ci));
if (st.rule50 == 0) {
if (wdl == -2) v = -1;
else {
v = probe_ab(pos, 1, 2, success);
v = (v == 2) ? 0 : -101;
}
} else {
v = -Tablebases::probe_dtz(pos, success) - 1;
}
pos.undo_move(move);
if (*success == 0) return 0;
if (v < best)
best = v;
}
return best;
}
}
static int wdl_to_dtz[] = {
-1, -101, 0, 101, 1
};
// Probe the DTZ table for a particular position.
// If *success != 0, the probe was successful.
// The return value is from the point of view of the side to move:
// n < -100 : loss, but draw under 50-move rule
// -100 <= n < -1 : loss in n ply (assuming 50-move counter == 0)
// 0 : draw
// 1 < n <= 100 : win in n ply (assuming 50-move counter == 0)
// 100 < n : win, but draw under 50-move rule
//
// The return value n can be off by 1: a return value -n can mean a loss
// in n+1 ply and a return value +n can mean a win in n+1 ply. This
// cannot happen for tables with positions exactly on the "edge" of
// the 50-move rule.
//
// This implies that if dtz > 0 is returned, the position is certainly
// a win if dtz + 50-move-counter <= 99. Care must be taken that the engine
// picks moves that preserve dtz + 50-move-counter <= 99.
//
// If n = 100 immediately after a capture or pawn move, then the position
// is also certainly a win, and during the whole phase until the next
// capture or pawn move, the inequality to be preserved is
// dtz + 50-movecounter <= 100.
//
// In short, if a move is available resulting in dtz + 50-move-counter <= 99,
// then do not accept moves leading to dtz + 50-move-counter == 100.
//
int Tablebases::probe_dtz(Position& pos, int *success)
{
*success = 1;
int v = probe_dtz_no_ep(pos, success);
if (pos.ep_square() == SQ_NONE)
return v;
if (*success == 0) return 0;
// Now handle en passant.
int v1 = -3;
ExtMove stack[192];
ExtMove *moves, *end;
StateInfo st;
if (!pos.checkers())
end = generate<CAPTURES>(pos, stack);
else
end = generate<EVASIONS>(pos, stack);
CheckInfo ci(pos);
for (moves = stack; moves < end; moves++) {
Move capture = moves->move;
if (type_of(capture) != ENPASSANT
|| !pos.legal(capture, ci.pinned))
continue;
pos.do_move(capture, st, ci, pos.gives_check(capture, ci));
int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture);
if (*success == 0) return 0;
if (v0 > v1) v1 = v0;
}
if (v1 > -3) {
v1 = wdl_to_dtz[v1 + 2];
if (v < -100) {
if (v1 >= 0)
v = v1;
} else if (v < 0) {
if (v1 >= 0 || v1 < 100)
v = v1;
} else if (v > 100) {
if (v1 > 0)
v = v1;
} else if (v > 0) {
if (v1 == 1)
v = v1;
} else if (v1 >= 0) {
v = v1;
} else {
for (moves = stack; moves < end; moves++) {
Move move = moves->move;
if (type_of(move) == ENPASSANT) continue;
if (pos.legal(move, ci.pinned)) break;
}
if (moves == end && !pos.checkers()) {
end = generate<QUIETS>(pos, end);
for (; moves < end; moves++) {
Move move = moves->move;
if (pos.legal(move, ci.pinned))
break;
}
}
if (moves == end)
v = v1;
}
}
return v;
}
// Check whether there has been at least one repetition of positions
// since the last capture or pawn move.
static int has_repeated(StateInfo *st)
{
while (1) {
int i = 4, e = std::min(st->rule50, st->pliesFromNull);
if (e < i)
return 0;
StateInfo *stp = st->previous->previous;
do {
stp = stp->previous->previous;
if (stp->key == st->key)
return 1;
i += 2;
} while (i <= e);
st = st->previous;
}
}
static Value wdl_to_Value[5] = {
-VALUE_MATE + MAX_PLY + 1,
VALUE_DRAW - 2,
VALUE_DRAW,
VALUE_DRAW + 2,
VALUE_MATE - MAX_PLY - 1
};
// Use the DTZ tables to filter out moves that don't preserve the win or draw.
// If the position is lost, but DTZ is fairly high, only keep moves that
// maximise DTZ.
//
// A return value false indicates that not all probes were successful and that
// no moves were filtered out.
bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Value& score)
{
int success;
int dtz = probe_dtz(pos, &success);
if (!success) return false;
StateInfo st;
CheckInfo ci(pos);
// Probe each move.
for (size_t i = 0; i < rootMoves.size(); i++) {
Move move = rootMoves[i].pv[0];
pos.do_move(move, st, ci, pos.gives_check(move, ci));
int v = 0;
if (pos.checkers() && dtz > 0) {
ExtMove s[192];
if (generate<LEGAL>(pos, s) == s)
v = 1;
}
if (!v) {
if (st.rule50 != 0) {
v = -Tablebases::probe_dtz(pos, &success);
if (v > 0) v++;
else if (v < 0) v--;
} else {
v = -Tablebases::probe_wdl(pos, &success);
v = wdl_to_dtz[v + 2];
}
}
pos.undo_move(move);
if (!success) return false;
rootMoves[i].score = (Value)v;
}
// Obtain 50-move counter for the root position.
// In Stockfish there seems to be no clean way, so we do it like this:
int cnt50 = st.previous->rule50;
// Use 50-move counter to determine whether the root position is
// won, lost or drawn.
int wdl = 0;
if (dtz > 0)
wdl = (dtz + cnt50 <= 100) ? 2 : 1;
else if (dtz < 0)
wdl = (-dtz + cnt50 <= 100) ? -2 : -1;
// Determine the score to report to the user.
score = wdl_to_Value[wdl + 2];
// If the position is winning or losing, but too few moves left, adjust the
// score to show how close it is to winning or losing.
// NOTE: int(PawnValueEg) is used as scaling factor in score_to_uci().
if (wdl == 1 && dtz <= 100)
score = (Value)(((200 - dtz - cnt50) * int(PawnValueEg)) / 200);
else if (wdl == -1 && dtz >= -100)
score = -(Value)(((200 + dtz - cnt50) * int(PawnValueEg)) / 200);
// Now be a bit smart about filtering out moves.
size_t j = 0;
if (dtz > 0) { // winning (or 50-move rule draw)
int best = 0xffff;
for (size_t i = 0; i < rootMoves.size(); i++) {
int v = rootMoves[i].score;
if (v > 0 && v < best)
best = v;
}
int max = best;
// If the current phase has not seen repetitions, then try all moves
// that stay safely within the 50-move budget, if there are any.
if (!has_repeated(st.previous) && best + cnt50 <= 99)
max = 99 - cnt50;
for (size_t i = 0; i < rootMoves.size(); i++) {
int v = rootMoves[i].score;
if (v > 0 && v <= max)
rootMoves[j++] = rootMoves[i];
}
} else if (dtz < 0) { // losing (or 50-move rule draw)
int best = 0;
for (size_t i = 0; i < rootMoves.size(); i++) {
int v = rootMoves[i].score;
if (v < best)
best = v;
}
// Try all moves, unless we approach or have a 50-move rule draw.
if (-best * 2 + cnt50 < 100)
return true;
for (size_t i = 0; i < rootMoves.size(); i++) {
if (rootMoves[i].score == best)
rootMoves[j++] = rootMoves[i];
}
} else { // drawing
// Try all moves that preserve the draw.
for (size_t i = 0; i < rootMoves.size(); i++) {
if (rootMoves[i].score == 0)
rootMoves[j++] = rootMoves[i];
}
}
rootMoves.resize(j, Search::RootMove(MOVE_NONE));
return true;
}
// Use the WDL tables to filter out moves that don't preserve the win or draw.
// This is a fallback for the case that some or all DTZ tables are missing.
//
// A return value false indicates that not all probes were successful and that
// no moves were filtered out.
bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoveVector& rootMoves, Value& score)
{
int success;
int wdl = Tablebases::probe_wdl(pos, &success);
if (!success) return false;
score = wdl_to_Value[wdl + 2];
StateInfo st;
CheckInfo ci(pos);
int best = -2;
// Probe each move.
for (size_t i = 0; i < rootMoves.size(); i++) {
Move move = rootMoves[i].pv[0];
pos.do_move(move, st, ci, pos.gives_check(move, ci));
int v = -Tablebases::probe_wdl(pos, &success);
pos.undo_move(move);
if (!success) return false;
rootMoves[i].score = (Value)v;
if (v > best)
best = v;
}
size_t j = 0;
for (size_t i = 0; i < rootMoves.size(); i++) {
if (rootMoves[i].score == best)
rootMoves[j++] = rootMoves[i];
}
rootMoves.resize(j, Search::RootMove(MOVE_NONE));
return true;
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef TBPROBE_H
#define TBPROBE_H
#include "../search.h"
namespace Tablebases {
extern int MaxCardinality;
void init(const std::string& path);
int probe_wdl(Position& pos, int *success);
int probe_dtz(Position& pos, int *success);
bool root_probe(Position& pos, Search::RootMoveVector& rootMoves, Value& score);
bool root_probe_wdl(Position& pos, Search::RootMoveVector& rootMoves, Value& score);
}
#endif
+159 -160
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -23,7 +23,7 @@
#include "movegen.h"
#include "search.h"
#include "thread.h"
#include "ucioption.h"
#include "uci.h"
using namespace Search;
@@ -40,7 +40,7 @@ namespace {
// Helpers to launch a thread after creation and joining before delete. Must be
// outside Thread c'tor and d'tor because the object will be fully initialized
// outside Thread c'tor and d'tor because the object must be fully initialized
// when start_routine (and hence virtual idle_loop) is called and when joining.
template<typename T> T* new_thread() {
@@ -50,7 +50,11 @@ namespace {
}
void delete_thread(ThreadBase* th) {
th->mutex.lock();
th->exit = true; // Search must be already finished
th->mutex.unlock();
th->notify_one();
thread_join(th->handle); // Wait for thread termination
delete th;
@@ -59,7 +63,7 @@ namespace {
}
// notify_one() wakes up the thread when there is some work to do
// ThreadBase::notify_one() wakes up the thread when there is some work to do
void ThreadBase::notify_one() {
@@ -69,20 +73,20 @@ void ThreadBase::notify_one() {
}
// wait_for() set the thread to sleep until condition 'b' turns true
// ThreadBase::wait_for() set the thread to sleep until 'condition' turns true
void ThreadBase::wait_for(volatile const bool& b) {
void ThreadBase::wait_for(volatile const bool& condition) {
mutex.lock();
while (!b) sleepCondition.wait(mutex);
while (!condition) sleepCondition.wait(mutex);
mutex.unlock();
}
// Thread c'tor just inits data and does not launch any execution thread.
// Such a thread will only be started when c'tor returns.
// Thread c'tor makes some init but does not launch any execution thread that
// will be started only when c'tor returns.
Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
Thread::Thread() /* : splitPoints() */ { // Initialization of non POD broken in MSVC
searching = false;
maxPly = splitPointsSize = 0;
@@ -92,7 +96,7 @@ Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
}
// cutoff_occurred() checks whether a beta cutoff has occurred in the
// 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 {
@@ -119,7 +123,7 @@ bool Thread::available_to(const Thread* master) const {
// Make a local copy to be sure it doesn't become zero under our feet while
// testing next condition and so leading to an out of bounds access.
int size = splitPointsSize;
const int size = splitPointsSize;
// No split points means that the thread is available as a slave for any
// other thread otherwise apply the "helpful master" concept if possible.
@@ -127,146 +131,25 @@ bool Thread::available_to(const Thread* master) const {
}
// TimerThread::idle_loop() is where the timer thread waits msec milliseconds
// and then calls check_time(). If msec is 0 thread sleeps until it's woken up.
void TimerThread::idle_loop() {
while (!exit)
{
mutex.lock();
if (!exit)
sleepCondition.wait_for(mutex, run ? Resolution : INT_MAX);
mutex.unlock();
if (run)
check_time();
}
}
// MainThread::idle_loop() is where the main thread is parked waiting to be started
// when there is a new search. The main thread will launch all the slave threads.
void MainThread::idle_loop() {
while (true)
{
mutex.lock();
thinking = false;
while (!thinking && !exit)
{
Threads.sleepCondition.notify_one(); // Wake up the UI thread if needed
sleepCondition.wait(mutex);
}
mutex.unlock();
if (exit)
return;
searching = true;
Search::think();
assert(searching);
searching = false;
}
}
// init() is called at startup to create and launch requested threads, that will
// go immediately to sleep. We cannot use a c'tor because 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<TimerThread>();
push_back(new_thread<MainThread>());
read_uci_options();
}
// exit() cleanly terminates the threads before the program exits. Cannot be done in
// d'tor because we have to terminate the threads before to free ThreadPool object.
void ThreadPool::exit() {
delete_thread(timer); // As first because check_time() accesses threads data
for (iterator it = begin(); it != end(); ++it)
delete_thread(*it);
}
// 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 all possible threads
// in advance (which include pawns and material tables), even if only a few
// are to be used.
void ThreadPool::read_uci_options() {
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
size_t requested = Options["Threads"];
assert(requested > 0);
// If zero (default) then set best minimum split depth automatically
if (!minimumSplitDepth)
minimumSplitDepth = requested < 8 ? 4 * ONE_PLY : 7 * ONE_PLY;
while (size() < requested)
push_back(new_thread<Thread>());
while (size() > requested)
{
delete_thread(back());
pop_back();
}
}
// available_slave() tries to find an idle thread which is available as a slave
// for the thread 'master'.
Thread* ThreadPool::available_slave(const Thread* master) const {
for (const_iterator it = begin(); it != end(); ++it)
if ((*it)->available_to(master))
return *it;
return NULL;
}
// split() does the actual work of distributing the work at a node between
// Thread::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), 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
// informed 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>
void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Value* bestValue,
void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bestValue,
Move* bestMove, Depth depth, int moveCount,
MovePicker* movePicker, int nodeType, bool cutNode) {
assert(pos.pos_is_ok());
assert(searching);
assert(-VALUE_INFINITE < *bestValue && *bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE);
assert(depth >= Threads.minimumSplitDepth);
assert(searching);
assert(splitPointsSize < MAX_SPLITPOINTS_PER_THREAD);
// Pick the next available split point from the split point stack
// Pick and init the next available split point
SplitPoint& sp = splitPoints[splitPointsSize];
sp.masterThread = this;
@@ -297,14 +180,15 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
activeSplitPoint = &sp;
activePosition = NULL;
if (!Fake)
for (Thread* slave; (slave = Threads.available_slave(this)) != NULL; )
{
sp.slavesMask.set(slave->idx);
slave->activeSplitPoint = &sp;
slave->searching = true; // Slave leaves idle_loop()
slave->notify_one(); // Could be sleeping
}
Thread* slave;
while ((slave = Threads.available_slave(this)) != NULL)
{
sp.slavesMask.set(slave->idx);
slave->activeSplitPoint = &sp;
slave->searching = true; // Slave leaves idle_loop()
slave->notify_one(); // Could be sleeping
}
// Everything is set up. The master thread enters the idle loop, from which
// it will instantly launch a search, because its 'searching' flag is set.
@@ -322,8 +206,8 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
assert(!activePosition);
// We have returned from the idle loop, which means that all threads are
// finished. Note that setting 'searching' and decreasing splitPointsSize is
// done under lock protection to avoid a race with Thread::available_to().
// finished. Note that setting 'searching' and decreasing splitPointsSize must
// be done under lock protection to avoid a race with Thread::available_to().
Threads.mutex.lock();
sp.mutex.lock();
@@ -339,27 +223,142 @@ void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Valu
Threads.mutex.unlock();
}
// Explicit template instantiations
template void Thread::split<false>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, int, MovePicker*, int, bool);
template void Thread::split< true>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, int, MovePicker*, int, bool);
// TimerThread::idle_loop() is where the timer thread waits Resolution milliseconds
// and then calls check_time(). When not searching, thread sleeps until it's woken up.
// wait_for_think_finished() waits for main thread to go to sleep then returns
void TimerThread::idle_loop() {
void ThreadPool::wait_for_think_finished() {
while (!exit)
{
mutex.lock();
MainThread* t = main();
t->mutex.lock();
while (t->thinking) sleepCondition.wait(t->mutex);
t->mutex.unlock();
if (!exit)
sleepCondition.wait_for(mutex, run ? Resolution : INT_MAX);
mutex.unlock();
if (run)
check_time();
}
}
// start_thinking() wakes up the main thread sleeping in MainThread::idle_loop()
// so to start a new search, then returns immediately.
// MainThread::idle_loop() is where the main thread is parked waiting to be started
// when there is a new search. The main thread will launch all the slave threads.
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits, StateStackPtr& states) {
void MainThread::idle_loop() {
while (!exit)
{
mutex.lock();
thinking = false;
while (!thinking && !exit)
{
Threads.sleepCondition.notify_one(); // Wake up the UI thread if needed
sleepCondition.wait(mutex);
}
mutex.unlock();
if (!exit)
{
searching = true;
Search::think();
assert(searching);
searching = false;
}
}
}
// ThreadPool::init() is called at startup to create and launch requested threads,
// that will go immediately to sleep. We cannot use a c'tor because 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<TimerThread>();
push_back(new_thread<MainThread>());
read_uci_options();
}
// ThreadPool::exit() terminates the threads before the program exits. Cannot be
// done in d'tor because threads must be terminated before freeing us.
void ThreadPool::exit() {
delete_thread(timer); // As first because check_time() accesses threads data
for (iterator it = begin(); it != end(); ++it)
delete_thread(*it);
}
// ThreadPool::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 all possible
// threads in advance (which include pawns and material tables), even if only a
// few are to be used.
void ThreadPool::read_uci_options() {
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
size_t requested = Options["Threads"];
assert(requested > 0);
// If zero (default) then set best minimum split depth automatically
if (!minimumSplitDepth)
minimumSplitDepth = requested < 8 ? 4 * ONE_PLY : 7 * ONE_PLY;
while (size() < requested)
push_back(new_thread<Thread>());
while (size() > requested)
{
delete_thread(back());
pop_back();
}
}
// ThreadPool::available_slave() tries to find an idle thread which is available
// as a slave for the thread 'master'.
Thread* ThreadPool::available_slave(const Thread* master) const {
for (const_iterator it = begin(); it != end(); ++it)
if ((*it)->available_to(master))
return *it;
return NULL;
}
// ThreadPool::wait_for_think_finished() waits for main thread to finish the search
void ThreadPool::wait_for_think_finished() {
MainThread* th = main();
th->mutex.lock();
while (th->thinking) sleepCondition.wait(th->mutex);
th->mutex.unlock();
}
// ThreadPool::start_thinking() wakes up the main thread sleeping in
// MainThread::idle_loop() and starts a new search, then returns immediately.
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
StateStackPtr& states) {
wait_for_think_finished();
SearchTime = Time::now(); // As early as possible
+20 -11
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -29,9 +29,14 @@
#include "position.h"
#include "search.h"
struct Thread;
const int MAX_THREADS = 128;
const int MAX_SPLITPOINTS_PER_THREAD = 8;
/// Mutex and ConditionVariable struct are wrappers of the low level locking
/// machinery and are modeled after the corresponding C++11 classes.
struct Mutex {
Mutex() { lock_init(l); }
~Mutex() { lock_destroy(l); }
@@ -57,13 +62,15 @@ private:
WaitCondition c;
};
struct Thread;
/// SplitPoint struct stores information shared by the threads searching in
/// parallel below the same split point. It is populated at splitting time.
struct SplitPoint {
// Const data after split point has been setup
const Position* pos;
const Search::Stack* ss;
Search::Stack* ss;
Thread* masterThread;
Depth depth;
Value beta;
@@ -74,7 +81,7 @@ struct SplitPoint {
MovePicker* movePicker;
SplitPoint* parentSplitPoint;
// Shared data
// Shared variable data
Mutex mutex;
std::bitset<MAX_THREADS> slavesMask;
volatile bool allSlavesSearching;
@@ -117,14 +124,13 @@ struct Thread : public ThreadBase {
bool cutoff_occurred() const;
bool available_to(const Thread* master) const;
template <bool Fake>
void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
void split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
Depth depth, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode);
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
Pawns::Table pawnsTable;
Material::Table materialTable;
Endgames endgames;
Pawns::Table pawnsTable;
Position* activePosition;
size_t idx;
int maxPly;
@@ -144,10 +150,13 @@ struct MainThread : public Thread {
};
struct TimerThread : public ThreadBase {
static const int Resolution = 5; // Millisec between two check_time() calls
TimerThread() : run(false) {}
virtual void idle_loop();
bool run;
static const int Resolution = 5; // msec between two check_time() calls
};
@@ -157,10 +166,10 @@ struct TimerThread : public ThreadBase {
struct ThreadPool : public std::vector<Thread*> {
void init(); // No c'tor and d'tor, threads rely on globals that should
void exit(); // be initialized and are valid during the whole thread lifetime.
void init(); // No c'tor and d'tor, threads rely on globals that should be
void exit(); // initialized and are valid during the whole thread lifetime.
MainThread* main() { return static_cast<MainThread*>((*this)[0]); }
MainThread* main() { return static_cast<MainThread*>(at(0)); }
void read_uci_options();
Thread* available_slave(const Thread* master) const;
void wait_for_think_finished();
+39 -49
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -23,7 +23,7 @@
#include "search.h"
#include "timeman.h"
#include "ucioption.h"
#include "uci.h"
namespace {
@@ -33,10 +33,6 @@ namespace {
const double MaxRatio = 7.0; // When in trouble, we can step over reserved time with this ratio
const double StealRatio = 0.33; // However we must not steal time from remaining moves over this ratio
const double xscale = 9.3;
const double xshift = 59.8;
const double skewfactor = 0.172;
// move_importance() is a skew-logistic function based on naive statistical
// analysis of "how many games are still undecided after n half-moves". Game
@@ -45,82 +41,76 @@ namespace {
double move_importance(int ply) {
return pow((1 + exp((ply - xshift) / xscale)), -skewfactor) + DBL_MIN; // Ensure non-zero
const double XScale = 9.3;
const double XShift = 59.8;
const double Skew = 0.172;
return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero
}
template<TimeType T>
int remaining(int myTime, int movesToGo, int currentPly, int slowMover)
int remaining(int myTime, int movesToGo, int ply, int slowMover)
{
const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
const double TStealRatio = (T == OptimumTime ? 0 : StealRatio);
double thisMoveImportance = (move_importance(currentPly) * slowMover) / 100;
double moveImportance = (move_importance(ply) * slowMover) / 100;
double otherMovesImportance = 0;
for (int i = 1; i < movesToGo; ++i)
otherMovesImportance += move_importance(currentPly + 2 * i);
otherMovesImportance += move_importance(ply + 2 * i);
double ratio1 = (TMaxRatio * thisMoveImportance) / (TMaxRatio * thisMoveImportance + otherMovesImportance);
double ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / (thisMoveImportance + otherMovesImportance);
double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance);
double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance);
return int(myTime * std::min(ratio1, ratio2));
return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks an explicit cast
}
} // namespace
void TimeManager::init(const Search::LimitsType& limits, int currentPly, Color us)
/// init() is called at the beginning of the search and calculates the allowed
/// thinking time out of the time control and current game ply. We support four
/// different kinds of time controls, passed in 'limits':
///
/// inc == 0 && movestogo == 0 means: x basetime [sudden death!]
/// inc == 0 && movestogo != 0 means: x moves in y minutes
/// inc > 0 && movestogo == 0 means: x basetime + z increment
/// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment
void TimeManager::init(const Search::LimitsType& limits, Color us, int ply)
{
/* We support four different kinds 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"];
int minThinkingTime = Options["Minimum Thinking Time"];
int moveOverhead = Options["Move Overhead"];
int slowMover = Options["Slow Mover"];
// Initialize unstablePvFactor to 1 and search times to maximum values
unstablePvFactor = 1;
optimumSearchTime = maximumSearchTime = std::max(limits.time[us], minThinkingTime);
// We calculate optimum time usage for different hypothetical "moves to go"-values and choose the
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
for (hypMTG = 1; hypMTG <= (limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon); ++hypMTG)
const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon;
// We calculate optimum time usage for different hypothetical "moves to go"-values
// and choose the minimum of calculated search time values. Usually the greatest
// hypMTG gives the minimum values.
for (int hypMTG = 1; hypMTG <= MaxMTG; ++hypMTG)
{
// Calculate thinking time for hypothetical "moves to go"-value
hypMyTime = limits.time[us]
+ limits.inc[us] * (hypMTG - 1)
- emergencyBaseTime
- emergencyMoveTime * std::min(hypMTG, emergencyMoveHorizon);
int hypMyTime = limits.time[us]
+ limits.inc[us] * (hypMTG - 1)
- moveOverhead * (2 + std::min(hypMTG, 40));
hypMyTime = std::max(hypMyTime, 0);
t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly, slowMover);
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly, slowMover);
int t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, ply, slowMover);
int t2 = minThinkingTime + remaining<MaxTime >(hypMyTime, hypMTG, ply, slowMover);
optimumSearchTime = std::min(optimumSearchTime, t1);
maximumSearchTime = std::min(maximumSearchTime, t2);
optimumSearchTime = std::min(t1, optimumSearchTime);
maximumSearchTime = std::min(t2, maximumSearchTime);
}
if (Options["Ponder"])
optimumSearchTime += optimumSearchTime / 4;
// Make sure that maxSearchTime is not over absoluteMaxSearchTime
optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime);
}
+2 -2
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -25,7 +25,7 @@
class TimeManager {
public:
void init(const Search::LimitsType& limits, int currentPly, Color us);
void init(const Search::LimitsType& limits, Color us, int ply);
void pv_instability(double bestMoveChanges) { unstablePvFactor = 1 + bestMoveChanges; }
int available_time() const { return int(optimumSearchTime * unstablePvFactor * 0.71); }
int maximum_time() const { return maximumSearchTime; }
+36 -59
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <cstring> // For std::memset
#include <iostream>
#include "bitboard.h"
@@ -30,18 +30,19 @@ TranspositionTable TT; // Our global transposition table
/// measured in megabytes. Transposition table consists of a power of 2 number
/// of clusters and each cluster consists of ClusterSize number of TTEntry.
void TranspositionTable::resize(uint64_t mbSize) {
void TranspositionTable::resize(size_t mbSize) {
assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32);
assert(sizeof(Cluster) == CacheLineSize / 2);
uint32_t size = ClusterSize << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize]));
size_t newClusterCount = size_t(1) << msb((mbSize * 1024 * 1024) / sizeof(Cluster));
if (hashMask == size - ClusterSize)
if (newClusterCount == clusterCount)
return;
hashMask = size - ClusterSize;
clusterCount = newClusterCount;
free(mem);
mem = calloc(size * sizeof(TTEntry) + CACHE_LINE_SIZE - 1, 1);
mem = calloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1, 1);
if (!mem)
{
@@ -50,72 +51,48 @@ void TranspositionTable::resize(uint64_t mbSize) {
exit(EXIT_FAILURE);
}
table = (TTEntry*)((uintptr_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1));
table = (Cluster*)((uintptr_t(mem) + CacheLineSize - 1) & ~(CacheLineSize - 1));
}
/// TranspositionTable::clear() overwrites the entire transposition table
/// with zeroes. It is called whenever the table is resized, or when the
/// with zeros. It is called whenever the table is resized, or when the
/// user asks the program to clear the table (from the UCI interface).
void TranspositionTable::clear() {
std::memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry));
std::memset(table, 0, clusterCount * sizeof(Cluster));
}
/// TranspositionTable::probe() looks up the current position in the
/// transposition table. Returns a pointer to the TTEntry or NULL if
/// position is not found.
/// TranspositionTable::probe() looks up the current position in the transposition
/// table. It returns true and a pointer to the TTEntry if the position is found.
/// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry
/// to be replaced later. 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.
const TTEntry* TranspositionTable::probe(const Key key) const {
TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
TTEntry* tte = first_entry(key);
uint32_t key32 = key >> 32;
TTEntry* const tte = first_entry(key);
const uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster
for (unsigned i = 0; i < ClusterSize; ++i, ++tte)
if (tte->key32 == key32)
for (int i = 0; i < ClusterSize; ++i)
if (!tte[i].key16 || tte[i].key16 == key16)
{
tte->generation8 = generation; // Refresh
return tte;
if (tte[i].key16)
tte[i].genBound8 = uint8_t(generation8 | tte[i].bound()); // Refresh
return found = (bool)tte[i].key16, &tte[i];
}
return NULL;
}
/// TranspositionTable::store() writes a new entry containing position key and
/// valuable information of current position. The lowest order bits of position
/// key are used to decide in which cluster the position will be placed.
/// When a new entry is written and there are no empty entries available in the
/// cluster, it replaces the least valuable of the 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 key, Value v, Bound b, Depth d, Move m, Value statV) {
TTEntry *tte, *replace;
uint32_t key32 = key >> 32; // Use the high 32 bits as key inside the cluster
tte = replace = first_entry(key);
for (unsigned i = 0; i < ClusterSize; ++i, ++tte)
{
if (!tte->key32 || tte->key32 == key32) // Empty or overwrite old
{
if (!m)
m = tte->move(); // Preserve any existing ttMove
replace = tte;
break;
}
// Implement replace strategy
if ( ( tte->generation8 == generation || tte->bound() == BOUND_EXACT)
- (replace->generation8 == generation)
- (tte->depth16 < replace->depth16) < 0)
replace = tte;
}
replace->save(key32, v, b, d, m, generation, statV);
// Find an entry to be replaced according to the replacement strategy
TTEntry* replace = tte;
for (int i = 1; i < ClusterSize; ++i)
if ( (( tte[i].genBound8 & 0xFC) == generation8 || tte[i].bound() == BOUND_EXACT)
- ((replace->genBound8 & 0xFC) == generation8)
- (tte[i].depth8 < replace->depth8) < 0)
replace = &tte[i];
return found = false, replace;
}
+47 -45
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -23,42 +23,45 @@
#include "misc.h"
#include "types.h"
/// The TTEntry is the 14 bytes transposition table entry, defined as below:
/// TTEntry struct is the 10 bytes transposition table entry, defined as below:
///
/// key 32 bit
/// key 16 bit
/// move 16 bit
/// bound type 8 bit
/// generation 8 bit
/// value 16 bit
/// depth 16 bit
/// eval value 16 bit
/// generation 6 bit
/// bound type 2 bit
/// depth 8 bit
struct TTEntry {
Move move() const { return (Move )move16; }
Bound bound() const { return (Bound)bound8; }
Value value() const { return (Value)value16; }
Depth depth() const { return (Depth)depth16; }
Value eval_value() const { return (Value)evalValue; }
Move move() const { return (Move )move16; }
Value value() const { return (Value)value16; }
Value eval() const { return (Value)eval16; }
Depth depth() const { return (Depth)depth8; }
Bound bound() const { return (Bound)(genBound8 & 0x3); }
void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) {
if (m || (k >> 48) != key16) // Preserve any existing move for the same position
move16 = (uint16_t)m;
key16 = (uint16_t)(k >> 48);
value16 = (int16_t)v;
eval16 = (int16_t)ev;
genBound8 = (uint8_t)(g | b);
depth8 = (int8_t)d;
}
private:
friend class TranspositionTable;
void save(uint32_t k, Value v, Bound b, Depth d, Move m, uint8_t g, Value ev) {
key32 = (uint32_t)k;
move16 = (uint16_t)m;
bound8 = (uint8_t)b;
generation8 = (uint8_t)g;
value16 = (int16_t)v;
depth16 = (int16_t)d;
evalValue = (int16_t)ev;
}
uint32_t key32;
uint16_t key16;
uint16_t move16;
uint8_t bound8, generation8;
int16_t value16, depth16, evalValue;
int16_t value16;
int16_t eval16;
uint8_t genBound8;
int8_t depth8;
};
@@ -70,35 +73,34 @@ private:
class TranspositionTable {
static const unsigned ClusterSize = 4;
static const int CacheLineSize = 64;
static const int ClusterSize = 3;
struct Cluster {
TTEntry entry[ClusterSize];
char padding[2]; // Align to the cache line size
};
public:
~TranspositionTable() { free(mem); }
void new_search() { ++generation; }
const TTEntry* probe(const Key key) const;
TTEntry* first_entry(const Key key) const;
void resize(uint64_t mbSize);
void new_search() { generation8 += 4; } // Lower 2 bits are used by Bound
uint8_t generation() const { return generation8; }
TTEntry* probe(const Key key, bool& found) const;
void resize(size_t mbSize);
void clear();
void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV);
// The lowest order bits of the key are used to get the index of the cluster
TTEntry* first_entry(const Key key) const {
return &table[(size_t)key & (clusterCount - 1)].entry[0];
}
private:
uint32_t hashMask;
TTEntry* table;
size_t clusterCount;
Cluster* table;
void* mem;
uint8_t generation; // Size must be not bigger than TTEntry::generation8
uint8_t generation8; // Size must be not bigger than TTEntry::genBound8
};
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 key) const {
return table + ((uint32_t)key & hashMask);
}
#endif // #ifndef TT_H_INCLUDED
+69 -98
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -20,20 +20,19 @@
#ifndef TYPES_H_INCLUDED
#define TYPES_H_INCLUDED
/// For Linux and OSX configuration is done automatically using Makefile. To get
/// started type 'make help'.
/// When compiling with provided Makefile (e.g. for Linux and OSX), configuration
/// is done automatically. To get started type 'make help'.
///
/// For Windows, part of the configuration is detected automatically, but some
/// switches need to be set manually:
/// When Makefile is not used (e.g. with Microsoft Visual Studio) some switches
/// need to be set manually:
///
/// -DNDEBUG | Disable debugging mode. Always use this.
/// -DNDEBUG | Disable debugging mode. Always use this for release.
///
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want
/// | the executable to run on some very old machines.
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to
/// | run on some very old machines.
///
/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
/// | only in 64-bit mode. For compiling requires hardware with
/// | popcnt support.
/// | only in 64-bit mode and requires hardware with popcnt support.
#include <cassert>
#include <cctype>
@@ -42,33 +41,33 @@
#include "platform.h"
#define unlikely(x) (x) // For code annotation purposes
/// Predefined macros hell:
///
/// __GNUC__ Compiler is gcc, Clang or Intel on Linux
/// __INTEL_COMPILER Compiler is Intel
/// _MSC_VER Compiler is MSVC or Intel on Windows
/// _WIN32 Building on Windows (any)
/// _WIN64 Building on Windows 64 bit
#if defined(_WIN64) && !defined(IS_64BIT)
#if defined(_WIN64) && !defined(IS_64BIT) // Last condition means Makefile is not used
# 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)
#if defined(USE_POPCNT) && defined(__INTEL_COMPILER) && defined(_MSC_VER)
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
#endif
#if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
# include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch()
#endif
#if defined(USE_PEXT)
# include <immintrin.h> // Header for _pext_u64() intrinsic
# define pext(b, m) _pext_u64(b, m)
#else
# define _pext_u64(b, m) (0)
#endif
# if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
# include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch()
# endif
#define CACHE_LINE_SIZE 64
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
# define CACHE_LINE_ALIGNMENT __declspec(align(CACHE_LINE_SIZE))
#else
# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(CACHE_LINE_SIZE)))
# define pext(b, m) (0)
#endif
#ifdef _MSC_VER
@@ -100,9 +99,8 @@ const bool Is64Bit = false;
typedef uint64_t Key;
typedef uint64_t Bitboard;
const int MAX_MOVES = 256;
const int MAX_PLY = 120;
const int MAX_PLY_PLUS_6 = MAX_PLY + 6;
const int MAX_MOVES = 256;
const int MAX_PLY = 128;
/// A move needs 16 bits to be stored
///
@@ -136,12 +134,12 @@ enum CastlingSide {
KING_SIDE, QUEEN_SIDE, CASTLING_SIDE_NB = 2
};
enum CastlingRight { // Defined as in PolyGlot book hash key
enum CastlingRight {
NO_CASTLING,
WHITE_OO,
WHITE_OOO = WHITE_OO << 1,
BLACK_OO = WHITE_OO << 2,
BLACK_OOO = WHITE_OO << 3,
WHITE_OOO = WHITE_OO << 1,
BLACK_OO = WHITE_OO << 2,
BLACK_OOO = WHITE_OO << 3,
ANY_CASTLING = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO,
CASTLING_RIGHT_NB = 16
};
@@ -181,8 +179,8 @@ enum Value {
VALUE_INFINITE = 32001,
VALUE_NONE = 32002,
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY,
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY,
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN,
@@ -211,14 +209,15 @@ enum Piece {
enum Depth {
ONE_PLY = 2,
ONE_PLY = 1,
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = 0 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
DEPTH_ZERO = 0,
DEPTH_QS_CHECKS = 0,
DEPTH_QS_NO_CHECKS = -1,
DEPTH_QS_RECAPTURES = -5,
DEPTH_NONE = -127 * ONE_PLY
DEPTH_NONE = -6,
DEPTH_MAX = MAX_PLY
};
enum Square {
@@ -256,9 +255,9 @@ enum Rank {
};
/// The Score enum stores a middlegame and an endgame value in a single integer
/// (enum). The least significant 16 bits are used to store the endgame value
/// and the upper 16 bits are used to store the middlegame value. The compiler
/// Score enum stores a middlegame and an endgame value in a single integer.
/// The least significant 16 bits are used to store the endgame value and
/// the upper 16 bits are used to store the middlegame value. The compiler
/// is free to choose the enum type as long as it can store the data, so we
/// ensure that Score is an integer type by assigning some big int values.
enum Score {
@@ -267,47 +266,41 @@ enum Score {
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
typedef union {
uint32_t full;
struct { int16_t eg, mg; } half;
} ScoreView;
inline Score make_score(int mg, int eg) {
ScoreView v;
v.half.mg = (int16_t)(mg - (uint16_t(eg) >> 15));
v.half.eg = (int16_t)eg;
return Score(v.full);
return Score((mg << 16) + eg);
}
/// Extracting the signed lower and upper 16 bits is not so trivial because
/// according to the standard a simple cast to short is implementation defined
/// and so is a right shift of a signed integer.
inline Value mg_value(Score s) {
ScoreView v;
v.full = s;
return Value(v.half.mg + (uint16_t(v.half.eg) >> 15));
union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s + 0x8000) >> 16) };
return Value(mg.s);
}
inline Value eg_value(Score s) {
ScoreView v;
v.full = s;
return Value(v.half.eg);
union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s)) };
return Value(eg.s);
}
#define ENABLE_BASE_OPERATORS_ON(T) \
inline T operator+(const T d1, const T d2) { return T(int(d1) + int(d2)); } \
inline T operator-(const T d1, const T d2) { return T(int(d1) - int(d2)); } \
inline T operator*(int i, const T d) { return T(i * int(d)); } \
inline T operator*(const T d, int i) { return T(int(d) * i); } \
inline T operator-(const T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, const T d2) { return d1 = d1 + d2; } \
inline T& operator-=(T& d1, const T d2) { return d1 = d1 - d2; } \
#define ENABLE_BASE_OPERATORS_ON(T) \
inline T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \
inline T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \
inline T operator*(int i, T d) { return T(i * int(d)); } \
inline T operator*(T d, int i) { return T(int(d) * i); } \
inline T operator-(T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \
inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; } \
inline T& operator*=(T& d, int i) { return d = T(int(d) * i); }
ENABLE_BASE_OPERATORS_ON(Score)
#define ENABLE_FULL_OPERATORS_ON(T) \
ENABLE_BASE_OPERATORS_ON(T) \
inline T& operator++(T& d) { return d = T(int(d) + 1); } \
inline T& operator--(T& d) { return d = T(int(d) - 1); } \
inline T operator/(const T d, int i) { return T(int(d) / i); } \
#define ENABLE_FULL_OPERATORS_ON(T) \
ENABLE_BASE_OPERATORS_ON(T) \
inline T& operator++(T& d) { return d = T(int(d) + 1); } \
inline T& operator--(T& d) { return d = T(int(d) - 1); } \
inline T operator/(T d, int i) { return T(int(d) / i); } \
inline int operator/(T d1, T d2) { return int(d1) / int(d2); } \
inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
ENABLE_FULL_OPERATORS_ON(Value)
@@ -319,6 +312,8 @@ ENABLE_FULL_OPERATORS_ON(Square)
ENABLE_FULL_OPERATORS_ON(File)
ENABLE_FULL_OPERATORS_ON(Rank)
ENABLE_BASE_OPERATORS_ON(Score)
#undef ENABLE_FULL_OPERATORS_ON
#undef ENABLE_BASE_OPERATORS_ON
@@ -339,15 +334,6 @@ inline Score operator/(Score s, int i) {
extern Value PieceValue[PHASE_NB][PIECE_NB];
struct ExtMove {
Move move;
Value value;
};
inline bool operator<(const ExtMove& f, const ExtMove& s) {
return f.value < s.value;
}
inline Color operator~(Color c) {
return Color(c ^ BLACK);
}
@@ -414,14 +400,6 @@ inline bool opposite_colors(Square s1, Square s2) {
return ((s >> 3) ^ s) & 1;
}
inline char to_char(File f, bool tolower = true) {
return char(f - FILE_A + (tolower ? 'a' : 'A'));
}
inline char to_char(Rank r) {
return char(r - RANK_1 + '1');
}
inline Square pawn_push(Color c) {
return c == WHITE ? DELTA_N : DELTA_S;
}
@@ -452,14 +430,7 @@ inline Move make(Square from, Square to, PieceType pt = KNIGHT) {
}
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 to_string(Square s) {
char ch[] = { to_char(file_of(s)), to_char(rank_of(s)), 0 };
return ch;
return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE
}
#endif // #ifndef TYPES_H_INCLUDED
+118 -55
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -17,18 +17,17 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include "evaluate.h"
#include "notation.h"
#include "movegen.h"
#include "position.h"
#include "search.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h"
#include "uci.h"
using namespace std;
@@ -39,9 +38,9 @@ namespace {
// FEN string of the initial position, normal chess
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// Keep a track of the position keys along the setup moves (from the start position
// to the position just before the search starts). This is needed by the repetition
// draw detection code.
// Stack to keep track of the position states along the setup moves (from the
// start position to the position just before the search starts). Needed by
// 'draw by repetition' detection.
Search::StateStackPtr SetupStates;
@@ -72,7 +71,7 @@ namespace {
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
// Parse move list (if any)
while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE)
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
{
SetupStates->push(StateInfo());
pos.do_move(m, SetupStates->top());
@@ -105,7 +104,7 @@ namespace {
// go() is called when engine receives the "go" UCI command. The function sets
// the thinking time and other parameters from the input string, and starts
// the thinking time and other parameters from the input string, then starts
// the search.
void go(const Position& pos, istringstream& is) {
@@ -114,10 +113,9 @@ namespace {
string token;
while (is >> token)
{
if (token == "searchmoves")
while (is >> token)
limits.searchmoves.push_back(move_from_uci(pos, token));
limits.searchmoves.push_back(UCI::to_move(pos, token));
else if (token == "wtime") is >> limits.time[WHITE];
else if (token == "btime") is >> limits.time[BLACK];
@@ -130,7 +128,6 @@ namespace {
else if (token == "mate") is >> limits.mate;
else if (token == "infinite") limits.infinite = true;
else if (token == "ponder") limits.ponder = true;
}
Threads.start_thinking(pos, limits, SetupStates);
}
@@ -138,10 +135,11 @@ namespace {
} // namespace
/// 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
/// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI
/// commands, the function also supports a few debug commands.
/// UCI::loop() waits for a command from stdin, parses it and calls the appropriate
/// function. Also intercepts EOF from stdin to ensure gracefully exiting if the
/// GUI dies unexpectedly. When called with some command line arguments, e.g. to
/// run 'bench', once the command is executed the function returns immediately.
/// In addition to the UCI ones, also some additional debug commands are supported.
void UCI::loop(int argc, char* argv[]) {
@@ -152,64 +150,56 @@ void UCI::loop(int argc, char* argv[]) {
cmd += std::string(argv[i]) + " ";
do {
if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input
if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF
cmd = "quit";
istringstream is(cmd);
token.clear(); // getline() could return empty or blank line
is >> skipws >> token;
if (token == "quit" || token == "stop" || token == "ponderhit")
// The GUI sends 'ponderhit' to tell us to ponder on the same move the
// opponent has played. In case Signals.stopOnPonderhit is set we are
// waiting for 'ponderhit' to stop the search (for instance because we
// already ran out of time), otherwise we should continue searching but
// switching from pondering to normal search.
if ( token == "quit"
|| token == "stop"
|| (token == "ponderhit" && Search::Signals.stopOnPonderhit))
{
// The GUI sends 'ponderhit' to tell us to ponder on the same move the
// opponent has played. In case Signals.stopOnPonderhit is set we are
// waiting for 'ponderhit' to stop the search (for instance because we
// already ran out of time), otherwise we should continue searching but
// switch from pondering to normal search.
if (token != "ponderhit" || Search::Signals.stopOnPonderhit)
{
Search::Signals.stop = true;
Threads.main()->notify_one(); // Could be sleeping
}
else
Search::Limits.ponder = false;
Search::Signals.stop = true;
Threads.main()->notify_one(); // Could be sleeping
}
else if (token == "perft" || token == "divide")
{
int depth;
stringstream ss;
is >> depth;
ss << Options["Hash"] << " "
<< Options["Threads"] << " " << depth << " current " << token;
benchmark(pos, ss);
}
else if (token == "key")
sync_cout << hex << uppercase << setfill('0')
<< "position key: " << setw(16) << pos.key()
<< "\nmaterial key: " << setw(16) << pos.material_key()
<< "\npawn key: " << setw(16) << pos.pawn_key()
<< dec << nouppercase << setfill(' ') << sync_endl;
else if (token == "ponderhit")
Search::Limits.ponder = false; // Switch to normal search
else if (token == "uci")
sync_cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << sync_endl;
else if (token == "eval")
{
Search::RootColor = pos.side_to_move(); // Ensure it is set
sync_cout << Eval::trace(pos) << sync_endl;
}
else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ }
else if (token == "isready") sync_cout << "readyok" << sync_endl;
else if (token == "ucinewgame") TT.clear();
else if (token == "go") go(pos, is);
else if (token == "position") position(pos, is);
else if (token == "setoption") setoption(is);
// Additional custom non-UCI commands, useful for debugging
else if (token == "flip") pos.flip();
else if (token == "bench") benchmark(pos, is);
else if (token == "d") sync_cout << pos.pretty() << sync_endl;
else if (token == "isready") sync_cout << "readyok" << sync_endl;
else if (token == "d") sync_cout << pos << sync_endl;
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
else if (token == "perft")
{
int depth;
stringstream ss;
is >> depth;
ss << Options["Hash"] << " "
<< Options["Threads"] << " " << depth << " current perft";
benchmark(pos, ss);
}
else
sync_cout << "Unknown command: " << cmd << sync_endl;
@@ -217,3 +207,76 @@ void UCI::loop(int argc, char* argv[]) {
Threads.wait_for_think_finished(); // Cannot quit whilst the search is running
}
/// UCI::value() converts a Value to a string suitable for use with the UCI
/// protocol specification:
///
/// cp <x> The score from the engine's point of view in centipawns.
/// mate <y> Mate in y moves, not plies. If the engine is getting mated
/// use negative values for y.
string UCI::value(Value v) {
stringstream ss;
if (abs(v) < VALUE_MATE - MAX_PLY)
ss << "cp " << v * 100 / PawnValueEg;
else
ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
return ss.str();
}
/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.)
std::string UCI::square(Square s) {
char sq[] = { char('a' + file_of(s)), char('1' + rank_of(s)), 0 }; // NULL terminated
return sq;
}
/// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q).
/// The only special case is castling, where we print in the e1g1 notation in
/// normal chess mode, and in e1h1 notation in chess960 mode. Internally all
/// castling moves are always encoded as 'king captures rook'.
string UCI::move(Move m, bool chess960) {
Square from = from_sq(m);
Square to = to_sq(m);
if (m == MOVE_NONE)
return "(none)";
if (m == MOVE_NULL)
return "0000";
if (type_of(m) == CASTLING && !chess960)
to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
string move = UCI::square(from) + UCI::square(to);
if (type_of(m) == PROMOTION)
move += " pnbrqk"[promotion_type(m)];
return move;
}
/// UCI::to_move() converts a string representing a move in coordinate notation
/// (g1f3, a7a8q) to the corresponding legal Move, if any.
Move UCI::to_move(const Position& pos, string& str) {
if (str.length() == 5) // Junior could send promotion piece in uppercase
str[4] = char(tolower(str[4]));
for (MoveList<LEGAL> it(pos); *it; ++it)
if (str == UCI::move(*it, pos.is_chess960()))
return *it;
return MOVE_NONE;
}
+11 -3
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -23,6 +23,10 @@
#include <map>
#include <string>
#include "types.h"
class Position;
namespace UCI {
class Option;
@@ -46,8 +50,8 @@ public:
Option(const char* v, OnChange = NULL);
Option(int v, int min, int max, OnChange = NULL);
Option& operator=(const std::string& v);
void operator<<(const Option& o);
Option& operator=(const std::string&);
void operator<<(const Option&);
operator int() const;
operator std::string() const;
@@ -62,6 +66,10 @@ private:
void init(OptionsMap&);
void loop(int argc, char* argv[]);
std::string value(Value v);
std::string square(Square s);
std::string move(Move m, bool chess960);
Move to_move(const Position& pos, std::string& str);
} // namespace UCI
+26 -36
View File
@@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2008-2015 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
@@ -22,11 +22,11 @@
#include <cstdlib>
#include <sstream>
#include "evaluate.h"
#include "misc.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h"
#include "uci.h"
#include "syzygy/tbprobe.h"
using std::string;
@@ -35,11 +35,11 @@ UCI::OptionsMap Options; // Global object
namespace UCI {
/// 'On change' actions, triggered by an option's value change
void on_logger(const Option& o) { start_logger(o); }
void on_eval(const Option&) { Eval::init(); }
void on_threads(const Option&) { Threads.read_uci_options(); }
void on_hash_size(const Option& o) { TT.resize(o); }
void on_clear_hash(const Option&) { TT.clear(); }
void on_hash_size(const Option& o) { TT.resize(o); }
void on_logger(const Option& o) { start_logger(o); }
void on_threads(const Option&) { Threads.read_uci_options(); }
void on_tb_path(const Option& o) { Tablebases::init(o); }
/// Our case insensitive less() function as required by UCI protocol
@@ -54,35 +54,25 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const
void init(OptionsMap& o) {
o["Write Debug Log"] << Option(false, on_logger);
o["Write Search Log"] << Option(false);
o["Search Log Filename"] << Option("SearchLog.txt");
o["Book File"] << Option("book.bin");
o["Best Book Move"] << Option(false);
o["Contempt Factor"] << Option(0, -50, 50);
o["Mobility (Midgame)"] << Option(100, 0, 200, on_eval);
o["Mobility (Endgame)"] << Option(100, 0, 200, on_eval);
o["Pawn Structure (Midgame)"] << Option(100, 0, 200, on_eval);
o["Pawn Structure (Endgame)"] << Option(100, 0, 200, on_eval);
o["Passed Pawns (Midgame)"] << Option(100, 0, 200, on_eval);
o["Passed Pawns (Endgame)"] << Option(100, 0, 200, on_eval);
o["Space"] << Option(100, 0, 200, on_eval);
o["Aggressiveness"] << Option(100, 0, 200, on_eval);
o["Cowardice"] << Option(100, 0, 200, on_eval);
o["Min Split Depth"] << Option(0, 0, 12, on_threads);
o["Threads"] << Option(1, 1, MAX_THREADS, on_threads);
o["Hash"] << Option(32, 1, 16384, on_hash_size);
o["Clear Hash"] << Option(on_clear_hash);
o["Ponder"] << Option(true);
o["OwnBook"] << Option(false);
o["MultiPV"] << Option(1, 1, 500);
o["Skill Level"] << Option(20, 0, 20);
o["Emergency Move Horizon"] << Option(40, 0, 50);
o["Emergency Base Time"] << Option(60, 0, 30000);
o["Emergency Move Time"] << Option(30, 0, 5000);
o["Minimum Thinking Time"] << Option(20, 0, 5000);
o["Slow Mover"] << Option(80, 10, 1000);
o["UCI_Chess960"] << Option(false);
const int MaxHashMB = Is64Bit ? 1024 * 1024 : 2048;
o["Write Debug Log"] << Option(false, on_logger);
o["Contempt"] << Option(0, -100, 100);
o["Min Split Depth"] << Option(0, 0, 12, on_threads);
o["Threads"] << Option(1, 1, MAX_THREADS, on_threads);
o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size);
o["Clear Hash"] << Option(on_clear_hash);
o["Ponder"] << Option(true);
o["MultiPV"] << Option(1, 1, 500);
o["Skill Level"] << Option(20, 0, 20);
o["Move Overhead"] << Option(30, 0, 5000);
o["Minimum Thinking Time"] << Option(20, 0, 5000);
o["Slow Mover"] << Option(80, 10, 1000);
o["UCI_Chess960"] << Option(false);
o["SyzygyPath"] << Option("<empty>", on_tb_path);
o["SyzygyProbeDepth"] << Option(1, 1, 100);
o["Syzygy50MoveRule"] << Option(true);
o["SyzygyProbeLimit"] << Option(6, 0, 6);
}