Compare commits

...

165 Commits

Author SHA1 Message Date
Marco Costalba 4d120ee02e Stockfish 4
Stockfish bench signature is: 4132374
2013-08-20 09:01:25 +02:00
Tom Vijlbrief f45eee318b Fix crash when reaching max ply
Bug introduced in 1b7223a53c that
updated the ss base stack without increasing
the dimension.

No functional change.
2013-08-19 16:53:46 +02:00
Tom Vijlbrief 8c2fd2170a Remove useless condition in KXK endgame
Because eval is never called when in check.

No functional change.
2013-08-19 08:55:17 +02:00
Leonid Pechenik 91c2c44fb1 Further tweak movecount pruning
Passed both short TC
LLR: 2.95 (-2.94,2.94)
Total: 15140 W: 3125 L: 2976 D: 9039

And long TC
LLR: 2.95 (-2.94,2.94)
Total: 17118 W: 3165 L: 2974 D: 10979

bench: 4132374
2013-08-18 09:13:57 +02:00
Marco Costalba 27e9fc1067 Normalize "pawn in front of minor" patch
No functional change.
2013-08-17 11:05:55 +02:00
homoSapiensSapiens e005270fb6 Use constants arguments where possible
No functional changes.
2013-08-16 09:57:21 +02:00
Marco Costalba 4f55ed14d3 Revert using exceptions
Due to crashes. It will be reapplied once
we understand what's happening.

No functional change.
2013-08-15 09:36:26 +02:00
homoSapiensSapiens a6c0ba2100 Simplify DistanceRingsBB init
Verified by same benchmark and picking some random values.

No functional change.
2013-08-14 10:53:43 +02:00
homoSapiensSapiens fc316cbca9 Some renaming in TT store()
No functional change.
2013-08-14 09:38:35 +02:00
Marco Costalba 11b1a76f35 Use exceptions to stop the search
Instead of classical flags, throw an
exception when we want to immediately halt
the search. Currently only one type
is used for both UCI stop and threads
cut off.

No functional change.
2013-08-14 08:29:57 +02:00
Tom Vijlbrief bd8f463b7e Bonus for a pawn in front of knight/bishop
Idea originated from a post of Don Dailey
on talkchess and reported by Eelco.

This is the last succesful attempt of a long
series of trials (as usually happens, the
'idea' alone is not enough).

Passed both short 15secs TC
LLR: 2.97 (-2.94,2.94)
Total: 7629 W: 1645 L: 1515 D: 4469

And long 60secs TC
LLR: 2.96 (-2.94,2.94)
Total: 10218 W: 1932 L: 1775 D: 6511

bench: 4944581
2013-08-13 14:20:02 +02:00
Ryan Takker 4d14f97482 Remove Now Unneeded Help Text
With the new automatic setting of split depth
instead of a default, the user no longer needs
guidance on setting the split point.

Also threads now defaults to one.

No functional change.
2013-08-13 07:36:33 +02:00
Marco Costalba 15616ad199 Don't set Search::RootColor in Eval::trace
Search::RootColor is a global parameter set
before to start a search, it is not something
trace() should change.

This patch allows to add trace() calls, for
debugging, inside search itself without altering
the bench, and also ensures that the values
returned by trace() and evaluate() are fully
equivalent.

No functional change.
2013-08-11 07:02:50 +02:00
Marco Costalba 94a3608ab9 Fix GrainSize rounding error
The rounding formula is different between
positive and negative scores due to the
GrainSize/2 term that is asymmetric.

So use truncation instead of rounding. This
guarantees that evaluation is rounded to zero
in the same way for both positive and negative
scores.

Found with position's flip

bench: 4634244
2013-08-10 17:11:13 +02:00
Marco Costalba 5769509d72 Fix 'improving' condition
Because VALUE_NONE is 30002, it happens that
after a check the next move is never an improving
one.

After this patch bench signature is independent from
VALUE_NONE actual value.

bench: 4303194
2013-08-09 08:21:55 +02:00
Marco Costalba fff6b9f061 Increase LMR when not improving
Apply to LMR the same Eelco's idea
applied to move count pruning.

This is the result of a series of
attempts started by Thomas Kolarik.

Passed both short TC
LLR: 2.95 (-2.94, 2.94)
Total: 5675 W: 1241 L: 1117 D: 3317


And long TC:
LLR: 2.95 (-2.94, 2.94)
Total: 8748 W: 1689 L: 1539 D: 5520

bench: 4356801
2013-08-08 10:28:48 +02:00
Marco Costalba 56d2c3844a Further tweak Position::flip
No functional change.
2013-08-05 14:44:06 +02:00
Marco Costalba 23b6809f3d Rewrite flip() to use FEN string manipulation
Instead of dealing directly with internal parameters
just "flip" the FEN string and set the position from
that.

No functional change.
2013-08-05 12:58:14 +02:00
Marco Costalba f31847302d Streamline time computation
No functional change.
2013-08-03 18:30:43 +02:00
Marco Costalba b1a4a18d63 Update polyglot.ini with new "Min Split Depth" default
No functional change.
2013-08-03 16:41:18 +02:00
Dan Schmidt f7096ea7ce Refactor do_castle()
Not a real functional change, but bench changed due to different piecelist
reordering. To verify it a temporary my canonicalize_rooks function was
written as follows. It just ensures that the rook on the "smaller" square
is listed first.

void Position::canonicalize_rooks(Color c)
{
   if (pieceCount[c][ROOK] == 2)
   {
      Square s0 = pieceList[c][ROOK][0];
      Square s1 = pieceList[c][ROOK][1];
      if (s0 > s1)
      {
         pieceList[c][ROOK][0] = s1;
         pieceList[c][ROOK][1] = s0;
         index[s0] = 1;
         index[s1] = 0;
      }
   }
}

With this both bench and the test on Chess960 positions

./stockfish bench 128 1 8 Chess960.epd file > /dev/null

Gives same result.

bench: 4424151
2013-08-03 16:18:28 +02:00
Joona Kiiski a16ba5bbd1 Retire cpu_count()
Set threads number always to 1 at startup and let the
user explicitly to chose the number of threads.

Also preserve the useful behavior of automatically set
"Min Split Depth" according to the requested threads,
indeed this parameter is too technical for a casual user,
so, when left to zero, we set it on a sensible value.

No functional change
2013-08-02 16:48:25 +02:00
Marco Costalba 408e6ee9b6 Further factor out position update code
Along the lines of previous patch.

No functional change
2013-08-01 16:32:46 +02:00
Dan Schmidt 7b4f5c8f72 Factor out pieceList updating code
The new Position methods add_piece, move_piece, and remove_piece
now manage the member variables pieceList, pieceCount, and index,
and 9 blocks of code in Position that used to manipulate those
data structures by hand now call the new methods.

There is a slightly slowdown (< 1%) on Clang and on perft,
but the cleanup compensates the little speed loss.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-08-01 15:50:19 +02:00
Marco Costalba 55948623e7 Rework Thread hierarchy
Introduce ThreadBase struct that is search
agnostic and just handles low level stuff,
and derive all the other specialized classes
form here.

In particular TimerThread does not hinerits
anymore all the search related stuff from Thread.

Also some renaming while there.

Suggested by Steven Edwards

No functional change.
2013-07-31 18:35:52 +02:00
Marco Costalba 4d46d29efe Fix a race at thread creation
At thread creation start_routine() is called
and from there the virtual function idle_loop()
because we do this inside Thread c'tor, where the
virtual mechanism is disabled, it could happen that
the base class idle_loop() is called instead.

The issue happens with TimerThread and MainThread
where, at launch, start_routine calls
Thread::idle_loop instead of the derived ones.

Normally this bug is hidden because c'tor finishes
before start_routine() is actually called in the
just created execution thread, but on some platforms
and in some cases this is not guaranteed and the
engine hangs.

Reported by Ted Wong on talkchess

No functional change.
2013-07-31 18:35:32 +02:00
Marco Costalba cc608a7aba Tidy up Position::pretty
No functional change.
2013-07-29 19:33:30 +02:00
Marco Costalba 1f40cd6d02 Small renaming
No functional change.
2013-07-29 19:32:59 +02:00
Marco Costalba 28bc8ed462 Speed up move generation
Pass the color as template parameter
to generate_all()

Speedup of 1,3% in perft and 2,5% in bench !

No functional change.
2013-07-29 19:01:50 +02:00
Eelco de Groot 5ee16a180a Increase pruning if evaluation is not improving
Add an additional set of margins to movecount pruning
to be used when static evaluation is getting worse
than previous move.

Here are the margins table with changing
depth (fm0 not improving, fm1 improving):

    d: 0, fm0: 3, fm1: 3
    d: 1, fm0: 4, fm1: 4
    d: 2, fm0: 6, fm1: 6
    d: 3, fm0: 7, fm1: 10
    d: 4, fm0: 11, fm1: 15
    d: 5, fm0: 15, fm1: 21
    d: 6, fm0: 21, fm1: 29
    d: 7, fm0: 27, fm1: 37
    d: 8, fm0: 35, fm1: 47
    d: 9, fm0: 42, fm1: 57
    d: 10, fm0: 51, fm1: 68
    d: 11, fm0: 60, fm1: 81
    d: 12, fm0: 70, fm1: 94
    d: 13, fm0: 81, fm1: 108
    d: 14, fm0: 92, fm1: 123
    d: 15, fm0: 104, fm1: 139

Good at both short TC

LLR: 2.97 (-2.94,2.94)
Total: 11502 W: 2503 L: 2361 D: 6638

And long TC

LLR: 2.98 (-2.94,2.94)
Total: 7189 W: 1421 L: 1277 D: 4491

bench: 4364793
2013-07-29 01:21:21 +02:00
Marco Costalba d30dfc084c Annotate an unlikely condition
No functional change.
2013-07-27 11:34:15 +02:00
Marco Costalba 6373e88b5b Fix an assert in KBK endgame
The endgame king + minor vs king is erroneusly
detected as king + minor vs king + minor

Here the fix is to detect king + minor earlier,
in particular to add these trivial cases to
endgame evaluation functions.

Spotted by Reuven Peleg

bench: 4727133
2013-07-27 08:25:45 +02:00
Tom Vijlbrief 7487eb0dca Rewrite pawn shield and storm code
Passes quickly both short TC:
LLR: 2.95 (-2.94,2.94)
Total: 5755 W: 1349 L: 1222 D: 3184

And long TC:
LLR: 2.95 (-2.94,2.94)
Total: 2744 W: 628 L: 505 D: 1611

bench: 4727133
2013-07-26 00:12:46 +02:00
Marco Costalba 2067a99c07 Fix a typo in bitboard.h
Introduced by previous patch.

Spotted by Joerg Oster

No functional change.
2013-07-25 07:44:27 +02:00
homoSapiensSapiens 002062ae93 Use #ifndef instead of #if !defined
And #ifdef instead of #if defined

This is more standard form (see for example iostream file).

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-07-24 19:49:17 +02:00
Marco Costalba 4064ee5406 Simplify captures ordering
A big simplification and removing of useless code.

Finished at 50% both at short TC (with SPRT) than
at long TC at fixed number of games:
ELO: -0.14 +-3.4 (95%) LOS: 46.8%
Total: 15206 W: 2836 L: 2842 D: 9528

bench: 5059948
2013-07-24 07:53:32 +02:00
Marco Costalba b0fd2b6b98 Revert "Halve king eval margin"
This reverts commit 4b3a0fdab0.

As Gary says: " It failed when I tried it at long TC previously, and only
barely passed this time.  Some anecdotal evidence is that it hurts vs other
engines as well (the Lightspeed rating list showed a 16 elo drop from previous
best version - still +- 5 error bars on both, but that's still significant)"

I also agree that if we have some doubts (like in this case) it is better to
be safe than sorry.

bench: 4615572
2013-07-24 07:46:25 +02:00
Ryan Schmitt d0bc951835 Tune pawn PSQT values
Reduces the influence of PSQT for entries such as
the extended center and the h-file.

Passed both short TC test:
LLR: 2.95 (-2.94,2.94)
Total: 23919 W: 5207 L: 5029 D: 13683

And long TC one:
LLR: 2.96 (-2.94,2.94)
Total: 5762 W: 1108 L: 974 D: 3680

Bench: 4617880

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-07-24 07:41:51 +02:00
Reuven Peleg 73131e7c78 Use arrow operator instead of * and .
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-07-22 20:22:40 +02:00
Reuven Peleg 1cc18d8a7a Better condition in is_pseudo_legal()
Simplify occupied destination condition.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-07-22 20:02:31 +02:00
Marco Costalba c45c6d308f Small touches in move generation
No functional change.
2013-07-21 11:01:24 +02:00
Marco Costalba f73bb438aa Some renaming in MovePicker
No functional change.
2013-07-21 09:55:08 +02:00
Marco Costalba 71dd8a333f Rewrite and simplify SEE
This very speed critical code was full of clever (!)
tricks and subtle details.

So I have rewritten it in a more straithforward way
and, as very often happens, result is even faster
than original.

No functional change.
2013-07-21 01:04:29 +02:00
Marco Costalba 9207baed65 Revert "Fix critical SEE bug (take 2)"
This reverts commit 3e95800814

For some reason it fails the short TC test:
LLR: -2.96 (-2.94,2.94)
Total: 20033 W: 4214 L: 4265 D: 11554

bench: 4769737
2013-07-20 18:45:38 +02:00
Marco Costalba b191df5ebe Revert "Yet another attempt at signature-build"
Still broken on OS X

No functional change.
2013-07-20 15:15:31 +02:00
Marco Costalba 3e95800814 Fix critical SEE bug (take 2)
It is somewhat unbilievable but our SEE is broken !

    If the first SEE move is a king capture and square is
    defended then SEE continues instead of breaking.

    The bug shows only on normal SEE, not see_sign() so
    probing with a:

    dbg_hit_on_c(slIndex==1, captured == KING);

    reports just a tiny:

    Total 3465656 Hits 6646 hit rate (%) 0

    Bug was there since Retire seeValues[] and move PieceValue[] out of Position of 26/6/2011 (!)
    although for some reason didn't show immediately, indeed the
    bougous patch was a "No functional change" (!!)

    bench: 4699504
2013-07-20 14:24:47 +02:00
Marco Costalba 2ed56f4d5f Revert all the SEE stuff
The speed up seems to introduce some
functionality change.

Revert to original master for now.

bench: 4769737
2013-07-20 14:20:33 +02:00
Marco Costalba 7b0b463720 Yet another attempt at signature-build
This one should work on all flavours of sed

Suggested by by Louis Zulli

No functional change.
2013-07-20 14:05:04 +02:00
Marco Costalba a6c5b4c6fb Fix critical SEE bug
It is somewhat unbilievable but our SEE is broken !

If the first SEE move is a king capture and square is
defended then SEE continues instead of breaking.

The bug shows only on normal SEE, not see_sign() so
probing with a:

dbg_hit_on_c(slIndex==1, captured == KING);

reports just a tiny:

Total 3465656 Hits 6646 hit rate (%) 0

Bug was there since 351ef5c85b of 26/6/2011 (!)
although for some reason didn't show immediately, indeed the
bougous patch was a "No functional change" (!!)

bench: 4793754
2013-07-20 13:37:12 +02:00
Marco Costalba 0504a6975d Speedup see()
And rename next_attacker() SEE helper

This very simple patch is able to speed up
bench run of almost 2% !

No functional change.
2013-07-20 12:34:35 +02:00
Marco Costalba a5b5a91512 Fix signature-build under OSX
On OS X when you use -i an extension for the in-place
substitution a backup files is required.

http://stackoverflow.com/questions/4247068/sed-command-failing-on-mac-but-works-on-linux

So rewrite to make sed flushing sign.txt in one go and avoid
using -i option.

Reported by Louis Zulli

No functional change.
2013-07-20 02:38:14 +02:00
Reuven Peleg 1a8f63a896 Microptimize gives_check() for castling case
Without patch we have 333198 nps, with patch 334249.

A very small +0.3%, not a lot manily becuase this is a
side path that is taken very few times.

Anyhow idea is correct becuase first 'quick' condition
has an hit rate of about 95%.

No functional change.
2013-07-19 17:07:54 +02:00
Marco Costalba ee5514b8fd Small simplification in space eval scoring
No functional change.
2013-07-19 11:13:18 +02:00
Marco Costalba 99e547f4cb Rename MoveStack to ExtMove
Stack has no meaning here, while ExtMove (extended move),
better clarifies that we have a move + a score.

No functional change.
2013-07-19 10:27:58 +02:00
Marco Costalba 110644d918 Better document what we skip when in check
No functional change.
2013-07-19 09:37:31 +02:00
homoSapiensSapiens 4b3a0fdab0 Halve king eval margin
But still keep the same original
margin for score.

Passed both short TC test
LR: 2.95 (-2.94,2.94)
Total: 3710 W: 845 L: 726 D: 2139

And long TC
LLR: 2.95 (-2.94,2.94)
Total: 57859 W: 10939 L: 10532 D: 36388

bench: 4769737

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-07-19 08:16:39 +02:00
Marco Costalba 05e31c5e5f Drop grep and tr dependency in Makefile
Use only sed to get the bench signature.

No functional change.
2013-07-15 21:39:06 +02:00
Marco Costalba 46fdb14b2f Don't use __builtin_expect
Partially revert previous patch and use
unlikey() just as code annotation.

Actually it is better to rely on a profiler for branch prediction:

http://blog.man7.org/2012/10/how-much-do-builtinexpect-likely-and.html

"In fact, even when only one in ten thousand values is nonzero,
we're still at only roughly the break-even point"

No functional change,
2013-07-15 21:09:06 +02:00
Marco Costalba cbb1a8ed31 Better annotate unlikely conditions
And in case of gcc we win also a small
speed optimization due to better branch
prediction.

No functional change.
2013-07-15 21:01:02 +02:00
Reuven Peleg a6c5f60caa Simplify a condition in refutes()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-07-15 20:40:49 +02:00
Marco Costalba 9518cc3254 Update 'make help'
No functional change.
2013-07-14 12:23:28 +02:00
Marco Costalba b0a177bc67 Add signature-profile-build make target
Extend patch 3f64a2af6a to profile builds.

here the make command is:

make signature-profile-build ARCH=xxx COMP=xxx

No functional change.
2013-07-14 11:57:06 +02:00
Marco Costalba 5b7b330616 Retire engine Tag
It is somewhat redundant and could make SF
name too long, so use just Version, in case
of a signature build Version will be set to
'sig-xxx' otherwise, if left empty, we fall
back on usual date stamp.

No functional change.
2013-07-14 11:13:13 +02:00
Marco Costalba 3f64a2af6a Add signature-build make target
When compiling with:

make signature-build ARCH=xxx COMP=xxx

After binary has been roduced, it will be run to
get the signature 'stockfish bench' and this
number will be used as Version, so that it
will be easy to track the original sources
from a binary.

No functinal change.
2013-07-14 10:59:25 +02:00
Marco Costalba 9749f1f14c Fix build on Intel compiler
Due to a strange issue (bug?) the ternary
operator does not return a BitCountType for
icc, so revert to the expression used
before bcbc9bfd1f

No functional change.
2013-07-13 23:13:30 +02:00
Marco Costalba 4ede49cd85 Fully qualify memset and memcpy
And other trivial touches.

Ispired by Lucas's DiscoCheck

No functional change.
2013-07-13 18:01:13 +02:00
Tom Vijlbrief 6960f41e03 Retire enoughMaterial + lower trapped rook threshold
Here speed up is the name of the game.

Speed up is gained:

- Removing the useless enoughMaterial code

- Limiting trapped rook evaluation to where it counts

Tested at long TC:
LLR: 2.97 (-2.94,2.94)
Total: 10061 W: 1948 L: 1790 D: 6323

bench: 4558173
2013-07-13 18:01:03 +02:00
Marco Costalba bf90499fc3 A useless assignment found by Clang’s static analyzer
Warning is: "Value stored to 'xxx' is never read" and
it is raised in SpNode case.

No functional change.
2013-07-13 16:57:03 +02:00
Marco Costalba 404c4122ce Fix build with MSVC 2013
Also add an assert hinted by MSVC code analysis tool.

No functional change.
2013-07-13 13:03:09 +02:00
Marco Costalba 2a0bbb9faa Fix printing of PV info: take 2
Now last PV line is printed twice, fix that.

No functional change.
2013-07-13 07:43:50 +02:00
Marco Costalba 128e097d03 Fix printing of PV info
It was erroneusly skipped after the
aspiration window rework.

Reported by Eelco.

No functional change.
2013-07-12 23:42:42 +02:00
Marco Costalba c1264e46d0 Rename some UCI options
Thanks to Don, Miguel, Louis and the other people
of talkchess forum for the suggestion:

http://www.talkchess.com/forum/viewtopic.php?t=48612

Also sync polyglot.ini with current UCI options

No functional change.
2013-07-11 16:07:10 +02:00
Marco Costalba 366f6b0dab Fix a crash with depth 1 perft
Bug recently introduced in e215a88cdd

No functional change.
2013-07-11 07:23:48 +02:00
Marco Costalba 58aee9a9ea Don't IID when in check also in PvNodes
This tiny functional change allows to
nicely simplify things.

Performed at 50% in short TC:
LLR: -0.43 (-2.94,2.94)
Total: 46406 W: 9681 L: 9565 D: 27160

And succesfully passed long TC reverse test:
LLR: -2.95 (-2.94,2.94)
Total: 4945 W: 858 L: 937 D: 3150

bench: 4507230
2013-07-09 19:03:11 +02:00
Marco Costalba 4b703b1429 Revert previous patch
Unfortunatly a reverse test at long TC failed:

master^ vs master
LLR: 1.37 (-2.94,2.94)
Total: 33682 W: 6294 L: 6071 D: 21317

So becuase short TC score is 50% there is a good
possibility patch is not scalable.

So revert it.

bench: 4507288
2013-07-09 08:04:12 +02:00
Marco Costalba 62d38f0196 Simplify "fail high upon reduction" in null search
Do not use threat move to detect the condition. This
let us to retire the big allows() function.

Test at short TC was within 50% score:
LLR: -2.95 (-2.94,2.94)
Total: 38272 W: 7941 L: 7940 D: 22391

To be verified with reverse long TC

bench: 4191565
2013-07-08 07:23:30 +02:00
Marco Costalba 7e575512ae Skip node-level cut-off tests when in check
No functional change.
2013-07-07 13:39:58 +02:00
Marco Costalba a55fb76dcc Simplify aspiration window code
Here the main difference is that now we center
aspiration window on last returned score. This allows
to simplify handling of mate scores.

We have done a reversed SPRT tests, where we wanted to
verify if master is stronger than this patch.

Long TC: master vs this patch (reverse test)
LLR: -2.95 (-2.94,2.94)
Total: 37992 W: 7012 L: 6920 D: 24060

bench: 4507288
2013-07-03 18:59:23 +02:00
Marco Costalba 838255ef91 Workaround github issue
Temporary revert aspiration window patch
so to be visible to everybody: it will be
re-applied with next patch

No functional change (together with next one)
2013-07-03 18:58:23 +02:00
Marco Costalba 6fbe027da0 Simplify aspiration window code
Here the main difference is that now we center
aspiration window on last returned score. This allows
to simplify handling of mate scores.

We have done a reversed SPRT tests, where we wanted to
verify if master is stronger than this patch.

Long TC: master vs this patch (reverse test)
LLR: -2.95 (-2.94,2.94)
Total: 37992 W: 7012 L: 6920 D: 24060

bench: 4507288
2013-07-03 09:06:03 +02:00
Marco Costalba ddcb572c41 Disable flto when debugging
Link-time optimization does not work well with
generation of debugging information:

http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

Reported by Louis Zulli

No functional change.
2013-07-03 08:21:21 +02:00
Marco Costalba 12c0dfc113 Revert "Remove confusing optimization"
This reverts commit e05c80a088.

we gain a speed up of 1.5% under gcc !

No functional change.
2013-07-02 20:12:57 +02:00
Marco Costalba db53883f06 Merge branch 'master' into aspiration
bench: 4507288
2013-07-02 07:27:08 +02:00
Marco Costalba 13ffb08136 Revert "Increase earlier aspiration window size"
This reverts commit b88bc7b766.
2013-07-02 07:25:39 +02:00
Marco Costalba 2d82db1d14 Entering a pawn endgame is no more dangerous
A simplification of the 'dangerous' definition.

Seems neutral at reverse test at long TC

master vs patch
LLR: -2.96 (-2.94,2.94)
Total: 16974 W: 3122 L: 3139 D: 10713

bench: 4689029
2013-07-02 07:24:17 +02:00
Marco Costalba b88bc7b766 Increase earlier aspiration window size
bench: 4377851
2013-07-01 19:29:38 +02:00
Marco Costalba e074a19f5c Merge branch 'master' into aspiration 2013-07-01 19:25:23 +02:00
Marco Costalba b8930d0c26 Fix a stale comment
No functional change.
2013-06-30 13:12:04 +02:00
Marco Costalba 4fc7734547 Simplify search results update
Also some rename while there.

No functional change.
2013-06-30 12:49:39 +02:00
Marco Costalba 92dcbfa658 Reorder conditions according to their frequency
This should minimize useless tests.

No functional change.
2013-06-30 11:35:53 +02:00
Marco Costalba b50eb6bea8 Center aspiration window on last returned score
bench: 4428212
2013-06-30 11:14:02 +02:00
Marco Costalba 6f079ae720 Simplify aspiration window loop
Don't open the window in case we find a mate score: this
will be takes care with next patch.

No functional change.
2013-06-30 10:54:09 +02:00
Marco Costalba 203fdc9ac1 Use calloc() in TranspositionTable::set_size()
Function calloc() already initializes memory to
zero, so avoid calling clear() afterwards.

Also some renaming while there (inspired by DiscoCheck).

No functional change.
2013-06-29 11:23:07 +02:00
Marco Costalba 17d41b3861 Fix some stale comments
No functional change.
2013-06-23 13:19:03 +02:00
Marco Costalba 8cff4862a6 Move SquareDistance[] to bitboard.cpp
No functional change.
2013-06-23 13:13:13 +02:00
Marco Costalba 908d98820b Don't explicitize enum values when not needed
Compiler will chose the correct values in sequential
order for you.

Also move file and rank bitboards definitions to
bitboard.h

No functional change.
2013-06-23 11:30:40 +02:00
Marco Costalba a4c11b71ac Retire in_front_bb(Color c, Square s) overload
Explciitly call rank_of() in the few places where
it is used.

No functional change.
2013-06-23 10:16:43 +02:00
Marco Costalba b2fadf32aa Retire ThisAndAdjacentFilesBB[]
It is unused. Also renamed attack_span_mask to
pawn_attack_span

No functional change.
2013-06-23 10:09:24 +02:00
Marco Costalba 378bcfe760 Simplify hidden_checkers()
De-templetize and pass color as function argument.
No speed change.

No functional change.
2013-06-23 09:03:40 +02:00
Marco Costalba fe2ed42661 Name functions along corresponding UCI commands
No functional change.
2013-06-22 12:46:03 +02:00
Marco Costalba e215a88cdd Micro-optimize perft
Avoid to call perft function when we just need to count
moves, at leaf nodes.

Speed up of almost 2%

No functional change.
2013-06-21 09:10:03 +02:00
Ryan Schmitt e95e69515a Include file attacks in 'major on pawn'
Passed both short TC:
LLR: 2.97 (-2.94,2.94)
Total: 57846 W: 12248 L: 11974 D: 33624

And long one:
LLR: 2.95 (-2.94,2.94)
Total: 9181 W: 1732 L: 1581 D: 5868

bench: 4609948
2013-06-19 07:22:10 +02:00
Reuven Peleg e05c80a088 Remove confusing optimization
Here we skip the call to pos.attacks_from<ROOK>(s) in the 98%
of cases, testing the first 2 members first. Unfortunatly
code is a bit triky and not clear. So we give up to the
speed optimization in exchange of more code clarity.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-06-17 18:12:15 +02:00
Reuven Peleg 7b31e81d77 Merge some if statements in pos_is_ok()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-06-17 18:02:59 +02:00
Marco Costalba c0964fc70f Remove redundant condition in probcut
When !ss->skipNullMove it is assured that excludedMove == MOVE_NONE

No functional change.
2013-06-17 09:30:59 +02:00
Marco Costalba cd782c11ec Rename piece_count and piece_list
No functional change.
2013-06-16 13:21:10 +02:00
Marco Costalba 5ea984ac35 Don't calculate pawnsOnSquares twice
And reformat some code while there.

No functional change.
2013-06-16 11:32:10 +02:00
Marco Costalba 1fd020a8ba Use move_pawns() in Pawns::probe
And rename some stuff.

No functional change.
2013-06-16 10:40:36 +02:00
Marco Costalba 02420d4670 Revert "Reduce more CUT nodes only if parent node is reduced"
This reverts commit d54e8a5955.

It was not proved with SPRT this tweak is stronger. So revert it
for now to follow fishtest guidelines.

bench: 5108393
2013-06-14 08:27:06 +02:00
Marco Costalba 2c7ab488a8 Fix description of TT entry
It was way outdated and wrong !

No functional change.
2013-06-14 08:21:02 +02:00
Marco Costalba d54e8a5955 Reduce more CUT nodes only if parent node is reduced
So when we are doing a LMR search at the parent ALL node.

This patch didn't prove stronger at 60" TC
LLR: -2.97 (-2.94,2.94)
Total: 22398 W: 4070 L: 4060 D: 14268

But, first, it scores at 50%, second (and most important for me) the opposite,
i.e. normal reduction when parent node is not reduced, seems very bad:
LLR: -2.95 (-2.94,2.94)
Total: 7036 W: 1446 L: 1534 D: 4056

According to Don, this idea of increased reduction of CUT nodes
works because if parent node is reduced, missing a cut-off due to
reduced depth search (meaning position is somehow tricky) forces
a full depth research at parent node, giving due insight in this
set of sensible positions.

IOW if we expect a node to fail-high at depth n, then we assume it
should fail-high also at depth n-1, if this doesn't happen it means
position is tricky enough to deserve a research at depth n+1.

bench: 4687419
2013-06-13 20:05:02 +02:00
Marco Costalba 4bebb15e94 Reduce more CUT nodes
We got a good result from this tweak, in line with
what was already found by Don Dailey.

At short TC:
LLR: 2.95 (-2.94,2.94)
Total: 13097 W: 2742 L: 2598 D: 7757

At long TC:
LLR: 2.97 (-2.94,2.94)
Total: 7281 W: 1408 L: 1265 D: 4608

bench: 5108393
2013-06-13 19:50:32 +02:00
Marco Costalba 3b8f66f8ac Introduce Cut/All node definitions
Follow Don Dailey definition of cut/all node:

"If the previous node was a cut node, we consider this an ALL node.
The only exception is for PV nodes which are a special case of ALL nodes.
In the PVS framework, the first zero width window searched from a PV
node is by our definition a CUT node and if you have to do a re-search
then it is suddenly promoted to a PV nodes (as per PVS search) and only
then can the cut and all nodes swap positions. In other words, these
internal search failures can force the status of every node in the subtree
to swap if it propagates back to the last PV nodes."

http://talkchess.com/forum/viewtopic.php?topic_view=threads&p=519741&t=47577

With this definition we have an hit rate higher than 90% on:

    if (!PvNode && depth > 4 * ONE_PLY)
        dbg_hit_on_c(cutNode, (bestValue >= beta));

And an hit rate of just 28% on:

    if (!PvNode && depth > 4 * ONE_PLY)
        dbg_hit_on_c(!cutNode, (bestValue >= beta));

No functional change.
2013-06-13 19:46:49 +02:00
Marco Costalba b6e9d901b0 Don't use std::vector::data()
It is a C++11 only function.

Reported by Eelco.

No functional change.
2013-06-13 07:42:43 +02:00
Marco Costalba 1b7223a53c Fix again early stop ss pointer
Fix was wrong becuase search starts from ss+1,
code is a bit tricky here, so rewrite in a way
to be more easy to read and understand.

Spotted by Eelco.

No functional change.
2013-06-09 23:36:46 +02:00
Marco Costalba de1dc4f2de Don't need to expose namespace Zobrist
It can be local to position.cpp

No functional change.
2013-06-09 23:27:07 +02:00
Marco Costalba a6e0f62a4f Zobrist::init() should be Position::init()
No functional change.
2013-06-09 13:54:38 +02:00
Marco Costalba 81e242a96d Convert pieceSquareTable to 3 dimensions
No functional change.
2013-06-09 13:10:21 +02:00
Marco Costalba dd1855eb2f More consistent 'piece' variable naming
No functional change.
2013-06-09 12:56:05 +02:00
Marco Costalba db4cd89cb8 Introduce operator~(Piece c)
Small syntactic sugar to reverse piece color.

No functional change.
2013-06-09 12:44:04 +02:00
Marco Costalba 7e95495b35 Retire psq_delta()
No functional change.
2013-06-09 12:32:16 +02:00
Marco Costalba 55eb7dd1e9 Use alpha instead of beta-1
It is more directly related to a fail-low.

No functional change.
2013-06-09 11:52:39 +02:00
Marco Costalba 902c0566a6 Fix incorrect 'ss' pointer in early stop check
The exclusion search used to verify one move is much
better than other shall be called with 'ss' and not
'ss+1'

No functional change.
2013-06-09 11:01:11 +02:00
Dariusz Orzechowski bc02cc0c8a Fix a typo
No functional change.
2013-06-08 11:01:28 +02:00
Marco Costalba 05c6f7a40b Fix search log when using skills
In case of we pick a sub-optimal move be
sure to print this, and not the best one
on seach log file.

Bug spotted by Guenther Demetz.

No functional change.
2013-06-08 10:56:20 +02:00
Marco Costalba 2a98042c21 Fix a crash when 'go' multiple times
Search is started after setting a position and
issuing UCI 'go' command. Then if we stop the search
and call 'go' again without setting a new position it
is assumed that the previous setup is preserved, but
this is not the case because what happens is that
SetupStates is reset to NULL, leading to a crash as
soon as RootPos.is_draw() is called because st->previous
is now stale.

UCI protocol is not very clear about requiring that a
position is setup always before launching a search,
so here we easy the life of GUI developers assuming
that the current state is preserved after returning
from a 'stop' command.

Bug reported by Gregor Cramer.

No functional change.
2013-06-01 16:19:42 +02:00
Marco Costalba 46409a7852 Assorted renaming in evaluation
And some reshuffle too.

No functional change.
2013-06-01 13:17:39 +02:00
jundery d8b266af8b Passed pawn tuning
A small number of tests with simulated
annealing at 15s indicated these values
may be better

And this is verified at long 60+0.05 TC
LLR: 2.95 (-2.94,2.94)
Total: 40658 W: 7821 L: 7501 D: 25336

bench: 4931544
2013-05-31 09:17:48 +02:00
Marco Costalba abb40777bf Shrink engine UCI name
Some GUI have problems with long names.

Reported by George Speight.

No functional change.
2013-05-27 17:43:38 +02:00
Marco Costalba 90abcac1c7 Add Pawn Structure also to polyglot.ini
No functional change.
2013-05-25 13:14:41 +02:00
Marco Costalba 7222f47350 Re-add "Pawn Structure" UCI option
And reshuffle the code to not special case
this parameter.

No functional change.
2013-05-25 12:38:14 +02:00
Marco Costalba eafb66e1aa More uniform tracing code
No functional change.
2013-05-25 12:01:50 +02:00
Uri Blass d4a02b135d Bunch of 3 small patches
This patch is the sum of:

- Grainsize of 4 instead of 8

- Removing "depth < DEPTH_ZERO"

- Change DEPTH_QS_RECAPTURES = -5 to -7

All the patches individually failed to pass SPRT but scored
around 50%.

Together they pass easily short TC:
LLR: 2.96 (-2.94,2.94)
Total: 4429 W: 964 L: 844 D: 2621

And with some difficult long TC of 60+0.05:
LLR: 2.95 (-2.94,2.94)
Total: 64133 W: 11968 L: 11532 D: 40633

bench: 4821467
2013-05-23 17:59:39 +02:00
Marco Costalba d3608c4e79 Microptimize MoveList loop
Add MOVE_NONE at the tail, this allows to loop
across MoveList checking for *it != MOVE_NONE,
and because *it is used imediately after compiler
is able to reuse it.

With this small patch perft speed increased of 3%

And it is also a semplification !

No functional change.
2013-05-19 22:00:49 +02:00
Marco Costalba 38cfbeeb50 Delay killers[] initialization
Most of the time we cut-off earlier, at captures, so this
results in useless work.

There is a small functionality change becuase 'ss' can change
from MovePicker c'tor to when killers are tried due, for
instance, to singular search.

bench: 4603795
2013-05-19 21:41:56 +02:00
Marco Costalba 77547a4ef1 Reduce countermoves less in LMR
Passed SPRT for both short TC 15+0.05:
LLR: 2.95 (-2.94,2.94)
Total: 17724 W: 3756 L: 3598 D: 10370

And long TC 60+0.05:
LLR: 2.95 (-2.94,2.94)
Total: 22672 W: 4232 L: 4011 D: 14429

bench: 4418832
2013-05-19 21:36:23 +02:00
Marco Costalba 8ceef92266 Mimic an iterator for looping across MoveList
Seems more conventional.

No functional change.
2013-05-19 13:28:25 +02:00
Joona Kiiski f7c013edd0 Use two counter moves instead of one
Very good at long 60"+0.05 TC
LLR: 2.95 (-2.94,2.94)
Total: 5954 W: 1151 L: 1016 D: 3787

[edit: slightly changed form original patch to avoid useless loop
 across killers when killer is MOVE_NONE]

bench: 4327405
2013-05-16 16:20:50 +02:00
Marco Costalba 148490f04c Rename Refutation to Countermove
Use proper naming according to:

http://chessprogramming.wikispaces.com/Countermove+Heuristic

The name of this idea is "Countermove Heuristic" and was
first introduced by Jos Uiterwijk in 1992

No functional change.
2013-05-15 20:59:56 +02:00
Uri Blass 7c6f346c90 Increased mobility array
Performed more or less well at short TC
LLR: 2.95 (-2.94,2.94)
Total: 50517 W: 9815 L: 9574 D: 31128

And a bit better at long TC
LLR: 2.96 (-2.94,2.94)
Total: 15564 W: 2805 L: 2624 D: 10135

bench: 4375253
2013-05-15 00:34:05 +02:00
Marco Costalba 1a34496761 Revert trapped rook bug fix
It seems that do  not limiting checking the
trapped rook only on rank 1 improves the
score.

At long TC
LLR: 2.97 (-2.94,2.94)
Total: 6581 W: 1346 L: 1204 D: 4031

bench: 4985012
2013-05-15 00:06:11 +02:00
Gary Linscott 049e5ca191 Minor bugfixes to refutation table
Don't update refutation table in case of
previous move is MOVE_NULL or MOVE_NONE
and don't try refutation if is already
a killer move.

Pass both short TC
LLR: 2.96 (-2.94,2.94)
Total: 4310 W: 953 L: 869 D: 2488

And long one
LLR: 2.95 (-2.94,2.94)
Total: 6707 W: 1254 L: 1184 D: 4269

bench: 4785954
2013-05-14 23:52:44 +02:00
Marco Costalba 19dd0de4ff Reformat previous patch
No functional change.
2013-05-13 20:42:44 +02:00
Joona Kiiski 8a61b030a6 Enable refuation table
Very good result both at short TC 15+0.05
LLR: 2.95 (-2.94,2.94)
Total: 2803 W: 596 L: 483 D: 1724

And at long TC 60+0.05
LLR: 2.95 (-2.94,2.94)
Total: 2862 W: 548 L: 431 D: 1883

bench: 4329221
2013-05-13 19:48:41 +02:00
Joona Kiiski c7e31d5aa8 Simple always overwrite Refutation table 2013-05-12 21:21:46 +01:00
Marco Costalba 818a3537a7 Use Them instead of ~Us
Unortunatly we have no guarantee that the call to
operator~(Color c) is resolved at compile time.

Perhaps the solution would be to use C++11 const_expr,
but for now simply use the good old-style ternary operator
that works as expected.

No functional change.
2013-05-11 11:49:44 +02:00
Marco Costalba bcbc9bfd1f Some code reformat in evaluate_pieces
No functional change.
2013-05-11 11:13:06 +02:00
Marco Costalba 7eda7335fd Simplify previous patch
No functional change.
2013-05-09 19:09:51 +02:00
Marco Costalba 02606a8c83 Merge 'passed_pawns' tweaks
Good at both short and long TC

15+0.05
LLR: 2.96 (-2.94,2.94)
Total: 28220 W: 5531 L: 5349 D: 17340

TC 60+0.05
LLR: 2.95 (-2.94,2.94)
Total: 12612 W: 2221 L: 2057 D: 8334

bench: 4857939
2013-05-08 23:04:11 +02:00
Reuven Peleg e7505324f6 Avoid explicit bitwise operators
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-05-05 22:51:18 +02:00
jundery 653c0527a7 Passed pawn eval
Use a stepped function to evaluate bonuses and add the bonus to the
middle game

bench: 4857939
2013-05-05 11:12:04 -06:00
Marco Costalba 7f4c7cd785 Merge increased 'movecount' pruning
Good at both short and long TC

15+0.05
LLR: 2.95 (-2.94,2.94)
Total: 13814 W: 2731 L: 2588 D: 8495

TC 60+0.05
LLR: 2.95 (-2.94,2.94)
Total: 18013 W: 3136 L: 2946 D: 11931

bench: 4306557
2013-05-05 13:46:26 +02:00
Marco Costalba 0958e5c6d3 Simplify previous condition
No functional change.
2013-05-05 12:31:32 +02:00
Marco Costalba 9fc77bc414 Fix trapped rook condition
A rook is trapped if on rank 1 as is the king.
Currently the condition aloows for the rook
to be also in front of the pawns as long
as king is on first rank.

Verified with short TC test:
LLR: -1.71 (-2.94,2.94)
Total: 23234 W: 4317 L: 4317 D: 14600

Here what it counts is that after 23K games
result is equal.

bench: 4696542
2013-05-05 12:27:11 +02:00
Marco Costalba 3b92159908 Further simplify previous patch
No functional change.
2013-05-04 12:15:31 +02:00
homoSapiensSapiens e00bb13e85 Merge some conditions
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2013-05-04 11:13:22 +02:00
Marco Costalba 3b41e62666 Drop some redundant defined(_WIN64)
When it is already defined(_WIN32).

According to Microsoft documentation:
http://msdn.microsoft.com/en-us/library/b0084kay.aspx

_WIN32 Defined for applications for Win32 and Win64. Always defined.

_WIN64 Defined for applications for Win64.

Patch suggested by Joona.

No functional change.
2013-05-03 15:24:54 +02:00
Marco Costalba 37c91aa94c Print time and node count before search ends
This info is normally printed together with
PV info in uci_pv() but when search is stopped,
for instance when max search time is reached,
uci_pv is not called and we miss this bits.

Suggested by gravy_train

No functional change.
2013-05-03 10:26:03 +02:00
Marco Costalba 43f67eab5f Merge mobility area tweak
A nice improvment.

Was good at 15+0.05
LLR: 2.96 (-2.94,2.94)
Total: 10731 W: 2176 L: 2040 D: 6515

And at 60"+0.05
LLR: 2.96 (-2.94,2.94)
Total: 10601 W: 1968 L: 1810 D: 6823

bench: 4676606
2013-05-03 10:12:31 +02:00
Gary Linscott 11d30b6298 Fix rounding issue 2013-05-02 14:37:55 -04:00
Gary Linscott 3edb15d183 More aggressive move count pruning 2013-05-02 09:47:34 -04:00
Marco Costalba d44ac0a485 Another take at TT alignment
This time revert to original version but using
uintptr_t instead of size_t

Suggested by Lucas.

No functional change.
2013-05-02 09:38:23 +02:00
Marco Costalba 481eda4ca0 Re-add "Cache line aligned TT"
But this time do not play with pointers, in
particular do not assume that size_t is an
unsigned type of the same width as pointers.

This code should be fully portable.

No functional change.
2013-05-01 23:42:16 +02:00
jhellis3 7323231786 Tweak Mobility Area
Only consider pawns and the king as restricting.
2013-05-01 02:37:50 -05:00
Marco Costalba e381951a24 Restore development version
No functional change.
2013-04-30 20:01:07 +02:00
43 changed files with 1539 additions and 1708 deletions
+5 -9
View File
@@ -7,14 +7,10 @@ Partner or Fritz) in order to be used comfortably. Read the
documentation for your GUI of choice for information about how to use documentation for your GUI of choice for information about how to use
Stockfish with it. Stockfish with it.
This version of Stockfish supports up to 64 CPUs, but has not been This version of Stockfish supports up to 64 CPUs. The engine defaults
tested thoroughly with more than 4. The program tries to detect the to one search thread it is therefore recommended to inspect the value of
number of CPUs on your computer and sets the number of search threads the *Threads* UCI parameter, and to make sure it equals the number of CPU
accordingly, but please be aware that the detection is not always cores on your computer.
correct. It is therefore recommended to inspect the value of the
*Threads* UCI parameter, and to make sure it equals the number of CPU
cores on your computer. If you are using more than eight threads, it is
recommended to raise the value of the *Min Split Depth* UCI parameter to 7.
### Files ### Files
@@ -25,7 +21,7 @@ This distribution of Stockfish consists of the following files:
* Copying.txt, a text file containing the GNU General Public License. * Copying.txt, a text file containing the GNU General Public License.
* src/, a subdirectory containing the full source code, including a Makefile * src, a subdirectory containing the full source code, including a Makefile
that can be used to compile Stockfish on Unix-like systems. For further that can be used to compile Stockfish on Unix-like systems. For further
information about how to compile Stockfish yourself read section below. information about how to compile Stockfish yourself read section below.
+9 -5
View File
@@ -14,22 +14,25 @@ ResignScore = 600
[Engine] [Engine]
Use Search Log = false Write Debug Log = false
Write Search Log = false
Search Log Filename = SearchLog.txt Search Log Filename = SearchLog.txt
Book File = book.bin Book File = book.bin
Best Book Move = false Best Book Move = false
Contempt Factor = 0 Contempt Factor = 0
Mobility (Middle Game) = 100 Mobility (Midgame) = 100
Mobility (Endgame) = 100 Mobility (Endgame) = 100
Passed Pawns (Middle Game) = 100 Pawn Structure (Midgame) = 100
Pawn Structure (Endgame) = 100
Passed Pawns (Midgame) = 100
Passed Pawns (Endgame) = 100 Passed Pawns (Endgame) = 100
Space = 100 Space = 100
Aggressiveness = 100 Aggressiveness = 100
Cowardice = 100 Cowardice = 100
Min Split Depth = 4 Min Split Depth = 0
Max Threads per Split Point = 5 Max Threads per Split Point = 5
Threads = 1 Threads = 1
Use Sleeping Threads = false Idle Threads Sleep = false
Hash = 128 Hash = 128
Ponder = true Ponder = true
OwnBook = false OwnBook = false
@@ -39,5 +42,6 @@ Emergency Move Horizon = 40
Emergency Base Time = 200 Emergency Base Time = 200
Emergency Move Time = 70 Emergency Move Time = 70
Minimum Thinking Time = 20 Minimum Thinking Time = 20
Slow Mover = 100
UCI_Chess960 = false UCI_Chess960 = false
UCI_AnalyseMode = false UCI_AnalyseMode = false
+31 -15
View File
@@ -34,8 +34,9 @@ ifeq ($(UNAME),Haiku)
endif endif
BINDIR = $(PREFIX)/bin BINDIR = $(PREFIX)/bin
### Built-in benchmark for pgo-builds ### Built-in benchmark for pgo-builds and signature
PGOBENCH = ./$(EXE) bench 32 1 10 default depth PGOBENCH = ./$(EXE) bench 32 1 10 default depth
SIGNBENCH = ./$(EXE) bench
### Object files ### Object files
OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \ OBJS = benchmark.o bitbase.o bitboard.o book.o endgame.o evaluate.o main.o \
@@ -231,7 +232,7 @@ ifeq ($(COMP),clang)
endif endif
### 3.2 General compiler settings ### 3.2 General compiler settings
CXXFLAGS = -g -Wall -Wcast-qual -fno-exceptions -fno-rtti $(EXTRACXXFLAGS) CXXFLAGS = -Wall -Wcast-qual -fno-exceptions -fno-rtti $(EXTRACXXFLAGS)
ifeq ($(comp),gcc) ifeq ($(comp),gcc)
CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra -Wshadow CXXFLAGS += -ansi -pedantic -Wno-long-long -Wextra -Wshadow
@@ -275,6 +276,8 @@ endif
### 3.4 Debugging ### 3.4 Debugging
ifeq ($(debug),no) ifeq ($(debug),no)
CXXFLAGS += -DNDEBUG CXXFLAGS += -DNDEBUG
else
CXXFLAGS += -g
endif endif
### 3.5 Optimization ### 3.5 Optimization
@@ -354,6 +357,7 @@ endif
### needs access to the optimization flags. ### needs access to the optimization flags.
ifeq ($(comp),gcc) ifeq ($(comp),gcc)
ifeq ($(optimize),yes) ifeq ($(optimize),yes)
ifeq ($(debug),no)
GCC_MAJOR := `$(CXX) -dumpversion | cut -f1 -d.` GCC_MAJOR := `$(CXX) -dumpversion | cut -f1 -d.`
GCC_MINOR := `$(CXX) -dumpversion | cut -f2 -d.` GCC_MINOR := `$(CXX) -dumpversion | cut -f2 -d.`
ifeq (1,$(shell expr \( $(GCC_MAJOR) \> 4 \) \| \( $(GCC_MAJOR) \= 4 \& $(GCC_MINOR) \>= 5 \))) ifeq (1,$(shell expr \( $(GCC_MAJOR) \> 4 \) \| \( $(GCC_MAJOR) \= 4 \& $(GCC_MINOR) \>= 5 \)))
@@ -362,6 +366,7 @@ ifeq ($(comp),gcc)
endif endif
endif endif
endif endif
endif
### ========================================================================== ### ==========================================================================
### Section 4. Public targets ### Section 4. Public targets
@@ -375,19 +380,20 @@ help:
@echo "" @echo ""
@echo "Supported targets:" @echo "Supported targets:"
@echo "" @echo ""
@echo "build > Build unoptimized version" @echo "build > Standard build"
@echo "profile-build > Build PGO-optimized version" @echo "signature-build > Standard build with embedded signature"
@echo "profile-build > PGO build"
@echo "signature-profile-build > PGO build with embedded signature"
@echo "strip > Strip executable" @echo "strip > Strip executable"
@echo "install > Install executable" @echo "install > Install executable"
@echo "clean > Clean up" @echo "clean > Clean up"
@echo "testrun > Make sample run"
@echo "" @echo ""
@echo "Supported archs:" @echo "Supported archs:"
@echo "" @echo ""
@echo "x86-64 > x86 64-bit" @echo "x86-64 > x86 64-bit"
@echo "x86-64-modern > x86 64-bit with runtime support for popcnt instruction" @echo "x86-64-modern > x86 64-bit with popcnt support"
@echo "x86-32 > x86 32-bit excluding old hardware without SSE-support" @echo "x86-32 > x86 32-bit with SSE support"
@echo "x86-32-old > x86 32-bit including also very old hardware" @echo "x86-32-old > x86 32-bit fall back for old hardware"
@echo "osx-ppc-64 > PPC-Mac OS X 64 bit" @echo "osx-ppc-64 > PPC-Mac OS X 64 bit"
@echo "osx-ppc-32 > PPC-Mac OS X 32 bit" @echo "osx-ppc-32 > PPC-Mac OS X 32 bit"
@echo "osx-x86-64 > x86-Mac OS X 64 bit" @echo "osx-x86-64 > x86-Mac OS X 64 bit"
@@ -396,12 +402,12 @@ help:
@echo "general-64 > unspecified 64-bit" @echo "general-64 > unspecified 64-bit"
@echo "general-32 > unspecified 32-bit" @echo "general-32 > unspecified 32-bit"
@echo "" @echo ""
@echo "Supported comps:" @echo "Supported compilers:"
@echo "" @echo ""
@echo "gcc > Gnu compiler (default)" @echo "gcc > Gnu compiler (default)"
@echo "icc > Intel compiler"
@echo "mingw > Gnu compiler with MinGW under Windows" @echo "mingw > Gnu compiler with MinGW under Windows"
@echo "clang > LLVM Clang compiler" @echo "clang > LLVM Clang compiler"
@echo "icc > Intel compiler"
@echo "" @echo ""
@echo "Non-standard targets:" @echo "Non-standard targets:"
@echo "" @echo ""
@@ -409,10 +415,11 @@ help:
@echo "" @echo ""
@echo "Examples. If you don't know what to do, you likely want to run: " @echo "Examples. If you don't know what to do, you likely want to run: "
@echo "" @echo ""
@echo "make profile-build ARCH=x86-64 (This is for 64-bit systems)" @echo "make build ARCH=x86-64 (This is for 64-bit systems)"
@echo "make profile-build ARCH=x86-32 (This is for 32-bit systems)" @echo "make build ARCH=x86-32 (This is for 32-bit systems)"
@echo "" @echo ""
.PHONY: build profile-build embed-signature
build: build:
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity $(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) all $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
@@ -437,6 +444,18 @@ profile-build:
@echo "Step 4/4. Deleting profile data ..." @echo "Step 4/4. Deleting profile data ..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean) $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_clean)
embed-signature:
@echo "Running benchmark for getting the signature ..."
@$(SIGNBENCH) 2>&1 | sed -n 's/Nodes searched : \(.*\)/\/string Version\/s\/"\\(.*\\)"\/"sig-\1"\//p' > sign.txt
@sed -f sign.txt misc.cpp > misc2.cpp
@mv misc2.cpp misc.cpp
@rm sign.txt
signature-build: build embed-signature
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
signature-profile-build: build embed-signature profile-build
strip: strip:
strip $(EXE) strip $(EXE)
@@ -448,9 +467,6 @@ install:
clean: clean:
$(RM) $(EXE) $(EXE).exe *.o .depend *~ core bench.txt *.gcda $(RM) $(EXE) $(EXE).exe *.o .depend *~ core bench.txt *.gcda
testrun:
@$(PGOBENCH)
default: default:
help help
+1 -1
View File
@@ -118,7 +118,7 @@ void benchmark(const Position& current, istream& is) {
for (size_t i = 0; i < fens.size(); i++) for (size_t i = 0; i < fens.size(); i++)
{ {
Position pos(fens[i], Options["UCI_Chess960"], Threads.main_thread()); Position pos(fens[i], Options["UCI_Chess960"], Threads.main());
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl; cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;
+7 -5
View File
@@ -39,9 +39,9 @@ namespace {
// bit 6-11: black king square (from SQ_A1 to SQ_H8) // bit 6-11: black king square (from SQ_A1 to SQ_H8)
// bit 12: side to move (WHITE or BLACK) // bit 12: side to move (WHITE or BLACK)
// bit 13-14: white pawn file (from FILE_A to FILE_D) // bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn 6 - rank (from 6 - RANK_7 to 6 - RANK_2) // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2)
unsigned index(Color us, Square bksq, Square wksq, Square psq) { unsigned index(Color us, Square bksq, Square wksq, Square psq) {
return wksq + (bksq << 6) + (us << 12) + (file_of(psq) << 13) + ((6 - rank_of(psq)) << 15); return wksq + (bksq << 6) + (us << 12) + (file_of(psq) << 13) + ((RANK_7 - rank_of(psq)) << 15);
} }
enum Result { enum Result {
@@ -110,7 +110,7 @@ namespace {
wksq = Square((idx >> 0) & 0x3F); wksq = Square((idx >> 0) & 0x3F);
bksq = Square((idx >> 6) & 0x3F); bksq = Square((idx >> 6) & 0x3F);
us = Color ((idx >> 12) & 0x01); us = Color ((idx >> 12) & 0x01);
psq = File((idx >> 13) & 3) | Rank(6 - (idx >> 15)); psq = File ((idx >> 13) & 0x03) | Rank(RANK_7 - (idx >> 15));
// Check if two pieces are on the same square or if a king can be captured // Check if two pieces are on the same square or if a king can be captured
if ( wksq == psq || wksq == bksq || bksq == psq if ( wksq == psq || wksq == bksq || bksq == psq
@@ -148,12 +148,14 @@ namespace {
// as WIN, the position is classified WIN otherwise the current position is // as WIN, the position is classified WIN otherwise the current position is
// classified UNKNOWN. // classified UNKNOWN.
const Color Them = (Us == WHITE ? BLACK : WHITE);
Result r = INVALID; Result r = INVALID;
Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq]; Bitboard b = StepAttacksBB[KING][Us == WHITE ? wksq : bksq];
while (b) while (b)
r |= Us == WHITE ? db[index(~Us, bksq, pop_lsb(&b), psq)] r |= Us == WHITE ? db[index(Them, bksq, pop_lsb(&b), psq)]
: db[index(~Us, pop_lsb(&b), wksq, psq)]; : db[index(Them, pop_lsb(&b), wksq, psq)];
if (Us == WHITE && rank_of(psq) < RANK_7) if (Us == WHITE && rank_of(psq) < RANK_7)
{ {
+10 -16
View File
@@ -42,14 +42,13 @@ Bitboard SquareBB[SQUARE_NB];
Bitboard FileBB[FILE_NB]; Bitboard FileBB[FILE_NB];
Bitboard RankBB[RANK_NB]; Bitboard RankBB[RANK_NB];
Bitboard AdjacentFilesBB[FILE_NB]; Bitboard AdjacentFilesBB[FILE_NB];
Bitboard ThisAndAdjacentFilesBB[FILE_NB];
Bitboard InFrontBB[COLOR_NB][RANK_NB]; Bitboard InFrontBB[COLOR_NB][RANK_NB];
Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard DistanceRingsBB[SQUARE_NB][8]; Bitboard DistanceRingsBB[SQUARE_NB][8];
Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB]; Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
int SquareDistance[SQUARE_NB][SQUARE_NB]; int SquareDistance[SQUARE_NB][SQUARE_NB];
@@ -84,7 +83,7 @@ namespace {
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard. /// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard. /// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
#if !defined(USE_BSFQ) #ifndef USE_BSFQ
Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; } Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; }
@@ -123,7 +122,7 @@ Square msb(Bitboard b) {
return (Square)(result + MS1BTable[b32]); return (Square)(result + MS1BTable[b32]);
} }
#endif // !defined(USE_BSFQ) #endif // ifndef USE_BSFQ
/// Bitboards::print() prints a bitboard in an easily readable format to the /// Bitboards::print() prints a bitboard in an easily readable format to the
@@ -171,10 +170,7 @@ void Bitboards::init() {
} }
for (File f = FILE_A; f <= FILE_H; f++) for (File f = FILE_A; f <= FILE_H; f++)
{
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0); AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndAdjacentFilesBB[f] = FileBB[f] | AdjacentFilesBB[f];
}
for (Rank r = RANK_1; r < RANK_8; r++) for (Rank r = RANK_1; r < RANK_8; r++)
InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]); InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]);
@@ -183,19 +179,17 @@ void Bitboards::init() {
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)
{ {
ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)]; ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)];
PassedPawnMask[c][s] = InFrontBB[c][rank_of(s)] & ThisAndAdjacentFilesBB[file_of(s)]; PawnAttackSpan[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
AttackSpanMask[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)]; PassedPawnMask[c][s] = ForwardBB[c][s] | PawnAttackSpan[c][s];
} }
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++) for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++) for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
{
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2)); SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
if (s1 != s2)
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++) DistanceRingsBB[s1][SquareDistance[s1][s2] - 1] |= s2;
for (int d = 1; d < 8; d++) }
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (SquareDistance[s1][s2] == d)
DistanceRingsBB[s1][d - 1] |= s2;
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 }, int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } }; {}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
@@ -322,7 +316,7 @@ namespace {
do magics[s] = pick_random(rk, booster); do magics[s] = pick_random(rk, booster);
while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6); while (popcount<Max15>((magics[s] * masks[s]) >> 56) < 6);
memset(attacks[s], 0, size * sizeof(Bitboard)); std::memset(attacks[s], 0, size * sizeof(Bitboard));
// A good magic must map every possible occupancy to an index that // A good magic must map every possible occupancy to an index that
// looks up the correct sliding attack in the attacks[s] database. // looks up the correct sliding attack in the attacks[s] database.
+74 -47
View File
@@ -18,7 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(BITBOARD_H_INCLUDED) #ifndef BITBOARD_H_INCLUDED
#define BITBOARD_H_INCLUDED #define BITBOARD_H_INCLUDED
#include "types.h" #include "types.h"
@@ -37,6 +37,24 @@ bool probe_kpk(Square wksq, Square wpsq, Square bksq, Color us);
} }
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1;
const Bitboard FileCBB = FileABB << 2;
const Bitboard FileDBB = FileABB << 3;
const Bitboard FileEBB = FileABB << 4;
const Bitboard FileFBB = FileABB << 5;
const Bitboard FileGBB = FileABB << 6;
const Bitboard FileHBB = FileABB << 7;
const Bitboard Rank1BB = 0xFF;
const Bitboard Rank2BB = Rank1BB << (8 * 1);
const Bitboard Rank3BB = Rank1BB << (8 * 2);
const Bitboard Rank4BB = Rank1BB << (8 * 3);
const Bitboard Rank5BB = Rank1BB << (8 * 4);
const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
extern Bitboard RMasks[SQUARE_NB]; extern Bitboard RMasks[SQUARE_NB];
@@ -53,17 +71,18 @@ extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard FileBB[FILE_NB]; extern Bitboard FileBB[FILE_NB];
extern Bitboard RankBB[RANK_NB]; extern Bitboard RankBB[RANK_NB];
extern Bitboard AdjacentFilesBB[FILE_NB]; extern Bitboard AdjacentFilesBB[FILE_NB];
extern Bitboard ThisAndAdjacentFilesBB[FILE_NB];
extern Bitboard InFrontBB[COLOR_NB][RANK_NB]; extern Bitboard InFrontBB[COLOR_NB][RANK_NB];
extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; extern Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard DistanceRingsBB[SQUARE_NB][8]; extern Bitboard DistanceRingsBB[SQUARE_NB][8];
extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; extern Bitboard ForwardBB[COLOR_NB][SQUARE_NB];
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
extern Bitboard AttackSpanMask[COLOR_NB][SQUARE_NB]; extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
const Bitboard BlackSquares = 0xAA55AA55AA55AA55ULL; extern int SquareDistance[SQUARE_NB][SQUARE_NB];
const Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
/// Overloads of bitwise operators between a Bitboard and a Square for testing /// Overloads of bitwise operators between a Bitboard and a Square for testing
/// whether a given bit is set in a bitboard, and for setting and clearing bits. /// whether a given bit is set in a bitboard, and for setting and clearing bits.
@@ -88,13 +107,34 @@ inline Bitboard operator^(Bitboard b, Square s) {
return b ^ SquareBB[s]; return b ^ SquareBB[s];
} }
/// more_than_one() returns true if in 'b' there is more than one bit set
inline bool more_than_one(Bitboard b) { inline bool more_than_one(Bitboard b) {
return b & (b - 1); return b & (b - 1);
} }
inline int square_distance(Square s1, Square s2) {
return SquareDistance[s1][s2];
}
inline int file_distance(Square s1, Square s2) {
return abs(file_of(s1) - file_of(s2));
}
inline int rank_distance(Square s1, Square s2) {
return abs(rank_of(s1) - rank_of(s2));
}
/// shift_bb() moves bitboard one step along direction Delta. Mainly for pawns.
template<Square Delta>
inline Bitboard shift_bb(Bitboard b) {
return Delta == DELTA_N ? b << 8 : Delta == DELTA_S ? b >> 8
: Delta == DELTA_NE ? (b & ~FileHBB) << 9 : Delta == DELTA_SE ? (b & ~FileHBB) >> 7
: Delta == DELTA_NW ? (b & ~FileABB) << 7 : Delta == DELTA_SW ? (b & ~FileABB) >> 9
: 0;
}
/// rank_bb() and file_bb() take a file or a square as input and return /// rank_bb() and file_bb() take a file or a square as input and return
/// a bitboard representing all squares on the given file or rank. /// a bitboard representing all squares on the given file or rank.
@@ -116,7 +156,7 @@ inline Bitboard file_bb(Square s) {
} }
/// adjacent_files_bb takes a file as input and returns a bitboard representing /// adjacent_files_bb() takes a file as input and returns a bitboard representing
/// all squares on the adjacent files. /// all squares on the adjacent files.
inline Bitboard adjacent_files_bb(File f) { inline Bitboard adjacent_files_bb(File f) {
@@ -124,30 +164,17 @@ inline Bitboard adjacent_files_bb(File f) {
} }
/// this_and_adjacent_files_bb takes a file as input and returns a bitboard /// in_front_bb() takes a color and a rank as input, and returns a bitboard
/// representing all squares on the given and adjacent files. /// representing all the squares on all ranks in front of the rank, from the
/// given color's point of view. For instance, in_front_bb(BLACK, RANK_3) will
inline Bitboard this_and_adjacent_files_bb(File f) { /// give all squares on ranks 1 and 2.
return ThisAndAdjacentFilesBB[f];
}
/// in_front_bb() takes a color and a rank or square as input, and returns a
/// bitboard representing all the squares on all ranks in front of the rank
/// (or square), from the given color's point of view. For instance,
/// in_front_bb(WHITE, RANK_5) will give all squares on ranks 6, 7 and 8, while
/// in_front_bb(BLACK, SQ_D3) will give all squares on ranks 1 and 2.
inline Bitboard in_front_bb(Color c, Rank r) { inline Bitboard in_front_bb(Color c, Rank r) {
return InFrontBB[c][r]; return InFrontBB[c][r];
} }
inline Bitboard in_front_bb(Color c, Square s) {
return InFrontBB[c][rank_of(s)];
}
/// between_bb() returns a bitboard representing all squares between two squares.
/// between_bb returns a bitboard representing all squares between two squares.
/// For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with the bits for /// For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with the bits for
/// square d5 and e6 set. If s1 and s2 are not on the same line, file or diagonal, /// square d5 and e6 set. If s1 and s2 are not on the same line, file or diagonal,
/// 0 is returned. /// 0 is returned.
@@ -157,7 +184,7 @@ inline Bitboard between_bb(Square s1, Square s2) {
} }
/// forward_bb takes a color and a square as input, and returns a bitboard /// forward_bb() takes a color and a square as input, and returns a bitboard
/// representing all squares along the line in front of the square, from the /// representing all squares along the line in front of the square, from the
/// point of view of the given color. Definition of the table is: /// point of view of the given color. Definition of the table is:
/// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s) /// ForwardBB[c][s] = in_front_bb(c, s) & file_bb(s)
@@ -167,27 +194,35 @@ inline Bitboard forward_bb(Color c, Square s) {
} }
/// passed_pawn_mask takes a color and a square as input, and returns a /// pawn_attack_span() takes a color and a square as input, and returns a bitboard
/// representing all squares that can be attacked by a pawn of the given color
/// when it moves along its file starting from the given square. Definition is:
/// PawnAttackSpan[c][s] = in_front_bb(c, s) & adjacent_files_bb(s);
inline Bitboard pawn_attack_span(Color c, Square s) {
return PawnAttackSpan[c][s];
}
/// passed_pawn_mask() takes a color and a square as input, and returns a
/// bitboard mask which can be used to test if a pawn of the given color on /// bitboard mask which can be used to test if a pawn of the given color on
/// the given square is a passed pawn. Definition of the table is: /// the given square is a passed pawn. Definition of the table is:
/// PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_adjacent_files_bb(s) /// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_bb(c, s)
inline Bitboard passed_pawn_mask(Color c, Square s) { inline Bitboard passed_pawn_mask(Color c, Square s) {
return PassedPawnMask[c][s]; return PassedPawnMask[c][s];
} }
/// attack_span_mask takes a color and a square as input, and returns a bitboard /// squares_of_color() returns a bitboard representing all squares with the same
/// representing all squares that can be attacked by a pawn of the given color /// color of the given square.
/// when it moves along its file starting from the given square. Definition is:
/// AttackSpanMask[c][s] = in_front_bb(c, s) & adjacent_files_bb(s);
inline Bitboard attack_span_mask(Color c, Square s) { inline Bitboard squares_of_color(Square s) {
return AttackSpanMask[c][s]; return DarkSquares & s ? DarkSquares : ~DarkSquares;
} }
/// squares_aligned returns true if the squares s1, s2 and s3 are aligned /// squares_aligned() returns true if the squares s1, s2 and s3 are aligned
/// either on a straight or on a diagonal line. /// either on a straight or on a diagonal line.
inline bool squares_aligned(Square s1, Square s2, Square s3) { inline bool squares_aligned(Square s1, Square s2, Square s3) {
@@ -196,14 +231,6 @@ inline bool squares_aligned(Square s1, Square s2, Square s3) {
} }
/// same_color_squares() returns a bitboard representing all squares with
/// the same color of the given square.
inline Bitboard same_color_squares(Square s) {
return BlackSquares & s ? BlackSquares : ~BlackSquares;
}
/// Functions for computing sliding attack bitboards. Function attacks_bb() takes /// Functions for computing sliding attack bitboards. Function attacks_bb() takes
/// a square and a bitboard of occupied squares as input, and returns a bitboard /// a square and a bitboard of occupied squares as input, and returns a bitboard
/// representing all squares attacked by Pt (bishop or rook) on the given square. /// representing all squares attacked by Pt (bishop or rook) on the given square.
@@ -231,7 +258,7 @@ inline Bitboard attacks_bb(Square s, Bitboard occ) {
/// lsb()/msb() finds the least/most significant bit in a nonzero bitboard. /// lsb()/msb() finds the least/most significant bit in a nonzero bitboard.
/// pop_lsb() finds and clears the least significant bit in a nonzero bitboard. /// pop_lsb() finds and clears the least significant bit in a nonzero bitboard.
#if defined(USE_BSFQ) #ifdef USE_BSFQ
# if defined(_MSC_VER) && !defined(__INTEL_COMPILER) # if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
@@ -284,7 +311,7 @@ FORCE_INLINE Square pop_lsb(Bitboard* b) {
return s; return s;
} }
#else // if !defined(USE_BSFQ) #else // if defined(USE_BSFQ)
extern Square msb(Bitboard b); extern Square msb(Bitboard b);
extern Square lsb(Bitboard b); extern Square lsb(Bitboard b);
@@ -292,4 +319,4 @@ extern Square pop_lsb(Bitboard* b);
#endif #endif
#endif // !defined(BITBOARD_H_INCLUDED) #endif // #ifndef BITBOARD_H_INCLUDED
+3 -3
View File
@@ -18,7 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(BITCOUNT_H_INCLUDED) #ifndef BITCOUNT_H_INCLUDED
#define BITCOUNT_H_INCLUDED #define BITCOUNT_H_INCLUDED
#include <cassert> #include <cassert>
@@ -81,7 +81,7 @@ inline int popcount<CNT_32_MAX15>(Bitboard b) {
template<> template<>
inline int popcount<CNT_HW_POPCNT>(Bitboard b) { inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#if !defined(USE_POPCNT) #ifndef USE_POPCNT
assert(false); assert(false);
return b != 0; // Avoid 'b not used' warning return b != 0; // Avoid 'b not used' warning
@@ -102,4 +102,4 @@ inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#endif #endif
} }
#endif // !defined(BITCOUNT_H_INCLUDED) #endif // #ifndef BITCOUNT_H_INCLUDED
+3 -3
View File
@@ -436,9 +436,9 @@ Move PolyglotBook::probe(const Position& pos, const string& fName, bool pickBest
move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1)); move = make<PROMOTION>(from_sq(move), to_sq(move), PieceType(pt + 1));
// Add 'special move' flags and verify it is legal // Add 'special move' flags and verify it is legal
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml) for (MoveList<LEGAL> it(pos); *it; ++it)
if (move == (ml.move() ^ type_of(ml.move()))) if (move == (*it ^ type_of(*it)))
return ml.move(); return *it;
return MOVE_NONE; return MOVE_NONE;
} }
+2 -2
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(BOOK_H_INCLUDED) #ifndef BOOK_H_INCLUDED
#define BOOK_H_INCLUDED #define BOOK_H_INCLUDED
#include <fstream> #include <fstream>
@@ -42,4 +42,4 @@ private:
std::string fileName; std::string fileName;
}; };
#endif // !defined(BOOK_H_INCLUDED) #endif // #ifndef BOOK_H_INCLUDED
+100 -104
View File
@@ -89,7 +89,10 @@ namespace {
Endgames::Endgames() { Endgames::Endgames() {
add<KK>("KK");
add<KPK>("KPK"); add<KPK>("KPK");
add<KBK>("KBK");
add<KNK>("KNK");
add<KNNK>("KNNK"); add<KNNK>("KNNK");
add<KBNK>("KBNK"); add<KBNK>("KBNK");
add<KRKP>("KRKP"); add<KRKP>("KRKP");
@@ -130,28 +133,25 @@ template<>
Value Endgame<KXK>::operator()(const Position& pos) const { Value Endgame<KXK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO); assert(!pos.count<PAWN>(weakerSide));
assert(!pos.checkers()); // Eval is never called when in check
// Stalemate detection with lone king // Stalemate detection with lone king
if ( pos.side_to_move() == weakerSide if (pos.side_to_move() == weakerSide && !MoveList<LEGAL>(pos).size())
&& !pos.checkers()
&& !MoveList<LEGAL>(pos).size()) {
return VALUE_DRAW; return VALUE_DRAW;
}
Square winnerKSq = pos.king_square(strongerSide); Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
Value result = pos.non_pawn_material(strongerSide) Value result = pos.non_pawn_material(strongerSide)
+ pos.piece_count(strongerSide, PAWN) * PawnValueEg + pos.count<PAWN>(strongerSide) * PawnValueEg
+ MateTable[loserKSq] + MateTable[loserKSq]
+ DistanceBonus[square_distance(winnerKSq, loserKSq)]; + DistanceBonus[square_distance(winnerKSq, loserKSq)];
if ( pos.piece_count(strongerSide, QUEEN) if ( pos.count<QUEEN>(strongerSide)
|| pos.piece_count(strongerSide, ROOK) || pos.count<ROOK>(strongerSide)
|| pos.bishop_pair(strongerSide)) { || pos.bishop_pair(strongerSide))
result += VALUE_KNOWN_WIN; result += VALUE_KNOWN_WIN;
}
return strongerSide == pos.side_to_move() ? result : -result; return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -162,16 +162,16 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
template<> template<>
Value Endgame<KBNK>::operator()(const Position& pos) const { Value Endgame<KBNK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg); assert(pos.non_pawn_material(strongerSide) == KnightValueMg + BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, KNIGHT) == 1); assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.count<KNIGHT>(strongerSide) == 1);
assert(pos.count< PAWN>(strongerSide) == 0);
assert(pos.count< PAWN>(weakerSide ) == 0);
Square winnerKSq = pos.king_square(strongerSide); Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0]; Square bishopSq = pos.list<BISHOP>(strongerSide)[0];
// kbnk_mate_table() tries to drive toward corners A1 or H8, // kbnk_mate_table() tries to drive toward corners A1 or H8,
// if we have a bishop that cannot reach the above squares we // if we have a bishop that cannot reach the above squares we
@@ -196,8 +196,8 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.count<PAWN>(strongerSide) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<PAWN>(weakerSide ) == 0);
Square wksq, bksq, wpsq; Square wksq, bksq, wpsq;
Color us; Color us;
@@ -206,14 +206,14 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
{ {
wksq = pos.king_square(WHITE); wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK); bksq = pos.king_square(BLACK);
wpsq = pos.piece_list(WHITE, PAWN)[0]; wpsq = pos.list<PAWN>(WHITE)[0];
us = pos.side_to_move(); us = pos.side_to_move();
} }
else else
{ {
wksq = ~pos.king_square(BLACK); wksq = ~pos.king_square(BLACK);
bksq = ~pos.king_square(WHITE); bksq = ~pos.king_square(WHITE);
wpsq = ~pos.piece_list(BLACK, PAWN)[0]; wpsq = ~pos.list<PAWN>(BLACK)[0];
us = ~pos.side_to_move(); us = ~pos.side_to_move();
} }
@@ -241,17 +241,17 @@ template<>
Value Endgame<KRKP>::operator()(const Position& pos) const { Value Endgame<KRKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide) == 0); assert(pos.non_pawn_material(weakerSide) == 0);
assert(pos.piece_count(weakerSide, PAWN) == 1); assert(pos.count<PAWN>(strongerSide) == 0);
assert(pos.count<PAWN>(weakerSide ) == 1);
Square wksq, wrsq, bksq, bpsq; Square wksq, wrsq, bksq, bpsq;
int tempo = (pos.side_to_move() == strongerSide); int tempo = (pos.side_to_move() == strongerSide);
wksq = pos.king_square(strongerSide); wksq = pos.king_square(strongerSide);
wrsq = pos.piece_list(strongerSide, ROOK)[0];
bksq = pos.king_square(weakerSide); bksq = pos.king_square(weakerSide);
bpsq = pos.piece_list(weakerSide, PAWN)[0]; wrsq = pos.list<ROOK>(strongerSide)[0];
bpsq = pos.list<PAWN>(weakerSide)[0];
if (strongerSide == BLACK) if (strongerSide == BLACK)
{ {
@@ -298,10 +298,10 @@ template<>
Value Endgame<KRKB>::operator()(const Position& pos) const { Value Endgame<KRKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); assert(pos.non_pawn_material(weakerSide ) == BishopValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<BISHOP>(weakerSide ) == 1);
assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.count< PAWN>(weakerSide ) == 0);
assert(pos.count< PAWN>(strongerSide) == 0);
Value result = Value(MateTable[pos.king_square(weakerSide)]); Value result = Value(MateTable[pos.king_square(weakerSide)]);
return strongerSide == pos.side_to_move() ? result : -result; return strongerSide == pos.side_to_move() ? result : -result;
@@ -314,15 +314,15 @@ template<>
Value Endgame<KRKN>::operator()(const Position& pos) const { Value Endgame<KRKN>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); assert(pos.non_pawn_material(weakerSide ) == KnightValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<KNIGHT>(weakerSide ) == 1);
assert(pos.piece_count(weakerSide, KNIGHT) == 1); assert(pos.count< PAWN>(weakerSide ) == 0);
assert(pos.count< PAWN>(strongerSide) == 0);
const int penalty[8] = { 0, 10, 14, 20, 30, 42, 58, 80 }; const int penalty[8] = { 0, 10, 14, 20, 30, 42, 58, 80 };
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
Square bnsq = pos.piece_list(weakerSide, KNIGHT)[0]; Square bnsq = pos.list<KNIGHT>(weakerSide)[0];
Value result = Value(MateTable[bksq] + penalty[square_distance(bksq, bnsq)]); Value result = Value(MateTable[bksq] + penalty[square_distance(bksq, bnsq)]);
return strongerSide == pos.side_to_move() ? result : -result; return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -335,13 +335,13 @@ template<>
Value Endgame<KQKP>::operator()(const Position& pos) const { Value Endgame<KQKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMg); assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == 0); assert(pos.count<PAWN>(strongerSide) == 0);
assert(pos.piece_count(weakerSide, PAWN) == 1); assert(pos.count<PAWN>(weakerSide ) == 1);
Square winnerKSq = pos.king_square(strongerSide); Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
Square pawnSq = pos.piece_list(weakerSide, PAWN)[0]; Square pawnSq = pos.list<PAWN>(weakerSide)[0];
Value result = QueenValueEg Value result = QueenValueEg
- PawnValueEg - PawnValueEg
@@ -368,9 +368,9 @@ template<>
Value Endgame<KQKR>::operator()(const Position& pos) const { Value Endgame<KQKR>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMg); assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 0);
assert(pos.non_pawn_material(weakerSide ) == RookValueMg); assert(pos.non_pawn_material(weakerSide ) == RookValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<PAWN>(strongerSide) == 0);
assert(pos.count<PAWN>(weakerSide ) == 0);
Square winnerKSq = pos.king_square(strongerSide); Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
@@ -386,16 +386,16 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
template<> template<>
Value Endgame<KBBKN>::operator()(const Position& pos) const { Value Endgame<KBBKN>::operator()(const Position& pos) const {
assert(pos.piece_count(strongerSide, BISHOP) == 2);
assert(pos.non_pawn_material(strongerSide) == 2 * BishopValueMg); assert(pos.non_pawn_material(strongerSide) == 2 * BishopValueMg);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); assert(pos.non_pawn_material(weakerSide ) == KnightValueMg);
assert(pos.count<BISHOP>(strongerSide) == 2);
assert(pos.count<KNIGHT>(weakerSide ) == 1);
assert(!pos.pieces(PAWN)); assert(!pos.pieces(PAWN));
Value result = BishopValueEg; Value result = BishopValueEg;
Square wksq = pos.king_square(strongerSide); Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
Square nsq = pos.piece_list(weakerSide, KNIGHT)[0]; Square nsq = pos.list<KNIGHT>(weakerSide)[0];
// Bonus for attacking king close to defending king // Bonus for attacking king close to defending king
result += Value(DistanceBonus[square_distance(wksq, bksq)]); result += Value(DistanceBonus[square_distance(wksq, bksq)]);
@@ -410,17 +410,13 @@ Value Endgame<KBBKN>::operator()(const Position& pos) const {
} }
/// K and two minors vs K and one or two minors or K and two knights against /// Some cases of trivial draws
/// king alone are always draw. template<> Value Endgame<KK>::operator()(const Position&) const { return VALUE_DRAW; }
template<> template<> Value Endgame<KBK>::operator()(const Position&) const { return VALUE_DRAW; }
Value Endgame<KmmKm>::operator()(const Position&) const { template<> Value Endgame<KNK>::operator()(const Position&) const { return VALUE_DRAW; }
return VALUE_DRAW; template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; }
} template<> Value Endgame<KmmKm>::operator()(const Position&) const { return VALUE_DRAW; }
template<>
Value Endgame<KNNK>::operator()(const Position&) const {
return VALUE_DRAW;
}
/// K, bishop and one or more pawns vs K. It checks for draws with rook pawns and /// K, bishop and one or more pawns vs K. It checks for draws with rook pawns and
/// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW /// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW
@@ -430,20 +426,20 @@ template<>
ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const { ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.piece_count(strongerSide, PAWN) >= 1); assert(pos.count< PAWN>(strongerSide) >= 1);
// No assertions about the material of weakerSide, because we want draws to // No assertions about the material of weakerSide, because we want draws to
// be detected even when the weaker side has some pawns. // be detected even when the weaker side has some pawns.
Bitboard pawns = pos.pieces(strongerSide, PAWN); Bitboard pawns = pos.pieces(strongerSide, PAWN);
File pawnFile = file_of(pos.piece_list(strongerSide, PAWN)[0]); File pawnFile = file_of(pos.list<PAWN>(strongerSide)[0]);
// All pawns are on a single rook file ? // All pawns are on a single rook file ?
if ( (pawnFile == FILE_A || pawnFile == FILE_H) if ( (pawnFile == FILE_A || pawnFile == FILE_H)
&& !(pawns & ~file_bb(pawnFile))) && !(pawns & ~file_bb(pawnFile)))
{ {
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0]; Square bishopSq = pos.list<BISHOP>(strongerSide)[0];
Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8); Square queeningSq = relative_square(strongerSide, pawnFile | RANK_8);
Square kingSq = pos.king_square(weakerSide); Square kingSq = pos.king_square(weakerSide);
@@ -477,7 +473,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
if ( (pawnFile == FILE_B || pawnFile == FILE_G) if ( (pawnFile == FILE_B || pawnFile == FILE_G)
&& !(pos.pieces(PAWN) & ~file_bb(pawnFile)) && !(pos.pieces(PAWN) & ~file_bb(pawnFile))
&& pos.non_pawn_material(weakerSide) == 0 && pos.non_pawn_material(weakerSide) == 0
&& pos.piece_count(weakerSide, PAWN) >= 1) && pos.count<PAWN>(weakerSide) >= 1)
{ {
// Get weaker pawn closest to opponent's queening square // Get weaker pawn closest to opponent's queening square
Bitboard wkPawns = pos.pieces(weakerSide, PAWN); Bitboard wkPawns = pos.pieces(weakerSide, PAWN);
@@ -485,7 +481,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
Square strongerKingSq = pos.king_square(strongerSide); Square strongerKingSq = pos.king_square(strongerSide);
Square weakerKingSq = pos.king_square(weakerSide); Square weakerKingSq = pos.king_square(weakerSide);
Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0]; Square bishopSq = pos.list<BISHOP>(strongerSide)[0];
// Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and // Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and
// weaker king can stop opposing opponent's king from penetrating. // weaker king can stop opposing opponent's king from penetrating.
@@ -505,10 +501,10 @@ template<>
ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const { ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMg); assert(pos.non_pawn_material(strongerSide) == QueenValueMg);
assert(pos.piece_count(strongerSide, QUEEN) == 1); assert(pos.count<QUEEN>(strongerSide) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.count< PAWN>(strongerSide) == 0);
assert(pos.piece_count(weakerSide, ROOK) == 1); assert(pos.count< ROOK>(weakerSide ) == 1);
assert(pos.piece_count(weakerSide, PAWN) >= 1); assert(pos.count< PAWN>(weakerSide ) >= 1);
Square kingSq = pos.king_square(weakerSide); Square kingSq = pos.king_square(weakerSide);
if ( relative_rank(weakerSide, kingSq) <= RANK_2 if ( relative_rank(weakerSide, kingSq) <= RANK_2
@@ -517,7 +513,7 @@ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
&& (pos.pieces(weakerSide, PAWN) & rank_bb(relative_rank(weakerSide, RANK_2))) && (pos.pieces(weakerSide, PAWN) & rank_bb(relative_rank(weakerSide, RANK_2)))
&& (pos.attacks_from<KING>(kingSq) & pos.pieces(weakerSide, PAWN))) && (pos.attacks_from<KING>(kingSq) & pos.pieces(weakerSide, PAWN)))
{ {
Square rsq = pos.piece_list(weakerSide, ROOK)[0]; Square rsq = pos.list<ROOK>(weakerSide)[0];
if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(weakerSide, PAWN)) if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(weakerSide, PAWN))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
} }
@@ -535,15 +531,15 @@ template<>
ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const { ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide) == RookValueMg); assert(pos.non_pawn_material(weakerSide) == RookValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<PAWN>(strongerSide) == 1);
assert(pos.count<PAWN>(weakerSide ) == 0);
Square wksq = pos.king_square(strongerSide); Square wksq = pos.king_square(strongerSide);
Square wrsq = pos.piece_list(strongerSide, ROOK)[0];
Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
Square brsq = pos.piece_list(weakerSide, ROOK)[0]; Square wrsq = pos.list<ROOK>(strongerSide)[0];
Square wpsq = pos.list<PAWN>(strongerSide)[0];
Square brsq = pos.list<ROOK>(weakerSide)[0];
// Orient the board in such a way that the stronger side is white, and the // Orient the board in such a way that the stronger side is white, and the
// pawn is on the left half of the board. // pawn is on the left half of the board.
@@ -653,12 +649,12 @@ template<>
ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const { ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMg); assert(pos.non_pawn_material(strongerSide) == RookValueMg);
assert(pos.piece_count(strongerSide, PAWN) == 2);
assert(pos.non_pawn_material(weakerSide) == RookValueMg); assert(pos.non_pawn_material(weakerSide) == RookValueMg);
assert(pos.piece_count(weakerSide, PAWN) == 1); assert(pos.count<PAWN>(strongerSide) == 2);
assert(pos.count<PAWN>(weakerSide ) == 1);
Square wpsq1 = pos.piece_list(strongerSide, PAWN)[0]; Square wpsq1 = pos.list<PAWN>(strongerSide)[0];
Square wpsq2 = pos.piece_list(strongerSide, PAWN)[1]; Square wpsq2 = pos.list<PAWN>(strongerSide)[1];
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
// Does the stronger side have a passed pawn? // Does the stronger side have a passed pawn?
@@ -691,9 +687,9 @@ template<>
ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const { ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) >= 2);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<PAWN>(strongerSide) >= 2);
assert(pos.count<PAWN>(weakerSide ) == 0);
Square ksq = pos.king_square(weakerSide); Square ksq = pos.king_square(weakerSide);
Bitboard pawns = pos.pieces(strongerSide, PAWN); Bitboard pawns = pos.pieces(strongerSide, PAWN);
@@ -704,7 +700,7 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
// Does the defending king block the pawns? // Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1 if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1
|| ( file_of(ksq) == FILE_A || ( file_of(ksq) == FILE_A
&& !(in_front_bb(strongerSide, ksq) & pawns))) && !(in_front_bb(strongerSide, rank_of(ksq)) & pawns)))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
} }
// Are all pawns on the 'h' file? // Are all pawns on the 'h' file?
@@ -713,7 +709,7 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
// Does the defending king block the pawns? // Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1 if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1
|| ( file_of(ksq) == FILE_H || ( file_of(ksq) == FILE_H
&& !(in_front_bb(strongerSide, ksq) & pawns))) && !(in_front_bb(strongerSide, rank_of(ksq)) & pawns)))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -728,15 +724,15 @@ template<>
ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const { ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); assert(pos.non_pawn_material(weakerSide ) == BishopValueMg);
assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<BISHOP>(weakerSide ) == 1);
assert(pos.count< PAWN>(strongerSide) == 1);
assert(pos.count< PAWN>(weakerSide ) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0]; Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[0]; Square strongerBishopSq = pos.list<BISHOP>(strongerSide)[0];
Square weakerBishopSq = pos.piece_list(weakerSide, BISHOP)[0]; Square weakerBishopSq = pos.list<BISHOP>(weakerSide)[0];
Square weakerKingSq = pos.king_square(weakerSide); Square weakerKingSq = pos.king_square(weakerSide);
// Case 1: Defending king blocks the pawn, and cannot be driven away // Case 1: Defending king blocks the pawn, and cannot be driven away
@@ -783,21 +779,21 @@ template<>
ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const { ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 2);
assert(pos.non_pawn_material(weakerSide ) == BishopValueMg); assert(pos.non_pawn_material(weakerSide ) == BishopValueMg);
assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<BISHOP>(weakerSide ) == 1);
assert(pos.count< PAWN>(strongerSide) == 2);
assert(pos.count< PAWN>(weakerSide ) == 0);
Square wbsq = pos.piece_list(strongerSide, BISHOP)[0]; Square wbsq = pos.list<BISHOP>(strongerSide)[0];
Square bbsq = pos.piece_list(weakerSide, BISHOP)[0]; Square bbsq = pos.list<BISHOP>(weakerSide)[0];
if (!opposite_colors(wbsq, bbsq)) if (!opposite_colors(wbsq, bbsq))
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
Square ksq = pos.king_square(weakerSide); Square ksq = pos.king_square(weakerSide);
Square psq1 = pos.piece_list(strongerSide, PAWN)[0]; Square psq1 = pos.list<PAWN>(strongerSide)[0];
Square psq2 = pos.piece_list(strongerSide, PAWN)[1]; Square psq2 = pos.list<PAWN>(strongerSide)[1];
Rank r1 = rank_of(psq1); Rank r1 = rank_of(psq1);
Rank r2 = rank_of(psq2); Rank r2 = rank_of(psq2);
Square blockSq1, blockSq2; Square blockSq1, blockSq2;
@@ -858,14 +854,14 @@ template<>
ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const { ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMg); assert(pos.non_pawn_material(strongerSide) == BishopValueMg);
assert(pos.piece_count(strongerSide, BISHOP) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide ) == KnightValueMg); assert(pos.non_pawn_material(weakerSide ) == KnightValueMg);
assert(pos.piece_count(weakerSide, KNIGHT) == 1); assert(pos.count<BISHOP>(strongerSide) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<KNIGHT>(weakerSide ) == 1);
assert(pos.count< PAWN>(strongerSide) == 1);
assert(pos.count< PAWN>(weakerSide ) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0]; Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[0]; Square strongerBishopSq = pos.list<BISHOP>(strongerSide)[0];
Square weakerKingSq = pos.king_square(weakerSide); Square weakerKingSq = pos.king_square(weakerSide);
if ( file_of(weakerKingSq) == file_of(pawnSq) if ( file_of(weakerKingSq) == file_of(pawnSq)
@@ -885,12 +881,12 @@ template<>
ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const { ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == KnightValueMg); assert(pos.non_pawn_material(strongerSide) == KnightValueMg);
assert(pos.piece_count(strongerSide, KNIGHT) == 1);
assert(pos.piece_count(strongerSide, PAWN) == 1);
assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.count<KNIGHT>(strongerSide) == 1);
assert(pos.count< PAWN>(strongerSide) == 1);
assert(pos.count< PAWN>(weakerSide ) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0]; Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square weakerKingSq = pos.king_square(weakerSide); Square weakerKingSq = pos.king_square(weakerSide);
if ( pawnSq == relative_square(strongerSide, SQ_A7) if ( pawnSq == relative_square(strongerSide, SQ_A7)
@@ -910,8 +906,8 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
template<> template<>
ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const { ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0]; Square pawnSq = pos.list<PAWN>(strongerSide)[0];
Square bishopSq = pos.piece_list(weakerSide, BISHOP)[0]; Square bishopSq = pos.list<BISHOP>(weakerSide)[0];
Square weakerKingSq = pos.king_square(weakerSide); Square weakerKingSq = pos.king_square(weakerSide);
// King needs to get close to promoting pawn to prevent knight from blocking. // King needs to get close to promoting pawn to prevent knight from blocking.
@@ -933,12 +929,12 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide ) == VALUE_ZERO);
assert(pos.piece_count(WHITE, PAWN) == 1); assert(pos.count<PAWN>(WHITE) == 1);
assert(pos.piece_count(BLACK, PAWN) == 1); assert(pos.count<PAWN>(BLACK) == 1);
Square wksq = pos.king_square(strongerSide); Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
Square wpsq = pos.piece_list(strongerSide, PAWN)[0]; Square wpsq = pos.list<PAWN>(strongerSide)[0];
Color us = pos.side_to_move(); Color us = pos.side_to_move();
if (strongerSide == BLACK) if (strongerSide == BLACK)
+6 -3
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(ENDGAME_H_INCLUDED) #ifndef ENDGAME_H_INCLUDED
#define ENDGAME_H_INCLUDED #define ENDGAME_H_INCLUDED
#include <map> #include <map>
@@ -33,6 +33,10 @@ enum EndgameType {
// Evaluation functions // Evaluation functions
KK, // K vs K
KBK, // KB vs K
KNK, // KN vs K
KNNK, // KNN vs K
KXK, // Generic "mate lone king" eval KXK, // Generic "mate lone king" eval
KBNK, // KBN vs K KBNK, // KBN vs K
KPK, // KP vs K KPK, // KP vs K
@@ -42,7 +46,6 @@ enum EndgameType {
KQKP, // KQ vs KP KQKP, // KQ vs KP
KQKR, // KQ vs KR KQKR, // KQ vs KR
KBBKN, // KBB vs KN KBBKN, // KBB vs KN
KNNK, // KNN vs K
KmmKm, // K and two minors vs K and one or two minors KmmKm, // K and two minors vs K and one or two minors
@@ -119,4 +122,4 @@ public:
{ return eg = map(eg).count(key) ? map(eg)[key] : NULL; } { return eg = map(eg).count(key) ? map(eg)[key] : NULL; }
}; };
#endif // !defined(ENDGAME_H_INCLUDED) #endif // #ifndef ENDGAME_H_INCLUDED
+275 -307
View File
@@ -31,6 +31,20 @@
namespace { namespace {
enum ExtendedPieceType { // Used for tracing
PST = 8, IMBALANCE, MOBILITY, THREAT, PASSED, UNSTOPPABLE, SPACE, TOTAL
};
namespace Tracing {
Score scores[COLOR_NB][TOTAL + 1];
std::stringstream stream;
void add(int idx, Score term_w, Score term_b = SCORE_ZERO);
void row(const char* name, int idx);
std::string do_trace(const Position& pos);
}
// Struct EvalInfo contains various information computed and collected // Struct EvalInfo contains various information computed and collected
// by the evaluation functions. // by the evaluation functions.
struct EvalInfo { struct EvalInfo {
@@ -72,10 +86,10 @@ namespace {
}; };
// Evaluation grain size, must be a power of 2 // Evaluation grain size, must be a power of 2
const int GrainSize = 8; const int GrainSize = 4;
// Evaluation weights, initialized from UCI options // Evaluation weights, initialized from UCI options
enum { Mobility, PassedPawns, Space, KingDangerUs, KingDangerThem }; enum { Mobility, PawnStructure, PassedPawns, Space, KingDangerUs, KingDangerThem };
Score Weights[6]; Score Weights[6];
typedef Value V; typedef Value V;
@@ -88,33 +102,33 @@ namespace {
// //
// Values modified by Joona Kiiski // Values modified by Joona Kiiski
const Score WeightsInternal[] = { const Score WeightsInternal[] = {
S(289, 344), S(221, 273), S(46, 0), S(271, 0), S(307, 0) S(289, 344), S(233, 201), S(221, 273), S(46, 0), S(271, 0), S(307, 0)
}; };
// MobilityBonus[PieceType][attacked] contains mobility bonuses for middle and // MobilityBonus[PieceType][attacked] contains bonuses for middle and end
// end game, indexed by piece type and number of attacked squares not occupied // game, indexed by piece type and number of attacked squares not occupied by
// by friendly pieces. // friendly pieces.
const Score MobilityBonus[][32] = { const Score MobilityBonus[][32] = {
{}, {}, {}, {},
{ S(-38,-33), S(-25,-23), S(-12,-13), S( 0, -3), S(12, 7), S(25, 17), // Knights { S(-35,-30), S(-22,-20), S(-9,-10), S( 3, 0), S(15, 10), S(27, 20), // Knights
S( 31, 22), S( 38, 27), S( 38, 27) }, S( 37, 28), S( 42, 31), S(44, 33) },
{ S(-25,-30), S(-11,-16), S( 3, -2), S(17, 12), S(31, 26), S(45, 40), // Bishops { S(-22,-27), S( -8,-13), S( 6, 1), S(20, 15), S(34, 29), S(48, 43), // Bishops
S( 57, 52), S( 65, 60), S( 71, 65), S(74, 69), S(76, 71), S(78, 73), S( 60, 55), S( 68, 63), S(74, 68), S(77, 72), S(80, 75), S(82, 77),
S( 79, 74), S( 80, 75), S( 81, 76), S(81, 76) }, S( 84, 79), S( 86, 81), S(87, 82), S(87, 82) },
{ S(-20,-36), S(-14,-19), S( -8, -3), S(-2, 13), S( 4, 29), S(10, 46), // Rooks { S(-17,-33), S(-11,-16), S(-5, 0), S( 1, 16), S( 7, 32), S(13, 48), // Rooks
S( 14, 62), S( 19, 79), S( 23, 95), S(26,106), S(27,111), S(28,114), S( 18, 64), S( 22, 80), S(26, 96), S(29,109), S(31,115), S(33,119),
S( 29,116), S( 30,117), S( 31,118), S(32,118) }, S( 35,122), S( 36,123), S(37,124), S(38,124) },
{ S(-10,-18), S( -8,-13), S( -6, -7), S(-3, -2), S(-1, 3), S( 1, 8), // Queens { S(-12,-20), S( -8,-13), S(-5, -7), S(-2, -1), S( 1, 5), S( 4, 11), // Queens
S( 3, 13), S( 5, 19), S( 8, 23), S(10, 27), S(12, 32), S(15, 34), S( 7, 17), S( 10, 23), S(13, 29), S(16, 34), S(18, 38), S(20, 40),
S( 16, 35), S( 17, 35), S( 18, 35), S(20, 35), S(20, 35), S(20, 35), S( 22, 41), S( 23, 41), S(24, 41), S(25, 41), S(25, 41), S(25, 41),
S( 20, 35), S( 20, 35), S( 20, 35), S(20, 35), S(20, 35), S(20, 35), S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41),
S( 20, 35), S( 20, 35), S( 20, 35), S(20, 35), S(20, 35), S(20, 35), S( 25, 41), S( 25, 41), S(25, 41), S(25, 41), S(25, 41), S(25, 41),
S( 20, 35), S( 20, 35) } S( 25, 41), S( 25, 41) }
}; };
// OutpostBonus[PieceType][Square] contains outpost bonuses of knights and // Outpost[PieceType][Square] contains bonuses of knights and bishops, indexed
// bishops, indexed by piece type and square (from white's point of view). // by piece type and square (from white's point of view).
const Value OutpostBonus[][SQUARE_NB] = { const Value Outpost[][SQUARE_NB] = {
{ {
// A B C D E F G H // A B C D E F G H
V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Knights V(0), V(0), V(0), V(0), V(0), V(0), V(0), V(0), // Knights
@@ -132,9 +146,9 @@ namespace {
V(0), V(5), V(8), V(8), V(8), V(8), V(5), V(0) } V(0), V(5), V(8), V(8), V(8), V(8), V(5), V(0) }
}; };
// ThreatBonus[attacking][attacked] contains threat bonuses according to // Threat[attacking][attacked] contains bonuses according to which piece
// which piece type attacks which one. // type attacks which one.
const Score ThreatBonus[][PIECE_TYPE_NB] = { const Score Threat[][PIECE_TYPE_NB] = {
{}, {}, {}, {},
{ S(0, 0), S( 7, 39), S( 0, 0), S(24, 49), S(41,100), S(41,100) }, // KNIGHT { S(0, 0), S( 7, 39), S( 0, 0), S(24, 49), S(41,100), S(41,100) }, // KNIGHT
{ S(0, 0), S( 7, 39), S(24, 49), S( 0, 0), S(41,100), S(41,100) }, // BISHOP { S(0, 0), S( 7, 39), S(24, 49), S( 0, 0), S(41,100), S(41,100) }, // BISHOP
@@ -142,45 +156,31 @@ namespace {
{ S(0, 0), S(15, 39), S(15, 39), S(15, 39), S(15, 39), S( 0, 0) } // QUEEN { S(0, 0), S(15, 39), S(15, 39), S(15, 39), S(15, 39), S( 0, 0) } // QUEEN
}; };
// ThreatenedByPawnPenalty[PieceType] contains a penalty according to which // ThreatenedByPawn[PieceType] contains a penalty according to which piece
// piece type is attacked by an enemy pawn. // type is attacked by an enemy pawn.
const Score ThreatenedByPawnPenalty[] = { const Score ThreatenedByPawn[] = {
S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118) S(0, 0), S(0, 0), S(56, 70), S(56, 70), S(76, 99), S(86, 118)
}; };
#undef S #undef S
const Score BishopPinBonus = make_score(66, 11);
// Bonus for having the side to move (modified by Joona Kiiski)
const Score Tempo = make_score(24, 11); const Score Tempo = make_score(24, 11);
const Score BishopPin = make_score(66, 11);
// Rooks and queens on the 7th rank const Score RookOn7th = make_score(11, 20);
const Score RookOn7thBonus = make_score(11, 20); const Score QueenOn7th = make_score( 3, 8);
const Score QueenOn7thBonus = make_score( 3, 8); const Score RookOnPawn = make_score(10, 28);
const Score QueenOnPawn = make_score( 4, 20);
// Rooks and queens attacking pawns on the same rank const Score RookOpenFile = make_score(43, 21);
const Score RookOnPawnBonus = make_score(10, 28); const Score RookSemiopenFile = make_score(19, 10);
const Score QueenOnPawnBonus = make_score( 4, 20); const Score BishopPawns = make_score( 8, 12);
const Score MinorBehindPawn = make_score(16, 0);
// Rooks on open files (modified by Joona Kiiski) const Score UndefendedMinor = make_score(25, 10);
const Score RookOpenFileBonus = make_score(43, 21); const Score TrappedRook = make_score(90, 0);
const Score RookHalfOpenFileBonus = make_score(19, 10);
// Penalty for rooks trapped inside a friendly king which has lost the
// right to castle.
const Value TrappedRookPenalty = Value(180);
// Penalty for bishop with pawns on the same coloured squares
const Score BishopPawnsPenalty = make_score(8, 12);
// Penalty for a bishop on a1/h1 (a8/h8 for black) which is trapped by // 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 // a friendly pawn on b2/g2 (b7/g7 for black). This can obviously only
// happen in Chess960 games. // happen in Chess960 games.
const Score TrappedBishopA1H1Penalty = make_score(100, 100); const Score TrappedBishopA1H1 = make_score(50, 50);
// Penalty for an undefended bishop or knight
const Score UndefendedMinorPenalty = make_score(25, 10);
// The SpaceMask[Color] contains the area of the board which is considered // The SpaceMask[Color] contains the area of the board which is considered
// by the space evaluation. In the middle game, each side is given a bonus // by the space evaluation. In the middle game, each side is given a bonus
@@ -196,24 +196,24 @@ namespace {
}; };
// King danger constants and variables. The king danger scores are taken // King danger constants and variables. The king danger scores are taken
// from the KingDangerTable[]. Various little "meta-bonuses" measuring // from the KingDanger[]. Various little "meta-bonuses" measuring
// the strength of the enemy attack are added up into an integer, which // the strength of the enemy attack are added up into an integer, which
// is used as an index to KingDangerTable[]. // is used as an index to KingDanger[].
// //
// KingAttackWeights[PieceType] contains king attack weights by piece type // KingAttackWeights[PieceType] contains king attack weights by piece type
const int KingAttackWeights[] = { 0, 0, 2, 2, 3, 5 }; const int KingAttackWeights[] = { 0, 0, 2, 2, 3, 5 };
// Bonuses for enemy's safe checks // Bonuses for enemy's safe checks
const int QueenContactCheckBonus = 6; const int QueenContactCheck = 6;
const int RookContactCheckBonus = 4; const int RookContactCheck = 4;
const int QueenCheckBonus = 3; const int QueenCheck = 3;
const int RookCheckBonus = 2; const int RookCheck = 2;
const int BishopCheckBonus = 1; const int BishopCheck = 1;
const int KnightCheckBonus = 1; const int KnightCheck = 1;
// InitKingDanger[Square] contains penalties based on the position of the // KingExposed[Square] contains penalties based on the position of the
// defending king, indexed by king's square (from white's point of view). // defending king, indexed by king's square (from white's point of view).
const int InitKingDanger[] = { const int KingExposed[] = {
2, 0, 2, 5, 5, 2, 0, 2, 2, 0, 2, 5, 5, 2, 0, 2,
2, 2, 4, 8, 8, 4, 2, 2, 2, 2, 4, 8, 8, 4, 2, 2,
7, 10, 12, 12, 12, 12, 10, 7, 7, 10, 12, 12, 12, 12, 10, 7,
@@ -224,19 +224,9 @@ namespace {
15, 15, 15, 15, 15, 15, 15, 15 15, 15, 15, 15, 15, 15, 15, 15
}; };
// KingDangerTable[Color][attackUnits] contains the actual king danger // KingDanger[Color][attackUnits] contains the actual king danger weighted
// weighted scores, indexed by color and by a calculated integer number. // scores, indexed by color and by a calculated integer number.
Score KingDangerTable[COLOR_NB][128]; Score KingDanger[COLOR_NB][128];
// TracedTerms[Color][PieceType || TracedType] contains a breakdown of the
// evaluation terms, used when tracing.
Score TracedScores[COLOR_NB][16];
std::stringstream TraceStream;
enum TracedType {
PST = 8, IMBALANCE = 9, MOBILITY = 10, THREAT = 11,
PASSED = 12, UNSTOPPABLE = 13, SPACE = 14, TOTAL = 15
};
// Function prototypes // Function prototypes
template<bool Trace> template<bool Trace>
@@ -249,24 +239,23 @@ namespace {
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility); Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility);
template<Color Us, bool Trace> template<Color Us, bool Trace>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]); Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]);
template<Color Us, bool Trace>
Score evaluate_threats(const Position& pos, const EvalInfo& ei);
template<Color Us, bool Trace>
Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei);
template<Color Us> template<Color Us>
Score evaluate_threats(const Position& pos, EvalInfo& ei); int evaluate_space(const Position& pos, const EvalInfo& ei);
template<Color Us> Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei);
int evaluate_space(const Position& pos, EvalInfo& ei);
template<Color Us>
Score evaluate_passed_pawns(const Position& pos, EvalInfo& ei);
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei);
Value interpolate(const Score& v, Phase ph, ScaleFactor sf); Value interpolate(const Score& v, Phase ph, ScaleFactor sf);
Score apply_weight(Score v, Score w);
Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight); Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight);
double to_cp(Value v); double to_cp(Value v);
void trace_add(int idx, Score term_w, Score term_b = SCORE_ZERO);
void trace_row(const char* name, int idx);
} }
@@ -281,13 +270,22 @@ namespace Eval {
} }
/// trace() is like evaluate() but instead of a value returns a string suitable
/// to be print on stdout with the detailed descriptions and values of each
/// evaluation term. Used mainly for debugging.
std::string trace(const Position& pos) {
return Tracing::do_trace(pos);
}
/// init() computes evaluation weights from the corresponding UCI parameters /// init() computes evaluation weights from the corresponding UCI parameters
/// and setup king tables. /// and setup king tables.
void init() { void init() {
Weights[Mobility] = weight_option("Mobility (Middle Game)", "Mobility (Endgame)", WeightsInternal[Mobility]); Weights[Mobility] = weight_option("Mobility (Midgame)", "Mobility (Endgame)", WeightsInternal[Mobility]);
Weights[PassedPawns] = weight_option("Passed Pawns (Middle Game)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]); Weights[PawnStructure] = weight_option("Pawn Structure (Midgame)", "Pawn Structure (Endgame)", WeightsInternal[PawnStructure]);
Weights[PassedPawns] = weight_option("Passed Pawns (Midgame)", "Passed Pawns (Endgame)", WeightsInternal[PassedPawns]);
Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]); Weights[Space] = weight_option("Space", "Space", WeightsInternal[Space]);
Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]); Weights[KingDangerUs] = weight_option("Cowardice", "Cowardice", WeightsInternal[KingDangerUs]);
Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]); Weights[KingDangerThem] = weight_option("Aggressiveness", "Aggressiveness", WeightsInternal[KingDangerThem]);
@@ -299,57 +297,11 @@ namespace Eval {
{ {
t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope)); t = std::min(Peak, std::min(int(0.4 * i * i), t + MaxSlope));
KingDangerTable[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]); KingDanger[1][i] = apply_weight(make_score(t, 0), Weights[KingDangerUs]);
KingDangerTable[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]); KingDanger[0][i] = apply_weight(make_score(t, 0), Weights[KingDangerThem]);
} }
} }
/// trace() is like evaluate() but instead of a value returns a string suitable
/// to be print on stdout with the detailed descriptions and values of each
/// evaluation term. Used mainly for debugging.
std::string trace(const Position& pos) {
Value margin;
std::string totals;
Search::RootColor = pos.side_to_move();
TraceStream.str("");
TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
memset(TracedScores, 0, 2 * 16 * sizeof(Score));
do_evaluate<true>(pos, margin);
totals = TraceStream.str();
TraceStream.str("");
TraceStream << std::setw(21) << "Eval term " << "| White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n"
<< "---------------------+-------------+-------------+---------------\n";
trace_row("Material, PST, Tempo", PST);
trace_row("Material imbalance", IMBALANCE);
trace_row("Pawns", PAWN);
trace_row("Knights", KNIGHT);
trace_row("Bishops", BISHOP);
trace_row("Rooks", ROOK);
trace_row("Queens", QUEEN);
trace_row("Mobility", MOBILITY);
trace_row("King safety", KING);
trace_row("Threats", THREAT);
trace_row("Passed pawns", PASSED);
trace_row("Unstoppable pawns", UNSTOPPABLE);
trace_row("Space", SPACE);
TraceStream << "---------------------+-------------+-------------+---------------\n";
trace_row("Total", TOTAL);
TraceStream << totals;
return TraceStream.str();
}
} // namespace Eval } // namespace Eval
@@ -388,7 +340,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// Probe the pawn hash table // Probe the pawn hash table
ei.pi = Pawns::probe(pos, th->pawnsTable); ei.pi = Pawns::probe(pos, th->pawnsTable);
score += ei.pi->pawns_value(); score += apply_weight(ei.pi->pawns_value(), Weights[PawnStructure]);
// Initialize attack and king safety bitboards // Initialize attack and king safety bitboards
init_eval_info<WHITE>(pos, ei); init_eval_info<WHITE>(pos, ei);
@@ -406,12 +358,12 @@ Value do_evaluate(const Position& pos, Value& margin) {
- evaluate_king<BLACK, Trace>(pos, ei, margins); - evaluate_king<BLACK, Trace>(pos, ei, margins);
// Evaluate tactical threats, we need full attack information including king // Evaluate tactical threats, we need full attack information including king
score += evaluate_threats<WHITE>(pos, ei) score += evaluate_threats<WHITE, Trace>(pos, ei)
- evaluate_threats<BLACK>(pos, ei); - evaluate_threats<BLACK, Trace>(pos, ei);
// Evaluate passed pawns, we need full attack information including king // Evaluate passed pawns, we need full attack information including king
score += evaluate_passed_pawns<WHITE>(pos, ei) score += evaluate_passed_pawns<WHITE, Trace>(pos, ei)
- evaluate_passed_pawns<BLACK>(pos, ei); - evaluate_passed_pawns<BLACK, Trace>(pos, ei);
// If one side has only a king, check whether exists any unstoppable passed pawn // If one side has only a king, check whether exists any unstoppable passed pawn
if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK)) if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
@@ -421,7 +373,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
if (ei.mi->space_weight()) if (ei.mi->space_weight())
{ {
int s = evaluate_space<WHITE>(pos, ei) - evaluate_space<BLACK>(pos, ei); int s = evaluate_space<WHITE>(pos, ei) - evaluate_space<BLACK>(pos, ei);
score += apply_weight(make_score(s * ei.mi->space_weight(), 0), Weights[Space]); score += apply_weight(s * ei.mi->space_weight(), Weights[Space]);
} }
// Scale winning side if position is more drawish that what it appears // Scale winning side if position is more drawish that what it appears
@@ -440,7 +392,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
{ {
// Check for KBP vs KB with only a single pawn that is almost // Check for KBP vs KB with only a single pawn that is almost
// certainly a draw or at least two pawns. // certainly a draw or at least two pawns.
bool one_pawn = (pos.piece_count(WHITE, PAWN) + pos.piece_count(BLACK, PAWN) == 1); bool one_pawn = (pos.count<PAWN>(WHITE) + pos.count<PAWN>(BLACK) == 1);
sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32); sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32);
} }
else else
@@ -455,18 +407,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
// In case of tracing add all single evaluation contributions for both white and black // In case of tracing add all single evaluation contributions for both white and black
if (Trace) if (Trace)
{ {
trace_add(PST, pos.psq_score()); Tracing::add(PST, pos.psq_score());
trace_add(IMBALANCE, ei.mi->material_value()); Tracing::add(IMBALANCE, ei.mi->material_value());
trace_add(PAWN, ei.pi->pawns_value()); Tracing::add(PAWN, ei.pi->pawns_value());
trace_add(MOBILITY, apply_weight(mobilityWhite, Weights[Mobility]), apply_weight(mobilityBlack, Weights[Mobility])); Tracing::add(UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei));
trace_add(THREAT, evaluate_threats<WHITE>(pos, ei), evaluate_threats<BLACK>(pos, ei)); Score w = ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei);
trace_add(PASSED, evaluate_passed_pawns<WHITE>(pos, ei), evaluate_passed_pawns<BLACK>(pos, ei)); Score b = ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei);
trace_add(UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei)); Tracing::add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
Score w = make_score(ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei), 0); Tracing::add(TOTAL, score);
Score b = make_score(ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei), 0); Tracing::stream << "\nUncertainty margin: White: " << to_cp(margins[WHITE])
trace_add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
trace_add(TOTAL, score);
TraceStream << "\nUncertainty margin: White: " << to_cp(margins[WHITE])
<< ", Black: " << to_cp(margins[BLACK]) << ", Black: " << to_cp(margins[BLACK])
<< "\nScaling: " << std::noshowpos << "\nScaling: " << std::noshowpos
<< std::setw(6) << 100.0 * ei.mi->game_phase() / 128.0 << "% MG, " << std::setw(6) << 100.0 * ei.mi->game_phase() / 128.0 << "% MG, "
@@ -486,15 +435,15 @@ Value do_evaluate(const Position& pos, Value& margin) {
void init_eval_info(const Position& pos, EvalInfo& ei) { void init_eval_info(const Position& pos, EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square Down = (Us == WHITE ? DELTA_S : DELTA_N);
Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them)); Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them));
ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us); ei.attackedBy[Us][PAWN] = ei.pi->pawn_attacks(Us);
// Init king safety tables only if we are going to use them // Init king safety tables only if we are going to use them
if ( pos.piece_count(Us, QUEEN) if (pos.count<QUEEN>(Us) && pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg)
&& pos.non_pawn_material(Us) > QueenValueMg + PawnValueMg)
{ {
ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8)); ei.kingRing[Them] = b | shift_bb<Down>(b);
b &= ei.attackedBy[Us][PAWN]; b &= ei.attackedBy[Us][PAWN];
ei.kingAttackersCount[Us] = b ? popcount<Max15>(b) / 2 : 0; ei.kingAttackersCount[Us] = b ? popcount<Max15>(b) / 2 : 0;
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0; ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
@@ -513,14 +462,14 @@ Value do_evaluate(const Position& pos, Value& margin) {
assert (Piece == BISHOP || Piece == KNIGHT); assert (Piece == BISHOP || Piece == KNIGHT);
// Initial bonus based on square // Initial bonus based on square
Value bonus = OutpostBonus[Piece == BISHOP][relative_square(Us, s)]; Value bonus = Outpost[Piece == BISHOP][relative_square(Us, s)];
// Increase bonus if supported by pawn, especially if the opponent has // Increase bonus if supported by pawn, especially if the opponent has
// no minor piece which can exchange the outpost piece. // no minor piece which can exchange the outpost piece.
if (bonus && (ei.attackedBy[Us][PAWN] & s)) if (bonus && (ei.attackedBy[Us][PAWN] & s))
{ {
if ( !pos.pieces(Them, KNIGHT) if ( !pos.pieces(Them, KNIGHT)
&& !(same_color_squares(s) & pos.pieces(Them, BISHOP))) && !(squares_of_color(s) & pos.pieces(Them, BISHOP)))
bonus += bonus + bonus / 2; bonus += bonus + bonus / 2;
else else
bonus += bonus / 2; bonus += bonus / 2;
@@ -535,27 +484,20 @@ Value do_evaluate(const Position& pos, Value& margin) {
Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) { Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) {
Bitboard b; Bitboard b;
Square s, ksq; Square s;
int mob;
File f;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square* pl = pos.piece_list(Us, Piece); const Square* pl = pos.list<Piece>(Us);
ei.attackedBy[Us][Piece] = 0; ei.attackedBy[Us][Piece] = 0;
while ((s = *pl++) != SQ_NONE) while ((s = *pl++) != SQ_NONE)
{ {
// Find attacked squares, including x-ray attacks for bishops and rooks // Find attacked squares, including x-ray attacks for bishops and rooks
if (Piece == KNIGHT || Piece == QUEEN) b = Piece == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(Us, QUEEN))
b = pos.attacks_from<Piece>(s); : Piece == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN))
else if (Piece == BISHOP) : pos.attacks_from<Piece>(s);
b = attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(Us, QUEEN));
else if (Piece == ROOK)
b = attacks_bb<ROOK>(s, pos.pieces() ^ pos.pieces(Us, ROOK, QUEEN));
else
assert(false);
ei.attackedBy[Us][Piece] |= b; ei.attackedBy[Us][Piece] |= b;
@@ -568,110 +510,91 @@ Value do_evaluate(const Position& pos, Value& margin) {
ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb); ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb);
} }
mob = (Piece != QUEEN ? popcount<Max15>(b & mobilityArea) int mob = Piece != QUEEN ? popcount<Max15>(b & mobilityArea)
: popcount<Full >(b & mobilityArea)); : popcount<Full >(b & mobilityArea);
mobility += MobilityBonus[Piece][mob]; mobility += MobilityBonus[Piece][mob];
// Decrease score if we are attacked by an enemy pawn. Remaining part // Decrease score if we are attacked by an enemy pawn. Remaining part
// of threat evaluation must be done later when we have full attack info. // of threat evaluation must be done later when we have full attack info.
if (ei.attackedBy[Them][PAWN] & s) if (ei.attackedBy[Them][PAWN] & s)
score -= ThreatenedByPawnPenalty[Piece]; score -= ThreatenedByPawn[Piece];
// Otherwise give a bonus if we are a bishop and can pin a piece or // Otherwise give a bonus if we are a bishop and can pin a piece or can
// can give a discovered check through an x-ray attack. // give a discovered check through an x-ray attack.
else if ( Piece == BISHOP else if ( Piece == BISHOP
&& (PseudoAttacks[Piece][pos.king_square(Them)] & s) && (PseudoAttacks[Piece][pos.king_square(Them)] & s)
&& !more_than_one(BetweenBB[s][pos.king_square(Them)] & pos.pieces())) && !more_than_one(BetweenBB[s][pos.king_square(Them)] & pos.pieces()))
score += BishopPinBonus; score += BishopPin;
// Penalty for bishop with same coloured pawns // Penalty for bishop with same coloured pawns
if (Piece == BISHOP) if (Piece == BISHOP)
score -= BishopPawnsPenalty * ei.pi->pawns_on_same_color_squares(Us, s); score -= BishopPawns * ei.pi->pawns_on_same_color_squares(Us, s);
if (Piece == BISHOP || Piece == KNIGHT)
{
// Bishop and knight outposts squares // Bishop and knight outposts squares
if ( (Piece == BISHOP || Piece == KNIGHT) if (!(pos.pieces(Them, PAWN) & pawn_attack_span(Us, s)))
&& !(pos.pieces(Them, PAWN) & attack_span_mask(Us, s)))
score += evaluate_outposts<Piece, Us>(pos, ei, s); score += evaluate_outposts<Piece, Us>(pos, ei, s);
if ((Piece == ROOK || Piece == QUEEN) && relative_rank(Us, s) >= RANK_5) // Bishop or knight behind a pawn
if ( relative_rank(Us, s) < RANK_5
&& (pos.pieces(PAWN) & (s + pawn_push(Us))))
score += MinorBehindPawn;
}
if ( (Piece == ROOK || Piece == QUEEN)
&& relative_rank(Us, s) >= RANK_5)
{ {
// Major piece on 7th rank // Major piece on 7th rank and enemy king trapped on 8th
if ( relative_rank(Us, s) == RANK_7 if ( relative_rank(Us, s) == RANK_7
&& relative_rank(Us, pos.king_square(Them)) == RANK_8) && relative_rank(Us, pos.king_square(Them)) == RANK_8)
score += (Piece == ROOK ? RookOn7thBonus : QueenOn7thBonus); score += Piece == ROOK ? RookOn7th : QueenOn7th;
// Major piece attacking pawns on the same rank // Major piece attacking enemy pawns on the same rank/file
Bitboard pawns = pos.pieces(Them, PAWN) & rank_bb(s); Bitboard pawns = pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s];
if (pawns) if (pawns)
score += (Piece == ROOK ? RookOnPawnBonus score += popcount<Max15>(pawns) * (Piece == ROOK ? RookOnPawn : QueenOnPawn);
: QueenOnPawnBonus) * popcount<Max15>(pawns);
}
// Special extra evaluation for bishops
if (Piece == BISHOP && pos.is_chess960())
{
// An important Chess960 pattern: A cornered bishop blocked by
// a friendly pawn diagonally in front of it is a very serious
// problem, especially when that pawn is also blocked.
if (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))
{
Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
{
if (!pos.is_empty(s + d + pawn_push(Us)))
score -= 2*TrappedBishopA1H1Penalty;
else if (pos.piece_on(s + 2*d) == make_piece(Us, PAWN))
score -= TrappedBishopA1H1Penalty;
else
score -= TrappedBishopA1H1Penalty / 2;
}
}
} }
// Special extra evaluation for rooks // Special extra evaluation for rooks
if (Piece == ROOK) if (Piece == ROOK)
{ {
// Open and half-open files // Give a bonus for a rook on a open or semi-open file
f = file_of(s); if (ei.pi->semiopen(Us, file_of(s)))
if (ei.pi->file_is_half_open(Us, f)) score += ei.pi->semiopen(Them, file_of(s)) ? RookOpenFile : RookSemiopenFile;
{
if (ei.pi->file_is_half_open(Them, f)) if (mob > 3 || ei.pi->semiopen(Us, file_of(s)))
score += RookOpenFileBonus; continue;
else
score += RookHalfOpenFileBonus; Square ksq = pos.king_square(Us);
}
// Penalize rooks which are trapped inside a king. Penalize more if // Penalize rooks which are trapped inside a king. Penalize more if
// king has lost right to castle. // king has lost right to castle.
if (mob > 6 || ei.pi->file_is_half_open(Us, f)) if ( ((file_of(ksq) < FILE_E) == (file_of(s) < file_of(ksq)))
continue; && (rank_of(ksq) == rank_of(s) || relative_rank(Us, ksq) == RANK_1)
&& !ei.pi->semiopen_on_side(Us, file_of(ksq), file_of(ksq) < FILE_E))
ksq = pos.king_square(Us); score -= (TrappedRook - make_score(mob * 8, 0)) * (pos.can_castle(Us) ? 1 : 2);
if ( file_of(ksq) >= FILE_E
&& file_of(s) > file_of(ksq)
&& (relative_rank(Us, ksq) == RANK_1 || rank_of(ksq) == rank_of(s)))
{
// Is there a half-open file between the king and the edge of the board?
if (!ei.pi->has_open_file_to_right(Us, file_of(ksq)))
score -= make_score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2
: (TrappedRookPenalty - mob * 16), 0);
} }
else if ( file_of(ksq) <= FILE_D
&& file_of(s) < file_of(ksq) // An important Chess960 pattern: A cornered bishop blocked by a friendly
&& (relative_rank(Us, ksq) == RANK_1 || rank_of(ksq) == rank_of(s))) // pawn diagonally in front of it is a very serious problem, especially
// when that pawn is also blocked.
if ( Piece == BISHOP
&& pos.is_chess960()
&& (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
{ {
// Is there a half-open file between the king and the edge of the board? const enum Piece P = make_piece(Us, PAWN);
if (!ei.pi->has_open_file_to_left(Us, file_of(ksq))) Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
score -= make_score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2 if (pos.piece_on(s + d) == P)
: (TrappedRookPenalty - mob * 16), 0); score -= !pos.is_empty(s + d + pawn_push(Us)) ? TrappedBishopA1H1 * 4
} : pos.piece_on(s + d + d) == P ? TrappedBishopA1H1 * 2
: TrappedBishopA1H1;
} }
} }
if (Trace) if (Trace)
TracedScores[Us][Piece] = score; Tracing::scores[Us][Piece] = score;
return score; return score;
} }
@@ -680,8 +603,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
// evaluate_threats<>() assigns bonuses according to the type of attacking piece // evaluate_threats<>() assigns bonuses according to the type of attacking piece
// and the type of attacked one. // and the type of attacked one.
template<Color Us> template<Color Us, bool Trace>
Score evaluate_threats(const Position& pos, EvalInfo& ei) { Score evaluate_threats(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -689,32 +612,33 @@ Value do_evaluate(const Position& pos, Value& margin) {
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
// Undefended minors get penalized even if not under attack // Undefended minors get penalized even if not under attack
undefendedMinors = pos.pieces(Them) undefendedMinors = pos.pieces(Them, BISHOP, KNIGHT)
& (pos.pieces(BISHOP) | pos.pieces(KNIGHT))
& ~ei.attackedBy[Them][ALL_PIECES]; & ~ei.attackedBy[Them][ALL_PIECES];
if (undefendedMinors) if (undefendedMinors)
score += UndefendedMinorPenalty; score += UndefendedMinor;
// Enemy pieces not defended by a pawn and under our attack // Enemy pieces not defended by a pawn and under our attack
weakEnemies = pos.pieces(Them) weakEnemies = pos.pieces(Them)
& ~ei.attackedBy[Them][PAWN] & ~ei.attackedBy[Them][PAWN]
& ei.attackedBy[Us][ALL_PIECES]; & ei.attackedBy[Us][ALL_PIECES];
if (!weakEnemies)
return score;
// Add bonus according to type of attacked enemy piece and to the // Add bonus according to type of attacked enemy piece and to the
// type of attacking piece, from knights to queens. Kings are not // type of attacking piece, from knights to queens. Kings are not
// considered because are already handled in king evaluation. // considered because are already handled in king evaluation.
if (weakEnemies)
for (PieceType pt1 = KNIGHT; pt1 < KING; pt1++) for (PieceType pt1 = KNIGHT; pt1 < KING; pt1++)
{ {
b = ei.attackedBy[Us][pt1] & weakEnemies; b = ei.attackedBy[Us][pt1] & weakEnemies;
if (b) if (b)
for (PieceType pt2 = PAWN; pt2 < KING; pt2++) for (PieceType pt2 = PAWN; pt2 < KING; pt2++)
if (b & pos.pieces(pt2)) if (b & pos.pieces(pt2))
score += ThreatBonus[pt1][pt2]; score += Threat[pt1][pt2];
} }
if (Trace)
Tracing::scores[Us][THREAT] = score;
return score; return score;
} }
@@ -730,7 +654,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
Score score = mobility = SCORE_ZERO; Score score = mobility = SCORE_ZERO;
// Do not include in mobility squares protected by enemy pawns or occupied by our pieces // Do not include in mobility squares protected by enemy pawns or occupied by our pieces
const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces(Us)); const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces(Us, PAWN, KING));
score += evaluate_pieces<KNIGHT, Us, Trace>(pos, ei, mobility, mobilityArea); score += evaluate_pieces<KNIGHT, Us, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<BISHOP, Us, Trace>(pos, ei, mobility, mobilityArea); score += evaluate_pieces<BISHOP, Us, Trace>(pos, ei, mobility, mobilityArea);
@@ -741,6 +665,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] ei.attackedBy[Us][ALL_PIECES] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
| ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK] | ei.attackedBy[Us][BISHOP] | ei.attackedBy[Us][ROOK]
| ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING]; | ei.attackedBy[Us][QUEEN] | ei.attackedBy[Us][KING];
if (Trace)
Tracing::scores[Us][MOBILITY] = apply_weight(mobility, Weights[Mobility]);
return score; return score;
} }
@@ -748,7 +675,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// evaluate_king<>() assigns bonuses and penalties to a king of a given color // evaluate_king<>() assigns bonuses and penalties to a king of a given color
template<Color Us, bool Trace> template<Color Us, bool Trace>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]) { Score evaluate_king(const Position& pos, const EvalInfo& ei, Value margins[]) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -772,13 +699,13 @@ Value do_evaluate(const Position& pos, Value& margin) {
| ei.attackedBy[Us][QUEEN]); | ei.attackedBy[Us][QUEEN]);
// Initialize the 'attackUnits' variable, which is used later on as an // Initialize the 'attackUnits' variable, which is used later on as an
// index to the KingDangerTable[] array. The initial value is based on // index to the KingDanger[] array. The initial value is based on the
// the number and types of the enemy's attacking pieces, the number of // number and types of the enemy's attacking pieces, the number of
// attacked and undefended squares around our king, the square of the // attacked and undefended squares around our king, the square of the
// king, and the quality of the pawn shelter. // king, and the quality of the pawn shelter.
attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended)) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended))
+ InitKingDanger[relative_square(Us, ksq)] + KingExposed[relative_square(Us, ksq)]
- mg_value(score) / 32; - mg_value(score) / 32;
// Analyse enemy's safe queen contact checks. First find undefended // Analyse enemy's safe queen contact checks. First find undefended
@@ -790,7 +717,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT] b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]); | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]);
if (b) if (b)
attackUnits += QueenContactCheckBonus attackUnits += QueenContactCheck
* popcount<Max15>(b) * popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1); * (Them == pos.side_to_move() ? 2 : 1);
} }
@@ -808,7 +735,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT] b &= ( ei.attackedBy[Them][PAWN] | ei.attackedBy[Them][KNIGHT]
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]); | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
if (b) if (b)
attackUnits += RookContactCheckBonus attackUnits += RookContactCheck
* popcount<Max15>(b) * popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1); * (Them == pos.side_to_move() ? 2 : 1);
} }
@@ -822,37 +749,37 @@ Value do_evaluate(const Position& pos, Value& margin) {
// Enemy queen safe checks // Enemy queen safe checks
b = (b1 | b2) & ei.attackedBy[Them][QUEEN]; b = (b1 | b2) & ei.attackedBy[Them][QUEEN];
if (b) if (b)
attackUnits += QueenCheckBonus * popcount<Max15>(b); attackUnits += QueenCheck * popcount<Max15>(b);
// Enemy rooks safe checks // Enemy rooks safe checks
b = b1 & ei.attackedBy[Them][ROOK]; b = b1 & ei.attackedBy[Them][ROOK];
if (b) if (b)
attackUnits += RookCheckBonus * popcount<Max15>(b); attackUnits += RookCheck * popcount<Max15>(b);
// Enemy bishops safe checks // Enemy bishops safe checks
b = b2 & ei.attackedBy[Them][BISHOP]; b = b2 & ei.attackedBy[Them][BISHOP];
if (b) if (b)
attackUnits += BishopCheckBonus * popcount<Max15>(b); attackUnits += BishopCheck * popcount<Max15>(b);
// Enemy knights safe checks // Enemy knights safe checks
b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT] & safe; b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT] & safe;
if (b) if (b)
attackUnits += KnightCheckBonus * popcount<Max15>(b); attackUnits += KnightCheck * popcount<Max15>(b);
// To index KingDangerTable[] attackUnits must be in [0, 99] range // To index KingDanger[] attackUnits must be in [0, 99] range
attackUnits = std::min(99, std::max(0, attackUnits)); attackUnits = std::min(99, std::max(0, attackUnits));
// Finally, extract the king danger score from the KingDangerTable[] // Finally, extract the king danger score from the KingDanger[]
// array and subtract the score from evaluation. Set also margins[] // array and subtract the score from evaluation. Set also margins[]
// value that will be used for pruning because this value can sometimes // value that will be used for pruning because this value can sometimes
// be very big, and so capturing a single attacking piece can therefore // be very big, and so capturing a single attacking piece can therefore
// result in a score change far bigger than the value of the captured piece. // result in a score change far bigger than the value of the captured piece.
score -= KingDangerTable[Us == Search::RootColor][attackUnits]; score -= KingDanger[Us == Search::RootColor][attackUnits];
margins[Us] += mg_value(KingDangerTable[Us == Search::RootColor][attackUnits]); margins[Us] += mg_value(KingDanger[Us == Search::RootColor][attackUnits]);
} }
if (Trace) if (Trace)
TracedScores[Us][KING] = score; Tracing::scores[Us][KING] = score;
return score; return score;
} }
@@ -860,8 +787,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
// evaluate_passed_pawns<>() evaluates the passed pawns of the given color // evaluate_passed_pawns<>() evaluates the passed pawns of the given color
template<Color Us> template<Color Us, bool Trace>
Score evaluate_passed_pawns(const Position& pos, EvalInfo& ei) { Score evaluate_passed_pawns(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -870,10 +797,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
b = ei.pi->passed_pawns(Us); b = ei.pi->passed_pawns(Us);
if (!b) while (b)
return SCORE_ZERO; {
do {
Square s = pop_lsb(&b); Square s = pop_lsb(&b);
assert(pos.pawn_is_passed(Us, s)); assert(pos.pawn_is_passed(Us, s));
@@ -882,8 +807,8 @@ Value do_evaluate(const Position& pos, Value& margin) {
int rr = r * (r - 1); int rr = r * (r - 1);
// Base bonus based on rank // Base bonus based on rank
Value mbonus = Value(20 * rr); Value mbonus = Value(17 * rr);
Value ebonus = Value(10 * (rr + r + 1)); Value ebonus = Value(7 * (rr + r + 1));
if (rr) if (rr)
{ {
@@ -906,22 +831,25 @@ Value do_evaluate(const Position& pos, Value& margin) {
// If there is an enemy rook or queen attacking the pawn from behind, // If there is an enemy rook or queen attacking the pawn from behind,
// add all X-ray attacks by the rook or queen. Otherwise consider only // add all X-ray attacks by the rook or queen. Otherwise consider only
// the squares in the pawn's path attacked or occupied by the enemy. // the squares in the pawn's path attacked or occupied by the enemy.
if ( (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN)) if ( unlikely(forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN))
&& (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from<ROOK>(s))) && (forward_bb(Them, s) & pos.pieces(Them, ROOK, QUEEN) & pos.attacks_from<ROOK>(s)))
unsafeSquares = squaresToQueen; unsafeSquares = squaresToQueen;
else else
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them)); unsafeSquares = squaresToQueen & (ei.attackedBy[Them][ALL_PIECES] | pos.pieces(Them));
// If there aren't enemy attacks or pieces along the path to queen give // If there aren't enemy attacks huge bonus, a bit smaller if at
// huge bonus. Even bigger if we protect the pawn's path. // least block square is not attacked, otherwise smallest bonus.
if (!unsafeSquares) int k = !unsafeSquares ? 15 : !(unsafeSquares & blockSq) ? 9 : 3;
ebonus += Value(rr * (squaresToQueen == defendedSquares ? 17 : 15));
else // Big bonus if the path to queen is fully defended, a bit less
// OK, there are enemy attacks or pieces (but not pawns). Are those // if at least block square is defended.
// squares which are attacked by the enemy also attacked by us ? if (defendedSquares == squaresToQueen)
// If yes, big bonus (but smaller than when there are no enemy attacks), k += 6;
// if no, somewhat smaller bonus.
ebonus += Value(rr * ((unsafeSquares & defendedSquares) == unsafeSquares ? 13 : 8)); else if (defendedSquares & blockSq)
k += (unsafeSquares & defendedSquares) == unsafeSquares ? 4 : 2;
mbonus += Value(k * rr), ebonus += Value(k * rr);
} }
} // rr != 0 } // rr != 0
@@ -949,7 +877,10 @@ Value do_evaluate(const Position& pos, Value& margin) {
} }
score += make_score(mbonus, ebonus); score += make_score(mbonus, ebonus);
} while (b); }
if (Trace)
Tracing::scores[Us][PASSED] = apply_weight(score, Weights[PassedPawns]);
// Add the scores to the middle game and endgame eval // Add the scores to the middle game and endgame eval
return apply_weight(score, Weights[PassedPawns]); return apply_weight(score, Weights[PassedPawns]);
@@ -959,7 +890,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides, this is quite // evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides, this is quite
// conservative and returns a winning score only when we are very sure that the pawn is winning. // conservative and returns a winning score only when we are very sure that the pawn is winning.
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei) { Score evaluate_unstoppable_pawns(const Position& pos, const EvalInfo& ei) {
Bitboard b, b2, blockers, supporters, queeningPath, candidates; Bitboard b, b2, blockers, supporters, queeningPath, candidates;
Square s, blockSq, queeningSquare; Square s, blockSq, queeningSquare;
@@ -1069,7 +1000,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// black pawns: a4, b4 white: b2 then pawn in b4 is giving support. // black pawns: a4, b4 white: b2 then pawn in b4 is giving support.
if (!opposed) if (!opposed)
{ {
b2 = supporters & in_front_bb(winnerSide, blockSq + pawn_push(winnerSide)); b2 = supporters & in_front_bb(winnerSide, rank_of(blockSq + pawn_push(winnerSide)));
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color) while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{ {
@@ -1079,7 +1010,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
} }
// Check pawns that can be sacrificed against the blocking pawn // Check pawns that can be sacrificed against the blocking pawn
b2 = attack_span_mask(winnerSide, blockSq) & candidates & ~(1ULL << s); b2 = pawn_attack_span(winnerSide, blockSq) & candidates & ~(1ULL << s);
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color) while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{ {
@@ -1124,7 +1055,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
// twice. Finally, the space bonus is scaled by a weight taken from the // twice. Finally, the space bonus is scaled by a weight taken from the
// material hash table. The aim is to improve play on game opening. // material hash table. The aim is to improve play on game opening.
template<Color Us> template<Color Us>
int evaluate_space(const Position& pos, EvalInfo& ei) { int evaluate_space(const Position& pos, const EvalInfo& ei) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -1160,9 +1091,14 @@ Value do_evaluate(const Position& pos, Value& margin) {
int ev = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL; int ev = (eg_value(v) * int(sf)) / SCALE_FACTOR_NORMAL;
int result = (mg_value(v) * int(ph) + ev * int(128 - ph)) / 128; int result = (mg_value(v) * int(ph) + ev * int(128 - ph)) / 128;
return Value((result + GrainSize / 2) & ~(GrainSize - 1)); return Value((result / GrainSize) * GrainSize); // Sign independent
} }
// apply_weight() weights score v by score w trying to prevent overflow
Score apply_weight(Score v, Score w) {
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
(int(eg_value(v)) * eg_value(w)) / 0x100);
}
// weight_option() computes the value of an evaluation weight, by combining // weight_option() computes the value of an evaluation weight, by combining
// two UCI-configurable weights (midgame and endgame) with an internal weight. // two UCI-configurable weights (midgame and endgame) with an internal weight.
@@ -1177,34 +1113,29 @@ Value do_evaluate(const Position& pos, Value& margin) {
} }
// A couple of little helpers used by tracing code, to_cp() converts a value to // Tracing functions definitions
// a double in centipawns scale, trace_add() stores white and black scores.
double to_cp(Value v) { return double(v) / double(PawnValueMg); } double to_cp(Value v) { return double(v) / double(PawnValueMg); }
void trace_add(int idx, Score wScore, Score bScore) { void Tracing::add(int idx, Score wScore, Score bScore) {
TracedScores[WHITE][idx] = wScore; scores[WHITE][idx] = wScore;
TracedScores[BLACK][idx] = bScore; scores[BLACK][idx] = bScore;
} }
void Tracing::row(const char* name, int idx) {
// trace_row() is an helper function used by tracing code to register the Score wScore = scores[WHITE][idx];
// values of a single evaluation term. Score bScore = scores[BLACK][idx];
void trace_row(const char* name, int idx) {
Score wScore = TracedScores[WHITE][idx];
Score bScore = TracedScores[BLACK][idx];
switch (idx) { switch (idx) {
case PST: case IMBALANCE: case PAWN: case UNSTOPPABLE: case TOTAL: case PST: case IMBALANCE: case PAWN: case UNSTOPPABLE: case TOTAL:
TraceStream << std::setw(20) << name << " | --- --- | --- --- | " stream << std::setw(20) << name << " | --- --- | --- --- | "
<< std::setw(6) << to_cp(mg_value(wScore)) << " " << std::setw(6) << to_cp(mg_value(wScore)) << " "
<< std::setw(6) << to_cp(eg_value(wScore)) << " \n"; << std::setw(6) << to_cp(eg_value(wScore)) << " \n";
break; break;
default: default:
TraceStream << std::setw(20) << name << " | " << std::noshowpos stream << std::setw(20) << name << " | " << std::noshowpos
<< std::setw(5) << to_cp(mg_value(wScore)) << " " << std::setw(5) << to_cp(mg_value(wScore)) << " "
<< std::setw(5) << to_cp(eg_value(wScore)) << " | " << std::setw(5) << to_cp(eg_value(wScore)) << " | "
<< std::setw(5) << to_cp(mg_value(bScore)) << " " << std::setw(5) << to_cp(mg_value(bScore)) << " "
@@ -1214,4 +1145,41 @@ Value do_evaluate(const Position& pos, Value& margin) {
<< std::setw(6) << to_cp(eg_value(wScore - bScore)) << " \n"; << std::setw(6) << to_cp(eg_value(wScore - bScore)) << " \n";
} }
} }
std::string Tracing::do_trace(const Position& pos) {
stream.str("");
stream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
std::memset(scores, 0, 2 * (TOTAL + 1) * sizeof(Score));
Value margin;
do_evaluate<true>(pos, margin);
std::string totals = stream.str();
stream.str("");
stream << std::setw(21) << "Eval term " << "| White | Black | Total \n"
<< " | MG EG | MG EG | MG EG \n"
<< "---------------------+-------------+-------------+---------------\n";
row("Material, PST, Tempo", PST);
row("Material imbalance", IMBALANCE);
row("Pawns", PAWN);
row("Knights", KNIGHT);
row("Bishops", BISHOP);
row("Rooks", ROOK);
row("Queens", QUEEN);
row("Mobility", MOBILITY);
row("King safety", KING);
row("Threats", THREAT);
row("Passed pawns", PASSED);
row("Unstoppable pawns", UNSTOPPABLE);
row("Space", SPACE);
stream << "---------------------+-------------+-------------+---------------\n";
row("Total", TOTAL);
stream << totals;
return stream.str();
}
} }
+2 -2
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(EVALUATE_H_INCLUDED) #ifndef EVALUATE_H_INCLUDED
#define EVALUATE_H_INCLUDED #define EVALUATE_H_INCLUDED
#include "types.h" #include "types.h"
@@ -32,4 +32,4 @@ extern std::string trace(const Position& pos);
} }
#endif // !defined(EVALUATE_H_INCLUDED) #endif // #ifndef EVALUATE_H_INCLUDED
+1 -1
View File
@@ -34,7 +34,7 @@ int main(int argc, char* argv[]) {
UCI::init(Options); UCI::init(Options);
Bitboards::init(); Bitboards::init();
Zobrist::init(); Position::init();
Bitbases::init_kpk(); Bitbases::init_kpk();
Search::init(); Search::init();
Eval::init(); Eval::init();
+31 -31
View File
@@ -35,8 +35,8 @@ namespace {
const int NoPawnsSF[4] = { 6, 12, 32 }; const int NoPawnsSF[4] = { 6, 12, 32 };
// Polynomial material balance parameters // Polynomial material balance parameters
const Value RedundantQueenPenalty = Value(320); const Value RedundantQueen = Value(320);
const Value RedundantRookPenalty = Value(554); const Value RedundantRook = Value(554);
// pair pawn knight bishop rook queen // pair pawn knight bishop rook queen
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 }; const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
@@ -75,24 +75,24 @@ namespace {
// Helper templates used to detect a given material distribution // Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) { template<Color Us> bool is_KXK(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.non_pawn_material(Them) == VALUE_ZERO return !pos.count<PAWN>(Them)
&& pos.piece_count(Them, PAWN) == 0 && pos.non_pawn_material(Them) == VALUE_ZERO
&& pos.non_pawn_material(Us) >= RookValueMg; && pos.non_pawn_material(Us) >= RookValueMg;
} }
template<Color Us> bool is_KBPsKs(const Position& pos) { template<Color Us> bool is_KBPsKs(const Position& pos) {
return pos.non_pawn_material(Us) == BishopValueMg return pos.non_pawn_material(Us) == BishopValueMg
&& pos.piece_count(Us, BISHOP) == 1 && pos.count<BISHOP>(Us) == 1
&& pos.piece_count(Us, PAWN) >= 1; && pos.count<PAWN >(Us) >= 1;
} }
template<Color Us> bool is_KQKRPs(const Position& pos) { template<Color Us> bool is_KQKRPs(const Position& pos) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
return pos.piece_count(Us, PAWN) == 0 return !pos.count<PAWN>(Us)
&& pos.non_pawn_material(Us) == QueenValueMg && pos.non_pawn_material(Us) == QueenValueMg
&& pos.piece_count(Us, QUEEN) == 1 && pos.count<QUEEN>(Us) == 1
&& pos.piece_count(Them, ROOK) == 1 && pos.count<ROOK>(Them) == 1
&& pos.piece_count(Them, PAWN) >= 1; && pos.count<PAWN>(Them) >= 1;
} }
/// imbalance() calculates imbalance comparing piece count of each /// imbalance() calculates imbalance comparing piece count of each
@@ -109,8 +109,8 @@ namespace {
// Redundancy of major pieces, formula based on Kaufman's paper // Redundancy of major pieces, formula based on Kaufman's paper
// "The Evaluation of Material Imbalances in Chess" // "The Evaluation of Material Imbalances in Chess"
if (pieceCount[Us][ROOK] > 0) if (pieceCount[Us][ROOK] > 0)
value -= RedundantRookPenalty * (pieceCount[Us][ROOK] - 1) value -= RedundantRook * (pieceCount[Us][ROOK] - 1)
+ RedundantQueenPenalty * pieceCount[Us][QUEEN]; + RedundantQueen * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad // Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++) for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
@@ -150,7 +150,7 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
if (e->key == key) if (e->key == key)
return e; return e;
memset(e, 0, sizeof(Entry)); std::memset(e, 0, sizeof(Entry));
e->key = key; e->key = key;
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL; e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
e->gamePhase = game_phase(pos); e->gamePhase = game_phase(pos);
@@ -180,8 +180,8 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
assert((pos.pieces(WHITE, KNIGHT) | pos.pieces(WHITE, BISHOP))); assert((pos.pieces(WHITE, KNIGHT) | pos.pieces(WHITE, BISHOP)));
assert((pos.pieces(BLACK, KNIGHT) | pos.pieces(BLACK, BISHOP))); assert((pos.pieces(BLACK, KNIGHT) | pos.pieces(BLACK, BISHOP)));
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2 if ( pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE) <= 2
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2) && pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK) <= 2)
{ {
e->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()]; e->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()];
return e; return e;
@@ -221,17 +221,17 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
if (npm_w + npm_b == VALUE_ZERO) if (npm_w + npm_b == VALUE_ZERO)
{ {
if (pos.piece_count(BLACK, PAWN) == 0) if (!pos.count<PAWN>(BLACK))
{ {
assert(pos.piece_count(WHITE, PAWN) >= 2); assert(pos.count<PAWN>(WHITE) >= 2);
e->scalingFunction[WHITE] = &ScaleKPsK[WHITE]; e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
} }
else if (pos.piece_count(WHITE, PAWN) == 0) else if (!pos.count<PAWN>(WHITE))
{ {
assert(pos.piece_count(BLACK, PAWN) >= 2); assert(pos.count<PAWN>(BLACK) >= 2);
e->scalingFunction[BLACK] = &ScaleKPsK[BLACK]; e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
} }
else if (pos.piece_count(WHITE, PAWN) == 1 && pos.piece_count(BLACK, PAWN) == 1) else if (pos.count<PAWN>(WHITE) == 1 && pos.count<PAWN>(BLACK) == 1)
{ {
// This is a special case because we set scaling functions // This is a special case because we set scaling functions
// for both colors instead of only one. // for both colors instead of only one.
@@ -241,35 +241,35 @@ Entry* probe(const Position& pos, Table& entries, Endgames& endgames) {
} }
// No pawns makes it difficult to win, even with a material advantage // No pawns makes it difficult to win, even with a material advantage
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMg) if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
{ {
e->factor[WHITE] = (uint8_t) e->factor[WHITE] = (uint8_t)
(npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]); (npm_w == npm_b || npm_w < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count<BISHOP>(WHITE), 2)]);
} }
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMg) if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg)
{ {
e->factor[BLACK] = (uint8_t) e->factor[BLACK] = (uint8_t)
(npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]); (npm_w == npm_b || npm_b < RookValueMg ? 0 : NoPawnsSF[std::min(pos.count<BISHOP>(BLACK), 2)]);
} }
// Compute the space weight // Compute the space weight
if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg) if (npm_w + npm_b >= 2 * QueenValueMg + 4 * RookValueMg + 2 * KnightValueMg)
{ {
int minorPieceCount = pos.piece_count(WHITE, KNIGHT) + pos.piece_count(WHITE, BISHOP) int minorPieceCount = pos.count<KNIGHT>(WHITE) + pos.count<BISHOP>(WHITE)
+ pos.piece_count(BLACK, KNIGHT) + pos.piece_count(BLACK, BISHOP); + pos.count<KNIGHT>(BLACK) + pos.count<BISHOP>(BLACK);
e->spaceWeight = minorPieceCount * minorPieceCount; e->spaceWeight = make_score(minorPieceCount * minorPieceCount, 0);
} }
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
// for the bishop pair "extended piece", this allow us to be more flexible // for the bishop pair "extended piece", this allow us to be more flexible
// in defining bishop pair bonuses. // in defining bishop pair bonuses.
const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = { const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
{ pos.piece_count(WHITE, BISHOP) > 1, pos.piece_count(WHITE, PAWN), pos.piece_count(WHITE, KNIGHT), { pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),
pos.piece_count(WHITE, BISHOP) , pos.piece_count(WHITE, ROOK), pos.piece_count(WHITE, QUEEN) }, pos.count<BISHOP>(WHITE) , pos.count<ROOK>(WHITE), pos.count<QUEEN >(WHITE) },
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT), { pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
pos.piece_count(BLACK, BISHOP) , pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } }; pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
e->value = (int16_t)((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16); e->value = (int16_t)((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
return e; return e;
+4 -4
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(MATERIAL_H_INCLUDED) #ifndef MATERIAL_H_INCLUDED
#define MATERIAL_H_INCLUDED #define MATERIAL_H_INCLUDED
#include "endgame.h" #include "endgame.h"
@@ -39,7 +39,7 @@ namespace Material {
struct Entry { struct Entry {
Score material_value() const { return make_score(value, value); } Score material_value() const { return make_score(value, value); }
int space_weight() const { return spaceWeight; } Score space_weight() const { return spaceWeight; }
Phase game_phase() const { return gamePhase; } Phase game_phase() const { return gamePhase; }
bool specialized_eval_exists() const { return evaluationFunction != NULL; } bool specialized_eval_exists() const { return evaluationFunction != NULL; }
Value evaluate(const Position& p) const { return (*evaluationFunction)(p); } Value evaluate(const Position& p) const { return (*evaluationFunction)(p); }
@@ -50,7 +50,7 @@ struct Entry {
uint8_t factor[COLOR_NB]; uint8_t factor[COLOR_NB];
EndgameBase<Value>* evaluationFunction; EndgameBase<Value>* evaluationFunction;
EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB];
int spaceWeight; Score spaceWeight;
Phase gamePhase; Phase gamePhase;
}; };
@@ -74,4 +74,4 @@ inline ScaleFactor Entry::scale_factor(const Position& pos, Color c) const {
} }
#endif // !defined(MATERIAL_H_INCLUDED) #endif // #ifndef MATERIAL_H_INCLUDED
+10 -53
View File
@@ -24,17 +24,11 @@
#include "misc.h" #include "misc.h"
#include "thread.h" #include "thread.h"
#if defined(__hpux)
# include <sys/pstat.h>
#endif
using namespace std; using namespace std;
/// Version number. If Version is left empty, then Tag plus current /// Version number. If Version is left empty, then compile date, in the
/// date, in the format DD-MM-YY, are used as a version number. /// format DD-MM-YY, is shown in engine_info.
static const string Version = "4";
static const string Version = "3";
static const string Tag = "";
/// engine_info() returns the full name of the current Stockfish version. This /// engine_info() returns the full name of the current Stockfish version. This
@@ -45,36 +39,26 @@ static const string Tag = "";
const string engine_info(bool to_uci) { const string engine_info(bool to_uci) {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
const string cpu64(Is64Bit ? " 64bit" : "");
const string popcnt(HasPopCnt ? " SSE4.2" : "");
string month, day, year; string month, day, year;
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008" stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008"
s << "Stockfish " << Version; s << "Stockfish " << Version << setfill('0');
if (Version.empty()) if (Version.empty())
{ {
date >> month >> day >> year; date >> month >> day >> year;
s << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
s << Tag << string(Tag.empty() ? "" : " ") << setfill('0') << setw(2) << day
<< "-" << setw(2) << (1 + months.find(month) / 4) << "-" << year.substr(2);
} }
s << cpu64 << popcnt << (to_uci ? "\nid author ": " by ") s << (Is64Bit ? " 64" : "")
<< (HasPopCnt ? " SSE4.2" : "")
<< (to_uci ? "\nid author ": " by ")
<< "Tord Romstad, Marco Costalba and Joona Kiiski"; << "Tord Romstad, Marco Costalba and Joona Kiiski";
return s.str(); return s.str();
} }
/// Convert system time to milliseconds. That's all we need.
Time::point Time::now() {
sys_time_t t; system_time(&t); return time_to_msec(t);
}
/// Debug functions used mainly to collect run-time statistics /// Debug functions used mainly to collect run-time statistics
static uint64_t hits[2], means[2]; static uint64_t hits[2], means[2];
@@ -174,37 +158,12 @@ std::ostream& operator<<(std::ostream& os, SyncCout sc) {
void start_logger(bool b) { Logger::start(b); } void start_logger(bool b) { Logger::start(b); }
/// cpu_count() tries to detect the number of CPU cores
int cpu_count() {
#if defined(_WIN32) || defined(_WIN64)
SYSTEM_INFO s;
GetSystemInfo(&s);
return s.dwNumberOfProcessors;
#else
# if defined(_SC_NPROCESSORS_ONLN)
return sysconf(_SC_NPROCESSORS_ONLN);
# elif defined(__hpux)
struct pst_dynamic psd;
if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1)
return 1;
return psd.psd_proc_cnt;
# else
return 1;
# endif
#endif
}
/// timed_wait() waits for msec milliseconds. It is mainly an helper to wrap /// timed_wait() waits for msec milliseconds. It is mainly an helper to wrap
/// conversion from milliseconds to struct timespec, as used by pthreads. /// conversion from milliseconds to struct timespec, as used by pthreads.
void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) { void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
#if defined(_WIN32) || defined(_WIN64) #ifdef _WIN32
int tm = msec; int tm = msec;
#else #else
timespec ts, *tm = &ts; timespec ts, *tm = &ts;
@@ -221,7 +180,7 @@ void timed_wait(WaitCondition& sleepCond, Lock& sleepLock, int msec) {
/// prefetch() preloads the given address in L1/L2 cache. This is a non /// prefetch() preloads the given address in L1/L2 cache. This is a non
/// blocking function and do not stalls the CPU waiting for data to be /// blocking function and do not stalls the CPU waiting for data to be
/// loaded from memory, that can be quite slow. /// loaded from memory, that can be quite slow.
#if defined(NO_PREFETCH) #ifdef NO_PREFETCH
void prefetch(char*) {} void prefetch(char*) {}
@@ -237,10 +196,8 @@ void prefetch(char* addr) {
# if defined(__INTEL_COMPILER) || defined(_MSC_VER) # if defined(__INTEL_COMPILER) || defined(_MSC_VER)
_mm_prefetch(addr, _MM_HINT_T0); _mm_prefetch(addr, _MM_HINT_T0);
_mm_prefetch(addr+64, _MM_HINT_T0); // 64 bytes ahead
# else # else
__builtin_prefetch(addr); __builtin_prefetch(addr);
__builtin_prefetch(addr+64);
# endif # endif
} }
+3 -4
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(MISC_H_INCLUDED) #ifndef MISC_H_INCLUDED
#define MISC_H_INCLUDED #define MISC_H_INCLUDED
#include <fstream> #include <fstream>
@@ -27,7 +27,6 @@
#include "types.h" #include "types.h"
extern const std::string engine_info(bool to_uci = false); extern const std::string engine_info(bool to_uci = false);
extern int cpu_count();
extern void timed_wait(WaitCondition&, Lock&, int); extern void timed_wait(WaitCondition&, Lock&, int);
extern void prefetch(char* addr); extern void prefetch(char* addr);
extern void start_logger(bool b); extern void start_logger(bool b);
@@ -46,7 +45,7 @@ struct Log : public std::ofstream {
namespace Time { namespace Time {
typedef int64_t point; typedef int64_t point;
point now(); inline point now() { return system_time_to_msec(); }
} }
@@ -66,4 +65,4 @@ std::ostream& operator<<(std::ostream&, SyncCout);
#define sync_cout std::cout << io_lock #define sync_cout std::cout << io_lock
#define sync_endl std::endl << io_unlock #define sync_endl std::endl << io_unlock
#endif // !defined(MISC_H_INCLUDED) #endif // #ifndef MISC_H_INCLUDED
+64 -76
View File
@@ -24,15 +24,15 @@
/// Simple macro to wrap a very common while loop, no facny, no flexibility, /// Simple macro to wrap a very common while loop, no facny, no flexibility,
/// hardcoded names 'mlist' and 'from'. /// hardcoded names 'mlist' and 'from'.
#define SERIALIZE(b) while (b) (*mlist++).move = make_move(from, pop_lsb(&b)) #define SERIALIZE(b) while (b) (mlist++)->move = make_move(from, pop_lsb(&b))
/// Version used for pawns, where the 'from' square is given as a delta from the 'to' square /// Version used for pawns, where the 'from' square is given as a delta from the 'to' square
#define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_lsb(&b); \ #define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_lsb(&b); \
(*mlist++).move = make_move(to - (d), to); } (mlist++)->move = make_move(to - (d), to); }
namespace { namespace {
template<CastlingSide Side, bool Checks, bool Chess960> template<CastlingSide Side, bool Checks, bool Chess960>
MoveStack* generate_castle(const Position& pos, MoveStack* mlist, Color us) { ExtMove* generate_castle(const Position& pos, ExtMove* mlist, Color us) {
if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side))) if (pos.castle_impeded(us, Side) || !pos.can_castle(make_castle_right(us, Side)))
return mlist; return mlist;
@@ -59,7 +59,7 @@ namespace {
if (Chess960 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies)) if (Chess960 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & enemies))
return mlist; return mlist;
(*mlist++).move = make<CASTLE>(kfrom, rfrom); (mlist++)->move = make<CASTLE>(kfrom, rfrom);
if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos))) if (Checks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos)))
mlist--; mlist--;
@@ -68,42 +68,30 @@ namespace {
} }
template<Square Delta>
inline Bitboard move_pawns(Bitboard p) {
return Delta == DELTA_N ? p << 8
: Delta == DELTA_S ? p >> 8
: Delta == DELTA_NE ? (p & ~FileHBB) << 9
: Delta == DELTA_SE ? (p & ~FileHBB) >> 7
: Delta == DELTA_NW ? (p & ~FileABB) << 7
: Delta == DELTA_SW ? (p & ~FileABB) >> 9 : 0;
}
template<GenType Type, Square Delta> template<GenType Type, Square Delta>
inline MoveStack* generate_promotions(MoveStack* mlist, Bitboard pawnsOn7, inline ExtMove* generate_promotions(ExtMove* mlist, Bitboard pawnsOn7,
Bitboard target, const CheckInfo* ci) { Bitboard target, const CheckInfo* ci) {
Bitboard b = move_pawns<Delta>(pawnsOn7) & target; Bitboard b = shift_bb<Delta>(pawnsOn7) & target;
while (b) while (b)
{ {
Square to = pop_lsb(&b); Square to = pop_lsb(&b);
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
(*mlist++).move = make<PROMOTION>(to - Delta, to, QUEEN); (mlist++)->move = make<PROMOTION>(to - Delta, to, QUEEN);
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
{ {
(*mlist++).move = make<PROMOTION>(to - Delta, to, ROOK); (mlist++)->move = make<PROMOTION>(to - Delta, to, ROOK);
(*mlist++).move = make<PROMOTION>(to - Delta, to, BISHOP); (mlist++)->move = make<PROMOTION>(to - Delta, to, BISHOP);
(*mlist++).move = make<PROMOTION>(to - Delta, to, KNIGHT); (mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
} }
// Knight-promotion is the only one that can give a direct check not // Knight-promotion is the only one that can give a direct check not
// already included in the queen-promotion. // already included in the queen-promotion.
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq)) if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
(*mlist++).move = make<PROMOTION>(to - Delta, to, KNIGHT); (mlist++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
else else
(void)ci; // Silence a warning under MSVC (void)ci; // Silence a warning under MSVC
} }
@@ -113,7 +101,7 @@ namespace {
template<Color Us, GenType Type> template<Color Us, GenType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, ExtMove* generate_pawn_moves(const Position& pos, ExtMove* mlist,
Bitboard target, const CheckInfo* ci) { Bitboard target, const CheckInfo* ci) {
// Compute our parametrized parameters at compile time, named according to // Compute our parametrized parameters at compile time, named according to
@@ -122,9 +110,9 @@ namespace {
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB); const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square UP = (Us == WHITE ? DELTA_N : DELTA_S); const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
const Square RIGHT = (Us == WHITE ? DELTA_NE : DELTA_SW); const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square LEFT = (Us == WHITE ? DELTA_NW : DELTA_SE); const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
Bitboard b1, b2, dc1, dc2, emptySquares; Bitboard b1, b2, dc1, dc2, emptySquares;
@@ -139,8 +127,8 @@ namespace {
{ {
emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces()); emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
b1 = move_pawns<UP>(pawnsNotOn7) & emptySquares; b1 = shift_bb<Up>(pawnsNotOn7) & emptySquares;
b2 = move_pawns<UP>(b1 & TRank3BB) & emptySquares; b2 = shift_bb<Up>(b1 & TRank3BB) & emptySquares;
if (Type == EVASIONS) // Consider only blocking squares if (Type == EVASIONS) // Consider only blocking squares
{ {
@@ -159,16 +147,16 @@ namespace {
// promotion has been already generated among captures. // promotion has been already generated among captures.
if (pawnsNotOn7 & ci->dcCandidates) if (pawnsNotOn7 & ci->dcCandidates)
{ {
dc1 = move_pawns<UP>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq); dc1 = shift_bb<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares; dc2 = shift_bb<Up>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1; b1 |= dc1;
b2 |= dc2; b2 |= dc2;
} }
} }
SERIALIZE_PAWNS(b1, UP); SERIALIZE_PAWNS(b1, Up);
SERIALIZE_PAWNS(b2, UP + UP); SERIALIZE_PAWNS(b2, Up + Up);
} }
// Promotions and underpromotions // Promotions and underpromotions
@@ -180,19 +168,19 @@ namespace {
if (Type == EVASIONS) if (Type == EVASIONS)
emptySquares &= target; emptySquares &= target;
mlist = generate_promotions<Type, RIGHT>(mlist, pawnsOn7, enemies, ci); mlist = generate_promotions<Type, Right>(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, LEFT>(mlist, pawnsOn7, enemies, ci); mlist = generate_promotions<Type, Left >(mlist, pawnsOn7, enemies, ci);
mlist = generate_promotions<Type, UP>(mlist, pawnsOn7, emptySquares, ci); mlist = generate_promotions<Type, Up>(mlist, pawnsOn7, emptySquares, ci);
} }
// Standard and en-passant captures // Standard and en-passant captures
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{ {
b1 = move_pawns<RIGHT>(pawnsNotOn7) & enemies; b1 = shift_bb<Right>(pawnsNotOn7) & enemies;
b2 = move_pawns<LEFT >(pawnsNotOn7) & enemies; b2 = shift_bb<Left >(pawnsNotOn7) & enemies;
SERIALIZE_PAWNS(b1, RIGHT); SERIALIZE_PAWNS(b1, Right);
SERIALIZE_PAWNS(b2, LEFT); SERIALIZE_PAWNS(b2, Left);
if (pos.ep_square() != SQ_NONE) if (pos.ep_square() != SQ_NONE)
{ {
@@ -201,7 +189,7 @@ namespace {
// An en passant capture can be an evasion only if the checking piece // An en passant capture can be an evasion only if the checking piece
// is the double pushed pawn and so is in the target. Otherwise this // is the double pushed pawn and so is in the target. Otherwise this
// is a discovery check and we are forced to do otherwise. // is a discovery check and we are forced to do otherwise.
if (Type == EVASIONS && !(target & (pos.ep_square() - UP))) if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
return mlist; return mlist;
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them); b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
@@ -209,7 +197,7 @@ namespace {
assert(b1); assert(b1);
while (b1) while (b1)
(*mlist++).move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square()); (mlist++)->move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
} }
} }
@@ -218,12 +206,12 @@ namespace {
template<PieceType Pt, bool Checks> FORCE_INLINE template<PieceType Pt, bool Checks> FORCE_INLINE
MoveStack* generate_moves(const Position& pos, MoveStack* mlist, Color us, ExtMove* generate_moves(const Position& pos, ExtMove* mlist, Color us,
Bitboard target, const CheckInfo* ci) { Bitboard target, const CheckInfo* ci) {
assert(Pt != KING && Pt != PAWN); assert(Pt != KING && Pt != PAWN);
const Square* pl = pos.piece_list(us, Pt); const Square* pl = pos.list<Pt>(us);
for (Square from = *pl; from != SQ_NONE; from = *++pl) for (Square from = *pl; from != SQ_NONE; from = *++pl)
{ {
@@ -233,7 +221,7 @@ namespace {
&& !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt])) && !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt]))
continue; continue;
if (ci->dcCandidates && (ci->dcCandidates & from)) if (unlikely(ci->dcCandidates) && (ci->dcCandidates & from))
continue; continue;
} }
@@ -249,38 +237,36 @@ namespace {
} }
template<GenType Type> FORCE_INLINE template<Color Us, GenType Type> FORCE_INLINE
MoveStack* generate_all(const Position& pos, MoveStack* mlist, Color us, ExtMove* generate_all(const Position& pos, ExtMove* mlist, Bitboard target,
Bitboard target, const CheckInfo* ci = NULL) { const CheckInfo* ci = NULL) {
const bool Checks = Type == QUIET_CHECKS; const bool Checks = Type == QUIET_CHECKS;
mlist = (us == WHITE ? generate_pawn_moves<WHITE, Type>(pos, mlist, target, ci) mlist = generate_pawn_moves<Us, Type>(pos, mlist, target, ci);
: generate_pawn_moves<BLACK, Type>(pos, mlist, target, ci)); mlist = generate_moves<KNIGHT, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves<BISHOP, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves<KNIGHT, Checks>(pos, mlist, us, target, ci); mlist = generate_moves< ROOK, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves<BISHOP, Checks>(pos, mlist, us, target, ci); mlist = generate_moves< QUEEN, Checks>(pos, mlist, Us, target, ci);
mlist = generate_moves<ROOK, Checks>(pos, mlist, us, target, ci);
mlist = generate_moves<QUEEN, Checks>(pos, mlist, us, target, ci);
if (Type != QUIET_CHECKS && Type != EVASIONS) if (Type != QUIET_CHECKS && Type != EVASIONS)
{ {
Square from = pos.king_square(us); Square from = pos.king_square(Us);
Bitboard b = pos.attacks_from<KING>(from) & target; Bitboard b = pos.attacks_from<KING>(from) & target;
SERIALIZE(b); SERIALIZE(b);
} }
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(us)) if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
{ {
if (pos.is_chess960()) if (pos.is_chess960())
{ {
mlist = generate_castle<KING_SIDE, Checks, true>(pos, mlist, us); mlist = generate_castle< KING_SIDE, Checks, true>(pos, mlist, Us);
mlist = generate_castle<QUEEN_SIDE, Checks, true>(pos, mlist, us); mlist = generate_castle<QUEEN_SIDE, Checks, true>(pos, mlist, Us);
} }
else else
{ {
mlist = generate_castle<KING_SIDE, Checks, false>(pos, mlist, us); mlist = generate_castle< KING_SIDE, Checks, false>(pos, mlist, Us);
mlist = generate_castle<QUEEN_SIDE, Checks, false>(pos, mlist, us); mlist = generate_castle<QUEEN_SIDE, Checks, false>(pos, mlist, Us);
} }
} }
@@ -301,7 +287,7 @@ namespace {
/// non-captures. Returns a pointer to the end of the move list. /// non-captures. Returns a pointer to the end of the move list.
template<GenType Type> template<GenType Type>
MoveStack* generate(const Position& pos, MoveStack* mlist) { ExtMove* generate(const Position& pos, ExtMove* mlist) {
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS); assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
assert(!pos.checkers()); assert(!pos.checkers());
@@ -312,22 +298,24 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) {
: Type == QUIETS ? ~pos.pieces() : Type == QUIETS ? ~pos.pieces()
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0; : Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
return generate_all<Type>(pos, mlist, us, target); return us == WHITE ? generate_all<WHITE, Type>(pos, mlist, target)
: generate_all<BLACK, Type>(pos, mlist, target);
} }
// Explicit template instantiations // Explicit template instantiations
template MoveStack* generate<CAPTURES>(const Position&, MoveStack*); template ExtMove* generate<CAPTURES>(const Position&, ExtMove*);
template MoveStack* generate<QUIETS>(const Position&, MoveStack*); template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
template MoveStack* generate<NON_EVASIONS>(const Position&, MoveStack*); template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight /// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight
/// underpromotions that give check. Returns a pointer to the end of the move list. /// underpromotions that give check. Returns a pointer to the end of the move list.
template<> template<>
MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) { ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* mlist) {
assert(!pos.checkers()); assert(!pos.checkers());
Color us = pos.side_to_move();
CheckInfo ci(pos); CheckInfo ci(pos);
Bitboard dc = ci.dcCandidates; Bitboard dc = ci.dcCandidates;
@@ -347,21 +335,21 @@ MoveStack* generate<QUIET_CHECKS>(const Position& pos, MoveStack* mlist) {
SERIALIZE(b); SERIALIZE(b);
} }
return generate_all<QUIET_CHECKS>(pos, mlist, pos.side_to_move(), ~pos.pieces(), &ci); return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, mlist, ~pos.pieces(), &ci)
: generate_all<BLACK, QUIET_CHECKS>(pos, mlist, ~pos.pieces(), &ci);
} }
/// generate<EVASIONS> generates all pseudo-legal check evasions when the side /// generate<EVASIONS> generates all pseudo-legal check evasions when the side
/// to move is in check. Returns a pointer to the end of the move list. /// to move is in check. Returns a pointer to the end of the move list.
template<> template<>
MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) { ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* mlist) {
assert(pos.checkers()); assert(pos.checkers());
Square from, checksq;
int checkersCnt = 0; int checkersCnt = 0;
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Square ksq = pos.king_square(us); Square ksq = pos.king_square(us), from = ksq /* For SERIALIZE */, checksq;
Bitboard sliderAttacks = 0; Bitboard sliderAttacks = 0;
Bitboard b = pos.checkers(); Bitboard b = pos.checkers();
@@ -400,25 +388,25 @@ MoveStack* generate<EVASIONS>(const Position& pos, MoveStack* mlist) {
// Generate evasions for king, capture and non capture moves // Generate evasions for king, capture and non capture moves
b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks; b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
from = ksq;
SERIALIZE(b); SERIALIZE(b);
if (checkersCnt > 1) if (checkersCnt > 1)
return mlist; // Double check, only a king move can save the day return mlist; // Double check, only a king move can save the day
// Generate blocking evasions or captures of the checking piece // Generate blocking evasions or captures of the checking piece
Bitboard target = between_bb(checksq, ksq) | pos.checkers(); Bitboard target = between_bb(checksq, ksq) | checksq;
return generate_all<EVASIONS>(pos, mlist, us, target); return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, mlist, target)
: generate_all<BLACK, EVASIONS>(pos, mlist, target);
} }
/// generate<LEGAL> generates all the legal moves in the given position /// generate<LEGAL> generates all the legal moves in the given position
template<> template<>
MoveStack* generate<LEGAL>(const Position& pos, MoveStack* mlist) { ExtMove* generate<LEGAL>(const Position& pos, ExtMove* mlist) {
MoveStack *end, *cur = mlist; ExtMove *end, *cur = mlist;
Bitboard pinned = pos.pinned_pieces(); Bitboard pinned = pos.pinned_pieces();
Square ksq = pos.king_square(pos.side_to_move()); Square ksq = pos.king_square(pos.side_to_move());
+8 -9
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(MOVEGEN_H_INCLUDED) #ifndef MOVEGEN_H_INCLUDED
#define MOVEGEN_H_INCLUDED #define MOVEGEN_H_INCLUDED
#include "types.h" #include "types.h"
@@ -34,26 +34,25 @@ enum GenType {
class Position; class Position;
template<GenType> template<GenType>
MoveStack* generate(const Position& pos, MoveStack* mlist); ExtMove* generate(const Position& pos, ExtMove* mlist);
/// The MoveList struct is a simple wrapper around generate(), sometimes comes /// The MoveList struct is a simple wrapper around generate(), sometimes comes
/// handy to use this class instead of the low level generate() function. /// handy to use this class instead of the low level generate() function.
template<GenType T> template<GenType T>
struct MoveList { struct MoveList {
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) {} explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) { last->move = MOVE_NONE; }
void operator++() { cur++; } void operator++() { cur++; }
bool end() const { return cur == last; } Move operator*() const { return cur->move; }
Move move() const { return cur->move; }
size_t size() const { return last - mlist; } size_t size() const { return last - mlist; }
bool contains(Move m) const { bool contains(Move m) const {
for (const MoveStack* it(mlist); it != last; ++it) if (it->move == m) return true; for (const ExtMove* it(mlist); it != last; ++it) if (it->move == m) return true;
return false; return false;
} }
private: private:
MoveStack mlist[MAX_MOVES]; ExtMove mlist[MAX_MOVES];
MoveStack *cur, *last; ExtMove *cur, *last;
}; };
#endif // !defined(MOVEGEN_H_INCLUDED) #endif // #ifndef MOVEGEN_H_INCLUDED
+46 -46
View File
@@ -25,7 +25,7 @@
namespace { namespace {
enum Sequencer { enum Stages {
MAIN_SEARCH, CAPTURES_S1, KILLERS_S1, QUIETS_1_S1, QUIETS_2_S1, BAD_CAPTURES_S1, MAIN_SEARCH, CAPTURES_S1, KILLERS_S1, QUIETS_1_S1, QUIETS_2_S1, BAD_CAPTURES_S1,
EVASION, EVASIONS_S2, EVASION, EVASIONS_S2,
QSEARCH_0, CAPTURES_S3, QUIET_CHECKS_S3, QSEARCH_0, CAPTURES_S3, QUIET_CHECKS_S3,
@@ -36,9 +36,9 @@ namespace {
}; };
// Our insertion sort, guaranteed to be stable, as is needed // Our insertion sort, guaranteed to be stable, as is needed
void insertion_sort(MoveStack* begin, MoveStack* end) void insertion_sort(ExtMove* begin, ExtMove* end)
{ {
MoveStack tmp, *p, *q; ExtMove tmp, *p, *q;
for (p = begin + 1; p < end; ++p) for (p = begin + 1; p < end; ++p)
{ {
@@ -51,12 +51,12 @@ namespace {
// Unary predicate used by std::partition to split positive scores from remaining // Unary predicate used by std::partition to split positive scores from remaining
// ones so to sort separately the two sets, and with the second sort delayed. // ones so to sort separately the two sets, and with the second sort delayed.
inline bool has_positive_score(const MoveStack& ms) { return ms.score > 0; } inline bool has_positive_score(const ExtMove& ms) { return ms.score > 0; }
// Picks and moves to the front the best move in the range [begin, end), // Picks and moves to the front the best move in the range [begin, end),
// it is faster than sorting all the moves in advance when moves are few, as // it is faster than sorting all the moves in advance when moves are few, as
// normally are the possible captures. // normally are the possible captures.
inline MoveStack* pick_best(MoveStack* begin, MoveStack* end) inline ExtMove* pick_best(ExtMove* begin, ExtMove* end)
{ {
std::swap(*begin, *std::max_element(begin, end)); std::swap(*begin, *std::max_element(begin, end));
return begin; return begin;
@@ -70,53 +70,40 @@ namespace {
/// search captures, promotions and some checks) and about how important good /// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node. /// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
Search::Stack* s, Value beta) : pos(p), Hist(h), depth(d) { Move* cm, Search::Stack* s) : pos(p), history(h), depth(d) {
assert(d > DEPTH_ZERO); assert(d > DEPTH_ZERO);
captureThreshold = 0;
cur = end = moves; cur = end = moves;
endBadCaptures = moves + MAX_MOVES - 1; endBadCaptures = moves + MAX_MOVES - 1;
countermoves = cm;
ss = s; ss = s;
if (p.checkers()) if (p.checkers())
phase = EVASION; stage = EVASION;
else else
{ stage = MAIN_SEARCH;
phase = MAIN_SEARCH;
killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1];
// Consider sligtly negative captures as good if at low depth and far from beta
if (ss && ss->staticEval < beta - PawnValueMg && d < 3 * ONE_PLY)
captureThreshold = -PawnValueMg;
// Consider negative captures as good if still enough to reach beta
else if (ss && ss->staticEval > beta)
captureThreshold = beta - ss->staticEval;
}
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE); ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
end += (ttMove != MOVE_NONE); end += (ttMove != MOVE_NONE);
} }
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
Square sq) : pos(p), Hist(h), cur(moves), end(moves) { Square sq) : pos(p), history(h), cur(moves), end(moves) {
assert(d <= DEPTH_ZERO); assert(d <= DEPTH_ZERO);
if (p.checkers()) if (p.checkers())
phase = EVASION; stage = EVASION;
else if (d > DEPTH_QS_NO_CHECKS) else if (d > DEPTH_QS_NO_CHECKS)
phase = QSEARCH_0; stage = QSEARCH_0;
else if (d > DEPTH_QS_RECAPTURES) else if (d > DEPTH_QS_RECAPTURES)
{ {
phase = QSEARCH_1; stage = QSEARCH_1;
// Skip TT move if is not a capture or a promotion, this avoids qsearch // Skip TT move if is not a capture or a promotion, this avoids qsearch
// tree explosion due to a possible perpetual check or similar rare cases // tree explosion due to a possible perpetual check or similar rare cases
@@ -126,7 +113,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
} }
else else
{ {
phase = RECAPTURE; stage = RECAPTURE;
recaptureSquare = sq; recaptureSquare = sq;
ttm = MOVE_NONE; ttm = MOVE_NONE;
} }
@@ -135,12 +122,12 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
end += (ttMove != MOVE_NONE); end += (ttMove != MOVE_NONE);
} }
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType pt) MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, PieceType pt)
: pos(p), Hist(h), cur(moves), end(moves) { : pos(p), history(h), cur(moves), end(moves) {
assert(!pos.checkers()); assert(!pos.checkers());
phase = PROBCUT; stage = PROBCUT;
// In ProbCut we generate only captures better than parent's captured piece // In ProbCut we generate only captures better than parent's captured piece
captureThreshold = PieceValue[MG][pt]; captureThreshold = PieceValue[MG][pt];
@@ -172,7 +159,7 @@ void MovePicker::score<CAPTURES>() {
// some SEE calls in case we get a cutoff (idea from Pablo Vazquez). // some SEE calls in case we get a cutoff (idea from Pablo Vazquez).
Move m; Move m;
for (MoveStack* it = moves; it != end; ++it) for (ExtMove* it = moves; it != end; ++it)
{ {
m = it->move; m = it->move;
it->score = PieceValue[MG][pos.piece_on(to_sq(m))] it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
@@ -191,10 +178,10 @@ void MovePicker::score<QUIETS>() {
Move m; Move m;
for (MoveStack* it = moves; it != end; ++it) for (ExtMove* it = moves; it != end; ++it)
{ {
m = it->move; m = it->move;
it->score = Hist[pos.piece_moved(m)][to_sq(m)]; it->score = history[pos.piece_moved(m)][to_sq(m)];
} }
} }
@@ -206,17 +193,17 @@ void MovePicker::score<EVASIONS>() {
Move m; Move m;
int seeScore; int seeScore;
for (MoveStack* it = moves; it != end; ++it) for (ExtMove* it = moves; it != end; ++it)
{ {
m = it->move; m = it->move;
if ((seeScore = pos.see_sign(m)) < 0) if ((seeScore = pos.see_sign(m)) < 0)
it->score = seeScore - History::Max; // At the bottom it->score = seeScore - HistoryStats::Max; // At the bottom
else if (pos.is_capture(m)) else if (pos.is_capture(m))
it->score = PieceValue[MG][pos.piece_on(to_sq(m))] it->score = PieceValue[MG][pos.piece_on(to_sq(m))]
- type_of(pos.piece_moved(m)) + History::Max; - type_of(pos.piece_moved(m)) + HistoryStats::Max;
else else
it->score = Hist[pos.piece_moved(m)][to_sq(m)]; it->score = history[pos.piece_moved(m)][to_sq(m)];
} }
} }
@@ -228,7 +215,7 @@ void MovePicker::generate_next() {
cur = moves; cur = moves;
switch (++phase) { switch (++stage) {
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6: case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
end = generate<CAPTURES>(pos, moves); end = generate<CAPTURES>(pos, moves);
@@ -238,6 +225,19 @@ void MovePicker::generate_next() {
case KILLERS_S1: case KILLERS_S1:
cur = killers; cur = killers;
end = cur + 2; end = cur + 2;
killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1];
killers[2].move = killers[3].move = MOVE_NONE;
// Be sure countermoves are different from killers
for (int i = 0; i < 2; i++)
if (countermoves[i] != cur->move && countermoves[i] != (cur+1)->move)
(end++)->move = countermoves[i];
if (countermoves[1] && countermoves[1] == countermoves[0]) // Due to SMP races
killers[3].move = MOVE_NONE;
return; return;
case QUIETS_1_S1: case QUIETS_1_S1:
@@ -271,7 +271,7 @@ void MovePicker::generate_next() {
return; return;
case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE:
phase = STOP; stage = STOP;
case STOP: case STOP:
end = cur + 1; // Avoid another next_phase() call end = cur + 1; // Avoid another next_phase() call
return; return;
@@ -296,7 +296,7 @@ Move MovePicker::next_move<false>() {
while (cur == end) while (cur == end)
generate_next(); generate_next();
switch (phase) { switch (stage) {
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT:
cur++; cur++;
@@ -306,9 +306,7 @@ Move MovePicker::next_move<false>() {
move = pick_best(cur++, end)->move; move = pick_best(cur++, end)->move;
if (move != ttMove) if (move != ttMove)
{ {
assert(captureThreshold <= 0); // Otherwise we cannot use see_sign() if (pos.see_sign(move) >= 0)
if (pos.see_sign(move) >= captureThreshold)
return move; return move;
// Losing capture, move it to the tail of the array // Losing capture, move it to the tail of the array
@@ -329,7 +327,9 @@ Move MovePicker::next_move<false>() {
move = (cur++)->move; move = (cur++)->move;
if ( move != ttMove if ( move != ttMove
&& move != killers[0].move && move != killers[0].move
&& move != killers[1].move) && move != killers[1].move
&& move != killers[2].move
&& move != killers[3].move)
return move; return move;
break; break;
+36 -24
View File
@@ -17,11 +17,11 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined MOVEPICK_H_INCLUDED #ifndef MOVEPICK_H_INCLUDED
#define MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED
#include <algorithm> // For std::max #include <algorithm> // For std::max
#include <cstring> // For memset #include <cstring> // For std::memset
#include "movegen.h" #include "movegen.h"
#include "position.h" #include "position.h"
@@ -30,20 +30,29 @@
/// The Stats struct stores moves statistics. According to the template parameter /// The Stats struct stores moves statistics. According to the template parameter
/// the class can store both History and Gains type statistics. History records /// the class can store History, Gains and Countermoves. History records how often
/// how often different moves have been successful or unsuccessful during the /// different moves have been successful or unsuccessful during the current search
/// current search and is used for reduction and move ordering decisions. Gains /// and is used for reduction and move ordering decisions. Gains records the move's
/// records the move's best evaluation gain from one ply to the next and is used /// best evaluation gain from one ply to the next and is used for pruning decisions.
/// for pruning decisions. Entries are stored according only to moving piece and /// Countermoves store the move that refute a previous one. Entries are stored
/// destination square, in particular two moves with different origin but same /// according only to moving piece and destination square, hence two moves with
/// destination and same piece will be considered identical. /// different origin but same destination and piece will be considered identical.
template<bool Gain> template<bool Gain, typename T>
struct Stats { struct Stats {
static const Value Max = Value(2000); static const Value Max = Value(2000);
const Value* operator[](Piece p) const { return &table[p][0]; } const T* operator[](Piece p) const { return table[p]; }
void clear() { memset(table, 0, sizeof(table)); } void clear() { std::memset(table, 0, sizeof(table)); }
void update(Piece p, Square to, Move m) {
if (m == table[p][to].first)
return;
table[p][to].second = table[p][to].first;
table[p][to].first = m;
}
void update(Piece p, Square to, Value v) { void update(Piece p, Square to, Value v) {
@@ -55,11 +64,12 @@ struct Stats {
} }
private: private:
Value table[PIECE_NB][SQUARE_NB]; T table[PIECE_NB][SQUARE_NB];
}; };
typedef Stats<false> History; typedef Stats< true, Value> GainsStats;
typedef Stats<true> Gains; typedef Stats<false, Value> HistoryStats;
typedef Stats<false, std::pair<Move, Move> > CountermovesStats;
/// MovePicker class is used to pick one pseudo legal move at a time from the /// MovePicker class is used to pick one pseudo legal move at a time from the
@@ -74,9 +84,10 @@ class MovePicker {
MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC
public: public:
MovePicker(const Position&, Move, Depth, const History&, Search::Stack*, Value); MovePicker(const Position&, Move, Depth, const HistoryStats&, Square);
MovePicker(const Position&, Move, Depth, const History&, Square); MovePicker(const Position&, Move, const HistoryStats&, PieceType);
MovePicker(const Position&, Move, const History&, PieceType); MovePicker(const Position&, Move, Depth, const HistoryStats&, Move*, Search::Stack*);
template<bool SpNode> Move next_move(); template<bool SpNode> Move next_move();
private: private:
@@ -84,15 +95,16 @@ private:
void generate_next(); void generate_next();
const Position& pos; const Position& pos;
const History& Hist; const HistoryStats& history;
Search::Stack* ss; Search::Stack* ss;
Move* countermoves;
Depth depth; Depth depth;
Move ttMove; Move ttMove;
MoveStack killers[2]; ExtMove killers[4];
Square recaptureSquare; Square recaptureSquare;
int captureThreshold, phase; int captureThreshold, stage;
MoveStack *cur, *end, *endQuiets, *endBadCaptures; ExtMove *cur, *end, *endQuiets, *endBadCaptures;
MoveStack moves[MAX_MOVES]; ExtMove moves[MAX_MOVES];
}; };
#endif // !defined(MOVEPICK_H_INCLUDED) #endif // #ifndef MOVEPICK_H_INCLUDED
+3 -3
View File
@@ -89,9 +89,9 @@ Move move_from_uci(const Position& pos, string& str) {
if (str.length() == 5) // Junior could send promotion piece in uppercase if (str.length() == 5) // Junior could send promotion piece in uppercase
str[4] = char(tolower(str[4])); str[4] = char(tolower(str[4]));
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml) for (MoveList<LEGAL> it(pos); *it; ++it)
if (str == move_to_uci(ml.move(), pos.is_chess960())) if (str == move_to_uci(*it, pos.is_chess960()))
return ml.move(); return *it;
return MOVE_NONE; return MOVE_NONE;
} }
+2 -2
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(NOTATION_H_INCLUDED) #ifndef NOTATION_H_INCLUDED
#define NOTATION_H_INCLUDED #define NOTATION_H_INCLUDED
#include <string> #include <string>
@@ -32,4 +32,4 @@ const std::string move_to_uci(Move m, bool chess960);
const std::string move_to_san(Position& pos, Move m); const std::string move_to_san(Position& pos, Move m);
std::string pretty_pv(Position& pos, int depth, Value score, int64_t msecs, Move pv[]); std::string pretty_pv(Position& pos, int depth, Value score, int64_t msecs, Move pv[]);
#endif // !defined(NOTATION_H_INCLUDED) #endif // #ifndef NOTATION_H_INCLUDED
+52 -63
View File
@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include "bitboard.h" #include "bitboard.h"
@@ -30,49 +31,48 @@ namespace {
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by opposed flag and file // Doubled pawn penalty by opposed flag and file
const Score DoubledPawnPenalty[2][FILE_NB] = { const Score Doubled[2][FILE_NB] = {
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48), { S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }, S(23, 48), S(23, 48), S(20, 48), S(13, 43) },
{ S(13, 43), S(20, 48), S(23, 48), S(23, 48), { S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) }}; S(23, 48), S(23, 48), S(20, 48), S(13, 43) }};
// Isolated pawn penalty by opposed flag and file // Isolated pawn penalty by opposed flag and file
const Score IsolatedPawnPenalty[2][FILE_NB] = { const Score Isolated[2][FILE_NB] = {
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52), { 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(60, 52), S(60, 52), S(54, 52), S(37, 45) },
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35), { S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35), S(25, 30) }}; S(40, 35), S(40, 35), S(36, 35), S(25, 30) }};
// Backward pawn penalty by opposed flag and file // Backward pawn penalty by opposed flag and file
const Score BackwardPawnPenalty[2][FILE_NB] = { const Score Backward[2][FILE_NB] = {
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46), { S(30, 42), S(43, 46), S(49, 46), S(49, 46),
S(49, 46), S(49, 46), S(43, 46), S(30, 42) }, S(49, 46), S(49, 46), S(43, 46), S(30, 42) },
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31), { S(20, 28), S(29, 31), S(33, 31), S(33, 31),
S(33, 31), S(33, 31), S(29, 31), S(20, 28) }}; S(33, 31), S(33, 31), S(29, 31), S(20, 28) }};
// Pawn chain membership bonus by file // Pawn chain membership bonus by file
const Score ChainBonus[FILE_NB] = { const Score ChainMember[FILE_NB] = {
S(11,-1), S(13,-1), S(13,-1), S(14,-1), S(11,-1), S(13,-1), S(13,-1), S(14,-1),
S(14,-1), S(13,-1), S(13,-1), S(11,-1) S(14,-1), S(13,-1), S(13,-1), S(11,-1)
}; };
// Candidate passed pawn bonus by rank // Candidate passed pawn bonus by rank
const Score CandidateBonus[RANK_NB] = { const Score CandidatePassed[RANK_NB] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29), S( 0, 0), S( 6, 13), S(6,13), S(14,29),
S(34,68), S(83,166), S(0, 0), S( 0, 0) S(34,68), S(83,166), S(0, 0), S( 0, 0)
}; };
const Score PawnStructureWeight = S(233, 201); // Weakness of our pawn shelter in front of the king indexed by [rank]
const Value ShelterWeakness[RANK_NB] =
{ V(100), V(0), V(27), V(73), V(92), V(101), V(101) };
// Weakness of our pawn shelter in front of the king indexed by [king pawn][rank] // Danger of enemy pawns moving toward our king indexed by
const Value ShelterWeakness[2][RANK_NB] = // [no friendly pawn | pawn unblocked | pawn blocked][rank of enemy pawn]
{ { V(141), V(0), V(38), V(102), V(128), V(141), V(141) }, const Value StormDanger[3][RANK_NB] = {
{ V( 61), V(0), V(16), V( 44), V( 56), V( 61), V( 61) } }; { V( 0), V(64), V(128), V(51), V(26) },
{ V(26), V(32), V( 96), V(38), V(20) },
// Danger of enemy pawns moving toward our king indexed by [pawn blocked][rank] { V( 0), V( 0), V( 64), V(25), V(13) }};
const Value StormDanger[2][RANK_NB] =
{ { V(26), V(0), V(128), V(51), V(26) },
{ V(13), V(0), V( 64), V(25), V(13) } };
// Max bonus for king safety. Corresponds to start position with all the pawns // Max bonus for king safety. Corresponds to start position with all the pawns
// in front of the king and no enemy pawn on the horizont. // in front of the king and no enemy pawn on the horizont.
@@ -82,10 +82,12 @@ namespace {
#undef V #undef V
template<Color Us> template<Color Us>
Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Score evaluate(const Position& pos, Pawns::Entry* e) {
Bitboard theirPawns, Pawns::Entry* e) {
const Color Them = (Us == WHITE ? BLACK : WHITE); 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);
Bitboard b; Bitboard b;
Square s; Square s;
@@ -93,7 +95,17 @@ namespace {
Rank r; Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate; bool passed, isolated, doubled, opposed, chain, backward, candidate;
Score value = SCORE_ZERO; Score value = SCORE_ZERO;
const Square* pl = pos.piece_list(Us, PAWN); const Square* pl = pos.list<PAWN>(Us);
Bitboard ourPawns = pos.pieces(Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = 0;
e->kingSquares[Us] = SQ_NONE;
e->semiopenFiles[Us] = 0xFF;
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns);
e->pawnsOnSquares[Us][BLACK] = popcount<Max15>(ourPawns & DarkSquares);
e->pawnsOnSquares[Us][WHITE] = pos.count<PAWN>(Us) - e->pawnsOnSquares[Us][BLACK];
// Loop through all pawns of the current color and score each pawn // Loop through all pawns of the current color and score each pawn
while ((s = *pl++) != SQ_NONE) while ((s = *pl++) != SQ_NONE)
@@ -103,8 +115,8 @@ namespace {
f = file_of(s); f = file_of(s);
r = rank_of(s); r = rank_of(s);
// This file cannot be half open // This file cannot be semi-open
e->halfOpenFiles[Us] &= ~(1 << f); e->semiopenFiles[Us] &= ~(1 << f);
// Our rank plus previous one. Used for chain detection // Our rank plus previous one. Used for chain detection
b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1)); b = rank_bb(r) | rank_bb(Us == WHITE ? r - Rank(1) : r + Rank(1));
@@ -124,7 +136,7 @@ namespace {
// be backward. If there are friendly pawns behind on adjacent files // be backward. If there are friendly pawns behind on adjacent files
// or if can capture an enemy pawn it cannot be backward either. // or if can capture an enemy pawn it cannot be backward either.
if ( !(passed | isolated | chain) if ( !(passed | isolated | chain)
&& !(ourPawns & attack_span_mask(Them, s)) && !(ourPawns & pawn_attack_span(Them, s))
&& !(pos.attacks_from<PAWN>(s, Us) & theirPawns)) && !(pos.attacks_from<PAWN>(s, Us) & theirPawns))
{ {
// We now know that there are no friendly pawns beside or behind this // We now know that there are no friendly pawns beside or behind this
@@ -136,22 +148,22 @@ namespace {
// Note that we are sure to find something because pawn is not passed // Note that we are sure to find something because pawn is not passed
// nor isolated, so loop is potentially infinite, but it isn't. // nor isolated, so loop is potentially infinite, but it isn't.
while (!(b & (ourPawns | theirPawns))) while (!(b & (ourPawns | theirPawns)))
Us == WHITE ? b <<= 8 : b >>= 8; b = shift_bb<Up>(b);
// The friendly pawn needs to be at least two ranks closer than the // The friendly pawn needs to be at least two ranks closer than the
// enemy pawn in order to help the potentially backward pawn advance. // enemy pawn in order to help the potentially backward pawn advance.
backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns; backward = (b | shift_bb<Up>(b)) & theirPawns;
} }
assert(opposed | passed | (attack_span_mask(Us, s) & theirPawns)); assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns));
// A not passed pawn is a candidate to become passed if it is free to // A not passed pawn is a candidate to become passed if it is free to
// advance and if the number of friendly pawns beside or behind this // advance and if the number of friendly pawns beside or behind this
// pawn on adjacent files is higher or equal than the number of // pawn on adjacent files is higher or equal than the number of
// enemy pawns in the forward direction on the adjacent files. // enemy pawns in the forward direction on the adjacent files.
candidate = !(opposed | passed | backward | isolated) candidate = !(opposed | passed | backward | isolated)
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != 0 && (b = pawn_attack_span(Them, s + pawn_push(Us)) & ourPawns) != 0
&& popcount<Max15>(b) >= popcount<Max15>(attack_span_mask(Us, s) & theirPawns); && popcount<Max15>(b) >= popcount<Max15>(pawn_attack_span(Us, s) & theirPawns);
// Passed pawns will be properly scored in evaluation because we need // Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate passed pawns. Only the frontmost passed // full attack info to evaluate passed pawns. Only the frontmost passed
@@ -161,30 +173,25 @@ namespace {
// Score this pawn // Score this pawn
if (isolated) if (isolated)
value -= IsolatedPawnPenalty[opposed][f]; value -= Isolated[opposed][f];
if (doubled) if (doubled)
value -= DoubledPawnPenalty[opposed][f]; value -= Doubled[opposed][f];
if (backward) if (backward)
value -= BackwardPawnPenalty[opposed][f]; value -= Backward[opposed][f];
if (chain) if (chain)
value += ChainBonus[f]; value += ChainMember[f];
if (candidate) if (candidate)
value += CandidateBonus[relative_rank(Us, s)]; value += CandidatePassed[relative_rank(Us, s)];
} }
e->pawnsOnSquares[Us][BLACK] = popcount<Max15>(ourPawns & BlackSquares);
e->pawnsOnSquares[Us][WHITE] = pos.piece_count(Us, PAWN) - e->pawnsOnSquares[Us][BLACK];
e->pawnsOnSquares[Them][BLACK] = popcount<Max15>(theirPawns & BlackSquares);
e->pawnsOnSquares[Them][WHITE] = pos.piece_count(Them, PAWN) - e->pawnsOnSquares[Them][BLACK];
return value; return value;
} }
}
} // namespace
namespace Pawns { namespace Pawns {
@@ -197,27 +204,11 @@ Entry* probe(const Position& pos, Table& entries) {
Key key = pos.pawn_key(); Key key = pos.pawn_key();
Entry* e = entries[key]; Entry* e = entries[key];
// If e->key matches the position's pawn hash key, it means that we
// have analysed this pawn structure before, and we can simply return
// the information we found the last time instead of recomputing it.
if (e->key == key) if (e->key == key)
return e; return e;
e->key = key; e->key = key;
e->passedPawns[WHITE] = e->passedPawns[BLACK] = 0; e->value = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e);
e->kingSquares[WHITE] = e->kingSquares[BLACK] = SQ_NONE;
e->halfOpenFiles[WHITE] = e->halfOpenFiles[BLACK] = 0xFF;
Bitboard wPawns = pos.pieces(WHITE, PAWN);
Bitboard bPawns = pos.pieces(BLACK, PAWN);
e->pawnAttacks[WHITE] = ((wPawns & ~FileHBB) << 9) | ((wPawns & ~FileABB) << 7);
e->pawnAttacks[BLACK] = ((bPawns & ~FileHBB) >> 7) | ((bPawns & ~FileABB) >> 9);
e->value = evaluate_pawns<WHITE>(pos, wPawns, bPawns, e)
- evaluate_pawns<BLACK>(pos, bPawns, wPawns, e);
e->value = apply_weight(e->value, PawnStructureWeight);
return e; return e;
} }
@@ -231,8 +222,8 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
Value safety = MaxSafetyBonus; Value safety = MaxSafetyBonus;
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, ksq) | rank_bb(ksq)); Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq));
Bitboard ourPawns = b & pos.pieces(Us) & ~rank_bb(ksq); Bitboard ourPawns = b & pos.pieces(Us);
Bitboard theirPawns = b & pos.pieces(Them); Bitboard theirPawns = b & pos.pieces(Them);
Rank rkUs, rkThem; Rank rkUs, rkThem;
File kf = file_of(ksq); File kf = file_of(ksq);
@@ -241,15 +232,13 @@ Value Entry::shelter_storm(const Position& pos, Square ksq) {
for (int f = kf - 1; f <= kf + 1; f++) for (int f = kf - 1; f <= kf + 1; f++)
{ {
// Shelter penalty is higher for the pawn in front of the king
b = ourPawns & FileBB[f]; b = ourPawns & FileBB[f];
rkUs = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1; rkUs = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1;
safety -= ShelterWeakness[f != kf][rkUs]; safety -= ShelterWeakness[rkUs];
// Storm danger is smaller if enemy pawn is blocked
b = theirPawns & FileBB[f]; b = theirPawns & FileBB[f];
rkThem = b ? rank_of(Us == WHITE ? lsb(b) : ~msb(b)) : RANK_1; rkThem = b ? relative_rank(Us, Us == WHITE ? lsb(b) : msb(b)) : RANK_1;
safety -= StormDanger[rkThem == rkUs + 1][rkThem]; safety -= StormDanger[rkUs == RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
} }
return safety; return safety;
+9 -7
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(PAWNS_H_INCLUDED) #ifndef PAWNS_H_INCLUDED
#define PAWNS_H_INCLUDED #define PAWNS_H_INCLUDED
#include "misc.h" #include "misc.h"
@@ -37,10 +37,12 @@ struct Entry {
Score pawns_value() const { return value; } Score pawns_value() const { return value; }
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
int file_is_half_open(Color c, File f) const { return halfOpenFiles[c] & (1 << int(f)); } int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(DarkSquares & s)]; }
int has_open_file_to_left(Color c, File f) const { return halfOpenFiles[c] & ((1 << int(f)) - 1); } int semiopen(Color c, File f) const { return semiopenFiles[c] & (1 << int(f)); }
int has_open_file_to_right(Color c, File f) const { return halfOpenFiles[c] & ~((1 << int(f+1)) - 1); } int semiopen_on_side(Color c, File f, bool left) const {
int pawns_on_same_color_squares(Color c, Square s) const { return pawnsOnSquares[c][!!(BlackSquares & s)]; }
return semiopenFiles[c] & (left ? ((1 << int(f)) - 1) : ~((1 << int(f+1)) - 1));
}
template<Color Us> template<Color Us>
Score king_safety(const Position& pos, Square ksq) { Score king_safety(const Position& pos, Square ksq) {
@@ -62,7 +64,7 @@ struct Entry {
int minKPdistance[COLOR_NB]; int minKPdistance[COLOR_NB];
int castleRights[COLOR_NB]; int castleRights[COLOR_NB];
Score value; Score value;
int halfOpenFiles[COLOR_NB]; int semiopenFiles[COLOR_NB];
Score kingSafety[COLOR_NB]; Score kingSafety[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; int pawnsOnSquares[COLOR_NB][COLOR_NB];
}; };
@@ -73,4 +75,4 @@ Entry* probe(const Position& pos, Table& entries);
} }
#endif // !defined(PAWNS_H_INCLUDED) #endif // #ifndef PAWNS_H_INCLUDED
+17 -14
View File
@@ -17,10 +17,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(PLATFORM_H_INCLUDED) #ifndef PLATFORM_H_INCLUDED
#define PLATFORM_H_INCLUDED #define PLATFORM_H_INCLUDED
#if defined(_MSC_VER) #ifdef _MSC_VER
// Disable some silly and noisy warning from MSVC compiler // Disable some silly and noisy warning from MSVC compiler
#pragma warning(disable: 4127) // Conditional expression is constant #pragma warning(disable: 4127) // Conditional expression is constant
@@ -40,16 +40,17 @@ typedef unsigned __int64 uint64_t;
#else #else
# include <inttypes.h> # include <inttypes.h>
# include <unistd.h> // Used by sysconf(_SC_NPROCESSORS_ONLN)
#endif #endif
#if !defined(_WIN32) && !defined(_WIN64) // Linux - Unix #ifndef _WIN32 // Linux - Unix
# include <sys/time.h> # include <sys/time.h>
typedef timeval sys_time_t;
inline void system_time(sys_time_t* t) { gettimeofday(t, NULL); } inline int64_t system_time_to_msec() {
inline int64_t time_to_msec(const sys_time_t& t) { return t.tv_sec * 1000LL + t.tv_usec / 1000; } timeval t;
gettimeofday(&t, NULL);
return t.tv_sec * 1000LL + t.tv_usec / 1000;
}
# include <pthread.h> # include <pthread.h>
typedef pthread_mutex_t Lock; typedef pthread_mutex_t Lock;
@@ -66,18 +67,20 @@ typedef void*(*pt_start_fn)(void*);
# define cond_signal(x) pthread_cond_signal(&(x)) # define cond_signal(x) pthread_cond_signal(&(x))
# define cond_wait(x,y) pthread_cond_wait(&(x),&(y)) # define cond_wait(x,y) pthread_cond_wait(&(x),&(y))
# define cond_timedwait(x,y,z) pthread_cond_timedwait(&(x),&(y),z) # define cond_timedwait(x,y,z) pthread_cond_timedwait(&(x),&(y),z)
# define thread_create(x,f,t) !pthread_create(&(x),NULL,(pt_start_fn)f,t) # define thread_create(x,f,t) pthread_create(&(x),NULL,(pt_start_fn)f,t)
# define thread_join(x) pthread_join(x, NULL) # define thread_join(x) pthread_join(x, NULL)
#else // Windows and MinGW #else // Windows and MinGW
# include <sys/timeb.h> # include <sys/timeb.h>
typedef _timeb sys_time_t;
inline void system_time(sys_time_t* t) { _ftime(t); } inline int64_t system_time_to_msec() {
inline int64_t time_to_msec(const sys_time_t& t) { return t.time * 1000LL + t.millitm; } _timeb t;
_ftime(&t);
return t.time * 1000LL + t.millitm;
}
#if !defined(NOMINMAX) #ifndef NOMINMAX
# define NOMINMAX // disable macros min() and max() # define NOMINMAX // disable macros min() and max()
#endif #endif
@@ -105,9 +108,9 @@ inline DWORD* dwWin9xKludge() { static DWORD dw; return &dw; }
# define cond_signal(x) SetEvent(x) # define cond_signal(x) SetEvent(x)
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); } # define cond_wait(x,y) { lock_release(y); WaitForSingleObject(x, INFINITE); lock_grab(y); }
# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); } # define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(x,z); lock_grab(y); }
# define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,dwWin9xKludge()), x != NULL) # define thread_create(x,f,t) (x = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)f,t,0,dwWin9xKludge()))
# define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); } # define thread_join(x) { WaitForSingleObject(x, INFINITE); CloseHandle(x); }
#endif #endif
#endif // !defined(PLATFORM_H_INCLUDED) #endif // #ifndef PLATFORM_H_INCLUDED
+160 -293
View File
@@ -17,12 +17,12 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <algorithm>
#include "bitcount.h" #include "bitcount.h"
#include "movegen.h" #include "movegen.h"
@@ -41,7 +41,7 @@ static const string PieceToChar(" PNBRQK pnbrqk");
CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
Score pieceSquareTable[PIECE_NB][SQUARE_NB]; Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
Value PieceValue[PHASE_NB][PIECE_NB] = { Value PieceValue[PHASE_NB][PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } }; { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
@@ -53,69 +53,24 @@ Key enpassant[FILE_NB];
Key castle[CASTLE_RIGHT_NB]; Key castle[CASTLE_RIGHT_NB];
Key side; Key side;
Key exclusion; Key exclusion;
/// init() initializes at startup the various arrays used to compute hash keys
/// and the piece square tables. The latter is a two-step operation: First, the
/// white halves of the tables are copied from PSQT[] tables. Second, the black
/// halves of the tables are initialized by flipping and changing the sign of
/// the white scores.
void init() {
RKISS rk;
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
psq[c][pt][s] = rk.rand<Key>();
for (File f = FILE_A; f <= FILE_H; f++)
enpassant[f] = rk.rand<Key>();
for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++)
{
Bitboard b = cr;
while (b)
{
Key k = castle[1ULL << pop_lsb(&b)];
castle[cr] ^= k ? k : rk.rand<Key>();
}
} }
side = rk.rand<Key>(); Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion;}
exclusion = rk.rand<Key>();
for (PieceType pt = PAWN; pt <= KING; pt++)
{
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
pieceSquareTable[make_piece(WHITE, pt)][ s] = (v + PSQT[pt][s]);
pieceSquareTable[make_piece(BLACK, pt)][~s] = -(v + PSQT[pt][s]);
}
}
}
} // namespace Zobrist
namespace { namespace {
/// next_attacker() is an helper function used by see() to locate the least // min_attacker() is an helper function used by see() to locate the least
/// valuable attacker for the side to move, remove the attacker we just found // valuable attacker for the side to move, remove the attacker we just found
/// from the 'occupied' bitboard and scan for new X-ray attacks behind it. // from the bitboards and scan for new X-ray attacks behind it.
template<int Pt> FORCE_INLINE template<int Pt> FORCE_INLINE
PieceType next_attacker(const Bitboard* bb, const Square& to, const Bitboard& stmAttackers, PieceType min_attacker(const Bitboard* bb, const Square& to, const Bitboard& stmAttackers,
Bitboard& occupied, Bitboard& attackers) { Bitboard& occupied, Bitboard& attackers) {
if (stmAttackers & bb[Pt])
{
Bitboard b = stmAttackers & bb[Pt]; Bitboard b = stmAttackers & bb[Pt];
if (!b)
return min_attacker<Pt+1>(bb, to, stmAttackers, occupied, attackers);
occupied ^= b & ~(b - 1); occupied ^= b & ~(b - 1);
if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN) if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN)
@@ -124,13 +79,12 @@ PieceType next_attacker(const Bitboard* bb, const Square& to, const Bitboard& st
if (Pt == ROOK || Pt == QUEEN) if (Pt == ROOK || Pt == QUEEN)
attackers |= attacks_bb<ROOK>(to, occupied) & (bb[ROOK] | bb[QUEEN]); attackers |= attacks_bb<ROOK>(to, occupied) & (bb[ROOK] | bb[QUEEN]);
attackers &= occupied; // After X-ray that may add already processed pieces
return (PieceType)Pt; return (PieceType)Pt;
} }
return next_attacker<Pt+1>(bb, to, stmAttackers, occupied, attackers);
}
template<> FORCE_INLINE template<> FORCE_INLINE
PieceType next_attacker<KING>(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) { PieceType min_attacker<KING>(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) {
return KING; // No need to update bitboards, it is the last cycle return KING; // No need to update bitboards, it is the last cycle
} }
@@ -156,13 +110,60 @@ CheckInfo::CheckInfo(const Position& pos) {
} }
/// Position::init() initializes at startup the various arrays used to compute
/// hash keys and the piece square tables. The latter is a two-step operation:
/// First, the white halves of the tables are copied from PSQT[] tables. Second,
/// the black halves of the tables are initialized by flipping and changing the
/// sign of the white scores.
void Position::init() {
RKISS rk;
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
Zobrist::psq[c][pt][s] = rk.rand<Key>();
for (File f = FILE_A; f <= FILE_H; f++)
Zobrist::enpassant[f] = rk.rand<Key>();
for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++)
{
Bitboard b = cr;
while (b)
{
Key k = Zobrist::castle[1ULL << pop_lsb(&b)];
Zobrist::castle[cr] ^= k ? k : rk.rand<Key>();
}
}
Zobrist::side = rk.rand<Key>();
Zobrist::exclusion = rk.rand<Key>();
for (PieceType pt = PAWN; pt <= KING; pt++)
{
PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt];
PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt];
Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
psq[WHITE][pt][ s] = (v + PSQT[pt][s]);
psq[BLACK][pt][~s] = -(v + PSQT[pt][s]);
}
}
}
/// Position::operator=() creates a copy of 'pos'. We want the new born Position /// Position::operator=() creates a copy of 'pos'. We want the new born Position
/// object do not depend on any external data so we detach state pointer from /// object do not depend on any external data so we detach state pointer from
/// the source one. /// the source one.
Position& Position::operator=(const Position& pos) { Position& Position::operator=(const Position& pos) {
memcpy(this, &pos, sizeof(Position)); std::memcpy(this, &pos, sizeof(Position));
startState = *st; startState = *st;
st = &startState; st = &startState;
nodes = 0; nodes = 0;
@@ -231,7 +232,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
else if ((p = PieceToChar.find(token)) != string::npos) else if ((p = PieceToChar.find(token)) != string::npos)
{ {
put_piece(Piece(p), sq); put_piece(sq, color_of(Piece(p)), type_of(Piece(p)));
sq++; sq++;
} }
} }
@@ -288,7 +289,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) {
st->key = compute_key(); st->key = compute_key();
st->pawnKey = compute_pawn_key(); st->pawnKey = compute_pawn_key();
st->materialKey = compute_material_key(); st->materialKey = compute_material_key();
st->psqScore = compute_psq_score(); st->psq = compute_psq_score();
st->npMaterial[WHITE] = compute_non_pawn_material(WHITE); st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
st->npMaterial[BLACK] = compute_non_pawn_material(BLACK); st->npMaterial[BLACK] = compute_non_pawn_material(BLACK);
st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove); st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove);
@@ -391,16 +392,18 @@ const string Position::pretty(Move move) const {
string brd = twoRows + twoRows + twoRows + twoRows + dottedLine; string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
for (Bitboard b = pieces(); b; )
{
Square s = pop_lsb(&b);
brd[513 - 68 * rank_of(s) + 4 * file_of(s)] = PieceToChar[piece_on(s)];
}
std::ostringstream ss; std::ostringstream ss;
if (move) if (move)
ss << "\nMove: " << (sideToMove == BLACK ? ".." : "") ss << "\nMove: " << (sideToMove == BLACK ? ".." : "")
<< move_to_san(*const_cast<Position*>(this), move); << move_to_san(*const_cast<Position*>(this), move);
for (Square sq = SQ_A1; sq <= SQ_H8; sq++)
if (piece_on(sq) != NO_PIECE)
brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)];
ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
<< std::setfill('0') << std::setw(16) << st->key << "\nCheckers: "; << std::setfill('0') << std::setw(16) << st->key << "\nCheckers: ";
@@ -408,43 +411,35 @@ const string Position::pretty(Move move) const {
ss << square_to_string(pop_lsb(&b)) << " "; ss << square_to_string(pop_lsb(&b)) << " ";
ss << "\nLegal moves: "; ss << "\nLegal moves: ";
for (MoveList<LEGAL> ml(*this); !ml.end(); ++ml) for (MoveList<LEGAL> it(*this); *it; ++it)
ss << move_to_san(*const_cast<Position*>(this), ml.move()) << " "; ss << move_to_san(*const_cast<Position*>(this), *it) << " ";
return ss.str(); return ss.str();
} }
/// Position:hidden_checkers<>() returns a bitboard of all pinned (against the /// Position:hidden_checkers() returns a bitboard of all pinned / discovery check
/// king) pieces for the given color. Or, when template parameter FindPinned is /// pieces, according to the call parameters. Pinned pieces protect our king,
/// false, the function return the pieces of the given color candidate for a /// discovery check pieces attack the enemy king.
/// discovery check against the enemy king.
template<bool FindPinned>
Bitboard Position::hidden_checkers() const {
// Pinned pieces protect our king, dicovery checks attack the enemy king Bitboard Position::hidden_checkers(Square ksq, Color c) const {
Bitboard b, result = 0;
Bitboard pinners = pieces(FindPinned ? ~sideToMove : sideToMove);
Square ksq = king_square(FindPinned ? sideToMove : ~sideToMove);
// Pinners are sliders, that give check when candidate pinned is removed Bitboard b, pinners, result = 0;
pinners &= (pieces(ROOK, QUEEN) & PseudoAttacks[ROOK][ksq])
| (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq]); // Pinners are sliders that give check when pinned piece is removed
pinners = ( (pieces( ROOK, QUEEN) & PseudoAttacks[ROOK ][ksq])
| (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq])) & pieces(c);
while (pinners) while (pinners)
{ {
b = between_bb(ksq, pop_lsb(&pinners)) & pieces(); b = between_bb(ksq, pop_lsb(&pinners)) & pieces();
if (b && !more_than_one(b) && (b & pieces(sideToMove))) if (!more_than_one(b))
result |= b; result |= b & pieces(sideToMove);
} }
return result; return result;
} }
// Explicit template instantiations
template Bitboard Position::hidden_checkers<true>() const;
template Bitboard Position::hidden_checkers<false>() const;
/// Position::attackers_to() computes a bitboard of all pieces which attack a /// Position::attackers_to() computes a bitboard of all pieces which attack a
/// given square. Slider attacks use occ bitboard as occupancy. /// given square. Slider attacks use occ bitboard as occupancy.
@@ -549,7 +544,7 @@ bool Position::is_pseudo_legal(const Move m) const {
return false; return false;
// The destination square cannot be occupied by a friendly piece // The destination square cannot be occupied by a friendly piece
if (piece_on(to) != NO_PIECE && color_of(piece_on(to)) == us) if (pieces(us) & to)
return false; return false;
// Handle the special case of a pawn move // Handle the special case of a pawn move
@@ -659,7 +654,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
return true; return true;
// Discovery check ? // Discovery check ?
if (ci.dcCandidates && (ci.dcCandidates & from)) if (unlikely(ci.dcCandidates) && (ci.dcCandidates & from))
{ {
// For pawn and king moves we need to verify also direction // For pawn and king moves we need to verify also direction
if ( (pt != PAWN && pt != KING) if ( (pt != PAWN && pt != KING)
@@ -697,9 +692,9 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
Square rfrom = to; // 'King captures the rook' notation Square rfrom = to; // 'King captures the rook' notation
Square kto = relative_square(us, rfrom > kfrom ? SQ_G1 : SQ_C1); Square kto = relative_square(us, rfrom > kfrom ? SQ_G1 : SQ_C1);
Square rto = relative_square(us, rfrom > kfrom ? SQ_F1 : SQ_D1); Square rto = relative_square(us, rfrom > kfrom ? SQ_F1 : SQ_D1);
Bitboard b = (pieces() ^ kfrom ^ rfrom) | rto | kto;
return attacks_bb<ROOK>(rto, b) & ksq; return (PseudoAttacks[ROOK][rto] & ksq)
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & ksq);
} }
default: default:
assert(false); assert(false);
@@ -729,7 +724,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Copy some fields of old state to our new StateInfo object except the ones // Copy some fields of old state to our new StateInfo object except the ones
// which are going to be recalculated from scratch anyway, then switch our state // which are going to be recalculated from scratch anyway, then switch our state
// pointer to point to the new, ready to be updated, state. // pointer to point to the new, ready to be updated, state.
memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t)); std::memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t));
newSt.previous = st; newSt.previous = st;
st = &newSt; st = &newSt;
@@ -747,17 +742,17 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
Color them = ~us; Color them = ~us;
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
Piece piece = piece_on(from); Piece pc = piece_on(from);
PieceType pt = type_of(piece); PieceType pt = type_of(pc);
PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
assert(color_of(piece) == us); assert(color_of(pc) == us);
assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE); assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE);
assert(capture != KING); assert(capture != KING);
if (type_of(m) == CASTLE) if (type_of(m) == CASTLE)
{ {
assert(piece == make_piece(us, KING)); assert(pc == make_piece(us, KING));
bool kingSide = to > from; bool kingSide = to > from;
Square rfrom = to; // Castle is encoded as "king captures friendly rook" Square rfrom = to; // Castle is encoded as "king captures friendly rook"
@@ -767,7 +762,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
do_castle(from, to, rfrom, rto); do_castle(from, to, rfrom, rto);
st->psqScore += psq_delta(make_piece(us, ROOK), rfrom, rto); st->psq += psq[us][ROOK][rto] - psq[us][ROOK][rfrom];
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
} }
@@ -797,22 +792,8 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
else else
st->npMaterial[them] -= PieceValue[MG][capture]; st->npMaterial[them] -= PieceValue[MG][capture];
// Remove the captured piece // Update board and piece lists
byTypeBB[ALL_PIECES] ^= capsq; remove_piece(capsq, them, capture);
byTypeBB[capture] ^= capsq;
byColorBB[them] ^= capsq;
// Update piece list, move the last piece at index[capsq] position and
// shrink the list.
//
// WARNING: This is a not reversible operation. When we will reinsert the
// captured piece 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.
Square lastSquare = pieceList[them][capture][--pieceCount[them][capture]];
index[lastSquare] = index[capsq];
pieceList[them][capture][index[lastSquare]] = lastSquare;
pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE;
// Update material hash key and prefetch access to materialTable // Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[them][capture][capsq]; k ^= Zobrist::psq[them][capture][capsq];
@@ -820,7 +801,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
prefetch((char*)thisThread->materialTable[st->materialKey]); prefetch((char*)thisThread->materialTable[st->materialKey]);
// Update incremental scores // Update incremental scores
st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq]; st->psq -= psq[them][capture][capsq];
// Reset rule 50 counter // Reset rule 50 counter
st->rule50 = 0; st->rule50 = 0;
@@ -849,20 +830,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
// Move the piece. The tricky Chess960 castle is handled earlier // Move the piece. The tricky Chess960 castle is handled earlier
if (type_of(m) != CASTLE) if (type_of(m) != CASTLE)
{ move_piece(from, to, us, pt);
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[us] ^= from_to_bb;
board[from] = NO_PIECE;
board[to] = piece;
// Update piece lists, index[from] is not updated and becomes stale. This
// works as long as index[] is accessed just by known occupied squares.
index[to] = index[from];
pieceList[us][pt][index[to]] = to;
}
// If the moving piece is a pawn do some special extra work // If the moving piece is a pawn do some special extra work
if (pt == PAWN) if (pt == PAWN)
@@ -882,29 +850,17 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
assert(relative_rank(us, to) == RANK_8); assert(relative_rank(us, to) == RANK_8);
assert(promotion >= KNIGHT && promotion <= QUEEN); assert(promotion >= KNIGHT && promotion <= QUEEN);
// Replace the pawn with the promoted piece remove_piece(to, us, PAWN);
byTypeBB[PAWN] ^= to; put_piece(to, us, promotion);
byTypeBB[promotion] |= to;
board[to] = make_piece(us, promotion);
// Update piece lists, move the last pawn at index[to] position
// and shrink the list. Add a new promotion piece to the list.
Square lastSquare = pieceList[us][PAWN][--pieceCount[us][PAWN]];
index[lastSquare] = index[to];
pieceList[us][PAWN][index[lastSquare]] = lastSquare;
pieceList[us][PAWN][pieceCount[us][PAWN]] = SQ_NONE;
index[to] = pieceCount[us][promotion];
pieceList[us][promotion][index[to]] = to;
// Update hash keys // Update hash keys
k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to]; k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to];
st->pawnKey ^= Zobrist::psq[us][PAWN][to]; st->pawnKey ^= Zobrist::psq[us][PAWN][to];
st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]++] st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]-1]
^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]]; ^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]];
// Update incremental score // Update incremental score
st->psqScore += pieceSquareTable[make_piece(us, promotion)][to] st->psq += psq[us][promotion][to] - psq[us][PAWN][to];
- pieceSquareTable[make_piece(us, PAWN)][to];
// Update material // Update material
st->npMaterial[us] += PieceValue[MG][promotion]; st->npMaterial[us] += PieceValue[MG][promotion];
@@ -919,7 +875,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
} }
// Update incremental scores // Update incremental scores
st->psqScore += psq_delta(piece, from, to); st->psq += psq[us][pt][to] - psq[us][pt][from];
// Set capture piece // Set capture piece
st->capturedType = capture; st->capturedType = capture;
@@ -985,20 +941,8 @@ void Position::undo_move(Move m) {
assert(relative_rank(us, to) == RANK_8); assert(relative_rank(us, to) == RANK_8);
assert(promotion >= KNIGHT && promotion <= QUEEN); assert(promotion >= KNIGHT && promotion <= QUEEN);
// Replace the promoted piece with the pawn remove_piece(to, us, promotion);
byTypeBB[promotion] ^= to; put_piece(to, us, PAWN);
byTypeBB[PAWN] |= to;
board[to] = make_piece(us, PAWN);
// Update piece lists, move the last promoted piece at index[to] position
// and shrink the list. Add a new pawn to the list.
Square lastSquare = pieceList[us][promotion][--pieceCount[us][promotion]];
index[lastSquare] = index[to];
pieceList[us][promotion][index[lastSquare]] = lastSquare;
pieceList[us][promotion][pieceCount[us][promotion]] = SQ_NONE;
index[to] = pieceCount[us][PAWN]++;
pieceList[us][PAWN][index[to]] = to;
pt = PAWN; pt = PAWN;
} }
@@ -1013,21 +957,7 @@ void Position::undo_move(Move m) {
do_castle(to, from, rto, rfrom); do_castle(to, from, rto, rfrom);
} }
else else
{ move_piece(to, from, us, pt); // Put the piece back at the source square
// Put the piece back at the source square
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[us] ^= from_to_bb;
board[to] = NO_PIECE;
board[from] = make_piece(us, pt);
// Update piece lists, index[to] is not updated and becomes stale. This
// works as long as index[] is accessed just by known occupied squares.
index[from] = index[to];
pieceList[us][pt][index[from]] = from;
}
if (capture) if (capture)
{ {
@@ -1043,16 +973,7 @@ void Position::undo_move(Move m) {
assert(piece_on(capsq) == NO_PIECE); assert(piece_on(capsq) == NO_PIECE);
} }
// Restore the captured piece put_piece(capsq, them, capture); // Restore the captured piece
byTypeBB[ALL_PIECES] |= capsq;
byTypeBB[capture] |= capsq;
byColorBB[them] |= capsq;
board[capsq] = make_piece(them, capture);
// Update piece list, add a new captured piece in capsq square
index[capsq] = pieceCount[them][capture]++;
pieceList[them][capture][index[capsq]] = capsq;
} }
// Finally point our state pointer back to the previous state // Finally point our state pointer back to the previous state
@@ -1068,25 +989,12 @@ void Position::undo_move(Move m) {
void Position::do_castle(Square kfrom, Square kto, Square rfrom, Square rto) { void Position::do_castle(Square kfrom, Square kto, Square rfrom, Square rto) {
Color us = sideToMove; // Remove both pieces first since squares could overlap in Chess960
Bitboard k_from_to_bb = SquareBB[kfrom] ^ SquareBB[kto]; remove_piece(kfrom, sideToMove, KING);
Bitboard r_from_to_bb = SquareBB[rfrom] ^ SquareBB[rto]; remove_piece(rfrom, sideToMove, ROOK);
byTypeBB[KING] ^= k_from_to_bb; board[kfrom] = board[rfrom] = NO_PIECE; // Since remove_piece doesn't do it for us
byTypeBB[ROOK] ^= r_from_to_bb; put_piece(kto, sideToMove, KING);
byTypeBB[ALL_PIECES] ^= k_from_to_bb ^ r_from_to_bb; put_piece(rto, sideToMove, ROOK);
byColorBB[us] ^= k_from_to_bb ^ r_from_to_bb;
// Could be from == to, so first set NO_PIECE then KING and ROOK
board[kfrom] = board[rfrom] = NO_PIECE;
board[kto] = make_piece(us, KING);
board[rto] = make_piece(us, ROOK);
// Could be kfrom == rto, so use a 'tmp' variable
int tmp = index[kfrom];
index[rto] = index[rfrom];
index[kto] = tmp;
pieceList[us][KING][index[kto]] = kto;
pieceList[us][ROOK][index[rto]] = rto;
} }
@@ -1097,7 +1005,7 @@ void Position::do_null_move(StateInfo& newSt) {
assert(!checkers()); assert(!checkers());
memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here std::memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here
newSt.previous = st; newSt.previous = st;
st = &newSt; st = &newSt;
@@ -1141,7 +1049,7 @@ int Position::see_sign(Move m) const {
// Early return if SEE cannot be negative because captured piece value // Early return if SEE cannot be negative because captured piece value
// is not less then capturing one. Note that king moves always return // is not less then capturing one. Note that king moves always return
// here because king midgame value is set to 0. // here because king midgame value is set to 0.
if (PieceValue[MG][piece_on(to_sq(m))] >= PieceValue[MG][piece_moved(m)]) if (PieceValue[MG][piece_moved(m)] <= PieceValue[MG][piece_on(to_sq(m))])
return 1; return 1;
return see(m); return see(m);
@@ -1159,36 +1067,31 @@ int Position::see(Move m, int asymmThreshold) const {
from = from_sq(m); from = from_sq(m);
to = to_sq(m); to = to_sq(m);
captured = type_of(piece_on(to)); swapList[0] = PieceValue[MG][type_of(piece_on(to))];
stm = color_of(piece_on(from));
occupied = pieces() ^ from; occupied = pieces() ^ from;
// Handle en passant moves
if (type_of(m) == ENPASSANT)
{
Square capQq = to - pawn_push(sideToMove);
assert(!captured);
assert(type_of(piece_on(capQq)) == PAWN);
// Remove the captured pawn
occupied ^= capQq;
captured = PAWN;
}
else if (type_of(m) == CASTLE)
// Castle moves are implemented as king capturing the rook so cannot be // Castle moves are implemented as king capturing the rook so cannot be
// handled correctly. Simply return 0 that is always the correct value // handled correctly. Simply return 0 that is always the correct value
// unless the rook is ends up under attack. // unless in the rare case the rook ends up under attack.
if (type_of(m) == CASTLE)
return 0; return 0;
if (type_of(m) == ENPASSANT)
{
occupied ^= to - pawn_push(stm); // Remove the captured pawn
swapList[0] = PieceValue[MG][PAWN];
}
// Find all attackers to the destination square, with the moving piece // Find all attackers to the destination square, with the moving piece
// removed, but possibly an X-ray attacker added behind it. // removed, but possibly an X-ray attacker added behind it.
attackers = attackers_to(to, occupied); attackers = attackers_to(to, occupied) & occupied;
// If the opponent has no attackers we are finished // If the opponent has no attackers we are finished
stm = ~color_of(piece_on(from)); stm = ~stm;
stmAttackers = attackers & pieces(stm); stmAttackers = attackers & pieces(stm);
if (!stmAttackers) if (!stmAttackers)
return PieceValue[MG][captured]; return swapList[0];
// The destination square is defended, which makes things rather more // The destination square is defended, which makes things rather more
// difficult to compute. We proceed by building up a "swap list" containing // difficult to compute. We proceed by building up a "swap list" containing
@@ -1196,7 +1099,6 @@ int Position::see(Move m, int asymmThreshold) const {
// destination square, where the sides alternately capture, and always // destination square, where the sides alternately capture, and always
// capture with the least valuable piece. After each capture, we look for // capture with the least valuable piece. After each capture, we look for
// new X-ray attacks from behind the capturing piece. // new X-ray attacks from behind the capturing piece.
swapList[0] = PieceValue[MG][captured];
captured = type_of(piece_on(from)); captured = type_of(piece_on(from));
do { do {
@@ -1206,19 +1108,15 @@ int Position::see(Move m, int asymmThreshold) const {
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured]; swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured];
slIndex++; slIndex++;
// Locate and remove from 'occupied' the next least valuable attacker // Locate and remove the next least valuable attacker
captured = next_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers); captured = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
attackers &= occupied; // Remove the just found attacker
stm = ~stm; stm = ~stm;
stmAttackers = attackers & pieces(stm); stmAttackers = attackers & pieces(stm);
if (captured == KING)
{
// Stop before processing a king capture // Stop before processing a king capture
if (stmAttackers) if (captured == KING && stmAttackers)
{
swapList[slIndex++] = QueenValueMg * 16; swapList[slIndex++] = QueenValueMg * 16;
break; break;
} }
@@ -1247,7 +1145,7 @@ int Position::see(Move m, int asymmThreshold) const {
void Position::clear() { void Position::clear() {
memset(this, 0, sizeof(Position)); std::memset(this, 0, sizeof(Position));
startState.epSquare = SQ_NONE; startState.epSquare = SQ_NONE;
st = &startState; st = &startState;
@@ -1257,24 +1155,6 @@ void Position::clear() {
} }
/// Position::put_piece() puts a piece on the given square of the board,
/// updating the board array, pieces list, bitboards, and piece counts.
void Position::put_piece(Piece p, Square s) {
Color c = color_of(p);
PieceType pt = type_of(p);
board[s] = p;
index[s] = pieceCount[c][pt]++;
pieceList[c][pt][index[s]] = s;
byTypeBB[ALL_PIECES] |= s;
byTypeBB[pt] |= s;
byColorBB[c] |= s;
}
/// Position::compute_key() computes the hash key of the position. The hash /// Position::compute_key() computes the hash key of the position. The hash
/// key is usually updated incrementally as moves are made and unmade, the /// key is usually updated incrementally as moves are made and unmade, the
/// compute_key() function is only used when a new position is set up, and /// compute_key() function is only used when a new position is set up, and
@@ -1332,7 +1212,7 @@ Key Position::compute_material_key() const {
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= QUEEN; pt++) for (PieceType pt = PAWN; pt <= QUEEN; pt++)
for (int cnt = 0; cnt < piece_count(c, pt); cnt++) for (int cnt = 0; cnt < pieceCount[c][pt]; cnt++)
k ^= Zobrist::psq[c][pt][cnt]; k ^= Zobrist::psq[c][pt][cnt];
return k; return k;
@@ -1350,7 +1230,8 @@ Score Position::compute_psq_score() const {
for (Bitboard b = pieces(); b; ) for (Bitboard b = pieces(); b; )
{ {
Square s = pop_lsb(&b); Square s = pop_lsb(&b);
score += pieceSquareTable[piece_on(s)][s]; Piece pc = piece_on(s);
score += psq[color_of(pc)][type_of(pc)][s];
} }
return score; return score;
@@ -1367,7 +1248,7 @@ Value Position::compute_non_pawn_material(Color c) const {
Value value = VALUE_ZERO; Value value = VALUE_ZERO;
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++) for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
value += piece_count(c, pt) * PieceValue[MG][pt]; value += pieceCount[c][pt] * PieceValue[MG][pt];
return value; return value;
} }
@@ -1412,42 +1293,36 @@ bool Position::is_draw() const {
/// Position::flip() flips position with the white and black sides reversed. This /// Position::flip() flips position with the white and black sides reversed. This
/// is only useful for debugging especially for finding evaluation symmetry bugs. /// is only useful for debugging especially for finding evaluation symmetry bugs.
static char toggle_case(char c) {
return char(islower(c) ? toupper(c) : tolower(c));
}
void Position::flip() { void Position::flip() {
const Position pos(*this); string f, token;
std::stringstream ss(fen());
clear(); for (Rank rank = RANK_8; rank >= RANK_1; rank--) // Piece placement
{
std::getline(ss, token, rank > RANK_1 ? '/' : ' ');
f.insert(0, token + (f.empty() ? " " : "/"));
}
sideToMove = ~pos.side_to_move(); ss >> token; // Active color
thisThread = pos.this_thread(); f += (token == "w" ? "B " : "W "); // Will be lowercased later
nodes = pos.nodes_searched();
chess960 = pos.is_chess960();
gamePly = pos.game_ply();
for (Square s = SQ_A1; s <= SQ_H8; s++) ss >> token; // Castling availability
if (!pos.is_empty(s)) f += token + " ";
put_piece(Piece(pos.piece_on(s) ^ 8), ~s);
if (pos.can_castle(WHITE_OO)) std::transform(f.begin(), f.end(), f.begin(), toggle_case);
set_castle_right(BLACK, ~pos.castle_rook_square(WHITE, KING_SIDE));
if (pos.can_castle(WHITE_OOO))
set_castle_right(BLACK, ~pos.castle_rook_square(WHITE, QUEEN_SIDE));
if (pos.can_castle(BLACK_OO))
set_castle_right(WHITE, ~pos.castle_rook_square(BLACK, KING_SIDE));
if (pos.can_castle(BLACK_OOO))
set_castle_right(WHITE, ~pos.castle_rook_square(BLACK, QUEEN_SIDE));
if (pos.st->epSquare != SQ_NONE) ss >> token; // En passant square
st->epSquare = ~pos.st->epSquare; f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3"));
st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove); std::getline(ss, token); // Half and full moves
f += token;
st->key = compute_key(); set(f, is_chess960(), this_thread());
st->pawnKey = compute_pawn_key();
st->materialKey = compute_material_key();
st->psqScore = compute_psq_score();
st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
st->npMaterial[BLACK] = compute_non_pawn_material(BLACK);
assert(pos_is_ok()); assert(pos_is_ok());
} }
@@ -1536,15 +1411,13 @@ bool Position::pos_is_ok(int* failedStep) const {
if ((*step)++, debugMaterialKey && st->materialKey != compute_material_key()) if ((*step)++, debugMaterialKey && st->materialKey != compute_material_key())
return false; return false;
if ((*step)++, debugIncrementalEval && st->psqScore != compute_psq_score()) if ((*step)++, debugIncrementalEval && st->psq != compute_psq_score())
return false; return false;
if ((*step)++, debugNonPawnMaterial) if ((*step)++, debugNonPawnMaterial)
{
if ( st->npMaterial[WHITE] != compute_non_pawn_material(WHITE) if ( st->npMaterial[WHITE] != compute_non_pawn_material(WHITE)
|| st->npMaterial[BLACK] != compute_non_pawn_material(BLACK)) || st->npMaterial[BLACK] != compute_non_pawn_material(BLACK))
return false; return false;
}
if ((*step)++, debugPieceCounts) if ((*step)++, debugPieceCounts)
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; c++)
@@ -1556,14 +1429,10 @@ bool Position::pos_is_ok(int* failedStep) const {
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++) for (PieceType pt = PAWN; pt <= KING; pt++)
for (int i = 0; i < pieceCount[c][pt]; i++) for (int i = 0; i < pieceCount[c][pt]; i++)
{ if ( board[pieceList[c][pt][i]] != make_piece(c, pt)
if (piece_on(piece_list(c, pt)[i]) != make_piece(c, pt)) || index[pieceList[c][pt][i]] != i)
return false; return false;
if (index[piece_list(c, pt)[i]] != i)
return false;
}
if ((*step)++, debugCastleSquares) if ((*step)++, debugCastleSquares)
for (Color c = WHITE; c <= BLACK; c++) for (Color c = WHITE; c <= BLACK; c++)
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1)) for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
@@ -1573,10 +1442,8 @@ bool Position::pos_is_ok(int* failedStep) const {
if (!can_castle(cr)) if (!can_castle(cr))
continue; continue;
if ((castleRightsMask[king_square(c)] & cr) != cr) if ( (castleRightsMask[king_square(c)] & cr) != cr
return false; || piece_on(castleRookSquare[c][s]) != make_piece(c, ROOK)
if ( piece_on(castleRookSquare[c][s]) != make_piece(c, ROOK)
|| castleRightsMask[castleRookSquare[c][s]] != cr) || castleRightsMask[castleRookSquare[c][s]] != cr)
return false; return false;
} }
+57 -23
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(POSITION_H_INCLUDED) #ifndef POSITION_H_INCLUDED
#define POSITION_H_INCLUDED #define POSITION_H_INCLUDED
#include <cassert> #include <cassert>
@@ -52,7 +52,7 @@ struct StateInfo {
Key pawnKey, materialKey; Key pawnKey, materialKey;
Value npMaterial[COLOR_NB]; Value npMaterial[COLOR_NB];
int castleRights, rule50, pliesFromNull; int castleRights, rule50, pliesFromNull;
Score psqScore; Score psq;
Square epSquare; Square epSquare;
Key key; Key key;
@@ -95,6 +95,7 @@ public:
Position(const Position& p, Thread* t) { *this = p; thisThread = t; } Position(const Position& p, Thread* t) { *this = p; thisThread = t; }
Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); } Position(const std::string& f, bool c960, Thread* t) { set(f, c960, t); }
Position& operator=(const Position&); Position& operator=(const Position&);
static void init();
// Text input/output // Text input/output
void set(const std::string& fen, bool isChess960, Thread* th); void set(const std::string& fen, bool isChess960, Thread* th);
@@ -112,8 +113,8 @@ public:
Square king_square(Color c) const; Square king_square(Color c) const;
Square ep_square() const; Square ep_square() const;
bool is_empty(Square s) const; bool is_empty(Square s) const;
const Square* piece_list(Color c, PieceType pt) const; template<PieceType Pt> int count(Color c) const;
int piece_count(Color c, PieceType pt) const; template<PieceType Pt> const Square* list(Color c) const;
// Castling // Castling
int can_castle(CastleRight f) const; int can_castle(CastleRight f) const;
@@ -169,7 +170,6 @@ public:
// Incremental piece-square evaluation // Incremental piece-square evaluation
Score psq_score() const; Score psq_score() const;
Score psq_delta(Piece p, Square from, Square to) const;
Value non_pawn_material(Color c) const; Value non_pawn_material(Color c) const;
// Other properties of the position // Other properties of the position
@@ -188,12 +188,14 @@ public:
private: private:
// Initialization helpers (used while setting up a position) // Initialization helpers (used while setting up a position)
void clear(); void clear();
void put_piece(Piece p, Square s);
void set_castle_right(Color c, Square rfrom); void set_castle_right(Color c, Square rfrom);
// Helper functions // Helper functions
void do_castle(Square kfrom, Square kto, Square rfrom, Square rto); void do_castle(Square kfrom, Square kto, Square rfrom, Square rto);
template<bool FindPinned> Bitboard hidden_checkers() const; Bitboard hidden_checkers(Square ksq, Color c) const;
void put_piece(Square s, Color c, PieceType pt);
void remove_piece(Square s, Color c, PieceType pt);
void move_piece(Square from, Square to, Color c, PieceType pt);
// Computing hash keys from scratch (for initialization and debugging) // Computing hash keys from scratch (for initialization and debugging)
Key compute_key() const; Key compute_key() const;
@@ -273,12 +275,12 @@ inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const {
return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]); return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]);
} }
inline int Position::piece_count(Color c, PieceType pt) const { template<PieceType Pt> inline int Position::count(Color c) const {
return pieceCount[c][pt]; return pieceCount[c][Pt];
} }
inline const Square* Position::piece_list(Color c, PieceType pt) const { template<PieceType Pt> inline const Square* Position::list(Color c) const {
return pieceList[c][pt]; return pieceList[c][Pt];
} }
inline Square Position::ep_square() const { inline Square Position::ep_square() const {
@@ -331,11 +333,11 @@ inline Bitboard Position::checkers() const {
} }
inline Bitboard Position::discovered_check_candidates() const { inline Bitboard Position::discovered_check_candidates() const {
return hidden_checkers<false>(); return hidden_checkers(king_square(~sideToMove), sideToMove);
} }
inline Bitboard Position::pinned_pieces() const { inline Bitboard Position::pinned_pieces() const {
return hidden_checkers<true>(); return hidden_checkers(king_square(sideToMove), ~sideToMove);
} }
inline bool Position::pawn_is_passed(Color c, Square s) const { inline bool Position::pawn_is_passed(Color c, Square s) const {
@@ -346,10 +348,6 @@ inline Key Position::key() const {
return st->key; return st->key;
} }
inline Key Position::exclusion_key() const {
return st->key ^ Zobrist::exclusion;
}
inline Key Position::pawn_key() const { inline Key Position::pawn_key() const {
return st->pawnKey; return st->pawnKey;
} }
@@ -358,12 +356,8 @@ inline Key Position::material_key() const {
return st->materialKey; return st->materialKey;
} }
inline Score Position::psq_delta(Piece p, Square from, Square to) const {
return pieceSquareTable[p][to] - pieceSquareTable[p][from];
}
inline Score Position::psq_score() const { inline Score Position::psq_score() const {
return st->psqScore; return st->psq;
} }
inline Value Position::non_pawn_material(Color c) const { inline Value Position::non_pawn_material(Color c) const {
@@ -422,4 +416,44 @@ inline Thread* Position::this_thread() const {
return thisThread; return thisThread;
} }
#endif // !defined(POSITION_H_INCLUDED) inline void Position::put_piece(Square s, Color c, PieceType pt) {
board[s] = make_piece(c, pt);
byTypeBB[ALL_PIECES] |= s;
byTypeBB[pt] |= s;
byColorBB[c] |= s;
index[s] = pieceCount[c][pt]++;
pieceList[c][pt][index[s]] = s;
}
inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) {
// index[from] is not updated and becomes stale. This works as long
// as index[] is accessed just by known occupied squares.
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
byTypeBB[ALL_PIECES] ^= from_to_bb;
byTypeBB[pt] ^= from_to_bb;
byColorBB[c] ^= from_to_bb;
board[from] = NO_PIECE;
board[to] = make_piece(c, pt);
index[to] = index[from];
pieceList[c][pt][index[to]] = to;
}
inline void Position::remove_piece(Square s, Color c, PieceType pt) {
// WARNING: This is not a reversible operation. If we remove a piece in
// do_move() and then replace it in undo_move() we will put it at the end of
// the list and not in its original place, it means index[] and pieceList[]
// are not guaranteed to be invariant to a do_move() + undo_move() sequence.
byTypeBB[ALL_PIECES] ^= s;
byTypeBB[pt] ^= s;
byColorBB[c] ^= s;
/* board[s] = NO_PIECE; */ // Not needed, will be overwritten by capturing
Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]];
index[lastSquare] = index[s];
pieceList[c][pt][index[lastSquare]] = lastSquare;
pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE;
}
#endif // #ifndef POSITION_H_INCLUDED
+8 -8
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(PSQTAB_H_INCLUDED) #ifndef PSQTAB_H_INCLUDED
#define PSQTAB_H_INCLUDED #define PSQTAB_H_INCLUDED
#include "types.h" #include "types.h"
@@ -33,12 +33,12 @@ static const Score PSQT[][SQUARE_NB] = {
{ }, { },
{ // Pawn { // Pawn
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0),
S(-28,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-28,-8), S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8),
S(-28,-8), S(-6,-8), S( 9,-8), S(36,-8), S(36,-8), S( 9,-8), S(-6,-8), S(-28,-8), S(-20,-8), S(-6,-8), S( 9,-8), S(34,-8), S(34,-8), S( 9,-8), S(-6,-8), S(-20,-8),
S(-28,-8), S(-6,-8), S(17,-8), S(58,-8), S(58,-8), S(17,-8), S(-6,-8), S(-28,-8), S(-20,-8), S(-6,-8), S(17,-8), S(54,-8), S(54,-8), S(17,-8), S(-6,-8), S(-20,-8),
S(-28,-8), S(-6,-8), S(17,-8), S(36,-8), S(36,-8), S(17,-8), S(-6,-8), S(-28,-8), S(-20,-8), S(-6,-8), S(17,-8), S(34,-8), S(34,-8), S(17,-8), S(-6,-8), S(-20,-8),
S(-28,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-28,-8), S(-20,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-20,-8),
S(-28,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-28,-8), S(-20,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-20,-8),
S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0) S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0)
}, },
{ // Knight { // Knight
@@ -95,4 +95,4 @@ static const Score PSQT[][SQUARE_NB] = {
#undef S #undef S
#endif // !defined(PSQTAB_H_INCLUDED) #endif // #ifndef PSQTAB_H_INCLUDED
+3 -5
View File
@@ -22,7 +22,7 @@
(at your option) any later version. (at your option) any later version.
*/ */
#if !defined(RKISS_H_INCLUDED) #ifndef RKISS_H_INCLUDED
#define RKISS_H_INCLUDED #define RKISS_H_INCLUDED
#include "types.h" #include "types.h"
@@ -43,14 +43,12 @@
class RKISS { class RKISS {
// Keep variables always together struct S { uint64_t a, b, c, d; } s; // Keep variables always together
struct S { uint64_t a, b, c, d; } s;
uint64_t rotate(uint64_t x, uint64_t k) const { uint64_t rotate(uint64_t x, uint64_t k) const {
return (x << k) | (x >> (64 - k)); return (x << k) | (x >> (64 - k));
} }
// Return 64 bit unsigned integer in between [0, 2^64 - 1]
uint64_t rand64() { uint64_t rand64() {
const uint64_t const uint64_t
@@ -73,4 +71,4 @@ public:
template<typename T> T rand() { return T(rand64()); } template<typename T> T rand() { return T(rand64()); }
}; };
#endif // !defined(RKISS_H_INCLUDED) #endif // #ifndef RKISS_H_INCLUDED
+182 -174
View File
@@ -66,7 +66,7 @@ namespace {
// Futility lookup tables (initialized at startup) and their access functions // Futility lookup tables (initialized at startup) and their access functions
Value FutilityMargins[16][64]; // [depth][moveNumber] Value FutilityMargins[16][64]; // [depth][moveNumber]
int FutilityMoveCounts[32]; // [depth] int FutilityMoveCounts[2][32]; // [improving][depth]
inline Value futility_margin(Depth d, int mn) { inline Value futility_margin(Depth d, int mn) {
@@ -75,22 +75,23 @@ namespace {
} }
// Reduction lookup tables (initialized at startup) and their access function // Reduction lookup tables (initialized at startup) and their access function
int8_t Reductions[2][64][64]; // [pv][depth][moveNumber] int8_t Reductions[2][2][64][64]; // [pv][improving][depth][moveNumber]
template <bool PvNode> inline Depth reduction(Depth d, int mn) { template <bool PvNode> inline Depth reduction(bool i, Depth d, int mn) {
return (Depth) Reductions[PvNode][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)]; return (Depth) Reductions[PvNode][i][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)];
} }
size_t PVSize, PVIdx; size_t PVSize, PVIdx;
TimeManager TimeMgr; TimeManager TimeMgr;
int BestMoveChanges; int BestMoveChanges;
Value DrawValue[COLOR_NB]; Value DrawValue[COLOR_NB];
History Hist; HistoryStats History;
Gains Gain; GainsStats Gains;
CountermovesStats Countermoves;
template <NodeType NT> template <NodeType NT>
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
template <NodeType NT, bool InCheck> template <NodeType NT, bool InCheck>
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth);
@@ -135,8 +136,14 @@ void Search::init() {
{ {
double pvRed = log(double(hd)) * log(double(mc)) / 3.0; double pvRed = log(double(hd)) * log(double(mc)) / 3.0;
double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25; double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25;
Reductions[1][hd][mc] = (int8_t) ( pvRed >= 1.0 ? floor( pvRed * int(ONE_PLY)) : 0); Reductions[1][1][hd][mc] = (int8_t) ( pvRed >= 1.0 ? floor( pvRed * int(ONE_PLY)) : 0);
Reductions[0][hd][mc] = (int8_t) (nonPVRed >= 1.0 ? floor(nonPVRed * int(ONE_PLY)) : 0); Reductions[0][1][hd][mc] = (int8_t) (nonPVRed >= 1.0 ? floor(nonPVRed * int(ONE_PLY)) : 0);
Reductions[1][0][hd][mc] = Reductions[1][1][hd][mc];
Reductions[0][0][hd][mc] = Reductions[0][1][hd][mc];
if (Reductions[0][0][hd][mc] > 2 * ONE_PLY)
Reductions[0][0][hd][mc] += ONE_PLY;
} }
// Init futility margins array // Init futility margins array
@@ -145,33 +152,35 @@ void Search::init() {
// Init futility move count array // Init futility move count array
for (d = 0; d < 32; d++) for (d = 0; d < 32; d++)
FutilityMoveCounts[d] = int(3.001 + 0.25 * pow(double(d), 2.0)); {
FutilityMoveCounts[0][d] = int(3.001 + 0.3 * pow(double(d ), 1.8)) * (d < 5 ? 4 : 3) / 4;
FutilityMoveCounts[1][d] = int(3.001 + 0.3 * pow(double(d + 0.98), 1.8));
}
} }
/// Search::perft() is our utility to verify move generation. All the leaf nodes /// Search::perft() is our utility to verify move generation. All the leaf nodes
/// up to the given depth are generated and counted and the sum returned. /// up to the given depth are generated and counted and the sum returned.
size_t Search::perft(Position& pos, Depth depth) { static size_t perft(Position& pos, Depth depth) {
// At the last ply just return the number of legal moves (leaf nodes)
if (depth == ONE_PLY)
return MoveList<LEGAL>(pos).size();
StateInfo st; StateInfo st;
size_t cnt = 0; size_t cnt = 0;
CheckInfo ci(pos); CheckInfo ci(pos);
const bool leaf = depth == 2 * ONE_PLY;
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml) for (MoveList<LEGAL> it(pos); *it; ++it)
{ {
pos.do_move(ml.move(), st, ci, pos.move_gives_check(ml.move(), ci)); pos.do_move(*it, st, ci, pos.move_gives_check(*it, ci));
cnt += perft(pos, depth - ONE_PLY); cnt += leaf ? MoveList<LEGAL>(pos).size() : ::perft(pos, depth - ONE_PLY);
pos.undo_move(ml.move()); pos.undo_move(*it);
} }
return cnt; return cnt;
} }
size_t Search::perft(Position& pos, Depth depth) {
return depth > ONE_PLY ? ::perft(pos, depth) : MoveList<LEGAL>(pos).size();
}
/// Search::think() is the external interface to Stockfish's search, and is /// Search::think() is the external interface to Stockfish's search, and is
/// called by the main thread when the program receives the UCI 'go' command. It /// called by the main thread when the program receives the UCI 'go' command. It
@@ -215,7 +224,7 @@ void Search::think() {
else else
DrawValue[WHITE] = DrawValue[BLACK] = VALUE_DRAW; DrawValue[WHITE] = DrawValue[BLACK] = VALUE_DRAW;
if (Options["Use Search Log"]) if (Options["Write Search Log"])
{ {
Log log(Options["Search Log Filename"]); Log log(Options["Search Log Filename"]);
log << "\nSearching: " << RootPos.fen() log << "\nSearching: " << RootPos.fen()
@@ -231,7 +240,7 @@ void Search::think() {
for (size_t i = 0; i < Threads.size(); i++) for (size_t i = 0; i < Threads.size(); i++)
Threads[i]->maxPly = 0; Threads[i]->maxPly = 0;
Threads.sleepWhileIdle = Options["Use Sleeping Threads"]; Threads.sleepWhileIdle = Options["Idle Threads Sleep"];
// Set best timer interval to avoid lagging under time pressure. Timer is // Set best timer interval to avoid lagging under time pressure. Timer is
// used to check for remaining available thinking time. // used to check for remaining available thinking time.
@@ -247,7 +256,7 @@ void Search::think() {
Threads.timer->msec = 0; // Stop the timer Threads.timer->msec = 0; // Stop the timer
Threads.sleepWhileIdle = true; // Send idle threads to sleep Threads.sleepWhileIdle = true; // Send idle threads to sleep
if (Options["Use Search Log"]) if (Options["Write Search Log"])
{ {
Time::point elapsed = Time::now() - SearchTime + 1; Time::point elapsed = Time::now() - SearchTime + 1;
@@ -264,6 +273,10 @@ void Search::think() {
finalize: finalize:
// When search is stopped this info is not printed
sync_cout << "info nodes " << RootPos.nodes_searched()
<< " time " << Time::now() - SearchTime + 1 << sync_endl;
// When we reach max depth we arrive here even without Signals.stop is raised, // When we reach max depth we arrive here even without Signals.stop is raised,
// but if we are pondering or in infinite search, according to UCI protocol, // but if we are pondering or in infinite search, according to UCI protocol,
// we shouldn't print the best move before the GUI sends a "stop" or "ponderhit" // we shouldn't print the best move before the GUI sends a "stop" or "ponderhit"
@@ -290,17 +303,21 @@ namespace {
void id_loop(Position& pos) { void id_loop(Position& pos) {
Stack ss[MAX_PLY_PLUS_2]; Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2)
int depth, prevBestMoveChanges; int depth, prevBestMoveChanges;
Value bestValue, alpha, beta, delta; Value bestValue, alpha, beta, delta;
memset(ss, 0, 4 * sizeof(Stack)); std::memset(ss-2, 0, 5 * sizeof(Stack));
(ss-1)->currentMove = MOVE_NULL; // Hack to skip update gains
depth = BestMoveChanges = 0; depth = BestMoveChanges = 0;
bestValue = delta = -VALUE_INFINITE; bestValue = delta = alpha = -VALUE_INFINITE;
ss->currentMove = MOVE_NULL; // Hack to skip update gains beta = VALUE_INFINITE;
TT.new_search(); TT.new_search();
Hist.clear(); History.clear();
Gain.clear(); Gains.clear();
Countermoves.clear();
PVSize = Options["MultiPV"]; PVSize = Options["MultiPV"];
Skill skill(Options["Skill Level"]); Skill skill(Options["Skill Level"]);
@@ -326,26 +343,19 @@ namespace {
// MultiPV loop. We perform a full root search for each PV line // MultiPV loop. We perform a full root search for each PV line
for (PVIdx = 0; PVIdx < PVSize; PVIdx++) for (PVIdx = 0; PVIdx < PVSize; PVIdx++)
{ {
// Set aspiration window default width // Reset aspiration window starting size
if (depth >= 5 && abs(RootMoves[PVIdx].prevScore) < VALUE_KNOWN_WIN) if (depth >= 5)
{ {
delta = Value(16); delta = Value(16);
alpha = RootMoves[PVIdx].prevScore - delta; alpha = std::max(RootMoves[PVIdx].prevScore - delta,-VALUE_INFINITE);
beta = RootMoves[PVIdx].prevScore + delta; beta = std::min(RootMoves[PVIdx].prevScore + delta, VALUE_INFINITE);
}
else
{
alpha = -VALUE_INFINITE;
beta = VALUE_INFINITE;
} }
// Start with a small aspiration window and, in case of fail high/low, // Start with a small aspiration window and, in case of fail high/low,
// research with bigger window until not failing high/low anymore. // research with bigger window until not failing high/low anymore.
while (true) while (true)
{ {
// Search starts from ss+1 to allow referencing (ss-1). This is bestValue = search<Root>(pos, ss, alpha, beta, depth * ONE_PLY, false);
// needed by update gains and ss copy when splitting at Root.
bestValue = search<Root>(pos, ss+1, alpha, beta, depth * ONE_PLY);
// Bring to front the best move. It is critical that sorting is // Bring to front the best move. It is critical that sorting is
// done with a stable algorithm because all the values but the first // done with a stable algorithm because all the values but the first
@@ -366,33 +376,28 @@ namespace {
if (Signals.stop) if (Signals.stop)
return; return;
// In case of failing high/low increase aspiration window and // When failing high/low give some update (without cluttering
// research, otherwise exit the loop. // the UI) before to research.
if (bestValue > alpha && bestValue < beta) if ( (bestValue <= alpha || bestValue >= beta)
break; && Time::now() - SearchTime > 3000)
// Give some update (without cluttering the UI) before to research
if (Time::now() - SearchTime > 3000)
sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl;
if (abs(bestValue) >= VALUE_KNOWN_WIN) // In case of failing low/high increase aspiration window and
{ // research, otherwise exit the loop.
alpha = -VALUE_INFINITE; if (bestValue <= alpha)
beta = VALUE_INFINITE;
}
else if (bestValue >= beta)
{
beta += delta;
delta += delta / 2;
}
else
{ {
alpha = std::max(bestValue - delta, -VALUE_INFINITE);
Signals.failedLowAtRoot = true; Signals.failedLowAtRoot = true;
Signals.stopOnPonderhit = false; Signals.stopOnPonderhit = false;
alpha -= delta;
delta += delta / 2;
} }
else if (bestValue >= beta)
beta = std::min(bestValue + delta, VALUE_INFINITE);
else
break;
delta += delta / 2;
assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE);
} }
@@ -408,10 +413,14 @@ namespace {
if (skill.enabled() && skill.time_to_pick(depth)) if (skill.enabled() && skill.time_to_pick(depth))
skill.pick_move(); skill.pick_move();
if (Options["Use Search Log"]) if (Options["Write Search Log"])
{ {
RootMove& rm = RootMoves[0];
if (skill.best != MOVE_NONE)
rm = *std::find(RootMoves.begin(), RootMoves.end(), skill.best);
Log log(Options["Search Log Filename"]); Log log(Options["Search Log Filename"]);
log << pretty_pv(pos, depth, bestValue, Time::now() - SearchTime, &RootMoves[0].pv[0]) log << pretty_pv(pos, depth, rm.score, Time::now() - SearchTime, &rm.pv[0])
<< std::endl; << std::endl;
} }
@@ -445,11 +454,11 @@ namespace {
|| Time::now() - SearchTime > (TimeMgr.available_time() * 20) / 100)) || Time::now() - SearchTime > (TimeMgr.available_time() * 20) / 100))
{ {
Value rBeta = bestValue - 2 * PawnValueMg; Value rBeta = bestValue - 2 * PawnValueMg;
(ss+1)->excludedMove = RootMoves[0].pv[0]; ss->excludedMove = RootMoves[0].pv[0];
(ss+1)->skipNullMove = true; ss->skipNullMove = true;
Value v = search<NonPV>(pos, ss+1, rBeta - 1, rBeta, (depth - 3) * ONE_PLY); Value v = search<NonPV>(pos, ss, rBeta - 1, rBeta, (depth - 3) * ONE_PLY, true);
(ss+1)->skipNullMove = false; ss->skipNullMove = false;
(ss+1)->excludedMove = MOVE_NONE; ss->excludedMove = MOVE_NONE;
if (v < rBeta) if (v < rBeta)
stop = true; stop = true;
@@ -477,7 +486,7 @@ namespace {
// here: This is taken care of after we return from the split point. // here: This is taken care of after we return from the split point.
template <NodeType NT> template <NodeType NT>
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) {
const bool PvNode = (NT == PV || NT == Root || NT == SplitPointPV || NT == SplitPointRoot); const bool PvNode = (NT == PV || NT == Root || NT == SplitPointPV || NT == SplitPointRoot);
const bool SpNode = (NT == SplitPointPV || NT == SplitPointNonPV || NT == SplitPointRoot); const bool SpNode = (NT == SplitPointPV || NT == SplitPointNonPV || NT == SplitPointRoot);
@@ -487,7 +496,7 @@ namespace {
assert(PvNode || (alpha == beta - 1)); assert(PvNode || (alpha == beta - 1));
assert(depth > DEPTH_ZERO); assert(depth > DEPTH_ZERO);
Move movesSearched[64]; Move quietsSearched[64];
StateInfo st; StateInfo st;
const TTEntry *tte; const TTEntry *tte;
SplitPoint* splitPoint; SplitPoint* splitPoint;
@@ -496,13 +505,12 @@ namespace {
Depth ext, newDepth; Depth ext, newDepth;
Value bestValue, value, ttValue; Value bestValue, value, ttValue;
Value eval, nullValue, futilityValue; Value eval, nullValue, futilityValue;
bool inCheck, givesCheck, pvMove, singularExtensionNode; bool inCheck, givesCheck, pvMove, singularExtensionNode, improving;
bool captureOrPromotion, dangerous, doFullDepthSearch; bool captureOrPromotion, dangerous, doFullDepthSearch;
int moveCount, playedMoveCount; int moveCount, quietCount;
// Step 1. Initialize node // Step 1. Initialize node
Thread* thisThread = pos.this_thread(); Thread* thisThread = pos.this_thread();
moveCount = playedMoveCount = 0;
inCheck = pos.checkers(); inCheck = pos.checkers();
if (SpNode) if (SpNode)
@@ -517,9 +525,10 @@ namespace {
assert(splitPoint->bestValue > -VALUE_INFINITE && splitPoint->moveCount > 0); assert(splitPoint->bestValue > -VALUE_INFINITE && splitPoint->moveCount > 0);
goto split_point_start; goto moves_loop;
} }
moveCount = quietCount = 0;
bestValue = -VALUE_INFINITE; bestValue = -VALUE_INFINITE;
ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE; ss->currentMove = threatMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
ss->ply = (ss-1)->ply + 1; ss->ply = (ss-1)->ply + 1;
@@ -566,9 +575,9 @@ namespace {
&& tte && tte
&& tte->depth() >= depth && tte->depth() >= depth
&& ttValue != VALUE_NONE // Only in case of TT access race && ttValue != VALUE_NONE // Only in case of TT access race
&& ( PvNode ? tte->type() == BOUND_EXACT && ( PvNode ? tte->bound() == BOUND_EXACT
: ttValue >= beta ? (tte->type() & BOUND_LOWER) : ttValue >= beta ? (tte->bound() & BOUND_LOWER)
: (tte->type() & BOUND_UPPER))) : (tte->bound() & BOUND_UPPER)))
{ {
TT.refresh(tte); TT.refresh(tte);
ss->currentMove = ttMove; // Can be MOVE_NONE ss->currentMove = ttMove; // Can be MOVE_NONE
@@ -586,7 +595,10 @@ namespace {
// Step 5. Evaluate the position statically and update parent's gain statistics // Step 5. Evaluate the position statically and update parent's gain statistics
if (inCheck) if (inCheck)
{
ss->staticEval = ss->evalMargin = eval = VALUE_NONE; ss->staticEval = ss->evalMargin = eval = VALUE_NONE;
goto moves_loop;
}
else if (tte) else if (tte)
{ {
@@ -597,8 +609,8 @@ namespace {
// Can ttValue be used as a better position evaluation? // Can ttValue be used as a better position evaluation?
if (ttValue != VALUE_NONE) if (ttValue != VALUE_NONE)
if ( ((tte->type() & BOUND_LOWER) && ttValue > eval) if ( ((tte->bound() & BOUND_LOWER) && ttValue > eval)
|| ((tte->type() & BOUND_UPPER) && ttValue < eval)) || ((tte->bound() & BOUND_UPPER) && ttValue < eval))
eval = ttValue; eval = ttValue;
} }
else else
@@ -610,20 +622,19 @@ namespace {
// Update gain for the parent non-capture move given the static position // Update gain for the parent non-capture move given the static position
// evaluation before and after the move. // evaluation before and after the move.
if ( (move = (ss-1)->currentMove) != MOVE_NULL if ( !pos.captured_piece_type()
&& (ss-1)->staticEval != VALUE_NONE
&& ss->staticEval != VALUE_NONE && ss->staticEval != VALUE_NONE
&& !pos.captured_piece_type() && (ss-1)->staticEval != VALUE_NONE
&& (move = (ss-1)->currentMove) != MOVE_NULL
&& type_of(move) == NORMAL) && type_of(move) == NORMAL)
{ {
Square to = to_sq(move); Square to = to_sq(move);
Gain.update(pos.piece_on(to), to, -(ss-1)->staticEval - ss->staticEval); Gains.update(pos.piece_on(to), to, -(ss-1)->staticEval - ss->staticEval);
} }
// Step 6. Razoring (is omitted in PV nodes) // Step 6. Razoring (skipped when in check)
if ( !PvNode if ( !PvNode
&& depth < 4 * ONE_PLY && depth < 4 * ONE_PLY
&& !inCheck
&& eval + razor_margin(depth) < beta && eval + razor_margin(depth) < beta
&& ttMove == MOVE_NONE && ttMove == MOVE_NONE
&& abs(beta) < VALUE_MATE_IN_MAX_PLY && abs(beta) < VALUE_MATE_IN_MAX_PLY
@@ -637,13 +648,12 @@ namespace {
return v; return v;
} }
// Step 7. Static null move pruning (is omitted in PV nodes) // Step 7. Static null move pruning (skipped when in check)
// We're betting that the opponent doesn't have a move that will reduce // We're betting that the opponent doesn't have a move that will reduce
// the score by more than futility_margin(depth) if we do a null move. // the score by more than futility_margin(depth) if we do a null move.
if ( !PvNode if ( !PvNode
&& !ss->skipNullMove && !ss->skipNullMove
&& depth < 4 * ONE_PLY && depth < 4 * ONE_PLY
&& !inCheck
&& eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta && eval - futility_margin(depth, (ss-1)->futilityMoveCount) >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY && abs(beta) < VALUE_MATE_IN_MAX_PLY
&& abs(eval) < VALUE_KNOWN_WIN && abs(eval) < VALUE_KNOWN_WIN
@@ -654,7 +664,6 @@ namespace {
if ( !PvNode if ( !PvNode
&& !ss->skipNullMove && !ss->skipNullMove
&& depth > ONE_PLY && depth > ONE_PLY
&& !inCheck
&& eval >= beta && eval >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY && abs(beta) < VALUE_MATE_IN_MAX_PLY
&& pos.non_pawn_material(pos.side_to_move())) && pos.non_pawn_material(pos.side_to_move()))
@@ -671,7 +680,7 @@ namespace {
pos.do_null_move(st); pos.do_null_move(st);
(ss+1)->skipNullMove = true; (ss+1)->skipNullMove = true;
nullValue = depth-R < ONE_PLY ? -qsearch<NonPV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO) nullValue = depth-R < ONE_PLY ? -qsearch<NonPV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R); : - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R, !cutNode);
(ss+1)->skipNullMove = false; (ss+1)->skipNullMove = false;
pos.undo_null_move(); pos.undo_null_move();
@@ -686,7 +695,7 @@ namespace {
// Do verification search at high depths // Do verification search at high depths
ss->skipNullMove = true; ss->skipNullMove = true;
Value v = search<NonPV>(pos, ss, alpha, beta, depth-R); Value v = search<NonPV>(pos, ss, alpha, beta, depth-R, false);
ss->skipNullMove = false; ss->skipNullMove = false;
if (v >= beta) if (v >= beta)
@@ -706,19 +715,17 @@ namespace {
&& (ss-1)->reduction && (ss-1)->reduction
&& threatMove != MOVE_NONE && threatMove != MOVE_NONE
&& allows(pos, (ss-1)->currentMove, threatMove)) && allows(pos, (ss-1)->currentMove, threatMove))
return beta - 1; return alpha;
} }
} }
// Step 9. ProbCut (is omitted in PV nodes) // Step 9. ProbCut (skipped when in check)
// If we have a very good capture (i.e. SEE > seeValues[captured_piece_type]) // If we have a very good capture (i.e. SEE > seeValues[captured_piece_type])
// and a reduced search returns a value much above beta, we can (almost) safely // and a reduced search returns a value much above beta, we can (almost) safely
// prune the previous move. // prune the previous move.
if ( !PvNode if ( !PvNode
&& depth >= 5 * ONE_PLY && depth >= 5 * ONE_PLY
&& !inCheck
&& !ss->skipNullMove && !ss->skipNullMove
&& excludedMove == MOVE_NONE
&& abs(beta) < VALUE_MATE_IN_MAX_PLY) && abs(beta) < VALUE_MATE_IN_MAX_PLY)
{ {
Value rbeta = beta + 200; Value rbeta = beta + 200;
@@ -728,7 +735,7 @@ namespace {
assert((ss-1)->currentMove != MOVE_NONE); assert((ss-1)->currentMove != MOVE_NONE);
assert((ss-1)->currentMove != MOVE_NULL); assert((ss-1)->currentMove != MOVE_NULL);
MovePicker mp(pos, ttMove, Hist, pos.captured_piece_type()); MovePicker mp(pos, ttMove, History, pos.captured_piece_type());
CheckInfo ci(pos); CheckInfo ci(pos);
while ((move = mp.next_move<false>()) != MOVE_NONE) while ((move = mp.next_move<false>()) != MOVE_NONE)
@@ -736,39 +743,47 @@ namespace {
{ {
ss->currentMove = move; ss->currentMove = move;
pos.do_move(move, st, ci, pos.move_gives_check(move, ci)); pos.do_move(move, st, ci, pos.move_gives_check(move, ci));
value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth); value = -search<NonPV>(pos, ss+1, -rbeta, -rbeta+1, rdepth, !cutNode);
pos.undo_move(move); pos.undo_move(move);
if (value >= rbeta) if (value >= rbeta)
return value; return value;
} }
} }
// Step 10. Internal iterative deepening // Step 10. Internal iterative deepening (skipped when in check)
if ( depth >= (PvNode ? 5 * ONE_PLY : 8 * ONE_PLY) if ( depth >= (PvNode ? 5 * ONE_PLY : 8 * ONE_PLY)
&& ttMove == MOVE_NONE && ttMove == MOVE_NONE
&& (PvNode || (!inCheck && ss->staticEval + Value(256) >= beta))) && (PvNode || ss->staticEval + Value(256) >= beta))
{ {
Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4); Depth d = depth - 2 * ONE_PLY - (PvNode ? DEPTH_ZERO : depth / 4);
ss->skipNullMove = true; ss->skipNullMove = true;
search<PvNode ? PV : NonPV>(pos, ss, alpha, beta, d); search<PvNode ? PV : NonPV>(pos, ss, alpha, beta, d, true);
ss->skipNullMove = false; ss->skipNullMove = false;
tte = TT.probe(posKey); tte = TT.probe(posKey);
ttMove = tte ? tte->move() : MOVE_NONE; ttMove = tte ? tte->move() : MOVE_NONE;
} }
split_point_start: // At split points actual search starts from here moves_loop: // When in check and at SpNode search starts from here
MovePicker mp(pos, ttMove, depth, Hist, ss, PvNode ? -VALUE_INFINITE : beta); Square prevMoveSq = to_sq((ss-1)->currentMove);
Move countermoves[] = { Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].first,
Countermoves[pos.piece_on(prevMoveSq)][prevMoveSq].second };
MovePicker mp(pos, ttMove, depth, History, countermoves, ss);
CheckInfo ci(pos); CheckInfo ci(pos);
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
improving = ss->staticEval >= (ss-2)->staticEval
|| ss->staticEval == VALUE_NONE
||(ss-2)->staticEval == VALUE_NONE;
singularExtensionNode = !RootNode singularExtensionNode = !RootNode
&& !SpNode && !SpNode
&& depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY) && depth >= (PvNode ? 6 * ONE_PLY : 8 * ONE_PLY)
&& ttMove != MOVE_NONE && ttMove != MOVE_NONE
&& !excludedMove // Recursive singular search is not allowed && !excludedMove // Recursive singular search is not allowed
&& (tte->type() & BOUND_LOWER) && (tte->bound() & BOUND_LOWER)
&& tte->depth() >= depth - 3 * ONE_PLY; && tte->depth() >= depth - 3 * ONE_PLY;
// Step 11. Loop through moves // Step 11. Loop through moves
@@ -802,7 +817,7 @@ split_point_start: // At split points actual search starts from here
{ {
Signals.firstRootMove = (moveCount == 1); Signals.firstRootMove = (moveCount == 1);
if (thisThread == Threads.main_thread() && Time::now() - SearchTime > 3000) if (thisThread == Threads.main() && Time::now() - SearchTime > 3000)
sync_cout << "info depth " << depth / ONE_PLY sync_cout << "info depth " << depth / ONE_PLY
<< " currmove " << move_to_uci(move, pos.is_chess960()) << " currmove " << move_to_uci(move, pos.is_chess960())
<< " currmovenumber " << moveCount + PVIdx << sync_endl; << " currmovenumber " << moveCount + PVIdx << sync_endl;
@@ -813,12 +828,7 @@ split_point_start: // At split points actual search starts from here
givesCheck = pos.move_gives_check(move, ci); givesCheck = pos.move_gives_check(move, ci);
dangerous = givesCheck dangerous = givesCheck
|| pos.is_passed_pawn_push(move) || pos.is_passed_pawn_push(move)
|| type_of(move) == CASTLE || type_of(move) == CASTLE;
|| ( captureOrPromotion // Entering a pawn endgame?
&& type_of(pos.piece_on(to_sq(move))) != PAWN
&& type_of(move) == NORMAL
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
- PieceValue[MG][pos.piece_on(to_sq(move))] == VALUE_ZERO));
// Step 12. Extend checks and, in PV nodes, also dangerous moves // Step 12. Extend checks and, in PV nodes, also dangerous moves
if (PvNode && dangerous) if (PvNode && dangerous)
@@ -843,7 +853,7 @@ split_point_start: // At split points actual search starts from here
Value rBeta = ttValue - int(depth); Value rBeta = ttValue - int(depth);
ss->excludedMove = move; ss->excludedMove = move;
ss->skipNullMove = true; ss->skipNullMove = true;
value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2); value = search<NonPV>(pos, ss, rBeta - 1, rBeta, depth / 2, cutNode);
ss->skipNullMove = false; ss->skipNullMove = false;
ss->excludedMove = MOVE_NONE; ss->excludedMove = MOVE_NONE;
@@ -864,7 +874,7 @@ split_point_start: // At split points actual search starts from here
{ {
// Move count based pruning // Move count based pruning
if ( depth < 16 * ONE_PLY if ( depth < 16 * ONE_PLY
&& moveCount >= FutilityMoveCounts[depth] && moveCount >= FutilityMoveCounts[improving][depth]
&& (!threatMove || !refutes(pos, move, threatMove))) && (!threatMove || !refutes(pos, move, threatMove)))
{ {
if (SpNode) if (SpNode)
@@ -876,9 +886,9 @@ split_point_start: // At split points actual search starts from here
// Value based pruning // Value based pruning
// We illogically ignore reduction condition depth >= 3*ONE_PLY for predicted depth, // We illogically ignore reduction condition depth >= 3*ONE_PLY for predicted depth,
// but fixing this made program slightly weaker. // but fixing this made program slightly weaker.
Depth predictedDepth = newDepth - reduction<PvNode>(depth, moveCount); Depth predictedDepth = newDepth - reduction<PvNode>(improving, depth, moveCount);
futilityValue = ss->staticEval + ss->evalMargin + futility_margin(predictedDepth, moveCount) futilityValue = ss->staticEval + ss->evalMargin + futility_margin(predictedDepth, moveCount)
+ Gain[pos.piece_moved(move)][to_sq(move)]; + Gains[pos.piece_moved(move)][to_sq(move)];
if (futilityValue < beta) if (futilityValue < beta)
{ {
@@ -919,8 +929,8 @@ split_point_start: // At split points actual search starts from here
pvMove = PvNode && moveCount == 1; pvMove = PvNode && moveCount == 1;
ss->currentMove = move; ss->currentMove = move;
if (!SpNode && !captureOrPromotion && playedMoveCount < 64) if (!SpNode && !captureOrPromotion && quietCount < 64)
movesSearched[playedMoveCount++] = move; quietsSearched[quietCount++] = move;
// Step 14. Make the move // Step 14. Make the move
pos.do_move(move, st, ci, givesCheck); pos.do_move(move, st, ci, givesCheck);
@@ -935,12 +945,19 @@ split_point_start: // At split points actual search starts from here
&& move != ss->killers[0] && move != ss->killers[0]
&& move != ss->killers[1]) && move != ss->killers[1])
{ {
ss->reduction = reduction<PvNode>(depth, moveCount); ss->reduction = reduction<PvNode>(improving, depth, moveCount);
if (!PvNode && cutNode)
ss->reduction += ONE_PLY;
if (move == countermoves[0] || move == countermoves[1])
ss->reduction = std::max(DEPTH_ZERO, ss->reduction-ONE_PLY);
Depth d = std::max(newDepth - ss->reduction, ONE_PLY); Depth d = std::max(newDepth - ss->reduction, ONE_PLY);
if (SpNode) if (SpNode)
alpha = splitPoint->alpha; alpha = splitPoint->alpha;
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d); value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
doFullDepthSearch = (value > alpha && ss->reduction != DEPTH_ZERO); doFullDepthSearch = (value > alpha && ss->reduction != DEPTH_ZERO);
ss->reduction = DEPTH_ZERO; ss->reduction = DEPTH_ZERO;
@@ -957,7 +974,7 @@ split_point_start: // At split points actual search starts from here
value = newDepth < ONE_PLY ? value = newDepth < ONE_PLY ?
givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) givesCheck ? -qsearch<NonPV, true>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
: -qsearch<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) : -qsearch<NonPV, false>(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO)
: - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth); : - search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
} }
// Only for PV nodes do a full PV search on the first move or after a fail // Only for PV nodes do a full PV search on the first move or after a fail
@@ -967,7 +984,7 @@ split_point_start: // At split points actual search starts from here
value = newDepth < ONE_PLY ? value = newDepth < ONE_PLY ?
givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha, DEPTH_ZERO) givesCheck ? -qsearch<PV, true>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: -qsearch<PV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO) : -qsearch<PV, false>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: - search<PV>(pos, ss+1, -beta, -alpha, newDepth); : - search<PV>(pos, ss+1, -beta, -alpha, newDepth, false);
// Step 17. Undo move // Step 17. Undo move
pos.undo_move(move); pos.undo_move(move);
@@ -1042,7 +1059,7 @@ split_point_start: // At split points actual search starts from here
assert(bestValue < beta); assert(bestValue < beta);
thisThread->split<FakeSplit>(pos, ss, alpha, beta, &bestValue, &bestMove, thisThread->split<FakeSplit>(pos, ss, alpha, beta, &bestValue, &bestMove,
depth, threatMove, moveCount, &mp, NT); depth, threatMove, moveCount, &mp, NT, cutNode);
if (bestValue >= beta) if (bestValue >= beta)
break; break;
} }
@@ -1064,41 +1081,37 @@ split_point_start: // At split points actual search starts from here
// If we have pruned all the moves without searching return a fail-low score // If we have pruned all the moves without searching return a fail-low score
if (bestValue == -VALUE_INFINITE) if (bestValue == -VALUE_INFINITE)
{
assert(!playedMoveCount);
bestValue = alpha; bestValue = alpha;
}
if (bestValue >= beta) // Failed high TT.store(posKey, value_to_tt(bestValue, ss->ply),
{ bestValue >= beta ? BOUND_LOWER :
TT.store(posKey, value_to_tt(bestValue, ss->ply), BOUND_LOWER, depth, PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
bestMove, ss->staticEval, ss->evalMargin); depth, bestMove, ss->staticEval, ss->evalMargin);
if (!pos.is_capture_or_promotion(bestMove) && !inCheck) // Quiet best move: update killers, history and countermoves
if ( bestValue >= beta
&& !pos.is_capture_or_promotion(bestMove)
&& !inCheck)
{ {
if (bestMove != ss->killers[0]) if (ss->killers[0] != bestMove)
{ {
ss->killers[1] = ss->killers[0]; ss->killers[1] = ss->killers[0];
ss->killers[0] = bestMove; ss->killers[0] = bestMove;
} }
// Increase history value of the cut-off move // Increase history value of the cut-off move and decrease all the other
// played non-capture moves.
Value bonus = Value(int(depth) * int(depth)); Value bonus = Value(int(depth) * int(depth));
Hist.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus); History.update(pos.piece_moved(bestMove), to_sq(bestMove), bonus);
for (int i = 0; i < quietCount - 1; i++)
// Decrease history of all the other played non-capture moves
for (int i = 0; i < playedMoveCount - 1; i++)
{ {
Move m = movesSearched[i]; Move m = quietsSearched[i];
Hist.update(pos.piece_moved(m), to_sq(m), -bonus); History.update(pos.piece_moved(m), to_sq(m), -bonus);
} }
if (is_ok((ss-1)->currentMove))
Countermoves.update(pos.piece_on(prevMoveSq), prevMoveSq, bestMove);
} }
}
else // Failed low or PV search
TT.store(posKey, value_to_tt(bestValue, ss->ply),
PvNode && bestMove != MOVE_NONE ? BOUND_EXACT : BOUND_UPPER,
depth, bestMove, ss->staticEval, ss->evalMargin);
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
@@ -1126,7 +1139,7 @@ split_point_start: // At split points actual search starts from here
Key posKey; Key posKey;
Move ttMove, move, bestMove; Move ttMove, move, bestMove;
Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha;
bool givesCheck, enoughMaterial, evasionPrunable; bool givesCheck, evasionPrunable;
Depth ttDepth; Depth ttDepth;
// To flag BOUND_EXACT a node with eval above alpha and no available moves // To flag BOUND_EXACT a node with eval above alpha and no available moves
@@ -1146,8 +1159,7 @@ split_point_start: // At split points actual search starts from here
ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS ttDepth = InCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS
: DEPTH_QS_NO_CHECKS; : DEPTH_QS_NO_CHECKS;
// Transposition table lookup. At PV nodes, we don't use the TT for // Transposition table lookup
// pruning, but only for move ordering.
posKey = pos.key(); posKey = pos.key();
tte = TT.probe(posKey); tte = TT.probe(posKey);
ttMove = tte ? tte->move() : MOVE_NONE; ttMove = tte ? tte->move() : MOVE_NONE;
@@ -1156,9 +1168,9 @@ split_point_start: // At split points actual search starts from here
if ( tte if ( tte
&& tte->depth() >= ttDepth && tte->depth() >= ttDepth
&& ttValue != VALUE_NONE // Only in case of TT access race && ttValue != VALUE_NONE // Only in case of TT access race
&& ( PvNode ? tte->type() == BOUND_EXACT && ( PvNode ? tte->bound() == BOUND_EXACT
: ttValue >= beta ? (tte->type() & BOUND_LOWER) : ttValue >= beta ? (tte->bound() & BOUND_LOWER)
: (tte->type() & BOUND_UPPER))) : (tte->bound() & BOUND_UPPER)))
{ {
ss->currentMove = ttMove; // Can be MOVE_NONE ss->currentMove = ttMove; // Can be MOVE_NONE
return ttValue; return ttValue;
@@ -1169,7 +1181,6 @@ split_point_start: // At split points actual search starts from here
{ {
ss->staticEval = ss->evalMargin = VALUE_NONE; ss->staticEval = ss->evalMargin = VALUE_NONE;
bestValue = futilityBase = -VALUE_INFINITE; bestValue = futilityBase = -VALUE_INFINITE;
enoughMaterial = false;
} }
else else
{ {
@@ -1197,14 +1208,13 @@ split_point_start: // At split points actual search starts from here
alpha = bestValue; alpha = bestValue;
futilityBase = ss->staticEval + ss->evalMargin + Value(128); futilityBase = ss->staticEval + ss->evalMargin + Value(128);
enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMg;
} }
// Initialize a MovePicker object for the current position, and prepare // Initialize a MovePicker object for the current position, and prepare
// to search the moves. Because the depth is <= 0 here, only captures, // to search the moves. Because the depth is <= 0 here, only captures,
// queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will
// be generated. // be generated.
MovePicker mp(pos, ttMove, depth, Hist, to_sq((ss-1)->currentMove)); MovePicker mp(pos, ttMove, depth, History, to_sq((ss-1)->currentMove));
CheckInfo ci(pos); CheckInfo ci(pos);
// Loop through the moves until no moves remain or a beta cutoff occurs // Loop through the moves until no moves remain or a beta cutoff occurs
@@ -1219,7 +1229,6 @@ split_point_start: // At split points actual search starts from here
&& !InCheck && !InCheck
&& !givesCheck && !givesCheck
&& move != ttMove && move != ttMove
&& enoughMaterial
&& type_of(move) != PROMOTION && type_of(move) != PROMOTION
&& !pos.is_passed_pawn_push(move)) && !pos.is_passed_pawn_push(move))
{ {
@@ -1236,7 +1245,6 @@ split_point_start: // At split points actual search starts from here
// Prune moves with negative or equal SEE and also moves with positive // Prune moves with negative or equal SEE and also moves with positive
// SEE where capturing piece loses a tempo and SEE < beta - futilityBase. // SEE where capturing piece loses a tempo and SEE < beta - futilityBase.
if ( futilityBase < beta if ( futilityBase < beta
&& depth < DEPTH_ZERO
&& pos.see(move, beta - futilityBase) <= 0) && pos.see(move, beta - futilityBase) <= 0)
{ {
bestValue = std::max(bestValue, futilityBase); bestValue = std::max(bestValue, futilityBase);
@@ -1245,8 +1253,7 @@ split_point_start: // At split points actual search starts from here
} }
// Detect non-capture evasions that are candidate to be pruned // Detect non-capture evasions that are candidate to be pruned
evasionPrunable = !PvNode evasionPrunable = InCheck
&& InCheck
&& bestValue > VALUE_MATED_IN_MAX_PLY && bestValue > VALUE_MATED_IN_MAX_PLY
&& !pos.is_capture(move) && !pos.is_capture(move)
&& !pos.can_castle(pos.side_to_move()); && !pos.can_castle(pos.side_to_move());
@@ -1449,18 +1456,18 @@ split_point_start: // At split points actual search starts from here
{ {
// Update occupancy as if the piece and the threat are moving // Update occupancy as if the piece and the threat are moving
Bitboard occ = pos.pieces() ^ m1from ^ m1to ^ m2from; Bitboard occ = pos.pieces() ^ m1from ^ m1to ^ m2from;
Piece piece = pos.piece_on(m1from); Piece pc = pos.piece_on(m1from);
// The moved piece attacks the square 'tto' ? // The moved piece attacks the square 'tto' ?
if (pos.attacks_from(piece, m1to, occ) & m2to) if (pos.attacks_from(pc, m1to, occ) & m2to)
return true; return true;
// Scan for possible X-ray attackers behind the moved piece // Scan for possible X-ray attackers behind the moved piece
Bitboard xray = (attacks_bb< ROOK>(m2to, occ) & pos.pieces(color_of(piece), QUEEN, ROOK)) Bitboard xray = (attacks_bb< ROOK>(m2to, occ) & pos.pieces(color_of(pc), QUEEN, ROOK))
| (attacks_bb<BISHOP>(m2to, occ) & pos.pieces(color_of(piece), QUEEN, BISHOP)); | (attacks_bb<BISHOP>(m2to, occ) & pos.pieces(color_of(pc), QUEEN, BISHOP));
// Verify attackers are triggered by our move and not already existing // Verify attackers are triggered by our move and not already existing
if (xray && (xray ^ (xray & pos.attacks_from<QUEEN>(m2to)))) if (unlikely(xray) && (xray & ~pos.attacks_from<QUEEN>(m2to)))
return true; return true;
} }
@@ -1521,7 +1528,7 @@ split_point_start: // At split points actual search starts from here
string uci_pv(const Position& pos, int depth, Value alpha, Value beta) { string uci_pv(const Position& pos, int depth, Value alpha, Value beta) {
std::stringstream s; std::stringstream s;
Time::point elaspsed = Time::now() - SearchTime + 1; Time::point elapsed = Time::now() - SearchTime + 1;
size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size()); size_t uciPVSize = std::min((size_t)Options["MultiPV"], RootMoves.size());
int selDepth = 0; int selDepth = 0;
@@ -1546,8 +1553,8 @@ split_point_start: // At split points actual search starts from here
<< " seldepth " << selDepth << " seldepth " << selDepth
<< " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v)) << " score " << (i == PVIdx ? score_to_uci(v, alpha, beta) : score_to_uci(v))
<< " nodes " << pos.nodes_searched() << " nodes " << pos.nodes_searched()
<< " nps " << pos.nodes_searched() * 1000 / elaspsed << " nps " << pos.nodes_searched() * 1000 / elapsed
<< " time " << elaspsed << " time " << elapsed
<< " multipv " << i + 1 << " multipv " << i + 1
<< " pv"; << " pv";
@@ -1568,8 +1575,8 @@ split_point_start: // At split points actual search starts from here
void RootMove::extract_pv_from_tt(Position& pos) { void RootMove::extract_pv_from_tt(Position& pos) {
StateInfo state[MAX_PLY_PLUS_2], *st = state; StateInfo state[MAX_PLY_PLUS_6], *st = state;
TTEntry* tte; const TTEntry* tte;
int ply = 0; int ply = 0;
Move m = pv[0]; Move m = pv[0];
@@ -1601,8 +1608,8 @@ void RootMove::extract_pv_from_tt(Position& pos) {
void RootMove::insert_pv_in_tt(Position& pos) { void RootMove::insert_pv_in_tt(Position& pos) {
StateInfo state[MAX_PLY_PLUS_2], *st = state; StateInfo state[MAX_PLY_PLUS_6], *st = state;
TTEntry* tte; const TTEntry* tte;
int ply = 0; int ply = 0;
do { do {
@@ -1671,15 +1678,16 @@ void Thread::idle_loop() {
Threads.mutex.lock(); Threads.mutex.lock();
assert(searching); assert(searching);
assert(activeSplitPoint);
SplitPoint* sp = activeSplitPoint; SplitPoint* sp = activeSplitPoint;
Threads.mutex.unlock(); Threads.mutex.unlock();
Stack ss[MAX_PLY_PLUS_2]; Stack stack[MAX_PLY_PLUS_6], *ss = stack+2; // To allow referencing (ss-2)
Position pos(*sp->pos, this); Position pos(*sp->pos, this);
memcpy(ss, sp->ss - 1, 4 * sizeof(Stack)); std::memcpy(ss-2, sp->ss-2, 5 * sizeof(Stack));
(ss+1)->splitPoint = sp; ss->splitPoint = sp;
sp->mutex.lock(); sp->mutex.lock();
@@ -1689,13 +1697,13 @@ void Thread::idle_loop() {
switch (sp->nodeType) { switch (sp->nodeType) {
case Root: case Root:
search<SplitPointRoot>(pos, ss+1, sp->alpha, sp->beta, sp->depth); search<SplitPointRoot>(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode);
break; break;
case PV: case PV:
search<SplitPointPV>(pos, ss+1, sp->alpha, sp->beta, sp->depth); search<SplitPointPV>(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode);
break; break;
case NonPV: case NonPV:
search<SplitPointNonPV>(pos, ss+1, sp->alpha, sp->beta, sp->depth); search<SplitPointNonPV>(pos, ss, sp->alpha, sp->beta, sp->depth, sp->cutNode);
break; break;
default: default:
assert(false); assert(false);
+3 -3
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(SEARCH_H_INCLUDED) #ifndef SEARCH_H_INCLUDED
#define SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED
#include <cstring> #include <cstring>
@@ -79,7 +79,7 @@ struct RootMove {
struct LimitsType { struct LimitsType {
LimitsType() { memset(this, 0, sizeof(LimitsType)); } LimitsType() { std::memset(this, 0, sizeof(LimitsType)); }
bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); } bool use_time_management() const { return !(mate | movetime | depth | nodes | infinite); }
int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder; int time[COLOR_NB], inc[COLOR_NB], movestogo, depth, nodes, movetime, mate, infinite, ponder;
@@ -109,4 +109,4 @@ extern void think();
} // namespace Search } // namespace Search
#endif // !defined(SEARCH_H_INCLUDED) #endif // #ifndef SEARCH_H_INCLUDED
+79 -66
View File
@@ -19,7 +19,6 @@
#include <algorithm> // For std::count #include <algorithm> // For std::count
#include <cassert> #include <cassert>
#include <iostream>
#include "movegen.h" #include "movegen.h"
#include "search.h" #include "search.h"
@@ -30,42 +29,64 @@ using namespace Search;
ThreadPool Threads; // Global object ThreadPool Threads; // Global object
namespace { extern "C" { namespace {
// start_routine() is the C function which is called when a new thread // start_routine() is the C function which is called when a new thread
// is launched. It is a wrapper to the virtual function idle_loop(). // is launched. It is a wrapper to the virtual function idle_loop().
long start_routine(Thread* th) { th->idle_loop(); return 0; } extern "C" { long start_routine(ThreadBase* th) { th->idle_loop(); return 0; } }
} }
// Thread c'tor starts a newly-created thread of execution that will call // Helpers to launch a thread after creation and joining before delete. Must be
// the the virtual function idle_loop(), going immediately to sleep. // outside Thread c'tor and d'tor because object shall be fully initialized
// when start_routine (and hence virtual idle_loop) is called and when joining.
template<typename T> T* new_thread() {
T* th = new T();
thread_create(th->handle, start_routine, th); // Will go to sleep
return th;
}
void delete_thread(ThreadBase* th) {
th->exit = true; // Search must be already finished
th->notify_one();
thread_join(th->handle); // Wait for thread termination
delete th;
}
}
// ThreadBase::notify_one() wakes up the thread when there is some work to do
void ThreadBase::notify_one() {
mutex.lock();
sleepCondition.notify_one();
mutex.unlock();
}
// ThreadBase::wait_for() set the thread to sleep until condition 'b' turns true
void ThreadBase::wait_for(volatile const bool& b) {
mutex.lock();
while (!b) sleepCondition.wait(mutex);
mutex.unlock();
}
// Thread c'tor just inits data but does not launch any thread of execution that
// instead will be started only upon c'tor returns.
Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC Thread::Thread() /* : splitPoints() */ { // Value-initialization bug in MSVC
searching = exit = false; searching = false;
maxPly = splitPointsSize = 0; maxPly = splitPointsSize = 0;
activeSplitPoint = NULL; activeSplitPoint = NULL;
activePosition = NULL; activePosition = NULL;
idx = Threads.size(); idx = Threads.size();
if (!thread_create(handle, start_routine, this))
{
std::cerr << "Failed to create thread number " << idx << std::endl;
::exit(EXIT_FAILURE);
}
}
// Thread d'tor waits for thread termination before to return
Thread::~Thread() {
exit = true; // Search must be already finished
notify_one();
thread_join(handle); // Wait for thread termination
} }
@@ -123,26 +144,6 @@ void MainThread::idle_loop() {
} }
// Thread::notify_one() wakes up the thread when there is some search to do
void Thread::notify_one() {
mutex.lock();
sleepCondition.notify_one();
mutex.unlock();
}
// Thread::wait_for() set the thread to sleep until condition 'b' turns true
void Thread::wait_for(volatile const bool& b) {
mutex.lock();
while (!b) sleepCondition.wait(mutex);
mutex.unlock();
}
// Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the // Thread::cutoff_occurred() checks whether a beta cutoff has occurred in the
// current active split point, or in some ancestor of the split point. // current active split point, or in some ancestor of the split point.
@@ -163,7 +164,7 @@ bool Thread::cutoff_occurred() const {
// which are busy searching the split point at the top of slaves split point // which are busy searching the split point at the top of slaves split point
// stack (the "helpful master concept" in YBWC terminology). // stack (the "helpful master concept" in YBWC terminology).
bool Thread::is_available_to(Thread* master) const { bool Thread::is_available_to(const Thread* master) const {
if (searching) if (searching)
return false; return false;
@@ -186,8 +187,8 @@ bool Thread::is_available_to(Thread* master) const {
void ThreadPool::init() { void ThreadPool::init() {
sleepWhileIdle = true; sleepWhileIdle = true;
timer = new TimerThread(); timer = new_thread<TimerThread>();
push_back(new MainThread()); push_back(new_thread<MainThread>());
read_uci_options(); read_uci_options();
} }
@@ -196,10 +197,10 @@ void ThreadPool::init() {
void ThreadPool::exit() { void ThreadPool::exit() {
delete timer; // As first because check_time() accesses threads data delete_thread(timer); // As first because check_time() accesses threads data
for (iterator it = begin(); it != end(); ++it) for (iterator it = begin(); it != end(); ++it)
delete *it; delete_thread(*it);
} }
@@ -216,12 +217,19 @@ void ThreadPool::read_uci_options() {
assert(requested > 0); assert(requested > 0);
// Value 0 has a special meaning: We determine the optimal minimum split depth
// automatically. Anyhow the minimumSplitDepth should never be under 4 plies.
if (!minimumSplitDepth)
minimumSplitDepth = (requested < 8 ? 4 : 7) * ONE_PLY;
else
minimumSplitDepth = std::max(4 * ONE_PLY, minimumSplitDepth);
while (size() < requested) while (size() < requested)
push_back(new Thread()); push_back(new_thread<Thread>());
while (size() > requested) while (size() > requested)
{ {
delete back(); delete_thread(back());
pop_back(); pop_back();
} }
} }
@@ -230,7 +238,7 @@ void ThreadPool::read_uci_options() {
// slave_available() tries to find an idle thread which is available as a slave // slave_available() tries to find an idle thread which is available as a slave
// for the thread 'master'. // for the thread 'master'.
Thread* ThreadPool::available_slave(Thread* master) const { Thread* ThreadPool::available_slave(const Thread* master) const {
for (const_iterator it = begin(); it != end(); ++it) for (const_iterator it = begin(); it != end(); ++it)
if ((*it)->is_available_to(master)) if ((*it)->is_available_to(master))
@@ -250,9 +258,9 @@ Thread* ThreadPool::available_slave(Thread* master) const {
// search() then split() returns. // search() then split() returns.
template <bool Fake> template <bool Fake>
void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bestValue, void Thread::split(Position& pos, const Stack* ss, Value alpha, Value beta, Value* bestValue,
Move* bestMove, Depth depth, Move threatMove, int moveCount, Move* bestMove, Depth depth, Move threatMove, int moveCount,
MovePicker* movePicker, int nodeType) { MovePicker* movePicker, int nodeType, bool cutNode) {
assert(pos.pos_is_ok()); assert(pos.pos_is_ok());
assert(*bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE); assert(*bestValue <= alpha && alpha < beta && beta <= VALUE_INFINITE);
@@ -274,6 +282,7 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes
sp.alpha = alpha; sp.alpha = alpha;
sp.beta = beta; sp.beta = beta;
sp.nodeType = nodeType; sp.nodeType = nodeType;
sp.cutNode = cutNode;
sp.movePicker = movePicker; sp.movePicker = movePicker;
sp.moveCount = moveCount; sp.moveCount = moveCount;
sp.pos = &pos; sp.pos = &pos;
@@ -339,15 +348,15 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes
} }
// Explicit template instantiations // Explicit template instantiations
template void Thread::split<false>(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int); template void Thread::split<false>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int, bool);
template void Thread::split< true>(Position&, Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int); template void Thread::split< true>(Position&, const Stack*, Value, Value, Value*, Move*, Depth, Move, int, MovePicker*, int, bool);
// wait_for_think_finished() waits for main thread to go to sleep then returns // wait_for_think_finished() waits for main thread to go to sleep then returns
void ThreadPool::wait_for_think_finished() { void ThreadPool::wait_for_think_finished() {
MainThread* t = main_thread(); MainThread* t = main();
t->mutex.lock(); t->mutex.lock();
while (t->thinking) sleepCondition.wait(t->mutex); while (t->thinking) sleepCondition.wait(t->mutex);
t->mutex.unlock(); t->mutex.unlock();
@@ -366,16 +375,20 @@ void ThreadPool::start_thinking(const Position& pos, const LimitsType& limits,
Signals.stopOnPonderhit = Signals.firstRootMove = false; Signals.stopOnPonderhit = Signals.firstRootMove = false;
Signals.stop = Signals.failedLowAtRoot = false; Signals.stop = Signals.failedLowAtRoot = false;
RootMoves.clear();
RootPos = pos; RootPos = pos;
Limits = limits; Limits = limits;
if (states.get()) // If we don't set a new position, preserve current state
{
SetupStates = states; // Ownership transfer here SetupStates = states; // Ownership transfer here
RootMoves.clear(); assert(!states.get());
}
for (MoveList<LEGAL> ml(pos); !ml.end(); ++ml)
if ( searchMoves.empty() for (MoveList<LEGAL> it(pos); *it; ++it)
|| std::count(searchMoves.begin(), searchMoves.end(), ml.move())) if ( searchMoves.empty()
RootMoves.push_back(RootMove(ml.move())); || std::count(searchMoves.begin(), searchMoves.end(), *it))
RootMoves.push_back(RootMove(*it));
main_thread()->thinking = true;
main_thread()->notify_one(); // Starts main thread main()->thinking = true;
main()->notify_one(); // Starts main thread
} }
+29 -18
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(THREAD_H_INCLUDED) #ifndef THREAD_H_INCLUDED
#define THREAD_H_INCLUDED #define THREAD_H_INCLUDED
#include <vector> #include <vector>
@@ -68,6 +68,7 @@ struct SplitPoint {
Value beta; Value beta;
int nodeType; int nodeType;
Move threatMove; Move threatMove;
bool cutNode;
// Const pointers to shared data // Const pointers to shared data
MovePicker* movePicker; MovePicker* movePicker;
@@ -85,25 +86,39 @@ struct SplitPoint {
}; };
/// ThreadBase struct is the base of the hierarchy from where we derive all the
/// specialized thread classes.
struct ThreadBase {
ThreadBase() : exit(false) {}
virtual ~ThreadBase() {}
virtual void idle_loop() = 0;
void notify_one();
void wait_for(volatile const bool& b);
Mutex mutex;
ConditionVariable sleepCondition;
NativeHandle handle;
volatile bool exit;
};
/// Thread struct keeps together all the thread related stuff like locks, state /// Thread struct keeps together all the thread related stuff like locks, state
/// and especially split points. We also use per-thread pawn and material hash /// and especially split points. We also use per-thread pawn and material hash
/// tables so that once we get a pointer to an entry its life time is unlimited /// 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. /// and we don't have to care about someone changing the entry under our feet.
struct Thread { struct Thread : public ThreadBase {
Thread(); Thread();
virtual ~Thread();
virtual void idle_loop(); virtual void idle_loop();
void notify_one();
bool cutoff_occurred() const; bool cutoff_occurred() const;
bool is_available_to(Thread* master) const; bool is_available_to(const Thread* master) const;
void wait_for(volatile const bool& b);
template <bool Fake> template <bool Fake>
void split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove, void split(Position& pos, const Search::Stack* ss, Value alpha, Value beta, Value* bestValue, Move* bestMove,
Depth depth, Move threatMove, int moveCount, MovePicker* movePicker, int nodeType); Depth depth, Move threatMove, int moveCount, MovePicker* movePicker, int nodeType, bool cutNode);
SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD]; SplitPoint splitPoints[MAX_SPLITPOINTS_PER_THREAD];
Material::Table materialTable; Material::Table materialTable;
@@ -112,17 +127,13 @@ struct Thread {
Position* activePosition; Position* activePosition;
size_t idx; size_t idx;
int maxPly; int maxPly;
Mutex mutex;
ConditionVariable sleepCondition;
NativeHandle handle;
SplitPoint* volatile activeSplitPoint; SplitPoint* volatile activeSplitPoint;
volatile int splitPointsSize; volatile int splitPointsSize;
volatile bool searching; volatile bool searching;
volatile bool exit;
}; };
/// MainThread and TimerThread are sublassed from Thread to characterize the two /// MainThread and TimerThread are derived classes used to characterize the two
/// special threads: the main one and the recurring timer. /// special threads: the main one and the recurring timer.
struct MainThread : public Thread { struct MainThread : public Thread {
@@ -131,7 +142,7 @@ struct MainThread : public Thread {
volatile bool thinking; volatile bool thinking;
}; };
struct TimerThread : public Thread { struct TimerThread : public ThreadBase {
TimerThread() : msec(0) {} TimerThread() : msec(0) {}
virtual void idle_loop(); virtual void idle_loop();
int msec; int msec;
@@ -147,9 +158,9 @@ struct ThreadPool : public std::vector<Thread*> {
void init(); // No c'tor and d'tor, threads rely on globals that should void init(); // No c'tor and d'tor, threads rely on globals that should
void exit(); // be initialized and valid during the whole thread lifetime. void exit(); // be initialized and valid during the whole thread lifetime.
MainThread* main_thread() { return static_cast<MainThread*>((*this)[0]); } MainThread* main() { return static_cast<MainThread*>((*this)[0]); }
void read_uci_options(); void read_uci_options();
Thread* available_slave(Thread* master) const; Thread* available_slave(const Thread* master) const;
void wait_for_think_finished(); void wait_for_think_finished();
void start_thinking(const Position&, const Search::LimitsType&, void start_thinking(const Position&, const Search::LimitsType&,
const std::vector<Move>&, Search::StateStackPtr&); const std::vector<Move>&, Search::StateStackPtr&);
@@ -164,4 +175,4 @@ struct ThreadPool : public std::vector<Thread*> {
extern ThreadPool Threads; extern ThreadPool Threads;
#endif // !defined(THREAD_H_INCLUDED) #endif // #ifndef THREAD_H_INCLUDED
+2 -2
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(TIMEMAN_H_INCLUDED) #ifndef TIMEMAN_H_INCLUDED
#define TIMEMAN_H_INCLUDED #define TIMEMAN_H_INCLUDED
/// The TimeManager class computes the optimal time to think depending on the /// The TimeManager class computes the optimal time to think depending on the
@@ -36,4 +36,4 @@ private:
int unstablePVExtraTime; int unstablePVExtraTime;
}; };
#endif // !defined(TIMEMAN_H_INCLUDED) #endif // #ifndef TIMEMAN_H_INCLUDED
+30 -30
View File
@@ -40,17 +40,17 @@ void TranspositionTable::set_size(size_t mbSize) {
return; return;
hashMask = size - ClusterSize; hashMask = size - ClusterSize;
delete [] table; free(mem);
table = new (std::nothrow) TTEntry[size]; mem = calloc(size * sizeof(TTEntry) + CACHE_LINE_SIZE - 1, 1);
if (!table) if (!mem)
{ {
std::cerr << "Failed to allocate " << mbSize std::cerr << "Failed to allocate " << mbSize
<< "MB for transposition table." << std::endl; << "MB for transposition table." << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
clear(); // Operator new is not guaranteed to initialize memory to zero table = (TTEntry*)((uintptr_t(mem) + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1));
} }
@@ -60,7 +60,24 @@ void TranspositionTable::set_size(size_t mbSize) {
void TranspositionTable::clear() { void TranspositionTable::clear() {
memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry)); std::memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry));
}
/// TranspositionTable::probe() looks up the current position in the
/// transposition table. Returns a pointer to the TTEntry or NULL if
/// position is not found.
const TTEntry* TranspositionTable::probe(const Key key) const {
const TTEntry* tte = first_entry(key);
uint32_t key32 = key >> 32;
for (unsigned i = 0; i < ClusterSize; i++, tte++)
if (tte->key() == key32)
return tte;
return NULL;
} }
@@ -72,7 +89,7 @@ void TranspositionTable::clear() {
/// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from /// more valuable than a TTEntry t2 if t1 is from the current search and t2 is from
/// a previous search, or if the depth of t1 is bigger than the depth of t2. /// a previous search, or if the depth of t1 is bigger than the depth of t2.
void TranspositionTable::store(const Key key, Value v, Bound t, Depth d, Move m, Value statV, Value kingD) { void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV, Value evalM) {
int c1, c2, c3; int c1, c2, c3;
TTEntry *tte, *replace; TTEntry *tte, *replace;
@@ -84,38 +101,21 @@ void TranspositionTable::store(const Key key, Value v, Bound t, Depth d, Move m,
{ {
if (!tte->key() || tte->key() == key32) // Empty or overwrite old if (!tte->key() || tte->key() == key32) // Empty or overwrite old
{ {
// Preserve any existing ttMove if (!m)
if (m == MOVE_NONE) m = tte->move(); // Preserve any existing ttMove
m = tte->move();
tte->save(key32, v, t, d, m, generation, statV, kingD); replace = tte;
return; break;
} }
// Implement replace strategy // Implement replace strategy
c1 = (replace->generation() == generation ? 2 : 0); c1 = (replace->generation() == generation ? 2 : 0);
c2 = (tte->generation() == generation || tte->type() == BOUND_EXACT ? -2 : 0); c2 = (tte->generation() == generation || tte->bound() == BOUND_EXACT ? -2 : 0);
c3 = (tte->depth() < replace->depth() ? 1 : 0); c3 = (tte->depth() < replace->depth() ? 1 : 0);
if (c1 + c2 + c3 > 0) if (c1 + c2 + c3 > 0)
replace = tte; replace = tte;
} }
replace->save(key32, v, t, d, m, generation, statV, kingD);
} replace->save(key32, v, b, d, m, generation, statV, evalM);
/// TranspositionTable::probe() looks up the current position in the
/// transposition table. Returns a pointer to the TTEntry or NULL if
/// position is not found.
TTEntry* TranspositionTable::probe(const Key key) const {
TTEntry* tte = first_entry(key);
uint32_t key32 = key >> 32;
for (unsigned i = 0; i < ClusterSize; i++, tte++)
if (tte->key() == key32)
return tte;
return NULL;
} }
+20 -27
View File
@@ -17,51 +17,43 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(TT_H_INCLUDED) #ifndef TT_H_INCLUDED
#define TT_H_INCLUDED #define TT_H_INCLUDED
#include "misc.h" #include "misc.h"
#include "types.h" #include "types.h"
/// The TTEntry is the class of transposition table entries /// The TTEntry is the 128 bit transposition table entry, defined as below:
/// ///
/// A TTEntry needs 128 bits to be stored /// key: 32 bit
/// /// move: 16 bit
/// bit 0-31: key /// bound type: 8 bit
/// bit 32-63: data /// generation: 8 bit
/// bit 64-79: value /// value: 16 bit
/// bit 80-95: depth /// depth: 16 bit
/// bit 96-111: static value /// static value: 16 bit
/// bit 112-127: margin of static value /// static margin: 16 bit
///
/// the 32 bits of the data field are so defined
///
/// bit 0-15: move
/// bit 16-20: not used
/// bit 21-22: value type
/// bit 23-31: generation
class TTEntry { struct TTEntry {
public:
void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) { void save(uint32_t k, Value v, Bound b, Depth d, Move m, int g, Value ev, Value em) {
key32 = (uint32_t)k; key32 = (uint32_t)k;
move16 = (uint16_t)m; move16 = (uint16_t)m;
bound = (uint8_t)b; bound8 = (uint8_t)b;
generation8 = (uint8_t)g; generation8 = (uint8_t)g;
value16 = (int16_t)v; value16 = (int16_t)v;
depth16 = (int16_t)d; depth16 = (int16_t)d;
evalValue = (int16_t)ev; evalValue = (int16_t)ev;
evalMargin = (int16_t)em; evalMargin = (int16_t)em;
} }
void set_generation(int g) { generation8 = (uint8_t)g; } void set_generation(uint8_t g) { generation8 = g; }
uint32_t key() const { return key32; } uint32_t key() const { return key32; }
Depth depth() const { return (Depth)depth16; } Depth depth() const { return (Depth)depth16; }
Move move() const { return (Move)move16; } Move move() const { return (Move)move16; }
Value value() const { return (Value)value16; } Value value() const { return (Value)value16; }
Bound type() const { return (Bound)bound; } Bound bound() const { return (Bound)bound8; }
int generation() const { return (int)generation8; } int generation() const { return (int)generation8; }
Value eval_value() const { return (Value)evalValue; } Value eval_value() const { return (Value)evalValue; }
Value eval_margin() const { return (Value)evalMargin; } Value eval_margin() const { return (Value)evalMargin; }
@@ -69,7 +61,7 @@ public:
private: private:
uint32_t key32; uint32_t key32;
uint16_t move16; uint16_t move16;
uint8_t bound, generation8; uint8_t bound8, generation8;
int16_t value16, depth16, evalValue, evalMargin; int16_t value16, depth16, evalValue, evalMargin;
}; };
@@ -85,10 +77,10 @@ class TranspositionTable {
static const unsigned ClusterSize = 4; // A cluster is 64 Bytes static const unsigned ClusterSize = 4; // A cluster is 64 Bytes
public: public:
~TranspositionTable() { delete [] table; } ~TranspositionTable() { free(mem); }
void new_search() { generation++; } void new_search() { generation++; }
TTEntry* probe(const Key key) const; const TTEntry* probe(const Key key) const;
TTEntry* first_entry(const Key key) const; TTEntry* first_entry(const Key key) const;
void refresh(const TTEntry* tte) const; void refresh(const TTEntry* tte) const;
void set_size(size_t mbSize); void set_size(size_t mbSize);
@@ -98,7 +90,8 @@ public:
private: private:
uint32_t hashMask; uint32_t hashMask;
TTEntry* table; TTEntry* table;
uint8_t generation; // Size must be not bigger then TTEntry::generation8 void* mem;
uint8_t generation; // Size must be not bigger than TTEntry::generation8
}; };
extern TranspositionTable TT; extern TranspositionTable TT;
@@ -122,4 +115,4 @@ inline void TranspositionTable::refresh(const TTEntry* tte) const {
const_cast<TTEntry*>(tte)->set_generation(generation); const_cast<TTEntry*>(tte)->set_generation(generation);
} }
#endif // !defined(TT_H_INCLUDED) #endif // #ifndef TT_H_INCLUDED
+35 -84
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(TYPES_H_INCLUDED) #ifndef TYPES_H_INCLUDED
#define TYPES_H_INCLUDED #define TYPES_H_INCLUDED
/// For Linux and OSX configuration is done automatically using Makefile. To get /// For Linux and OSX configuration is done automatically using Makefile. To get
@@ -42,6 +42,8 @@
#include "platform.h" #include "platform.h"
#define unlikely(x) (x) // For code annotation purposes
#if defined(_WIN64) && !defined(IS_64BIT) #if defined(_WIN64) && !defined(IS_64BIT)
# include <intrin.h> // MSVC popcnt and bsfq instrinsics # include <intrin.h> // MSVC popcnt and bsfq instrinsics
# define IS_64BIT # define IS_64BIT
@@ -56,13 +58,14 @@
# include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch() # include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch()
# endif # endif
#define CACHE_LINE_SIZE 64
#if defined(_MSC_VER) || defined(__INTEL_COMPILER) #if defined(_MSC_VER) || defined(__INTEL_COMPILER)
# define CACHE_LINE_ALIGNMENT __declspec(align(64)) # define CACHE_LINE_ALIGNMENT __declspec(align(CACHE_LINE_SIZE))
#else #else
# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64))) # define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(CACHE_LINE_SIZE)))
#endif #endif
#if defined(_MSC_VER) #ifdef _MSC_VER
# define FORCE_INLINE __forceinline # define FORCE_INLINE __forceinline
#elif defined(__GNUC__) #elif defined(__GNUC__)
# define FORCE_INLINE inline __attribute__((always_inline)) # define FORCE_INLINE inline __attribute__((always_inline))
@@ -70,13 +73,13 @@
# define FORCE_INLINE inline # define FORCE_INLINE inline
#endif #endif
#if defined(USE_POPCNT) #ifdef USE_POPCNT
const bool HasPopCnt = true; const bool HasPopCnt = true;
#else #else
const bool HasPopCnt = false; const bool HasPopCnt = false;
#endif #endif
#if defined(IS_64BIT) #ifdef IS_64BIT
const bool Is64Bit = true; const bool Is64Bit = true;
#else #else
const bool Is64Bit = false; const bool Is64Bit = false;
@@ -87,26 +90,7 @@ typedef uint64_t Bitboard;
const int MAX_MOVES = 192; const int MAX_MOVES = 192;
const int MAX_PLY = 100; const int MAX_PLY = 100;
const int MAX_PLY_PLUS_2 = MAX_PLY + 2; const int MAX_PLY_PLUS_6 = MAX_PLY + 6;
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1;
const Bitboard FileCBB = FileABB << 2;
const Bitboard FileDBB = FileABB << 3;
const Bitboard FileEBB = FileABB << 4;
const Bitboard FileFBB = FileABB << 5;
const Bitboard FileGBB = FileABB << 6;
const Bitboard FileHBB = FileABB << 7;
const Bitboard Rank1BB = 0xFF;
const Bitboard Rank2BB = Rank1BB << (8 * 1);
const Bitboard Rank3BB = Rank1BB << (8 * 2);
const Bitboard Rank4BB = Rank1BB << (8 * 3);
const Bitboard Rank5BB = Rank1BB << (8 * 4);
const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
/// A move needs 16 bits to be stored /// A move needs 16 bits to be stored
/// ///
@@ -120,24 +104,24 @@ const Bitboard Rank8BB = Rank1BB << (8 * 7);
/// while MOVE_NONE and MOVE_NULL have the same origin and destination square. /// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
enum Move { enum Move {
MOVE_NONE = 0, MOVE_NONE,
MOVE_NULL = 65 MOVE_NULL = 65
}; };
enum MoveType { enum MoveType {
NORMAL = 0, NORMAL,
PROMOTION = 1 << 14, PROMOTION = 1 << 14,
ENPASSANT = 2 << 14, ENPASSANT = 2 << 14,
CASTLE = 3 << 14 CASTLE = 3 << 14
}; };
enum CastleRight { // Defined as in PolyGlot book hash key enum CastleRight { // Defined as in PolyGlot book hash key
CASTLES_NONE = 0, CASTLES_NONE,
WHITE_OO = 1, WHITE_OO,
WHITE_OOO = 2, WHITE_OOO = WHITE_OO << 1,
BLACK_OO = 4, BLACK_OO = WHITE_OO << 2,
BLACK_OOO = 8, BLACK_OOO = WHITE_OO << 3,
ALL_CASTLES = 15, ALL_CASTLES = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO,
CASTLE_RIGHT_NB = 16 CASTLE_RIGHT_NB = 16
}; };
@@ -148,7 +132,7 @@ enum CastlingSide {
}; };
enum Phase { enum Phase {
PHASE_ENDGAME = 0, PHASE_ENDGAME,
PHASE_MIDGAME = 128, PHASE_MIDGAME = 128,
MG = 0, EG = 1, PHASE_NB = 2 MG = 0, EG = 1, PHASE_NB = 2
}; };
@@ -161,9 +145,9 @@ enum ScaleFactor {
}; };
enum Bound { enum Bound {
BOUND_NONE = 0, BOUND_NONE,
BOUND_UPPER = 1, BOUND_UPPER,
BOUND_LOWER = 2, BOUND_LOWER,
BOUND_EXACT = BOUND_UPPER | BOUND_LOWER BOUND_EXACT = BOUND_UPPER | BOUND_LOWER
}; };
@@ -189,15 +173,15 @@ enum Value {
}; };
enum PieceType { enum PieceType {
NO_PIECE_TYPE = 0, ALL_PIECES = 0, NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6, ALL_PIECES = 0,
PIECE_TYPE_NB = 8 PIECE_TYPE_NB = 8
}; };
enum Piece { enum Piece {
NO_PIECE = 0, NO_PIECE,
W_PAWN = 1, W_KNIGHT = 2, W_BISHOP = 3, W_ROOK = 4, W_QUEEN = 5, W_KING = 6, W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14, B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
PIECE_NB = 16 PIECE_NB = 16
}; };
@@ -212,7 +196,7 @@ enum Depth {
DEPTH_ZERO = 0 * ONE_PLY, DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY, DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY, DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_QS_RECAPTURES = -5 * ONE_PLY, DEPTH_QS_RECAPTURES = -7 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY DEPTH_NONE = -127 * ONE_PLY
}; };
@@ -244,11 +228,11 @@ enum Square {
}; };
enum File { enum File {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB = 8 FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB
}; };
enum Rank { enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB = 8 RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB
}; };
@@ -257,7 +241,7 @@ enum Rank {
/// for midgame value. Compiler is free to choose the enum type as long as can /// for midgame value. Compiler is free to choose the enum type as long as can
/// keep its data, so ensure Score to be an integer type. /// keep its data, so ensure Score to be an integer type.
enum Score { enum Score {
SCORE_ZERO = 0, SCORE_ZERO,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX, SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
}; };
@@ -267,7 +251,7 @@ inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
/// Extracting the signed lower and upper 16 bits it not so trivial because /// Extracting the signed lower and upper 16 bits it not so trivial because
/// according to the standard a simple cast to short is implementation defined /// according to the standard a simple cast to short is implementation defined
/// and so is a right shift of a signed integer. /// and so is a right shift of a signed integer.
inline Value mg_value(Score s) { return Value(((s + 32768) & ~0xffff) / 0x10000); } inline Value mg_value(Score s) { return Value(((s + 0x8000) & ~0xffff) / 0x10000); }
/// On Intel 64 bit we have a small speed regression with the standard conforming /// On Intel 64 bit we have a small speed regression with the standard conforming
/// version, so use a faster code in this case that, although not 100% standard /// version, so use a faster code in this case that, although not 100% standard
@@ -324,38 +308,17 @@ inline Score operator/(Score s, int i) {
return make_score(mg_value(s) / i, eg_value(s) / i); return make_score(mg_value(s) / i, eg_value(s) / i);
} }
/// Weight score v by score w trying to prevent overflow
inline Score apply_weight(Score v, Score w) {
return make_score((int(mg_value(v)) * mg_value(w)) / 0x100,
(int(eg_value(v)) * eg_value(w)) / 0x100);
}
#undef ENABLE_OPERATORS_ON #undef ENABLE_OPERATORS_ON
#undef ENABLE_SAFE_OPERATORS_ON #undef ENABLE_SAFE_OPERATORS_ON
namespace Zobrist {
extern Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
extern Key enpassant[FILE_NB];
extern Key castle[CASTLE_RIGHT_NB];
extern Key side;
extern Key exclusion;
void init();
}
CACHE_LINE_ALIGNMENT
extern Score pieceSquareTable[PIECE_NB][SQUARE_NB];
extern Value PieceValue[PHASE_NB][PIECE_NB]; extern Value PieceValue[PHASE_NB][PIECE_NB];
extern int SquareDistance[SQUARE_NB][SQUARE_NB];
struct MoveStack { struct ExtMove {
Move move; Move move;
int score; int score;
}; };
inline bool operator<(const MoveStack& f, const MoveStack& s) { inline bool operator<(const ExtMove& f, const ExtMove& s) {
return f.score < s.score; return f.score < s.score;
} }
@@ -429,18 +392,6 @@ inline bool opposite_colors(Square s1, Square s2) {
return ((s >> 3) ^ s) & 1; return ((s >> 3) ^ s) & 1;
} }
inline int file_distance(Square s1, Square s2) {
return abs(file_of(s1) - file_of(s2));
}
inline int rank_distance(Square s1, Square s2) {
return abs(rank_of(s1) - rank_of(s2));
}
inline int square_distance(Square s1, Square s2) {
return SquareDistance[s1][s2];
}
inline char file_to_char(File f, bool tolower = true) { inline char file_to_char(File f, bool tolower = true) {
return char(f - FILE_A + (tolower ? 'a' : 'A')); return char(f - FILE_A + (tolower ? 'a' : 'A'));
} }
@@ -489,4 +440,4 @@ inline const std::string square_to_string(Square s) {
return ch; return ch;
} }
#endif // !defined(TYPES_H_INCLUDED) #endif // #ifndef TYPES_H_INCLUDED
+16 -12
View File
@@ -42,8 +42,8 @@ namespace {
// position just before to start searching). Needed by repetition draw detection. // position just before to start searching). Needed by repetition draw detection.
Search::StateStackPtr SetupStates; Search::StateStackPtr SetupStates;
void set_option(istringstream& up); void setoption(istringstream& up);
void set_position(Position& pos, istringstream& up); void position(Position& pos, istringstream& up);
void go(const Position& pos, istringstream& up); void go(const Position& pos, istringstream& up);
} }
@@ -55,7 +55,7 @@ namespace {
void UCI::loop(const string& args) { void UCI::loop(const string& args) {
Position pos(StartFEN, false, Threads.main_thread()); // The root position Position pos(StartFEN, false, Threads.main()); // The root position
string token, cmd = args; string token, cmd = args;
do { do {
@@ -76,7 +76,7 @@ void UCI::loop(const string& args) {
if (token != "ponderhit" || Search::Signals.stopOnPonderhit) if (token != "ponderhit" || Search::Signals.stopOnPonderhit)
{ {
Search::Signals.stop = true; Search::Signals.stop = true;
Threads.main_thread()->notify_one(); // Could be sleeping Threads.main()->notify_one(); // Could be sleeping
} }
else else
Search::Limits.ponder = false; Search::Limits.ponder = false;
@@ -102,15 +102,19 @@ void UCI::loop(const string& args) {
<< "\n" << Options << "\n" << Options
<< "\nuciok" << sync_endl; << "\nuciok" << sync_endl;
else if (token == "eval")
{
Search::RootColor = pos.side_to_move(); // Ensure it is set
sync_cout << Eval::trace(pos) << sync_endl;
}
else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ } else if (token == "ucinewgame") { /* Avoid returning "Unknown command" */ }
else if (token == "go") go(pos, is); else if (token == "go") go(pos, is);
else if (token == "position") set_position(pos, is); else if (token == "position") position(pos, is);
else if (token == "setoption") set_option(is); else if (token == "setoption") setoption(is);
else if (token == "flip") pos.flip(); else if (token == "flip") pos.flip();
else if (token == "bench") benchmark(pos, is); else if (token == "bench") benchmark(pos, is);
else if (token == "d") sync_cout << pos.pretty() << sync_endl; else if (token == "d") sync_cout << pos.pretty() << sync_endl;
else if (token == "isready") sync_cout << "readyok" << sync_endl; else if (token == "isready") sync_cout << "readyok" << sync_endl;
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
else else
sync_cout << "Unknown command: " << cmd << sync_endl; sync_cout << "Unknown command: " << cmd << sync_endl;
@@ -122,12 +126,12 @@ void UCI::loop(const string& args) {
namespace { namespace {
// set_position() is called when engine receives the "position" UCI command. // position() is called when engine receives the "position" UCI command.
// The function sets up the position described in the given fen string ("fen") // The function sets up the position described in the given fen string ("fen")
// or the starting position ("startpos") and then makes the moves given in the // or the starting position ("startpos") and then makes the moves given in the
// following move list ("moves"). // following move list ("moves").
void set_position(Position& pos, istringstream& is) { void position(Position& pos, istringstream& is) {
Move m; Move m;
string token, fen; string token, fen;
@@ -145,7 +149,7 @@ namespace {
else else
return; return;
pos.set(fen, Options["UCI_Chess960"], Threads.main_thread()); pos.set(fen, Options["UCI_Chess960"], Threads.main());
SetupStates = Search::StateStackPtr(new std::stack<StateInfo>()); SetupStates = Search::StateStackPtr(new std::stack<StateInfo>());
// Parse move list (if any) // Parse move list (if any)
@@ -157,10 +161,10 @@ namespace {
} }
// set_option() is called when engine receives the "setoption" UCI command. The // setoption() is called when engine receives the "setoption" UCI command. The
// function updates the UCI option ("name") to the given value ("value"). // function updates the UCI option ("name") to the given value ("value").
void set_option(istringstream& is) { void setoption(istringstream& is) {
string token, name, value; string token, name, value;
+9 -12
View File
@@ -51,31 +51,28 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const
/// init() initializes the UCI options to their hard coded default values /// init() initializes the UCI options to their hard coded default values
/// and initializes the default value of "Threads" and "Min Split Depth"
/// parameters according to the number of CPU cores detected.
void init(OptionsMap& o) { void init(OptionsMap& o) {
int cpus = std::min(cpu_count(), MAX_THREADS); o["Write Debug Log"] = Option(false, on_logger);
int msd = cpus < 8 ? 4 : 7; o["Write Search Log"] = Option(false);
o["Use Debug Log"] = Option(false, on_logger);
o["Use Search Log"] = Option(false);
o["Search Log Filename"] = Option("SearchLog.txt"); o["Search Log Filename"] = Option("SearchLog.txt");
o["Book File"] = Option("book.bin"); o["Book File"] = Option("book.bin");
o["Best Book Move"] = Option(false); o["Best Book Move"] = Option(false);
o["Contempt Factor"] = Option(0, -50, 50); o["Contempt Factor"] = Option(0, -50, 50);
o["Mobility (Middle Game)"] = Option(100, 0, 200, on_eval); o["Mobility (Midgame)"] = Option(100, 0, 200, on_eval);
o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval); o["Mobility (Endgame)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Middle Game)"] = Option(100, 0, 200, on_eval); o["Pawn Structure (Midgame)"] = Option(100, 0, 200, on_eval);
o["Pawn Structure (Endgame)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Midgame)"] = Option(100, 0, 200, on_eval);
o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval); o["Passed Pawns (Endgame)"] = Option(100, 0, 200, on_eval);
o["Space"] = Option(100, 0, 200, on_eval); o["Space"] = Option(100, 0, 200, on_eval);
o["Aggressiveness"] = Option(100, 0, 200, on_eval); o["Aggressiveness"] = Option(100, 0, 200, on_eval);
o["Cowardice"] = Option(100, 0, 200, on_eval); o["Cowardice"] = Option(100, 0, 200, on_eval);
o["Min Split Depth"] = Option(msd, 4, 12, on_threads); o["Min Split Depth"] = Option(0, 0, 12, on_threads);
o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads); o["Max Threads per Split Point"] = Option(5, 4, 8, on_threads);
o["Threads"] = Option(cpus, 1, MAX_THREADS, on_threads); o["Threads"] = Option(1, 1, MAX_THREADS, on_threads);
o["Use Sleeping Threads"] = Option(false); o["Idle Threads Sleep"] = Option(false);
o["Hash"] = Option(32, 1, 8192, on_hash_size); o["Hash"] = Option(32, 1, 8192, on_hash_size);
o["Clear Hash"] = Option(on_clear_hash); o["Clear Hash"] = Option(on_clear_hash);
o["Ponder"] = Option(true); o["Ponder"] = Option(true);
+2 -2
View File
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(UCIOPTION_H_INCLUDED) #ifndef UCIOPTION_H_INCLUDED
#define UCIOPTION_H_INCLUDED #define UCIOPTION_H_INCLUDED
#include <map> #include <map>
@@ -66,4 +66,4 @@ void loop(const std::string&);
extern UCI::OptionsMap Options; extern UCI::OptionsMap Options;
#endif // !defined(UCIOPTION_H_INCLUDED) #endif // #ifndef UCIOPTION_H_INCLUDED