Compare commits

..
168 Commits
Author SHA1 Message Date
Marco Costalba 369eff437c Stockfish 8
Bench: 5926706

No functional change
2016-11-01 10:19:17 +01:00
Stefan GeschwentnerandMarco Costalba 344616e917 Bonus for attacked passed pawn promotion path
Bonus for each attacked square on the promotion path
of an enemy passed pawn.

STC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 141511 W: 25295 L: 24579 D: 91637

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 23161 W: 3022 L: 2831 D: 17308

Bench: 5926706
2016-10-30 13:48:03 +01:00
Joost VandevondeleandMarco Costalba 40b1b27178 Fix a series of undefined behaviours
Avoid shifting negative signed integers and use typed
enum to avoids decrementing a variable beyond its defined
range, like:
       for (Rank r = RANK_8; r >= RANK_1; --r)

Changes were tested individually and passed SPRT[-3, 1].

With this patch gcc --sanitize builds cleanly.

No functional change.
2016-10-27 06:44:41 +02:00
Joost VandeVondeleandMarco Costalba bf51b4796a travis-ci: Enable undefined behavior checking 2016-10-27 06:29:24 +02:00
syzygyandMarco Costalba e18e557e77 Output PV if last iteration does not complete
Instead of outputting "info nodes ... time ..." when the last
iteration is interrupted, simply call UCI::pv() to output the PV.

I thought about calling UCI:pv() with bounds -VALUE_INFINITE, VALUE_INFINITE
to avoid "lowerbound" or "upperbound" appearing in it, but I'm not sure that
would be any better.

This patch fixes rare inconsistencies between the first move of
the last PV output and the bestmove played. It also makes sure
that all the latest statistics are sent to the GUI (not only nodes
and time but also nps, tbhits, hashfull).

No functional change.
2016-10-27 06:26:22 +02:00
Stéphane NicoletandMarco Costalba 818b4a126d Endgame malus for having a king in a pawnless flank
Original idea by "ElbertoOne", while "FauziAkram" suggested to put a
small midgame penalty too.

STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 71808 W: 13038 L: 12610 D: 46160

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 150874 W: 19828 L: 19221 D: 111825

Bench: 6077005
2016-10-25 06:57:29 +02:00
VoyagerOneandMarco Costalba e77f38c431 History Stat Comparison
Adjust LMR by comparing history stats
with opponent (prior ply).

STC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 27754 W: 5066 L: 4824 D: 17864

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 216596 W: 28157 L: 27343 D: 161096

Bench: 5437729
2016-10-25 06:44:19 +02:00
Marco Costalba e18321f55a Correcty resey TB hit counter
Restore original behaviour to reset
the counter before a new move search.

Also fixed some warnings and added const
qualifier to a couple of functions, as
suggested by m_stembera.

Thanks to Werner Bergmans for reporting
the regression.

No functional change.
2016-10-22 08:22:13 +02:00
syzygyandMarco Costalba ca67752645 Per-thread TB hit counters
Use a per-thread counter to reduce contention
with many cores and endgame positions.

Measured around 1% speed-up on a 12 core and 8%
on 28 cores with 6-men, searching on:
 7R/1p3k2/2p2P2/3nR1P1/8/3b1P2/7K/r7 b - - 3 38

Also retire the unused set_nodes_searched() and fix
a couple of return types and naming conventions.

No functional change.
2016-10-21 06:15:45 +02:00
Joost VandevondeleandMarco Costalba 3686e719a1 Simplify next_move by always scoring evasions
For a default bench, this fixes the last valgrind
error (jump on uninitialised value).

Passed STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 187869 W: 33303 L: 33463 D: 121103

No functional change.
2016-10-20 17:17:14 +02:00
Joost Vandevondele 9893e7fd53 Make valgrind testing part of travis ci. 2016-10-19 21:29:00 +02:00
ajithcjandMarco Costalba 99f3ad6858 Remove useless assignments to currentMove
We reference (ss-1)->currentMove, i.e. we peek
current move of the parent node, so currentMove
should be valid in the main move loop, when we
search() the subtree, but outside of main loop
it is useless.

No functional change.
2016-10-18 09:00:52 +02:00
VoyagerOneandMarco Costalba e27d3bb884 Use explicit logic for pruning
Also a speedup since we don't need to recalculate SEE
for extensions...as it already determined to be positive.

Results for 12 tests for each version:

        Base      Test      Diff
Mean    2132395   2191002   -58607
StDev   128058    85917     134239
p-value: 0.669
speedup: 0.027

Non functional change.
2016-10-18 08:53:51 +02:00
JacquesandMarco Costalba 16e1881126 Fixes for ARM compilation: take 2
The target:

Odroid U3 (http://www.hardkernel.com/main/products/prdt_info.php?g_code=g138745696275)
Debian Jessie
As listed in #550 and #638 three modifications are needed for compilation to work:

float-abi flag for GCC If an FPU is present and supported by the installed os then passed value need to be hard.
I didn't find any better solution than using readelf to check for the availibilty of Tag_ABI_VFP_args which sould indicate support for the FPU. The check is only done if the arch is arm and if readelf is not present
on the system, there will be an error (/bin/sh: 1: readelf: not found) but it will not break and will continue with the default softfp value. Outputing the error is not really acceptable but I wanted some feedback on the
check itself.

-lpthread is needed on armv7 outside of Android
I replaced UNAME with KERNEL and OS to allow to differentiate Android.

m32 flag
My understanding is that outside of Android the flag is generating errors on armv7.

These modifications should introduce change only for non Android armv7 build.

No functional change.
2016-10-14 08:58:07 +02:00
Marco Costalba e1f600f186 Revert "Fixes for ARM compilation"
This reverts commit a3fe80c36a.

Break compilation on mingw for me.
2016-10-13 08:36:30 +02:00
JacquesandMarco Costalba a3fe80c36a Fixes for ARM compilation
The target:

Odroid U3 (http://www.hardkernel.com/main/products/prdt_info.php?g_code=g138745696275)
Debian Jessie
As listed in #550 and #638 three modifications are needed for compilation to work:

float-abi flag for GCC If an FPU is present and supported by the installed os then passed value need to be hard.
I didn't find any better solution than using readelf to check for the availibilty of Tag_ABI_VFP_args which sould indicate support for the FPU. The check is only done if the arch is arm and if readelf is not present
on the system, there will be an error (/bin/sh: 1: readelf: not found) but it will not break and will continue with the default softfp value. Outputing the error is not really acceptable but I wanted some feedback on the
check itself.

-lpthread is needed on armv7 outside of Android
I replaced UNAME with KERNEL and OS to allow to differentiate Android.

m32 flag
My understanding is that outside of Android the flag is generating errors on armv7.

These modifications should introduce change only for non Android armv7 build.

No functional change.
2016-10-13 08:34:04 +02:00
Marco Costalba fdf3a51c68 AppVeyor: run bench after build
And show resulting bench signature.

The run is very slow becuase optimizations
are all disabled by default /Od /RTC1

No functional change.
2016-10-10 21:00:59 +02:00
Marco Costalba e61f7b1e6d Add AppVeyor integration
It is like Trevis CI but for Windows platform.

Currently just compile builds, wthouth benching
the resulting executable.

No functional change.
2016-10-10 16:29:29 +02:00
ajithcjandMarco Costalba f799610d4b Simplify futility pruning return value
Return eval as it is while doing futility pruning.

STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 167687 W: 29778 L: 29904 D: 108005

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 26905 W: 3503 L: 3390 D: 20012

Bench: 5936728
2016-10-09 09:54:43 +02:00
atumanianandMarco Costalba 073eed590e Optimisation of Position::see and Position::see_sign
Stephane's patch removes the only usage of Position::see, where the
returned value isn't immediately compared with a value. So I replaced
this function by its optimised and more specific version see_ge. This
function also supersedes the function Position::see_sign.

bool Position::see_ge(Move m, Value v) const;

This function tests if the SEE of a move is greater or equal than a
given value. We use forward iteration on captures instread of backward
one, therefore we don't need the swapList array. Also we stop as soon
as we have enough information to obtain the result, avoiding unnecessary
calls to the min_attacker function.

Speed tests (Windows 7), 20 runs for each engine:
Test engine: mean 866648, st. dev. 5964
Base engine: mean 846751, st. dev. 22846
Speedup: 1.023

Speed test by Stephane Nicolet

Fishtest STC test:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 26040 W: 4675 L: 4442 D: 16923
http://tests.stockfishchess.org/tests/view/57f648990ebc59038170fa03

No functional change.
2016-10-08 06:38:36 +02:00
Stéphane Nicolet 1e586288ca Do not use SEE in evasion scoring
Idea by Aram Tumanian (atumanian)

STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 43889 W: 7849 L: 7767 D: 28273

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 29333 W: 3809 L: 3700 D: 21824

Bench: 6421663
2016-10-06 00:00:27 +02:00
Stefano CardanobileandMarco Costalba 0162fb83c2 Retire implicit malus for stonewalls
STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 75864 W: 13466 L: 13437 D: 48961

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 99050 W: 12472 L: 12451 D: 74127

bench: 6098474
2016-10-05 09:32:08 +02:00
VoyagerOneandMarco Costalba ab26c61971 Allow inCheck pruning
This is a bit tricky because we don't want
to prune the only legal evasions, even if
with negative SEE. So add an assert to avoid
this subtle bug to slip in later.

STC:
LLR: 2.96 (-2.94,2.94) [0.00,4.00]
Total: 14140 W: 2625 L: 2421 D: 9094

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,4.00]
Total: 11558 W: 1555 L: 1379 D: 8624

bench: 5256717
2016-10-03 16:18:53 +02:00
Marco Costalba eccccba0ce Remove useless razoring condition
Condition is always true! For any value of the
array index! Even an out of bound array, like
razor_margin[120]!!!!

No functional change.
2016-09-29 15:24:36 +02:00
HiraokaTakuyaandMarco Costalba b77bae0529 Make razor_margin[4] ONE_PLY value independent
No functional change.
2016-09-29 15:20:07 +02:00
Stéphane NicoletandMarco Costalba 7ae3c05795 Rename shift_bb() to shift()
Rename shift_bb() to shift(), and DELTA_S to SOUTH, etc.
to improve code readability, especially in evaluate.cpp
when they are used together:

    old b = shift_bb<DELTA_S>(pos.pieces(PAWN))
    new b = shift<SOUTH>(pos.pieces(PAWN))

While there fix some small code style issues.

No functional change.
2016-09-25 10:45:10 +02:00
joergosterandMarco Costalba 351844061e Allowing singular extension in mate positions
Drop useless condition

abs(ttValue) < VALUE_KNOWN_WIN

And extend singular extension search to cases when ttValue
stores a mate score. This improves mate finding and does
not introduce any regression.

Yery tested this patch against current master on the 6500+
Chest mate suite with 200K fixed nodes:

    shortest mates found: master: 1206 patch:1205
    any mate found: master: 1903 patch: 2003

with 1 sec time:

    shortest mates found: master: 2667 patch: 2628
    any mate found: master: 3585 patch: 3646

Verified for no regression:

STC
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 25655 W: 4578 L: 4465 D: 16612

LTC
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 66247 W: 8618 L: 8557 D: 49072

bench: 6335042
2016-09-24 19:56:02 +02:00
Marco Costalba 8662bdfa12 Fix crash when passing a mate/stalemate position
Both Tablebases::filter_root_moves() and
extract_ponder_from_tt(9 were unable to handle
a mate/stalemate position.

Spotted and reported by Dann Corbit.

Added some mate/stalemate positions to bench so
to early catch this regression in the future.

No functional change.
2016-09-24 07:37:52 +02:00
Stéphane NicoletandMarco Costalba 28240d375c Simplify pinners conditions in SEE()
Use the following transformations:

- to check that A is included in B, testing "(A & ~B) == 0" is faster
than "(A & B) == A"

- to remove the intersection of A and B from A, doing "A &= ~B;" is as
fast as "if (A & B) A &= ~B;" but is simpler.

Overall, the simpler patch version is 0.3% than current master.

No functional change.
2016-09-22 08:31:23 +02:00
Guenther DemetzandMarco Costalba 943ae89be1 Fix pin-aware SEE
Correct pinners calculation and fix bug with pinned
pieces giving check. With this patch 'pinners' only
returns sliders with exactly one defensive piece between
the slider and the attacked square (in other words, pinners
returns exact pinners).

This was a co-operation between Marco Costalba,
Stéphane Nicolet and me.

Special thanks to Ronald de Man for reporting the bug with
pinned pieces giving check, discussed here:
https://groups.google.com/forum/?fromgroups=#!topic/fishcooking/S_4E_Xs5HaE

STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 132118 W: 23578 L: 23645 D: 84895

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 36424 W: 4770 L: 4670 D: 26984

bench: 6272231
2016-09-21 08:42:25 +02:00
Joost VandevondeleandMarco Costalba 4b0043ae7c Use fixed depth bench to make PGO builds more reproducible
Discussed on fishcooking

proposal and objdump verification:
https://groups.google.com/d/msg/fishcooking/4_ausUwMXP0/EGPsMYqOFAAJ

verified no significant speed difference between depth and time:
https://groups.google.com/d/msg/fishcooking/4_ausUwMXP0/KazW5QZmFgAJ

stockfish_time - stats:
mean = 2207232.56        std = 7079.51        std/mean = 0.003207

stockfish_depth - stats:
mean = 2201783.57        std = 6356.69        std/mean = 0.002887

No functional change
2016-09-18 08:13:34 +02:00
Marco Costalba 92f01aa2bd Fix a warning with MSVC
warning C4706: assignment within conditional expression

No functional change.
2016-09-17 10:14:28 +02:00
Stéphane NicoletandMarco Costalba ea41f18e6e Swap mg and eg in internal representation of Score
Instrumentation shows that in make_score(mg, eg) calls, the mg value is
zero in 25,9% of the calls while the eg value is zero in 36,8% of the
calls.

Swapping the internal fields of mg and eg in the internal
representation of Score allows the compiler to optimize away the shift
in (eg << 16) + mg in more cases, thus resulting in a 0.3% speed-up
overall.

No functional change
2016-09-17 09:56:36 +02:00
Marco Costalba 057d710fc2 Fix indentation in struct FromToStats
And other little trivial stuff.

No functional change.
2016-09-17 09:51:20 +02:00
Stéphane NicoletandMarco Costalba 01f2466f6e Retire KingDanger array
Rescales the king danger variables in evaluate_king() to
suppress the KingDanger[] array. This avoids the cost of
the memory accesses to the array and simplifies the non-linear
transformation used.

Full credits to "hxim" for the seminal idea and implementation,
see pull request #786.
https://github.com/official-stockfish/Stockfish/pull/786

Passed STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 9649 W: 1829 L: 1689 D: 6131

Passed LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 53494 W: 7254 L: 7178 D: 39062

Bench: 6116200
2016-09-16 08:30:06 +02:00
Marco Costalba 5c58d1f5cb Use per-thread counterMoveHistory
Drops a scalability bottleneck due to memory contention
of a single shared table across threads. The effect starts
to be sensible with a high number of threads. Specifically
we have a small regression with 7 threads both at 60 and
180 seconds TC:

10000 @ 60+0.6 th 7
ELO: -2.46 +-3.2 (95%) LOS: 6.5%
Total: 9896 W: 1037 L: 1107 D: 7752

5000 @ 180+0.6 th 7
ELO: -1.95 +-4.1 (95%) LOS: 17.7%
Total: 5000 W: 444 L: 472 D: 4084

We have a regression because counterMoveHistory table is
quite big and it takes time for a single thread to fill it.
Sharing the table yields to a higher fill rate and better
quality of moves and up to 7 threads the benefits of sharing
more then compensate the loss in speed due to contention.
Interestingly even with a 3X longer TC, so with more time
for the single thread to catch up, the improvment is quite
limited and below noise level. It seems we really need much
longer TC to saturate the table.

When we move to high threads number it's another story:

5000 @ 60+0.6 th 22
ELO: 3.49 +-4.3 (95%) LOS: 94.6%
Total: 4880 W: 490 L: 441 D: 3949

2000 @ 60+0.6 th 32
ELO: 8.34 +-6.9 (95%) LOS: 99.1%
Total: 2000 W: 229 L: 181 D: 1590

As expected the speed-up more than compensates the filling
rate, and we expect that with tournament TC, where single
thread is able to saturate the table, the difference will
be even stronger. For instance for TCEC 9 super-final time
control will be 180 minutes + 15 seconds and this scalability
improvement seems definitely the way to go.

So, summarizing:

GOOD:

Measured big improvement in high core scenario

Suitable for TCEC 9 superfinal (big hardware, very long TC)

Consistent and natural patch that extends to counterMoveHistory
what we already do for remaining history tables, that are all per-thread

Non functional change for the common case of a single core

Very simple (just 6 lines modified, no added ones)

BAD:

Small regression (within 2-3 ELO) with few threads and short TC

bench: 5341477
2016-09-16 08:15:07 +02:00
Marco Costalba b96dd754ed Renaming in MovePicker
Rename stages and simplify a bit the code.

No functional change.
2016-09-15 09:07:49 +02:00
Marco Costalba 01ee509a5c Retire MovePicker::see_sign()
No more used after last patch.

No functional change.
2016-09-14 15:43:56 +02:00
VoyagerOneandMarco Costalba 95ad2b51b7 Tweak SEE margin in pruning conditions
Use 35 * depth^2 to calculate see_margin.

STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 22636 W: 4212 L: 3990 D: 14434

LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 47241 W: 6314 L: 6041 D: 34886

The Movepick SEE is now dead code, retire it.

Bench: 5341477
2016-09-14 15:38:38 +02:00
syzygyandMarco Costalba 438805aee8 Integrate next_stage() logic into next_move()
Measured bench speed up goes from 0,7% to 2%,
given the unreliable measure a reverse simmplification
test was done on fishtest:

master vs patch
LLR: -2.94 (-2.94,2.94) [-3.00,1.00]
Total: 15499 W: 2685 L: 2867 D: 9947

Test result is positive, master is weaker.

No functional change.
2016-09-13 07:14:09 +02:00
Guenther DemetzandMarco Costalba ace8e951d7 Simplify code for pinaware SEE
This is the most compact and neatest version
is was able to produce.

On normal builds I have a small slowdown:
normal builds base vs. simplification (gcc 4.8.1 Win7-64 i7-3770 @ 3.4GHz x86-64-modern)
Results for 20 tests for each version:

        Base      Test      Diff
Mean    1974744   1969333   5411
StDev   11825     10281     5874
p-value: 0,178
speedup: -0,003

On pgo-builds however I measure a nice 1.1% speedup

pgo-builds base vs. simplification
Results for 20 tests for each version:

        Base      Test      Diff
Mean    1974119   1995444   -21325
StDev   8703      5717      4623
p-value: 1
speedup: 0,011

No functional change.
2016-09-12 15:45:00 +02:00
Guenther DemetzandMarco Costalba 90ce24b11e Pinned aware SEE
Don't allow pinned pieces to attack the exchange-square as long all
pinners (this includes also potential ones) are on their original
square.
As soon a pinner moves to the exchange-square or get captured on it, we
fall back to standard SEE behaviour.

This correctly handles the majority of cases with absolute pins.

bench: 6883133
2016-09-12 09:31:09 +02:00
Stefano CardanobileandMarco Costalba 4c95edddbf Reorder evaluation start
In evaluate, we start by initializing the pos.psq_score
and adding the material imbalance. After that, we check
whether a specialized eval exists and if yes we return
that value and discard whatever we have computed until now.

It sounds more logical to first probe material entry and
return if we have a specialized eval, and only if it is
not the case initialize eval with some values. There is
no measurable speed-difference on my computer.

Non functional change.
2016-09-11 07:42:12 +02:00
Marco Costalba 602d7fbb07 Use Movepick SEE value in search
This halves the calls to the costly pos.see_sign(),
speed up is about 1-1.3%

Non functional change.
2016-09-09 17:11:54 +02:00
Marco Costalba d909d10f33 Refactor previous patch
No functional change.
2016-09-08 06:02:42 +02:00
ajithcjandMarco Costalba 38428ada54 Prune dangerous moves at low depth
At very low depths prune captures,
promotions and checks if see is negative.

STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 6772 W: 1328 L: 1173 D: 4271

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 8917 W: 1270 L: 1122 D: 6525

bench: 6024713
2016-09-08 05:55:10 +02:00
Marco Costalba e340ce221c Syntactic sugar to loop across pieces
Also add some comments to the new operator~(Piece).

No functional change.
2016-09-04 15:33:17 +02:00
syzygyandMarco Costalba ca6c9f85a5 Change from [Color][PieceType] to [Piece]
Speed up of almost 1% in both normal and
pgo builds.

No functional change.
2016-09-04 09:22:09 +02:00
Marco Costalba c5828c4eba Fix syzygy with partial TB
In case we have installed a not complete set of 6-men tables and
there is 6 piece position on board, but no corresponding
tablebase engine is not using any syzygy at all.

Reported by Jouni Uski, fix by Peter Österlund,
confirmed as a bug by Ronald de Man.

bench: 7591630
2016-09-03 08:21:05 +02:00
Stéphane Nicolet d37dfe9ae4 Space bonus in presence of open files
If the opponent has a cramped position, opening a file often
helps him/her to exchange pieces, so it makes sense to reduce
the space bonus if there are open files.

Credits: Leonardo Ljubičić for the strategic idea, Alain Savard for the
implementation of the open files calculation, "CrunchyNYC" for the
compensation of the numerator.

STC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 49112 W: 9239 L: 8900 D: 30973

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 89415 W: 12014 L: 11601 D: 65800

Bench: 7591630
2016-09-03 00:04:20 +02:00
lucasartandMarco Costalba 13b4444d9e Change exclusion key setup
Should depend on which move is excluded. This
allow us to remove the dedicated Position::exclusion_key().

STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 59814 W: 11136 L: 11083 D: 37595

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 31023 W: 4187 L: 4080 D: 22756

bench 7553379
2016-09-02 08:37:01 +02:00
Stefano80andMarco Costalba 7f2eb10e93 Retire linear imbalance
Retire linear imbalance and compensate
in piece values enumeration.

STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 43596 W: 8105 L: 8023 D: 27468

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 24482 W: 3352 L: 3237 D: 17893

Bench: 7777707
2016-09-02 08:25:17 +02:00
ajithcjandMarco Costalba 5cffc032da Optimize order of a few conditions in search
Also fix size of KingDanger array to reduce memory footprint.

Small speed up of around 0.5%

No functional change.
2016-08-31 13:47:45 +02:00
VoyagerOneandMarco Costalba 2731bbaf6b Remove condition on killers in history pruning
Now allows main killer to be history prune.

STC:
LLR: 2.94 (-2.94,2.94) [-3.00,1.00]
Total: 15852 W: 2910 L: 2781 D: 10161

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 56428 W: 7610 L: 7537 D: 41281

Bench: 8032058
2016-08-30 09:09:55 +02:00
Stefan GeschwentnerandMarco Costalba 6aa9308f08 Tweak probcut threshold
Use better threshold for capture move generation.

STC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 23265 W: 4415 L: 4188 D: 14662

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,4.00]
Total: 36618 W: 5083 L: 4836 D: 26699

bench: 7030088
2016-08-29 22:15:03 +02:00
Andrew GrantandMarco Costalba b4f6728e61 Removed an extra space
No functional change.
2016-08-28 09:47:30 +02:00
Alain SAVARDandMarco Costalba 2b57b61cb1 Move king tropism to evaluate_king
No functional change.
2016-08-28 08:49:40 +02:00
Marco Costalba 1ee2838214 Retire CheckInfo
Move its content directly under StateInfo.

Verified for no speed regression.

No functional change.
2016-08-28 08:08:13 +02:00
Marco Costalba 0b944c7186 Silence some warnings with MSVC 2013
No functional change.
2016-08-27 12:16:13 +02:00
Stéphane NicoletandMarco Costalba 805afcbf3d Move CheckInfo under StateInfo
This greately simplifies usage because hides to the
search the implementation specific CheckInfo.

This is based on the work done by Marco in pull request #716,
implementing on top of it the ideas in the discussion: caching
the calls to slider_blockers() in the CheckInfo structure,
and simplifying the slider_blockers() function by removing its
first parameter.

Compared to master, bench is identical but the number of calls
to slider_blockers() during bench goes down from 22461515 to 18853422,
hopefully being a little bit faster overall.

archlinux, gcc-6
make profile-build ARCH=x86-64-bmi2
50 runs each

bench:
base = 2356320 +/- 981
test = 2403811 +/- 981
diff = 47490 +/- 1828

speedup = 0.0202
P(speedup > 0) = 1.0000

perft 6:
base = 175498484 +/- 429925
test = 183997959 +/- 429925
diff = 8499474 +/- 469401

speedup = 0.0484
P(speedup > 0) = 1.0000

perft 7 (but only 10 runs):
base = 185403228 +/- 468705
test = 188777591 +/- 468705
diff = 3374363 +/- 476687

speedup = 0.0182
P(speedup > 0) = 1.0000

$ ./pyshbench ../Stockfish/master ../Stockfish/test 20
run base     test     diff
...

base = 2501728 +/- 182034
test = 2532997 +/- 182034
diff = 31268 +/- 5116

speedup = 0.0125
P(speedup > 0) = 1.0000

No functional change.
2016-08-27 09:53:26 +02:00
Marco Costalba 4c5cbb1b14 Make engine ONE_PLY value independent
This non-functional change patch is a deep work to allow SF to be independent
from the actual value of ONE_PLY (currently set to 1). I have verified SF is
now independent for ONE_PLY values 1, 2, 4, 8, 16, 32 and 256.

This patch gives consistency to search code and enables future work, opening
the door to safely tweaking the ONE_PLY value for any reason.

Verified for no speed regression at STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 95643 W: 17728 L: 17737 D: 60178

No functional change.
2016-08-27 09:12:25 +02:00
gamanderandMarco Costalba 133808851d Fixed wrong definition of WhiteCamp and BlackCamp
No functional change.
2016-08-27 08:48:07 +02:00
hximandMarco Costalba 9bd856e28e Simplify stats update
Simplify code by moving countermove and follow-up move
history update into procedure.

No functional change.
2016-08-27 08:39:32 +02:00
Marco Costalba 3e739f883f Reformat stats update
Rewritten in a way to have explicit in the search
the bonus/penalty we apply: hopefully this will lead
to further simplification/fix of current rather messy
stats update code.

No functional change.
2016-08-25 09:51:50 +02:00
VoyagerOneandMarco Costalba 0b4f6e562b Refutation penalty on captures
Apply refutation penalty for prior PV quiet move on captures

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 8208 W: 1153 L: 1008 D: 6047
http://tests.stockfishchess.org/tests/view/57bc5a9f0ebc59030fbe47b5

Only LTC because a very similar patch already passed STC + LTC

bench: 7038730
2016-08-24 08:26:32 +02:00
VoyagerOneandMarco Costalba 5596492f6e Simplify IID
STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 30468 W: 5687 L: 5582 D: 19199
http://tests.stockfishchess.org/tests/view/57b1ddd80ebc591c761f63e2

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 87406 W: 11756 L: 11725 D: 63925
http://tests.stockfishchess.org/tests/view/57b212590ebc591c761f63f9

bench: 6554900
2016-08-19 08:59:18 +02:00
VoyagerOneandMarco Costalba 8493c8c6b8 Do LMR on captures
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 5361 W: 1086 L: 936 D: 3339
http://tests.stockfishchess.org/tests/view/57b31b0f0ebc591c761f643d

LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 54694 W: 7591 L: 7287 D: 39816
http://tests.stockfishchess.org/tests/view/57b3442b0ebc591c761f6450

bench: 6881120
2016-08-19 08:50:14 +02:00
Marco Costalba e3af492142 Remove a stale assignment
No more used after previous patch.

Spotted by Jekaa .

No functional change.
2016-08-18 10:40:56 +02:00
Stefano80andMarco Costalba 9585f8ef58 Retire pawn span
Retire pawn span and replace with pawn count in evaluate_scale_factor.

STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 26482 W: 4929 L: 4818 D: 16735

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 61938 W: 8400 L: 8335 D: 45203

Bench: 7662861
2016-08-18 10:16:56 +02:00
VoyagerOneandMarco Costalba 7396c08b79 Use predicted depth for history pruning
STC: (Yellow)
LLR: -2.96 (-2.94,2.94) [0.00,4.00]
Total: 69115 W: 12880 L: 12797 D: 43438

LTC:
LLR: 2.96 (-2.94,2.94) [0.00,4.00]
Total: 124163 W: 16923 L: 16442 D: 90798

Note: Note based off past experiments / patches... history pruning
is quite TC sensitive. I believe the reason for this TC dependency
is that the CMH/FMH is a very large table that takes time to fill
up with. In addition having more time for will increase the accuracy
of the stats' value.

Bench: 7351698
2016-08-15 14:45:40 +02:00
Luca BrivioandMarco Costalba 3d10cfcdd2 Cap space evaluation bonus
When computing space evaluation, limit the bonus square count to 16.

STC @ 10+0.1 th 1:
LLR: 2.97 (-2.94,2.94) [0.00,5.00]
Total: 30793 W: 5910 L: 5648 D: 19235

LTC @ 60+0.6 th 1:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 31361 W: 4410 L: 4184 D: 22767

Bench: 7165385
2016-08-13 10:12:09 +02:00
Alain SAVARDandMarco Costalba 8abb98455f Simplify space formula
STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 16868 W: 3260 L: 3132 D: 10476

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 16910 W: 2381 L: 2255 D: 12274

bench: 6663531
2016-08-10 12:49:24 +02:00
VoyagerOneandMarco Costalba b3525fa9ea Use Color-From-To history stats to help sort moves
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 33502 W: 6498 L: 6223 D: 20781
http://tests.stockfishchess.org/tests/view/578abb940ebc5972faa169e2

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 50782 W: 7124 L: 6832 D: 36826
http://tests.stockfishchess.org/tests/view/578b8e5d0ebc5972faa169fd

LTC: (Sanity test against latest master)
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 32759 W: 4600 L: 4370 D: 23789
http://tests.stockfishchess.org/tests/view/5798b7d30ebc591c761f5b72

bench: 6985912

P.S. Thanks @mstembera for rewriting my code to make it smp compatible. A BIG thank you!
2016-08-02 09:17:14 +02:00
VoyagerOneandMarco Costalba 85924db496 Futility tweak
Use a different margin for pruning child nodes.

STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 16692 W: 3251 L: 3051 D: 10390
http://tests.stockfishchess.org/tests/view/579b95d10ebc591c761f5c03

LTC:
LLR: 2.97 (-2.94,2.94) [0.00,5.00]
Total: 24140 W: 3501 L: 3297 D: 17342
http://tests.stockfishchess.org/tests/view/579bb15d0ebc591c761f5c0b

Bench: 7927017
2016-08-01 21:47:42 +02:00
ajithcjandMarco Costalba f2f3a06a1a Allow null pruning at depth 1
This removes a check that prevents null pruning at depth 1 PLY.

STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 23445 W: 4638 L: 4521 D: 14286

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 61416 W: 8627 L: 8563 D: 44226

bench: 8145304
2016-07-24 09:54:07 +02:00
Stefano80andMarco Costalba 714329dbdc See prune at higher depth
Allow SEE pruning at higher depths in shallow depth
pruning using a threshold increasing with depth.

STC
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 35366 W: 7011 L: 6724 D: 21631

LTC
LLR: 2.97 (-2.94,2.94) [0.00,5.00]
Total: 15578 W: 2243 L: 2070 D: 11265

Bench: 8417887
2016-07-24 08:55:58 +02:00
ajithcjandMarco Costalba 76971d8acb Gradually relax the NMP staticEval check
Gradually relax the NMP staticEval check as we go to
higher depths.

Use tuned values.

STC
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 16745 W: 3371 L: 3168 D: 10206

LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 5906 W: 875 L: 736 D: 4295

bench: 8548212
2016-07-16 07:35:31 +02:00
Lyudmil AntonovandMarco Costalba f619f1d38c Workaround gcc stack alignment bug
GCC uses SSE instructions to move data but in 32-bit gcc version used
by abrok the stack is not 16-byte aligned due to a bug.

This patch workaround teh bug by not using the stack
to store KingFlank[]

Fixes issue #721.

No functional change.
2016-07-16 07:06:13 +02:00
joergosterandMarco Costalba 82d02a3133 Fix extract_ponder_from_tt()
Checking for legality of a possible ponder move
must be done before we undo the first pv move,
of course. (spotted by mohammed li.)

This obviously only has any effect when playing in ponder mode.

No functional change.
2016-07-12 08:47:21 +02:00
Stéphane NicoletandMarco Costalba 1d09ee70f7 King tropism
Bonus for each square that we attack in the flank where the opponent
king is. Squares that we attack twice and are not protected by an enemy
pawn count double.

Passed STC:
http://tests.stockfishchess.org/tests/view/577dfca60ebc5972faa166b8
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 48373 W: 9832 L: 9481 D: 29060

And LTC:
http://tests.stockfishchess.org/tests/view/577e77870ebc5972faa166df
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 8881 W: 1408 L: 1255 D: 6218

Bench: 7577046
2016-07-08 09:35:22 +02:00
Alain SAVARDandMarco Costalba eb20a87c67 More safe checks
Consider a check given by a rook or a minor to be a "safe check"
also in the case where supported by another piece,
and given on a square only defended by a queen

Was yellow STC
http://tests.stockfishchess.org/tests/view/576fcbc80ebc5972faa163e8
LLR: -2.96 (-2.94,2.94) [0.00,5.00]
Total: 55453 W: 10431 L: 10315 D: 34707

Passed LTC
http://tests.stockfishchess.org/tests/view/57733a0b0ebc5972faa164b7
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 54550 W: 7671 L: 7365 D: 39514

bench: 7398346
2016-07-07 08:39:58 +02:00
ajithcjandMarco Costalba ade3bb9a4e Use staticEval in null prune condition
Don't null prune at depth < 12 if staticEval < beta

STC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 62858 W: 12035 L: 11632 D: 39191

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 49784 W: 7009 L: 6720 D: 36055

bench: 8054611
2016-07-02 16:43:49 +02:00
loco-locoandMarco Costalba 716a145a6c Removing inCheck condition for counter move bonus
STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 20206 W: 3946 L: 3823 D:

LTC:
LLR: 3.10 (-2.94,2.94) [-3.00,1.00]
Total: 25004 W: 3512 L: 3390 D: 18102

Bench: 8172428
2016-06-26 10:25:05 +02:00
Marco Costalba 190d2ea4bc Restore standard passed pawn definition
Use the usual and accepted passed pawn semantic
instead of a non-standard one and remove a FIXME.

STC (http://tests.stockfishchess.org/tests/view/576401350ebc5972faa1608d):
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 29646 W: 5663 L: 5557 D: 18426

LTC (http://tests.stockfishchess.org/tests/view/5764e4e90ebc5972faa160c3):
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 40224 W: 5578 L: 5484 D: 29162

bench: 7543902
2016-06-25 11:46:17 +02:00
ElbertoOneandMarco Costalba c2c0e6b07d Remove redundant PvNode condition
After commit 6d58bf777c we always call PvNodes
with cutNode set to false.

No functional change.
2016-06-24 08:46:36 +02:00
VoyagerOneandMarco Costalba c94145b65c Comment out a redundant condition
Take advantage that VALUE_NONE = 32002 to remove
the condition.

Commented out and not removed becuase it is tricky
to rely on the hidden value of VALUE_NONE and code
can break in case we change VALUE_NONE in the future.

No functional change.
2016-06-24 08:26:39 +02:00
Jonathan CalovskiandMarco Costalba 09efbf915e Remove scalefactor dependency
STC
http://tests.stockfishchess.org/tests/view/5764539e0ebc5972faa160a4
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 43878 W: 8289 L: 8208 D: 27381

LTC
http://tests.stockfishchess.org/tests/view/5764f0130ebc5972faa160c9
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 39338 W: 5408 L: 5313 D: 28617

bench: 7977279
2016-06-21 09:01:39 +02:00
ElbertoOneandMarco Costalba 6d58bf777c On IID do not always search with cutNode = true
On IID now search with cutNode value instead of fixed value true.

STC (http://tests.stockfishchess.org/tests/view/575fa3860ebc5972faa15f67):
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 163974 W: 30744 L: 30874 D: 102356

LTC (http://tests.stockfishchess.org/tests/view/5763b0640ebc5972faa16075):
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 48363 W: 6611 L: 6528 D: 35224

Bench: 7806393
2016-06-19 18:04:22 +02:00
VoyagerOneandMarco Costalba 1c0c4db677 Simplify Check Extension
STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 32704 W: 6146 L: 6045 D: 20513

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 146622 W: 19967 L: 20017 D: 106638

Bench: 8245662
2016-06-18 08:32:25 +02:00
ajithcjandMarco Costalba 8e45e70e55 Don't insert pv back into tt
This code was added before the accurate pv patch, when
we retrieved PV directly from TT.

It's not required for correct (and long) PVs any more and
should be safe to remove it.

Also, allowing helper threads to repeatedly over-write
TT doesn't seem to make sense(that was probably an un-intended
side-effect of lazy smp). Before Lazy SMP only Main Thread used
to run ID loop and insert PV into TT.

STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 74346 W: 13946 L: 13918 D: 46482

LTC
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 47265 W: 6531 L: 6447 D: 34287

bench: 8819179
2016-06-15 08:55:40 +02:00
lucasartandMarco Costalba 126036abb0 Do not hardcode Debug Log File
Allow to specifiy the log file name, this comes
handy in case of self-matches so that each SF
instance writes into a different log file.

No functional change.
2016-06-15 08:47:08 +02:00
Marco Costalba ca14345ba2 Filter root moves filter before copy to threads
Currently root moves are copied to all teh threads
but are DTZ filtered only in main thread at the
beginning of teh search.

This patch moves the TB filtering before the
copy of root moves fixing issue #679

https://github.com/official-stockfish/Stockfish/issues/679

No bench change.
2016-06-11 09:24:40 +02:00
VoyagerOneandMarco Costalba 7d2a79f037 Stat Formula Tweak
bonus = d * d + 2 * d - 2

STC:
LLR: 2.94 (-2.94,2.94) [0.00,4.00]
Total: 99444 W: 18274 L: 17778 D: 63392

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,4.00]
Total: 89757 W: 12285 L: 11896 D: 65576

bench: 8276130
2016-06-10 07:27:47 +02:00
VoyagerOneandMarco Costalba 7c5d724724 Tweak check extension condition
There are two concepts with this patch:

Limit check extensions by using move count.
The idea is to limit search explosion.

Always extend check if the first move gives check.
The idea is to save expensive SEE calls, since the vast
majority of first move will have SEE value >= 0, also
first move may still be strong even if the SEE is negative.

STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 16503 W: 3068 L: 2873 D: 10562

LTC:
LLR: 2.97 (-2.94,2.94) [0.00,5.00]
Total: 37202 W: 5261 L: 5014 D: 26927

bench: 8543366
2016-06-10 07:15:56 +02:00
Alain SAVARDandMarco Costalba 6fed8ff22a Small Queen simplification
Moving a few lines from evaluate_threats to evaluate_pieces allows to
a) Remove a condition pos.count<QUEEN>(Them) == 1
b) use precalculated s instead of pos.square(Them)
c) do not check the condition at all in queenless endings

Passed STC
http://tests.stockfishchess.org/tests/view/5752e0410ebc59029919b1f4
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 67175 W: 12194 L: 12152 D: 42829

and LTC
http://tests.stockfishchess.org/tests/view/57587b140ebc59029919b2f4
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 20276 W: 2774 L: 2653 D: 14849

bench: 7907962
2016-06-10 06:53:07 +02:00
mstemberaandMarco Costalba 0c076f1136 Avoid some redundant scaling function calls
Posted by Mohammed Li here:
https://groups.google.com/forum/?fromgroups=#!topic/fishcooking/N-PHfN0O79o

No functional change.
2016-06-10 06:43:37 +02:00
ElbertoOneandMarco Costalba e48c7547c5 LMR reduction parameter tweak
More reduction for cut nodes, less for moves that escape a capture:

STC (http://tests.stockfishchess.org/tests/view/57548c1e0ebc59029919b247):
LLR: 2.96 (-2.94,2.94) [0.00,4.00]
Total: 60165 W: 11519 L: 11149 D: 37497

LTC (http://tests.stockfishchess.org/tests/view/57555b570ebc59029919b260):
LLR: 2.95 (-2.94,2.94) [0.00,4.00]
Total: 10353 W: 1493 L: 1317 D: 7543

Bench: 8902859
2016-06-07 15:15:49 +02:00
Marco Costalba 6e2ca97d93 Fix syzygy DTZ bug
In this position: 3K4/8/3k4/8/4p3/4B3/5P2/8 w - - 0 5

Current DTZ probe returns 1 instead of 15

What happens is that the double push f4 is erroneously detected as a win move.

After the push we have:

[D]3K4/8/3k4/8/4pP2/4B3/8/8 b - f3 0 5

And here the code misses the possible ep capture exf3.

The bug is in probe_dtz_no_ep() where is used probe_ab() that is
blind to ep captures so it returns v == 2 (win) for position

3K4/8/3k4/8/4pP2/4B3/8/8 b - f3 0 5

Note that at the caller site the original position did not have any
possible ep capture, so probe_dtz() returns immediately after calling
probe_dtz_no_ep().

The fix is to call the ep-aware probe_wdl() instead of probe_ab()

I have verified that DTZ is correct now and also there are no more
mistmatches compared to the new 'syzygy' branch. Tested on a set of
more than 600 endgame positions, included some tricky ones.

For people interested to redo the test or doing additional tests
please pull branch tb_dbg from https://github.com/mcostalba/Stockfish repo.

bench: 8450534 (bench unaffected because syzygy is not exercized during bench)
2016-06-07 15:06:26 +02:00
VoyagerOneandMarco Costalba 5f096e9bef Simplify Futility Pruning
Don't update bestValue when futility pruning.

STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 21933 W: 4031 L: 3912 D: 13990

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 46225 W: 6115 L: 6028 D: 34082

Bench: 8450534
2016-06-03 19:58:42 +02:00
ElbertoOneandMarco Costalba 20023ac9b8 LMR Simplification
LMR simplification that also gives a slight ELO gain, especially at LTC:

STC (http://tests.stockfishchess.org/tests/view/574ec8e20ebc59029919b147):
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 32402 W: 5967 L: 5866 D: 20569

LTC (http://tests.stockfishchess.org/tests/view/574fbebf0ebc59029919b16d):
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 15103 W: 2103 L: 1975 D: 11025

Bench: 8248133
2016-06-03 19:53:04 +02:00
joergosterandMarco Costalba 3549d98d07 Tuned values for piece check and attack unit factors
A middle ground patch of two successful tuning patches,
one at STC, the other at LTC, which now passed both.

LLR: 2.95 (-2.94,2.94) [0.00,4.00]
Total: 67893 W: 12777 L: 12384 D: 42732

LLR: 2.95 (-2.94,2.94) [0.00,4.00]
Total: 30165 W: 4189 L: 3960 D: 22016

bench: 9209507
2016-06-03 07:39:27 +02:00
Stéphane NicoletandMarco Costalba 07b247f943 Pins or discovered attacks on the opponent's queen
Bonus for pins or discovered attacks on the opponent's queen

STC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 32020 W: 5914 L: 5652 D: 20454

LTC:
LLR: 2.97 (-2.94,2.94) [0.00,5.00]
Total: 10946 W: 1530 L: 1375 D: 8041

Bench: 7031649
2016-05-28 14:57:58 +02:00
Stéphane NicoletandMarco Costalba abac509ccb Teach check_blockers to check also non-king pieces
This is a prerequisite for next patch

No functional change.
2016-05-28 14:52:21 +02:00
Stéphane NicoletandMarco Costalba ab0f4c0353 Simplify doubled pawn
Only use doubled pawn malus when the doubled pawns are on consecutive squares.

Passed STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 7678 W: 1469 L: 1325 D: 4884

And LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 26739 W: 3562 L: 3449 D: 19728

Bench: 8211685
2016-05-26 12:33:44 +02:00
Leonid PechenikandMarco Costalba 71bfbb22fc More detailed dependence of time allocation on the magnitude of score change
10+0.1:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 5657 W: 1130 L: 979 D: 3548

60+0.6:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36884 W: 5002 L: 4762 D: 27120

bench: 8428997
2016-05-20 19:44:50 +02:00
loco-locoandMarco Costalba 7cb8cbb403 Assorted pruning tweaks
LTC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 38257 W: 5206 L: 4961 D: 28090

STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 16550 W: 3110 L: 2914 D: 10526

Bench: 8428997
2016-05-20 19:34:49 +02:00
mstemberaandMarco Costalba 0784bd542b Fix a multiPV bug in lazy SMP
Where the helper threads were not doing multiPV at all.

Regression tested sprt @ 5+0.05 th 7

LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 73918 W: 11891 L: 11853 D: 50174

bench: 8716243
2016-05-14 21:34:55 +02:00
Stéphane NicoletandMarco Costalba 16c603ce9b Double pawn simplification
Try doubled pawn simplification, with psq
table compensation.

STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 36094 W: 6558 L: 6463 D: 23073

LTC:
LLR: 2.94 (-2.94,2.94) [-3.00,1.00]
Total: 102352 W: 13417 L: 13404 D: 75531

Bench: 8716243
2016-05-13 13:01:59 +02:00
loco-locoandMarco Costalba 969982406c Merge good and bad quiets
STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 58613 W: 10779 L: 10723 D: 37111

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 33608 W: 4539 L: 4436 D: 24633

Bench: 9441294
2016-05-10 18:15:16 +02:00
Alain SAVARDandMarco Costalba 4b9ed6566a Unsafe checks
Introducing a new multi-purpose penalty related to King safety, which
includes all kind of potential checks (from unsafe or unavailable
squares currently occupied by some other piece)

This will indirectly detect and reward some pins, discovered checks, and
motifs such as square vacation, or rook behind its pawn and aligned with
King (example Black Rg8, g7 against Kg1),
and penalize some pawn blockers (if they move, it allows a discovered
check by the pawn).

And since it looks also at protected squares, it detects some potential
defense overloading.

Finally, the rook contact checks had been removed some time ago. This
test will give a small bonus for them, as well as for bishop contact
checks.

Passed STC
http://tests.stockfishchess.org/tests/view/5729ec740ebc59301a354b36
LLR: 2.94 (-2.94,2.94) [0.00,5.00]
Total: 13306 W: 2477 L: 2296 D: 8533

and LTC
http://tests.stockfishchess.org/tests/view/572a5be00ebc59301a354b65
LLR: 2.97 (-2.94,2.94) [0.00,5.00]
Total: 20369 W: 2750 L: 2565 D: 15054

bench: 9298175
2016-05-06 20:04:57 +02:00
Marco Costalba 5e4cd3fc0d Retire __popcnt64 intrinsic
Just use _mm_popcnt_u64() that is available
both for MSVC abd Intel compiler.

Verified on MSVC that the produced assembly
has the hardware 'popcnt' instruction.

No functional change.
2016-05-05 09:09:07 +02:00
VoyagerOneandMarco Costalba 5486911e01 Simplify History LMR Formula
STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 41713 W: 7589 L: 7504 D: 26620

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 41353 W: 5484 L: 5391 D: 30478

Bench: 8946983
2016-05-05 08:53:50 +02:00
Marco Costalba 3487eb9f9e Fix a warning with MSVC
Introduced by 2dd24dc4e6 ("Use popcount intrinsic with Intel")

No functional change.
2016-05-01 15:10:33 +02:00
joergosterandMarco Costalba dc0030de4f Fix LazySMP when searching to a fixed depth.
Currently, helper threads will only search up to the
specified depth limit. Now let them search until the
main thread has finished the specified depth.

On the other hand, we don't want to pick a thread with
a higher search depth.

This may be considered cheating. ;-)

No functional change.
2016-05-01 14:30:50 +02:00
erbsenzaehlerandMarco Costalba 2dd24dc4e6 Use popcount intrinsic with Interl compiler
It seems that icc used our fallback version of popcount.
Now use intrinsics.

icc version 16.0.2 (gcc version 5.3.0 compatibility)
bmi2 compile
uname -r 4.5.1-1-ARCH

20xbench gives a nice speedup
./stockfish-icc-master 2161515 +- 34462
./stockfish-icc-sse42 2260857 +- 50349
2016-05-01 14:18:16 +02:00
KrgpandMarco Costalba 8f934dff9a Remove useless -mbmi flag in Makefile
I could not find anything documented that is necessary that prepending -mbmi to -mbmi2 gives some benefit.
Instead at
https://gcc.gnu.org/onlinedocs/gcc/x86-Built-in-Functions.html#x86-Built-in-Functions

The following built-in functions are available when -mbmi is used. All of them generate the machine instruction that is part of the name.
unsigned int __builtin_ia32_bextr_u32(unsigned int, unsigned int);
unsigned long long __builtin_ia32_bextr_u64 (unsigned long long, unsigned long long);

The following built-in functions are available when -mbmi2 is used. All of them generate the machine instruction that is part of the name.
unsigned int _bzhi_u32 (unsigned int, unsigned int)
unsigned int _pdep_u32 (unsigned int, unsigned int)
unsigned int _pext_u32 (unsigned int, unsigned int)
unsigned long long _bzhi_u64 (unsigned long long, unsigned long long)
unsigned long long _pdep_u64 (unsigned long long, unsigned long long)
unsigned long long _pext_u64 (unsigned long long, unsigned long long)

and at
https://gcc.gnu.org/ml/gcc/2014-02/msg00204.html

( "... The real optimization comes from being able to use pext
(parallel bit extract), which can implement several bextr expressions in
parallel.")

Apart from that we don't use all -msse -msse2 -msse3 -msse4.2 etc. but just -msse3 (or -msse4.2) only.

As regards to the speedup within noise level - this pull request is actually reversal of mcostalba#198 wherein prepending -mbmi to -mbmi2 was claimed to be 0.3% faster and here (removing -mbmi) gives 0.4% speed gain.
2016-05-01 14:11:28 +02:00
Stéphane NicoletandJoona Kiiski 2694ef23c3 Isolated pawn simplification
STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 117822 W: 21697 L: 21744 D: 74381

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 92307 W: 12330 L: 12305 D: 67672

Bench: 8813983

Resolves #659
2016-04-30 22:23:22 +01:00
VoyagerOneandJoona Kiiski e082112cfe Use FMHs to assist with LMR formula.
STC:
LLR: 2.99 (-2.94,2.94) [0.00,5.00]
Total: 52232 W: 9654 L: 9304 D: 33274

LTC:
LLR: 2.97 (-2.94,2.94) [0.00,5.00]
Total: 115988 W: 15550 L: 15049 D: 85389

Bench: 7890808

Resolves #651
2016-04-24 01:00:58 +01:00
erbsenzaehlerandJoona Kiiski 4048bae47b Use -O3 for all compilers (including ICC)
There seems to be no benefit from using -fast over -O3 with icc.
So use -O3 everywhere.

No functional change

Resolves #652
2016-04-24 00:55:56 +01:00
DU-jdtoandJoona Kiiski c737062436 Remove some pointless micro-optimizations
Seems to give around 1% speed-up for CPUs with popcnt support.
Seems to give a very minor speed-up for CPUs without popcnt.

No functional change

Resolves #646
2016-04-23 02:04:28 +01:00
Marco Costalba 94e41274bb Fix incorrect draw detection
In this position we should have draw for repetition:

position fen rnbqkbnr/2pppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 moves g1f3 g8f6 f3g1
go infinite

But latest patch broke it.

Actually we had two(!) very subtle bugs, the first is that Position::set()
clears the passed state and in particular 'previous' member, so
that on passing setupStates, 'previous' pointer was reset.

Second bug is even more subtle: SetupStates was based on std::vector
as container, but when vector grows, std::vector copies all its contents
to a new location invalidating all references to its entries. Because
all StateInfo records are linked by 'previous' pointer, this made pointers
go stale upon adding more element to setupStates. So revert to use a
std::deque that ensures references are preserved when pushing back new
elements.

No functional change.
2016-04-18 00:13:16 +02:00
loco-locoandJoona Kiiski ec6aab0136 Add a second level of follow-up moves
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 6438 W: 1229 L: 1077 D: 4132

LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 4000 W: 605 L: 473 D: 2922

bench: 7378965

Resolves #636
2016-04-17 15:19:20 +01:00
Marco Costalba 7eaea3848c StateInfo is usually allocated on the stack by search()
And passed in do_move(), this ensures maximum efficiency and
speed and at the same time unlimited move numbers.

The draw back is that to handle Position init we need to
reserve a StateInfo inside Position itself and use at
init time and when copying from another Position.

After lazy SMP we don't need anymore this gimmick and we can
get rid of this special case and always pass an external
StateInfo to Position object.

Also rewritten and simplified Position constructors.

Verified it does not regress with a 3 threads SMP test:
ELO: -0.00 +-12.7 (95%) LOS: 50.0%
Total: 1000 W: 173 L: 173 D: 654

No functional change.
2016-04-17 08:29:33 +02:00
Niklas FiekasandJoona Kiiski ee7a68ea5f Fix last search info carried over to mate position
When starting search in a mate or stalemate position, Stockfish does not
even care to reinitialize and start worker threads. However after search
all threads are checked for the best move.

This can lead to bestmove and info beeing carried over from the last
search.

Example session:

    setoption name threads value 7
    go movetime 4000
    position startpos moves f2f3 e7e5 g2g4 d8h4
    go movetime 4000

Actual output is like (almost always):

    [...]
    bestmove e2e4
    info depth 0 score mate 0
    info depth 20 seldepth 29 multipv 1 score cp 28 [...] pv e2e4
    bestmove e2e4

Expected output / output after fix:

    [...]
    bestmove e2e4 ponder e7e6
    info depth 0 score mate 0
    bestmove (none)

Resolves #623
2016-04-16 10:22:36 +01:00
Marco Costalba d30994ecd5 Hide global visibility when not needed
Also move PieceValue definition in psqt.cpp,
where it is initialized.

Fix a warning in popcount16() with Intel compiler

No functional change.
2016-04-09 10:42:04 +02:00
Marco Costalba bd04f9a0f1 Fix Travis Cl
Broken after "32-bit/64-bit Makefile fix" commit.

Ubuntu "Precise" 12.04.5 supports multilib only until
g++ 4.6 that is not enough to compile Stockfish.

So move to Ubuntu 14.04.4 LTS (Trusty Tahr)

No functional change.
2016-04-09 09:35:19 +02:00
DU-jdtoandJoona Kiiski 1cbba8d6fa Small passed pawn simplification
STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 21993 W: 4197 L: 4078 D: 13718

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 67213 W: 9135 L: 9077 D: 49001

Bench: 7482426

Resolves #622
2016-04-08 19:48:03 +01:00
Alain SAVARDandJoona Kiiski e9e5f72c74 Undefended King Ring
There was already a penalty for squares only defended by King (undefended)

This test records a penalty for completely undefended squares in the so called extended king-ring
(so if we exclude squares defended by a Kg8 for example, we only look at h6 g6 and f6)

We also exclude squares occupied by opponent pieces in this computation,
based on the following results

Was yellow at STC
LLR: -2.97 (-2.94,2.94) [0.00,5.00]
Total: 112499 W: 20649 L: 20293 D: 71557

and passed LTC
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 36805 W: 5100 L: 4857 D: 26848

Bench: 8430233

Resolves: #619
2016-04-08 19:22:33 +01:00
Alain SAVARDandJoona Kiiski 29b5842da8 Backward simplication
On top of the usual conditions
a) some opponent in front (but no lever)
b) some neighbours (in front) (but no neighbour behind or same rank)
c) < rank_5

to find out if a pawn is backward we look at the squares in front of this pawn to reach the same rank as the next neighbour.

In current master, a pawn is backward if any of those squares is controlled by an enemy pawn on an adjacent file

In this version, a pawn is ALSO backward if any of those squares is occupied by an enemy pawn.

STC:
http://tests.stockfishchess.org/tests/view/56fe7efd0ebc59301a3541f1
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 19051 W: 3557 L: 3433 D: 12061

LTC:
http://tests.stockfishchess.org/tests/view/56febc2d0ebc59301a354209
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 40810 W: 5619 L: 5526 D: 29665

Bench: 7525245

Resolves #614
2016-04-08 19:12:55 +01:00
mstemberaandJoona Kiiski 8fb45caade Simplify popcnt
Also a speedup(about 1%) on 64-bit w/o hardware popcnt

Retire Max15 and Full template parameters
(Contributed by Marco Costalba)

Now that we have just SW and HW versions, use
template default parameter to get rid of explicit
template parameters.

Retire bitcount.h and move the only defined
function to bitboard.h

No functional change

Resolves #620
2016-04-08 18:52:15 +01:00
lucasartandJoona Kiiski 900279a06f 32-bit/64-bit Makefile fix
Counter intuitively, make build ARCH=x86-32 does NOT produce a 32-bit compile
when running a 64-bit OS. Nor would ARCH=x86-64 produce a 64-bit compile when
running a 32-bit OS (assuming it compiled w/o errors).

No functional change

Resolves #621
2016-04-08 18:47:31 +01:00
NicklasPerssonandJoona Kiiski d5b24ad77b A combo patch of two tuning patches
STC:
LLR: 2.96 (-2.94,2.94) [0.00,4.00]
Total: 14223 W: 2700 L: 2494 D: 9029

LTC:
LLR: 2.96 (-2.94,2.94) [0.00,4.00]
Total: 66294 W: 9065 L: 8739 D: 48490

Bench: 7607385

Resolves #612
2016-03-31 13:26:00 +01:00
lucasartandJoona Kiiski f256388e08 Guard against UB in lsb/msb
lsb(b) and msb(b) are undefined when b == 0. This can lead to subtle bugs, where
the resulting code behaves differently on different configurations:
- It can be the home grown software LSB/MSB
- It can be the compiler generated software LSB/MSB (when using compiler
  intrinsics without the right compiler flags to allow compiler to use hardware
  LSB/MSB). Which of course depends on the compiler.
- It can be hardware LSB/MSB generated by the compiler.
- Not to mention that hardware LSB/MSB can return different value on different
  hardware when b == 0.

No functional change

Resolves #610
2016-03-31 13:22:37 +01:00
Marco CostalbaandJoona Kiiski db4b0d8b7d Rewrite bsfq management
Use compiler intrinsics when possible to
avoid writing platform specific asm code.

Tested on Windows 7 with MSVC 2013 and mingw 4.8.3 (32 and 64 bit)
and on Linux Mint with g++ 4.8.4 and clang 3.4 (32 and 64 bit).

No functional change

Resolves #609
2016-03-28 15:46:55 +01:00
snicoletandJoona Kiiski 24dac5ccd3 Bonus for loose enemies
STC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 30504 W: 5743 L: 5485 D: 19276

LTC:
LLR: 2.97 (-2.94,2.94) [0.00,5.00]
Total: 11936 W: 1651 L: 1493 D: 8792

Bench: 8880041

Resolves #606
2016-03-27 20:56:54 +01:00
mbootsectorandJoona Kiiski 8788612828 Raise endgame passed pawn and material values
STC:
LLR: 2.95 (-2.94,2.94) [0.00,4.00]
Total: 136149 W: 25213 L: 24588 D: 86348

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,4.00]
Total: 54637 W: 7533 L: 7238 D: 39866

Bench: 8546808

Resolves #608
2016-03-27 20:22:48 +01:00
fanonandJoona Kiiski 5d1644ba69 Simplify pawns King Safety calculation
STC
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 130209 W: 23516 L: 23581 D: 83112

LTC
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 33541 W: 4563 L: 4460 D: 24518

Bench: 8644370

Resolves #604
2016-03-27 20:08:56 +01:00
VoyagerOneandJoona Kiiski 60590577f2 A small simplification in movepick.h
No functional change

Resolves #597
2016-03-14 20:54:49 -07:00
mstemberaandJoona Kiiski 04be84e0e2 Simplify Safe Checks
STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 11796 W: 2211 L: 2074 D: 7511

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 14324 W: 1935 L: 1806 D: 10583

Bench: 8075202

Resolves #600
2016-03-14 20:51:16 -07:00
Marco CostalbaandJoona Kiiski 647402ff79 Assorted cleanup of latest commits
No functional change.

Resolves #601
2016-03-14 20:42:44 -07:00
Stefan GeschwentnerandJoona Kiiski a273b6ef8c Add followup moves history for move ordering
STC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 7955 W: 1538 L: 1378 D: 5039

LTC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 5323 W: 778 L: 642 D: 3903

Bench: 8261839

Resolves #599
2016-03-10 14:26:06 -08:00
mbootsectorandJoona Kiiski e1a7d135b2 Passed pawn bonus simplification
STC: (yellow)

LLR: -2.96 (-2.94,2.94) [0.00,4.00]
Total: 86114 W: 16063 L: 15921 D: 54130

LTC:

LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 14347 W: 2025 L: 1896 D: 10426

Bench: 8576437

Resolves #595
2016-03-02 19:10:56 +00:00
snicoletandJoona Kiiski c1be0c68c7 Tweak initiative formula
Give more weight to the pawns number and
the vertical king distance in evaluate_initiative()

Passed STC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 26729 W: 5067 L: 4825 D: 16837

and LTC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 60480 W: 8338 L: 8016 D: 44126

Bench: 8295162

Resolves #594
2016-03-02 00:02:03 +00:00
ElbertoOneandJoona Kiiski c13052f344 Clean up depth reduction calculation
Might also be a slight speed up

No functional change

Resolves #593
2016-02-28 13:40:47 +00:00
joergosterandJoona Kiiski 8de29390f2 Pass endgame value to evaluate_scale_factor()
No functional change

Resolves #592
2016-02-28 13:35:34 +00:00
VoyagerOneandJoona Kiiski 45a309d92e Simplify Reduction Formula
Formula now only contains one coefficient. Making it much easier to tune.

STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 187443 W: 34858 L: 35028 D: 117557

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 88329 W: 11982 L: 11953 D: 64394

Bench: 7521394

Resolves #591
2016-02-28 13:31:25 +00:00
Leonid PechenikandJoona Kiiski d5ba8e827d Revert "Remove slowMover"
This reverts commit 77fa960f89.

Resolves #590
2016-02-28 13:25:05 +00:00
IIvecandJoona Kiiski 77fa960f89 Remove slowMover
Removes a slowMover and one paramater from move_importance function.

STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 77023 W: 14456 L: 14433 D: 48134

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 37175 W: 5190 L: 5092 D: 26893

Resolves #589
2016-02-21 20:16:28 +00:00
VoyagerOneandJoona Kiiski bfe9044ad9 History Stat Formula Simplification
STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 67476 W: 12561 L: 12521 D: 42394

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 111923 W: 15147 L: 15149 D: 81627

Bench: 8430465

Resolves #588
2016-02-21 20:12:33 +00:00
VoyagerOneandJoona Kiiski 744ed85a4d Fix futility pruning bug
PredictedDepth can be negative, causing the futility_margin to be negative.
It will be very difficult to tweak moveCount pruning and reduction formula, as they are tuned to prevent this behavior.

No functional change

Resolves #587
2016-02-14 19:48:46 +00:00
hximandJoona Kiiski 56dd58e6f9 Remove Weights
Removed remaining redundant weights for pawn structure,
passed pawns, space and king safety by redistributing them
into individual evaluation terms.

STC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 15173 W: 2790 L: 2659 D: 9724

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 43433 W: 5936 L: 5846 D: 31651

Bench: 7156237

Resolves #586
2016-02-07 21:00:24 +00:00
Marco CostalbaandJoona Kiiski 4f6aa15228 Document HalfDensityMap
No functional change.

Resolves #584
2016-02-07 20:54:25 +00:00
Leonid PechenikandJoona Kiiski aedebe35cf Time management simplification
10+0.1:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 41963 W: 7967 L: 7883 D: 26113

60+0.6:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 132314 W: 17939 L: 17969 D: 96406

Resolves #580
2016-01-29 00:47:07 +00:00
Guenther DemetzandJoona Kiiski 9a10313a9d rotating symmetric patterns with increasing skipsize
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00] sprt @ 5+0.1 th 21
Total: 7068 W: 1121 L: 975 D: 4972

LTC:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00] sprt @ 12+0.12 th 21
Total: 26691 W: 3594 L: 3481 D: 19616

No functional change with a single thread

Resolves #574
2016-01-24 14:08:01 +00:00
Joona KiiskiandJoona Kiiski 8c3a5bbc52 Do not probe syzygy bases when castling is possible
Almost no functional change. Bench is unchanged.

Resolves #230
Resolves #573
2016-01-20 15:24:21 +00:00
lucasartandJoona Kiiski 28933a580e Retire RootNode template
There is no reason to compile 3 different copies of search(). PV nodes are on
the cold path, and PvNode is a template parameter, so there is no cost in
computing:

const bool RootNode = PvNode && (ss-1)->ply == 0;

And this simplifies code a tiny bit as well.

Speed impact is negligible on my machine (i7-3770k, linux 4.2, gcc 5.2):

            nps   +/-
test    2378605  3118
master  2383128  2793
diff      -4523  2746

Bench: 7751425

No functional change.

Resolves #568
2016-01-18 22:21:42 +00:00
Guenther DemetzandJoona Kiiski 12eb345ebd Depth margin parameter-tweak in TT-save
Verified that is improvement with multiple threads:

LLR: 2.95 (-2.94,2.94) [0.00,4.00] sprt @ 30+0.3 th 3
Total: 14817 W: 2103 L: 1915 D: 10799

LLR: 2.96 (-2.94,2.94) [0.00,4.00] sprt @ 15+0.15 th 7
Total: 10264 W: 1498 L: 1321 D: 7445

Verified that is not a significant regression with a single thread:

LLR: 2.96 (-2.94,2.94) [-4.00,0.00] sprt @ 60+0.6 th 1
Total: 23975 W: 3294 L: 3210 D: 17471

Resolves #575
2016-01-18 22:04:38 +00:00
Joona Kiiski ebec2fa48e Remove redundant -std=c++0x flag
This flag is functionally identical to '-std=c++11' flag which
is part of standard flags.

No functional change

Resolves #571
2016-01-18 21:54:40 +00:00
Joona Kiiski 552b3ccb66 Makefile: Allow specifying compiler executable
No functional change

Resolves #570
2016-01-18 21:47:52 +00:00
Marco Costalba 356147d99a Rewrite time formula
Time management is really too complex, our aim is
to simplify it, but for time being at least rewrite
in an understandable way.

No functional change.
2016-01-18 17:12:18 +01:00
Lyudmil AntonovandJoona Kiiski 89723339d9 Assorted English grammar changes
No functional change

Resolves #567
2016-01-16 21:34:29 +00:00
Stefano80andJoona Kiiski 74e2fa97b7 Adjust reductions based on history and cmh tables
STC:
LLR: 4.06 (-2.94,2.94) [0.00,5.00]
Total: 149395 W: 28029 L: 27208 D: 94158

LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 9628 W: 1368 L: 1217 D: 7043

bench: 8076724

Resolves #565
2016-01-13 16:18:35 +00:00
Stefano80andJoona Kiiski dcd8ce7094 Update comments in LMR step
No functional change

Resolves #564
2016-01-13 16:03:53 +00:00
Leonid PechenikandJoona Kiiski 4d1220d672 Tune time management for LTC
60+0.6:
LLR: 2.96 (-2.94,2.94) [0.00,4.00]
Total: 102533 W: 14270 L: 13842 D: 74421

Resolves #558
2016-01-09 09:21:52 +00:00
lucasartandJoona Kiiski aa31f7f096 Retire CenterBind
And compensate in the PSQT.

STC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 27714 W: 5161 L: 5052 D: 17501

LTC:
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 36354 W: 5008 L: 4909 D: 26437

Bench: 8603285

Resolves #556
2016-01-03 17:33:36 +00:00
Alain SAVARDandJoona Kiiski 1de97337c2 Fine tuning of unsupported pawn penalty
Adjust the unsupported pawn penalty when the pawn is supporting 2 pawns
(for example g7 in f6-g7-h6)

Passed STC
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 23833 W: 4384 L: 4158 D: 15291

Passed LTC
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 42711 W: 5918 L: 5655 D: 31138

Bench: 8390233

Resolves #549
2016-01-03 14:15:29 +00:00
Leonid PechenikandJoona Kiiski 9eceb894ac Adjust time used for move based on previous score
Use less time if evaluation is not worse than for previous move and even less time if in addition no fail low encountered for current iteration.

STC: 10+0.1
ELO: 5.37 +-2.9 (95%) LOS: 100.0%
Total: 20000 W: 3832 L: 3523 D: 12645

STC: 10+0.1
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 17527 W: 3334 L: 3132 D: 11061

LTC: 60+0.6
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 28233 W: 3939 L: 3725 D: 20569

LTC: 60+0.6
ELO: 2.43 +-1.4 (95%) LOS: 100.0%
Total: 60000 W: 8266 L: 7847 D: 43887

LTC: 60+0.06
LLR: 2.95 (-2.94,2.94) [-1.00,3.00]
Total: 38932 W: 5408 L: 5207 D: 28317

Resolves #547
2016-01-03 14:01:15 +00:00
Joona Kiiski 5972c4a678 Restore development version 2016-01-03 13:52:01 +00:00
40 changed files with 1693 additions and 1638 deletions
+12 -6
View File
@@ -1,4 +1,6 @@
language: cpp
sudo: required
dist: trusty
matrix:
include:
@@ -7,25 +9,25 @@ matrix:
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-4.8']
packages: ['g++-6', 'g++-6-multilib', 'g++-multilib', 'valgrind']
env:
- COMPILER=g++-4.8
- COMPILER=g++-6
- COMP=gcc
- os: linux
compiler: clang
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7']
packages: ['clang-3.7']
sources: ['ubuntu-toolchain-r-test']
packages: ['clang', 'g++-multilib', 'valgrind']
env:
- COMPILER=clang++-3.7
- COMPILER=clang++
- COMP=clang
- os: osx
compiler: gcc
env:
- COMPILER=g++-4.8
- COMPILER=g++
- COMP=gcc
- os: osx
@@ -47,3 +49,7 @@ script:
- echo "Checking for same bench numbers..."
- diff bench1 bench2 > result
- test ! -s result
# if valgrind is available check the build is without error, reduce depth to speedup testing, but not too shallow to catch more cases.
- if [ -x "$(command -v valgrind )" ] ; then make clean && make ARCH=x86-64 debug=yes build && valgrind --error-exitcode=42 ./stockfish bench 128 1 10 default depth 1>/dev/null ; fi
# use g++-6 as a proxy for having sanitizers ... might need revision as they become available for more recent versions of clang/gcc than trusty provides
- if [[ "$COMPILER" == "g++-6" ]]; then make clean && make ARCH=x86-64 sanitize=yes build && ! ./stockfish bench 2>&1 | grep "runtime error:" ; fi
+2 -1
View File
@@ -1,6 +1,7 @@
### Overview
[![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish)
[![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish)
Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
not a complete chess program and requires some UCI-compatible GUI
@@ -111,4 +112,4 @@ to where the source code can be found. If you make any changes to the
source code, these changes must also be made available under the GPL.
For full details, read the copy of the GPL found in the file named
*Copying.txt*
*Copying.txt*.
+45
View File
@@ -0,0 +1,45 @@
version: 1.0.{build}
clone_depth: 5
branches:
only:
- master
- appveyor
# Operating system (build VM template)
os: Visual Studio 2015
# Build platform, i.e. x86, x64, AnyCPU. This setting is optional.
platform:
- x86
- x64
- Any CPU
# build Configuration, i.e. Debug, Release, etc.
configuration: Debug
matrix:
# The build fail immediately once one of the job fails
fast_finish: true
# Scripts that are called at very beginning, before repo cloning
init:
- cmake --version
- msbuild /version
before_build:
- cd src
- echo project (Stockfish) >> CMakeLists.txt
- echo add_executable(stockfish benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp >> CMakeLists.txt
- echo main.cpp material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp >> CMakeLists.txt
- echo search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp syzygy/tbprobe.cpp) >> CMakeLists.txt
- echo set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src) >> CMakeLists.txt
# - echo target_compile_options(stockfish PUBLIC "/Ox") >> CMakeLists.txt
build_script:
- cmake -G "Visual Studio 14 2015 Win64" .
- cmake --build .
before_test:
- cd Debug
- stockfish.exe bench > null
+77 -55
View File
@@ -22,7 +22,10 @@
### ==========================================================================
### Establish the operating system name
UNAME = $(shell uname)
KERNEL = $(shell uname -s)
ifeq ($(KERNEL),Linux)
OS = $(shell uname -o)
endif
### Executable name
EXE = stockfish
@@ -32,7 +35,7 @@ PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
### Built-in benchmark for pgo-builds
PGOBENCH = ./$(EXE) bench 16 1 1000 default time
PGOBENCH = ./$(EXE) bench
### Object files
OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \
@@ -47,13 +50,12 @@ OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \
# ----------------------------------------------------------------------------
#
# debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode
# sanitize = yes/no --- (-fsanitize ) --- enable undefined behavior checks
# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations
# arch = (name) --- (-arch) --- Target architecture
# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch x86 asm-instruction
# bsfq = yes/no --- -DUSE_BSFQ --- Use bsfq x86_64 asm-instruction (only
# with GCC and ICC 64-bit)
# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt x86_64 asm-instruction
# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction
# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction
# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions
# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction
#
@@ -64,9 +66,9 @@ OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \
### 2.1. General and architecture defaults
optimize = yes
debug = no
sanitize = no
bits = 32
prefetch = no
bsfq = no
popcnt = no
sse = no
pext = no
@@ -96,7 +98,6 @@ ifeq ($(ARCH),x86-64)
arch = x86_64
bits = 64
prefetch = yes
bsfq = yes
sse = yes
endif
@@ -104,7 +105,6 @@ ifeq ($(ARCH),x86-64-modern)
arch = x86_64
bits = 64
prefetch = yes
bsfq = yes
popcnt = yes
sse = yes
endif
@@ -113,7 +113,6 @@ ifeq ($(ARCH),x86-64-bmi2)
arch = x86_64
bits = 64
prefetch = yes
bsfq = yes
popcnt = yes
sse = yes
pext = yes
@@ -122,7 +121,6 @@ endif
ifeq ($(ARCH),armv7)
arch = armv7
prefetch = yes
bsfq = yes
endif
ifeq ($(ARCH),ppc-32)
@@ -153,7 +151,16 @@ ifeq ($(COMP),gcc)
comp=gcc
CXX=g++
CXXFLAGS += -pedantic -Wextra -Wshadow
ifneq ($(UNAME),Darwin)
ifeq ($(ARCH),armv7)
ifeq ($(OS),Android)
CXXFLAGS += -m$(bits)
endif
else
CXXFLAGS += -m$(bits)
endif
ifneq ($(KERNEL),Darwin)
LDFLAGS += -Wl,--no-as-needed
endif
endif
@@ -161,7 +168,7 @@ endif
ifeq ($(COMP),mingw)
comp=mingw
ifeq ($(UNAME),Linux)
ifeq ($(KERNEL),Linux)
ifeq ($(bits),64)
ifeq ($(shell which x86_64-w64-mingw32-c++-posix),)
CXX=x86_64-w64-mingw32-c++
@@ -193,9 +200,20 @@ ifeq ($(COMP),clang)
comp=clang
CXX=clang++
CXXFLAGS += -pedantic -Wextra -Wshadow
ifeq ($(UNAME),Darwin)
CXXFLAGS += -std=c++0x -stdlib=libc++
DEPENDFLAGS += -std=c++0x -stdlib=libc++
ifeq ($(ARCH),armv7)
ifeq ($(OS),Android)
CXXFLAGS += -m$(bits)
LDFLAGS += -m$(bits)
endif
else
CXXFLAGS += -m$(bits)
LDFLAGS += -m$(bits)
endif
ifeq ($(KERNEL),Darwin)
CXXFLAGS += -stdlib=libc++
DEPENDFLAGS += -stdlib=libc++
endif
endif
@@ -211,41 +229,52 @@ else
profile_clean = gcc-profile-clean
endif
ifeq ($(UNAME),Darwin)
ifeq ($(KERNEL),Darwin)
CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.9
LDFLAGS += -arch $(arch) -mmacosx-version-min=10.9
endif
### Travis CI script uses COMPILER to overwrite CXX
ifdef COMPILER
CXX=$(COMPILER)
COMPCXX=$(COMPILER)
endif
### Allow overwriting CXX from command line
ifdef COMPCXX
CXX=$(COMPCXX)
endif
### On mingw use Windows threads, otherwise POSIX
ifneq ($(comp),mingw)
# On Android Bionic's C library comes with its own pthread implementation bundled in
ifneq ($(arch),armv7)
ifneq ($(OS),Android)
# Haiku has pthreads in its libroot, so only link it in on other platforms
ifneq ($(UNAME),Haiku)
ifneq ($(KERNEL),Haiku)
LDFLAGS += -lpthread
endif
endif
endif
### 3.4 Debugging
### 3.2.1 Debugging
ifeq ($(debug),no)
CXXFLAGS += -DNDEBUG
else
CXXFLAGS += -g
endif
### 3.5 Optimization
### 3.2.2 Debugging with undefined behavior sanitizers
ifeq ($(sanitize),yes)
CXXFLAGS += -g3 -fsanitize=undefined
endif
### 3.3 Optimization
ifeq ($(optimize),yes)
ifeq ($(comp),gcc)
CXXFLAGS += -O3
CXXFLAGS += -O3
ifeq ($(UNAME),Darwin)
ifeq ($(comp),gcc)
ifeq ($(KERNEL),Darwin)
ifeq ($(arch),i386)
CXXFLAGS += -mdynamic-no-pic
endif
@@ -254,27 +283,19 @@ ifeq ($(optimize),yes)
endif
endif
ifeq ($(arch),armv7)
ifeq ($(OS), Android)
CXXFLAGS += -fno-gcse -mthumb -march=armv7-a -mfloat-abi=softfp
endif
endif
ifeq ($(comp),mingw)
CXXFLAGS += -O3
endif
ifeq ($(comp),icc)
ifeq ($(UNAME),Darwin)
CXXFLAGS += -fast -mdynamic-no-pic
else
CXXFLAGS += -fast
ifeq ($(KERNEL),Darwin)
CXXFLAGS += -mdynamic-no-pic
endif
endif
ifeq ($(comp),clang)
CXXFLAGS += -O3
ifeq ($(UNAME),Darwin)
ifeq ($(KERNEL),Darwin)
ifeq ($(pext),no)
CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS)
@@ -289,12 +310,12 @@ ifeq ($(optimize),yes)
endif
endif
### 3.6. Bits
### 3.4 Bits
ifeq ($(bits),64)
CXXFLAGS += -DIS_64BIT
endif
### 3.7 prefetch
### 3.5 prefetch
ifeq ($(prefetch),yes)
ifeq ($(sse),yes)
CXXFLAGS += -msse
@@ -304,12 +325,7 @@ else
CXXFLAGS += -DNO_PREFETCH
endif
### 3.8 bsfq
ifeq ($(bsfq),yes)
CXXFLAGS += -DUSE_BSFQ
endif
### 3.9 popcnt
### 3.6 popcnt
ifeq ($(popcnt),yes)
ifeq ($(comp),icc)
CXXFLAGS += -msse3 -DUSE_POPCNT
@@ -318,15 +334,15 @@ ifeq ($(popcnt),yes)
endif
endif
### 3.10 pext
### 3.7 pext
ifeq ($(pext),yes)
CXXFLAGS += -DUSE_PEXT
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -mbmi -mbmi2
CXXFLAGS += -mbmi2
endif
endif
### 3.11 Link Time Optimization, it works since gcc 4.5 but not on mingw under Windows.
### 3.8 Link Time Optimization, it works since gcc 4.5 but not on mingw under Windows.
### This is a mix of compile and link time options because the lto link phase
### needs access to the optimization flags.
ifeq ($(comp),gcc)
@@ -339,7 +355,7 @@ ifeq ($(comp),gcc)
endif
ifeq ($(comp),mingw)
ifeq ($(UNAME),Linux)
ifeq ($(KERNEL),Linux)
ifeq ($(optimize),yes)
ifeq ($(debug),no)
CXXFLAGS += -flto
@@ -349,9 +365,9 @@ ifeq ($(comp),mingw)
endif
endif
### 3.12 Android 5 can only run position independent executables. Note that this
### 3.9 Android 5 can only run position independent executables. Note that this
### breaks Android 4.0 and earlier.
ifeq ($(arch),armv7)
ifeq ($(OS), Android)
CXXFLAGS += -fPIE
LDFLAGS += -fPIE -pie
endif
@@ -365,7 +381,7 @@ help:
@echo ""
@echo "To compile stockfish, type: "
@echo ""
@echo "make target ARCH=arch [COMP=comp]"
@echo "make target ARCH=arch [COMP=compiler] [COMPCXX=cxx]"
@echo ""
@echo "Supported targets:"
@echo ""
@@ -395,11 +411,17 @@ help:
@echo "clang > LLVM Clang compiler"
@echo "icc > Intel compiler"
@echo ""
@echo "Examples. If you don't know what to do, you likely want to run: "
@echo "Simple examples. If you don't know what to do, you likely want to run: "
@echo ""
@echo "make build ARCH=x86-64 (This is for 64-bit systems)"
@echo "make build ARCH=x86-32 (This is for 32-bit systems)"
@echo ""
@echo "Advanced examples, for experienced users: "
@echo ""
@echo "make build ARCH=x86-64 COMP=clang"
@echo "make profile-build ARCH=x86-64-modern COMP=gcc COMPCXX=g++-4.8"
@echo ""
.PHONY: build profile-build
build:
@@ -453,8 +475,9 @@ config-sanity:
@echo "optimize: '$(optimize)'"
@echo "arch: '$(arch)'"
@echo "bits: '$(bits)'"
@echo "kernel: '$(KERNEL)'"
@echo "os: '$(OS)'"
@echo "prefetch: '$(prefetch)'"
@echo "bsfq: '$(bsfq)'"
@echo "popcnt: '$(popcnt)'"
@echo "sse: '$(sse)'"
@echo "pext: '$(pext)'"
@@ -472,7 +495,6 @@ config-sanity:
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "armv7"
@test "$(bits)" = "32" || test "$(bits)" = "64"
@test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
@test "$(bsfq)" = "yes" || test "$(bsfq)" = "no"
@test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
@test "$(sse)" = "yes" || test "$(sse)" = "no"
@test "$(pext)" = "yes" || test "$(pext)" = "no"
+13 -5
View File
@@ -76,7 +76,14 @@ const vector<string> Defaults = {
"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
"8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw
// Mate and stalemate positions
"8/8/8/8/8/6k1/6p1/6K1 w - -",
"5k2/5P2/5K2/8/8/8/8/8 b - -",
"8/8/8/8/8/4k3/4p3/4K3 w - -",
"8/8/8/8/8/5K2/8/3Q1k2 b - -",
"7k/7P/6K1/8/3B4/8/8/8 b - -"
};
} // namespace
@@ -144,10 +151,12 @@ void benchmark(const Position& current, istream& is) {
uint64_t nodes = 0;
TimePoint elapsed = now();
Position pos;
for (size_t i = 0; i < fens.size(); ++i)
{
Position pos(fens[i], Options["UCI_Chess960"], Threads.main());
StateListPtr states(new std::deque<StateInfo>(1));
pos.set(fens[i], Options["UCI_Chess960"], &states->back(), Threads.main());
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;
@@ -156,9 +165,8 @@ void benchmark(const Position& current, istream& is) {
else
{
Search::StateStackPtr st;
limits.startTime = now();
Threads.start_thinking(pos, limits, st);
Threads.start_thinking(pos, states, limits);
Threads.main()->wait_for_search_finished();
nodes += Threads.nodes_searched();
}
@@ -166,7 +174,7 @@ void benchmark(const Position& current, istream& is) {
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
dbg_print(); // Just before to exit
dbg_print(); // Just before exiting
cerr << "\n==========================="
<< "\nTotal time (ms) : " << elapsed
+7 -7
View File
@@ -123,9 +123,9 @@ namespace {
// Immediate win if a pawn can be promoted without getting captured
else if ( us == WHITE
&& rank_of(psq) == RANK_7
&& ksq[us] != psq + DELTA_N
&& ( distance(ksq[~us], psq + DELTA_N) > 1
|| (StepAttacksBB[KING][ksq[us]] & (psq + DELTA_N))))
&& ksq[us] != psq + NORTH
&& ( distance(ksq[~us], psq + NORTH) > 1
|| (StepAttacksBB[KING][ksq[us]] & (psq + NORTH))))
result = WIN;
// Immediate draw if it is a stalemate or a king captures undefended pawn
@@ -166,12 +166,12 @@ namespace {
if (Us == WHITE)
{
if (rank_of(psq) < RANK_7) // Single push
r |= db[index(Them, ksq[Them], ksq[Us], psq + DELTA_N)];
r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH)];
if ( rank_of(psq) == RANK_2 // Double push
&& psq + DELTA_N != ksq[Us]
&& psq + DELTA_N != ksq[Them])
r |= db[index(Them, ksq[Them], ksq[Us], psq + DELTA_N + DELTA_N)];
&& psq + NORTH != ksq[Us]
&& psq + NORTH != ksq[Them])
r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH + NORTH)];
}
return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad;
+22 -7
View File
@@ -21,9 +21,9 @@
#include <algorithm>
#include "bitboard.h"
#include "bitcount.h"
#include "misc.h"
uint8_t PopCnt16[1 << 16];
int SquareDistance[SQUARE_NB][SQUARE_NB];
Bitboard RookMasks [SQUARE_NB];
@@ -74,18 +74,30 @@ namespace {
return Is64Bit ? (b * DeBruijn64) >> 58
: ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn32) >> 26;
}
// popcount16() counts the non-zero bits using SWAR-Popcount algorithm
unsigned popcount16(unsigned u) {
u -= (u >> 1) & 0x5555U;
u = ((u >> 2) & 0x3333U) + (u & 0x3333U);
u = ((u >> 4) + u) & 0x0F0FU;
return (u * 0x0101U) >> 8;
}
}
#ifndef USE_BSFQ
#ifdef NO_BSF
/// Software fall-back of lsb() and msb() for CPU lacking hardware support
Square lsb(Bitboard b) {
assert(b);
return BSFTable[bsf_index(b)];
}
Square msb(Bitboard b) {
assert(b);
unsigned b32;
int result = 0;
@@ -112,7 +124,7 @@ Square msb(Bitboard b) {
return Square(result + MSBTable[b32]);
}
#endif // ifndef USE_BSFQ
#endif // ifdef NO_BSF
/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable
@@ -139,6 +151,9 @@ const std::string Bitboards::pretty(Bitboard b) {
void Bitboards::init() {
for (unsigned i = 0; i < (1 << 16); ++i)
PopCnt16[i] = (uint8_t) popcount16(i);
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
SquareBB[s] = 1ULL << s;
@@ -190,8 +205,8 @@ void Bitboards::init() {
StepAttacksBB[make_piece(c, pt)][s] |= to;
}
Square RookDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W };
Square BishopDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW };
Square RookDeltas[] = { NORTH, EAST, SOUTH, WEST };
Square BishopDeltas[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
init_magics(RookTable, RookAttacks, RookMagics, RookMasks, RookShifts, RookDeltas, magic_index<ROOK>);
init_magics(BishopTable, BishopAttacks, BishopMagics, BishopMasks, BishopShifts, BishopDeltas, magic_index<BISHOP>);
@@ -263,7 +278,7 @@ namespace {
// the number of 1s of the mask. Hence we deduce the size of the shift to
// apply to the 64 or 32 bits word to get the index.
masks[s] = sliding_attack(deltas, s, 0) & ~edges;
shifts[s] = (Is64Bit ? 64 : 32) - popcount<Max15>(masks[s]);
shifts[s] = (Is64Bit ? 64 : 32) - popcount(masks[s]);
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
// store the corresponding sliding attack bitboard in reference[].
@@ -294,7 +309,7 @@ namespace {
do {
do
magics[s] = rng.sparse_rand<Bitboard>();
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
while (popcount((magics[s] * masks[s]) >> 56) < 6);
// A good magic must map every possible occupancy to an index that
// looks up the correct sliding attack in the attacks[s] database.
+56 -50
View File
@@ -61,16 +61,6 @@ const Bitboard Rank8BB = Rank1BB << (8 * 7);
extern int SquareDistance[SQUARE_NB][SQUARE_NB];
extern Bitboard RookMasks [SQUARE_NB];
extern Bitboard RookMagics [SQUARE_NB];
extern Bitboard* RookAttacks[SQUARE_NB];
extern unsigned RookShifts [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];
extern Bitboard RankBB[RANK_NB];
@@ -134,13 +124,13 @@ inline Bitboard file_bb(Square s) {
}
/// shift_bb() moves a bitboard one step along direction Delta. Mainly for pawns
/// shift() moves a bitboard one step along direction D. 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
template<Square D>
inline Bitboard shift(Bitboard b) {
return D == NORTH ? b << 8 : D == SOUTH ? b >> 8
: D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == SOUTH_EAST ? (b & ~FileHBB) >> 7
: D == NORTH_WEST ? (b & ~FileABB) << 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9
: 0;
}
@@ -225,6 +215,13 @@ template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_
template<PieceType Pt>
inline unsigned magic_index(Square s, Bitboard occupied) {
extern Bitboard RookMasks[SQUARE_NB];
extern Bitboard RookMagics[SQUARE_NB];
extern unsigned RookShifts[SQUARE_NB];
extern Bitboard BishopMasks[SQUARE_NB];
extern Bitboard BishopMagics[SQUARE_NB];
extern unsigned BishopShifts[SQUARE_NB];
Bitboard* const Masks = Pt == ROOK ? RookMasks : BishopMasks;
Bitboard* const Magics = Pt == ROOK ? RookMagics : BishopMagics;
unsigned* const Shifts = Pt == ROOK ? RookShifts : BishopShifts;
@@ -242,6 +239,10 @@ inline unsigned magic_index(Square s, Bitboard occupied) {
template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
extern Bitboard* RookAttacks[SQUARE_NB];
extern Bitboard* BishopAttacks[SQUARE_NB];
return (Pt == ROOK ? RookAttacks : BishopAttacks)[s][magic_index<Pt>(s, occupied)];
}
@@ -257,56 +258,61 @@ inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) {
}
/// popcount() counts the number of non-zero bits in a bitboard
inline int popcount(Bitboard b) {
#ifndef USE_POPCNT
extern uint8_t PopCnt16[1 << 16];
union { Bitboard bb; uint16_t u[4]; } v = { b };
return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]];
#elif defined(_MSC_VER) || defined(__INTEL_COMPILER)
return (int)_mm_popcnt_u64(b);
#else // Assumed gcc or compatible compiler
return __builtin_popcountll(b);
#endif
}
/// lsb() and msb() return the least/most significant bit in a non-zero bitboard
#ifdef USE_BSFQ
# if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
#if defined(__GNUC__)
inline Square lsb(Bitboard b) {
assert(b);
return Square(__builtin_ctzll(b));
}
inline Square msb(Bitboard b) {
assert(b);
return Square(63 - __builtin_clzll(b));
}
#elif defined(_WIN64) && defined(_MSC_VER)
inline Square lsb(Bitboard b) {
assert(b);
unsigned long idx;
_BitScanForward64(&idx, b);
return (Square) idx;
}
inline Square msb(Bitboard b) {
assert(b);
unsigned long idx;
_BitScanReverse64(&idx, b);
return (Square) idx;
}
# elif defined(__arm__)
#else
inline int lsb32(uint32_t v) {
__asm__("rbit %0, %1" : "=r"(v) : "r"(v));
return __builtin_clz(v);
}
inline Square msb(Bitboard b) {
return (Square) (63 - __builtin_clzll(b));
}
inline Square lsb(Bitboard b) {
return (Square) (uint32_t(b) ? lsb32(uint32_t(b)) : 32 + lsb32(uint32_t(b >> 32)));
}
# else // Assumed gcc or compatible compiler
inline Square lsb(Bitboard b) { // Assembly code by Heinz van Saanen
Bitboard idx;
__asm__("bsfq %1, %0": "=r"(idx): "rm"(b) );
return (Square) idx;
}
inline Square msb(Bitboard b) {
Bitboard idx;
__asm__("bsrq %1, %0": "=r"(idx): "rm"(b) );
return (Square) idx;
}
# endif
#else // ifdef(USE_BSFQ)
#define NO_BSF // Fallback on software implementation for other cases
Square lsb(Bitboard b);
Square msb(Bitboard b);
-105
View File
@@ -1,105 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BITCOUNT_H_INCLUDED
#define BITCOUNT_H_INCLUDED
#include <cassert>
#include "types.h"
enum BitCountType {
CNT_64,
CNT_64_MAX15,
CNT_32,
CNT_32_MAX15,
CNT_HW_POPCNT
};
/// 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 Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
/// popcount() counts the number of non-zero bits in a bitboard
template<BitCountType> inline int popcount(Bitboard);
template<>
inline int popcount<CNT_64>(Bitboard b) {
b -= (b >> 1) & 0x5555555555555555ULL;
b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b >> 4) + b) & 0x0F0F0F0F0F0F0F0FULL;
return (b * 0x0101010101010101ULL) >> 56;
}
template<>
inline int popcount<CNT_64_MAX15>(Bitboard b) {
b -= (b >> 1) & 0x5555555555555555ULL;
b = ((b >> 2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
return (b * 0x1111111111111111ULL) >> 60;
}
template<>
inline int popcount<CNT_32>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
v = ((v >> 4) + v + (w >> 4) + w) & 0x0F0F0F0F;
return (v * 0x01010101) >> 24;
}
template<>
inline int popcount<CNT_32_MAX15>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
v = ((v >> 2) & 0x33333333) + (v & 0x33333333); // 0-4 in 4 bits
w = ((w >> 2) & 0x33333333) + (w & 0x33333333);
return ((v + w) * 0x11111111) >> 28;
}
template<>
inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#ifndef USE_POPCNT
assert(false);
return b != 0; // Avoid 'b not used' warning
#elif defined(_MSC_VER) && defined(__INTEL_COMPILER)
return _mm_popcnt_u64(b);
#elif defined(_MSC_VER)
return (int)__popcnt64(b);
#else // Assumed gcc or compatible compiler
return __builtin_popcountll(b);
#endif
}
#endif // #ifndef BITCOUNT_H_INCLUDED
+7 -7
View File
@@ -22,7 +22,6 @@
#include <cassert>
#include "bitboard.h"
#include "bitcount.h"
#include "endgame.h"
#include "movegen.h"
@@ -100,7 +99,8 @@ namespace {
string fen = sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/8/8/"
+ sides[1] + char(8 - sides[1].length() + '0') + " w - - 0 10";
return Position(fen, false, nullptr).material_key();
StateInfo st;
return Position().set(fen, false, &st, nullptr).material_key();
}
} // namespace
@@ -259,8 +259,8 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
result = Value(80) - 8 * distance(wksq, psq);
else
result = Value(200) - 8 * ( distance(wksq, psq + DELTA_S)
- distance(bksq, psq + DELTA_S)
result = Value(200) - 8 * ( distance(wksq, psq + SOUTH)
- distance(bksq, psq + SOUTH)
- distance(psq, queeningSq));
return strongSide == pos.side_to_move() ? result : -result;
@@ -496,7 +496,7 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
// If the defending king blocks the pawn and the attacking king is too far
// away, it's a draw.
if ( r <= RANK_5
&& bksq == wpsq + DELTA_N
&& bksq == wpsq + NORTH
&& distance(wksq, wpsq) - tempo >= 2
&& distance(wksq, brsq) - tempo >= 2)
return SCALE_FACTOR_DRAW;
@@ -517,10 +517,10 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
&& file_of(wrsq) == f
&& wrsq < wpsq
&& (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo)
&& (distance(wksq, wpsq + DELTA_N) < distance(bksq, wpsq + DELTA_N) - 2 + tempo)
&& (distance(wksq, wpsq + NORTH) < distance(bksq, wpsq + NORTH) - 2 + tempo)
&& ( distance(bksq, wrsq) + tempo >= 3
|| ( distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo
&& (distance(wksq, wpsq + DELTA_N) < distance(bksq, wrsq) + tempo))))
&& (distance(wksq, wpsq + NORTH) < distance(bksq, wrsq) + tempo))))
return ScaleFactor( SCALE_FACTOR_MAX
- 8 * distance(wpsq, queeningSq)
- 2 * distance(wksq, queeningSq));
+215 -197
View File
@@ -24,7 +24,7 @@
#include <iomanip>
#include <sstream>
#include "bitcount.h"
#include "bitboard.h"
#include "evaluate.h"
#include "material.h"
#include "pawns.h"
@@ -33,7 +33,7 @@ namespace {
namespace Trace {
enum Term { // First 8 entries are for PieceType
enum Term { // The first 8 entries are for PieceType
MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, TOTAL, TERM_NB
};
@@ -77,6 +77,11 @@ namespace {
// attacked by a given color and piece type (can be also ALL_PIECES).
Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB];
// attackedBy2[color] are the squares attacked by 2 pieces of a given color,
// possibly via x-ray or by one pawn and one piece. Diagonal x-ray through
// pawn or squares attacked by 2 pawns are not explicitly added.
Bitboard attackedBy2[COLOR_NB];
// kingRing[color] is the zone around the king which is considered
// by the king safety evaluation. This consists of the squares directly
// adjacent to the king, and the three (or two, for a king on an edge file)
@@ -89,7 +94,7 @@ namespace {
// which attack a square in the kingRing of the enemy king.
int kingAttackersCount[COLOR_NB];
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
// kingAttackersWeight[color] is the sum of the "weights" 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 elements in the
// KingAttackWeights array.
@@ -107,18 +112,6 @@ namespace {
Pawns::Entry* pi;
};
// Evaluation weights, indexed by the corresponding evaluation term
enum { PawnStructure, PassedPawns, Space, KingSafety };
const struct Weight { int mg, eg; } Weights[] = {
{214, 203}, {193, 262}, {47, 0}, {330, 0} };
Score operator*(Score s, const Weight& w) {
return make_score(mg_value(s) * w.mg / 256, eg_value(s) * w.eg / 256);
}
#define V(v) Value(v)
#define S(mg, eg) make_score(mg, eg)
@@ -126,7 +119,7 @@ namespace {
// game, indexed by piece type and number of attacked squares in the MobilityArea.
const Score MobilityBonus[][32] = {
{}, {},
{ S(-75,-76), S(-56,-54), S(- 9,-26), S( -2,-10), S( 6, 5), S( 15, 11), // Knights
{ S(-75,-76), S(-56,-54), S( -9,-26), S( -2,-10), S( 6, 5), S( 15, 11), // Knights
S( 22, 26), S( 30, 28), S( 36, 29) },
{ S(-48,-58), S(-21,-19), S( 16, -2), S( 26, 12), S( 37, 22), S( 51, 42), // Bishops
S( 54, 54), S( 63, 58), S( 65, 63), S( 71, 70), S( 79, 74), S( 81, 86),
@@ -144,62 +137,69 @@ namespace {
// Outpost[knight/bishop][supported by pawn] contains bonuses for knights and
// bishops outposts, bigger if outpost piece is supported by a pawn.
const Score Outpost[][2] = {
{ S(42,11), S(63,17) }, // Knights
{ S(18, 5), S(27, 8) } // Bishops
{ S(43,11), S(65,20) }, // Knights
{ S(20, 3), S(29, 8) } // Bishops
};
// ReachableOutpost[knight/bishop][supported by pawn] contains bonuses for
// knights and bishops which can reach an outpost square in one move, bigger
// if outpost square is supported by a pawn.
const Score ReachableOutpost[][2] = {
{ S(21, 5), S(31, 8) }, // Knights
{ S( 8, 2), S(13, 4) } // Bishops
{ S(21, 5), S(35, 8) }, // Knights
{ S( 8, 0), S(14, 4) } // Bishops
};
// RookOnFile[semiopen/open] contains bonuses for each rook when there is no
// friendly pawn on the rook file.
const Score RookOnFile[2] = { S(19, 10), S(43, 21) };
const Score RookOnFile[2] = { S(20, 7), S(45, 20) };
// ThreatBySafePawn[PieceType] contains bonuses according to which piece
// type is attacked by a pawn which is protected or not attacked.
// type is attacked by a pawn which is protected or is not attacked.
const Score ThreatBySafePawn[PIECE_TYPE_NB] = {
S(0, 0), S(0, 0), S(176, 139), S(131, 127), S(217, 218), S(203, 215) };
S(0, 0), S(0, 0), S(176, 139), S(131, 127), S(217, 218), S(203, 215)
};
// Threat[by minor/by rook][attacked PieceType] contains
// bonuses according to which piece type attacks which one.
// Attacks on lesser pieces which are pawn defended are not considered.
// Attacks on lesser pieces which are pawn-defended are not considered.
const Score Threat[][PIECE_TYPE_NB] = {
{ S(0, 0), S(0, 33), S(45, 43), S(46, 47), S(72,107), S(48,118) }, // by Minor
{ S(0, 0), S(0, 25), S(40, 62), S(40, 59), S( 0, 34), S(35, 48) } // by Rook
};
// ThreatByKing[on one/on many] contains bonuses for King attacks on
// pawns or pieces which are not pawn defended.
// pawns or pieces which are not pawn-defended.
const Score ThreatByKing[2] = { S(3, 62), S(9, 138) };
// Passed[mg/eg][Rank] contains midgame and endgame bonuses for passed pawns.
// We don't use a Score because we process the two components independently.
const Value Passed[][RANK_NB] = {
{ V(0), V( 1), V(34), V(90), V(214), V(328) },
{ V(7), V(14), V(37), V(63), V(134), V(189) }
{ V(5), V( 5), V(31), V(73), V(166), V(252) },
{ V(7), V(14), V(38), V(73), V(166), V(252) }
};
// PassedFile[File] contains a bonus according to the file of a passed pawn
const Score PassedFile[FILE_NB] = {
S( 12, 10), S( 3, 10), S( 1, -8), S(-27,-12),
S(-27,-12), S( 1, -8), S( 3, 10), S( 12, 10)
S( 9, 10), S( 2, 10), S( 1, -8), S(-20,-12),
S(-20,-12), S( 1, -8), S( 2, 10), S( 9, 10)
};
// Assorted bonuses and penalties used by evaluation
const Score MinorBehindPawn = S(16, 0);
const Score BishopPawns = S( 8, 12);
const Score RookOnPawn = S( 7, 27);
const Score RookOnPawn = S( 8, 24);
const Score TrappedRook = S(92, 0);
const Score Checked = S(20, 20);
const Score ThreatByHangingPawn = S(70, 63);
const Score Hanging = S(48, 28);
const Score ThreatByPawnPush = S(31, 19);
const Score CloseEnemies = S( 7, 0);
const Score SafeCheck = S(20, 20);
const Score OtherCheck = S(10, 10);
const Score ThreatByHangingPawn = S(71, 61);
const Score LooseEnemies = S( 0, 25);
const Score WeakQueen = S(35, 0);
const Score Hanging = S(48, 27);
const Score ThreatByPawnPush = S(38, 22);
const Score Unstoppable = S( 0, 20);
const Score PawnlessFlank = S(20, 80);
const Score HinderPassedPawn = S( 7, 0);
// 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
@@ -209,43 +209,38 @@ namespace {
#undef S
#undef V
// 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[].
Score KingDanger[512];
// KingAttackWeights[PieceType] contains king attack weights by piece type
const int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 7, 5, 4, 1 };
const int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 78, 56, 45, 11 };
// Penalties for enemy's safe checks
const int QueenContactCheck = 89;
const int QueenCheck = 50;
const int RookCheck = 45;
const int BishopCheck = 6;
const int KnightCheck = 14;
const int QueenContactCheck = 997;
const int QueenCheck = 695;
const int RookCheck = 638;
const int BishopCheck = 538;
const int KnightCheck = 874;
// eval_init() initializes king and attack bitboards for given color
// eval_init() initializes king and attack bitboards for a given color
// adding pawn attacks. To be done at the beginning of the evaluation.
template<Color Us>
void eval_init(const Position& pos, EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Down = (Us == WHITE ? DELTA_S : DELTA_N);
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Down = (Us == WHITE ? SOUTH : NORTH);
ei.pinnedPieces[Us] = pos.pinned_pieces(Us);
Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.square<KING>(Them));
Bitboard b = ei.attackedBy[Them][KING];
ei.attackedBy[Them][ALL_PIECES] |= b;
ei.attackedBy[Us][ALL_PIECES] |= ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
ei.attackedBy2[Us] = ei.attackedBy[Us][PAWN] & ei.attackedBy[Us][KING];
// Init king safety tables only if we are going to use them
if (pos.non_pawn_material(Us) >= QueenValueMg)
{
ei.kingRing[Them] = b | shift_bb<Down>(b);
ei.kingRing[Them] = b | shift<Down>(b);
b &= ei.attackedBy[Us][PAWN];
ei.kingAttackersCount[Us] = b ? popcount<Max15>(b) : 0;
ei.kingAttackersCount[Us] = popcount(b);
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
}
else
@@ -281,15 +276,14 @@ namespace {
if (ei.pinnedPieces[Us] & s)
b &= LineBB[pos.square<KING>(Us)][s];
ei.attackedBy2[Us] |= ei.attackedBy[Us][ALL_PIECES] & b;
ei.attackedBy[Us][ALL_PIECES] |= ei.attackedBy[Us][Pt] |= b;
if (b & ei.kingRing[Them])
{
ei.kingAttackersCount[Us]++;
ei.kingAttackersWeight[Us] += KingAttackWeights[Pt];
bb = b & ei.attackedBy[Them][KING];
if (bb)
ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb);
ei.kingAdjacentZoneAttacksCount[Us] += popcount(b & ei.attackedBy[Them][KING]);
}
if (Pt == QUEEN)
@@ -297,7 +291,7 @@ namespace {
| ei.attackedBy[Them][BISHOP]
| ei.attackedBy[Them][ROOK]);
int mob = popcount<Pt == QUEEN ? Full : Max15>(b & mobilityArea[Us]);
int mob = popcount(b & mobilityArea[Us]);
mobility[Us] += MobilityBonus[Pt][mob];
@@ -319,7 +313,7 @@ namespace {
&& (pos.pieces(PAWN) & (s + pawn_push(Us))))
score += MinorBehindPawn;
// Penalty for pawns on same color square of bishop
// Penalty for pawns on the same color square as the bishop
if (Pt == BISHOP)
score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
@@ -330,7 +324,7 @@ namespace {
&& 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);
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
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
@@ -342,17 +336,13 @@ namespace {
{
// Bonus for aligning with enemy pawns on the same rank/file
if (relative_rank(Us, s) >= RANK_5)
{
Bitboard alignedPawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s];
if (alignedPawns)
score += RookOnPawn * popcount<Max15>(alignedPawns);
}
score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
// Bonus when on an open or semi-open file
if (ei.pi->semiopen_file(Us, file_of(s)))
score += RookOnFile[!!ei.pi->semiopen_file(Them, file_of(s))];
// Penalize when trapped by the king, even more if king cannot castle
// Penalize when trapped by the king, even more if the king cannot castle
else if (mob <= 3)
{
Square ksq = pos.square<KING>(Us);
@@ -363,12 +353,20 @@ namespace {
score -= (TrappedRook - make_score(mob * 22, 0)) * (1 + !pos.can_castle(Us));
}
}
if (Pt == QUEEN)
{
// Penalty if any relative pin or discovered attack against the queen
Bitboard pinners;
if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, pinners))
score -= WeakQueen;
}
}
if (DoTrace)
Trace::add(Pt, Us, score);
// Recursively call evaluate_pieces() of next piece type until KING excluded
// Recursively call evaluate_pieces() of next piece type until KING is excluded
return score - evaluate_pieces<DoTrace, Them, NextPt>(pos, ei, mobility, mobilityArea);
}
@@ -380,13 +378,27 @@ namespace {
// evaluate_king() assigns bonuses and penalties to a king of a given color
const Bitboard WhiteCamp = Rank1BB | Rank2BB | Rank3BB | Rank4BB | Rank5BB;
const Bitboard BlackCamp = Rank8BB | Rank7BB | Rank6BB | Rank5BB | Rank4BB;
const Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB;
const Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
const Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
const Bitboard KingFlank[COLOR_NB][FILE_NB] = {
{ QueenSide & WhiteCamp, QueenSide & WhiteCamp, QueenSide & WhiteCamp, CenterFiles & WhiteCamp,
CenterFiles & WhiteCamp, KingSide & WhiteCamp, KingSide & WhiteCamp, KingSide & WhiteCamp },
{ QueenSide & BlackCamp, QueenSide & BlackCamp, QueenSide & BlackCamp, CenterFiles & BlackCamp,
CenterFiles & BlackCamp, KingSide & BlackCamp, KingSide & BlackCamp, KingSide & BlackCamp },
};
template<Color Us, bool DoTrace>
Score evaluate_king(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Up = (Us == WHITE ? NORTH : SOUTH);
Bitboard undefended, b, b1, b2, safe;
int attackUnits;
Bitboard undefended, b, b1, b2, safe, other;
int kingDanger;
const Square ksq = pos.square<KING>(Us);
// King shelter and enemy pawns storm
@@ -395,83 +407,100 @@ namespace {
// Main king safety evaluation
if (ei.kingAttackersCount[Them])
{
// Find the attacked squares around the king which have no defenders
// apart from the king itself.
undefended = ei.attackedBy[Them][ALL_PIECES]
& ei.attackedBy[Us][KING]
& ~( ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
| ei.attackedBy[Us][QUEEN]);
// Find the attacked squares which are defended only by the king...
undefended = ei.attackedBy[Them][ALL_PIECES]
& ei.attackedBy[Us][KING]
& ~ei.attackedBy2[Us];
// Initialize the 'attackUnits' variable, which is used later on as an
// index into the KingDanger[] array. The initial value is based on the
// ... and those which are not defended at all in the larger king ring
b = ei.attackedBy[Them][ALL_PIECES] & ~ei.attackedBy[Us][ALL_PIECES]
& ei.kingRing[Us] & ~pos.pieces(Them);
// Initialize the 'kingDanger' variable, which will be transformed
// later into a king danger score. 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(72, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them])
+ 9 * ei.kingAdjacentZoneAttacksCount[Them]
+ 27 * popcount<Max15>(undefended)
+ 11 * !!ei.pinnedPieces[Us]
- 64 * !pos.count<QUEEN>(Them)
- mg_value(score) / 8;
kingDanger = std::min(807, ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them])
+ 101 * ei.kingAdjacentZoneAttacksCount[Them]
+ 235 * popcount(undefended)
+ 134 * (popcount(b) + !!ei.pinnedPieces[Us])
- 717 * !pos.count<QUEEN>(Them)
- 7 * mg_value(score) / 5 - 5;
// Analyse the enemy's safe queen contact checks. Firstly, find the
// 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]
| ei.attackedBy[Them][KING];
if (b)
attackUnits += QueenContactCheck * popcount<Max15>(b);
}
// ...and keep squares supported by another enemy piece
kingDanger += QueenContactCheck * popcount(b & ei.attackedBy2[Them]);
// Analyse the enemy's safe distance checks for sliders and knights
safe = ~(ei.attackedBy[Us][ALL_PIECES] | pos.pieces(Them));
// Analyse the safe enemy's checks which are possible on next move...
safe = ~(ei.attackedBy[Us][ALL_PIECES] | pos.pieces(Them));
b1 = pos.attacks_from<ROOK >(ksq) & safe;
b2 = pos.attacks_from<BISHOP>(ksq) & safe;
// ... and some other potential checks, only requiring the square to be
// safe from pawn-attacks, and not being occupied by a blocked pawn.
other = ~( ei.attackedBy[Us][PAWN]
| (pos.pieces(Them, PAWN) & shift<Up>(pos.pieces(PAWN))));
b1 = pos.attacks_from<ROOK >(ksq);
b2 = pos.attacks_from<BISHOP>(ksq);
// Enemy queen safe checks
b = (b1 | b2) & ei.attackedBy[Them][QUEEN];
if (b)
{
attackUnits += QueenCheck * popcount<Max15>(b);
score -= Checked;
}
if ((b1 | b2) & ei.attackedBy[Them][QUEEN] & safe)
kingDanger += QueenCheck, score -= SafeCheck;
// Enemy rooks safe checks
b = b1 & ei.attackedBy[Them][ROOK];
if (b)
{
attackUnits += RookCheck * popcount<Max15>(b);
score -= Checked;
}
// For other pieces, also consider the square safe if attacked twice,
// and only defended by a queen.
safe |= ei.attackedBy2[Them]
& ~(ei.attackedBy2[Us] | pos.pieces(Them))
& ei.attackedBy[Us][QUEEN];
// Enemy bishops safe checks
b = b2 & ei.attackedBy[Them][BISHOP];
if (b)
{
attackUnits += BishopCheck * popcount<Max15>(b);
score -= Checked;
}
// Enemy rooks safe and other checks
if (b1 & ei.attackedBy[Them][ROOK] & safe)
kingDanger += RookCheck, score -= SafeCheck;
// Enemy knights safe checks
b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT] & safe;
if (b)
{
attackUnits += KnightCheck * popcount<Max15>(b);
score -= Checked;
}
else if (b1 & ei.attackedBy[Them][ROOK] & other)
score -= OtherCheck;
// Finally, extract the king danger score from the KingDanger[]
// array and subtract the score from evaluation.
score -= KingDanger[std::max(std::min(attackUnits, 399), 0)];
// Enemy bishops safe and other checks
if (b2 & ei.attackedBy[Them][BISHOP] & safe)
kingDanger += BishopCheck, score -= SafeCheck;
else if (b2 & ei.attackedBy[Them][BISHOP] & other)
score -= OtherCheck;
// Enemy knights safe and other checks
b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT];
if (b & safe)
kingDanger += KnightCheck, score -= SafeCheck;
else if (b & other)
score -= OtherCheck;
// Compute the king danger score and subtract it from the evaluation
if (kingDanger > 0)
score -= make_score(std::min(kingDanger * kingDanger / 4096, 2 * int(BishopValueMg)), 0);
}
// King tropism: firstly, find squares that opponent attacks in our king flank
File kf = file_of(ksq);
b = ei.attackedBy[Them][ALL_PIECES] & KingFlank[Us][kf];
assert(((Us == WHITE ? b << 4 : b >> 4) & b) == 0);
assert(popcount(Us == WHITE ? b << 4 : b >> 4) == popcount(b));
// Secondly, add the squares which are attacked twice in that flank and
// which are not defended by our pawns.
b = (Us == WHITE ? b << 4 : b >> 4)
| (b & ei.attackedBy2[Them] & ~ei.attackedBy[Us][PAWN]);
score -= CloseEnemies * popcount(b);
// Penalty when our king is on a pawnless flank
if (!(pos.pieces(PAWN) & (KingFlank[WHITE][kf] | KingFlank[BLACK][kf])))
score -= PawnlessFlank;
if (DoTrace)
Trace::add(KING, Us, score);
@@ -479,24 +508,29 @@ namespace {
}
// evaluate_threats() assigns bonuses according to the type of attacking piece
// and the type of attacked one.
// evaluate_threats() assigns bonuses according to the types of the attacking
// and the attacked pieces.
template<Color Us, bool DoTrace>
Score evaluate_threats(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Bitboard TRank2BB = (Us == WHITE ? Rank2BB : Rank7BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Up = (Us == WHITE ? NORTH : SOUTH);
const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
const Bitboard TRank2BB = (Us == WHITE ? Rank2BB : Rank7BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
enum { Minor, Rook };
Bitboard b, weak, defended, safeThreats;
Score score = SCORE_ZERO;
// Small bonus if the opponent has loose pawns or pieces
if ( (pos.pieces(Them) ^ pos.pieces(Them, QUEEN, KING))
& ~(ei.attackedBy[Us][ALL_PIECES] | ei.attackedBy[Them][ALL_PIECES]))
score += LooseEnemies;
// Non-pawn enemies attacked by a pawn
weak = (pos.pieces(Them) ^ pos.pieces(Them, PAWN)) & ei.attackedBy[Us][PAWN];
@@ -505,7 +539,7 @@ namespace {
b = pos.pieces(Us, PAWN) & ( ~ei.attackedBy[Them][ALL_PIECES]
| ei.attackedBy[Us][ALL_PIECES]);
safeThreats = (shift_bb<Right>(b) | shift_bb<Left>(b)) & weak;
safeThreats = (shift<Right>(b) | shift<Left>(b)) & weak;
if (weak ^ safeThreats)
score += ThreatByHangingPawn;
@@ -533,9 +567,7 @@ namespace {
while (b)
score += Threat[Rook ][type_of(pos.piece_on(pop_lsb(&b)))];
b = weak & ~ei.attackedBy[Them][ALL_PIECES];
if (b)
score += Hanging * popcount<Max15>(b);
score += Hanging * popcount(weak & ~ei.attackedBy[Them][ALL_PIECES]);
b = weak & ei.attackedBy[Us][KING];
if (b)
@@ -544,18 +576,17 @@ namespace {
// Bonus if some pawns can safely push and attack an enemy piece
b = pos.pieces(Us, PAWN) & ~TRank7BB;
b = shift_bb<Up>(b | (shift_bb<Up>(b & TRank2BB) & ~pos.pieces()));
b = shift<Up>(b | (shift<Up>(b & TRank2BB) & ~pos.pieces()));
b &= ~pos.pieces()
& ~ei.attackedBy[Them][PAWN]
& (ei.attackedBy[Us][ALL_PIECES] | ~ei.attackedBy[Them][ALL_PIECES]);
b = (shift_bb<Left>(b) | shift_bb<Right>(b))
b = (shift<Left>(b) | shift<Right>(b))
& pos.pieces(Them)
& ~ei.attackedBy[Us][PAWN];
if (b)
score += ThreatByPawnPush * popcount<Max15>(b);
score += ThreatByPawnPush * popcount(b);
if (DoTrace)
Trace::add(THREAT, Us, score);
@@ -571,7 +602,7 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b, squaresToQueen, defendedSquares, unsafeSquares;
Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares;
Score score = SCORE_ZERO;
b = ei.pi->passed_pawns(Us);
@@ -581,6 +612,10 @@ namespace {
Square s = pop_lsb(&b);
assert(pos.pawn_passed(Us, s));
assert(!(pos.pieces(PAWN) & forward_bb(Us, s)));
bb = forward_bb(Us, s) & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
score -= HinderPassedPawn * popcount(bb);
int r = relative_rank(Us, s) - RANK_2;
int rr = r * (r - 1);
@@ -607,7 +642,7 @@ namespace {
// in the pawn's path attacked or occupied by the enemy.
defendedSquares = unsafeSquares = squaresToQueen = forward_bb(Us, s);
Bitboard bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s);
bb = forward_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s);
if (!(pos.pieces(Us) & bb))
defendedSquares &= ei.attackedBy[Us][ALL_PIECES];
@@ -619,7 +654,7 @@ namespace {
// assign a smaller bonus if the block square isn't attacked.
int k = !unsafeSquares ? 18 : !(unsafeSquares & blockSq) ? 8 : 0;
// If the path to queen is fully defended, assign a big bonus.
// If the path to the queen is fully defended, assign a big bonus.
// Otherwise assign a smaller bonus if the block square is defended.
if (defendedSquares == squaresToQueen)
k += 6;
@@ -630,20 +665,17 @@ namespace {
mbonus += k * rr, ebonus += k * rr;
}
else if (pos.pieces(Us) & blockSq)
mbonus += rr * 3 + r * 2 + 3, ebonus += rr + r * 2;
mbonus += rr + r * 2, ebonus += rr + r * 2;
} // rr != 0
if (pos.count<PAWN>(Us) < pos.count<PAWN>(Them))
ebonus += ebonus / 4;
score += make_score(mbonus, ebonus) + PassedFile[file_of(s)];
}
if (DoTrace)
Trace::add(PASSED, Us, score * Weights[PassedPawns]);
Trace::add(PASSED, Us, score);
// Add the scores to the middlegame and endgame eval
return score * Weights[PassedPawns];
return score;
}
@@ -678,24 +710,25 @@ namespace {
assert(unsigned(safe >> (Us == WHITE ? 32 : 0)) == 0);
// ...count safe + (behind & safe) with a single popcount
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);
int bonus = popcount((Us == WHITE ? safe << 32 : safe >> 32) | (behind & safe));
bonus = std::min(16, bonus);
int weight = pos.count<ALL_PIECES>(Us) - 2 * ei.pi->open_files();
return make_score(bonus * weight * weight, 0);
return make_score(bonus * weight * weight / 18, 0);
}
// evaluate_initiative() computes the initiative correction value for the
// position, i.e. second order bonus/malus based on the known attacking/defending
// position, i.e., second order bonus/malus based on the known attacking/defending
// status of the players.
Score evaluate_initiative(const Position& pos, int asymmetry, Value eg) {
int kingDistance = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
int kingDistance = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
- distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
int pawns = pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK);
// Compute the initiative bonus for the attacking side
int initiative = 8 * (pawns + asymmetry + kingDistance - 15);
int initiative = 8 * (asymmetry + kingDistance - 15) + 12 * pawns;
// Now apply the bonus: note that we find the attacking side by extracting
// the sign of the endgame value, and that we carefully cap the bonus so
@@ -707,9 +740,9 @@ namespace {
// evaluate_scale_factor() computes the scale factor for the winning side
ScaleFactor evaluate_scale_factor(const Position& pos, const EvalInfo& ei, Score score) {
ScaleFactor evaluate_scale_factor(const Position& pos, const EvalInfo& ei, Value eg) {
Color strongSide = eg_value(score) > VALUE_DRAW ? WHITE : BLACK;
Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
ScaleFactor sf = ei.me->scale_factor(pos, strongSide);
// If we don't already have an unusual scale factor, check for certain
@@ -720,7 +753,7 @@ namespace {
if (pos.opposite_bishops())
{
// 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.
// is almost a draw, in case of KBP vs KB, it 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(31) : ScaleFactor(9);
@@ -728,14 +761,14 @@ namespace {
// Endgame with opposite-colored bishops, but also other pieces. Still
// a bit drawish, but not as drawish as with only the two bishops.
else
sf = ScaleFactor(46 * sf / SCALE_FACTOR_NORMAL);
sf = ScaleFactor(46);
}
// 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
else if ( abs(eg) <= BishopValueEg
&& pos.count<PAWN>(strongSide) <= 2
&& !pos.pawn_passed(~strongSide, pos.square<KING>(~strongSide)))
sf = ei.pi->pawn_span(strongSide) ? ScaleFactor(51) : ScaleFactor(37);
sf = ScaleFactor(37 + 7 * pos.count<PAWN>(strongSide));
}
return sf;
@@ -752,36 +785,37 @@ Value Eval::evaluate(const Position& pos) {
assert(!pos.checkers());
Score mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO };
EvalInfo ei;
Score score, mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO };
// Initialize score by reading the incrementally updated scores included in
// the position object (material + piece square tables). Score is computed
// internally from the white point of view.
score = pos.psq_score();
// Probe the material hash table
ei.me = Material::probe(pos);
score += ei.me->imbalance();
// If we have a specialized evaluation function for the current material
// configuration, call it and return.
if (ei.me->specialized_eval_exists())
return ei.me->evaluate(pos);
// Initialize score by reading the incrementally updated scores included in
// the position object (material + piece square tables) and the material
// imbalance. Score is computed internally from the white point of view.
Score score = pos.psq_score() + ei.me->imbalance();
// Probe the pawn hash table
ei.pi = Pawns::probe(pos);
score += ei.pi->pawns_score() * Weights[PawnStructure];
score += ei.pi->pawns_score();
// Initialize attack and king safety bitboards
ei.attackedBy[WHITE][ALL_PIECES] = ei.attackedBy[BLACK][ALL_PIECES] = 0;
ei.attackedBy[WHITE][KING] = pos.attacks_from<KING>(pos.square<KING>(WHITE));
ei.attackedBy[BLACK][KING] = pos.attacks_from<KING>(pos.square<KING>(BLACK));
eval_init<WHITE>(pos, ei);
eval_init<BLACK>(pos, ei);
// Pawns blocked or on ranks 2 and 3 will be excluded from the mobility area
Bitboard blockedPawns[] = {
pos.pieces(WHITE, PAWN) & (shift_bb<DELTA_S>(pos.pieces()) | Rank2BB | Rank3BB),
pos.pieces(BLACK, PAWN) & (shift_bb<DELTA_N>(pos.pieces()) | Rank7BB | Rank6BB)
pos.pieces(WHITE, PAWN) & (shift<SOUTH>(pos.pieces()) | Rank2BB | Rank3BB),
pos.pieces(BLACK, PAWN) & (shift<NORTH>(pos.pieces()) | Rank7BB | Rank6BB)
};
// Do not include in mobility area squares protected by enemy pawns, or occupied
@@ -821,14 +855,14 @@ Value Eval::evaluate(const Position& pos) {
// Evaluate space for both sides, only during opening
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >= 12222)
score += ( evaluate_space<WHITE>(pos, ei)
- evaluate_space<BLACK>(pos, ei)) * Weights[Space];
score += evaluate_space<WHITE>(pos, ei)
- evaluate_space<BLACK>(pos, ei);
// Evaluate position potential for the winning side
score += evaluate_initiative(pos, ei.pi->pawn_asymmetry(), eg_value(score));
// Evaluate scale factor for the winning side
ScaleFactor sf = evaluate_scale_factor(pos, ei, score);
ScaleFactor sf = evaluate_scale_factor(pos, ei, eg_value(score));
// Interpolate between a middlegame and a (scaled by 'sf') endgame score
Value v = mg_value(score) * int(ei.me->game_phase())
@@ -841,10 +875,10 @@ Value Eval::evaluate(const Position& pos) {
{
Trace::add(MATERIAL, pos.psq_score());
Trace::add(IMBALANCE, ei.me->imbalance());
Trace::add(PAWN, ei.pi->pawns_score() * Weights[PawnStructure]);
Trace::add(PAWN, ei.pi->pawns_score());
Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]);
Trace::add(SPACE, evaluate_space<WHITE>(pos, ei) * Weights[Space]
, evaluate_space<BLACK>(pos, ei) * Weights[Space]);
Trace::add(SPACE, evaluate_space<WHITE>(pos, ei)
, evaluate_space<BLACK>(pos, ei));
Trace::add(TOTAL, score);
}
@@ -891,19 +925,3 @@ std::string Eval::trace(const Position& pos) {
return ss.str();
}
/// init() computes evaluation weights, usually at startup
void Eval::init() {
const int MaxSlope = 8700;
const int Peak = 1280000;
int t = 0;
for (int i = 0; i < 400; ++i)
{
t = std::min(Peak, std::min(i * i * 27, t + MaxSlope));
KingDanger[i] = make_score(t / 1000, 0) * Weights[KingSafety];
}
}
-1
View File
@@ -31,7 +31,6 @@ namespace Eval {
const Value Tempo = Value(20); // Must be visible to search
void init();
std::string trace(const Position& pos);
template<bool DoTrace = false>
+4 -2
View File
@@ -21,7 +21,6 @@
#include <iostream>
#include "bitboard.h"
#include "evaluate.h"
#include "position.h"
#include "search.h"
#include "thread.h"
@@ -29,6 +28,10 @@
#include "uci.h"
#include "syzygy/tbprobe.h"
namespace PSQT {
void init();
}
int main(int argc, char* argv[]) {
std::cout << engine_info() << std::endl;
@@ -39,7 +42,6 @@ int main(int argc, char* argv[]) {
Position::init();
Bitbases::init();
Search::init();
Eval::init();
Pawns::init();
Threads.init();
Tablebases::init(Options["SyzygyPath"]);
+2 -5
View File
@@ -31,13 +31,10 @@ namespace {
// Polynomial material imbalance parameters
// pair pawn knight bishop rook queen
const int Linear[6] = { 1667, -168, -1027, -166, 238, -138 };
const int QuadraticOurs[][PIECE_TYPE_NB] = {
// OUR PIECES
// pair pawn knight bishop rook queen
{ 0 }, // Bishop pair
{1667 }, // Bishop pair
{ 40, 2 }, // Pawn
{ 32, 255, -3 }, // Knight OUR PIECES
{ 0, 104, 4, 0 }, // Bishop
@@ -100,7 +97,7 @@ namespace {
if (!pieceCount[Us][pt1])
continue;
int v = Linear[pt1];
int v = 0;
for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2]
+3 -3
View File
@@ -50,9 +50,9 @@ struct Entry {
// 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);
ScaleFactor sf = scalingFunction[c] ? (*scalingFunction[c])(pos)
: SCALE_FACTOR_NONE;
return sf != SCALE_FACTOR_NONE ? sf : ScaleFactor(factor[c]);
}
Key key;
+7 -7
View File
@@ -32,7 +32,7 @@ namespace {
/// Version number. If Version is left empty, then compile date in the format
/// DD-MM-YY and show in engine_info.
const string Version = "7";
const string Version = "8";
/// 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
@@ -65,23 +65,23 @@ struct Tie: public streambuf { // MSVC requires split streambuf for cin and cout
class Logger {
Logger() : in(cin.rdbuf(), file.rdbuf()), out(cout.rdbuf(), file.rdbuf()) {}
~Logger() { start(false); }
~Logger() { start(""); }
ofstream file;
Tie in, out;
public:
static void start(bool b) {
static void start(const std::string& fname) {
static Logger l;
if (b && !l.file.is_open())
if (!fname.empty() && !l.file.is_open())
{
l.file.open("io_log.txt", ifstream::out);
l.file.open(fname, ifstream::out);
cin.rdbuf(&l.in);
cout.rdbuf(&l.out);
}
else if (!b && l.file.is_open())
else if (fname.empty() && l.file.is_open())
{
cout.rdbuf(l.out.buf);
cin.rdbuf(l.in.buf);
@@ -157,7 +157,7 @@ std::ostream& operator<<(std::ostream& os, SyncCout sc) {
/// Trampoline helper to avoid moving Logger to misc.h
void start_logger(bool b) { Logger::start(b); }
void start_logger(const std::string& fname) { Logger::start(fname); }
/// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking
+1 -1
View File
@@ -31,7 +31,7 @@
const std::string engine_info(bool to_uci = false);
void prefetch(void* addr);
void start_logger(bool b);
void start_logger(const std::string& fname);
void dbg_hit_on(bool b);
void dbg_hit_on(bool c, bool b);
+60 -60
View File
@@ -26,7 +26,7 @@
namespace {
template<CastlingRight Cr, bool Checks, bool Chess960>
ExtMove* generate_castling(const Position& pos, ExtMove* moveList, Color us, const CheckInfo* ci) {
ExtMove* generate_castling(const Position& pos, ExtMove* moveList, Color us) {
static const bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO);
@@ -42,8 +42,8 @@ namespace {
assert(!pos.checkers());
const Square K = Chess960 ? kto > kfrom ? DELTA_W : DELTA_E
: KingSide ? DELTA_W : DELTA_E;
const Square K = Chess960 ? kto > kfrom ? WEST : EAST
: KingSide ? WEST : EAST;
for (Square s = kto; s != kfrom; s += K)
if (pos.attackers_to(s) & enemies)
@@ -57,53 +57,50 @@ namespace {
Move m = make<CASTLING>(kfrom, rfrom);
if (Checks && !pos.gives_check(m, *ci))
if (Checks && !pos.gives_check(m))
return moveList;
else
(void)ci; // Silence a warning under MSVC
*moveList++ = m;
return moveList;
}
template<GenType Type, Square Delta>
ExtMove* make_promotions(ExtMove* moveList, Square to, const CheckInfo* ci) {
template<GenType Type, Square D>
ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
*moveList++ = make<PROMOTION>(to - Delta, to, QUEEN);
*moveList++ = make<PROMOTION>(to - D, to, QUEEN);
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
{
*moveList++ = make<PROMOTION>(to - Delta, to, ROOK);
*moveList++ = make<PROMOTION>(to - Delta, to, BISHOP);
*moveList++ = make<PROMOTION>(to - Delta, to, KNIGHT);
*moveList++ = make<PROMOTION>(to - D, to, ROOK);
*moveList++ = make<PROMOTION>(to - D, to, BISHOP);
*moveList++ = make<PROMOTION>(to - D, 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))
*moveList++ = make<PROMOTION>(to - Delta, to, KNIGHT);
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ksq))
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
else
(void)ci; // Silence a warning under MSVC
(void)ksq; // Silence a warning under MSVC
return moveList;
}
template<Color Us, GenType Type>
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList,
Bitboard target, const CheckInfo* ci) {
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
// Compute our parametrized parameters at compile time, named according to
// the point of view of white side.
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square Up = (Us == WHITE ? NORTH : SOUTH);
const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
Bitboard emptySquares;
@@ -118,8 +115,8 @@ namespace {
{
emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
Bitboard b1 = shift_bb<Up>(pawnsNotOn7) & emptySquares;
Bitboard b2 = shift_bb<Up>(b1 & TRank3BB) & emptySquares;
Bitboard b1 = shift<Up>(pawnsNotOn7) & emptySquares;
Bitboard b2 = shift<Up>(b1 & TRank3BB) & emptySquares;
if (Type == EVASIONS) // Consider only blocking squares
{
@@ -129,17 +126,20 @@ namespace {
if (Type == QUIET_CHECKS)
{
b1 &= pos.attacks_from<PAWN>(ci->ksq, Them);
b2 &= pos.attacks_from<PAWN>(ci->ksq, Them);
Square ksq = pos.square<KING>(Them);
b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(ksq, Them);
// Add pawn pushes which give discovered check. This is possible only
// if the pawn is not on the same file as the enemy king, because we
// don't generate captures. Note that a possible discovery check
// promotion has been already generated amongst the captures.
if (pawnsNotOn7 & ci->dcCandidates)
Bitboard dcCandidates = pos.discovered_check_candidates();
if (pawnsNotOn7 & dcCandidates)
{
Bitboard dc1 = shift_bb<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
Bitboard dc2 = shift_bb<Up>(dc1 & TRank3BB) & emptySquares;
Bitboard dc1 = shift<Up>(pawnsNotOn7 & dcCandidates) & emptySquares & ~file_bb(ksq);
Bitboard dc2 = shift<Up>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1;
b2 |= dc2;
@@ -168,25 +168,27 @@ namespace {
if (Type == EVASIONS)
emptySquares &= target;
Bitboard b1 = shift_bb<Right>(pawnsOn7) & enemies;
Bitboard b2 = shift_bb<Left >(pawnsOn7) & enemies;
Bitboard b3 = shift_bb<Up >(pawnsOn7) & emptySquares;
Bitboard b1 = shift<Right>(pawnsOn7) & enemies;
Bitboard b2 = shift<Left >(pawnsOn7) & enemies;
Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
Square ksq = pos.square<KING>(Them);
while (b1)
moveList = make_promotions<Type, Right>(moveList, pop_lsb(&b1), ci);
moveList = make_promotions<Type, Right>(moveList, pop_lsb(&b1), ksq);
while (b2)
moveList = make_promotions<Type, Left >(moveList, pop_lsb(&b2), ci);
moveList = make_promotions<Type, Left >(moveList, pop_lsb(&b2), ksq);
while (b3)
moveList = make_promotions<Type, Up >(moveList, pop_lsb(&b3), ci);
moveList = make_promotions<Type, Up >(moveList, pop_lsb(&b3), ksq);
}
// Standard and en-passant captures
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{
Bitboard b1 = shift_bb<Right>(pawnsNotOn7) & enemies;
Bitboard b2 = shift_bb<Left >(pawnsNotOn7) & enemies;
Bitboard b1 = shift<Right>(pawnsNotOn7) & enemies;
Bitboard b2 = shift<Left >(pawnsNotOn7) & enemies;
while (b1)
{
@@ -225,7 +227,7 @@ namespace {
template<PieceType Pt, bool Checks>
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us,
Bitboard target, const CheckInfo* ci) {
Bitboard target) {
assert(Pt != KING && Pt != PAWN);
@@ -236,17 +238,17 @@ namespace {
if (Checks)
{
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
&& !(PseudoAttacks[Pt][from] & target & ci->checkSquares[Pt]))
&& !(PseudoAttacks[Pt][from] & target & pos.check_squares(Pt)))
continue;
if (ci->dcCandidates && (ci->dcCandidates & from))
if (pos.discovered_check_candidates() & from)
continue;
}
Bitboard b = pos.attacks_from<Pt>(from) & target;
if (Checks)
b &= ci->checkSquares[Pt];
b &= pos.check_squares(Pt);
while (b)
*moveList++ = make_move(from, pop_lsb(&b));
@@ -257,16 +259,15 @@ namespace {
template<Color Us, GenType Type>
ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target,
const CheckInfo* ci = nullptr) {
ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) {
const bool Checks = Type == QUIET_CHECKS;
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);
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target);
moveList = generate_moves<BISHOP, Checks>(pos, moveList, Us, target);
moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target);
moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target);
if (Type != QUIET_CHECKS && Type != EVASIONS)
{
@@ -280,13 +281,13 @@ namespace {
{
if (pos.is_chess960())
{
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);
moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, true>(pos, moveList, Us);
moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, true>(pos, moveList, Us);
}
else
{
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);
moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, false>(pos, moveList, Us);
moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, false>(pos, moveList, Us);
}
}
@@ -335,8 +336,7 @@ ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
assert(!pos.checkers());
Color us = pos.side_to_move();
CheckInfo ci(pos);
Bitboard dc = ci.dcCandidates;
Bitboard dc = pos.discovered_check_candidates();
while (dc)
{
@@ -349,14 +349,14 @@ ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
Bitboard b = pos.attacks_from(Piece(pt), from) & ~pos.pieces();
if (pt == KING)
b &= ~PseudoAttacks[QUEEN][ci.ksq];
b &= ~PseudoAttacks[QUEEN][pos.square<KING>(~us)];
while (b)
*moveList++ = make_move(from, pop_lsb(&b));
}
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList, ~pos.pieces(), &ci)
: generate_all<BLACK, QUIET_CHECKS>(pos, moveList, ~pos.pieces(), &ci);
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList, ~pos.pieces())
: generate_all<BLACK, QUIET_CHECKS>(pos, moveList, ~pos.pieces());
}
@@ -411,7 +411,7 @@ ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
: generate<NON_EVASIONS>(pos, moveList);
while (cur != moveList)
if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT)
&& !pos.legal(*cur, pinned))
&& !pos.legal(*cur))
*cur = (--moveList)->move;
else
++cur;
+184 -152
View File
@@ -26,13 +26,12 @@
namespace {
enum Stages {
MAIN_SEARCH, GOOD_CAPTURES, KILLERS, GOOD_QUIETS, BAD_QUIETS, BAD_CAPTURES,
EVASION, ALL_EVASIONS,
QSEARCH_WITH_CHECKS, QCAPTURES_1, CHECKS,
QSEARCH_WITHOUT_CHECKS, QCAPTURES_2,
PROBCUT, PROBCUT_CAPTURES,
RECAPTURE, RECAPTURES,
STOP
MAIN_SEARCH, CAPTURES_INIT, GOOD_CAPTURES, KILLERS, COUNTERMOVE, QUIET_INIT, QUIET, BAD_CAPTURES,
EVASION, EVASIONS_INIT, ALL_EVASIONS,
PROBCUT, PROBCUT_INIT, PROBCUT_CAPTURES,
QSEARCH_WITH_CHECKS, QCAPTURES_1_INIT, QCAPTURES_1, QCHECKS,
QSEARCH_NO_CHECKS, QCAPTURES_2_INIT, QCAPTURES_2,
QSEARCH_RECAPTURES, QRECAPTURES
};
// Our insertion sort, which is guaranteed to be stable, as it should be
@@ -51,7 +50,7 @@ namespace {
// pick_best() finds the best move in the range (begin, end) and moves it to
// the front. It's faster than sorting all the moves in advance when there
// are few moves e.g. the possible captures.
// are few moves, e.g., the possible captures.
Move pick_best(ExtMove* begin, ExtMove* end)
{
std::swap(*begin, *std::max_element(begin, end));
@@ -64,23 +63,24 @@ namespace {
/// Constructors of the MovePicker class. As arguments we pass information
/// to help it to return the (presumably) good moves first, to decide which
/// moves to return (in the quiescence search, for instance, we only want to
/// search captures, promotions and some checks) and how important good move
/// search captures, promotions, and some checks) and how important good move
/// ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
const CounterMovesStats& cmh, Move cm, Search::Stack* s)
: pos(p), history(h), counterMovesHistory(&cmh), ss(s), countermove(cm), depth(d) {
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Search::Stack* s)
: pos(p), ss(s), depth(d) {
assert(d > DEPTH_ZERO);
Square prevSq = to_sq((ss-1)->currentMove);
countermove = pos.this_thread()->counterMoves[pos.piece_on(prevSq)][prevSq];
stage = pos.checkers() ? EVASION : MAIN_SEARCH;
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
endMoves += (ttMove != MOVE_NONE);
stage += (ttMove == MOVE_NONE);
}
MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
const HistoryStats& h, Square s)
: pos(p), history(h), counterMovesHistory(nullptr) {
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, Square s)
: pos(p) {
assert(d <= DEPTH_ZERO);
@@ -91,21 +91,21 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
stage = QSEARCH_WITH_CHECKS;
else if (d > DEPTH_QS_RECAPTURES)
stage = QSEARCH_WITHOUT_CHECKS;
stage = QSEARCH_NO_CHECKS;
else
{
stage = RECAPTURE;
stage = QSEARCH_RECAPTURES;
recaptureSquare = s;
ttm = MOVE_NONE;
return;
}
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
endMoves += (ttMove != MOVE_NONE);
stage += (ttMove == MOVE_NONE);
}
MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, Value th)
: pos(p), history(h), counterMovesHistory(nullptr), threshold(th) {
MovePicker::MovePicker(const Position& p, Move ttm, Value th)
: pos(p), threshold(th) {
assert(!pos.checkers());
@@ -115,9 +115,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, Value
ttMove = ttm
&& pos.pseudo_legal(ttm)
&& pos.capture(ttm)
&& pos.see(ttm) > threshold ? ttm : MOVE_NONE;
&& pos.see_ge(ttm, threshold + 1)? ttm : MOVE_NONE;
endMoves += (ttMove != MOVE_NONE);
stage += (ttMove == MOVE_NONE);
}
@@ -127,9 +127,9 @@ template<>
void MovePicker::score<CAPTURES>() {
// Winning and equal captures in the main search are ordered by MVV, preferring
// captures near our home rank. Surprisingly, this appears to perform slightly
// better than SEE based move ordering: exchanging big pieces before capturing
// better than SEE-based move ordering: exchanging big pieces before capturing
// a hanging piece probably helps to reduce the subtree size.
// In main search we want to push captures with negative SEE values to the
// In the main search we want to push captures with negative SEE values to the
// badCaptures[] array, but instead of doing it now we delay until the move
// has been picked up, saving some SEE calls in case we get a cutoff.
for (auto& m : *this)
@@ -140,93 +140,36 @@ void MovePicker::score<CAPTURES>() {
template<>
void MovePicker::score<QUIETS>() {
const HistoryStats& history = pos.this_thread()->history;
const FromToStats& fromTo = pos.this_thread()->fromTo;
const CounterMoveStats* cm = (ss-1)->counterMoves;
const CounterMoveStats* fm = (ss-2)->counterMoves;
const CounterMoveStats* f2 = (ss-4)->counterMoves;
Color c = pos.side_to_move();
for (auto& m : *this)
m.value = history[pos.moved_piece(m)][to_sq(m)]
+ (*counterMovesHistory)[pos.moved_piece(m)][to_sq(m)];
m.value = history[pos.moved_piece(m)][to_sq(m)]
+ (cm ? (*cm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO)
+ (fm ? (*fm)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO)
+ (f2 ? (*f2)[pos.moved_piece(m)][to_sq(m)] : VALUE_ZERO)
+ fromTo.get(c, m);
}
template<>
void MovePicker::score<EVASIONS>() {
// Try winning and equal captures captures ordered by MVV/LVA, then non-captures
// ordered by history value, then bad-captures and quiet moves with a negative
// SEE ordered by SEE value.
Value see;
// Try captures ordered by MVV/LVA, then non-captures ordered by history value
const HistoryStats& history = pos.this_thread()->history;
const FromToStats& fromTo = pos.this_thread()->fromTo;
Color c = pos.side_to_move();
for (auto& m : *this)
if ((see = pos.see_sign(m)) < VALUE_ZERO)
m.value = see - HistoryStats::Max; // At the bottom
else if (pos.capture(m))
if (pos.capture(m))
m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
- Value(type_of(pos.moved_piece(m))) + HistoryStats::Max;
else
m.value = history[pos.moved_piece(m)][to_sq(m)];
}
/// generate_next_stage() generates, scores and sorts the next bunch of moves,
/// when there are no more moves to try for the current stage.
void MovePicker::generate_next_stage() {
assert(stage != STOP);
cur = moves;
switch (++stage) {
case GOOD_CAPTURES: case QCAPTURES_1: case QCAPTURES_2:
case PROBCUT_CAPTURES: case RECAPTURES:
endMoves = generate<CAPTURES>(pos, moves);
score<CAPTURES>();
break;
case KILLERS:
killers[0] = ss->killers[0];
killers[1] = ss->killers[1];
killers[2] = countermove;
cur = killers;
endMoves = cur + 2 + (countermove != killers[0] && countermove != killers[1]);
break;
case GOOD_QUIETS:
endQuiets = endMoves = generate<QUIETS>(pos, moves);
score<QUIETS>();
endMoves = std::partition(cur, endMoves, [](const ExtMove& m) { return m.value > VALUE_ZERO; });
insertion_sort(cur, endMoves);
break;
case BAD_QUIETS:
cur = endMoves;
endMoves = endQuiets;
if (depth >= 3 * ONE_PLY)
insertion_sort(cur, endMoves);
break;
case BAD_CAPTURES:
// Just pick them in reverse order to get correct ordering
cur = moves + MAX_MOVES - 1;
endMoves = endBadCaptures;
break;
case ALL_EVASIONS:
endMoves = generate<EVASIONS>(pos, moves);
if (endMoves - moves > 1)
score<EVASIONS>();
break;
case CHECKS:
endMoves = generate<QUIET_CHECKS>(pos, moves);
break;
case EVASION: case QSEARCH_WITH_CHECKS: case QSEARCH_WITHOUT_CHECKS:
case PROBCUT: case RECAPTURE: case STOP:
stage = STOP;
break;
default:
assert(false);
}
m.value = history[pos.moved_piece(m)][to_sq(m)] + fromTo.get(c, m);
}
@@ -239,80 +182,169 @@ Move MovePicker::next_move() {
Move move;
while (true)
{
while (cur == endMoves && stage != STOP)
generate_next_stage();
switch (stage) {
switch (stage) {
case MAIN_SEARCH: case EVASION: case QSEARCH_WITH_CHECKS:
case QSEARCH_NO_CHECKS: case PROBCUT:
++stage;
return ttMove;
case MAIN_SEARCH: case EVASION: case QSEARCH_WITH_CHECKS:
case QSEARCH_WITHOUT_CHECKS: case PROBCUT:
++cur;
return ttMove;
case CAPTURES_INIT:
endBadCaptures = cur = moves;
endMoves = generate<CAPTURES>(pos, cur);
score<CAPTURES>();
++stage;
case GOOD_CAPTURES:
case GOOD_CAPTURES:
while (cur < endMoves)
{
move = pick_best(cur++, endMoves);
if (move != ttMove)
{
if (pos.see_sign(move) >= VALUE_ZERO)
if (pos.see_ge(move, VALUE_ZERO))
return move;
// Losing capture, move it to the tail of the array
*endBadCaptures-- = move;
// Losing capture, move it to the beginning of the array
*endBadCaptures++ = move;
}
break;
}
case KILLERS:
move = *cur++;
if ( move != MOVE_NONE
&& move != ttMove
&& pos.pseudo_legal(move)
&& !pos.capture(move))
return move;
break;
++stage;
move = ss->killers[0]; // First killer move
if ( move != MOVE_NONE
&& move != ttMove
&& pos.pseudo_legal(move)
&& !pos.capture(move))
return move;
case GOOD_QUIETS: case BAD_QUIETS:
case KILLERS:
++stage;
move = ss->killers[1]; // Second killer move
if ( move != MOVE_NONE
&& move != ttMove
&& pos.pseudo_legal(move)
&& !pos.capture(move))
return move;
case COUNTERMOVE:
++stage;
move = countermove;
if ( move != MOVE_NONE
&& move != ttMove
&& move != ss->killers[0]
&& move != ss->killers[1]
&& pos.pseudo_legal(move)
&& !pos.capture(move))
return move;
case QUIET_INIT:
cur = endBadCaptures;
endMoves = generate<QUIETS>(pos, cur);
score<QUIETS>();
if (depth < 3 * ONE_PLY)
{
ExtMove* goodQuiet = std::partition(cur, endMoves, [](const ExtMove& m)
{ return m.value > VALUE_ZERO; });
insertion_sort(cur, goodQuiet);
} else
insertion_sort(cur, endMoves);
++stage;
case QUIET:
while (cur < endMoves)
{
move = *cur++;
if ( move != ttMove
&& move != killers[0]
&& move != killers[1]
&& move != killers[2])
&& move != ss->killers[0]
&& move != ss->killers[1]
&& move != countermove)
return move;
break;
}
++stage;
cur = moves; // Point to beginning of bad captures
case BAD_CAPTURES:
return *cur--;
case BAD_CAPTURES:
if (cur < endBadCaptures)
return *cur++;
break;
case ALL_EVASIONS: case QCAPTURES_1: case QCAPTURES_2:
case EVASIONS_INIT:
cur = moves;
endMoves = generate<EVASIONS>(pos, cur);
score<EVASIONS>();
++stage;
case ALL_EVASIONS:
while (cur < endMoves)
{
move = pick_best(cur++, endMoves);
if (move != ttMove)
return move;
}
break;
case PROBCUT_INIT:
cur = moves;
endMoves = generate<CAPTURES>(pos, cur);
score<CAPTURES>();
++stage;
case PROBCUT_CAPTURES:
while (cur < endMoves)
{
move = pick_best(cur++, endMoves);
if ( move != ttMove
&& pos.see_ge(move, threshold + 1))
return move;
}
break;
case QCAPTURES_1_INIT: case QCAPTURES_2_INIT:
cur = moves;
endMoves = generate<CAPTURES>(pos, cur);
score<CAPTURES>();
++stage;
case QCAPTURES_1: case QCAPTURES_2:
while (cur < endMoves)
{
move = pick_best(cur++, endMoves);
if (move != ttMove)
return move;
}
if (stage == QCAPTURES_2)
break;
cur = moves;
endMoves = generate<QUIET_CHECKS>(pos, cur);
++stage;
case PROBCUT_CAPTURES:
move = pick_best(cur++, endMoves);
if (move != ttMove && pos.see(move) > threshold)
return move;
break;
case QCHECKS:
while (cur < endMoves)
{
move = cur++->move;
if (move != ttMove)
return move;
}
break;
case RECAPTURES:
case QSEARCH_RECAPTURES:
cur = moves;
endMoves = generate<CAPTURES>(pos, cur);
score<CAPTURES>();
++stage;
case QRECAPTURES:
while (cur < endMoves)
{
move = pick_best(cur++, endMoves);
if (to_sq(move) == recaptureSquare)
return move;
break;
case CHECKS:
move = *cur++;
if (move != ttMove)
return move;
break;
case STOP:
return MOVE_NONE;
default:
assert(false);
}
break;
default:
assert(false);
}
return MOVE_NONE;
}
+34 -24
View File
@@ -26,7 +26,6 @@
#include "movegen.h"
#include "position.h"
#include "search.h"
#include "types.h"
@@ -45,30 +44,44 @@ struct Stats {
const T* operator[](Piece pc) const { return table[pc]; }
T* operator[](Piece pc) { return table[pc]; }
void clear() { std::memset(table, 0, sizeof(table)); }
void update(Piece pc, Square to, Move m) {
if (m != table[pc][to])
table[pc][to] = m;
}
void update(Piece pc, Square to, Move m) { table[pc][to] = m; }
void update(Piece pc, Square to, Value v) {
if (abs(int(v)) >= 324)
return;
table[pc][to] -= table[pc][to] * abs(int(v)) / (CM ? 512 : 324);
table[pc][to] += int(v) * (CM ? 64 : 32);
table[pc][to] -= table[pc][to] * abs(int(v)) / (CM ? 936 : 324);
table[pc][to] += int(v) * 32;
}
private:
T table[PIECE_NB][SQUARE_NB];
};
typedef Stats<Move> MovesStats;
typedef Stats<Move> MoveStats;
typedef Stats<Value, false> HistoryStats;
typedef Stats<Value, true> CounterMovesStats;
typedef Stats<CounterMovesStats> CounterMovesHistoryStats;
typedef Stats<Value, true> CounterMoveStats;
typedef Stats<CounterMoveStats> CounterMoveHistoryStats;
struct FromToStats {
Value get(Color c, Move m) const { return table[c][from_sq(m)][to_sq(m)]; }
void clear() { std::memset(table, 0, sizeof(table)); }
void update(Color c, Move m, Value v) {
if (abs(int(v)) >= 324)
return;
Square from = from_sq(m);
Square to = to_sq(m);
table[c][from][to] -= table[c][from][to] * abs(int(v)) / 324;
table[c][from][to] += int(v) * 32;
}
private:
Value table[COLOR_NB][SQUARE_NB][SQUARE_NB];
};
/// MovePicker class is used to pick one pseudo legal move at a time from the
@@ -77,37 +90,34 @@ typedef Stats<CounterMovesStats> CounterMovesHistoryStats;
/// when MOVE_NONE is returned. In order to improve the efficiency of the alpha
/// beta algorithm, MovePicker attempts to return the moves which are most likely
/// to get a cut-off first.
namespace Search { struct Stack; }
class MovePicker {
public:
MovePicker(const MovePicker&) = delete;
MovePicker& operator=(const MovePicker&) = delete;
MovePicker(const Position&, Move, Depth, const HistoryStats&, Square);
MovePicker(const Position&, Move, const HistoryStats&, Value);
MovePicker(const Position&, Move, Depth, const HistoryStats&, const CounterMovesStats&, Move, Search::Stack*);
MovePicker(const Position&, Move, Value);
MovePicker(const Position&, Move, Depth, Square);
MovePicker(const Position&, Move, Depth, Search::Stack*);
Move next_move();
private:
template<GenType> void score();
void generate_next_stage();
ExtMove* begin() { return moves; }
ExtMove* begin() { return cur; }
ExtMove* end() { return endMoves; }
const Position& pos;
const HistoryStats& history;
const CounterMovesStats* counterMovesHistory;
Search::Stack* ss;
const Search::Stack* ss;
Move countermove;
Depth depth;
Move ttMove;
ExtMove killers[3];
Square recaptureSquare;
Value threshold;
int stage;
ExtMove *endQuiets, *endBadCaptures = moves + MAX_MOVES - 1;
ExtMove moves[MAX_MOVES], *cur = moves, *endMoves = moves;
ExtMove *cur, *endMoves, *endBadCaptures;
ExtMove moves[MAX_MOVES];
};
#endif // #ifndef MOVEPICK_H_INCLUDED
+56 -84
View File
@@ -22,7 +22,6 @@
#include <cassert>
#include "bitboard.h"
#include "bitcount.h"
#include "pawns.h"
#include "position.h"
#include "thread.h"
@@ -32,41 +31,34 @@ namespace {
#define V Value
#define S(mg, eg) make_score(mg, eg)
// Isolated pawn penalty by opposed flag and file
const Score Isolated[2][FILE_NB] = {
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52),
S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35), S(25, 30) } };
// Isolated pawn penalty by opposed flag
const Score Isolated[2] = { S(45, 40), S(30, 27) };
// Backward pawn penalty by opposed flag
const Score Backward[2] = { S(67, 42), S(49, 24) };
// Unsupported pawn penalty, for pawns which are neither isolated or backward
const Score Unsupported = S(20, 10);
const Score Backward[2] = { S(56, 33), S(41, 19) };
// Unsupported pawn penalty for pawns which are neither isolated or backward
const Score Unsupported = S(17, 8);
// Connected pawn bonus by opposed, phalanx, twice supported and rank
Score Connected[2][2][2][RANK_NB];
// Doubled pawn penalty by file
const Score Doubled[FILE_NB] = {
S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) };
// Doubled pawn penalty
const Score Doubled = S(18,38);
// Lever 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) };
// Center bind bonus, when two pawns controls the same central square
const Score CenterBind = S(16, 0);
S(17, 16), S(33, 32), S(0, 0), S(0, 0)
};
// Weakness of our pawn shelter in front of the king by [distance from edge][rank]
const Value ShelterWeakness[][RANK_NB] = {
{ V( 97), V(21), V(26), V(51), V(87), V( 89), V( 99) },
{ V(120), V( 0), V(28), V(76), V(88), V(103), V(104) },
{ V(101), V( 7), V(54), V(78), V(77), V( 92), V(101) },
{ V( 80), V(11), V(44), V(68), V(87), V( 90), V(119) } };
{ V( 80), V(11), V(44), V(68), V(87), V( 90), V(119) }
};
// Danger of enemy pawns moving toward our king by [type][distance from edge][rank]
const Value StormDanger[][4][RANK_NB] = {
@@ -85,7 +77,8 @@ namespace {
{ { V( 0), V(-283), V(-281), V(57), V(31) },
{ V( 0), V( 58), V( 141), V(39), V(18) },
{ V( 0), V( 65), V( 142), V(48), V(32) },
{ V( 0), V( 60), V( 126), V(51), V(19) } } };
{ V( 0), V( 60), V( 126), V(51), V(19) } }
};
// 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.
@@ -97,18 +90,14 @@ namespace {
template<Color Us>
Score evaluate(const Position& pos, Pawns::Entry* e) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Up = (Us == WHITE ? NORTH : SOUTH);
const Square Right = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
const Square Left = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
const Bitboard CenterBindMask =
Us == WHITE ? (FileDBB | FileEBB) & (Rank5BB | Rank6BB | Rank7BB)
: (FileDBB | FileEBB) & (Rank4BB | Rank3BB | Rank2BB);
Bitboard b, neighbours, doubled, supported, phalanx;
Bitboard b, neighbours, stoppers, doubled, supported, phalanx;
Square s;
bool passed, isolated, opposed, backward, lever, connected;
bool opposed, lever, connected, backward;
Score score = SCORE_ZERO;
const Square* pl = pos.squares<PAWN>(Us);
const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)];
@@ -116,11 +105,11 @@ namespace {
Bitboard ourPawns = pos.pieces(Us , PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0;
e->kingSquares[Us] = SQ_NONE;
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = 0;
e->semiopenFiles[Us] = 0xFF;
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns);
e->pawnsOnSquares[Us][BLACK] = popcount<Max15>(ourPawns & DarkSquares);
e->kingSquares[Us] = SQ_NONE;
e->pawnAttacks[Us] = shift<Right>(ourPawns) | shift<Left>(ourPawns);
e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & DarkSquares);
e->pawnsOnSquares[Us][WHITE] = pos.count<PAWN>(Us) - e->pawnsOnSquares[Us][BLACK];
// Loop through all pawns of the current color and score each pawn
@@ -130,53 +119,44 @@ namespace {
File f = file_of(s);
e->semiopenFiles[Us] &= ~(1 << f);
e->semiopenFiles[Us] &= ~(1 << f);
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
// Flag the pawn
neighbours = ourPawns & adjacent_files_bb(f);
doubled = ourPawns & forward_bb(Us, s);
opposed = theirPawns & forward_bb(Us, s);
passed = !(theirPawns & passed_pawn_mask(Us, s));
lever = theirPawns & pawnAttacksBB[s];
phalanx = neighbours & rank_bb(s);
supported = neighbours & rank_bb(s - Up);
connected = supported | phalanx;
isolated = !neighbours;
opposed = theirPawns & forward_bb(Us, s);
stoppers = theirPawns & passed_pawn_mask(Us, s);
lever = theirPawns & pawnAttacksBB[s];
doubled = ourPawns & (s + Up);
neighbours = ourPawns & adjacent_files_bb(f);
phalanx = neighbours & rank_bb(s);
supported = neighbours & rank_bb(s - Up);
connected = supported | phalanx;
// Test for backward pawn.
// If the pawn is passed, isolated, lever or connected it cannot be
// backward. If there are friendly pawns behind on adjacent files
// or if it is sufficiently advanced, it cannot be backward either.
if ( (passed | isolated | lever | connected)
|| (ourPawns & pawn_attack_span(Them, s))
|| (relative_rank(Us, s) >= RANK_5))
// A pawn is backward when it is behind all pawns of the same color on the
// adjacent files and cannot be safely advanced.
if (!neighbours || lever || relative_rank(Us, s) >= RANK_5)
backward = false;
else
{
// We now know there are no friendly pawns beside or behind this
// pawn on adjacent files. We now check whether the pawn is
// backward by looking in the forward direction on the adjacent
// files, and picking the closest pawn there.
b = pawn_attack_span(Us, s) & (ourPawns | theirPawns);
b = pawn_attack_span(Us, s) & rank_bb(backmost_sq(Us, b));
// Find the backmost rank with neighbours or stoppers
b = rank_bb(backmost_sq(Us, neighbours | stoppers));
// If we have an enemy pawn in the same or next rank, the pawn is
// backward because it cannot advance without being captured.
backward = (b | shift_bb<Up>(b)) & theirPawns;
// The pawn is backward when it cannot safely progress to that rank:
// either there is a stopper in the way on this rank, or there is a
// stopper on adjacent file which controls the way to that rank.
backward = (b | shift<Up>(b & adjacent_files_bb(f))) & stoppers;
assert(!backward || !(pawn_attack_span(Them, s + Up) & neighbours));
}
assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns));
// Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate them. Only the frontmost passed
// pawn on each file is considered a true passed pawn.
if (passed && !doubled)
// full attack info to evaluate them.
if (!stoppers && !(ourPawns & forward_bb(Us, s)))
e->passedPawns[Us] |= s;
// Score this pawn
if (isolated)
score -= Isolated[opposed][f];
if (!neighbours)
score -= Isolated[opposed];
else if (backward)
score -= Backward[opposed];
@@ -188,18 +168,12 @@ namespace {
score += Connected[opposed][!!phalanx][more_than_one(supported)][relative_rank(Us, s)];
if (doubled)
score -= Doubled[f] / distance<Rank>(s, frontmost_sq(Us, doubled));
score -= Doubled;
if (lever)
score += Lever[relative_rank(Us, s)];
}
b = e->semiopenFiles[Us] ^ 0xFF;
e->pawnSpan[Us] = b ? int(msb(b) - lsb(b)) : 0;
b = shift_bb<Right>(ourPawns) & shift_bb<Left>(ourPawns) & CenterBindMask;
score += CenterBind * popcount<Max15>(b);
return score;
}
@@ -211,9 +185,9 @@ namespace Pawns {
/// hard-coded tables, when makes sense, we prefer to calculate them with a formula
/// to reduce independent parameters and to allow easier tuning and better insight.
void init()
{
static const int Seed[RANK_NB] = { 0, 6, 15, 10, 57, 75, 135, 258 };
void init() {
static const int Seed[RANK_NB] = { 0, 8, 19, 13, 71, 94, 169, 324 };
for (int opposed = 0; opposed <= 1; ++opposed)
for (int phalanx = 0; phalanx <= 1; ++phalanx)
@@ -222,7 +196,7 @@ void init()
{
int v = (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed;
v += (apex ? v / 2 : 0);
Connected[opposed][phalanx][apex][r] = make_score(3 * v / 2, v);
Connected[opposed][phalanx][apex][r] = make_score(v, v * 5 / 8);
}
}
@@ -242,7 +216,8 @@ Entry* probe(const Position& pos) {
e->key = key;
e->score = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e);
e->asymmetry = popcount<Max15>(e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]);
e->asymmetry = popcount(e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]);
e->openFiles = popcount(e->semiopenFiles[WHITE] & e->semiopenFiles[BLACK]);
return e;
}
@@ -297,9 +272,6 @@ Score Entry::do_king_safety(const Position& pos, Square ksq) {
if (pawns)
while (!(DistanceRingBB[ksq][minKingPawnDistance++] & pawns)) {}
if (relative_rank(Us, ksq) > RANK_4)
return make_score(0, -16 * minKingPawnDistance);
Value bonus = shelter_storm<Us>(pos, ksq);
// If we can castle use the bonus after the castling if it is bigger
+3 -3
View File
@@ -37,8 +37,8 @@ struct Entry {
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
int pawn_span(Color c) const { return pawnSpan[c]; }
int pawn_asymmetry() const { return asymmetry; }
int open_files() const { return openFiles; }
int semiopen_file(Color c, File f) const {
return semiopenFiles[c] & (1 << f);
@@ -53,7 +53,7 @@ struct Entry {
}
template<Color Us>
Score king_safety(const Position& pos, Square ksq) {
Score king_safety(const Position& pos, Square ksq) {
return kingSquares[Us] == ksq && castlingRights[Us] == pos.can_castle(Us)
? kingSafety[Us] : (kingSafety[Us] = do_king_safety<Us>(pos, ksq));
}
@@ -73,9 +73,9 @@ struct Entry {
Score kingSafety[COLOR_NB];
int castlingRights[COLOR_NB];
int semiopenFiles[COLOR_NB];
int pawnSpan[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
int asymmetry;
int openFiles;
};
typedef HashTable<Entry, 16384> Table;
+210 -239
View File
@@ -20,11 +20,12 @@
#include <algorithm>
#include <cassert>
#include <cstring> // For std::memset, std::memcmp
#include <cstddef> // For offsetof()
#include <cstring> // For std::memset, std::memcmp
#include <iomanip>
#include <sstream>
#include "bitcount.h"
#include "bitboard.h"
#include "misc.h"
#include "movegen.h"
#include "position.h"
@@ -34,21 +35,18 @@
using std::string;
Value PieceValue[PHASE_NB][PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
namespace PSQT {
extern Score psq[PIECE_NB][SQUARE_NB];
}
namespace Zobrist {
Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
Key psq[PIECE_NB][SQUARE_NB];
Key enpassant[FILE_NB];
Key castling[CASTLING_RIGHT_NB];
Key side;
Key exclusion;
}
Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion; }
namespace {
const string PieceToChar(" PNBRQK pnbrqk");
@@ -85,25 +83,6 @@ PieceType min_attacker<KING>(const Bitboard*, Square, Bitboard, Bitboard&, Bitbo
} // namespace
/// CheckInfo constructor
CheckInfo::CheckInfo(const Position& pos) {
Color them = ~pos.side_to_move();
ksq = pos.square<KING>(them);
pinned = pos.pinned_pieces(pos.side_to_move());
dcCandidates = pos.discovered_check_candidates();
checkSquares[PAWN] = pos.attacks_from<PAWN>(ksq, them);
checkSquares[KNIGHT] = pos.attacks_from<KNIGHT>(ksq);
checkSquares[BISHOP] = pos.attacks_from<BISHOP>(ksq);
checkSquares[ROOK] = pos.attacks_from<ROOK>(ksq);
checkSquares[QUEEN] = checkSquares[BISHOP] | checkSquares[ROOK];
checkSquares[KING] = 0;
}
/// operator<<(Position) returns an ASCII representation of the position
std::ostream& operator<<(std::ostream& os, const Position& pos) {
@@ -135,10 +114,9 @@ void Position::init() {
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] = rng.rand<Key>();
for (Piece pc : Pieces)
for (Square s = SQ_A1; s <= SQ_H8; ++s)
Zobrist::psq[pc][s] = rng.rand<Key>();
for (File f = FILE_A; f <= FILE_H; ++f)
Zobrist::enpassant[f] = rng.rand<Key>();
@@ -155,38 +133,6 @@ void Position::init() {
}
Zobrist::side = rng.rand<Key>();
Zobrist::exclusion = rng.rand<Key>();
}
/// 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) {
std::memcpy(this, &pos, sizeof(Position));
std::memcpy(&startState, st, sizeof(StateInfo));
st = &startState;
nodes = 0;
assert(pos_is_ok());
return *this;
}
/// Position::clear() erases the position object to a pristine state, with an
/// empty board, white to move, and no castling rights.
void Position::clear() {
std::memset(this, 0, sizeof(Position));
startState.epSquare = SQ_NONE;
st = &startState;
for (int i = 0; i < PIECE_TYPE_NB; ++i)
for (int j = 0; j < 16; ++j)
pieceList[WHITE][i][j] = pieceList[BLACK][i][j] = SQ_NONE;
}
@@ -194,7 +140,7 @@ void Position::clear() {
/// This function is not very robust - make sure that input FENs are correct,
/// this is assumed to be the responsibility of the GUI.
void Position::set(const string& fenStr, bool isChess960, Thread* th) {
Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Thread* th) {
/*
A FEN string defines a particular position using only the ASCII character set.
@@ -234,7 +180,11 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
Square sq = SQ_A8;
std::istringstream ss(fenStr);
clear();
std::memset(this, 0, sizeof(Position));
std::memset(si, 0, sizeof(StateInfo));
std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE);
st = si;
ss >> std::noskipws;
// 1. Piece placement
@@ -248,7 +198,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
else if ((idx = PieceToChar.find(token)) != string::npos)
{
put_piece(color_of(Piece(idx)), type_of(Piece(idx)), sq);
put_piece(Piece(idx), sq);
++sq;
}
}
@@ -295,6 +245,8 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN)))
st->epSquare = SQ_NONE;
}
else
st->epSquare = SQ_NONE;
// 5-6. Halfmove clock and fullmove number
ss >> std::skipws >> st->rule50 >> gamePly;
@@ -308,6 +260,8 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
set_state(st);
assert(pos_is_ok());
return *this;
}
@@ -338,6 +292,24 @@ void Position::set_castling_right(Color c, Square rfrom) {
}
/// Position::set_check_info() sets king attacks to detect if a move gives check
void Position::set_check_info(StateInfo* si) const {
si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square<KING>(WHITE), si->pinnersForKing[WHITE]);
si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square<KING>(BLACK), si->pinnersForKing[BLACK]);
Square ksq = square<KING>(~sideToMove);
si->checkSquares[PAWN] = attacks_from<PAWN>(ksq, ~sideToMove);
si->checkSquares[KNIGHT] = attacks_from<KNIGHT>(ksq);
si->checkSquares[BISHOP] = attacks_from<BISHOP>(ksq);
si->checkSquares[ROOK] = attacks_from<ROOK>(ksq);
si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK];
si->checkSquares[KING] = 0;
}
/// Position::set_state() computes the hash keys of the position, and other
/// data that once computed is updated incrementally as moves are made.
/// The function is only used when a new position is set up, and to verify
@@ -348,15 +320,16 @@ void Position::set_state(StateInfo* si) const {
si->key = si->pawnKey = si->materialKey = 0;
si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO;
si->psq = SCORE_ZERO;
si->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
set_check_info(si);
for (Bitboard b = pieces(); b; )
{
Square s = pop_lsb(&b);
Piece pc = piece_on(s);
si->key ^= Zobrist::psq[color_of(pc)][type_of(pc)][s];
si->psq += PSQT::psq[color_of(pc)][type_of(pc)][s];
si->key ^= Zobrist::psq[pc][s];
si->psq += PSQT::psq[pc][s];
}
if (si->epSquare != SQ_NONE)
@@ -370,17 +343,17 @@ void Position::set_state(StateInfo* si) const {
for (Bitboard b = pieces(PAWN); b; )
{
Square s = pop_lsb(&b);
si->pawnKey ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s];
si->pawnKey ^= Zobrist::psq[piece_on(s)][s];
}
for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= KING; ++pt)
for (int cnt = 0; cnt < pieceCount[c][pt]; ++cnt)
si->materialKey ^= Zobrist::psq[c][pt][cnt];
for (Piece pc : Pieces)
{
if (type_of(pc) != PAWN && type_of(pc) != KING)
si->nonPawnMaterial[color_of(pc)] += pieceCount[pc] * PieceValue[MG][pc];
for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = KNIGHT; pt <= QUEEN; ++pt)
si->nonPawnMaterial[c] += pieceCount[c][pt] * PieceValue[MG][pt];
for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
si->materialKey ^= Zobrist::psq[pc][cnt];
}
}
@@ -447,28 +420,33 @@ Phase Position::game_phase() const {
}
/// Position::check_blockers() returns a bitboard of all the pieces with color
/// 'c' that are blocking check on the king with color 'kingColor'. A piece
/// blocks a check if removing that piece from the board would result in a
/// position where the king is in check. A check blocking piece can be either a
/// pinned or a discovered check piece, according if its color 'c' is the same
/// or the opposite of 'kingColor'.
/// Position::slider_blockers() returns a bitboard of all the pieces (both colors)
/// that are blocking attacks on the square 's' from 'sliders'. A piece blocks a
/// slider if removing that piece from the board would result in a position where
/// square 's' is attacked. For example, a king-attack blocking piece can be either
/// a pinned or a discovered check piece, according if its color is the opposite
/// or the same of the color of the slider.
Bitboard Position::check_blockers(Color c, Color kingColor) const {
Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const {
Bitboard b, pinners, result = 0;
Square ksq = square<KING>(kingColor);
Bitboard result = 0;
pinners = 0;
// Pinners are sliders that give check when a pinned piece is removed
pinners = ( (pieces( ROOK, QUEEN) & PseudoAttacks[ROOK ][ksq])
| (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq])) & pieces(~kingColor);
// Snipers are sliders that attack 's' when a piece is removed
Bitboard snipers = ( (PseudoAttacks[ROOK ][s] & pieces(QUEEN, ROOK))
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
while (pinners)
while (snipers)
{
b = between_bb(ksq, pop_lsb(&pinners)) & pieces();
Square sniperSq = pop_lsb(&snipers);
Bitboard b = between_bb(s, sniperSq) & pieces();
if (!more_than_one(b))
result |= b & pieces(c);
if (!more_than_one(b))
{
result |= b;
if (b & pieces(color_of(piece_on(s))))
pinners |= sniperSq;
}
}
return result;
}
@@ -490,10 +468,9 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
/// Position::legal() tests whether a pseudo-legal move is legal
bool Position::legal(Move m, Bitboard pinned) const {
bool Position::legal(Move m) const {
assert(is_ok(m));
assert(pinned == pinned_pieces(sideToMove));
Color us = sideToMove;
Square from = from_sq(m);
@@ -528,8 +505,7 @@ bool Position::legal(Move m, Bitboard pinned) const {
// A non-king move is legal if and only if it is not pinned or it
// is moving along the ray towards or away from the king.
return !pinned
|| !(pinned & from)
return !(pinned_pieces(us) & from)
|| aligned(from, to_sq(m), square<KING>(us));
}
@@ -608,23 +584,21 @@ bool Position::pseudo_legal(const Move m) const {
/// Position::gives_check() tests whether a pseudo-legal move gives a check
bool Position::gives_check(Move m, const CheckInfo& ci) const {
bool Position::gives_check(Move m) const {
assert(is_ok(m));
assert(ci.dcCandidates == discovered_check_candidates());
assert(color_of(moved_piece(m)) == sideToMove);
Square from = from_sq(m);
Square to = to_sq(m);
// Is there a direct check?
if (ci.checkSquares[type_of(piece_on(from))] & to)
if (st->checkSquares[type_of(piece_on(from))] & to)
return true;
// Is there a discovered check?
if ( ci.dcCandidates
&& (ci.dcCandidates & from)
&& !aligned(from, to, ci.ksq))
if ( (discovered_check_candidates() & from)
&& !aligned(from, to, square<KING>(~sideToMove)))
return true;
switch (type_of(m))
@@ -633,7 +607,7 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
return false;
case PROMOTION:
return attacks_bb(Piece(promotion_type(m)), to, pieces() ^ from) & ci.ksq;
return attacks_bb(Piece(promotion_type(m)), to, pieces() ^ from) & square<KING>(~sideToMove);
// En passant capture with check? We have already handled the case
// of direct checks and ordinary discovered check, so the only case we
@@ -644,8 +618,8 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
Square capsq = make_square(file_of(to), rank_of(from));
Bitboard b = (pieces() ^ from ^ capsq) | to;
return (attacks_bb< ROOK>(ci.ksq, b) & pieces(sideToMove, QUEEN, ROOK))
| (attacks_bb<BISHOP>(ci.ksq, b) & pieces(sideToMove, QUEEN, BISHOP));
return (attacks_bb< ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK))
| (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP));
}
case CASTLING:
{
@@ -654,8 +628,8 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const {
Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1);
Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1);
return (PseudoAttacks[ROOK][rto] & ci.ksq)
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & ci.ksq);
return (PseudoAttacks[ROOK][rto] & square<KING>(~sideToMove))
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
}
default:
assert(false);
@@ -693,23 +667,24 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
Color them = ~us;
Square from = from_sq(m);
Square to = to_sq(m);
PieceType pt = type_of(piece_on(from));
PieceType captured = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
Piece pc = piece_on(from);
Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to);
assert(color_of(piece_on(from)) == us);
assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == (type_of(m) != CASTLING ? them : us));
assert(captured != KING);
assert(color_of(pc) == us);
assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us));
assert(type_of(captured) != KING);
if (type_of(m) == CASTLING)
{
assert(pt == KING);
assert(pc == make_piece(us, KING));
assert(captured == make_piece(us, ROOK));
Square rfrom, rto;
do_castling<true>(us, from, to, rfrom, rto);
captured = NO_PIECE_TYPE;
st->psq += PSQT::psq[us][ROOK][rto] - PSQT::psq[us][ROOK][rfrom];
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
st->psq += PSQT::psq[captured][rto] - PSQT::psq[captured][rfrom];
k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
captured = NO_PIECE;
}
if (captured)
@@ -718,13 +693,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// If the captured piece is a pawn, update pawn hash key, otherwise
// update non-pawn material.
if (captured == PAWN)
if (type_of(captured) == PAWN)
{
if (type_of(m) == ENPASSANT)
{
capsq -= pawn_push(us);
assert(pt == PAWN);
assert(pc == make_piece(us, PAWN));
assert(to == st->epSquare);
assert(relative_rank(us, to) == RANK_6);
assert(piece_on(to) == NO_PIECE);
@@ -733,28 +708,28 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
board[capsq] = NO_PIECE; // Not done by remove_piece()
}
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
st->pawnKey ^= Zobrist::psq[captured][capsq];
}
else
st->nonPawnMaterial[them] -= PieceValue[MG][captured];
// Update board and piece lists
remove_piece(them, captured, capsq);
remove_piece(captured, capsq);
// Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[them][captured][capsq];
st->materialKey ^= Zobrist::psq[them][captured][pieceCount[them][captured]];
k ^= Zobrist::psq[captured][capsq];
st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]];
prefetch(thisThread->materialTable[st->materialKey]);
// Update incremental scores
st->psq -= PSQT::psq[them][captured][capsq];
st->psq -= PSQT::psq[captured][capsq];
// Reset rule 50 counter
st->rule50 = 0;
}
// Update hash key
k ^= Zobrist::psq[us][pt][from] ^ Zobrist::psq[us][pt][to];
k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
// Reset en passant square
if (st->epSquare != SQ_NONE)
@@ -773,10 +748,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// Move the piece. The tricky Chess960 castling is handled earlier
if (type_of(m) != CASTLING)
move_piece(us, pt, from, to);
move_piece(pc, from, to);
// If the moving piece is a pawn do some special extra work
if (pt == PAWN)
if (type_of(pc) == PAWN)
{
// Set en-passant square if the moved pawn can be captured
if ( (int(to) ^ int(from)) == 16
@@ -788,29 +763,29 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
else if (type_of(m) == PROMOTION)
{
PieceType promotion = promotion_type(m);
Piece promotion = make_piece(us, promotion_type(m));
assert(relative_rank(us, to) == RANK_8);
assert(promotion >= KNIGHT && promotion <= QUEEN);
assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
remove_piece(us, PAWN, to);
put_piece(us, promotion, to);
remove_piece(pc, to);
put_piece(promotion, to);
// Update hash keys
k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to];
st->pawnKey ^= Zobrist::psq[us][PAWN][to];
st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]-1]
^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]];
k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to];
st->pawnKey ^= Zobrist::psq[pc][to];
st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1]
^ Zobrist::psq[pc][pieceCount[pc]];
// Update incremental score
st->psq += PSQT::psq[us][promotion][to] - PSQT::psq[us][PAWN][to];
st->psq += PSQT::psq[promotion][to] - PSQT::psq[pc][to];
// Update material
st->nonPawnMaterial[us] += PieceValue[MG][promotion];
}
// Update pawn hash key and prefetch access to pawnsTable
st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to];
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
prefetch(thisThread->pawnsTable[st->pawnKey]);
// Reset rule 50 draw counter
@@ -818,10 +793,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
}
// Update incremental scores
st->psq += PSQT::psq[us][pt][to] - PSQT::psq[us][pt][from];
st->psq += PSQT::psq[pc][to] - PSQT::psq[pc][from];
// Set capture piece
st->capturedType = captured;
st->capturedPiece = captured;
// Update the key with the final value
st->key = k;
@@ -831,6 +806,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
sideToMove = ~sideToMove;
// Update king attacks used for fast check detection
set_check_info(st);
assert(pos_is_ok());
}
@@ -847,20 +825,20 @@ void Position::undo_move(Move m) {
Color us = sideToMove;
Square from = from_sq(m);
Square to = to_sq(m);
PieceType pt = type_of(piece_on(to));
Piece pc = piece_on(to);
assert(empty(from) || type_of(m) == CASTLING);
assert(st->capturedType != KING);
assert(type_of(st->capturedPiece) != KING);
if (type_of(m) == PROMOTION)
{
assert(relative_rank(us, to) == RANK_8);
assert(pt == promotion_type(m));
assert(pt >= KNIGHT && pt <= QUEEN);
assert(type_of(pc) == promotion_type(m));
assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN);
remove_piece(us, pt, to);
put_piece(us, PAWN, to);
pt = PAWN;
remove_piece(pc, to);
pc = make_piece(us, PAWN);
put_piece(pc, to);
}
if (type_of(m) == CASTLING)
@@ -870,9 +848,9 @@ void Position::undo_move(Move m) {
}
else
{
move_piece(us, pt, to, from); // Put the piece back at the source square
move_piece(pc, to, from); // Put the piece back at the source square
if (st->capturedType)
if (st->capturedPiece)
{
Square capsq = to;
@@ -880,14 +858,14 @@ void Position::undo_move(Move m) {
{
capsq -= pawn_push(us);
assert(pt == PAWN);
assert(type_of(pc) == PAWN);
assert(to == st->previous->epSquare);
assert(relative_rank(us, to) == RANK_6);
assert(piece_on(capsq) == NO_PIECE);
assert(st->capturedType == PAWN);
assert(st->capturedPiece == make_piece(~us, PAWN));
}
put_piece(~us, st->capturedType, capsq); // Restore the captured piece
put_piece(st->capturedPiece, capsq); // Restore the captured piece
}
}
@@ -900,7 +878,7 @@ void Position::undo_move(Move m) {
/// Position::do_castling() is a helper used to do/undo a castling move. This
/// is a bit tricky, especially in Chess960.
/// is a bit tricky in Chess960 where from/to squares can overlap.
template<bool Do>
void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) {
@@ -910,11 +888,11 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
// Remove both pieces first since squares could overlap in Chess960
remove_piece(us, KING, Do ? from : to);
remove_piece(us, ROOK, Do ? rfrom : rto);
remove_piece(make_piece(us, KING), Do ? from : to);
remove_piece(make_piece(us, ROOK), Do ? rfrom : rto);
board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us
put_piece(us, KING, Do ? to : from);
put_piece(us, ROOK, Do ? rto : rfrom);
put_piece(make_piece(us, KING), Do ? to : from);
put_piece(make_piece(us, ROOK), Do ? rto : rfrom);
}
@@ -944,6 +922,8 @@ void Position::do_null_move(StateInfo& newSt) {
sideToMove = ~sideToMove;
set_check_info(st);
assert(pos_is_ok());
}
@@ -962,103 +942,96 @@ void Position::undo_null_move() {
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));
Piece pc = piece_on(from);
Piece captured = piece_on(to);
Key k = st->key ^ Zobrist::side;
if (captured)
k ^= Zobrist::psq[~us][captured][to];
k ^= Zobrist::psq[captured][to];
return k ^ Zobrist::psq[us][pt][to] ^ Zobrist::psq[us][pt][from];
return k ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
}
/// Position::see() is a static exchange evaluator: It tries to estimate the
/// material gain or loss resulting from a move.
/// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the
/// SEE value of move is greater or equal to the given value. We'll use an
/// algorithm similar to alpha-beta pruning with a null window.
Value Position::see_sign(Move m) const {
bool Position::see_ge(Move m, Value v) const {
assert(is_ok(m));
// Early return if SEE cannot be negative because captured piece value
// is not less then capturing one. Note that king moves always return
// here because king midgame value is set to 0.
if (PieceValue[MG][moved_piece(m)] <= PieceValue[MG][piece_on(to_sq(m))])
return VALUE_KNOWN_WIN;
return see(m);
}
Value Position::see(Move m) const {
Square from, to;
Bitboard occupied, attackers, stmAttackers;
Value swapList[32];
int slIndex = 1;
PieceType captured;
Color stm;
assert(is_ok(m));
from = from_sq(m);
to = to_sq(m);
swapList[0] = PieceValue[MG][piece_on(to)];
stm = color_of(piece_on(from));
occupied = pieces() ^ from;
// Castling moves are implemented as king capturing the rook so cannot
// be handled correctly. Simply return VALUE_ZERO that is always correct
// unless in the rare case the rook ends up under attack.
// Castling moves are implemented as king capturing the rook so cannot be
// handled correctly. Simply assume the SEE value is VALUE_ZERO that is always
// correct unless in the rare case the rook ends up under attack.
if (type_of(m) == CASTLING)
return VALUE_ZERO;
return VALUE_ZERO >= v;
Square from = from_sq(m), to = to_sq(m);
PieceType nextVictim = type_of(piece_on(from));
Color stm = ~color_of(piece_on(from)); // First consider opponent's move
Value balance; // Values of the pieces taken by us minus opponent's ones
Bitboard occupied, stmAttackers;
if (type_of(m) == ENPASSANT)
{
occupied ^= to - pawn_push(stm); // Remove the captured pawn
swapList[0] = PieceValue[MG][PAWN];
occupied = SquareBB[to - pawn_push(~stm)]; // Remove the captured pawn
balance = PieceValue[MG][PAWN];
}
else
{
balance = PieceValue[MG][piece_on(to)];
occupied = 0;
}
// Find all attackers to the destination square, with the moving piece
// removed, but possibly an X-ray attacker added behind it.
attackers = attackers_to(to, occupied) & occupied;
if (balance < v)
return false;
// If the opponent has no attackers we are finished
stm = ~stm;
stmAttackers = attackers & pieces(stm);
if (!stmAttackers)
return swapList[0];
if (nextVictim == KING)
return true;
// The destination square is defended, which makes things rather more
// difficult to compute. We proceed by building up a "swap list" containing
// the material gain or loss at each stop in a sequence of captures to the
// destination square, where the sides alternately capture, and always
// capture with the least valuable piece. After each capture, we look for
// new X-ray attacks from behind the capturing piece.
captured = type_of(piece_on(from));
balance -= PieceValue[MG][nextVictim];
do {
assert(slIndex < 32);
if (balance >= v)
return true;
// Add the new entry to the swap list
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured];
bool relativeStm = true; // True if the opponent is to move
occupied ^= pieces() ^ from ^ to;
// Find all attackers to the destination square, with the moving piece removed,
// but possibly an X-ray attacker added behind it.
Bitboard attackers = attackers_to(to, occupied) & occupied;
while (true)
{
stmAttackers = attackers & pieces(stm);
// Don't allow pinned pieces to attack pieces except the king as long all
// pinners are on their original square.
if (!(st->pinnersForKing[stm] & ~occupied))
stmAttackers &= ~st->blockersForKing[stm];
if (!stmAttackers)
return relativeStm;
// Locate and remove the next least valuable attacker
captured = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
if (nextVictim == KING)
return relativeStm == bool(attackers & pieces(~stm));
balance += relativeStm ? PieceValue[MG][nextVictim]
: -PieceValue[MG][nextVictim];
relativeStm = !relativeStm;
if (relativeStm == (balance >= v))
return relativeStm;
stm = ~stm;
stmAttackers = attackers & pieces(stm);
++slIndex;
} while (stmAttackers && (captured != KING || (--slIndex, false))); // Stop before a king capture
// Having built the swap list, we negamax through it to find the best
// achievable score from the point of view of the side to move.
while (--slIndex)
swapList[slIndex - 1] = std::min(-swapList[slIndex], swapList[slIndex - 1]);
return swapList[0];
}
}
@@ -1112,7 +1085,7 @@ void Position::flip() {
std::getline(ss, token); // Half and full moves
f += token;
set(f, is_chess960(), this_thread());
set(f, is_chess960(), st, this_thread());
assert(pos_is_ok());
}
@@ -1167,17 +1140,15 @@ bool Position::pos_is_ok(int* failedStep) const {
}
if (step == Lists)
for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt = PAWN; pt <= KING; ++pt)
{
if (pieceCount[c][pt] != popcount<Full>(pieces(c, pt)))
return false;
for (Piece pc : Pieces)
{
if (pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc))))
return false;
for (int i = 0; i < pieceCount[c][pt]; ++i)
if ( board[pieceList[c][pt][i]] != make_piece(c, pt)
|| index[pieceList[c][pt][i]] != i)
return false;
}
for (int i = 0; i < pieceCount[pc]; ++i)
if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i)
return false;
}
if (step == Castling)
for (Color c = WHITE; c <= BLACK; ++c)
+62 -85
View File
@@ -22,35 +22,13 @@
#define POSITION_H_INCLUDED
#include <cassert>
#include <cstddef> // For offsetof()
#include <deque>
#include <memory> // For std::unique_ptr
#include <string>
#include "bitboard.h"
#include "types.h"
class Position;
class Thread;
namespace PSQT {
extern Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
void init();
}
/// CheckInfo struct is initialized at constructor time and keeps info used to
/// detect if a move gives check.
struct CheckInfo {
explicit CheckInfo(const Position&);
Bitboard dcCandidates;
Bitboard pinned;
Bitboard checkSquares[PIECE_TYPE_NB];
Square ksq;
};
/// 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
@@ -68,32 +46,36 @@ struct StateInfo {
Score psq;
Square epSquare;
// Not copied when making a move
// Not copied when making a move (will be recomputed anyhow)
Key key;
Bitboard checkersBB;
PieceType capturedType;
Piece capturedPiece;
StateInfo* previous;
Bitboard blockersForKing[COLOR_NB];
Bitboard pinnersForKing[COLOR_NB];
Bitboard checkSquares[PIECE_TYPE_NB];
};
// In a std::deque references to elements are unaffected upon resizing
typedef std::unique_ptr<std::deque<StateInfo>> StateListPtr;
/// Position class stores information regarding the board representation as
/// pieces, side to move, hash keys, castling info, etc. Important methods are
/// do_move() and undo_move(), used by the search to update node info when
/// traversing the search tree.
class Thread;
class Position {
public:
static void init();
Position() = default; // To define the global object RootPos
Position() = default;
Position(const Position&) = delete;
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
Position& operator=(const Position&) = delete;
// FEN string input/output
void set(const std::string& fenStr, bool isChess960, Thread* th);
Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th);
const std::string fen() const;
// Position representation
@@ -120,6 +102,7 @@ public:
Bitboard checkers() const;
Bitboard discovered_check_candidates() const;
Bitboard pinned_pieces(Color c) const;
Bitboard check_squares(PieceType pt) const;
// Attacks to/from a given square
Bitboard attackers_to(Square s) const;
@@ -127,16 +110,17 @@ public:
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;
Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const;
// Properties of moves
bool legal(Move m, Bitboard pinned) const;
bool legal(Move m) const;
bool pseudo_legal(const Move m) const;
bool capture(Move m) const;
bool capture_or_promotion(Move m) const;
bool gives_check(Move m, const CheckInfo& ci) const;
bool gives_check(Move m) const;
bool advanced_pawn_push(Move m) const;
Piece moved_piece(Move m) const;
PieceType captured_piece_type() const;
Piece captured_piece() const;
// Piece specific
bool pawn_passed(Color c, Square s) const;
@@ -148,14 +132,12 @@ public:
void do_null_move(StateInfo& st);
void undo_null_move();
// Static exchange evaluation
Value see(Move m) const;
Value see_sign(Move m) const;
// Static Exchange Evaluation
bool see_ge(Move m, Value value) const;
// Accessing hash keys
Key key() const;
Key key_after(Move m) const;
Key exclusion_key() const;
Key material_key() const;
Key pawn_key() const;
@@ -166,7 +148,6 @@ public:
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;
@@ -178,15 +159,14 @@ public:
private:
// Initialization helpers (used while setting up a position)
void clear();
void set_castling_right(Color c, Square rfrom);
void set_state(StateInfo* si) const;
void set_check_info(StateInfo* si) const;
// Other helpers
Bitboard check_blockers(Color c, Color kingColor) const;
void put_piece(Color c, PieceType pt, Square s);
void remove_piece(Color c, PieceType pt, Square s);
void move_piece(Color c, PieceType pt, Square from, Square to);
void put_piece(Piece pc, Square s);
void remove_piece(Piece pc, Square s);
void move_piece(Piece pc, Square from, Square to);
template<bool Do>
void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto);
@@ -194,13 +174,12 @@ private:
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 pieceCount[PIECE_NB];
Square pieceList[PIECE_NB][16];
int index[SQUARE_NB];
int castlingRightsMask[SQUARE_NB];
Square castlingRookSquare[CASTLING_RIGHT_NB];
Bitboard castlingPath[CASTLING_RIGHT_NB];
StateInfo startState;
uint64_t nodes;
int gamePly;
Color sideToMove;
@@ -252,16 +231,16 @@ inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const {
}
template<PieceType Pt> inline int Position::count(Color c) const {
return pieceCount[c][Pt];
return pieceCount[make_piece(c, Pt)];
}
template<PieceType Pt> inline const Square* Position::squares(Color c) const {
return pieceList[c][Pt];
return pieceList[make_piece(c, Pt)];
}
template<PieceType Pt> inline Square Position::square(Color c) const {
assert(pieceCount[c][Pt] == 1);
return pieceList[c][Pt][0];
assert(pieceCount[make_piece(c, Pt)] == 1);
return pieceList[make_piece(c, Pt)][0];
}
inline Square Position::ep_square() const {
@@ -309,11 +288,15 @@ inline Bitboard Position::checkers() const {
}
inline Bitboard Position::discovered_check_candidates() const {
return check_blockers(sideToMove, ~sideToMove);
return st->blockersForKing[~sideToMove] & pieces(sideToMove);
}
inline Bitboard Position::pinned_pieces(Color c) const {
return check_blockers(c, c);
return st->blockersForKing[c] & pieces(c);
}
inline Bitboard Position::check_squares(PieceType pt) const {
return st->checkSquares[pt];
}
inline bool Position::pawn_passed(Color c, Square s) const {
@@ -357,13 +340,9 @@ 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
return pieceCount[W_BISHOP] == 1
&& pieceCount[B_BISHOP] == 1
&& opposite_colors(square<BISHOP>(WHITE), square<BISHOP>(BLACK));
}
@@ -372,66 +351,64 @@ inline bool Position::is_chess960() const {
}
inline bool Position::capture_or_promotion(Move m) const {
assert(is_ok(m));
return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m));
}
inline bool Position::capture(Move m) const {
// Castling is encoded as "king captures the rook"
assert(is_ok(m));
// Castling is encoded as "king captures rook"
return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT;
}
inline PieceType Position::captured_piece_type() const {
return st->capturedType;
inline Piece Position::captured_piece() const {
return st->capturedPiece;
}
inline Thread* Position::this_thread() const {
return thisThread;
}
inline void Position::put_piece(Color c, PieceType pt, Square s) {
inline void Position::put_piece(Piece pc, Square s) {
board[s] = make_piece(c, pt);
board[s] = pc;
byTypeBB[ALL_PIECES] |= s;
byTypeBB[pt] |= s;
byColorBB[c] |= s;
index[s] = pieceCount[c][pt]++;
pieceList[c][pt][index[s]] = s;
pieceCount[c][ALL_PIECES]++;
byTypeBB[type_of(pc)] |= s;
byColorBB[color_of(pc)] |= s;
index[s] = pieceCount[pc]++;
pieceList[pc][index[s]] = s;
pieceCount[make_piece(color_of(pc), ALL_PIECES)]++;
}
inline void Position::remove_piece(Color c, PieceType pt, Square s) {
inline void Position::remove_piece(Piece pc, Square s) {
// WARNING: This is not a reversible operation. If we remove a piece in
// do_move() and then replace it in undo_move() we will put it at the end of
// the list and not in its original place, it means index[] and pieceList[]
// are not guaranteed to be invariant to a do_move() + undo_move() sequence.
// are not invariant to a do_move() + undo_move() sequence.
byTypeBB[ALL_PIECES] ^= s;
byTypeBB[pt] ^= s;
byColorBB[c] ^= s;
byTypeBB[type_of(pc)] ^= s;
byColorBB[color_of(pc)] ^= s;
/* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */
Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]];
Square lastSquare = pieceList[pc][--pieceCount[pc]];
index[lastSquare] = index[s];
pieceList[c][pt][index[lastSquare]] = lastSquare;
pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE;
pieceCount[c][ALL_PIECES]--;
pieceList[pc][index[lastSquare]] = lastSquare;
pieceList[pc][pieceCount[pc]] = SQ_NONE;
pieceCount[make_piece(color_of(pc), ALL_PIECES)]--;
}
inline void Position::move_piece(Color c, PieceType pt, Square from, Square to) {
inline void Position::move_piece(Piece pc, Square from, Square to) {
// index[from] is not updated and becomes stale. This works as long as index[]
// is accessed just by known occupied squares.
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[c] ^= from_to_bb;
byTypeBB[type_of(pc)] ^= from_to_bb;
byColorBB[color_of(pc)] ^= from_to_bb;
board[from] = NO_PIECE;
board[to] = make_piece(c, pt);
board[to] = pc;
index[to] = index[from];
pieceList[c][pt][index[to]] = to;
pieceList[pc][index[to]] = to;
}
#endif // #ifndef POSITION_H_INCLUDED
+22 -15
View File
@@ -18,8 +18,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include "types.h"
Value PieceValue[PHASE_NB][PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg }
};
namespace PSQT {
#define S(mg, eg) make_score(mg, eg)
@@ -32,13 +39,12 @@ const Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ },
{ // Pawn
{ S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },
{ S(-19, 5), S( 1,-4), S( 7, 8), S( 3,-2) },
{ S(-26,-6), S( -7,-5), S( 19, 5), S(24, 4) },
{ S(-25, 1), S(-14, 3), S( 16,-8), S(31,-3) },
{ S(-14, 6), S( 0, 9), S( -1, 7), S(17,-6) },
{ S(-14, 6), S(-13,-5), S(-10, 2), S(-6, 4) },
{ S(-12, 1), S( 15,-9), S( -8, 1), S(-4,18) },
{ S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) }
{ S(-16, 7), S( 1,-4), S( 7, 8), S( 3,-2) },
{ S(-23,-4), S( -7,-5), S( 19, 5), S(24, 4) },
{ S(-22, 3), S(-14, 3), S( 20,-8), S(35,-3) },
{ S(-11, 8), S( 0, 9), S( 3, 7), S(21,-6) },
{ S(-11, 8), S(-13,-5), S( -6, 2), S(-2, 4) },
{ S( -9, 3), S( 15,-9), S( -8, 1), S(-4,18) }
},
{ // Knight
{ S(-143, -97), S(-96,-82), S(-80,-46), S(-73,-14) },
@@ -94,24 +100,25 @@ const Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
#undef S
Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
Score psq[PIECE_NB][SQUARE_NB];
// init() initializes piece square tables: the white halves of the tables are
// init() initializes piece-square tables: the white halves of the tables are
// copied from Bonus[] adding the piece value, then the black halves of the
// tables are initialized by flipping and changing the sign of the white scores.
void init() {
for (PieceType pt = PAWN; pt <= KING; ++pt)
for (Piece pc = W_PAWN; pc <= W_KING; ++pc)
{
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
PieceValue[MG][~pc] = PieceValue[MG][pc];
PieceValue[EG][~pc] = PieceValue[EG][pc];
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
Score v = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
int edgeDistance = file_of(s) < FILE_E ? file_of(s) : FILE_H - file_of(s);
psq[BLACK][pt][~s] = -(psq[WHITE][pt][s] = v + Bonus[pt][rank_of(s)][edgeDistance]);
File f = std::min(file_of(s), FILE_H - file_of(s));
psq[ pc][ s] = v + Bonus[pc][rank_of(s)][f];
psq[~pc][~s] = -psq[pc][s];
}
}
}
+378 -342
View File
File diff suppressed because it is too large Load Diff
+13 -12
View File
@@ -22,14 +22,14 @@
#define SEARCH_H_INCLUDED
#include <atomic>
#include <memory> // For std::unique_ptr
#include <stack>
#include <vector>
#include "misc.h"
#include "position.h"
#include "movepick.h"
#include "types.h"
class Position;
namespace Search {
/// Stack struct keeps track of the information we need to remember from nodes
@@ -43,10 +43,13 @@ struct Stack {
Move excludedMove;
Move killers[2];
Value staticEval;
Value history;
bool skipEarlyPruning;
int moveCount;
CounterMoveStats* counterMoves;
};
/// 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.
@@ -57,7 +60,6 @@ struct RootMove {
bool operator<(const RootMove& m) const { return m.score < score; } // Descending sort
bool operator==(const Move& m) const { return pv[0] == m; }
void insert_pv_in_tt(Position& pos);
bool extract_ponder_from_tt(Position& pos);
Value score = -VALUE_INFINITE;
@@ -65,7 +67,8 @@ struct RootMove {
std::vector<Move> pv;
};
typedef std::vector<RootMove> RootMoveVector;
typedef std::vector<RootMove> RootMoves;
/// 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
@@ -74,8 +77,8 @@ typedef std::vector<RootMove> RootMoveVector;
struct LimitsType {
LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC
nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movestogo =
depth = movetime = mate = infinite = ponder = 0;
nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] =
npmsec = movestogo = depth = movetime = mate = infinite = ponder = 0;
}
bool use_time_management() const {
@@ -88,18 +91,16 @@ struct LimitsType {
TimePoint startTime;
};
/// The SignalsType struct stores atomic flags updated during the search
/// typically in an async fashion e.g. to stop the search by the GUI.
/// SignalsType struct stores atomic flags updated during the search, typically
/// in an async fashion e.g. to stop the search by the GUI.
struct SignalsType {
std::atomic_bool stop, stopOnPonderhit;
};
typedef std::unique_ptr<std::stack<StateInfo>> StateStackPtr;
extern SignalsType Signals;
extern LimitsType Limits;
extern StateStackPtr SetupStates;
void init();
void clear();
+1 -1
View File
@@ -3,7 +3,7 @@
This file may be redistributed and/or modified without restrictions.
tbcore.c contains engine-independent routines of the tablebase probing code.
This file should not need to much adaptation to add tablebase probing to
This file should not need too much adaptation to add tablebase probing to
a particular engine, provided the engine is written in C or C++.
*/
+31 -40
View File
@@ -15,7 +15,6 @@
#include "../movegen.h"
#include "../bitboard.h"
#include "../search.h"
#include "../bitcount.h"
#include "tbprobe.h"
#include "tbcore.h"
@@ -23,7 +22,7 @@
#include "tbcore.cpp"
namespace Zobrist {
extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
extern Key psq[PIECE_NB][SQUARE_NB];
}
int Tablebases::MaxCardinality = 0;
@@ -39,12 +38,12 @@ static void prt_str(Position& pos, char *str, int mirror)
color = !mirror ? WHITE : BLACK;
for (pt = KING; pt >= PAWN; --pt)
for (i = popcount<Max15>(pos.pieces(color, pt)); i > 0; i--)
for (i = popcount(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--)
for (i = popcount(pos.pieces(color, pt)); i > 0; i--)
*str++ = pchr[6 - pt];
*str++ = 0;
}
@@ -60,12 +59,12 @@ static uint64 calc_key(Position& pos, int mirror)
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];
for (i = popcount(pos.pieces(color, pt)); i > 0; i--)
key ^= Zobrist::psq[make_piece(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];
for (i = popcount(pos.pieces(color, pt)); i > 0; i--)
key ^= Zobrist::psq[make_piece(BLACK, pt)][i - 1];
return key;
}
@@ -84,11 +83,11 @@ static uint64 calc_key_from_pcs(int *pcs, int mirror)
color = !mirror ? 0 : 8;
for (pt = PAWN; pt <= KING; ++pt)
for (i = 0; i < pcs[color + pt]; i++)
key ^= Zobrist::psq[WHITE][pt][i];
key ^= Zobrist::psq[make_piece(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];
key ^= Zobrist::psq[make_piece(BLACK, pt)][i];
return key;
}
@@ -124,7 +123,7 @@ static int probe_wdl_table(Position& pos, int *success)
key = pos.material_key();
// Test for KvK.
if (key == (Zobrist::psq[WHITE][KING][0] ^ Zobrist::psq[BLACK][KING][0]))
if (key == (Zobrist::psq[W_KING][0] ^ Zobrist::psq[B_KING][0]))
return 0;
ptr2 = TB_hash[key >> (64 - TBHASHBITS)];
@@ -362,14 +361,12 @@ static int probe_ab(Position& pos, int alpha, int beta, int *success)
} 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))
|| !pos.legal(capture))
continue;
pos.do_move(capture, st, pos.gives_check(capture, ci));
pos.do_move(capture, st, pos.gives_check(capture));
v = -probe_ab(pos, -beta, -alpha, success);
pos.undo_move(capture);
if (*success == 0) return 0;
@@ -425,14 +422,12 @@ int Tablebases::probe_wdl(Position& pos, int *success)
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))
|| !pos.legal(capture))
continue;
pos.do_move(capture, st, pos.gives_check(capture, ci));
pos.do_move(capture, st, pos.gives_check(capture));
int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture);
if (*success == 0) return 0;
@@ -445,13 +440,13 @@ int Tablebases::probe_wdl(Position& pos, int *success)
for (moves = stack; moves < end; moves++) {
Move capture = moves->move;
if (type_of(capture) == ENPASSANT) continue;
if (pos.legal(capture, ci.pinned)) break;
if (pos.legal(capture)) 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))
if (pos.legal(move))
break;
}
}
@@ -480,7 +475,6 @@ static int probe_dtz_no_ep(Position& pos, int *success)
ExtMove stack[192];
ExtMove *moves, *end = NULL;
StateInfo st;
CheckInfo ci(pos);
if (wdl > 0) {
// Generate at least all legal non-capturing pawn moves
@@ -493,10 +487,10 @@ static int probe_dtz_no_ep(Position& pos, int *success)
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))
|| !pos.legal(move))
continue;
pos.do_move(move, st, pos.gives_check(move, ci));
int v = -probe_ab(pos, -2, -wdl + 1, success);
pos.do_move(move, st, pos.gives_check(move));
int v = -Tablebases::probe_wdl(pos, success);
pos.undo_move(move);
if (*success == 0) return 0;
if (v == wdl)
@@ -515,9 +509,9 @@ static int probe_dtz_no_ep(Position& pos, int *success)
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))
|| !pos.legal(move))
continue;
pos.do_move(move, st, pos.gives_check(move, ci));
pos.do_move(move, st, pos.gives_check(move));
int v = -Tablebases::probe_dtz(pos, success);
pos.undo_move(move);
if (*success == 0) return 0;
@@ -534,9 +528,9 @@ static int probe_dtz_no_ep(Position& pos, int *success)
for (moves = stack; moves < end; moves++) {
int v;
Move move = moves->move;
if (!pos.legal(move, ci.pinned))
if (!pos.legal(move))
continue;
pos.do_move(move, st, pos.gives_check(move, ci));
pos.do_move(move, st, pos.gives_check(move));
if (st.rule50 == 0) {
if (wdl == -2) v = -1;
else {
@@ -605,14 +599,13 @@ int Tablebases::probe_dtz(Position& pos, int *success)
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))
|| !pos.legal(capture))
continue;
pos.do_move(capture, st, pos.gives_check(capture, ci));
pos.do_move(capture, st, pos.gives_check(capture));
int v0 = -probe_ab(pos, -2, 2, success);
pos.undo_move(capture);
if (*success == 0) return 0;
@@ -638,13 +631,13 @@ int Tablebases::probe_dtz(Position& pos, int *success)
for (moves = stack; moves < end; moves++) {
Move move = moves->move;
if (type_of(move) == ENPASSANT) continue;
if (pos.legal(move, ci.pinned)) break;
if (pos.legal(move)) 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))
if (pos.legal(move))
break;
}
}
@@ -689,7 +682,7 @@ static Value wdl_to_Value[5] = {
//
// 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)
bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score)
{
int success;
@@ -697,12 +690,11 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Va
if (!success) return false;
StateInfo st;
CheckInfo ci(pos);
// Probe each move.
for (size_t i = 0; i < rootMoves.size(); i++) {
Move move = rootMoves[i].pv[0];
pos.do_move(move, st, pos.gives_check(move, ci));
pos.do_move(move, st, pos.gives_check(move));
int v = 0;
if (pos.checkers() && dtz > 0) {
ExtMove s[192];
@@ -796,7 +788,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoveVector& rootMoves, Va
//
// 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)
bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score)
{
int success;
@@ -805,14 +797,13 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoveVector& rootMoves
score = wdl_to_Value[wdl + 2];
StateInfo st;
CheckInfo ci(pos);
int best = -2;
// Probe each move.
for (size_t i = 0; i < rootMoves.size(); i++) {
Move move = rootMoves[i].pv[0];
pos.do_move(move, st, pos.gives_check(move, ci));
pos.do_move(move, st, pos.gives_check(move));
int v = -Tablebases::probe_wdl(pos, &success);
pos.undo_move(move);
if (!success) return false;
+3 -2
View File
@@ -10,8 +10,9 @@ 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);
bool root_probe(Position& pos, Search::RootMoves& rootMoves, Value& score);
bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, Value& score);
void filter_root_moves(Position& pos, Search::RootMoves& rootMoves);
}
+56 -28
View File
@@ -25,18 +25,18 @@
#include "search.h"
#include "thread.h"
#include "uci.h"
using namespace Search;
#include "syzygy/tbprobe.h"
ThreadPool Threads; // Global object
/// Thread constructor launch the thread and then wait until it goes to sleep
/// Thread constructor launches the thread and then waits until it goes to sleep
/// in idle_loop().
Thread::Thread() {
resetCalls = exit = false;
maxPly = callsCnt = 0;
tbHits = 0;
history.clear();
counterMoves.clear();
idx = Threads.size(); // Start from 0
@@ -48,7 +48,7 @@ Thread::Thread() {
}
/// Thread destructor wait for thread termination before returning
/// Thread destructor waits for thread termination before returning
Thread::~Thread() {
@@ -60,7 +60,8 @@ Thread::~Thread() {
}
/// Thread::wait_for_search_finished() wait on sleep condition until not searching
/// Thread::wait_for_search_finished() waits on sleep condition
/// until not searching
void Thread::wait_for_search_finished() {
@@ -69,7 +70,7 @@ void Thread::wait_for_search_finished() {
}
/// Thread::wait() wait on sleep condition until condition is true
/// Thread::wait() waits on sleep condition until condition is true
void Thread::wait(std::atomic_bool& condition) {
@@ -78,7 +79,7 @@ void Thread::wait(std::atomic_bool& condition) {
}
/// Thread::start_searching() wake up the thread that will start the search
/// Thread::start_searching() wakes up the thread that will start the search
void Thread::start_searching(bool resume) {
@@ -115,7 +116,7 @@ void Thread::idle_loop() {
}
/// ThreadPool::init() create and launch requested threads, that will go
/// ThreadPool::init() creates and launches requested threads that will go
/// immediately to sleep. We cannot use a constructor because Threads is a
/// static object and we need a fully initialized engine at this point due to
/// allocation of Endgames in the Thread constructor.
@@ -127,9 +128,9 @@ void ThreadPool::init() {
}
/// ThreadPool::exit() terminate threads before the program exits. Cannot be
/// ThreadPool::exit() terminates threads before the program exits. Cannot be
/// done in destructor because threads must be terminated before deleting any
/// static objects, so while still in main().
/// static objects while still in main().
void ThreadPool::exit() {
@@ -156,40 +157,67 @@ void ThreadPool::read_uci_options() {
}
/// ThreadPool::nodes_searched() return the number of nodes searched
/// ThreadPool::nodes_searched() returns the number of nodes searched
int64_t ThreadPool::nodes_searched() {
uint64_t ThreadPool::nodes_searched() const {
int64_t nodes = 0;
uint64_t nodes = 0;
for (Thread* th : *this)
nodes += th->rootPos.nodes_searched();
return nodes;
}
/// ThreadPool::start_thinking() wake up the main thread sleeping in idle_loop()
/// and start a new search, then return immediately.
/// ThreadPool::tb_hits() returns the number of TB hits
void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
StateStackPtr& states) {
uint64_t ThreadPool::tb_hits() const {
uint64_t hits = 0;
for (Thread* th : *this)
hits += th->tbHits;
return hits;
}
/// ThreadPool::start_thinking() wakes up the main thread sleeping in idle_loop()
/// and starts a new search, then returns immediately.
void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
const Search::LimitsType& limits) {
main()->wait_for_search_finished();
Signals.stopOnPonderhit = Signals.stop = false;
main()->rootMoves.clear();
main()->rootPos = pos;
Limits = limits;
if (states.get()) // If we don't set a new position, preserve current state
{
SetupStates = std::move(states); // Ownership transfer here
assert(!states.get());
}
Search::Signals.stopOnPonderhit = Search::Signals.stop = false;
Search::Limits = limits;
Search::RootMoves rootMoves;
for (const auto& m : MoveList<LEGAL>(pos))
if ( limits.searchmoves.empty()
|| std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
main()->rootMoves.push_back(RootMove(m));
rootMoves.push_back(Search::RootMove(m));
if (!rootMoves.empty())
Tablebases::filter_root_moves(pos, rootMoves);
// After ownership transfer 'states' becomes empty, so if we stop the search
// and call 'go' again without setting a new position states.get() == NULL.
assert(states.get() || setupStates.get());
if (states.get())
setupStates = std::move(states); // Ownership transfer, states is now empty
StateInfo tmp = setupStates->back();
for (Thread* th : Threads)
{
th->maxPly = 0;
th->tbHits = 0;
th->rootDepth = DEPTH_ZERO;
th->rootMoves = rootMoves;
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
}
setupStates->back() = tmp; // Restore st->previous, cleared by Position::set()
main()->start_searching();
}
+15 -7
View File
@@ -36,7 +36,7 @@
#include "thread_win32.h"
/// Thread struct keeps together all the thread related stuff. We also use
/// Thread struct keeps together all the thread-related stuff. We also use
/// per-thread pawn and material hash tables so that once we get a pointer to an
/// entry its life time is unlimited and we don't have to care about someone
/// changing the entry under our feet.
@@ -62,14 +62,17 @@ public:
Endgames endgames;
size_t idx, PVIdx;
int maxPly, callsCnt;
uint64_t tbHits;
Position rootPos;
Search::RootMoveVector rootMoves;
Search::RootMoves rootMoves;
Depth rootDepth;
HistoryStats history;
MovesStats counterMoves;
Depth completedDepth;
std::atomic_bool resetCalls;
HistoryStats history;
MoveStats counterMoves;
FromToStats fromTo;
CounterMoveHistoryStats counterMoveHistory;
};
@@ -80,10 +83,11 @@ struct MainThread : public Thread {
bool easyMovePlayed, failedLow;
double bestMoveChanges;
Value previousScore;
};
/// ThreadPool struct handles all the threads related stuff like init, starting,
/// ThreadPool struct handles all the threads-related stuff like init, starting,
/// parking and, most importantly, launching a thread. All the access to threads
/// data is done through this class.
@@ -93,9 +97,13 @@ struct ThreadPool : public std::vector<Thread*> {
void exit(); // be initialized and valid during the whole thread lifetime.
MainThread* main() { return static_cast<MainThread*>(at(0)); }
void start_thinking(const Position&, const Search::LimitsType&, Search::StateStackPtr&);
void start_thinking(Position&, StateListPtr&, const Search::LimitsType&);
void read_uci_options();
int64_t nodes_searched();
uint64_t nodes_searched() const;
uint64_t tb_hits() const;
private:
StateListPtr setupStates;
};
extern ThreadPool Threads;
+2 -2
View File
@@ -25,11 +25,11 @@
/// relies on libwinpthread. Currently libwinpthread implements mutexes directly
/// on top of Windows semaphores. Semaphores, being kernel objects, require kernel
/// mode transition in order to lock or unlock, which is very slow compared to
/// interlocked operations (about 30% slower on bench test). To workaround this
/// interlocked operations (about 30% slower on bench test). To work around this
/// issue, we define our wrappers to the low level Win32 calls. We use critical
/// sections to support Windows XP and older versions. Unfortunately, cond_wait()
/// is racy between unlock() and WaitForSingleObject() but they have the same
/// speed performance of SRW locks.
/// speed performance as the SRW locks.
#include <condition_variable>
#include <mutex>
+12 -13
View File
@@ -33,27 +33,27 @@ namespace {
enum TimeType { OptimumTime, MaxTime };
const int MoveHorizon = 50; // Plan time management at most this many moves ahead
const double MaxRatio = 6.93; // When in trouble, we can step over reserved time with this ratio
const double StealRatio = 0.36; // However we must not steal time from remaining moves over this ratio
const double MaxRatio = 7.09; // When in trouble, we can step over reserved time with this ratio
const double StealRatio = 0.35; // However we must not steal time from remaining moves over this ratio
// move_importance() is a skew-logistic function based on naive statistical
// analysis of "how many games are still undecided after n half-moves". Game
// is considered "undecided" as long as neither side has >275cp advantage.
// Data was extracted from CCRL game database with some simple filtering criteria.
// Data was extracted from the CCRL game database with some simple filtering criteria.
double move_importance(int ply) {
const double XScale = 8.27;
const double XShift = 59.;
const double Skew = 0.179;
const double XScale = 7.64;
const double XShift = 58.4;
const double Skew = 0.183;
return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero
}
template<TimeType T>
int remaining(int myTime, int movesToGo, int ply, int slowMover)
{
int remaining(int myTime, int movesToGo, int ply, int slowMover) {
const double TMaxRatio = (T == OptimumTime ? 1 : MaxRatio);
const double TStealRatio = (T == OptimumTime ? 0 : StealRatio);
@@ -66,7 +66,7 @@ namespace {
double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance);
double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance);
return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks an explicit cast
return int(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for an explicit cast
}
} // namespace
@@ -81,8 +81,8 @@ namespace {
/// inc > 0 && movestogo == 0 means: x basetime + z increment
/// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
{
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
int minThinkingTime = Options["Minimum Thinking Time"];
int moveOverhead = Options["Move Overhead"];
int slowMover = Options["Slow Mover"];
@@ -91,7 +91,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
// If we have to play in 'nodes as time' mode, then convert from time
// to nodes, and use resulting values in time management formulas.
// WARNING: Given npms (nodes per millisecond) must be much lower then
// real engine speed to avoid time losses.
// the real engine speed to avoid time losses.
if (npmsec)
{
if (!availableNodes) // Only once at game start
@@ -104,7 +104,6 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
}
startTime = limits.startTime;
unstablePvFactor = 1;
optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime);
const int MaxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon;
+1 -3
View File
@@ -31,8 +31,7 @@
class TimeManagement {
public:
void init(Search::LimitsType& limits, Color us, int ply);
void pv_instability(double bestMoveChanges) { unstablePvFactor = 1 + bestMoveChanges; }
int available() const { return int(optimumTime * unstablePvFactor * 1.016); }
int optimum() const { return optimumTime; }
int maximum() const { return maximumTime; }
int elapsed() const { return int(Search::Limits.npmsec ? Threads.nodes_searched() : now() - startTime); }
@@ -42,7 +41,6 @@ private:
TimePoint startTime;
int optimumTime;
int maximumTime;
double unstablePvFactor;
};
extern TimeManagement Time;
+6 -6
View File
@@ -92,19 +92,19 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
// nature we add 259 (256 is the modulus plus 3 to keep the lowest
// two bound bits from affecting the result) to calculate the entry
// age correctly even after generation8 overflows into the next cycle.
if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2 * ONE_PLY
> tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2 * ONE_PLY)
if ( replace->depth8 - ((259 + generation8 - replace->genBound8) & 0xFC) * 2
> tte[i].depth8 - ((259 + generation8 - tte[i].genBound8) & 0xFC) * 2)
replace = &tte[i];
return found = false, replace;
}
/// Returns an approximation of the hashtable occupation during a search. The
/// hash is x permill full, as per UCI protocol.
/// TranspositionTable::hashfull() returns an approximation of the hashtable
/// occupation during a search. The hash is x permill full, as per UCI protocol.
int TranspositionTable::hashfull() const {
int TranspositionTable::hashfull() const
{
int cnt = 0;
for (int i = 0; i < 1000 / ClusterSize; i++)
{
+5 -3
View File
@@ -39,18 +39,20 @@ struct TTEntry {
Move move() const { return (Move )move16; }
Value value() const { return (Value)value16; }
Value eval() const { return (Value)eval16; }
Depth depth() const { return (Depth)depth8; }
Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)); }
Bound bound() const { return (Bound)(genBound8 & 0x3); }
void save(Key k, Value v, Bound b, Depth d, Move m, Value ev, uint8_t g) {
assert(d / ONE_PLY * ONE_PLY == d);
// Preserve any existing move for the same position
if (m || (k >> 48) != key16)
move16 = (uint16_t)m;
// Don't overwrite more valuable entries
if ( (k >> 48) != key16
|| d > depth8 - 2
|| d / ONE_PLY > depth8 - 4
/* || g != (genBound8 & 0xFC) // Matching non-zero keys are already refreshed by probe() */
|| b == BOUND_EXACT)
{
@@ -58,7 +60,7 @@ struct TTEntry {
value16 = (int16_t)v;
eval16 = (int16_t)ev;
genBound8 = (uint8_t)(g | b);
depth8 = (int8_t)d;
depth8 = (int8_t)(d / ONE_PLY);
}
}
+53 -47
View File
@@ -60,13 +60,12 @@
/// _WIN64 Building on Windows 64 bit
#if defined(_WIN64) && defined(_MSC_VER) // No Makefile used
# include <intrin.h> // MSVC popcnt and bsfq instrinsics
# include <intrin.h> // Microsoft header for _BitScanForward64()
# define IS_64BIT
# define USE_BSFQ
#endif
#if defined(USE_POPCNT) && defined(__INTEL_COMPILER) && defined(_MSC_VER)
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
#if defined(USE_POPCNT) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
# include <nmmintrin.h> // Intel and Microsoft header for _mm_popcnt_u64()
#endif
#if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER))
@@ -116,7 +115,7 @@ const int MAX_PLY = 128;
/// any normal move destination square is always different from origin square
/// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
enum Move {
enum Move : int {
MOVE_NONE,
MOVE_NULL = 65
};
@@ -184,13 +183,13 @@ enum Value : int {
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY,
PawnValueMg = 198, PawnValueEg = 258,
KnightValueMg = 817, KnightValueEg = 846,
BishopValueMg = 836, BishopValueEg = 857,
RookValueMg = 1270, RookValueEg = 1281,
QueenValueMg = 2521, QueenValueEg = 2558,
PawnValueMg = 188, PawnValueEg = 248,
KnightValueMg = 753, KnightValueEg = 832,
BishopValueMg = 826, BishopValueEg = 897,
RookValueMg = 1285, RookValueEg = 1371,
QueenValueMg = 2513, QueenValueEg = 2650,
MidgameLimit = 15581, EndgameLimit = 3998
MidgameLimit = 15258, EndgameLimit = 3915
};
enum PieceType {
@@ -206,19 +205,25 @@ enum Piece {
PIECE_NB = 16
};
const Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
extern Value PieceValue[PHASE_NB][PIECE_NB];
enum Depth {
ONE_PLY = 1,
DEPTH_ZERO = 0,
DEPTH_QS_CHECKS = 0,
DEPTH_QS_NO_CHECKS = -1,
DEPTH_QS_RECAPTURES = -5,
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_NONE = -6,
DEPTH_MAX = MAX_PLY
DEPTH_NONE = -6 * ONE_PLY,
DEPTH_MAX = MAX_PLY * ONE_PLY
};
static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
enum Square {
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
@@ -232,52 +237,51 @@ enum Square {
SQUARE_NB = 64,
DELTA_N = 8,
DELTA_E = 1,
DELTA_S = -8,
DELTA_W = -1,
NORTH = 8,
EAST = 1,
SOUTH = -8,
WEST = -1,
DELTA_NN = DELTA_N + DELTA_N,
DELTA_NE = DELTA_N + DELTA_E,
DELTA_SE = DELTA_S + DELTA_E,
DELTA_SS = DELTA_S + DELTA_S,
DELTA_SW = DELTA_S + DELTA_W,
DELTA_NW = DELTA_N + DELTA_W
NORTH_EAST = NORTH + EAST,
SOUTH_EAST = SOUTH + EAST,
SOUTH_WEST = SOUTH + WEST,
NORTH_WEST = NORTH + WEST
};
enum File {
enum File : int {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB
};
enum Rank {
enum Rank : int {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB
};
/// Score enum stores a middlegame and an endgame value in a single integer
/// (enum). The least significant 16 bits are used to store the endgame value
/// and the upper 16 bits are used to store the middlegame value.
/// and the upper 16 bits are used to store the middlegame value. Take some
/// care to avoid left-shifting a signed int to avoid undefined behavior.
enum Score : int { SCORE_ZERO };
inline Score make_score(int mg, int eg) {
return Score((mg << 16) + eg);
return Score((int)((unsigned int)eg << 16) + mg);
}
/// Extracting the signed lower and upper 16 bits is not so trivial because
/// according to the standard a simple cast to short is implementation defined
/// and so is a right shift of a signed integer.
inline Value mg_value(Score s) {
union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s + 0x8000) >> 16) };
return Value(mg.s);
}
inline Value eg_value(Score s) {
union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s)) };
union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s + 0x8000) >> 16) };
return Value(eg.s);
}
inline Value mg_value(Score s) {
union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s)) };
return Value(mg.s);
}
#define ENABLE_BASE_OPERATORS_ON(T) \
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)); } \
@@ -325,16 +329,18 @@ inline Score operator/(Score s, int i) {
return make_score(mg_value(s) / i, eg_value(s) / i);
}
extern Value PieceValue[PHASE_NB][PIECE_NB];
inline Color operator~(Color c) {
return Color(c ^ BLACK);
return Color(c ^ BLACK); // Toggle color
}
inline Square operator~(Square s) {
return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8
}
inline Piece operator~(Piece pc) {
return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT
}
inline CastlingRight operator|(Color c, CastlingSide s) {
return CastlingRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c));
}
@@ -348,14 +354,14 @@ inline Value mated_in(int ply) {
}
inline Square make_square(File f, Rank r) {
return Square((r << 3) | f);
return Square((r << 3) + f);
}
inline Piece make_piece(Color c, PieceType pt) {
return Piece((c << 3) | pt);
return Piece((c << 3) + pt);
}
inline PieceType type_of(Piece pc) {
inline PieceType type_of(Piece pc) {
return PieceType(pc & 7);
}
@@ -394,7 +400,7 @@ inline bool opposite_colors(Square s1, Square s2) {
}
inline Square pawn_push(Color c) {
return c == WHITE ? DELTA_N : DELTA_S;
return c == WHITE ? NORTH : SOUTH;
}
inline Square from_sq(Move m) {
@@ -414,12 +420,12 @@ inline PieceType promotion_type(Move m) {
}
inline Move make_move(Square from, Square to) {
return Move(to | (from << 6));
return Move((from << 6) + to);
}
template<MoveType T>
inline Move make(Square from, Square to, PieceType pt = KNIGHT) {
return Move(to | (from << 6) | T | ((pt - KNIGHT) << 12));
return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
}
inline bool is_ok(Move m) {
+11 -9
View File
@@ -39,10 +39,10 @@ namespace {
// FEN string of the initial position, normal chess
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// Stack to keep track of the position states along the setup moves (from the
// A list to keep track of the position states along the setup moves (from the
// start position to the position just before the search starts). Needed by
// 'draw by repetition' detection.
Search::StateStackPtr SetupStates;
StateListPtr States(new std::deque<StateInfo>(1));
// position() is called when engine receives the "position" UCI command.
@@ -68,14 +68,14 @@ namespace {
else
return;
pos.set(fen, Options["UCI_Chess960"], Threads.main());
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>);
States = StateListPtr(new std::deque<StateInfo>(1));
pos.set(fen, Options["UCI_Chess960"], &States->back(), Threads.main());
// Parse move list (if any)
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
{
SetupStates->push(StateInfo());
pos.do_move(m, SetupStates->top(), pos.gives_check(m, CheckInfo(pos)));
States->push_back(StateInfo());
pos.do_move(m, States->back(), pos.gives_check(m));
}
}
@@ -108,7 +108,7 @@ namespace {
// the thinking time and other parameters from the input string, then starts
// the search.
void go(const Position& pos, istringstream& is) {
void go(Position& pos, istringstream& is) {
Search::LimitsType limits;
string token;
@@ -132,7 +132,7 @@ namespace {
else if (token == "infinite") limits.infinite = 1;
else if (token == "ponder") limits.ponder = 1;
Threads.start_thinking(pos, limits, SetupStates);
Threads.start_thinking(pos, States, limits);
}
} // namespace
@@ -146,9 +146,11 @@ namespace {
void UCI::loop(int argc, char* argv[]) {
Position pos(StartFEN, false, Threads.main()); // The root position
Position pos;
string token, cmd;
pos.set(StartFEN, false, &States->back(), Threads.main());
for (int i = 1; i < argc; ++i)
cmd += std::string(argv[i]) + " ";
+2 -2
View File
@@ -57,7 +57,7 @@ void init(OptionsMap& o) {
const int MaxHashMB = Is64Bit ? 1024 * 1024 : 2048;
o["Write Debug Log"] << Option(false, on_logger);
o["Debug Log File"] << Option("", on_logger);
o["Contempt"] << Option(0, -100, 100);
o["Threads"] << Option(1, 1, 128, on_threads);
o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size);
@@ -67,7 +67,7 @@ void init(OptionsMap& o) {
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(84, 10, 1000);
o["Slow Mover"] << Option(89, 10, 1000);
o["nodestime"] << Option(0, 0, 10000);
o["UCI_Chess960"] << Option(false);
o["SyzygyPath"] << Option("<empty>", on_tb_path);